Indent/Unindent Selected Lines

Was getting rather annoyed that I can’t highlight multiple lines of text and indent/unindent them all at once like you can in Visual Studio Code. Wrote a couple operators that do that, added in some keymaps too so it’s easy to use from the get-go. Ctrl + ] indents the highlighted block, Ctrl + [ unindents it.

Feel free to copy and paste this code

bl_info = {
    "name": "Indent Block",
    "author": "Fez_Master",
    "version": (1, 0),
    "blender": (3, 1, 2),
    "location": "Text Editor",
    "description": "Indent and unindent highlighted block",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "MyAddons"}

import bpy
from bpy_extras import keyconfig_utils

class TEXT_EDITOR_OT_indent_block(bpy.types.Operator):
    bl_idname = 'text.indent_block'
    bl_label = 'Indent Highlighted Block'
    bl_options = {'REGISTER', 'UNDO_GROUPED'}
    
    @classmethod
    def poll(cls, context):
        return context.area.type == 'TEXT_EDITOR'
    
    def execute(self, context):
        ct = bpy.context.edit_text
        line_range = [int(ct.current_line_index), int(ct.select_end_line_index)]
        charindex = [int(ct.current_character), int(ct.select_end_character)]
        if line_range[0] > line_range[1]:
            line_range.sort()
            charindex.append(charindex[0])
            charindex.pop(0)
        for i in range(line_range[0], line_range[1] + 1):
            ct.cursor_set(i)
            ct.write('    ')
        ct.cursor_set(line_range[0], character=charindex[0] + 4)
        ct.cursor_set(line_range[1], character=charindex[1] + 4, select=True)
        return {'FINISHED'}

class TEXT_EDITOR_OT_unindent_block(bpy.types.Operator):
    bl_idname = 'text.unindent_block'
    bl_label = 'Unindent Highlighted block'
    bl_options = {'REGISTER', 'UNDO_GROUPED'}
    
    @classmethod
    def poll(cls, context):
        return context.area.type == 'TEXT_EDITOR'
    
    def execute(self, context):
        ct = bpy.context.edit_text
        line_range = [int(ct.current_line_index), int(ct.select_end_line_index)]
        charindex = [int(ct.current_character), int(ct.select_end_character)]
        if line_range[0] > line_range[1]:
            line_range.sort()
            charindex.append(charindex[0])
            charindex.pop(0)
        for i in range(line_range[0], line_range[1] + 1):
            if ct.lines[i].body.startswith('    '):
                if (i in line_range) and (charindex[line_range.index(i)] < 4):
                    charindex[line_range.index(i)] = 4
                ct.cursor_set(i)
                ct.cursor_set(i, character=4, select=True)
                ct.write('')
            elif (i in line_range) and (charindex[line_range.index(i)] < 4):
                charindex[line_range.index(i)] += 4
        ct.cursor_set(line_range[0], character=charindex[0] - 4)
        ct.cursor_set(line_range[1], character=charindex[1] - 4, select=True)
        return {'FINISHED'}

keymap_set = [(
    "Text",
    {"space_type": 'TEXT_EDITOR', "region_type": 'WINDOW'},
    
    {"items": [
    ("text.indent_block", {'type': 'RIGHT_BRACKET', 'value': 'PRESS', 'ctrl': True}, None),
    ("text.unindent_block", {'type': 'LEFT_BRACKET', 'value': 'PRESS', 'ctrl': True}, None),
    ]})]

def register():
    bpy.utils.register_class(TEXT_EDITOR_OT_indent_block)
    bpy.utils.register_class(TEXT_EDITOR_OT_unindent_block)
    keyconfig_utils.addon_keymap_register(keymap_set)
    
def unregister():
    keyconfig_utils.addon_keymap_unregister(keymap_set)
    bpy.utils.unregister_class(TEXT_EDITOR_OT_indent_block)
    bpy.utils.unregister_class(TEXT_EDITOR_OT_unindent_block)
    
if __name__ == '__main__':
    register()
1 Like