bl_info = {
    "name": "Text Replacement",
    "author": "Mackraken (mackraken2023@hotmail.com)",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "Text Editor -> Text Replacement",
    "description": "Automatically replaces text while typing on the text editor",
    "warning": "Disable the script at Text Properties (Ctrl+F) if enters on an infinite loop i may have missed. No need to restart or lose any work.",
    "wiki_url": "",
    "category": "Text Editor",
}

# WARNING: if the script enters and infinite loop i might have missed, disable it at Text Properties (Ctrl + F)
# There is not need to restart or lose the current work

# edit your replacements here as:
replacements = {
    #"search": ("replacement", cursor offset after replacement)
    "false": ("False", 0),
    "true": ("True", 0),
    '"': ('""', 0),
    "(": ("()", 0),
    "'": ("''", 0),
    "[": ("[]", 0),
    "{": ("{}", 0)
}

for k in __builtins__:
    obj = __builtins__[k]
    cls = type(obj).__name__
    if "method" in cls or "type" in cls:
        replacements[k] = ("%s()" % k, 1)
        
    # print(k, type(obj).__name__)
import bpy
from bpy.props import BoolProperty, PointerProperty, StringProperty

handler = None
last = None 
last_index = None 

def get_space(context):
    for area in context.screen.areas:
        if area.type == "TEXT_EDITOR":
            return area.spaces.active

# test ground: type replacements in between characters
#abcdeghijkl

def text_handler(spc):
    global last, last_index
    
    text = spc.text
    if not text:
        return
               
    line = text.current_line.body
    
    #if first time typing or switching lines dont do anything
    if last is None or last_index != text.current_line_index:
        last = line
        last_index = text.current_line_index
        return
    
    # if we are adding characters (typing), check the replacements
    if line != last and len(line)>len(last):
        for search, (repl, offset) in replacements.items():
            s = text.current_character - len(search)
            e = text.current_character
            if line[s:e] == search:
                # print(line)
                start = line[:s]
                end = line[len(start)+len(search):]
                # print(start, repl, end)
                last = start + repl + end
                text.current_line.body = last
                
                # you can automatically move the cursor to a different position
                # after a replacement
                for i in range(0, abs(offset)):
                    if offset < 0:
                        bpy.ops.text.move(type="PREVIOUS_CHARACTER")
                    else:
                        bpy.ops.text.move(type="NEXT_CHARACTER")
                        
                #if a replacement is found dont check any other
                return
                
    last = line

        
def activate_handler(self, context):
    global handler
    
    spc = get_space(context)
    if not spc:
        return
    
    enabled = context.scene.text_replace.enabled
        
    if enabled:
        handler = spc.draw_handler_add(text_handler, (spc,), "WINDOW", "POST_PIXEL")
        print("handler activated", handler)
    else:
        if handler is not None:
            spc.draw_handler_remove(handler, "WINDOW")
        handler = None
        print("handler deactivated")


class TextReplaceProperties(bpy.types.PropertyGroup):
    enabled = BoolProperty(name="Enabled", description="Enables hotstrings on the text editor", update=activate_handler)

bpy.utils.register_class(TextReplaceProperties)
bpy.types.Scene.text_replace = PointerProperty(type=TextReplaceProperties)

class TextReplacementPanel(bpy.types.Panel):
    """Text replacement properties"""
    bl_label = "Text Replacement"
    bl_idname = "TEXT_PT_text_replacement"
    bl_space_type = 'TEXT_EDITOR'
    bl_region_type = 'UI'
    bl_category = "Text"

    def draw(self, context):
        repl = context.scene.text_replace
        layout = self.layout
        layout.prop(repl, "enabled")

def register():
    bpy.utils.register_class(TextReplacementPanel)

def unregister():
    bpy.utils.unregister_class(TextReplacementPanel)


if __name__ == "__main__":
    register()
