From bdabf65e52fca3d3bfb08248d28a388758aa3484 Mon Sep 17 00:00:00 2001 From: tin2tin Date: Thu, 19 Sep 2019 23:44:46 +0200 Subject: [PATCH 1/3] Updates to 2.80 wip --- README.md | 2 +- __init__.py | 2 +- addon_development/addon_selection.py | 4 +-- addon_development/convert_indentation.py | 4 +-- addon_development/export_addon.py | 2 +- addon_development/file_operators.py | 22 +++++++------- addon_development/panels.py | 4 +-- .../suggestions/generate_fake_bpy.py | 4 +-- autocompletion/suggestions/rna_utils.py | 2 +- code_templates/base.py | 2 +- code_templates/insert_keymap.py | 2 +- code_templates/insert_license.py | 4 +-- code_templates/insert_menu.py | 2 +- code_templates/insert_operator.py | 10 +++---- quick_operators.py | 12 ++++---- settings.py | 30 +++++++++---------- 16 files changed, 54 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 1b51a55..c5954f0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -Autocompletion, templates and addon development tools for Blenders text editor. +Autocompletion, templates and addon development tools for the text editor. Manual: http://code-autocomplete-manual.readthedocs.org/en/latest/ diff --git a/__init__.py b/__init__.py index c545809..f5e0c6b 100644 --- a/__init__.py +++ b/__init__.py @@ -25,7 +25,7 @@ "description": "Autocompletion, templates and addon development tools for the text editor.", "author": "Jacques Lucke", "version": (2, 0, 0), - "blender": (2, 7, 4), + "blender": (2, 80, 0), "location": "Text Editor", "category": "Development" } diff --git a/addon_development/addon_selection.py b/addon_development/addon_selection.py index b10e167..b9125f9 100644 --- a/addon_development/addon_selection.py +++ b/addon_development/addon_selection.py @@ -26,7 +26,7 @@ def get_items(self, context): items.append((addon, addon, "")) return items - item = bpy.props.EnumProperty(items = get_items) + item: bpy.props.EnumProperty(items = get_items) def invoke(self, context, event): context.window_manager.invoke_search_popup(self) @@ -66,7 +66,7 @@ class CreateNewAddon(bpy.types.Operator): bl_description = "Create a folder in the addon directory and setup a basic code base" bl_options = {"REGISTER"} - new_addon_type = EnumProperty(default = "BASIC", items = new_addon_type_items) + new_addon_type: EnumProperty(default = "BASIC", items = new_addon_type_items) @classmethod def poll(cls, context): diff --git a/addon_development/convert_indentation.py b/addon_development/convert_indentation.py index 9ffbbb8..1c7aae3 100644 --- a/addon_development/convert_indentation.py +++ b/addon_development/convert_indentation.py @@ -9,8 +9,8 @@ class ConvertAddonIndentation(bpy.types.Operator): bl_description = "" bl_options = {"REGISTER"} - old_indentation = StringProperty(default = "\t") - new_indentation = StringProperty(default = " ") + old_indentation: StringProperty(default = "\t") + new_indentation: StringProperty(default = " ") @classmethod def poll(cls, context): diff --git a/addon_development/export_addon.py b/addon_development/export_addon.py index e037aad..60ac4b9 100644 --- a/addon_development/export_addon.py +++ b/addon_development/export_addon.py @@ -10,7 +10,7 @@ class ExportAddon(bpy.types.Operator): bl_description = "Save a .zip file of the addon" bl_options = {"REGISTER"} - filepath = StringProperty(subtype = "FILE_PATH") + filepath: StringProperty(subtype = "FILE_PATH") @classmethod def poll(cls, context): diff --git a/addon_development/file_operators.py b/addon_development/file_operators.py index 5806cb6..d3b3d61 100644 --- a/addon_development/file_operators.py +++ b/addon_development/file_operators.py @@ -18,9 +18,9 @@ class NewFile(bpy.types.Operator): bl_description = "Create a new file in this directory" bl_options = {"REGISTER"} - directory = StringProperty(name = "Directory", default = "") - name = StringProperty(name = "File Name", default = "") - content = StringProperty(name = "Content", default = "") + directory: StringProperty(name = "Directory", default = "") + name: StringProperty(name = "File Name", default = "") + content: StringProperty(name = "Content", default = "") @classmethod def poll(cls, context): @@ -48,8 +48,8 @@ class NewDirectory(bpy.types.Operator): bl_description = "Create a new subdirectory" bl_options = {"REGISTER"} - directory = StringProperty(name = "Directory", default = "") - name = StringProperty(name = "Directory Name", default = "") + directory: StringProperty(name = "Directory", default = "") + name: StringProperty(name = "Directory Name", default = "") @classmethod def poll(cls, context): @@ -74,7 +74,7 @@ class FileMenuOpener(bpy.types.Operator): bl_idname = "code_autocomplete.open_file_menu" bl_label = "Open File Menu" - path = StringProperty(name = "Path", default = "") + path: StringProperty(name = "Path", default = "") def invoke(self, context, event): context.window_manager.popup_menu(self.drawMenu, title = "{} - File Menu".format(os.path.basename(self.path))) @@ -105,7 +105,7 @@ class OpenFile(bpy.types.Operator): bl_description = "Load the file into the text editor" bl_options = {"REGISTER"} - path = StringProperty(name = "Path", default = "") + path: StringProperty(name = "Path", default = "") def execute(self, context): text = None @@ -126,7 +126,7 @@ class OpenExternalFileBrowser(bpy.types.Operator): bl_description = "" bl_options = {"REGISTER"} - directory = StringProperty(name = "Directory", default = "") + directory: StringProperty(name = "Directory", default = "") def execute(self, context): bpy.ops.wm.path_open(filepath = self.directory) @@ -139,8 +139,8 @@ class RenameFile(bpy.types.Operator): bl_description = "" bl_options = {"REGISTER"} - path = StringProperty(name = "Directory", default = "") - new_name = StringProperty(name = "Directory", description = "New file name", default = "") + path: StringProperty(name = "Directory", default = "") + new_name: StringProperty(name = "Directory", description = "New file name", default = "") def invoke(self, context, event): self.new_name = os.path.basename(self.path) @@ -169,7 +169,7 @@ class DeleteFile(bpy.types.Operator): bl_description = "Delete file on the hard drive" bl_options = {"REGISTER"} - path = StringProperty(name = "Directory", default = "") + path: StringProperty(name = "Directory", default = "") def invoke(self, context, event): return context.window_manager.invoke_confirm(self, event) diff --git a/addon_development/panels.py b/addon_development/panels.py index 4d69bc6..d77bfab 100644 --- a/addon_development/panels.py +++ b/addon_development/panels.py @@ -112,8 +112,8 @@ class SetDirectoryVisibility(bpy.types.Operator): bl_description = "" bl_options = {"REGISTER"} - directory = StringProperty(name = "Directory", default = "") - visibility = BoolProperty(name = "Visibility", default = True) + directory: StringProperty(name = "Directory", default = "") + visibility: BoolProperty(name = "Visibility", default = True) def execute(self, context): global directory_visibility diff --git a/autocompletion/suggestions/generate_fake_bpy.py b/autocompletion/suggestions/generate_fake_bpy.py index 336bd8d..fab1e68 100644 --- a/autocompletion/suggestions/generate_fake_bpy.py +++ b/autocompletion/suggestions/generate_fake_bpy.py @@ -47,7 +47,7 @@ def generate_fake_bpy(): def create_init(): path = os.path.join(directory, "__init__.py") - file = open(path, "w+", encoding='utf-8') + file = open(path, "w+") file.write(init_content) file.close() @@ -248,7 +248,7 @@ def get_property_declaration(property): def write_code_file(name, code): path = os.path.join(private_path, name.lower() + ".py") - file = open(path, "w+", encoding='utf-8') + file = open(path, "w+") file.write(code) file.close() diff --git a/autocompletion/suggestions/rna_utils.py b/autocompletion/suggestions/rna_utils.py index 5939064..49ec108 100644 --- a/autocompletion/suggestions/rna_utils.py +++ b/autocompletion/suggestions/rna_utils.py @@ -52,7 +52,7 @@ def get_enum_items(property): def get_property_default(property): if len(getattr(property, "default_array", [])) > 0: return repr(property.default_array[:]) - return repr(getattr(property, "default", None)) + return repr(property.default) def get_readable_property_type(property): suffix = "[{}]".format(property.array_length) if getattr(property, "array_length", 1) > 1 else "" diff --git a/code_templates/base.py b/code_templates/base.py index 560b0ec..99c261b 100644 --- a/code_templates/base.py +++ b/code_templates/base.py @@ -40,7 +40,7 @@ def poll(cls, context): return TextBlock.get_active() class InsertClassTemplateBase(InsertTemplateBase): - class_name = StringProperty(name = "Class Name", default = "") + class_name: StringProperty(name = "Class Name", default = "") def invoke(self, context, event): dpiFactor = getDpiFactor() diff --git a/code_templates/insert_keymap.py b/code_templates/insert_keymap.py index 1b90f4f..c6e9415 100644 --- a/code_templates/insert_keymap.py +++ b/code_templates/insert_keymap.py @@ -9,7 +9,7 @@ class InsertKeymap(bpy.types.Operator, InsertTemplateBase): bl_label = "Insert Keymap" bl_description = "" - insert_callers = BoolProperty(name = "Extend Register Functions", default = True, + insert_callers: BoolProperty(name = "Extend Register Functions", default = True, description = "Insert code in register and unregister functions if they exist") def invoke(self, context, event): diff --git a/code_templates/insert_license.py b/code_templates/insert_license.py index 18ef889..567aa94 100644 --- a/code_templates/insert_license.py +++ b/code_templates/insert_license.py @@ -9,8 +9,8 @@ class InsertLicense(bpy.types.Operator, InsertTemplateBase): bl_label = "Insert License" bl_description = "" - author_name = StringProperty(name = "Name", default = bpy.context.user_preferences.system.author) - author_mail = StringProperty(name = "eMail", default = "") + author_name: StringProperty(name = "Name", default = bpy.context.user_preferences.system.author) + author_mail: StringProperty(name = "eMail", default = "") def invoke(self, context, event): dpiFactor = getDpiFactor() diff --git a/code_templates/insert_menu.py b/code_templates/insert_menu.py index fa695e4..041a8c4 100644 --- a/code_templates/insert_menu.py +++ b/code_templates/insert_menu.py @@ -14,7 +14,7 @@ class InsertMenu(bpy.types.Operator, InsertClassTemplateBase): bl_label = "Insert Menu" bl_description = "" - menu_type = EnumProperty(items = menu_type_items, default = "NORMAL") + menu_type: EnumProperty(items = menu_type_items, default = "NORMAL") def execute(self, context): if self.menu_type == "NORMAL": code = menu_template diff --git a/code_templates/insert_operator.py b/code_templates/insert_operator.py index d81bc36..7a940af 100644 --- a/code_templates/insert_operator.py +++ b/code_templates/insert_operator.py @@ -15,7 +15,7 @@ class InsertOperator(bpy.types.Operator, InsertClassTemplateBase): bl_label = "Insert Operator" bl_description = "" - operator_type = EnumProperty(items = operator_type_items, default = "NORMAL") + operator_type: EnumProperty(items = operator_type_items, default = "NORMAL") def execute(self, context): if self.operator_type == "NORMAL": code = operator_template @@ -78,8 +78,8 @@ def poll(cls, context): return True def invoke(self, context, event): - args = () - self.draw_handler = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL") + args = (self, context) + self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL") context.window_manager.modal_handler_add(self) return {"RUNNING_MODAL"} @@ -95,9 +95,9 @@ def modal(self, context, event): return {"RUNNING_MODAL"} def finish(self): - bpy.types.SpaceView3D.draw_handler_remove(self.draw_handler, "WINDOW") + bpy.types.SpaceView3D.draw_handler_remove(self._handle, "WINDOW") return {"FINISHED"} - def draw_callback_px(self): + def draw_callback_px(tmp, self, context): pass ''' diff --git a/quick_operators.py b/quick_operators.py index 5b60067..d0dde38 100644 --- a/quick_operators.py +++ b/quick_operators.py @@ -44,9 +44,9 @@ class ConvertFileIndentation(bpy.types.Operator): bl_description = "" bl_options = {"REGISTER"} - path = StringProperty() - old_indentation = StringProperty(default = "\t") - new_indentation = StringProperty(default = " ") + path: StringProperty() + old_indentation: StringProperty(default = "\t") + new_indentation: StringProperty(default = " ") @classmethod def poll(cls, context): @@ -97,7 +97,7 @@ class OpenTextBlock(bpy.types.Operator): bl_description = "" bl_options = {"REGISTER"} - name = StringProperty() + name: StringProperty() @classmethod def poll(cls, context): @@ -142,9 +142,9 @@ def format_menu_extension(self, context): def register_menus(): - bpy.types.TEXT_MT_toolbox.append(right_click_menu_extension) + bpy.types.TEXT_MT_context_menu.append(right_click_menu_extension) bpy.types.TEXT_MT_format.append(format_menu_extension) def unregister_menus(): - bpy.types.TEXT_MT_toolbox.remove(right_click_menu_extension) + bpy.types.TEXT_MT_context_menu.remove(right_click_menu_extension) bpy.types.TEXT_MT_format.remove(format_menu_extension) diff --git a/settings.py b/settings.py index 5ed5189..fbc722c 100644 --- a/settings.py +++ b/settings.py @@ -11,34 +11,34 @@ def prop_changed(self, context): area.tag_redraw() class CompletionProviders (bpy.types.PropertyGroup): - use_jedi_completion = BoolProperty(default = True, name = "Use Jedi Completion", + use_jedi_completion: BoolProperty(default = True, name = "Use Jedi Completion", update = prop_changed, description = "Use the Jedi autocompletion library for python") - use_word_completion = BoolProperty(default = True, name = "Use Word Completion", + use_word_completion: BoolProperty(default = True, name = "Use Word Completion", update = prop_changed, description = "The context box will also contain words that you already used in the file") - use_operator_completion = BoolProperty(default = True, name = "Use Operator Completion", + use_operator_completion: BoolProperty(default = True, name = "Use Operator Completion", update = prop_changed, description = "Activate the autocompletion for calling operators (bpy.ops)") class ContextBoxProperties(bpy.types.PropertyGroup): - font_size = IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) - line_height = IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) - width = IntProperty(default = 200, name = "Width", min = 10, update = prop_changed) - padding = IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) - lines = IntProperty(default = 8, name = "Lines", min = 1, update = prop_changed) + font_size: IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) + line_height: IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) + width: IntProperty(default = 200, name = "Width", min = 10, update = prop_changed) + padding: IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) + lines: IntProperty(default = 8, name = "Lines", min = 1, update = prop_changed) class DescriptionBoxProperties(bpy.types.PropertyGroup): - font_size = IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) - line_height = IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) - padding = IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) + font_size: IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) + line_height: IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) + padding: IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) class AddonPreferences(bpy.types.AddonPreferences): bl_idname = addon_name - completion_providers = PointerProperty(type = CompletionProviders) - context_box = PointerProperty(type = ContextBoxProperties) - description_box = PointerProperty(type = DescriptionBoxProperties) + completion_providers: PointerProperty(type = CompletionProviders) + context_box: PointerProperty(type = ContextBoxProperties) + description_box: PointerProperty(type = DescriptionBoxProperties) - debug = BoolProperty(default = False, name = "Debug", + debug: BoolProperty(default = False, name = "Debug", update = prop_changed, description = "Turn on to get some debug information from this addon") def draw(self, context): From 24433df21783a1e146d2ad0e36205f6e0627849e Mon Sep 17 00:00:00 2001 From: tin2tin Date: Tue, 19 Nov 2019 09:19:21 +0100 Subject: [PATCH 2/3] Collected all classes in init --- __pycache__/__init__.cpython-37.pyc | Bin 0 -> 4034 bytes __pycache__/developer_utils.cpython-37.pyc | Bin 0 -> 1815 bytes __pycache__/quick_operators.cpython-37.pyc | Bin 0 -> 5209 bytes __pycache__/settings.cpython-37.pyc | Bin 0 -> 3205 bytes __pycache__/text_block.cpython-37.pyc | Bin 0 -> 13520 bytes addon_development/__init__.py | 10 +- .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 601 bytes .../addon_selection.cpython-37.pyc | Bin 0 -> 4300 bytes .../convert_indentation.cpython-37.pyc | Bin 0 -> 1592 bytes .../__pycache__/export_addon.cpython-37.pyc | Bin 0 -> 1991 bytes .../__pycache__/file_operators.cpython-37.pyc | Bin 0 -> 7859 bytes .../__pycache__/panels.cpython-37.pyc | Bin 0 -> 4446 bytes .../restart_blender.cpython-37.pyc | Bin 0 -> 3322 bytes .../__pycache__/run_addon.cpython-37.pyc | Bin 0 -> 1169 bytes .../__pycache__/utils.cpython-37.pyc | Bin 0 -> 3139 bytes addon_development/addon_selection.py | 216 ++--- addon_development/addon_templates/basic.txt | 44 +- .../addon_templates/developer_utils.txt | 84 +- .../addon_templates/multifile.txt | 122 +-- addon_development/convert_indentation.py | 68 +- addon_development/export_addon.py | 88 +-- addon_development/file_operators.py | 414 +++++----- addon_development/panels.py | 242 +++--- addon_development/restart_blender.py | 184 ++--- addon_development/run_addon.py | 52 +- addon_development/utils.py | 134 ++-- .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 208 bytes .../active_text_area.cpython-37.pyc | Bin 0 -> 2556 bytes .../autocomplete_handler.cpython-37.pyc | Bin 0 -> 8407 bytes .../__pycache__/event_utils.cpython-37.pyc | Bin 0 -> 1765 bytes .../__pycache__/exception.cpython-37.pyc | Bin 0 -> 405 bytes .../__pycache__/modal_operator.cpython-37.pyc | Bin 0 -> 4916 bytes autocompletion/active_text_area.py | 100 +-- autocompletion/autocomplete_handler.py | 484 ++++++------ autocompletion/event_utils.py | 80 +- autocompletion/exception.py | 10 +- autocompletion/modal_operator.py | 254 +++--- autocompletion/suggestions/__init__.py | 54 +- .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1185 bytes .../generate_fake_bpy.cpython-37.pyc | Bin 0 -> 13969 bytes .../__pycache__/interface.cpython-37.pyc | Bin 0 -> 988 bytes .../jedi_completion.cpython-37.pyc | Bin 0 -> 3007 bytes .../operator_completion.cpython-37.pyc | Bin 0 -> 4938 bytes .../__pycache__/rna_utils.cpython-37.pyc | Bin 0 -> 3291 bytes .../static_pattern_completion.cpython-37.pyc | Bin 0 -> 3093 bytes .../word_completion.cpython-37.pyc | Bin 0 -> 1622 bytes .../suggestions/generate_fake_bpy.py | 748 +++++++++--------- autocompletion/suggestions/interface.py | 34 +- autocompletion/suggestions/jedi_completion.py | 142 ++-- .../suggestions/operator_completion.py | 250 +++--- autocompletion/suggestions/rna_utils.py | 140 ++-- .../suggestions/static_pattern_completion.py | 108 +-- autocompletion/suggestions/word_completion.py | 60 +- .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 208 bytes .../__pycache__/base.cpython-37.pyc | Bin 0 -> 3239 bytes .../insert_addon_info.cpython-37.pyc | Bin 0 -> 1145 bytes .../__pycache__/insert_keymap.cpython-37.pyc | Bin 0 -> 2444 bytes .../insert_keymap_item.cpython-37.pyc | Bin 0 -> 2130 bytes .../__pycache__/insert_license.cpython-37.pyc | Bin 0 -> 2196 bytes .../__pycache__/insert_menu.cpython-37.pyc | Bin 0 -> 1629 bytes .../insert_operator.cpython-37.pyc | Bin 0 -> 3250 bytes .../__pycache__/insert_panel.cpython-37.pyc | Bin 0 -> 1276 bytes .../insert_register.cpython-37.pyc | Bin 0 -> 940 bytes code_templates/base.py | 128 +-- code_templates/insert_addon_info.py | 48 +- code_templates/insert_keymap.py | 118 +-- code_templates/insert_keymap_item.py | 96 +-- code_templates/insert_license.py | 102 +-- code_templates/insert_menu.py | 86 +- code_templates/insert_operator.py | 206 ++--- code_templates/insert_panel.py | 58 +- code_templates/insert_register.py | 42 +- 72 files changed, 2503 insertions(+), 2503 deletions(-) create mode 100644 __pycache__/__init__.cpython-37.pyc create mode 100644 __pycache__/developer_utils.cpython-37.pyc create mode 100644 __pycache__/quick_operators.cpython-37.pyc create mode 100644 __pycache__/settings.cpython-37.pyc create mode 100644 __pycache__/text_block.cpython-37.pyc create mode 100644 addon_development/__pycache__/__init__.cpython-37.pyc create mode 100644 addon_development/__pycache__/addon_selection.cpython-37.pyc create mode 100644 addon_development/__pycache__/convert_indentation.cpython-37.pyc create mode 100644 addon_development/__pycache__/export_addon.cpython-37.pyc create mode 100644 addon_development/__pycache__/file_operators.cpython-37.pyc create mode 100644 addon_development/__pycache__/panels.cpython-37.pyc create mode 100644 addon_development/__pycache__/restart_blender.cpython-37.pyc create mode 100644 addon_development/__pycache__/run_addon.cpython-37.pyc create mode 100644 addon_development/__pycache__/utils.cpython-37.pyc create mode 100644 autocompletion/__pycache__/__init__.cpython-37.pyc create mode 100644 autocompletion/__pycache__/active_text_area.cpython-37.pyc create mode 100644 autocompletion/__pycache__/autocomplete_handler.cpython-37.pyc create mode 100644 autocompletion/__pycache__/event_utils.cpython-37.pyc create mode 100644 autocompletion/__pycache__/exception.cpython-37.pyc create mode 100644 autocompletion/__pycache__/modal_operator.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/__init__.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/generate_fake_bpy.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/interface.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/jedi_completion.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/operator_completion.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/rna_utils.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/static_pattern_completion.cpython-37.pyc create mode 100644 autocompletion/suggestions/__pycache__/word_completion.cpython-37.pyc create mode 100644 code_templates/__pycache__/__init__.cpython-37.pyc create mode 100644 code_templates/__pycache__/base.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_addon_info.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_keymap.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_keymap_item.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_license.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_menu.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_operator.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_panel.cpython-37.pyc create mode 100644 code_templates/__pycache__/insert_register.cpython-37.pyc diff --git a/__pycache__/__init__.cpython-37.pyc b/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34f6fe2e796514fb1bb4823ea94a17036ce1bdbb GIT binary patch literal 4034 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 literal 0 HcmV?d00001 diff --git a/__pycache__/quick_operators.cpython-37.pyc b/__pycache__/quick_operators.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6028bdcb27b1149e8cc50b6b26bcab882269328 GIT binary patch literal 5209 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 Z<72X!A_!?Sy!Oy(d&BmyHS~wC{}0uq9*+P3 literal 0 HcmV?d00001 diff --git a/__pycache__/text_block.cpython-37.pyc b/__pycache__/text_block.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c188a5803db7a327b31f02321a4822250e9c9a4d GIT binary patch literal 13520 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 PV>pOL$LzOofa3fgloYuK literal 0 HcmV?d00001 diff --git a/addon_development/__init__.py b/addon_development/__init__.py index 458e33a..f2efe96 100644 --- a/addon_development/__init__.py +++ b/addon_development/__init__.py @@ -1,5 +1,5 @@ -import bpy -from bpy.props import * - -class AddonDevelopmentSceneProperties(bpy.types.PropertyGroup): - addon_name = StringProperty(name = "Addon Name", default = "", description = "Name of the currently selected addon") +import bpy +from bpy.props import * + +class AddonDevelopmentSceneProperties(bpy.types.PropertyGroup): + addon_name: StringProperty(name = "Addon Name", default = "", description = "Name of the currently selected addon") diff --git a/addon_development/__pycache__/__init__.cpython-37.pyc b/addon_development/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b705e8b26e50e37a8aec7e53684d6172ee3809b9 GIT binary patch literal 601 zcmYLGJ#Q2-5ViM%91EBHgls{boE%deo~ z_t3dRRQv@h#(SJHlHa`XzI~b*pUvkLqo}_BeD{m;?^NufAaX*-KP3PLT(M2cnc{ER zC4(G_I|c=^o9rh4nr{l=Yqlu=keaO)Jo0C4qPr-GoY3(#0kRDTw#lv-=D;CKX5Vrw zpgd;qfNb{S;VFR87x)>Ku|4XrZc*cFXDqtVp}(yrLA`2vjO_MF%C)(!!$+*!!8!Uv zjkQOGZ9u4-rk$>pB}cqhmFRWKygaWt_>0N*OCDdjo?);*X{L zG&CxSsM=o#*Xa-c+ZyA6l#SLVG|A9Q`GdKAnsvr%4P_rHN90BaQi{R{mO)to$i8^OFO!p z`bcl;GlGgwHBFhnw_|)ru(Y&WAImUW^l`o$>C&0OPU8DXw(Nj>;_P_4nU_3SC;u6r GW&Z$k*s*K? literal 0 HcmV?d00001 diff --git a/addon_development/__pycache__/addon_selection.cpython-37.pyc b/addon_development/__pycache__/addon_selection.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98f9502ea79290c1cd3122a5756b10aed6485cf9 GIT binary patch literal 4300 zcma)9&2!tv6~_V~34#>$S=nhkEz{IZm^y0fY12&Vrj}&N)l_COvYbpY%3xsbN&<}! zW&v6jhdRku&-BnkkJ9wizo)mo=9GV-r~cl8q$NuI0B7GWci%4FzV~|{*qEDh4Lt7O z|M|_mdBgaQ{!AVlgNJC@-$1y*S!8%c&pd|zW@PTse=D+L%d?olZC;A(xa5`MvR95P zUL|%sC$4(c*!A4F=GEf5SC8ksIc7XIc$rsT8@wV|uhE-7v%Ce&IlPLw>U3_AyS#R2 zdP`WV^Es@|P1lw=dup^A-$Od1(_)H!1mf@@7kL3p zOs#YWQi>$^1J2XL7q7xB&(w05jVFl9e;Gs}SF5!DDtkZ3k5q%^i|xd%7P~2q?B@PK z7zzCvRVS8A!h;Xiu&8>8h099d{Os4kluYG!d zKkLe{pJ)5}%xwRf5{m_&iWpQtK>m03v_I%W-OD&lu7Ic#Zj(%|3^f~n=TQ8 zglvEyqZk<>(FLOES7x^9RF+J~*6Up?O&<%RhiDlAVG{PP2R%{YC0>4QoH5Vg749Ik zRe2TAcGc38;DuPHkk&y&{))o)@PlgxU5mkWDRLKaemGB?o3ybx-l-?@BT+=OCaJX< zG<8;J76#CBl5RPLG{rf}Jez14K`uAWjJlCF2-bB2a2T`Yet32 z?x#`oD>_I=Pjh(d`UTgBXZm*1;nbn-;19hWZp0=Il$&ra>yqm*ua2xY^&a^i$z;na zI!UoedyM=E&0Y=zHfdu)0IkRp{T4`CTw6XqldlOxdgJ*PF650Ty|={Kgbj zUU^^$x2SRe;2Kpo0C2siZIG)Qd|v131-^)Wo-Y9~%c`-7K$DA(IM(PMzIz?i(|Tf9 z1=~0|Zxs80+JiKrR2C-9w}74+9qF4l5#vTH)d#fQEG5 zQH|+4{rsdarXalH0u%I^IxWNJBZ^3hu188eM#^MD3%UAXAU$DP}T_fY%phSV5 zQBT;1FIYXDRLCQ4p@@Bh3O|)?gJ7=UlT}X^g zXel$zloKOr;)_lSo-EDXF{6DwXcZbX4(osl%kcUx5EW@SQNr15?AL1~z1l;i{t~7d7MGBy-E0mSx zvdHchDrQxAiMkgxfSkilNh(%Wk{&Dj1eM5j8hAJ zMfH`3T2phgEhj4HE>iP*Mh?s!D&a`N}4bQ#PqkmAX-y38X=d zW^#086PvQ>%VJg=TUb9t%Zl3lwstm$4d0uA`E~1bS_}V229%@>wQ@Du7M~1~(yjcA z$lJ_F?k34Zk|et%t#(W8k)LDX4`|s6$h0^!7mzBQs$)3|k~mr`h2q}F7`0{pWKGwF z1zJiPlEsG@d8e*sidZ&NJWw*$FjFPU69^h#px_MfIj)O9otq`WNx$?ad6n~^D7ZeS X(4;F$3TO_#GVZx6cUBfnRyO|)j)Dz4DbC_?#Y* zX8AotA?+r$^!|Ga%cf)R#sj59X2OVbnT2AQXrt{HXTpU4J;@`}jq&@N;lknxh<*)9 z5X>iVUO|q5%bkF6H}E*?(x4>Dn9n;Zd|rM`!15PX1*{6M@|A0HMT07@@zrY*tnfN- zz+B^Nkn_6zaz|!wg)&bu!GnxLvXMz-Hu^r6T!hiU$XNFCR2b2U=dKGd$&&NEMU<@~ z%+^vIORRsm_x#}K<=){3S^;*-T%1OO)YyhhdAJ;fZNadOOk6B)#||)n183xQ2Sk@Z z8FEDc1O?6&))EMJ<5b%+Bx=Oa{6SO$wmyKWHBu{k*r^EZYXQWdSlgO;Vw)djdJT zGS_zfBhbOd4My}`2KsOGCbLY7^iJHwG9L&`k9+kyV!~-Kf14E-Tp+2pG zIF!*k9ewjDnik*1*cLdDB_E_<>IE#$$b=5}4VhBoT#*?CwjOodi8FC0Y5Ig4Z7IrgFbbrW^<shp%?!qKokEYs*jq_z$# zF6O-t?}4p?EpvPtw5=Z*1^=8EO6Kr%T-rt$Mp-83FNcQsD{&fW-4~`Sxmt(x)mNwx z(h|&~3$Otc)(u5tcT@o@{z^`r)}0#jE5&)UqCQ98uR+-gv}P-Z`+>uONm5-r0P{+C l<}!!w^?!lnA|qAt_5WRK{l{}ge!~9I*f>5koln=CzX1nzvZw$6 literal 0 HcmV?d00001 diff --git a/addon_development/__pycache__/export_addon.cpython-37.pyc b/addon_development/__pycache__/export_addon.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ef93e0fa195d1c31015292432713f2cab7b59a3 GIT binary patch literal 1991 zcmZWqOK%%D5GMDbT|J#9M%<)Gsvgw>LXk^@A}C@3EgZazj#< zEv$e7PH)YBuuz~te@}0`_LRTSQ)j3+a8nW-4$0B%JiZxiG@CU7qxSn>FOv!(f8*g| zc);9;rJey{gwc#d%biBlo}I{n-O1d2!d&KM-q?@)aV4sZgD9ZnAz?nNyd|u{$tWCE z&)lfSYAj^cw=}9Vx<@+o_n@2fJJisx-&0^au2~&Uq~y7d6UIay^OIC*Wm>QJ#Z8`! zxw($#|4J5#J{qFFi{S!uAC~$N2q$QDoH9b67ew>ULkA+l@*?Y7Q1vn!Y4+Qr8q<`4(&xU82~fhp;ToYRslD20q3 zA)1`NY3@Gl_qY2G4obK5N{2b`T=31czIpwbhmZj*tU$z5%tw;Nhy)XE zZTGkL9&PqIp~UYRM=0YeUPy$4tmBoRi7B`rLQd5*g&#FRST;MDsOJq!4BptqRN*4Pni$U5?ua*?y;K z!q|Q&j!i9&$AV2V4tyhy-%OGWO@LGNAd6FG?FoU+k^`QZ7I2I&!3Z%iH8_g`!vjrY zPfLLEjuZu#`qZq%agygkTQn=sSRZDIQe&=<1d~_5Sa}^u8wnbJi69hY00fsp*&6li ztT-*F;f0=kHRK~?8y|!%4E}(_>2I2h=(&TSox7Tx0}O!kG{B&wjB0OGS-5lmZN~P zD{Eza;ZX8s<2iZLeNN!6zJPA=oaCz%c1m=EIcHEpKa<>f0qq^i$n6_Coqh2@OfuGn zXtl9ZVTbyNiB41A0Dqu<5q<&O+oX8YBztWF5XT}jL6jC-$TXtuow&ET^>}@MvuC`K zNOR*tcab+yi9eO8=EfgpLcvx*#&oA4Z^N7X5(z>nY8Hw8Q#ceEG^G!{EZ0$pFt(rb zq!2m^mIBK7w$P0Stt*L|$$=6Xz|vwiYD%5~29B4Dh5 i04;W`&P&!<1lkr9-dmQ$57_7x0xN`a!nx|K-uxF0dlx4F literal 0 HcmV?d00001 diff --git a/addon_development/__pycache__/file_operators.cpython-37.pyc b/addon_development/__pycache__/file_operators.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..814a26ba4f07d318fd0d37b68c8da33cf673019b GIT binary patch literal 7859 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 O{cQb2y^7I7eer*8%(OQE literal 0 HcmV?d00001 diff --git a/addon_development/__pycache__/panels.cpython-37.pyc b/addon_development/__pycache__/panels.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac012ec8e31752ec917399bb47cdcf9efb980a10 GIT binary patch literal 4446 zcmb7HNpBm;74DU6uG;8*?BN8n(U~a8%mhdR!(-W`Yyk=-P}JDd(4^5~m1W9JHeKD6 zC8BlGB!>V720?DQ*vKXM7x@jzt**Jm=UjqZlJ8ZM)apZ;sK;7fRlTZu^?k4Imr8jJ zU;a;j|Mrgyn)VMWOg;;ldq~kAAehGVKy%Z7-PKVx0;6ZTrcQNcV6`=7F*~q(88_3* zy4jxNI=!5m>*d{iuizGXMYq^1xg}kDsId&oey6c4*LKUhGe@R7%S}Far2F$|b65^- zxoO)1>hi3Bx`L{!K>rfcH?&&uEiBWTHC+}Mf9el|m~}fbiu&!hI$COuEbj2wi+CJ& z`#X`mOr_3H2;Pr9pRus-4gC0-temdn``svxzv zwU#tCS2|<^eKt}C=>byoH;6>r)eenVKhh4(#Mp&h%y^|AT8Xu5CMGlK;9tQJP`2o3 z=?JMD+ecdT`#1wRh_gpH+UJJ&HAZmgKw0sXp=&R`N7-T}R%SD4uNQw!jMv5+?a*Sg ziRI-mYM#xpdA7hR(6Dr^zc|&nG}f@#Wp;&KO-E28USrqUjT7bf*v)iCreXJPHQVqL z8%Mg+m-*vEkyaB&gC;BH`|;?bPRO|D596>C_67lu`Qp=VpLxnd;8Ll7$b93QM!mJR zzTRj_t!7B8+X?#u7fKu5T84Qa44#3PHC+)m)B~#xJpjZ3GGI!wbmNVKM3KRs>%|8HF3o1+ z@hI2iFVmrRU=6;1UpR^w_R~?^`L)gV>PoZG^6E?NC2xIgqcO_0hW&Jq(XI0q@clt3 z##c{f8c){OTJ2G(p=zssRCT+9(FfTy+z0s-_WG3*jov|RN zm8J+^N^2m(fpq9f__(7Yix%OKD3XpH_}e^?*{I{=z8=W=iK2&FG4zmnHCtK{51z^_ zj*k{y8%tj| zya#LR-;8mnW+{Icv?x)9kY)0aW7dNL(NwxJbv4EaN;G$7sp{~50quycpz=|pte;;m z&CsD^rf(LBuB}?{@36~Qm6jN9bnuV>9<8=zzP0l3QCpQqw=ZzHQ-RZ7H|lP8gKm8A_P_X+Yo_8$MT9cLd)r|Y zjXpa+3edU_`i;5h`#~CWZ3Fdzz2XvVam-M8yzPh;~ zErJ;Ina(_V|KdK=uv6yhlL)jnFDr;sr%j&PSd49Mhx>}D(kpC)5m1MMT{{|J+huOr zMdptWLuUL=+}-0c(+PuNuP<`69bZC7GvWh5KrM4BY$7re1#12PqBfgi0>Oc(lU5#P zjg1lasFr9mJztwBI+B@aNM{F_3$`nAW&hvTIgB$ zA=7F0h-}h8ijGkOZ00KijbA#orttf~v z#1Bb)M&d^#7D@b=#7`h(p%eH~)Z_89kcls-T5&dQo}hV8*g{c{Q0)Cv_BZvuyZ{l5f@{#jQcQcB|O_q6OaKP~$jpyh=F)rut;2p?{ ziqlgrzr4ZY`s6x%J%yG32ZBa7+F`g8@alA&>hVAdSESQwJY3mmH(KH@%%>P%#X#Bd z!*lHN$GzI30libsKzJ!g%kbe30|D;tj*bpR3d7waU*8z!D{ zQ8ba|`@A!Z`5yEl+GY*jI=Xf7zATQFo3+9_Jws9i$9Ni?B8-!Hl*0jquXyZk!~&u1 zI<7DtHV1OX^Zb54j8$|SQNVaV41-Ctcmi`Sbe|R)dPot)R{1pa!(F8CRZv^)GB9j# zAeNAagCZWnQ->@NbgK}E4h|Mm9I=OS7lD?I>NIFM4*3;)5oo1ImA(Mz$rDLiMSd#Q tl-4#(PB)wURyu2w*8^E{k@?cOlSasQDF_^20m2SP|1?*5-!Uq8{|DxGu8{x$ literal 0 HcmV?d00001 diff --git a/addon_development/__pycache__/restart_blender.cpython-37.pyc b/addon_development/__pycache__/restart_blender.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fa357f104337411de5c05bcba7ed5fee22bbcad GIT binary patch literal 3322 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. -''' - -bl_info = { - "name": "Your Addon Name", - "description": "", - "author": "Your Name", - "version": (0, 0, 1), - "blender": BLENDER_VERSION, - "location": "View3D", - "warning": "This addon is still in development.", - "wiki_url": "", - "category": "Object" } - - -import bpy - - -# load and reload submodules -################################## - -import importlib -from . import developer_utils -importlib.reload(developer_utils) -modules = developer_utils.setup_addon_modules(__path__, __name__, "bpy" in locals()) - - - -# register -################################## - -import traceback - -def register(): - try: bpy.utils.register_module(__name__) - except: traceback.print_exc() - - print("Registered {} with {} modules".format(bl_info["name"], len(modules))) - -def unregister(): - try: bpy.utils.unregister_module(__name__) - except: traceback.print_exc() - - print("Unregistered {}".format(bl_info["name"])) +''' +Copyright (C) CURRENT_YEAR YOUR NAME +YOUR@MAIL.com + +Created by YOUR NAME + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +''' + +bl_info = { + "name": "Your Addon Name", + "description": "", + "author": "Your Name", + "version": (0, 0, 1), + "blender": BLENDER_VERSION, + "location": "View3D", + "warning": "This addon is still in development.", + "wiki_url": "", + "category": "Object" } + + +import bpy + + +# load and reload submodules +################################## + +import importlib +from . import developer_utils +importlib.reload(developer_utils) +modules = developer_utils.setup_addon_modules(__path__, __name__, "bpy" in locals()) + + + +# register +################################## + +import traceback + +def register(): + try: bpy.utils.register_module(__name__) + except: traceback.print_exc() + + print("Registered {} with {} modules".format(bl_info["name"], len(modules))) + +def unregister(): + try: bpy.utils.unregister_module(__name__) + except: traceback.print_exc() + + print("Unregistered {}".format(bl_info["name"])) diff --git a/addon_development/convert_indentation.py b/addon_development/convert_indentation.py index 1c7aae3..04e62a4 100644 --- a/addon_development/convert_indentation.py +++ b/addon_development/convert_indentation.py @@ -1,34 +1,34 @@ -import bpy -import os -from bpy.props import * -from . utils import current_addon_exists, get_current_addon_path - -class ConvertAddonIndentation(bpy.types.Operator): - bl_idname = "code_autocomplete.convert_addon_indentation" - bl_label = "Convert Addon Indentation" - bl_description = "" - bl_options = {"REGISTER"} - - old_indentation: StringProperty(default = "\t") - new_indentation: StringProperty(default = " ") - - @classmethod - def poll(cls, context): - return current_addon_exists() - - def execute(self, context): - paths = self.get_addon_files() - for path in paths: - bpy.ops.code_autocomplete.convert_file_indentation( - path = path, - old_indentation = self.old_indentation, - new_indentation = self.new_indentation) - return {"FINISHED"} - - def get_addon_files(self): - paths = [] - for root, dirs, files in os.walk(get_current_addon_path()): - for file in files: - if file.endswith(".py"): - paths.append(os.path.join(root, file)) - return paths +import bpy +import os +from bpy.props import * +from . utils import current_addon_exists, get_current_addon_path + +class ConvertAddonIndentation(bpy.types.Operator): + bl_idname = "code_autocomplete.convert_addon_indentation" + bl_label = "Convert Addon Indentation" + bl_description = "" + bl_options = {"REGISTER"} + + old_indentation: StringProperty(default = "\t") + new_indentation: StringProperty(default = " ") + + @classmethod + def poll(cls, context): + return current_addon_exists() + + def execute(self, context): + paths = self.get_addon_files() + for path in paths: + bpy.ops.code_autocomplete.convert_file_indentation( + path = path, + old_indentation = self.old_indentation, + new_indentation = self.new_indentation) + return {"FINISHED"} + + def get_addon_files(self): + paths = [] + for root, dirs, files in os.walk(get_current_addon_path()): + for file in files: + if file.endswith(".py"): + paths.append(os.path.join(root, file)) + return paths diff --git a/addon_development/export_addon.py b/addon_development/export_addon.py index 60ac4b9..8ed7e4c 100644 --- a/addon_development/export_addon.py +++ b/addon_development/export_addon.py @@ -1,44 +1,44 @@ -import bpy -import os -import zipfile -from bpy.props import * -from . utils import current_addon_exists, get_addon_name, get_current_addon_path - -class ExportAddon(bpy.types.Operator): - bl_idname = "code_autocomplete.export_addon" - bl_label = "Export Addon" - bl_description = "Save a .zip file of the addon" - bl_options = {"REGISTER"} - - filepath: StringProperty(subtype = "FILE_PATH") - - @classmethod - def poll(cls, context): - return current_addon_exists() - - def invoke(self, context, event): - context.window_manager.fileselect_add(self) - return {"RUNNING_MODAL"} - - def execute(self, context): - subdirectory_name = get_addon_name() + os.sep - source_path = get_current_addon_path() - output_path = self.filepath - if not output_path.lower().endswith(".zip"): - output_path += ".zip" - zip_directory(source_path, output_path, additional_path = subdirectory_name) - return {"FINISHED"} - - -def zip_directory(source_path, output_path, additional_path = ""): - try: - parent_folder = os.path.dirname(source_path) - content = os.walk(source_path) - zip_file = zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) - for root, folders, files in content: - for data in folders + files: - absolute_path = os.path.join(root, data) - relative_path = additional_path + absolute_path[len(parent_folder+os.sep):] - zip_file.write(absolute_path, relative_path) - zip_file.close() - except: print("Could not zip the directory") +import bpy +import os +import zipfile +from bpy.props import * +from . utils import current_addon_exists, get_addon_name, get_current_addon_path + +class ExportAddon(bpy.types.Operator): + bl_idname = "code_autocomplete.export_addon" + bl_label = "Export Addon" + bl_description = "Save a .zip file of the addon" + bl_options = {"REGISTER"} + + filepath: StringProperty(subtype = "FILE_PATH") + + @classmethod + def poll(cls, context): + return current_addon_exists() + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL"} + + def execute(self, context): + subdirectory_name = get_addon_name() + os.sep + source_path = get_current_addon_path() + output_path = self.filepath + if not output_path.lower().endswith(".zip"): + output_path += ".zip" + zip_directory(source_path, output_path, additional_path = subdirectory_name) + return {"FINISHED"} + + +def zip_directory(source_path, output_path, additional_path = ""): + try: + parent_folder = os.path.dirname(source_path) + content = os.walk(source_path) + zip_file = zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) + for root, folders, files in content: + for data in folders + files: + absolute_path = os.path.join(root, data) + relative_path = additional_path + absolute_path[len(parent_folder+os.sep):] + zip_file.write(absolute_path, relative_path) + zip_file.close() + except: print("Could not zip the directory") diff --git a/addon_development/file_operators.py b/addon_development/file_operators.py index d3b3d61..a857aec 100644 --- a/addon_development/file_operators.py +++ b/addon_development/file_operators.py @@ -1,207 +1,207 @@ -import bpy -import os -from bpy.props import * -from . utils import (get_directory_names, - current_addon_exists, - is_addon_name_valid, - get_current_addon_path, - get_addon_name, - correct_file_name, - get_settings, - addons_path, - new_file, - new_directory) - -class NewFile(bpy.types.Operator): - bl_idname = "code_autocomplete.new_file" - bl_label = "New File" - bl_description = "Create a new file in this directory" - bl_options = {"REGISTER"} - - directory: StringProperty(name = "Directory", default = "") - name: StringProperty(name = "File Name", default = "") - content: StringProperty(name = "Content", default = "") - - @classmethod - def poll(cls, context): - return current_addon_exists() - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width = 400) - - def draw(self, context): - layout = self.layout - layout.prop(self, "name") - - def execute(self, context): - if self.name != "": - path = self.directory + self.name - new_file(self.directory + self.name, self.content) - bpy.ops.code_autocomplete.open_file(path = path) - context.area.tag_redraw() - return {"FINISHED"} - - -class NewDirectory(bpy.types.Operator): - bl_idname = "code_autocomplete.new_directory" - bl_label = "New Directory" - bl_description = "Create a new subdirectory" - bl_options = {"REGISTER"} - - directory: StringProperty(name = "Directory", default = "") - name: StringProperty(name = "Directory Name", default = "") - - @classmethod - def poll(cls, context): - return current_addon_exists() - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width = 400) - - def draw(self, context): - layout = self.layout - layout.prop(self, "name") - - def execute(self, context): - if self.name != "": - new_directory(self.directory + self.name) - new_file(os.path.join(self.directory + self.name, "__init__.py")) - context.area.tag_redraw() - return {"FINISHED"} - - -class FileMenuOpener(bpy.types.Operator): - bl_idname = "code_autocomplete.open_file_menu" - bl_label = "Open File Menu" - - path: StringProperty(name = "Path", default = "") - - def invoke(self, context, event): - context.window_manager.popup_menu(self.drawMenu, title = "{} - File Menu".format(os.path.basename(self.path))) - return {"FINISHED"} - - def drawMenu(fileProps, self, context): - layout = self.layout - layout.operator_context = "INVOKE_DEFAULT" - - props = layout.operator("code_autocomplete.rename_file", text = "Rename") - props.path = fileProps.path - - props = layout.operator("code_autocomplete.open_file", text = "Open in Text Editor") - props.path = fileProps.path - - props = layout.operator("code_autocomplete.open_external_file_browser", text = "Open External") - props.directory = os.path.dirname(fileProps.path) - - layout.separator() - - props = layout.operator("code_autocomplete.delete_file", text = "Delete", icon = "ERROR") - props.path = fileProps.path - - -class OpenFile(bpy.types.Operator): - bl_idname = "code_autocomplete.open_file" - bl_label = "Open File" - bl_description = "Load the file into the text editor" - bl_options = {"REGISTER"} - - path: StringProperty(name = "Path", default = "") - - def execute(self, context): - text = None - for text_block in bpy.data.texts: - if text_block.filepath == self.path: - text = text_block - break - if not text: - text = bpy.data.texts.load(self.path, internal = False) - - context.space_data.text = text - return {"FINISHED"} - - -class OpenExternalFileBrowser(bpy.types.Operator): - bl_idname = "code_autocomplete.open_external_file_browser" - bl_label = "Open External File Browser" - bl_description = "" - bl_options = {"REGISTER"} - - directory: StringProperty(name = "Directory", default = "") - - def execute(self, context): - bpy.ops.wm.path_open(filepath = self.directory) - return {"FINISHED"} - - -class RenameFile(bpy.types.Operator): - bl_idname = "code_autocomplete.rename_file" - bl_label = "Open External File Browser" - bl_description = "" - bl_options = {"REGISTER"} - - path: StringProperty(name = "Directory", default = "") - new_name: StringProperty(name = "Directory", description = "New file name", default = "") - - def invoke(self, context, event): - self.new_name = os.path.basename(self.path) - return context.window_manager.invoke_props_dialog(self, width = 400) - - def draw(self, context): - layout = self.layout - layout.prop(self, "new_name") - - def execute(self, context): - new_path = os.path.join(os.path.dirname(self.path), self.new_name) - os.rename(self.path, new_path) - self.correct_text_block_paths(self.path, new_path) - context.area.tag_redraw() - return {"FINISHED"} - - def correct_text_block_paths(self, old_path, new_path): - for text in bpy.data.texts: - if text.filepath == old_path: - text.filepath = new_path - - -class DeleteFile(bpy.types.Operator): - bl_idname = "code_autocomplete.delete_file" - bl_label = "Delete File" - bl_description = "Delete file on the hard drive" - bl_options = {"REGISTER"} - - path: StringProperty(name = "Directory", default = "") - - def invoke(self, context, event): - return context.window_manager.invoke_confirm(self, event) - - def execute(self, context): - os.remove(self.path) - context.area.tag_redraw() - return {"FINISHED"} - - -class SaveFiles(bpy.types.Operator): - bl_idname = "code_autocomplete.save_files" - bl_label = "Save All Files" - bl_description = "Save all files which correspond to a file on the hard drive" - bl_options = {"REGISTER"} - - @classmethod - def poll(cls, context): - return True - - def execute(self, context): - for text in bpy.data.texts: - save_text_block(text) - try: bpy.ops.text.resolve_conflict(resolution = "IGNORE") - except: pass - return {"FINISHED"} - - -def save_text_block(text_block): - if not text_block: return - if not os.path.exists(text_block.filepath): return - - file = open(text_block.filepath, mode = "w") - file.write(text_block.as_string()) - file.close() +import bpy +import os +from bpy.props import * +from . utils import (get_directory_names, + current_addon_exists, + is_addon_name_valid, + get_current_addon_path, + get_addon_name, + correct_file_name, + get_settings, + addons_path, + new_file, + new_directory) + +class NewFile(bpy.types.Operator): + bl_idname = "code_autocomplete.new_file" + bl_label = "New File" + bl_description = "Create a new file in this directory" + bl_options = {"REGISTER"} + + directory: StringProperty(name = "Directory", default = "") + name: StringProperty(name = "File Name", default = "") + content: StringProperty(name = "Content", default = "") + + @classmethod + def poll(cls, context): + return current_addon_exists() + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width = 400) + + def draw(self, context): + layout = self.layout + layout.prop(self, "name") + + def execute(self, context): + if self.name != "": + path = self.directory + self.name + new_file(self.directory + self.name, self.content) + bpy.ops.code_autocomplete.open_file(path = path) + context.area.tag_redraw() + return {"FINISHED"} + + +class NewDirectory(bpy.types.Operator): + bl_idname = "code_autocomplete.new_directory" + bl_label = "New Directory" + bl_description = "Create a new subdirectory" + bl_options = {"REGISTER"} + + directory: StringProperty(name = "Directory", default = "") + name: StringProperty(name = "Directory Name", default = "") + + @classmethod + def poll(cls, context): + return current_addon_exists() + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width = 400) + + def draw(self, context): + layout = self.layout + layout.prop(self, "name") + + def execute(self, context): + if self.name != "": + new_directory(self.directory + self.name) + new_file(os.path.join(self.directory + self.name, "__init__.py")) + context.area.tag_redraw() + return {"FINISHED"} + + +class FileMenuOpener(bpy.types.Operator): + bl_idname = "code_autocomplete.open_file_menu" + bl_label = "Open File Menu" + + path: StringProperty(name = "Path", default = "") + + def invoke(self, context, event): + context.window_manager.popup_menu(self.drawMenu, title = "{} - File Menu".format(os.path.basename(self.path))) + return {"FINISHED"} + + def drawMenu(fileProps, self, context): + layout = self.layout + layout.operator_context = "INVOKE_DEFAULT" + + props = layout.operator("code_autocomplete.rename_file", text = "Rename") + props.path = fileProps.path + + props = layout.operator("code_autocomplete.open_file", text = "Open in Text Editor") + props.path = fileProps.path + + props = layout.operator("code_autocomplete.open_external_file_browser", text = "Open External") + props.directory = os.path.dirname(fileProps.path) + + layout.separator() + + props = layout.operator("code_autocomplete.delete_file", text = "Delete", icon = "ERROR") + props.path = fileProps.path + + +class OpenFile(bpy.types.Operator): + bl_idname = "code_autocomplete.open_file" + bl_label = "Open File" + bl_description = "Load the file into the text editor" + bl_options = {"REGISTER"} + + path: StringProperty(name = "Path", default = "") + + def execute(self, context): + text = None + for text_block in bpy.data.texts: + if text_block.filepath == self.path: + text = text_block + break + if not text: + text = bpy.data.texts.load(self.path, internal = False) + + context.space_data.text = text + return {"FINISHED"} + + +class OpenExternalFileBrowser(bpy.types.Operator): + bl_idname = "code_autocomplete.open_external_file_browser" + bl_label = "Open External File Browser" + bl_description = "" + bl_options = {"REGISTER"} + + directory: StringProperty(name = "Directory", default = "") + + def execute(self, context): + bpy.ops.wm.path_open(filepath = self.directory) + return {"FINISHED"} + + +class RenameFile(bpy.types.Operator): + bl_idname = "code_autocomplete.rename_file" + bl_label = "Open External File Browser" + bl_description = "" + bl_options = {"REGISTER"} + + path: StringProperty(name = "Directory", default = "") + new_name: StringProperty(name = "Directory", description = "New file name", default = "") + + def invoke(self, context, event): + self.new_name = os.path.basename(self.path) + return context.window_manager.invoke_props_dialog(self, width = 400) + + def draw(self, context): + layout = self.layout + layout.prop(self, "new_name") + + def execute(self, context): + new_path = os.path.join(os.path.dirname(self.path), self.new_name) + os.rename(self.path, new_path) + self.correct_text_block_paths(self.path, new_path) + context.area.tag_redraw() + return {"FINISHED"} + + def correct_text_block_paths(self, old_path, new_path): + for text in bpy.data.texts: + if text.filepath == old_path: + text.filepath = new_path + + +class DeleteFile(bpy.types.Operator): + bl_idname = "code_autocomplete.delete_file" + bl_label = "Delete File" + bl_description = "Delete file on the hard drive" + bl_options = {"REGISTER"} + + path: StringProperty(name = "Directory", default = "") + + def invoke(self, context, event): + return context.window_manager.invoke_confirm(self, event) + + def execute(self, context): + os.remove(self.path) + context.area.tag_redraw() + return {"FINISHED"} + + +class SaveFiles(bpy.types.Operator): + bl_idname = "code_autocomplete.save_files" + bl_label = "Save All Files" + bl_description = "Save all files which correspond to a file on the hard drive" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + for text in bpy.data.texts: + save_text_block(text) + try: bpy.ops.text.resolve_conflict(resolution = "IGNORE") + except: pass + return {"FINISHED"} + + +def save_text_block(text_block): + if not text_block: return + if not os.path.exists(text_block.filepath): return + + file = open(text_block.filepath, mode = "w") + file.write(text_block.as_string()) + file.close() diff --git a/addon_development/panels.py b/addon_development/panels.py index d77bfab..da93bd3 100644 --- a/addon_development/panels.py +++ b/addon_development/panels.py @@ -1,121 +1,121 @@ -import bpy -import os -from collections import defaultdict -from bpy.props import * -from . utils import (get_settings, - get_current_addon_path, - current_addon_exists, - is_addon_name_valid, - get_directory_names, - get_file_names, - get_current_filepath, - get_addon_name) - -class AddonDeveloperPanel(bpy.types.Panel): - bl_idname = "addon_developer_panel" - bl_label = "Addon Development" - bl_space_type = "TEXT_EDITOR" - bl_region_type = "UI" - - def draw(self, context): - layout = self.layout - setting = get_settings() - row = layout.row(align = True) - row.prop(setting, "addon_name", text = "Name") - row.operator("code_autocomplete.find_existing_addon", icon = "EYEDROPPER", text = "") - - if not current_addon_exists(): - if not is_addon_name_valid(): - if get_addon_name() == "": - layout.label("Insert the name of your addon", icon = "INFO") - else: - layout.operator("code_autocomplete.make_addon_name_valid", icon = "ERROR", text = "Correct Addon Name") - else: - row = layout.row() - row.scale_y = 1.2 - row.operator_menu_enum("code_autocomplete.new_addon", "new_addon_type", icon = "NEW", text = "New Addon") - else: - row = layout.row() - row.scale_y = 1.5 - row.operator("code_autocomplete.run_addon", icon = "OUTLINER_DATA_POSE", text = "Run Addon") - layout.operator("code_autocomplete.export_addon", icon = "EXPORT", text = "Export as Zip") - layout.operator("code_autocomplete.restart_blender", icon = "BLENDER") - - -directory_visibility = defaultdict(bool) - -class AddonFilesPanel(bpy.types.Panel): - bl_idname = "addon_files_panel" - bl_label = "Addon Files" - bl_space_type = "TEXT_EDITOR" - bl_region_type = "UI" - - @classmethod - def poll(cls, context): - return current_addon_exists() - - def draw(self, context): - layout = self.layout - addon_path = get_current_addon_path() - - layout.operator("code_autocomplete.save_files", icon = "SAVE_COPY") - self.draw_directory(layout, addon_path) - - def draw_directory(self, layout, directory): - is_visible = self.is_directory_visible(directory) - box = layout.box() - - row = box.row(align = True) - - icon = "DOWNARROW_HLT" if is_visible else "RIGHTARROW" - props = row.operator("code_autocomplete.set_directory_visibility", text = os.path.split(directory[:-1])[-1], icon = icon, emboss = False) - props.directory = directory - props.visibility = not is_visible - - subrow = row.row(align = True) - subrow.active = False - props = subrow.operator("code_autocomplete.open_external_file_browser", text = "", icon = "FILESEL", emboss = False) - props.directory = directory - - if is_visible: - col = box.column(align = True) - directory_names = get_directory_names(directory) - for directory_name in directory_names: - row = col.row() - self.draw_directory(row, directory + directory_name + os.sep) - - file_names = get_file_names(directory) - col = box.column(align = True) - for file_name in file_names: - row = col.row() - row.alignment = "LEFT" - full_path = directory + file_name - props = row.operator("code_autocomplete.open_file_menu", icon = "COLLAPSEMENU", text = "", emboss = True) - props.path = full_path - if full_path == get_current_filepath(): - row.label("", icon = "RIGHTARROW_THIN") - operator = row.operator("code_autocomplete.open_file", text = file_name, emboss = False) - operator.path = full_path - - row = box.row(align = True) - operator = row.operator("code_autocomplete.new_file", icon = "PLUS", text = "File") - operator.directory = directory - operator = row.operator("code_autocomplete.new_directory", icon = "PLUS", text = "Directory") - operator.directory = directory - - def is_directory_visible(self, directory): - return directory_visibility[directory] - -class SetDirectoryVisibility(bpy.types.Operator): - bl_idname = "code_autocomplete.set_directory_visibility" - bl_label = "Toogle Directory Visibility" - bl_description = "" - bl_options = {"REGISTER"} - - directory: StringProperty(name = "Directory", default = "") - visibility: BoolProperty(name = "Visibility", default = True) - - def execute(self, context): - global directory_visibility - directory_visibility[self.directory] = self.visibility - return {"FINISHED"} +import bpy +import os +from collections import defaultdict +from bpy.props import * +from . utils import (get_settings, + get_current_addon_path, + current_addon_exists, + is_addon_name_valid, + get_directory_names, + get_file_names, + get_current_filepath, + get_addon_name) + +class AddonDeveloperPanel(bpy.types.Panel): + bl_idname = "addon_developer_panel" + bl_label = "Addon Development" + bl_space_type = "TEXT_EDITOR" + bl_region_type = "UI" + + def draw(self, context): + layout = self.layout + setting = get_settings() + row = layout.row(align = True) + row.prop(setting, "addon_name", text = "Name") + row.operator("code_autocomplete.find_existing_addon", icon = "EYEDROPPER", text = "") + + if not current_addon_exists(): + if not is_addon_name_valid(): + if get_addon_name() == "": + layout.label("Insert the name of your addon", icon = "INFO") + else: + layout.operator("code_autocomplete.make_addon_name_valid", icon = "ERROR", text = "Correct Addon Name") + else: + row = layout.row() + row.scale_y = 1.2 + row.operator_menu_enum("code_autocomplete.new_addon", "new_addon_type", icon = "NEW", text = "New Addon") + else: + row = layout.row() + row.scale_y = 1.5 + row.operator("code_autocomplete.run_addon", icon = "OUTLINER_DATA_POSE", text = "Run Addon") + layout.operator("code_autocomplete.export_addon", icon = "EXPORT", text = "Export as Zip") + layout.operator("code_autocomplete.restart_blender", icon = "BLENDER") + + +directory_visibility = defaultdict(bool) + +class AddonFilesPanel(bpy.types.Panel): + bl_idname = "addon_files_panel" + bl_label = "Addon Files" + bl_space_type = "TEXT_EDITOR" + bl_region_type = "UI" + + @classmethod + def poll(cls, context): + return current_addon_exists() + + def draw(self, context): + layout = self.layout + addon_path = get_current_addon_path() + + layout.operator("code_autocomplete.save_files", icon = "SAVE_COPY") + self.draw_directory(layout, addon_path) + + def draw_directory(self, layout, directory): + is_visible = self.is_directory_visible(directory) + box = layout.box() + + row = box.row(align = True) + + icon = "DOWNARROW_HLT" if is_visible else "RIGHTARROW" + props = row.operator("code_autocomplete.set_directory_visibility", text = os.path.split(directory[:-1])[-1], icon = icon, emboss = False) + props.directory = directory + props.visibility = not is_visible + + subrow = row.row(align = True) + subrow.active = False + props = subrow.operator("code_autocomplete.open_external_file_browser", text = "", icon = "FILESEL", emboss = False) + props.directory = directory + + if is_visible: + col = box.column(align = True) + directory_names = get_directory_names(directory) + for directory_name in directory_names: + row = col.row() + self.draw_directory(row, directory + directory_name + os.sep) + + file_names = get_file_names(directory) + col = box.column(align = True) + for file_name in file_names: + row = col.row() + row.alignment = "LEFT" + full_path = directory + file_name + props = row.operator("code_autocomplete.open_file_menu", icon = "COLLAPSEMENU", text = "", emboss = True) + props.path = full_path + if full_path == get_current_filepath(): + row.label("", icon = "RIGHTARROW_THIN") + operator = row.operator("code_autocomplete.open_file", text = file_name, emboss = False) + operator.path = full_path + + row = box.row(align = True) + operator = row.operator("code_autocomplete.new_file", icon = "PLUS", text = "File") + operator.directory = directory + operator = row.operator("code_autocomplete.new_directory", icon = "PLUS", text = "Directory") + operator.directory = directory + + def is_directory_visible(self, directory): + return directory_visibility[directory] + +class SetDirectoryVisibility(bpy.types.Operator): + bl_idname = "code_autocomplete.set_directory_visibility" + bl_label = "Toogle Directory Visibility" + bl_description = "" + bl_options = {"REGISTER"} + + directory: StringProperty(name = "Directory", default = "") + visibility: BoolProperty(name = "Visibility", default = True) + + def execute(self, context): + global directory_visibility + directory_visibility[self.directory] = self.visibility + return {"FINISHED"} diff --git a/addon_development/restart_blender.py b/addon_development/restart_blender.py index 7b76b74..1a0b6c7 100644 --- a/addon_development/restart_blender.py +++ b/addon_development/restart_blender.py @@ -1,92 +1,92 @@ -import os -import bpy -import sys -from bpy.app.handlers import persistent -from . utils import get_addon_name, get_settings -from . panels import directory_visibility - -class RestartBlender(bpy.types.Operator): - bl_idname = "code_autocomplete.restart_blender" - bl_label = "Restart Blender" - bl_description = "Close and open a new Blender instance to test the Addon on the startup file. (Currently only supported for windows)" - bl_options = {"REGISTER"} - - @classmethod - def poll(cls, context): - return sys.platform == "win32" - - def invoke(self, context, event): - return context.window_manager.invoke_confirm(self, event) - - def execute(self, context): - bpy.ops.code_autocomplete.save_files() - save_status() - start_another_blender_instance() - bpy.ops.wm.quit_blender() - return {"FINISHED"} - - -# Save current settings to reload them in the new instance -########################################################## - -restart_data_path = os.path.join(os.path.dirname(__file__), "restart_data.txt") - -id_addon_name = "ADDON_NAME: " -id_current_path = "CURRENT_PATH: " -id_visiblie_path = "VISIBLE_PATH: " - -def save_status(): - file = open(restart_data_path, "w") - file.write(id_addon_name + get_addon_name() + "\n") - text_block = bpy.context.space_data.text - if text_block: - file.write(id_current_path + text_block.filepath + "\n") - for path, is_open in directory_visibility.items(): - if is_open: - file.write(id_visiblie_path + path + "\n") - - file.close() - -@persistent -def open_status(scene): - if os.path.exists(restart_data_path): - file = open(restart_data_path) - lines = file.readlines() - file.close() - os.remove(restart_data_path) - parse_startup_file_lines(lines) - -bpy.app.handlers.load_post.append(open_status) - -def parse_startup_file_lines(lines): - for line in lines: - if line.startswith(id_addon_name): - get_settings().addon_name = line[len(id_addon_name):].strip() - if line.startswith(id_current_path): - path = line[len(id_current_path):].strip() - if os.path.exists(path): - text_block = bpy.data.texts.load(path, internal = False) - for screen in bpy.data.screens: - for area in screen.areas: - for space in area.spaces: - if space.type == "TEXT_EDITOR": - space.text = text_block - if line.startswith(id_visiblie_path): - path = line[len(id_visiblie_path):].strip() - bpy.ops.code_autocomplete.set_directory_visibility(directory = path, visibility = True) - - - -# Restart Blender -########################## - -def start_another_blender_instance(): - open_file(bpy.app.binary_path) - -# only works for windows currently -def open_file(path): - if sys.platform == "win32": - os.startfile(path) - else: - opener = "open" if sys.platform == "darwin" else "xdg-open" - subprocess.call([opener, path]) +import os +import bpy +import sys +from bpy.app.handlers import persistent +from . utils import get_addon_name, get_settings +from . panels import directory_visibility + +class RestartBlender(bpy.types.Operator): + bl_idname = "code_autocomplete.restart_blender" + bl_label = "Restart Blender" + bl_description = "Close and open a new Blender instance to test the Addon on the startup file. (Currently only supported for windows)" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return sys.platform == "win32" + + def invoke(self, context, event): + return context.window_manager.invoke_confirm(self, event) + + def execute(self, context): + bpy.ops.code_autocomplete.save_files() + save_status() + start_another_blender_instance() + bpy.ops.wm.quit_blender() + return {"FINISHED"} + + +# Save current settings to reload them in the new instance +########################################################## + +restart_data_path = os.path.join(os.path.dirname(__file__), "restart_data.txt") + +id_addon_name = "ADDON_NAME: " +id_current_path = "CURRENT_PATH: " +id_visiblie_path = "VISIBLE_PATH: " + +def save_status(): + file = open(restart_data_path, "w") + file.write(id_addon_name + get_addon_name() + "\n") + text_block = bpy.context.space_data.text + if text_block: + file.write(id_current_path + text_block.filepath + "\n") + for path, is_open in directory_visibility.items(): + if is_open: + file.write(id_visiblie_path + path + "\n") + + file.close() + +@persistent +def open_status(scene): + if os.path.exists(restart_data_path): + file = open(restart_data_path) + lines = file.readlines() + file.close() + os.remove(restart_data_path) + parse_startup_file_lines(lines) + +bpy.app.handlers.load_post.append(open_status) + +def parse_startup_file_lines(lines): + for line in lines: + if line.startswith(id_addon_name): + get_settings().addon_name = line[len(id_addon_name):].strip() + if line.startswith(id_current_path): + path = line[len(id_current_path):].strip() + if os.path.exists(path): + text_block = bpy.data.texts.load(path, internal = False) + for screen in bpy.data.screens: + for area in screen.areas: + for space in area.spaces: + if space.type == "TEXT_EDITOR": + space.text = text_block + if line.startswith(id_visiblie_path): + path = line[len(id_visiblie_path):].strip() + bpy.ops.code_autocomplete.set_directory_visibility(directory = path, visibility = True) + + + +# Restart Blender +########################## + +def start_another_blender_instance(): + open_file(bpy.app.binary_path) + +# only works for windows currently +def open_file(path): + if sys.platform == "win32": + os.startfile(path) + else: + opener = "open" if sys.platform == "darwin" else "xdg-open" + subprocess.call([opener, path]) diff --git a/addon_development/run_addon.py b/addon_development/run_addon.py index ce581a2..d199cfd 100644 --- a/addon_development/run_addon.py +++ b/addon_development/run_addon.py @@ -1,26 +1,26 @@ -import bpy -import addon_utils -import importlib -import sys -from . utils import current_addon_exists, get_addon_name - -class RunAddon(bpy.types.Operator): - bl_idname = "code_autocomplete.run_addon" - bl_label = "Run Addon" - bl_description = "Unregister, reload and register it again." - bl_options = {"REGISTER"} - - @classmethod - def poll(cls, context): - return current_addon_exists() - - def execute(self, context): - bpy.ops.code_autocomplete.save_files() - - addon_name = get_addon_name() - module = sys.modules.get(addon_name) - if module: - addon_utils.disable(addon_name) - importlib.reload(module) - addon_utils.enable(addon_name) - return {"FINISHED"} +import bpy +import addon_utils +import importlib +import sys +from . utils import current_addon_exists, get_addon_name + +class RunAddon(bpy.types.Operator): + bl_idname = "code_autocomplete.run_addon" + bl_label = "Run Addon" + bl_description = "Unregister, reload and register it again." + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return current_addon_exists() + + def execute(self, context): + bpy.ops.code_autocomplete.save_files() + + addon_name = get_addon_name() + module = sys.modules.get(addon_name) + if module: + addon_utils.disable(addon_name) + importlib.reload(module) + addon_utils.enable(addon_name) + return {"FINISHED"} diff --git a/addon_development/utils.py b/addon_development/utils.py index 53f308b..6b139e7 100644 --- a/addon_development/utils.py +++ b/addon_development/utils.py @@ -1,67 +1,67 @@ -import bpy -import os - -addons_path = bpy.utils.user_resource("SCRIPTS", "addons") - -def get_current_filepath(): - try: return bpy.context.space_data.text.filepath - except: return "" - -def current_addon_exists(): - return os.path.exists(get_current_addon_path()) and get_settings().addon_name != "" - -def get_current_addon_path(): - return os.path.join(addons_path, get_addon_name()) + os.sep - -def is_addon_name_valid(): - name = get_addon_name() - return name == correct_file_name(name, is_directory = True) and name != "" - -def get_addon_name(): - return get_settings().addon_name - -def get_settings(): - return bpy.context.scene.addon_development - - -def new_addon_file(path, default = ""): - new_file(get_current_addon_path() + path, default) - -def new_file(path, default = ""): - dirname = os.path.dirname(path) - new_directory(dirname) - if not os.path.exists(path): - file = open(path, "a") - file.write(default) - file.close() - -def new_directory(path): - if not os.path.exists(path): - os.makedirs(path) - - -def correct_file_name(name, is_directory = False): - new_name = "" - for char in name: - if char.isupper(): - new_name += char.lower() - elif char.islower() or char == "_": - new_name += char - elif char == " ": - new_name += "_" - elif char.isdigit() and len(new_name) > 0: - new_name += char - elif not is_directory and char == "." and new_name.count(".") == 0: - new_name += char - return new_name - - - -def get_directory_names(directory): - return [name for path, name in get_directory_content(directory) if not os.path.isfile(path)] -def get_file_names(directory): - return [name for path, name in get_directory_content(directory) if os.path.isfile(path)] - -ignore_names = ["__pycache__", ".git"] -def get_directory_content(directory): - return [(os.path.join(directory, name), name) for name in os.listdir(directory) if name not in ignore_names] +import bpy +import os + +addons_path = bpy.utils.user_resource("SCRIPTS", "addons") + +def get_current_filepath(): + try: return bpy.context.space_data.text.filepath + except: return "" + +def current_addon_exists(): + return os.path.exists(get_current_addon_path()) and get_settings().addon_name != "" + +def get_current_addon_path(): + return os.path.join(addons_path, get_addon_name()) + os.sep + +def is_addon_name_valid(): + name = get_addon_name() + return name == correct_file_name(name, is_directory = True) and name != "" + +def get_addon_name(): + return get_settings().addon_name + +def get_settings(): + return bpy.context.scene.addon_development + + +def new_addon_file(path, default = ""): + new_file(get_current_addon_path() + path, default) + +def new_file(path, default = ""): + dirname = os.path.dirname(path) + new_directory(dirname) + if not os.path.exists(path): + file = open(path, "a") + file.write(default) + file.close() + +def new_directory(path): + if not os.path.exists(path): + os.makedirs(path) + + +def correct_file_name(name, is_directory = False): + new_name = "" + for char in name: + if char.isupper(): + new_name += char.lower() + elif char.islower() or char == "_": + new_name += char + elif char == " ": + new_name += "_" + elif char.isdigit() and len(new_name) > 0: + new_name += char + elif not is_directory and char == "." and new_name.count(".") == 0: + new_name += char + return new_name + + + +def get_directory_names(directory): + return [name for path, name in get_directory_content(directory) if not os.path.isfile(path)] +def get_file_names(directory): + return [name for path, name in get_directory_content(directory) if os.path.isfile(path)] + +ignore_names = ["__pycache__", ".git"] +def get_directory_content(directory): + return [(os.path.join(directory, name), name) for name in os.listdir(directory) if name not in ignore_names] diff --git a/autocompletion/__pycache__/__init__.cpython-37.pyc b/autocompletion/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32167abc40e0c8da9d1d8fae693a24510076c570 GIT binary patch literal 208 zcmZ?b<>g`k0>K!yVl7qb9~6oz01O-8?!3`HPe1o5lV*(xTqIJKxa zrW8oVI2IJRB$gz`1m!2@X6B{GIOU|~rKA=qxaF7Tr6iVQ=I6nLVvO`GjADwDi!uvJ zienN}Qu6bPW0LbzQsWa#OY)QRa|?1(OHyNyI3UgO@tJv9r=%G@nezeib;Eo*3po zeEnORvA^hL_;?sRMl*NONhW!Nm6;UMYw)Hhi>6ohIQxc4U#5nUi_gK+eiburu|aE2%rd`;UYx`J={d&~E2>W5e+dp5nN;@!QZz^!jo zqxJ{WIehf-$ES}fFOAZs(nVic?{qezB&uw+qh{RNtvqQcVrBVR8|*|$+-?o0D)(0& z-mjRdjys8|L{hd}rc!N76-M2pU2Qiz4V9?MnHTyva_)u+{B@+SboOl!hH)!S!jK#` z#5Uu->*tAC8SbgNb!;I^%`tQjgFZI_G1BLCVu>s<#bJpp0qc!eo|54ZsqjYVr9QIFq4tXayx&ol+1&Q# ztL<=eM8%uiwXGp3f33U48@I*&#~V8giLVqNFfAw z1o;785@=!oRWB3lLw*2d9E0u?&RJ_7V-MkgN-*cq+$sHb=K1Ec?eAW^EN$A<_gkB# z64XP{)O5a`N2C*EiwSGG-5fZxhy`qNVoJG6<9sUThkj2JSi0Lmk}3MuxDv?oa5FCg z-dmV(bg*;e>;IY!Q=_p2kT(hFg*>=90res~7gR-Dnn&n z6TgbLJ`UiT3M~})35>l;BBdaOGOpE>R;{XncE!q9jt=am2#u@5$Lwk&HVGBJwE|XL zAxpE~!wEK~YJX+5(XK`fvxdR&ka+G3#zrntg{L5w=x9paKCT4kw%ts#>X*O%ZLz z(PTYbBg3Ma8#8H!a2<&xAx*0P63tLfF@A^lZvPvMqZBw-U0>k{N$D*p>FFY<^BYjo z*Ql?jb4dNVn3zNA*S(23T)TCDJm;?5f&VG%MLTBdHFCBXXUA?2V&bCXeEtkD@1YHo zh+jMX^=?h^eL{`JLXPgvR@78sXtQD1Y|Czg#`!RO+l?B76+MeT&{Xm@RZmw&^`|tV z6n3<0!f^)M7$9BA6c00p4)>fFWP)_y2LY_FX|<5jpTm@MuE>SmByN~-ZFF0voeWJ5 N;I5IfUCdF?#Xr>oGcW)E literal 0 HcmV?d00001 diff --git a/autocompletion/__pycache__/autocomplete_handler.cpython-37.pyc b/autocompletion/__pycache__/autocomplete_handler.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74df13173caae9ec20f7da4c122a7a6d275f3be9 GIT binary patch literal 8407 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 O@E6#Dm-{2@>;Db>+N7ia literal 0 HcmV?d00001 diff --git a/autocompletion/__pycache__/event_utils.cpython-37.pyc b/autocompletion/__pycache__/event_utils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0480f6324490233e38f06f7f822e379f89b0e5c GIT binary patch literal 1765 zcma)7&2Jk;6rY*>@OtZ*G);ku2nAG-%%O*#DufV8nkAKjL9t6kwAE_anZ|3cciowB zaHQ=i5&sA${vK{~<%Gmvz=`)}H&J47V6B;XAM^2>-+OPozp)V#_`=`*c>T9W$e*ZO ze=Z2$KvQGrIN`J)r#P~L&eCdaB5{VRNc~vH>HT?p1_r$p<_3uRbF#n(W;LG@>5>mymOvZbQ#q(6B@ky1= z^YSd-FGR_O+>NSb$x{t`UYp{lPo6!ERVMR=R&mOCRjN3vxJc5auCi*rD1;XAEd>|b zvy-LH3-x4iVIG|co!mjZs8o)}Am0jaM5qj)H{K5?hoe!HA!__^M875FR}g>>2+Pcu zea_xFR&zkx0nN3V3)&Z;d9}m68I#YzN@1pJwFoqv=~->ZCy zx3Qz>>Dp$=_(>W?B};NegYf^R!rsfiEFniM)^9L+rM&dw|_Gc1Tv! zbwDyft`Zvw)YPZY5gHQrrUk^OZDS#Ae)4^ob%%XiY}v;RTBbU{J{b`W$!RUD27YVr z(r=QKH4t&D7PIm2X6pZ--n>SgmAjQk~M&ly&CHGZSNJlB@IQMaeju%=wwx?#Y!6=N=%5-c&QYrGFvw!ROdn_XdEz$ z)wrmvhk6?;3>^s+Ff6cP-vLAltBTM~pe~>iLzvl{fZ#y8L64r*PEDu8B4;+V43G!- zYP}5rIa2Bj#}Sa)X&xSBw(sI>%zi;TubebTqjQ2~< z^~6vJNMpJ%{zT+w6Wy{dKZEnv@9-FG7moRce{0_P12^YznWKkS%|OkFT?&-1hmHjC+vrA-~?Xa{R<3l BehdHr literal 0 HcmV?d00001 diff --git a/autocompletion/__pycache__/exception.cpython-37.pyc b/autocompletion/__pycache__/exception.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72eabd8e31124beb33e39209439ffc7a008b8614 GIT binary patch literal 405 zcmYjNu};G<5Oth_O4P21k+n;QP6#1X3WbdU#8MfZtlSAjB(|{=R9N9N*qHbpwygXG zOF5@1aMC@$v+th$JR1%(!I8ax-a1~tVzG%HlPPD|V?ePG7XlNMuL30{FX=*JdM!r1 z;%&8B<=pZc39~6@7%-4nBs?rJ;bz%pv(o1G^G7oEbkIIv!y2EgNX{c zFI*jzMzkiV+#-Q)YMa~2l~j}ZSHxjj=H#Nx*gg4ZEB;B44E?05qlaApPo>oe*7N01 f@DrBoVrRaY?pps`tl2iJwEd&C>Eo!`3HRg=H0y4q literal 0 HcmV?d00001 diff --git a/autocompletion/__pycache__/modal_operator.cpython-37.pyc b/autocompletion/__pycache__/modal_operator.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..335e50a53653ed1454255be7ec9e64b7ea85788f GIT binary patch literal 4916 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 JUnngU{|hIF(qI4p literal 0 HcmV?d00001 diff --git a/autocompletion/active_text_area.py b/autocompletion/active_text_area.py index 8bc3c79..360900f 100644 --- a/autocompletion/active_text_area.py +++ b/autocompletion/active_text_area.py @@ -1,50 +1,50 @@ -import bpy -from . event_utils import is_event, get_area_under_event - -class ActiveTextArea: - def __init__(self): - self.x, self.y, self.width, self.height = 0, 0, 0, 0 - - def set_area(self, area): - if not area: return - self.x = area.x - self.y = area.y - self.width = area.width - self.height = area.height - - def get_text(self): - area = self.get() - if area: - space = area.spaces[0] - return getattr(space, "text", None) - - def get(self): - area = self.get_nearest_text_area() - if getattr(area, "type", "") == "TEXT_EDITOR": return area - - def update(self, event): - if is_event(event, "LEFTMOUSE", "PRESS"): - area = get_area_under_event(event) - self.settings_from_area(area) - else: - nearest_area = self.get_nearest_text_area() - self.settings_from_area(nearest_area) - - def settings_from_area(self, area): - if not area: return - self.x = area.x - self.y = area.y - self.width = area.width - self.height = area.height - - def get_nearest_text_area(self): - differences = [(area, self.get_area_difference(area)) for area in bpy.context.screen.areas if area.type == "TEXT_EDITOR"] + [(bpy.context.area, 10000)] - return min(differences, key = lambda x: x[1])[0] - - def get_area_difference(self, area): - difference = 0 - difference += abs(area.x - self.x) - difference += abs(area.y - self.y) - difference += abs(area.width - self.width) - difference += abs(area.height - self.height) - return difference +import bpy +from . event_utils import is_event, get_area_under_event + +class ActiveTextArea: + def __init__(self): + self.x, self.y, self.width, self.height = 0, 0, 0, 0 + + def set_area(self, area): + if not area: return + self.x = area.x + self.y = area.y + self.width = area.width + self.height = area.height + + def get_text(self): + area = self.get() + if area: + space = area.spaces[0] + return getattr(space, "text", None) + + def get(self): + area = self.get_nearest_text_area() + if getattr(area, "type", "") == "TEXT_EDITOR": return area + + def update(self, event): + if is_event(event, "LEFTMOUSE", "PRESS"): + area = get_area_under_event(event) + self.settings_from_area(area) + else: + nearest_area = self.get_nearest_text_area() + self.settings_from_area(nearest_area) + + def settings_from_area(self, area): + if not area: return + self.x = area.x + self.y = area.y + self.width = area.width + self.height = area.height + + def get_nearest_text_area(self): + differences = [(area, self.get_area_difference(area)) for area in bpy.context.screen.areas if area.type == "TEXT_EDITOR"] + [(bpy.context.area, 10000)] + return min(differences, key = lambda x: x[1])[0] + + def get_area_difference(self, area): + difference = 0 + difference += abs(area.x - self.x) + difference += abs(area.y - self.y) + difference += abs(area.width - self.width) + difference += abs(area.height - self.height) + return difference diff --git a/autocompletion/autocomplete_handler.py b/autocompletion/autocomplete_handler.py index 7f67e29..ed4ef11 100644 --- a/autocompletion/autocomplete_handler.py +++ b/autocompletion/autocomplete_handler.py @@ -1,242 +1,242 @@ -import bpy -import re -from mathutils import Vector -from . exception import BlockEvent -from . suggestions import complete -from .. settings import get_preferences -from .. graphics.text_box import TextBox -from .. graphics.utils import getDpiFactor -from .. graphics.list_box import ListItem, ListBox -from . event_utils import (is_event, is_event_in_list, - is_mouse_click, get_mouse_region_position, - is_event_over_area) - -move_index_commands = { - "DOWN_ARROW" : 1, - "UP_ARROW" : -1, - "PAGE_DOWN" : 4, - "PAGE_UP" : -4, - "END" : 10000, - "HOME" : -10000 } - -def is_event_changing_the_text(event): - if len(event.unicode) > 0: return True - if is_event_in_list(event, ["BACK_SPACE", "RET", "DEL"], "PRESS"): return True - return False - -class AutocompleteHandler: - def __init__(self): - self.context_ui = ContextUI() - self.completions = [] - self.draw_max = 8 - self.top_index = 0 - self.active_index = 0 - self.reload_completions = False - self.hide() - - def update(self, event, text_block, area): - if not is_event_over_area(event, area): return - self.update_settings() - self.check_event_for_insertion(event, text_block) - self.update_visibility(event, text_block) - if self.is_hidden: return - - if is_event_changing_the_text(event): - self.reload_completions = True - - if self.completions_amount > 0: - self.move_active_index(event) - - def update_settings(self): - self.draw_max = get_preferences().context_box.lines - - def check_event_for_insertion(self, event, text_block): - def insert_with_keyboard(): - if is_event_in_list(event, ("TAB", "RET")): - self.insert_completion(text_block, self.completions[self.active_index]) - raise BlockEvent() - - def insert_with_mouse(): - if not is_event(event, "LEFTMOUSE"): return - item = self.context_ui.get_item_under_event(event) - if item: - self.insert_completion(text_block, item.data) - raise BlockEvent() - - if self.completions_amount == 0: return - if self.is_hidden: return - insert_with_keyboard() - insert_with_mouse() - - def insert_completion(self, text_block, completion): - completion.insert(text_block) - if completion.finished_statement: self.hide() - self.active_index = 0 - - def update_visibility(self, event, text_block): - if is_mouse_click(event): - return self.hide() - - if is_event(event, "ESC", shift = True): - return self.show() - - # open after removing after . or ' or " - if is_event_in_list(event, ["BACK_SPACE", "DEL"], "PRESS"): - line = text_block.text_before_cursor - if len(line) > 0: - if line[-1] in "\"\'.": - return self.show() - - if is_event_in_list(event, ["BACK_SPACE", "DEL", "ESC", "RET", "LEFT_ARROW", "RIGHT_ARROW"], "PRESS"): - return self.hide() - - char = event.unicode.lower() - if len(char) > 0 and not event.alt: - if char in "abcdefghijklmnopqrstuvwxyz0123456789_({[\\/=@.": - return self.show() - if char in ":": - return self.hide() - - # open with string declaration start and close with its end - line = text_block.text_before_cursor - if char in "\"": - if line.count("\"") % 2 == 0: return self.show() - else: return self.hide() - if char in '\'': - if line.count('\'') % 2 == 0: return self.show() - else: return self.hide() - - # open with space in import statement and after comma - if is_event(event, "SPACE"): - line = text_block.text_before_cursor - if re.search("(import|from)\s*\.?\s*$", line) or re.search("(,|=)\s*$", line): - return self.show() - else: - return self.hide() - - def show(self): - if self.is_hidden: - self.is_hidden = False - self.reload_completions = True - - def hide(self): - self.is_hidden = True - - def move_active_index(self, event): - def move_with_keyboard(): - for key, amount in move_index_commands.items(): - if is_event(event, key): - self.change_active_index(amount) - raise BlockEvent() - def move_with_mouse(): - if not self.context_ui.event_over_context_box(event): return - if is_event(event, "WHEELUPMOUSE"): - self.change_active_index(-1) - raise BlockEvent() - if is_event(event, "WHEELDOWNMOUSE"): - self.change_active_index(1) - raise BlockEvent() - move_with_keyboard() - move_with_mouse() - - def change_active_index(self, amount): - self.active_index += amount - self.correct_selection_indices() - - def update_completions(self, text_block): - self.completions = complete(text_block) - self.correct_selection_indices() - - def correct_selection_indices(self): - index = self.active_index - if index < 0: - index = 0 - if index >= self.completions_amount: - index = self.completions_amount - 1 - if index < self.top_index: - self.top_index = index - if index > self.bottom_index: - self.top_index = index - self.draw_max + 1 - if self.completions_amount < self.draw_max: - self.top_index = 0 - self.active_index = index - - - def draw(self, text_block): - if self.is_hidden: return - - if self.reload_completions: - self.update_completions(text_block) - self.reload_completions = False - - items = self.get_display_items() - self.context_ui.update_settings() - self.context_ui.insert_items(items) - self.context_ui.draw(text_block) - - def get_display_items(self): - items = [] - for i, c in enumerate(self.completions): - if not self.top_index <= i < self.top_index + self.draw_max: continue - item = ListItem(c.name) - item.active = self.active_index == i - item.data = c - item.offset = 10 * getDpiFactor() if c.type.endswith("PARAMETER") else 0 - items.append(item) - return items - - @property - def completions_amount(self): - return len(self.completions) - - @property - def bottom_index(self): - return self.top_index + self.draw_max - 1 - - -class ContextUI: - def __init__(self): - self.context_box = ListBox() - self.description_box = TextBox() - - def update_settings(self): - settings = get_preferences() - - s = settings.context_box - self.context_box.font_size = s.font_size - self.context_box.line_height = s.line_height * getDpiFactor() - self.context_box.width = s.width * getDpiFactor() - self.context_box.padding = s.padding * getDpiFactor() - - s = settings.description_box - self.description_box.font_size = s.font_size - self.description_box.line_height = s.line_height * getDpiFactor() - self.description_box.padding = s.padding * getDpiFactor() - - def insert_items(self, items): - self.context_box.items = items - active_item = self.get_active_item() - if active_item: self.description_box.text = active_item.data.description - else: self.description_box.text = "" - - def get_active_item(self): - for item in self.context_box.items: - if item.active: return item - - def draw(self, text_block): - cursor = text_block.current_cursor_region_location - self.context_box.position = cursor.copy() - self.description_box.position = cursor + Vector((self.context_box.width + 10, 0)) - - if len(self.context_box.items) > 0: - self.context_box.draw() - if len(self.description_box.text) > 0: - self.description_box.draw() - - def event_over_context_box(self, event): - point = get_mouse_region_position(event) - return self.context_box.contains(point) - - def get_item_under_event(self, event): - point = get_mouse_region_position(event) - return self.context_box.get_item_under_point(point) +import bpy +import re +from mathutils import Vector +from . exception import BlockEvent +from . suggestions import complete +from .. settings import get_preferences +from .. graphics.text_box import TextBox +from .. graphics.utils import getDpiFactor +from .. graphics.list_box import ListItem, ListBox +from . event_utils import (is_event, is_event_in_list, + is_mouse_click, get_mouse_region_position, + is_event_over_area) + +move_index_commands = { + "DOWN_ARROW" : 1, + "UP_ARROW" : -1, + "PAGE_DOWN" : 4, + "PAGE_UP" : -4, + "END" : 10000, + "HOME" : -10000 } + +def is_event_changing_the_text(event): + if len(event.unicode) > 0: return True + if is_event_in_list(event, ["BACK_SPACE", "RET", "DEL"], "PRESS"): return True + return False + +class AutocompleteHandler: + def __init__(self): + self.context_ui = ContextUI() + self.completions = [] + self.draw_max = 8 + self.top_index = 0 + self.active_index = 0 + self.reload_completions = False + self.hide() + + def update(self, event, text_block, area): + if not is_event_over_area(event, area): return + self.update_settings() + self.check_event_for_insertion(event, text_block) + self.update_visibility(event, text_block) + if self.is_hidden: return + + if is_event_changing_the_text(event): + self.reload_completions = True + + if self.completions_amount > 0: + self.move_active_index(event) + + def update_settings(self): + self.draw_max = get_preferences().context_box.lines + + def check_event_for_insertion(self, event, text_block): + def insert_with_keyboard(): + if is_event_in_list(event, ("TAB", "RET")): + self.insert_completion(text_block, self.completions[self.active_index]) + raise BlockEvent() + + def insert_with_mouse(): + if not is_event(event, "LEFTMOUSE"): return + item = self.context_ui.get_item_under_event(event) + if item: + self.insert_completion(text_block, item.data) + raise BlockEvent() + + if self.completions_amount == 0: return + if self.is_hidden: return + insert_with_keyboard() + insert_with_mouse() + + def insert_completion(self, text_block, completion): + completion.insert(text_block) + if completion.finished_statement: self.hide() + self.active_index = 0 + + def update_visibility(self, event, text_block): + if is_mouse_click(event): + return self.hide() + + if is_event(event, "ESC", shift = True): + return self.show() + + # open after removing after . or ' or " + if is_event_in_list(event, ["BACK_SPACE", "DEL"], "PRESS"): + line = text_block.text_before_cursor + if len(line) > 0: + if line[-1] in "\"\'.": + return self.show() + + if is_event_in_list(event, ["BACK_SPACE", "DEL", "ESC", "RET", "LEFT_ARROW", "RIGHT_ARROW"], "PRESS"): + return self.hide() + + char = event.unicode.lower() + if len(char) > 0 and not event.alt: + if char in "abcdefghijklmnopqrstuvwxyz0123456789_({[\\/=@.": + return self.show() + if char in ":": + return self.hide() + + # open with string declaration start and close with its end + line = text_block.text_before_cursor + if char in "\"": + if line.count("\"") % 2 == 0: return self.show() + else: return self.hide() + if char in '\'': + if line.count('\'') % 2 == 0: return self.show() + else: return self.hide() + + # open with space in import statement and after comma + if is_event(event, "SPACE"): + line = text_block.text_before_cursor + if re.search("(import|from)\s*\.?\s*$", line) or re.search("(,|=)\s*$", line): + return self.show() + else: + return self.hide() + + def show(self): + if self.is_hidden: + self.is_hidden = False + self.reload_completions = True + + def hide(self): + self.is_hidden = True + + def move_active_index(self, event): + def move_with_keyboard(): + for key, amount in move_index_commands.items(): + if is_event(event, key): + self.change_active_index(amount) + raise BlockEvent() + def move_with_mouse(): + if not self.context_ui.event_over_context_box(event): return + if is_event(event, "WHEELUPMOUSE"): + self.change_active_index(-1) + raise BlockEvent() + if is_event(event, "WHEELDOWNMOUSE"): + self.change_active_index(1) + raise BlockEvent() + move_with_keyboard() + move_with_mouse() + + def change_active_index(self, amount): + self.active_index += amount + self.correct_selection_indices() + + def update_completions(self, text_block): + self.completions = complete(text_block) + self.correct_selection_indices() + + def correct_selection_indices(self): + index = self.active_index + if index < 0: + index = 0 + if index >= self.completions_amount: + index = self.completions_amount - 1 + if index < self.top_index: + self.top_index = index + if index > self.bottom_index: + self.top_index = index - self.draw_max + 1 + if self.completions_amount < self.draw_max: + self.top_index = 0 + self.active_index = index + + + def draw(self, text_block): + if self.is_hidden: return + + if self.reload_completions: + self.update_completions(text_block) + self.reload_completions = False + + items = self.get_display_items() + self.context_ui.update_settings() + self.context_ui.insert_items(items) + self.context_ui.draw(text_block) + + def get_display_items(self): + items = [] + for i, c in enumerate(self.completions): + if not self.top_index <= i < self.top_index + self.draw_max: continue + item = ListItem(c.name) + item.active = self.active_index == i + item.data = c + item.offset = 10 * getDpiFactor() if c.type.endswith("PARAMETER") else 0 + items.append(item) + return items + + @property + def completions_amount(self): + return len(self.completions) + + @property + def bottom_index(self): + return self.top_index + self.draw_max - 1 + + +class ContextUI: + def __init__(self): + self.context_box = ListBox() + self.description_box = TextBox() + + def update_settings(self): + settings = get_preferences() + + s = settings.context_box + self.context_box.font_size = s.font_size + self.context_box.line_height = s.line_height * getDpiFactor() + self.context_box.width = s.width * getDpiFactor() + self.context_box.padding = s.padding * getDpiFactor() + + s = settings.description_box + self.description_box.font_size = s.font_size + self.description_box.line_height = s.line_height * getDpiFactor() + self.description_box.padding = s.padding * getDpiFactor() + + def insert_items(self, items): + self.context_box.items = items + active_item = self.get_active_item() + if active_item: self.description_box.text = active_item.data.description + else: self.description_box.text = "" + + def get_active_item(self): + for item in self.context_box.items: + if item.active: return item + + def draw(self, text_block): + cursor = text_block.current_cursor_region_location + self.context_box.position = cursor.copy() + self.description_box.position = cursor + Vector((self.context_box.width + 10, 0)) + + if len(self.context_box.items) > 0: + self.context_box.draw() + if len(self.description_box.text) > 0: + self.description_box.draw() + + def event_over_context_box(self, event): + point = get_mouse_region_position(event) + return self.context_box.contains(point) + + def get_item_under_event(self, event): + point = get_mouse_region_position(event) + return self.context_box.get_item_under_point(point) diff --git a/autocompletion/event_utils.py b/autocompletion/event_utils.py index f4a03fe..dd69bdb 100644 --- a/autocompletion/event_utils.py +++ b/autocompletion/event_utils.py @@ -1,40 +1,40 @@ -import bpy -from mathutils import Vector - -def get_mouse_region_position(event): - return Vector((event.mouse_region_x, event.mouse_region_y)) - -def is_event(event, type, value = "PRESS", shift = False, ctrl = False, alt = False): - if event.type in ("LEFT_SHIFT", "RIGHT_SHIFT"): shift = True - if event.type in ("LEFT_CTRL", "RIGHT_CTRL"): ctrl = True - if event.type in ("LEFT_ALT", "RIGHT_ALT"): alt = True - if shift == "ANY": shift = event.shift - if ctrl == "ANY": ctrl = event.ctrl - if alt == "ANY": alt = event.alt - - return event.type == type and \ - event.value == value and \ - event.shift == shift and \ - event.ctrl == ctrl and \ - event.alt == alt - -def is_event_in_list(event, types, value = "PRESS", shift = False, ctrl = False, alt = False): - if not event.type in types: return - return is_event(event, event.type, value, shift, ctrl, alt) - -def is_mouse_click(event): - return is_event_in_list(event, ("LEFTMOUSE", "RIGHTMOUSE")) - -def get_area_under_event(event): - for area in bpy.context.screen.areas: - if is_event_over_area(event, area): return area - return None - -def is_event_over_area(event, area): - for region in area.regions: - if is_event_over_region(event, region): return True - return False - -def is_event_over_region(event, region): - return region.x <= event.mouse_x <= region.x + region.width and \ - region.y <= event.mouse_y <= region.y + region.height +import bpy +from mathutils import Vector + +def get_mouse_region_position(event): + return Vector((event.mouse_region_x, event.mouse_region_y)) + +def is_event(event, type, value = "PRESS", shift = False, ctrl = False, alt = False): + if event.type in ("LEFT_SHIFT", "RIGHT_SHIFT"): shift = True + if event.type in ("LEFT_CTRL", "RIGHT_CTRL"): ctrl = True + if event.type in ("LEFT_ALT", "RIGHT_ALT"): alt = True + if shift == "ANY": shift = event.shift + if ctrl == "ANY": ctrl = event.ctrl + if alt == "ANY": alt = event.alt + + return event.type == type and \ + event.value == value and \ + event.shift == shift and \ + event.ctrl == ctrl and \ + event.alt == alt + +def is_event_in_list(event, types, value = "PRESS", shift = False, ctrl = False, alt = False): + if not event.type in types: return + return is_event(event, event.type, value, shift, ctrl, alt) + +def is_mouse_click(event): + return is_event_in_list(event, ("LEFTMOUSE", "RIGHTMOUSE")) + +def get_area_under_event(event): + for area in bpy.context.screen.areas: + if is_event_over_area(event, area): return area + return None + +def is_event_over_area(event, area): + for region in area.regions: + if is_event_over_region(event, region): return True + return False + +def is_event_over_region(event, region): + return region.x <= event.mouse_x <= region.x + region.width and \ + region.y <= event.mouse_y <= region.y + region.height diff --git a/autocompletion/exception.py b/autocompletion/exception.py index 1acf90e..61e23ff 100644 --- a/autocompletion/exception.py +++ b/autocompletion/exception.py @@ -1,5 +1,5 @@ -# Reload this module in the beginning -__reload_order_index__ = -100 - -class BlockEvent(Exception): - pass +# Reload this module in the beginning +__reload_order_index__ = -100 + +class BlockEvent(Exception): + pass diff --git a/autocompletion/modal_operator.py b/autocompletion/modal_operator.py index 8156b92..4f77a7d 100644 --- a/autocompletion/modal_operator.py +++ b/autocompletion/modal_operator.py @@ -1,127 +1,127 @@ -import bpy -from . exception import BlockEvent -from . event_utils import is_event -from .. text_block import TextBlock -from .. settings import get_preferences -from . active_text_area import ActiveTextArea -from . autocomplete_handler import AutocompleteHandler -from . suggestions.jedi_completion import jedi_module_found -from . suggestions.generate_fake_bpy import fake_bpy_module_exists - -is_running = False -active_text_area = ActiveTextArea() - -class Autocomplete(bpy.types.Panel): - bl_idname = "autocomplete" - bl_label = "Autocomplete" - bl_space_type = "TEXT_EDITOR" - bl_region_type = "UI" - - def draw(self, context): - layout = self.layout - - - row = layout.row(align = True) - if not is_running: - row.operator("code_autocomplete.start_modal_operator", text = "Start") - else: - row.operator("code_autocomplete.stop_modal_operator", text = "Stop") - - if fake_bpy_module_exists(): - row.operator("code_autocomplete.regenerate_fake_bpy", text = "", icon = "RECOVER_AUTO") - else: - layout.operator("code_autocomplete.regenerate_fake_bpy", "Build BPY Module", icon = "ERROR") - - if jedi_module_found(): - providers = get_preferences().completion_providers - layout.prop(providers, "use_jedi_completion") - else: - layout.label("Jedi library not found", icon = "ERROR") - - -class StartModalOperator(bpy.types.Operator): - bl_idname = "code_autocomplete.start_modal_operator" - bl_label = "Start Modal Operator" - bl_description = "Activate the autocomplete feature in the text editor" - bl_options = {"REGISTER"} - - def execute(self, context): - bpy.ops.code_autocomplete.modal_text_operator("INVOKE_DEFAULT") - active_text_area.set_area(context.area) - global is_running - is_running = True - return {"FINISHED"} - - -class StopModalOperator(bpy.types.Operator): - bl_idname = "code_autocomplete.stop_modal_operator" - bl_label = "Stop Modal Operator" - bl_description = "Stop the autocompletion in the text editor" - bl_options = {"REGISTER"} - - def execute(self, context): - global is_running - is_running = False - return {"FINISHED"} - - -class ModalTextOperator(bpy.types.Operator): - bl_idname = "code_autocomplete.modal_text_operator" - bl_label = "Modal Text Operator" - bl_description = "" - bl_options = {"REGISTER"} - - def invoke(self, context, event): - args = (self, context) - self._handle = bpy.types.SpaceTextEditor.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL") - context.window_manager.modal_handler_add(self) - self.handlers = [AutocompleteHandler()] - return {"RUNNING_MODAL"} - - def modal(self, context, event): - self.redraw_text_editors() - active_text_area.update(event) - - if not is_running or event.type == "F8": - return self.finish() - - return self.update_handlers(event) - - def redraw_text_editors(self): - for area in bpy.context.screen.areas: - if area.type == "TEXT_EDITOR": - area.tag_redraw() - - def update_handlers(self, event): - text_block = self.get_text_block() - area = active_text_area.get() - if not text_block: return {"PASS_THROUGH"} - - try: - for handler in self.handlers: - handler.update(event, text_block, area) - return {"PASS_THROUGH"} - except BlockEvent: - if get_preferences().debug: print("Event blocked: {} - {}".format(event.type, event.value)) - return {"RUNNING_MODAL"} - - def finish(self): - bpy.types.SpaceTextEditor.draw_handler_remove(self._handle, "WINDOW") - if get_preferences().debug: print("Finished modal text operator") - return {"FINISHED"} - - def draw_callback_px(tmp, self, context): - if context.area == active_text_area.get(): - text_block = self.get_text_block() - if not text_block: return - - for handler in self.handlers: - handler.draw(text_block) - - def get_text_block(self): - text = active_text_area.get_text() - area = active_text_area.get() - if text: - text_block = TextBlock(text) - text_block.set_context(area = area, space = area.spaces[0]) - return text_block +import bpy +from . exception import BlockEvent +from . event_utils import is_event +from .. text_block import TextBlock +from .. settings import get_preferences +from . active_text_area import ActiveTextArea +from . autocomplete_handler import AutocompleteHandler +from . suggestions.jedi_completion import jedi_module_found +from . suggestions.generate_fake_bpy import fake_bpy_module_exists + +is_running = False +active_text_area = ActiveTextArea() + +class Autocomplete(bpy.types.Panel): + bl_idname = "autocomplete" + bl_label = "Autocomplete" + bl_space_type = "TEXT_EDITOR" + bl_region_type = "UI" + + def draw(self, context): + layout = self.layout + + + row = layout.row(align = True) + if not is_running: + row.operator("code_autocomplete.start_modal_operator", text = "Start") + else: + row.operator("code_autocomplete.stop_modal_operator", text = "Stop") + + if fake_bpy_module_exists(): + row.operator("code_autocomplete.regenerate_fake_bpy", text = "", icon = "RECOVER_AUTO") + else: + layout.operator("code_autocomplete.regenerate_fake_bpy", "Build BPY Module", icon = "ERROR") + + if jedi_module_found(): + providers = get_preferences().completion_providers + layout.prop(providers, "use_jedi_completion") + else: + layout.label(text="Jedi library not found", icon = "ERROR") + + +class StartModalOperator(bpy.types.Operator): + bl_idname = "code_autocomplete.start_modal_operator" + bl_label = "Start Modal Operator" + bl_description = "Activate the autocomplete feature in the text editor" + bl_options = {"REGISTER"} + + def execute(self, context): + bpy.ops.code_autocomplete.modal_text_operator("INVOKE_DEFAULT") + active_text_area.set_area(context.area) + global is_running + is_running = True + return {"FINISHED"} + + +class StopModalOperator(bpy.types.Operator): + bl_idname = "code_autocomplete.stop_modal_operator" + bl_label = "Stop Modal Operator" + bl_description = "Stop the autocompletion in the text editor" + bl_options = {"REGISTER"} + + def execute(self, context): + global is_running + is_running = False + return {"FINISHED"} + + +class ModalTextOperator(bpy.types.Operator): + bl_idname = "code_autocomplete.modal_text_operator" + bl_label = "Modal Text Operator" + bl_description = "" + bl_options = {"REGISTER"} + + def invoke(self, context, event): + args = (self, context) + self._handle = bpy.types.SpaceTextEditor.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL") + context.window_manager.modal_handler_add(self) + self.handlers = [AutocompleteHandler()] + return {"RUNNING_MODAL"} + + def modal(self, context, event): + self.redraw_text_editors() + active_text_area.update(event) + + if not is_running or event.type == "F8": + return self.finish() + + return self.update_handlers(event) + + def redraw_text_editors(self): + for area in bpy.context.screen.areas: + if area.type == "TEXT_EDITOR": + area.tag_redraw() + + def update_handlers(self, event): + text_block = self.get_text_block() + area = active_text_area.get() + if not text_block: return {"PASS_THROUGH"} + + try: + for handler in self.handlers: + handler.update(event, text_block, area) + return {"PASS_THROUGH"} + except BlockEvent: + if get_preferences().debug: print("Event blocked: {} - {}".format(event.type, event.value)) + return {"RUNNING_MODAL"} + + def finish(self): + bpy.types.SpaceTextEditor.draw_handler_remove(self._handle, "WINDOW") + if get_preferences().debug: print("Finished modal text operator") + return {"FINISHED"} + + def draw_callback_px(tmp, self, context): + if context.area == active_text_area.get(): + text_block = self.get_text_block() + if not text_block: return + + for handler in self.handlers: + handler.draw(text_block) + + def get_text_block(self): + text = active_text_area.get_text() + area = active_text_area.get() + if text: + text_block = TextBlock(text) + text_block.set_context(area = area, space = area.spaces[0]) + return text_block diff --git a/autocompletion/suggestions/__init__.py b/autocompletion/suggestions/__init__.py index bf9e9b7..f8ee377 100644 --- a/autocompletion/suggestions/__init__.py +++ b/autocompletion/suggestions/__init__.py @@ -1,27 +1,27 @@ -from . jedi_completion import JediCompletionProvider -from . word_completion import WordCompletionProvider -from . operator_completion import OperatorCompletionProvider -from . static_pattern_completion import StaticPatternProvider -from ... settings import get_preferences - -jedi_provider = JediCompletionProvider() -word_provider = WordCompletionProvider() -operator_provider = OperatorCompletionProvider() -static_pattern_provider = StaticPatternProvider() - -def complete(text_block): - setting = get_preferences().completion_providers - - completions = [] - completions.extend(static_pattern_provider.complete(text_block)) - if setting.use_operator_completion: completions.extend(operator_provider.complete(text_block)) - if setting.use_jedi_completion: completions.extend(jedi_provider.complete(text_block)) - if setting.use_word_completion: completions.extend(word_provider.complete(text_block)) - - list1, list2, list3 = [], [], [] - for c in completions: - if c.type == "OPERATOR_PARAMETER": list1.append(c) - elif c.type == "PARAMETER": list2.append(c) - else: list3.append(c) - - return list1 + list2 + list3 +from . jedi_completion import JediCompletionProvider +from . word_completion import WordCompletionProvider +from . operator_completion import OperatorCompletionProvider +from . static_pattern_completion import StaticPatternProvider +from ... settings import get_preferences + +jedi_provider = JediCompletionProvider() +word_provider = WordCompletionProvider() +operator_provider = OperatorCompletionProvider() +static_pattern_provider = StaticPatternProvider() + +def complete(text_block): + setting = get_preferences().completion_providers + + completions = [] + completions.extend(static_pattern_provider.complete(text_block)) + if setting.use_operator_completion: completions.extend(operator_provider.complete(text_block)) + if setting.use_jedi_completion: completions.extend(jedi_provider.complete(text_block)) + if setting.use_word_completion: completions.extend(word_provider.complete(text_block)) + + list1, list2, list3 = [], [], [] + for c in completions: + if c.type == "OPERATOR_PARAMETER": list1.append(c) + elif c.type == "PARAMETER": list2.append(c) + else: list3.append(c) + + return list1 + list2 + list3 diff --git a/autocompletion/suggestions/__pycache__/__init__.cpython-37.pyc b/autocompletion/suggestions/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce4da78c00f11ef04673714e0bec5af24eb01168 GIT binary patch literal 1185 zcmZuxOK;RL5VrH&XScMp6`T;ay;Nekpb8n$Llc9m}97MbW zMG?gziZH=g*;=SYI?)ST4~@tqW?>tl71_iF+oV=lipr#n5yKaVRG1FB%1qETW`nL% zo0h&HvP8>=s9Cwja7+{D>#rCMc9JL!nGBM+!;?<|Wn6iV6HagS+&@Se_hiCv^{yRC zPX>O+laldi!fSnWUZcmPn{xJnaTfbb_^J*yY!DM<2Soe^CD8@y<00;$p_XZvn$$0i z3|-+X;3UI+leY@gT0m|7vOr4($AvJZRp`kuBhUVFJrtPAWt)Z1!{VK($ z8vmB*m$l4N=Y5m~ssZ{lGv-ipW*?(W&x{R}nHvZg*7wQEL1(YqK04^Sop!hVdhckj zo0q3%v(D8!le_+ueIRB;zsN0iCRt4L4NUH0TiGP~gf=L;~y}UZB zMG!L#gnTqwTBGH0j{RSVuK^4HzV2)}Zv^AQfeN|PPSagadQLa-B1q+Ihj0$#4_+jL zn8N3k#1qJAZ9Zu^!skIMh2v40#KQ3t%3NV>$f(l0G)H*ow($grRgOEK_7f!5zs^Rg} IEL=?OKlOT7L;wH) literal 0 HcmV?d00001 diff --git a/autocompletion/suggestions/__pycache__/generate_fake_bpy.cpython-37.pyc b/autocompletion/suggestions/__pycache__/generate_fake_bpy.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0077b8acc9630223602e399caa5245877983b9f4 GIT binary patch literal 13969 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 literal 0 HcmV?d00001 diff --git a/autocompletion/suggestions/__pycache__/interface.cpython-37.pyc b/autocompletion/suggestions/__pycache__/interface.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb5d5d8f867ce9da55c1237cf199bf5685483834 GIT binary patch literal 988 zcmZuw&2H2%5VjNVZpgNXB7}P2&;ysftWd=PA%sd7!2u~nEEUqq$O^G{iJCub2hoZf z6>q_b6VJoVSKfjXGhVVkP)jrU#-8!`o0%N%?#2XG{Qc+KZQR zH!l3bE64=IFi;Buo;3MEMuB5S6>}F`^~s8NMV*~#boc7_*U3YEYLqs-1bCgd_`xAJna&$)cq(OG8J^WriL|wKR+mkotl}F6 z2Ag)VPzLWYJg=liHz-KDy zS)g}hFhv{!-v5gk;JbJi?DqlRlY=Qt3)~$gjv*CC+)%1zl_85`_PkNE!D;d$IeC+~ zoAbQN&3h$5pa_j9+mMUfYi{F> zfGwU|6X)MeldbePiT|eW=M_wv#iXk~Z{%8@CZnDnU~q)$8Y&FXd#KiYJo0_TpAmr{ d{)k4g#+?M}fRFUXRdFtQw49*FtD(5g{s7~n);9nE literal 0 HcmV?d00001 diff --git a/autocompletion/suggestions/__pycache__/jedi_completion.cpython-37.pyc b/autocompletion/suggestions/__pycache__/jedi_completion.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af80e064f797aaf0dba25fd2ecfe15489ea7ce96 GIT binary patch literal 3007 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 literal 0 HcmV?d00001 diff --git a/autocompletion/suggestions/__pycache__/static_pattern_completion.cpython-37.pyc b/autocompletion/suggestions/__pycache__/static_pattern_completion.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4dcdff304ae8662031c792e4fe725064c41f7c00 GIT binary patch literal 3093 zcmcIm&2JmW6`$E%lFJXtw7%sgj^a46s5(sQw1p7FhAEPkh$tjMN*21Hu~>7)^h)HC znpxVGLIDM2^xjKPJxJ)Oe@}07?J564PI+(mA<9vKUb2gw&o^)0{NC@q*{zwG1c4{< zum8N*i4*c)?2MloXgq~aeoF~qgwZx}hBb94Gnm;nI;LZGEXSf`n=p$--V+w#*173K zpdV#1=*PGT{V20p{5^4EoF3YT@x#Pn(n;|dmSD;Egr`_)l^o6%$T>Zy&Mh{>X4x&4 zX1C#;WAiQBNwWnlc^lTx9nPOqly&B{wgs(i5wtC8nZ;|`mSB8oG`>8w%QC;k(`<#U zvODZ|>?8IuyURXl*=&t{%I>kx*nReSE6ToL57;`(v@Ev4e$O7VFIkp-#lB{L0Le$} zo0h@n*&ipCtqkqrOMIFA>7PkwmC-uM{P`zDwEPpGNHT`9tD^hHXI!X6zT4@wdFXe8 zCW?$70~$}^lOI9B34l!;g8}3Kpvg>TK@DJo>ZqDM=n6Ka2iM|*HGX;cB>H$j2gC*U z!75tLP-O*Phl4qm3YpY~*^qf){#?TG#9NL94OV z<^h<$_N?0vn1|MnhZ>Kvn~xf@Dg0h28y;ibKsK6P#$B%;cALY~^2SsL7t4OD#U<*I z4H<&MrrYzvkc+?_ErF?Q??lj!@5x-mSIJ}Et2z79ZkEYqAu!h{7HiL^%If(*zZ zy`*%4hoKe(@3p-qcbk17co4dXh=_uG#t^unN`(Af=)P=sn@1X_VS(7Hs2>1QA-2bM zEq-9vP@nfXJ^o1T3)BVqK$M9fCck<;}pQKut(OI)Dg0M^*!gfIMwqhHBXdXSpNSUAat- zfD@QVJ~)E1$85kujxoD?qe0oRXgOnvTc``YC~jl1h{a71&A|ljt5w@nV84kGh!0|i zA0j6I2F1I-hU9`?k^*@VHjFcRW(>%MF#w93nFEuNBU^lNoEVrF<|R2ZTjb1o_jF)@ z^yXP)5W&$4YhYed@jQh7IektjIo=$Y!?~lV*c@0F(cv7@?s)Tn46Gv1ZwDn42NNR3 zRB|dFR0N>yNOiZx!wE4p4ZT;0`6y0Qq~GgdLIEF$P`>rU*P0P3>cM!xGEwaV6`RN- zxur^Ag^m-Uvmkl13-K&Y@{_D2g$>|L;j7t)j6(3`3KWFS(FC=LL2bi=)+|lIJ4Xdd zO#sJOoWvlyq6HE7B5?+EIwXHLj;;D|7syKqNR2`0zw)^Nf*IMbm4z65V8qXf?~r=P zj3;ZLPrErv#64&$D?I6O6+I}G3zY*E-6`e@#hPR1cXH)&aksAGd&M8Bm0GU$7iH|1 zRAR4E*>$V6O0}+H)mrg+sd#YBatt?}J?=n5TRw-DUx3ptsk8=kcmgnEBWtB+1vrz!LUfff*?v``AYPL{(mfPRmbMw2Eda)_jYQP{p}qUgHXUQQx;rso-(^V zsUpZKsQRJbmdb)qdaX=|FBRztoVQueOk!je zey@&9am6J=V6qY$SfF!9gmU6&g}y&-^7%I)Iigi2dAjcesAy~ zb9x7+1aHB``s=xmVXoU7-Nze`vm5IV2R~*eOP67~2~KEr#mP0D^MZ$c0r&YvxmEb|YGql)AUg0jC)D+TEnamd=hdUz^RHXO@@|~0BSh-hfo#b#t zvkv~1^|U^j6kE7@35%<^*DVZwWq&vPsd$16l36I=7lEeDbS!Pzvnje_EEr1$)O5u( Rr~%1(783Uyjq0{Re*xEi|GxkL literal 0 HcmV?d00001 diff --git a/autocompletion/suggestions/__pycache__/word_completion.cpython-37.pyc b/autocompletion/suggestions/__pycache__/word_completion.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8941d357d54368b1c4280fbb22fe1b9326cf300f GIT binary patch literal 1622 zcmah}O>f*b5aowfmesZEBu?D|1(H($T^2=>Ll7875-*zDqKHuh5M&UFNUvpUr8UWo z9YcGvkL@A9e~^NzvIKSVFX z&M$v$KTZBLN}FT?HhDfC_fwlDuc~yEm&4>mp-Kq<{_oYKlqp8vE+r3pPaY;F)A`t% zB$cu%O_EhoiF9JCY?iG`<|cU9Ool^c(2q$l3~{*;7JB1%E)XIwb1Q_8sWkb~(~awC z_u4(#eEvY~EdRA8{|i!+1Nwo|D-@Vp*J@m(nG)GVYgJl-0cgA_VtNTaF0|^ziZ?}- z9X9C93?l0MyaaqKV%ElW_eNZ^9fPQYX&~Z4xKM~uB_{=ryFwgI(qi^U{{Wu!DjM`T zXYe}R0fXJ3L<1V|W@WK*)?v%H$^q^@_tW(dTQy!Ghu*J{djgZ1UXTxD%4&K*r@ZDh zJM{Janq9D(BbpXgYJSG1ml~~EpPZra`5;5hV?$cd@eXu@!3}|o-R_iHaDPVMvmkS;fgquZfEQ)074!T3%J~@4~IImtSyWS>nm=!4wqPh;C89`WBjB&@58NZTNxVG;jn|Km-GB zfme7NmK{LB`x`ht*{n&Q>^`1)wTJ9q@ETr@?gQ(vf~+4sfQr)m12sIT`Ty&$3F=`3 zD-FX+!w=OvcmSQEI)A&QP03-!;~R9kbL+kNM!<1v(#sH1;|@ES1V?i(lzVwBo! zUzvENvinN6(?!bA8*mh9Fqk|d9n#gNcWDx;WfZD ZI~T$1g3a06-9lv>lxINK_*$?Qd<9MVdoch2 literal 0 HcmV?d00001 diff --git a/autocompletion/suggestions/generate_fake_bpy.py b/autocompletion/suggestions/generate_fake_bpy.py index fab1e68..a72f120 100644 --- a/autocompletion/suggestions/generate_fake_bpy.py +++ b/autocompletion/suggestions/generate_fake_bpy.py @@ -1,374 +1,374 @@ -import bpy -import os -import sys -import shutil -import inspect -import textwrap -from ... settings import get_preferences -from . rna_utils import get_readable_property_type - -fake_package_name = "_bpy_fake" -top_directory = os.path.join(os.path.dirname(__file__), "dynamic") -directory = os.path.join(top_directory, fake_package_name) -private_path = os.path.join(directory, "__private__") - -sys.path.append(top_directory) - -docstring_width = 70 -use_quote_marks = False - - -class GenerateFakeBPY(bpy.types.Operator): - bl_idname = "code_autocomplete.regenerate_fake_bpy" - bl_label = "Generate Fake BPY" - bl_description = "Regenerate the fake bpy module that the jedi autocompletion needs" - bl_options = {"REGISTER"} - - def execute(self, context): - regenerate_fake_bpy() - return {"FINISHED"} - -def fake_bpy_module_exists(): - return os.path.exists(private_path) - -def regenerate_fake_bpy(): - remove_old_fake() - generate_fake_bpy() - -def remove_old_fake(): - if os.path.exists(directory): - shutil.rmtree(directory, ignore_errors = True) - -def generate_fake_bpy(): - try: os.makedirs(directory) - except: pass - create_init() - create_private_subdirectory() - -def create_init(): - path = os.path.join(directory, "__init__.py") - file = open(path, "w+") - file.write(init_content) - file.close() - -init_content = ''' -from . __private__.context import Context as context -from . __private__.blenddata import BlendData as data -''' - -def create_private_subdirectory(): - os.makedirs(private_path) - open(os.path.join(private_path, "__init__.py"), "a").close() - write_code_file("__init__", "") - write_code_file("bpy_struct", bpy_struct_content) - generate_code_files() - - -collection_types = {} -def generate_code_files(create_all = False): - types_to_generate = {"Context", "Panel"} - generated_types = set() - - while len(types_to_generate) > 0: - name = types_to_generate.pop() - generated_types.add(name) - type = getattr(bpy.types, name) - code, dependencies = get_code_and_dependencies(name, type) - write_code_file(name, code) - types_to_generate.update([d for d in dependencies if d not in generated_types]) - - if len(types_to_generate) == 0 and create_all: - bpy_types = [(name, type) for name, type in inspect.getmembers(bpy.types) if "." not in name] - for name, type in bpy_types: - if name not in generated_types: - types_to_generate.add(name) - - -def get_code_and_dependencies(name, type): - dependencies = get_dependencies(name, type) - - lines = [] - lines.extend(get_import_code_lines(dependencies)) - lines.append("class {}({}):".format(name, "" if name == "Context" else "bpy_struct")) - lines.extend(get_property_code_lines(type)) - lines.extend(get_function_code_lines(name, type)) - - return "\n".join(lines), dependencies - -def get_import_code_lines(dependencies): - return ["from . {} import {}".format(d.lower(), d) for d in dependencies] + ["from . bpy_struct import bpy_struct", "import mathutils", ""] - -def get_property_code_lines(type): - lines = [] - for property in get_type_properties(type): - lines.extend(get_property_definition_code_lines(property)) - return lines - -def get_property_definition_code_lines(property): - lines = [] - lines.append(" @property") - lines.append(" def {}(self):".format(property.identifier)) - lines.extend(get_property_docstring_lines(property, docstring_width)) - lines.append(" return {}".format(get_property_declaration(property))) - return lines - -def get_function_code_lines(name, type): - lines = [] - for function in type.bl_rna.functions: - lines.append(" def {}({}):".format(function.identifier, get_function_parameter_list(function))) - lines.extend(get_function_docstring_lines(function, docstring_width)) - lines.append(" return {}".format(get_function_return_list(function))) - - global collection_types - if name in collection_types: - subtype = collection_types[name] - lines.append(" def get(key): return {}()".format(subtype)) - lines.append(" def __getitem__(key): return {}()".format(subtype)) - lines.append(" def __iter__(key): yield {}()".format(subtype)) - - return lines - -def get_property_docstring_lines(property, width = 70, indent = 8): - lines = get_property_description_lines(property, width) - lines.extend(get_enum_item_lines(property, width)) - return make_docstring_from_lines(lines, indent) - -def get_function_docstring_lines(function, width = 70, indent = 8): - lines = get_function_description_lines(function, width) - parameter_lines = get_parameter_lines(function, width) - lines.extend(parameter_lines) - return make_docstring_from_lines(lines, indent) - -def get_parameter_lines(function, width): - lines = [] - params = [p for p in function.parameters if not p.is_output] - if len(params) > 0: - lines.append("") - lines.append("Parameter:") - lines.extend(get_parameter_list_lines(params, width)) - returns = [p for p in function.parameters if p.is_output] - if len(returns) > 0: - lines.append("") - lines.append("Returns:") - lines.extend(get_parameter_list_lines(returns, width)) - return lines - -def get_parameter_list_lines(params, width): - lines = [] - for param in params: - lines.append("{}:".format(param.identifier)) - description_lines = get_property_description_lines(param, width) - amount = len(description_lines) - if amount == 0: lines[-1] += " " - elif amount == 1: lines[-1] += " " + description_lines[0] - else: - indent_lines(description_lines, 2) - lines.extend(description_lines) - indent_lines(lines, 2) - return lines - -def get_property_description_lines(property, width): - type = "({})".format(get_readable_property_type(property)) - if property.description in (None, ""): return [type] - return textwrap.wrap(type + " " + property.description, width) - -def get_function_description_lines(function, width): - if function.description in (None, ""): return [] - return textwrap.wrap(function.description, width) - -def get_enum_item_lines(property, width): - if getattr(property, "enum_items", None) is None: return [] - items = property.enum_items - if len(items) == 0: return [] - quote_mark = "'" if use_quote_marks else "" - item_string = "["+ ", ".join(quote_mark + item.identifier + quote_mark for item in items) +"]" - return [""] + textwrap.wrap(item_string, width) - -def make_docstring_from_lines(lines, indent = 8): - if len(lines) == 0: return [] - lines[0] = "'''" + lines[0] - lines[-1] += "'''" - indent_lines(lines, indent) - return lines - -def indent_lines(lines, indent = 4): - spaces = " " * indent - for i in range(len(lines)): - lines[i] = spaces + lines[i] - -def get_dependencies(name, type): - def find_property_dependency(property): - if property.type == "POINTER": - dependencies.add(property.fixed_type.identifier) - if property.type == "COLLECTION": - if property.srna is None: dependencies.add(property.fixed_type.identifier) - else: dependencies.add(property.srna.identifier) - - dependencies = set() - if name in collection_types: - dependencies.add(collection_types[name]) - for property in get_type_properties(type): - find_property_dependency(property) - for function in type.bl_rna.functions: - for parameter in function.parameters: - find_property_dependency(parameter) - return dependencies - -def get_type_properties(type): - fakes = [] - if type.bl_rna.identifier == "Context": - fakes = fake_context_properties - return list(type.bl_rna.properties) + fakes - -def get_function_parameter_list(function): - parameters = ["self"] + [parameter.identifier for parameter in function.parameters if not parameter.is_output] - return ", ".join(parameters) - -def get_function_return_list(function): - returns = [parameter for parameter in function.parameters if parameter.is_output] - return ", ".join([get_property_declaration(parameter) for parameter in returns]) - -def get_property_declaration(property): - global collection_types - if property.type == "BOOLEAN": return "bool()" - if property.type == "INT": return "int()" - if property.type in ("STRING", "ENUM"): return "str()" - if property.type == "COLLECTION": - if property.srna is None: return "({}(),)".format(property.fixed_type.identifier) - else: - collection_types[property.srna.identifier] = property.fixed_type.identifier - return property.srna.identifier + "()" - if property.type == "FLOAT": - if property.array_length <= 1: return "float()" - if property.array_length in (2, 3): return "mathutils.Vector()" - if property.array_length == 16: return "mathutils.Matrix()" - if property.type == "POINTER": - return property.fixed_type.identifier + "()" - return "''" - -def write_code_file(name, code): - path = os.path.join(private_path, name.lower() + ".py") - file = open(path, "w+") - file.write(code) - file.close() - - - - -bpy_struct_content = ''' -from . fcurve import FCurve - -class bpy_struct: - id_data = ID() - def as_pointer(): - return int() - def driver_add(path, index): - return FCurve() - def driver_remove(path, index): - return bool() - def keyframe_delete(data_path, index, frame, group): - return bool() - def keyframe_insert(data_path, index, frame, group): - return bool() - def path_from_id(property): - return str() - def path_resolve(path, coerce): - return - def property_unsert(property): - return -''' - -class FakeProp: - def __init__(self, identifier): - self.identifier = identifier - def __getattr__(self, name): - if name == "type": return "" - if name == "array_length": return 0 - if name == "srna": return None - if name == "fixed_type": return None - -class FakePointer(FakeProp): - def __init__(self, name, fixed_identifier): - self.identifier = name - self.type = "POINTER" - self.fixed_type = FakeProp(fixed_identifier) - -class FakeSequence(FakeProp): - def __init__(self, name, fixed_identifier): - self.identifier = name - self.type = "COLLECTION" - self.fixed_type = FakeProp(fixed_identifier) - -FP = FakePointer -FS = FakeSequence - -fake_context_properties = [ - FP("active_bone", "EditBone"), - FP("active_pose_bone", "PoseBone"), - FP("active_base", "ObjectBase"), - FP("active_object", "Object"), - FP("object", "Object"), - FP("edit_object", "Object"), - FP("sculpt_object", "Object"), - FP("vertex_paint_object", "Object"), - FP("weight_paint_object", "Object"), - FP("image_paint_object", "Object"), - FP("particle_edit_object", "Object"), - FP("gpencil_data", "GreasePencil"), - FP("gpencil_data_owner", "ID"), - FP("active_operator", "Operator"), - FP("texture_slot", "MaterialTextureSlot"), - FP("world", "World"), - FP("mesh", "Mesh"), - FP("armature", "Armature"), - FP("lattice", "Lattice"), - FP("curve", "Curve"), - FP("meta_ball", "MetaBall"), - FP("lamp", "Lamp"), - FP("speaker", "Speaker"), - FP("camera", "Camera"), - FP("material", "Material"), - FP("material_slot", "MaterialSlot"), - FP("texture", "Texture"), - FP("texture_user", "ID"), - FP("texture_user_property", "Property"), - FP("bone", "Bone"), - FP("particle_system", "ParticleSystem"), - FP("particle_system_editable", "ParticleSystem"), - FP("particle_settings", "ParticleSettings"), - FP("cloth", "ClothModifier"), - FP("soft_body", "SoftBodyModifier"), - FP("fluid", "FluidSimulationModifier"), - FP("smoke", "SmokeModifier"), - FP("collision", "CollisionModifier"), - FP("brush", "Brush"), - FP("dynamic_paint", "DynamicPaintModifier"), - FP("line_style", "FreestyleLineStyle"), - FP("edit_image", "Image"), - FP("edit_mask", "Mask"), - FP("active_node", "Node"), - FP("edit_text", "Text"), - FP("edit_movieclip", "MovieClip"), - FS("visible_objects", "Object"), - FS("visible_bases", "ObjectBase"), - FS("selectable_objects", "Object"), - FS("selectable_bases", "ObjectBase"), - FS("selected_objects", "Object"), - FS("selected_bases", "ObjectBase"), - FS("selected_editable_objects", "Object"), - FS("selected_editable_bases", "ObjectBase"), - FS("visible_bones", "EditBone"), - FS("editable_bones", "EditBone"), - FS("selected_bones", "EditBone"), - FS("selected_editable_bones", "EditBone"), - FS("visible_pose_bones", "PoseBone"), - FS("selected_pose_bones", "PoseBone"), - FS("sequences", "Sequence"), - FS("selected_sequences", "Sequence"), - FS("selected_editable_sequences", "Sequence"), - FS("visible_gpencil_layers", "GPencilLayer"), - FS("editable_gpencil_layers", "GPencilLayer"), - FS("editable_gpencil_strokes", "GPencilStroke"), - FS("active_gpencil_layer", "GPencilLayer"), - FS("active_gpencil_frame", "GPencilLayer"), - FS("selected_nodes", "Node") ] +import bpy +import os +import sys +import shutil +import inspect +import textwrap +from ... settings import get_preferences +from . rna_utils import get_readable_property_type + +fake_package_name = "_bpy_fake" +top_directory = os.path.join(os.path.dirname(__file__), "dynamic") +directory = os.path.join(top_directory, fake_package_name) +private_path = os.path.join(directory, "__private__") + +sys.path.append(top_directory) + +docstring_width = 70 +use_quote_marks = False + + +class GenerateFakeBPY(bpy.types.Operator): + bl_idname = "code_autocomplete.regenerate_fake_bpy" + bl_label = "Generate Fake BPY" + bl_description = "Regenerate the fake bpy module that the jedi autocompletion needs" + bl_options = {"REGISTER"} + + def execute(self, context): + regenerate_fake_bpy() + return {"FINISHED"} + +def fake_bpy_module_exists(): + return os.path.exists(private_path) + +def regenerate_fake_bpy(): + remove_old_fake() + generate_fake_bpy() + +def remove_old_fake(): + if os.path.exists(directory): + shutil.rmtree(directory, ignore_errors = True) + +def generate_fake_bpy(): + try: os.makedirs(directory) + except: pass + create_init() + create_private_subdirectory() + +def create_init(): + path = os.path.join(directory, "__init__.py") + file = open(path, "w+") + file.write(init_content) + file.close() + +init_content = ''' +from . __private__.context import Context as context +from . __private__.blenddata import BlendData as data +''' + +def create_private_subdirectory(): + os.makedirs(private_path) + open(os.path.join(private_path, "__init__.py"), "a").close() + write_code_file("__init__", "") + write_code_file("bpy_struct", bpy_struct_content) + generate_code_files() + + +collection_types = {} +def generate_code_files(create_all = False): + types_to_generate = {"Context", "Panel"} + generated_types = set() + + while len(types_to_generate) > 0: + name = types_to_generate.pop() + generated_types.add(name) + type = getattr(bpy.types, name) + code, dependencies = get_code_and_dependencies(name, type) + write_code_file(name, code) + types_to_generate.update([d for d in dependencies if d not in generated_types]) + + if len(types_to_generate) == 0 and create_all: + bpy_types = [(name, type) for name, type in inspect.getmembers(bpy.types) if "." not in name] + for name, type in bpy_types: + if name not in generated_types: + types_to_generate.add(name) + + +def get_code_and_dependencies(name, type): + dependencies = get_dependencies(name, type) + + lines = [] + lines.extend(get_import_code_lines(dependencies)) + lines.append("class {}({}):".format(name, "" if name == "Context" else "bpy_struct")) + lines.extend(get_property_code_lines(type)) + lines.extend(get_function_code_lines(name, type)) + + return "\n".join(lines), dependencies + +def get_import_code_lines(dependencies): + return ["from . {} import {}".format(d.lower(), d) for d in dependencies] + ["from . bpy_struct import bpy_struct", "import mathutils", ""] + +def get_property_code_lines(type): + lines = [] + for property in get_type_properties(type): + lines.extend(get_property_definition_code_lines(property)) + return lines + +def get_property_definition_code_lines(property): + lines = [] + lines.append(" @property") + lines.append(" def {}(self):".format(property.identifier)) + lines.extend(get_property_docstring_lines(property, docstring_width)) + lines.append(" return {}".format(get_property_declaration(property))) + return lines + +def get_function_code_lines(name, type): + lines = [] + for function in type.bl_rna.functions: + lines.append(" def {}({}):".format(function.identifier, get_function_parameter_list(function))) + lines.extend(get_function_docstring_lines(function, docstring_width)) + lines.append(" return {}".format(get_function_return_list(function))) + + global collection_types + if name in collection_types: + subtype = collection_types[name] + lines.append(" def get(key): return {}()".format(subtype)) + lines.append(" def __getitem__(key): return {}()".format(subtype)) + lines.append(" def __iter__(key): yield {}()".format(subtype)) + + return lines + +def get_property_docstring_lines(property, width = 70, indent = 8): + lines = get_property_description_lines(property, width) + lines.extend(get_enum_item_lines(property, width)) + return make_docstring_from_lines(lines, indent) + +def get_function_docstring_lines(function, width = 70, indent = 8): + lines = get_function_description_lines(function, width) + parameter_lines = get_parameter_lines(function, width) + lines.extend(parameter_lines) + return make_docstring_from_lines(lines, indent) + +def get_parameter_lines(function, width): + lines = [] + params = [p for p in function.parameters if not p.is_output] + if len(params) > 0: + lines.append("") + lines.append("Parameter:") + lines.extend(get_parameter_list_lines(params, width)) + returns = [p for p in function.parameters if p.is_output] + if len(returns) > 0: + lines.append("") + lines.append("Returns:") + lines.extend(get_parameter_list_lines(returns, width)) + return lines + +def get_parameter_list_lines(params, width): + lines = [] + for param in params: + lines.append("{}:".format(param.identifier)) + description_lines = get_property_description_lines(param, width) + amount = len(description_lines) + if amount == 0: lines[-1] += " " + elif amount == 1: lines[-1] += " " + description_lines[0] + else: + indent_lines(description_lines, 2) + lines.extend(description_lines) + indent_lines(lines, 2) + return lines + +def get_property_description_lines(property, width): + type = "({})".format(get_readable_property_type(property)) + if property.description in (None, ""): return [type] + return textwrap.wrap(type + " " + property.description, width) + +def get_function_description_lines(function, width): + if function.description in (None, ""): return [] + return textwrap.wrap(function.description, width) + +def get_enum_item_lines(property, width): + if getattr(property, "enum_items", None) is None: return [] + items = property.enum_items + if len(items) == 0: return [] + quote_mark = "'" if use_quote_marks else "" + item_string = "["+ ", ".join(quote_mark + item.identifier + quote_mark for item in items) +"]" + return [""] + textwrap.wrap(item_string, width) + +def make_docstring_from_lines(lines, indent = 8): + if len(lines) == 0: return [] + lines[0] = "'''" + lines[0] + lines[-1] += "'''" + indent_lines(lines, indent) + return lines + +def indent_lines(lines, indent = 4): + spaces = " " * indent + for i in range(len(lines)): + lines[i] = spaces + lines[i] + +def get_dependencies(name, type): + def find_property_dependency(property): + if property.type == "POINTER": + dependencies.add(property.fixed_type.identifier) + if property.type == "COLLECTION": + if property.srna is None: dependencies.add(property.fixed_type.identifier) + else: dependencies.add(property.srna.identifier) + + dependencies = set() + if name in collection_types: + dependencies.add(collection_types[name]) + for property in get_type_properties(type): + find_property_dependency(property) + for function in type.bl_rna.functions: + for parameter in function.parameters: + find_property_dependency(parameter) + return dependencies + +def get_type_properties(type): + fakes = [] + if type.bl_rna.identifier == "Context": + fakes = fake_context_properties + return list(type.bl_rna.properties) + fakes + +def get_function_parameter_list(function): + parameters = ["self"] + [parameter.identifier for parameter in function.parameters if not parameter.is_output] + return ", ".join(parameters) + +def get_function_return_list(function): + returns = [parameter for parameter in function.parameters if parameter.is_output] + return ", ".join([get_property_declaration(parameter) for parameter in returns]) + +def get_property_declaration(property): + global collection_types + if property.type == "BOOLEAN": return "bool()" + if property.type == "INT": return "int()" + if property.type in ("STRING", "ENUM"): return "str()" + if property.type == "COLLECTION": + if property.srna is None: return "({}(),)".format(property.fixed_type.identifier) + else: + collection_types[property.srna.identifier] = property.fixed_type.identifier + return property.srna.identifier + "()" + if property.type == "FLOAT": + if property.array_length <= 1: return "float()" + if property.array_length in (2, 3): return "mathutils.Vector()" + if property.array_length == 16: return "mathutils.Matrix()" + if property.type == "POINTER": + return property.fixed_type.identifier + "()" + return "''" + +def write_code_file(name, code): + path = os.path.join(private_path, name.lower() + ".py") + file = open(path, "w+") + file.write(code) + file.close() + + + + +bpy_struct_content = ''' +from . fcurve import FCurve + +class bpy_struct: + id_data = ID() + def as_pointer(): + return int() + def driver_add(path, index): + return FCurve() + def driver_remove(path, index): + return bool() + def keyframe_delete(data_path, index, frame, group): + return bool() + def keyframe_insert(data_path, index, frame, group): + return bool() + def path_from_id(property): + return str() + def path_resolve(path, coerce): + return + def property_unsert(property): + return +''' + +class FakeProp: + def __init__(self, identifier): + self.identifier = identifier + def __getattr__(self, name): + if name == "type": return "" + if name == "array_length": return 0 + if name == "srna": return None + if name == "fixed_type": return None + +class FakePointer(FakeProp): + def __init__(self, name, fixed_identifier): + self.identifier = name + self.type = "POINTER" + self.fixed_type = FakeProp(fixed_identifier) + +class FakeSequence(FakeProp): + def __init__(self, name, fixed_identifier): + self.identifier = name + self.type = "COLLECTION" + self.fixed_type = FakeProp(fixed_identifier) + +FP = FakePointer +FS = FakeSequence + +fake_context_properties = [ + FP("active_bone", "EditBone"), + FP("active_pose_bone", "PoseBone"), + FP("active_base", "ObjectBase"), + FP("active_object", "Object"), + FP("object", "Object"), + FP("edit_object", "Object"), + FP("sculpt_object", "Object"), + FP("vertex_paint_object", "Object"), + FP("weight_paint_object", "Object"), + FP("image_paint_object", "Object"), + FP("particle_edit_object", "Object"), + FP("gpencil_data", "GreasePencil"), + FP("gpencil_data_owner", "ID"), + FP("active_operator", "Operator"), + FP("texture_slot", "MaterialTextureSlot"), + FP("world", "World"), + FP("mesh", "Mesh"), + FP("armature", "Armature"), + FP("lattice", "Lattice"), + FP("curve", "Curve"), + FP("meta_ball", "MetaBall"), + FP("light", "Light"), + FP("speaker", "Speaker"), + FP("camera", "Camera"), + FP("material", "Material"), + FP("material_slot", "MaterialSlot"), + FP("texture", "Texture"), + FP("texture_user", "ID"), + FP("texture_user_property", "Property"), + FP("bone", "Bone"), + FP("particle_system", "ParticleSystem"), + FP("particle_system_editable", "ParticleSystem"), + FP("particle_settings", "ParticleSettings"), + FP("cloth", "ClothModifier"), + FP("soft_body", "SoftBodyModifier"), + FP("fluid", "FluidSimulationModifier"), + FP("smoke", "SmokeModifier"), + FP("collision", "CollisionModifier"), + FP("brush", "Brush"), + FP("dynamic_paint", "DynamicPaintModifier"), + FP("line_style", "FreestyleLineStyle"), + FP("edit_image", "Image"), + FP("edit_mask", "Mask"), + FP("active_node", "Node"), + FP("edit_text", "Text"), + FP("edit_movieclip", "MovieClip"), + FS("visible_objects", "Object"), + FS("visible_bases", "ObjectBase"), + FS("selectable_objects", "Object"), + FS("selectable_bases", "ObjectBase"), + FS("selected_objects", "Object"), + FS("selected_bases", "ObjectBase"), + FS("selected_editable_objects", "Object"), + FS("selected_editable_bases", "ObjectBase"), + FS("visible_bones", "EditBone"), + FS("editable_bones", "EditBone"), + FS("selected_bones", "EditBone"), + FS("selected_editable_bones", "EditBone"), + FS("visible_pose_bones", "PoseBone"), + FS("selected_pose_bones", "PoseBone"), + FS("sequences", "Sequence"), + FS("selected_sequences", "Sequence"), + FS("selected_editable_sequences", "Sequence"), + FS("visible_gpencil_layers", "GPencilLayer"), + FS("editable_gpencil_layers", "GPencilLayer"), + FS("editable_gpencil_strokes", "GPencilStroke"), + FS("active_gpencil_layer", "GPencilLayer"), + FS("active_gpencil_frame", "GPencilLayer"), + FS("selected_nodes", "Node") ] diff --git a/autocompletion/suggestions/interface.py b/autocompletion/suggestions/interface.py index b959115..a9e2d62 100644 --- a/autocompletion/suggestions/interface.py +++ b/autocompletion/suggestions/interface.py @@ -1,17 +1,17 @@ -class Provider: - def complete(self, text_block): - return [] - -class Completion: - def __getattr__(self, name): - if name == "name": - return "" - if name == "description": - return "" - if name == "type": - return "UNKNOWN" - if name == "finished_statement": - return True - - def insert(self, text_block): - pass +class Provider: + def complete(self, text_block): + return [] + +class Completion: + def __getattr__(self, name): + if name == "name": + return "" + if name == "description": + return "" + if name == "type": + return "UNKNOWN" + if name == "finished_statement": + return True + + def insert(self, text_block): + pass diff --git a/autocompletion/suggestions/jedi_completion.py b/autocompletion/suggestions/jedi_completion.py index 95341e2..f7df1d8 100644 --- a/autocompletion/suggestions/jedi_completion.py +++ b/autocompletion/suggestions/jedi_completion.py @@ -1,71 +1,71 @@ -import re -from . interface import Provider, Completion -from . generate_fake_bpy import fake_package_name - -try: import jedi -except: print("jedi library not found") - -def jedi_module_found(): - return "jedi" in globals() - -class JediCompletion(Completion): - def __init__(self, suggestion): - self.name = suggestion.name - self.description = suggestion.docstring() - if suggestion.type == "function": self.type = "FUNCTION" - if suggestion.type == "class": self.type = "CLASS" - if suggestion.type == "param": - self.type = "PARAMETER" - - def insert(self, text_block): - text_block.replace_current_word(self.name) - - -class JediCompletionProvider(Provider): - def complete(self, text_block): - source, line_index, character_index, filepath = get_completion_source(text_block) - - # jedi raises an error when trying to complete parts of the bpy module - # or when the jedi module is not found - try: - script = jedi.Script(source, line_index, character_index, filepath) - completions = script.completions() - ignored_words = (fake_package_name, "_bpy_path") - return [JediCompletion(c) for c in completions if c.name not in ignored_words] - except: - return [] - -def get_completion_source(text_block): - new_lines = [] - corrected_line_number = 0 - for line_number, line in enumerate(text_block.iter_lines()): - new_lines.extend(list(iter_corrected_lines_from_line(line))) - if line_number == text_block.current_line_index: - corrected_line_number = len(new_lines) - text = "\n".join(new_lines) - - filepath = text_block.filepath - character_index = len(text_block.text_before_cursor) - return text, corrected_line_number, character_index, filepath - -def iter_corrected_lines_from_line(line): - if "bpy" in line: - line = line.replace("import bpy", "import {} as bpy".format(fake_package_name)) - line = line.replace("from bpy", "from {}".format(fake_package_name)) - yield line - - if "def draw(self, context):" in line: - indentation = line.index("d") + 4 - yield " " * indentation + "context = bpy.__private__.context.Context()" - yield " " * indentation + "self.layout = bpy.__private__.uilayout.UILayout()" - - if "def execute(self, context):" in line or \ - "def poll(cls, context):" in line: - indentation = line.index("d") + 4 - yield " " * indentation + "context = bpy.__private__.context.Context()" - - if "def invoke(self, context, event):" in line or \ - "def modal(self, context, event):" in line: - indentation = line.index("d") + 4 - yield " " * indentation + "context = bpy.__private__.context.Context()" - yield " " * indentation + "event = bpy.__private__.event.Event()" +import re +from . interface import Provider, Completion +from . generate_fake_bpy import fake_package_name + +try: import jedi +except: print("jedi library not found") + +def jedi_module_found(): + return "jedi" in globals() + +class JediCompletion(Completion): + def __init__(self, suggestion): + self.name = suggestion.name + self.description = suggestion.docstring() + if suggestion.type == "function": self.type = "FUNCTION" + if suggestion.type == "class": self.type = "CLASS" + if suggestion.type == "param": + self.type = "PARAMETER" + + def insert(self, text_block): + text_block.replace_current_word(self.name) + + +class JediCompletionProvider(Provider): + def complete(self, text_block): + source, line_index, character_index, filepath = get_completion_source(text_block) + + # jedi raises an error when trying to complete parts of the bpy module + # or when the jedi module is not found + try: + script = jedi.Script(source, line_index, character_index, filepath) + completions = script.completions() + ignored_words = (fake_package_name, "_bpy_path") + return [JediCompletion(c) for c in completions if c.name not in ignored_words] + except: + return [] + +def get_completion_source(text_block): + new_lines = [] + corrected_line_number = 0 + for line_number, line in enumerate(text_block.iter_lines()): + new_lines.extend(list(iter_corrected_lines_from_line(line))) + if line_number == text_block.current_line_index: + corrected_line_number = len(new_lines) + text = "\n".join(new_lines) + + filepath = text_block.filepath + character_index = len(text_block.text_before_cursor) + return text, corrected_line_number, character_index, filepath + +def iter_corrected_lines_from_line(line): + if "bpy" in line: + line = line.replace("import bpy", "import {} as bpy".format(fake_package_name)) + line = line.replace("from bpy", "from {}".format(fake_package_name)) + yield line + + if "def draw(self, context):" in line: + indentation = line.index("d") + 4 + yield " " * indentation + "context = bpy.__private__.context.Context()" + yield " " * indentation + "self.layout = bpy.__private__.uilayout.UILayout()" + + if "def execute(self, context):" in line or \ + "def poll(cls, context):" in line: + indentation = line.index("d") + 4 + yield " " * indentation + "context = bpy.__private__.context.Context()" + + if "def invoke(self, context, event):" in line or \ + "def modal(self, context, event):" in line: + indentation = line.index("d") + 4 + yield " " * indentation + "context = bpy.__private__.context.Context()" + yield " " * indentation + "event = bpy.__private__.event.Event()" diff --git a/autocompletion/suggestions/operator_completion.py b/autocompletion/suggestions/operator_completion.py index 84bb645..0264324 100644 --- a/autocompletion/suggestions/operator_completion.py +++ b/autocompletion/suggestions/operator_completion.py @@ -1,125 +1,125 @@ -import re -import bpy -import textwrap -from . interface import Provider, Completion -from . rna_utils import (get_enum_items, - get_property_default, - get_enum_items_string, - get_operator_parameters, - make_operator_description, - get_readable_property_type) - - -class WordCompletion(Completion): - def __init__(self, word): - self.name = word - - def insert(self, text_block): - text_block.replace_current_word(self.name) - -class OperatorCompletion(Completion): - def __init__(self, category, operator_name): - self.operator = getattr(category, operator_name) - self.name = operator_name - - def insert(self, text_block): - text_block.replace_current_word(self.name) - - @property - def description(self): - return make_operator_description(self.operator) - -class ParameterCompletion(Completion): - def __init__(self, parameter): - self.name = parameter.identifier + " = " - self.type = "OPERATOR_PARAMETER" - self.description = "{} ({}) = {}\n\n{}\n{}".format( - parameter.name, - get_readable_property_type(parameter), - get_property_default(parameter), - parameter.description, - get_enum_items_string(parameter, 70)) - - def insert(self, text_block): - text_block.replace_current_word(self.name) - - -class OperatorCompletionProvider(Provider): - def complete(self, text_block): - current_word = text_block.current_word - parents = text_block.parents_of_current_word - operator = get_current_operator(text_block) - - if parents[:1] == ["bpy"]: - if len(parents) == 1 and "ops".startswith(current_word): - return [WordCompletion("ops")] - - operator = get_current_operator(text_block) - if operator is not None: - return list(iter_operator_inner_completions(operator, text_block)) - - completions = [] - completions.extend(iter_operator_completion_after_pattern(text_block, "bpy\.ops\.")) - completions.extend(iter_operator_completion_after_pattern(text_block, "\.operator\((\"|\')")) - completions.extend(iter_operator_completion_after_pattern(text_block, "keymap_items\.new\((\"|\')")) - return completions - -# pattern#text#.#move# -def iter_operator_completion_after_pattern(text_block, pattern = ""): - operator_start = text_block.get_current_text_after_pattern(pattern) - if operator_start is None: return - if "." not in operator_start: - yield from get_category_completions(operator_start) - else: - category, operator_name_start = operator_start.split(".", maxsplit = 1)[:2] - yield from iter_operator_completions(operator_name_start, category_name = category) - -# bpy.ops.#text# -def get_category_completions(current_word): - return [WordCompletion(category) for category in dir(bpy.ops) if category.startswith(current_word)] - -# bpy.ops.text.#move# -def iter_operator_completions(current_word, category_name): - category = getattr(bpy.ops, category_name, None) - if category is None: return - for operator_name in dir(category): - if current_word not in operator_name: continue - yield OperatorCompletion(category, operator_name) - -def get_current_operator(text_block): - function_path = text_block.get_current_function_path() - if function_path is None: return None - - parts = function_path.split(".") - if len(parts) != 4: return None - if not function_path.startswith("bpy.ops"): return None - category_name, operator_name = parts[2:] - category = getattr(bpy.ops, category_name, None) - if category is None: return None - operator = getattr(category, operator_name, None) - return operator - -# bpy.ops.text.move(#type# = "#NEXT_CHARACTER#") -def iter_operator_inner_completions(operator, text_block): - yield from iter_parameter_completions(operator, text_block) - yield from iter_enum_parameter_completions(operator, text_block) - -# bpy.ops.text.move(#type# = "NEXT_CHARACTER") -def iter_parameter_completions(operator, text_block): - word_start = text_block.get_current_text_after_pattern("[\(\,]\s*") - if word_start is None: return - for parameter in get_operator_parameters(operator): - if word_start in parameter.identifier: - yield ParameterCompletion(parameter) - -# bpy.ops.text.move(type = "#NEXT_CHARACTER#") -def iter_enum_parameter_completions(operator, text_block): - for parameter in get_operator_parameters(operator): - pattern = parameter.identifier + "\s*=\s*(\"|\')" - word_start = text_block.get_current_text_after_pattern(pattern) - if word_start is None: continue - word_start = word_start.upper() - for enum_item in get_enum_items(parameter): - if word_start in enum_item.upper(): - completion = WordCompletion(enum_item) - yield completion +import re +import bpy +import textwrap +from . interface import Provider, Completion +from . rna_utils import (get_enum_items, + get_property_default, + get_enum_items_string, + get_operator_parameters, + make_operator_description, + get_readable_property_type) + + +class WordCompletion(Completion): + def __init__(self, word): + self.name = word + + def insert(self, text_block): + text_block.replace_current_word(self.name) + +class OperatorCompletion(Completion): + def __init__(self, category, operator_name): + self.operator = getattr(category, operator_name) + self.name = operator_name + + def insert(self, text_block): + text_block.replace_current_word(self.name) + + @property + def description(self): + return make_operator_description(self.operator) + +class ParameterCompletion(Completion): + def __init__(self, parameter): + self.name = parameter.identifier + " = " + self.type = "OPERATOR_PARAMETER" + self.description = "{} ({}) = {}\n\n{}\n{}".format( + parameter.name, + get_readable_property_type(parameter), + get_property_default(parameter), + parameter.description, + get_enum_items_string(parameter, 70)) + + def insert(self, text_block): + text_block.replace_current_word(self.name) + + +class OperatorCompletionProvider(Provider): + def complete(self, text_block): + current_word = text_block.current_word + parents = text_block.parents_of_current_word + operator = get_current_operator(text_block) + + if parents[:1] == ["bpy"]: + if len(parents) == 1 and "ops".startswith(current_word): + return [WordCompletion("ops")] + + operator = get_current_operator(text_block) + if operator is not None: + return list(iter_operator_inner_completions(operator, text_block)) + + completions = [] + completions.extend(iter_operator_completion_after_pattern(text_block, "bpy\.ops\.")) + completions.extend(iter_operator_completion_after_pattern(text_block, "\.operator\((\"|\')")) + completions.extend(iter_operator_completion_after_pattern(text_block, "keymap_items\.new\((\"|\')")) + return completions + +# pattern#text#.#move# +def iter_operator_completion_after_pattern(text_block, pattern = ""): + operator_start = text_block.get_current_text_after_pattern(pattern) + if operator_start is None: return + if "." not in operator_start: + yield from get_category_completions(operator_start) + else: + category, operator_name_start = operator_start.split(".", maxsplit = 1)[:2] + yield from iter_operator_completions(operator_name_start, category_name = category) + +# bpy.ops.#text# +def get_category_completions(current_word): + return [WordCompletion(category) for category in dir(bpy.ops) if category.startswith(current_word)] + +# bpy.ops.text.#move# +def iter_operator_completions(current_word, category_name): + category = getattr(bpy.ops, category_name, None) + if category is None: return + for operator_name in dir(category): + if current_word not in operator_name: continue + yield OperatorCompletion(category, operator_name) + +def get_current_operator(text_block): + function_path = text_block.get_current_function_path() + if function_path is None: return None + + parts = function_path.split(".") + if len(parts) != 4: return None + if not function_path.startswith("bpy.ops"): return None + category_name, operator_name = parts[2:] + category = getattr(bpy.ops, category_name, None) + if category is None: return None + operator = getattr(category, operator_name, None) + return operator + +# bpy.ops.text.move(#type# = "#NEXT_CHARACTER#") +def iter_operator_inner_completions(operator, text_block): + yield from iter_parameter_completions(operator, text_block) + yield from iter_enum_parameter_completions(operator, text_block) + +# bpy.ops.text.move(#type# = "NEXT_CHARACTER") +def iter_parameter_completions(operator, text_block): + word_start = text_block.get_current_text_after_pattern("[\(\,]\s*") + if word_start is None: return + for parameter in get_operator_parameters(operator): + if word_start in parameter.identifier: + yield ParameterCompletion(parameter) + +# bpy.ops.text.move(type = "#NEXT_CHARACTER#") +def iter_enum_parameter_completions(operator, text_block): + for parameter in get_operator_parameters(operator): + pattern = parameter.identifier + "\s*=\s*(\"|\')" + word_start = text_block.get_current_text_after_pattern(pattern) + if word_start is None: continue + word_start = word_start.upper() + for enum_item in get_enum_items(parameter): + if word_start in enum_item.upper(): + completion = WordCompletion(enum_item) + yield completion diff --git a/autocompletion/suggestions/rna_utils.py b/autocompletion/suggestions/rna_utils.py index 49ec108..504fd41 100644 --- a/autocompletion/suggestions/rna_utils.py +++ b/autocompletion/suggestions/rna_utils.py @@ -1,70 +1,70 @@ -import textwrap - -def join_lines(function): - def wrapper(*args, **kwargs): - return "\n".join(list(function(*args, **kwargs))) - return wrapper - -def indent(lines, indentation = 4): - prefix = " " * indentation - if isinstance(lines, str): lines = lines.split("\n") - return [prefix + line for line in lines] - -@join_lines -def make_operator_description(operator, width = 70): - rna = operator.get_rna().bl_rna - yield rna.name - yield "" - yield from textwrap.wrap(rna.description, width) - yield "" - - parameters = get_operator_parameters(operator) - if len(parameters) == 0: return - - yield "Parameters:" - for parameter in parameters: - yield from indent(make_property_description(parameter, width - 3), indentation = 3) - -@join_lines -def make_property_description(property, width = 70): - identifier = "{}: ({})".format(property.identifier, get_readable_property_type(property)) - description = property.description - - if len(identifier + description) + 2 > width: - yield identifier - yield from indent(textwrap.wrap(description, width - 3), indentation = 3) - else: - yield "{} {}".format(identifier, description) - -def get_operator_parameters(operator): - rna = operator.get_rna().bl_rna - return [prop for prop in rna.properties if prop.identifier != "rna_type"] - -def get_enum_items_string(property, width = 70): - items = get_enum_items(property) - if len(items) == 0: return "" - lines = textwrap.wrap(str(items), width) - return "\n".join(lines) - -def get_enum_items(property): - return [item.identifier for item in getattr(property, "enum_items", [])] - -def get_property_default(property): - if len(getattr(property, "default_array", [])) > 0: - return repr(property.default_array[:]) - return repr(property.default) - -def get_readable_property_type(property): - suffix = "[{}]".format(property.array_length) if getattr(property, "array_length", 1) > 1 else "" - if property.type == "BOOLEAN": return "Boolean" + suffix - if property.type == "INT": return "Integer" + suffix - if property.type == "STRING": return "String" + suffix - if property.type == "COLLECTION": return "Sequence of " + property.fixed_type.identifier - if property.type == "FLOAT": - if property.array_length <= 1: return "Float" - if property.array_length == 2: return "Vector 2D" - if property.array_length == 3: return "Vector 3D" - if property.array_length == 16: return "Matrix" - return "Float[{}]".format(property.array_length) - if property.type == "POINTER": return property.fixed_type.identifier - if property.type == "ENUM": return "Enum" +import textwrap + +def join_lines(function): + def wrapper(*args, **kwargs): + return "\n".join(list(function(*args, **kwargs))) + return wrapper + +def indent(lines, indentation = 4): + prefix = " " * indentation + if isinstance(lines, str): lines = lines.split("\n") + return [prefix + line for line in lines] + +@join_lines +def make_operator_description(operator, width = 70): + rna = operator.get_rna_type().bl_rna + yield rna.name + yield "" + yield from textwrap.wrap(rna.description, width) + yield "" + + parameters = get_operator_parameters(operator) + if len(parameters) == 0: return + + yield "Parameters:" + for parameter in parameters: + yield from indent(make_property_description(parameter, width - 3), indentation = 3) + +@join_lines +def make_property_description(property, width = 70): + identifier = "{}: ({})".format(property.identifier, get_readable_property_type(property)) + description = property.description + + if len(identifier + description) + 2 > width: + yield identifier + yield from indent(textwrap.wrap(description, width - 3), indentation = 3) + else: + yield "{} {}".format(identifier, description) + +def get_operator_parameters(operator): + rna = operator.get_rna_type().bl_rna + return [prop for prop in rna.properties if prop.identifier != "rna_type"] + +def get_enum_items_string(property, width = 70): + items = get_enum_items(property) + if len(items) == 0: return "" + lines = textwrap.wrap(str(items), width) + return "\n".join(lines) + +def get_enum_items(property): + return [item.identifier for item in getattr(property, "enum_items", [])] + +def get_property_default(property): + if len(getattr(property, "default_array", [])) > 0: + return repr(property.default_array[:]) + return repr(property.default) + +def get_readable_property_type(property): + suffix = "[{}]".format(property.array_length) if getattr(property, "array_length", 1) > 1 else "" + if property.type == "BOOLEAN": return "Boolean" + suffix + if property.type == "INT": return "Integer" + suffix + if property.type == "STRING": return "String" + suffix + if property.type == "COLLECTION": return "Sequence of " + property.fixed_type.identifier + if property.type == "FLOAT": + if property.array_length <= 1: return "Float" + if property.array_length == 2: return "Vector 2D" + if property.array_length == 3: return "Vector 3D" + if property.array_length == 16: return "Matrix" + return "Float[{}]".format(property.array_length) + if property.type == "POINTER": return property.fixed_type.identifier + if property.type == "ENUM": return "Enum" diff --git a/autocompletion/suggestions/static_pattern_completion.py b/autocompletion/suggestions/static_pattern_completion.py index 98f4aab..d9611ec 100644 --- a/autocompletion/suggestions/static_pattern_completion.py +++ b/autocompletion/suggestions/static_pattern_completion.py @@ -1,54 +1,54 @@ -import re -import bpy -from . interface import Provider, Completion - - -class WordCompletion(Completion): - def __init__(self, word): - self.name = word - - def insert(self, text_block): - text_block.replace_current_word(self.name) - -class StaticPatternProvider(Provider): - def complete(self, text_block): - return list(iter_static_completions(text_block)) - -def iter_static_completions(text_block): - for pattern, words in suggestions.items(): - word_start = text_block.get_current_text_after_pattern(pattern) - if word_start is None: continue - word_start = word_start.upper() - - secondaryCompletions = [] - for word in words: - if word.upper().startswith(word_start): - yield WordCompletion(word) - elif word_start in word.upper(): - secondaryCompletions.append(WordCompletion(word)) - yield from secondaryCompletions - -space_properties = bpy.types.Space.bl_rna.properties -space_types = sorted([item.identifier for item in space_properties["type"].enum_items]) - -region_types = ["WINDOW", "HEADER", "CHANNELS", "TEMPORARY", "UI", "TOOLS", "TOOL_PROPS", "PREVIEW"] - -handlers = [name for name in dir(bpy.app.handlers) if not name.startswith("_")] - -event_properties = bpy.types.Event.bl_rna.properties -event_types = sorted([item.identifier for item in event_properties["type"].enum_items]) -event_values = sorted([item.identifier for item in event_properties["value"].enum_items]) - -suggestions = { - "bl_space_type\s*=\s*(\"|\')" : space_types, - "bl_region_type\s*=\s*(\"|\')" : region_types, - "bl_options\s*=.*(,|{)\s*(\"|\')" : ["REGISTER", "UNDO", "BLOCKING", "GRAB_POINTER", "PRESET", "INTERNAL", "DEFAULT_CLOSED", "HIDE_HEADER"], - "bl_category\s*=\s*(\"|\')" : ["Tools", "Create", "Relations", "Animation", "Physics", "Grease Pencil"], - "return\s*\{\s*(\"|\')" : ["RUNNING_MODAL", "CANCELLED", "FINISHED", "PASS_THROUGH"], - "bpy\." : ["context", "data", "ops", "types", "utils", "path", "app", "props"], - "bpy\.app\." : ["handlers", "translations"], - "bpy\.app\.handlers\." : handlers, - "bpy\.props\." : [type_name for type_name in dir(bpy.props) if type_name[0] != "_"], - "keymap_items\.new\(.*, type = (\"|\')" : event_types, - "keymap_items\.new\(.*, value = (\"|\')" : event_values -} +import re +import bpy +from . interface import Provider, Completion + + +class WordCompletion(Completion): + def __init__(self, word): + self.name = word + + def insert(self, text_block): + text_block.replace_current_word(self.name) + +class StaticPatternProvider(Provider): + def complete(self, text_block): + return list(iter_static_completions(text_block)) + +def iter_static_completions(text_block): + for pattern, words in suggestions.items(): + word_start = text_block.get_current_text_after_pattern(pattern) + if word_start is None: continue + word_start = word_start.upper() + + secondaryCompletions = [] + for word in words: + if word.upper().startswith(word_start): + yield WordCompletion(word) + elif word_start in word.upper(): + secondaryCompletions.append(WordCompletion(word)) + yield from secondaryCompletions + +space_properties = bpy.types.Space.bl_rna.properties +space_types = sorted([item.identifier for item in space_properties["type"].enum_items]) + +region_types = ["WINDOW", "HEADER", "CHANNELS", "TEMPORARY", "UI", "UI", "TOOL_PROPS", "PREVIEW"] + +handlers = [name for name in dir(bpy.app.handlers) if not name.startswith("_")] + +event_properties = bpy.types.Event.bl_rna.properties +event_types = sorted([item.identifier for item in event_properties["type"].enum_items]) +event_values = sorted([item.identifier for item in event_properties["value"].enum_items]) + +suggestions = { + "bl_space_type\s*=\s*(\"|\')" : space_types, + "bl_region_type\s*=\s*(\"|\')" : region_types, + "bl_options\s*=.*(,|{)\s*(\"|\')" : ["REGISTER", "UNDO", "BLOCKING", "GRAB_POINTER", "PRESET", "INTERNAL", "DEFAULT_CLOSED", "HIDE_HEADER"], + "bl_category\s*=\s*(\"|\')" : ["Tools", "Create", "Relations", "Animation", "Physics", "Grease Pencil"], + "return\s*\{\s*(\"|\')" : ["RUNNING_MODAL", "CANCELLED", "FINISHED", "PASS_THROUGH"], + "bpy\." : ["context", "data", "ops", "types", "utils", "path", "app", "props"], + "bpy\.app\." : ["handlers", "translations"], + "bpy\.app\.handlers\." : handlers, + "bpy\.props\." : [type_name for type_name in dir(bpy.props) if type_name[0] != "_"], + "keymap_items\.new\(.*, type = (\"|\')" : event_types, + "keymap_items\.new\(.*, value = (\"|\')" : event_values +} diff --git a/autocompletion/suggestions/word_completion.py b/autocompletion/suggestions/word_completion.py index 33f6e9d..e9442cf 100644 --- a/autocompletion/suggestions/word_completion.py +++ b/autocompletion/suggestions/word_completion.py @@ -1,30 +1,30 @@ -import re -from . interface import Provider, Completion - - -class WordCompletion(Completion): - def __init__(self, word): - self.name = word - - def insert(self, text_block): - text_block.replace_current_word(self.name) - - -class WordCompletionProvider(Provider): - def complete(self, text_block): - words = text_block.get_existing_words() - current_word = text_block.current_word - if current_word in words: words.remove(current_word) - words = sort_words(words, current_word) - return [WordCompletion(word) for word in words] - -def sort_words(words, current_word): - current_word = current_word.lower() - best_matches, other_matches = [], [] - words.sort(key = str.lower) - for word in words: - if word.lower().startswith(current_word): - best_matches.append(word) - elif current_word in word.lower(): - other_matches.append(word) - return best_matches + other_matches +import re +from . interface import Provider, Completion + + +class WordCompletion(Completion): + def __init__(self, word): + self.name = word + + def insert(self, text_block): + text_block.replace_current_word(self.name) + + +class WordCompletionProvider(Provider): + def complete(self, text_block): + words = text_block.get_existing_words() + current_word = text_block.current_word + if current_word in words: words.remove(current_word) + words = sort_words(words, current_word) + return [WordCompletion(word) for word in words] + +def sort_words(words, current_word): + current_word = current_word.lower() + best_matches, other_matches = [], [] + words.sort(key = str.lower) + for word in words: + if word.lower().startswith(current_word): + best_matches.append(word) + elif current_word in word.lower(): + other_matches.append(word) + return best_matches + other_matches diff --git a/code_templates/__pycache__/__init__.cpython-37.pyc b/code_templates/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d7a056364f6e65c4a33c8daf32abcaf75133809 GIT binary patch literal 208 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 literal 0 HcmV?d00001 diff --git a/code_templates/__pycache__/insert_keymap.cpython-37.pyc b/code_templates/__pycache__/insert_keymap.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a4e7aa6e3a149f726328dc39740cfc692e49475 GIT binary patch literal 2444 zcmZ`*OK;mo5MDkcQM7C)j+->-BZPaX85BAsJp@4-w2qV3XrUH~lK`xO1J z%CZExDBxWCA8hp0f6{x=%U*l(U+Aebq$FFevjTUQJF|mtzIjk<^Ybo&;r{;Do4_ID zAAFgN8f?CXN4|oI5oRPLz|~BQO~Nc@CuV8|mVx_PVyCsB2Kzc|B=xirG*TyU4Du~u z4r{(6tjWn_5*ZqQ=J3*yh6fo;<94Q<>#Ghqw6<|aQ>Ye^pM!%J(2tMwv& z7Dg%;X9jARdTAq*T&PW+7D=f1S}3_*jLS2s#)lCao{Xt0Kfnnmfx$>%z66&7i1^={d$_B;o??>xD70C`=MA zWMqx$vSF>m^8_CGDNKqSlOt=`HK2v{3g%j5af0uId1*`J{0TLO933;j4_d7q|X z7VdDNZ^YR_zRzhP@T<9CYby8mnRRq zFCpBr+k?6LO;N0eD(pVb!!*uzx@!rLhpgqjjD_gei9|H4z9 zOtMCHXQI_E2D)CsUj!@?nIT5q7~ZG?Qw0$sj1ESazc`Rb)GYZ!A4X)49GN5Y#6b2= zO(+vm@+KBQ3JG0L!a?3s+D49m$0axxH*mU*6WXYzTd>nM6JZ~v=XkEq;6eEm4Wh!Q zMDsCh6j8>pVIV3~+#Hb;>y!W@V;2k3>}coNMrY&2cTd-STR=}Bda+8~X=#s3jihg00DlR0gCFv!r#Lzx(K0a{Ak#61%QNuDY7tgD4&-vm=dbXh$cb@Ej*PgXS=4Q8H#-*1;+llR`V;AZEFn|67# z^SX*$tutJNlA6}zs{e3!%k^MPN|IKb$yI-N9W~F36?Jcyy60!-+P)*uRqaCGqtNu? z1FoAq>!n;kr_;40&bZXBRH0C^AFExx6!nCF(xE7#PlyHk!DUZ2}v>C-6v&CPd6!Hrz653)9_jWhQ=CCPv3^X@^p5 zN0e%p(llqi1otgU-}b^}ENN~fG-hSja$uW;TRhPVu)El~T5EZp`~7C#NFcOjU`usS+bRa_aer&iHpMoi2S~Q#9oa6m%X|ANTq<8t4xba*&Uk+n z$pFF1{dMnE%=;@}c(Mp1PSv0&&tGjkeNFGLulS{r&)`FWG4?7GUJSiWdb?ckb8Hj9 z?XV~yEmrqa-z`15DkUqQ`iSlQ4wbgIbK!vx@S&O^tTKSrQ7Oczv7e58q$R*}9n`xd zYzvMF4KU-%N7U(;24+Z{vNqMP)MwDNqlAZI)pz}QZGl6&iC&c-C-FIE1=e4%up`1^ zH;!bxr{Y9bUp9LS{%Huzn5r?>FalCLKSCac@c*Pt7=gudRk~^CQI+$Lv9ZY(Ob#@^ L7W}T6H^2N36OXiB literal 0 HcmV?d00001 diff --git a/code_templates/__pycache__/insert_keymap_item.cpython-37.pyc b/code_templates/__pycache__/insert_keymap_item.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54ae07cfbed3ed60844f26a10a1df0af007951ea GIT binary patch literal 2130 zcmZuy&2QX96rZujUazxR(v*IP3bBMDbs^eAE1*JvN}2{jrKM^@kyeY0SXS~rQ|!p8{B(IxF<-` zNIbz_utAg44bp9%z+R+(Vu9>Z-P{zov?QST9n5&LfRiU?yYv7#o|CWo~=LnIk>k9kol?%BN0@GrO@<=cAh+aw6W2( zG&2@wKTJm=UIx@p{)V`^bGm&H=e#%wvM>*Kgw#G9Gb-}!ct>d`2g4Bff_iqg%kG>CZ~WGbPe(-2lH8 zFhe_HU*wgx4l>>3r}_Vj$PZwP*DoG^Is6I8qJ|@o!*9!SHLSwn(<02`d}p|liku7C zSt~|4539JyPff$Sz592EDw1(osi8TV8eYnFZNcb~h-!E_`MvT`yY)HdARuK*iMsSU zy=l30eB(_@0e|q!5potUWCw%Cu;W)4K<3GjHL<4j*sx>@yhvFaQJ*@tw@zAv{gz<)7C)yR!-utjb8Jy|(`XyIp%9$_Z<5?R10Hsa?r&Bty z5=ch^;6l+_36r-c&{)2})9(1{TO4R_ zZN0y~@%^LK?qbal7b6I(bBf6&KvRB%;x-B!g!ZAA2hhmleSy`3-Co~9`&gYCA~lw>GjZScD`{}zSC z>`{id<$0d(+2$KWV{es*@-v)lkY`))Hj3l}gsSZb0 zWH{);L3jCsE)sOrKk2@^*>>e$=&ENZDY2WhL-6u)W_Wq-xsTc2*l-OD_qRWP_$6l; zf9aRi$Uu3BJNX`h8O#g~Pd}~DZ0n~TT4T$zOoKTr6WU|PbD(EgF3gOxUUr=Ga;EXv zV0l(}VXy)>j`BwZo;`Csm$}{=Gh0S!?Hsm^M#)s>R}gtdC6~dNpIey5#QKoSN0VTO z_GK*2O+9I;YxOAMLbmyM5@P>0O}M%eq;tMpPWH9k)o`FZ#GPzIaKkg1;aMGcdwXuM(8}vE;dqVm#<363r zxE}))m%O~h6E1fY7ax%27s|jd_h=Bp9Ven=u5yeI=rojS4Q58M@W)u~+W@SG_8^P9 ziaYrXLK?ZGMPN0l6IRh9S6~{yB+d~aY1(CX@sZ-akQH1-YtK7x101uFp)`@G8RWk_ZNnq(-S-mk)=x%)D0N_jkV1h)I5`W#kn}h~!x>D&&C~{1 zAy4b=C;Oc?sWcAB(@L{hX|xZ&!*29Ztm+1q+6vH`12_wM1&!n!_)`;mwPy7RzNu{2 zckAs#xIuR6?MAKDB0Kv{QXvPGX1iYP>{goOpwm3qZ`I0#v_KR1NG<#i&4V;lf)mCi zI(o8n{Sd)T054=@M2|Uw+~>hDz)_qf6Adke{NIMt|Dt6_!_-miew$1{S04}*z$Qt6 z^8QH5$-S+u*=$xGM$>XEhFi-bPqrSEU7gjXb!GR^R0|O0^h8LzgI7dyPDVjLDNiNP zFHRDtha*?$YFV_5jGRw+qVoGFKZ=7oEg12+UVl%isr>y#u|Lq~d9n_XH}mE?uB+Cq G&3^!!vv5TK literal 0 HcmV?d00001 diff --git a/code_templates/__pycache__/insert_menu.cpython-37.pyc b/code_templates/__pycache__/insert_menu.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f88886334e5768a4d0125daecfb79d33660ab0a GIT binary patch literal 1629 zcmc&!O>Y}F5aoVpS8GXj(x3*~v=xjTtfJ7txfDTKM7EVyfUOvDav`=Qs3l{svtK2- zb|ly*=h|yeMK3+|_w>eVPwr3Xsl#2_vC$t;3LJ5UL+ktjPSMUrp9a!4>^2&ncz}D-NTymjyQYz&u zo|P$8e49$HSCe{!sAcfVHU}KNe#8|yrD?*ziiFNmPI8)Y{e{^`i!&}r3>T2IL>-eV zXI#j*5L`A1l9yD#GZ-Y5iGq6;us$mUlW=)!`r_sOXb=7ik*_ z4D)8VuzChlZh_$lTMS|QHGqg6W@DE**yAnkGnaWk+dN>cr!`#f5pJ_K021o3dHsE! zSBuZ$f^kACRm24V!4)4aG04pJN|V|*xpgVRunG`*31B3(&-fc!rAoI=I#KhI#|ASq zOrd7(z?4%k=ja5=d~1uIvM%h}v%!0)>v#n`ejOfDE3;#6XXOG!%;NWm)Z*+J_rlb8a_9wf|!rL2dkN0%&d@`LJem~wF z1mYf?)7_@-CExm9nQ(F^Bz%(=#zreO=OTcBXd7d6JaDxudHP2Cagi(jPHERb3hpt@ zkGT8;iBBNypYxq3(Q8nhj4H6vs4RD>qS4EOW=Vb&ZKvF*rvJPE^-z@*`E@AT8h*VM z$yg+%k`ZOB$Yt~&)|$w*Ze?_f#9=wtK7YsKO7V}N6nP&EvO?sVX$Mxv>RF++xc@;G z!)5vu)Dn>TwF%K7Az8sH&_2-}LVl`fYMKDN_ADg{t2-iqWlCo})je1--V_e+qdVig z$__+PLhH@->WyGXBc)YBXr33UetAiVcnDS2*xfRpnph8vX$xtz7+S9R*=ryxa2LPS zO!Yr#U~^W^henukxDOf~JPG?SZVK*y)8AN%WaE0LuHS6bqS*-RI4Hf(1U=g{Y=6~X zvc0^{T$$2&Q7JfP5{8XepER3`-~P9{K+<=M8rIN5cyn-5-(Xcc02D9@Xq7;c)b0$v zXu@=GEp+^OrIJ(*?|c9Wz;Ftp27c>Cxz!wx;Gp(u;C1i?d-=!tP$D)prZK5y3tKFG^-bF;Qo~_2jIgW}FVgGc`SiCG%!M?*V-L4iKujI?-I? z5WMZ0#BeQQx&>OKC1R1n4+gbK@t*SQ8_g|~GWe{L)KU-a#T4w0*x9XdL@2^j5sf48 z7ZtRbxhl$+tfwMQE~Ct15(?n)f(I_;Nr}*>cUUlrHohj(L6VW{mOV{zBT|v z#bXJL(mua@=-21hflH*$oS1wE+($OGeIDZ^I2As>MCOiPX#k(OAE`A$OLLwiN&e^G%1av zP|z15v1E8)c#Ojd<-cg`CM^5o`}W<@Apo6^ra(unIPPG9NBa>Dyl^tw_NfG_^DqLy zV&O$$&KlimetBoa$IOcbA7MhGkdOX?fRvM`3Lm`#h-Q46l;{gOo(g&e%;8soXnIAn z^3SmKs$SD8`q|ZsaBF5_13;pUlnW3_DhPqsrjU9lsUq~<6#Fs_PH2Gu^EC3)%;kk|w4#P+&XK;krEwEVma)~&Sw+=+Y zhD&@`bW8q>%ZB3PD{#%I;5iHUY#zm@O^J2h+{-eiakt_?o7?W(cj_6&>iLX1ikwiG zo3oeBB`4?nD3)R5z1oV8mxTg?dN>&L20KW-1bJj-T$F}YyU+D_ z5I|{>LZWkD;;Q@bV0Z6u*sZ5%Od~Z9{)ITMP~(LXZM~gGnW8PH-W|5<=PMMpTZ4AD z-|u#E^W~N+518A7e=`5?*uiWfGy7tY7Z{Nl*+eOZVAVxSW_^Aw0voBM9XQ)G6?GsoH0$-f4hfQcw?PV&^ZXdS=;%)cL6-*Cdf8&k}K9nArEy fis36+_;Xo>*t)G>)~^%{-O!81)f?6|>$5)r1W&7n literal 0 HcmV?d00001 diff --git a/code_templates/__pycache__/insert_panel.cpython-37.pyc b/code_templates/__pycache__/insert_panel.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..538beb6d517b79d4bef780ed42615c8f2cc54739 GIT binary patch literal 1276 zcmZuxOOMnz5Vn(aCliJN2^el{G$&d@Ilu)WK+FqhH1w{vc}P)^<&I5HM$QA<&Cnw_ z4L^e$Cw>n%UpbAmzkt)0o$h88%jh`)Y+GY$~?VHeBM1LYA+ z^$Y|<3=5PY<}qL3%FDdU&wPwnzDPhgLr6khCvQIbA4DVF7>XUO1RB>~ubOxdqn`fRq;G64HuE~2U zN(NGvbRsyZX~oSS)*zZ=E=d6a$Z@F;$*g8vs-lrxbqI} zT=R5MP3!FIopa*64JzzqcK9%%4`>&rNyftCb)S7?Lpd8YA%GEa{uflRo^LUq~H%dPx&diQp&3R$*V<&?3eR{8%R>~@xPQ2BX8({^q`{(%=W z&DS9p<#_EyIKmq^#wSfgc5Z$$%#5GNz9C|jDkeW*lJs?!I2+~rBkb>00#WGrLyp!U}U+k`>CMTrw zLy$P}d${eDQ~v@dX5*L^%2+!d&(7?;_p~1O`vE}d2|7Up z%}7LqBixMUPUPfnf}=tez3&&aTMjlE=gP0^Keoq zkoq0uRmL@pxdL-PUFDcwCllMz8e(cNgctycs34IeXyghf@`M|8n(20;on{8jfKxb6 zl?J(dl#~J(uXUM}I1@CC*5hPb(r;SAH?YWgWk_OYTC;Dvj&P{~I!!daq7;?*Xt_C4 z. -''' -""" +import bpy +from bpy.props import * +from datetime import datetime +from .. graphics.utils import getDpiFactor +from . base import InsertTemplateBase, insert_template + +class InsertLicense(bpy.types.Operator, InsertTemplateBase): + bl_idname = "code_autocomplete.insert_license" + bl_label = "Insert License" + bl_description = "" + + #author_name: StringProperty(name = "Name", default = bpy.context.preferences.filepaths.author) + author_mail: StringProperty(name = "eMail", default = "") + + def invoke(self, context, event): + dpiFactor = getDpiFactor() + return context.window_manager.invoke_props_dialog(self, 300 * dpiFactor, 200 * dpiFactor) + + def draw(self, context): + layout = self.layout + #layout.prop(self, "author_name", text = "Name") + layout.prop(self, "author_mail", text = "E-Mail") + + def execute(self, context): + changes = { + #"YOUR_NAME" : self.author_name, + "YOUR_MAIL" : self.author_mail, + "CURRENT_YEAR" : str(datetime.now().year) } + insert_template(license_template, changes) + return {"FINISHED"} + +license_template = """''' +Copyright (C) CURRENT_YEAR YOUR_NAME +YOUR_MAIL + +Created by YOUR_NAME + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +''' +""" diff --git a/code_templates/insert_menu.py b/code_templates/insert_menu.py index 041a8c4..1200da2 100644 --- a/code_templates/insert_menu.py +++ b/code_templates/insert_menu.py @@ -1,43 +1,43 @@ -import bpy -from bpy.props import * -from . base import InsertClassTemplateBase, insert_template -from .. utils.variable_name_conversion import (get_valid_variable_name, - get_lower_case_with_underscores, - get_separated_capitalized_words) - -menu_type_items = [ - ("NORMAL", "Normal", ""), - ("PIE", "Pie", "") ] - -class InsertMenu(bpy.types.Operator, InsertClassTemplateBase): - bl_idname = "code_autocomplete.insert_menu" - bl_label = "Insert Menu" - bl_description = "" - - menu_type: EnumProperty(items = menu_type_items, default = "NORMAL") - - def execute(self, context): - if self.menu_type == "NORMAL": code = menu_template - if self.menu_type == "PIE": code = pie_menu_template - changes = { - "CLASS_NAME" : get_valid_variable_name(self.class_name), - "ID_NAME" : "view3d." + get_lower_case_with_underscores(self.class_name), - "LABEL" : get_separated_capitalized_words(self.class_name) } - insert_template(code, changes) - return {"FINISHED"} - -menu_template = '''class CLASS_NAME(bpy.types.Menu): - bl_idname = "ID_NAME" - bl_label = "LABEL" - - def draw(self, context): - layout = self.layout - ''' - -pie_menu_template = '''class CLASS_NAME(bpy.types.Menu): - bl_idname = "ID_NAME" - bl_label = "LABEL" - - def draw(self, context): - pie = self.layout.menu_pie() - ''' +import bpy +from bpy.props import * +from . base import InsertClassTemplateBase, insert_template +from .. utils.variable_name_conversion import (get_valid_variable_name, + get_lower_case_with_underscores, + get_separated_capitalized_words) + +menu_type_items = [ + ("NORMAL", "Normal", ""), + ("PIE", "Pie", "") ] + +class InsertMenu(bpy.types.Operator, InsertClassTemplateBase): + bl_idname = "code_autocomplete.insert_menu" + bl_label = "Insert Menu" + bl_description = "" + + menu_type: EnumProperty(items = menu_type_items, default = "NORMAL") + + def execute(self, context): + if self.menu_type == "NORMAL": code = menu_template + if self.menu_type == "PIE": code = pie_menu_template + changes = { + "CLASS_NAME" : get_valid_variable_name(self.class_name), + "ID_NAME" : "view3d." + get_lower_case_with_underscores(self.class_name), + "LABEL" : get_separated_capitalized_words(self.class_name) } + insert_template(code, changes) + return {"FINISHED"} + +menu_template = '''class CLASS_NAME(bpy.types.Menu): + bl_idname = "ID_NAME" + bl_label = "LABEL" + + def draw(self, context): + layout = self.layout + ''' + +pie_menu_template = '''class CLASS_NAME(bpy.types.Menu): + bl_idname = "ID_NAME" + bl_label = "LABEL" + + def draw(self, context): + pie = self.layout.menu_pie() + ''' diff --git a/code_templates/insert_operator.py b/code_templates/insert_operator.py index 7a940af..4d6a29b 100644 --- a/code_templates/insert_operator.py +++ b/code_templates/insert_operator.py @@ -1,103 +1,103 @@ -import bpy -from bpy.props import * -from . base import InsertClassTemplateBase, insert_template -from .. utils.variable_name_conversion import (get_valid_variable_name, - get_lower_case_with_underscores, - get_separated_capitalized_words) - -operator_type_items = [ - ("NORMAL", "Normal", ""), - ("MODAL", "Modal", ""), - ("MODAL_DRAW", "Modal Draw", "") ] - -class InsertOperator(bpy.types.Operator, InsertClassTemplateBase): - bl_idname = "code_autocomplete.insert_operator" - bl_label = "Insert Operator" - bl_description = "" - - operator_type: EnumProperty(items = operator_type_items, default = "NORMAL") - - def execute(self, context): - if self.operator_type == "NORMAL": code = operator_template - if self.operator_type == "MODAL": code = modal_operator_template - if self.operator_type == "MODAL_DRAW": code = modal_operator_draw_template - changes = { - "CLASS_NAME" : get_valid_variable_name(self.class_name), - "ID_NAME" : "my_operator." + get_lower_case_with_underscores(self.class_name), - "LABEL" : get_separated_capitalized_words(self.class_name) } - insert_template(code, changes) - return {"FINISHED"} - -operator_template = '''class CLASS_NAME(bpy.types.Operator): - bl_idname = "ID_NAME" - bl_label = "LABEL" - bl_description = "" - bl_options = {"REGISTER"} - - @classmethod - def poll(cls, context): - return True - - def execute(self, context): - return {"FINISHED"} - ''' - -modal_operator_template = '''class CLASS_NAME(bpy.types.Operator): - bl_idname = "ID_NAME" - bl_label = "LABEL" - bl_description = "" - bl_options = {"REGISTER"} - - @classmethod - def poll(cls, context): - return True - - def invoke(self, context, event): - context.window_manager.modal_handler_add(self) - return {"RUNNING_MODAL"} - - def modal(self, context, event): - - if event.type == "LEFTMOUSE": - return {"FINISHED"} - - if event.type in {"RIGHTMOUSE", "ESC"}: - return {"CANCELLED"} - - return {"RUNNING_MODAL"} - ''' - -modal_operator_draw_template = '''class CLASS_NAME(bpy.types.Operator): - bl_idname = "ID_NAME" - bl_label = "LABEL" - bl_description = "" - bl_options = {"REGISTER"} - - @classmethod - def poll(cls, context): - return True - - def invoke(self, context, event): - args = (self, context) - self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL") - context.window_manager.modal_handler_add(self) - return {"RUNNING_MODAL"} - - def modal(self, context, event): - context.area.tag_redraw() - - if event.type == "LEFTMOUSE": - return self.finish() - - if event.type in {"RIGHTMOUSE", "ESC"}: - return self.finish() - - return {"RUNNING_MODAL"} - - def finish(self): - bpy.types.SpaceView3D.draw_handler_remove(self._handle, "WINDOW") - return {"FINISHED"} - - def draw_callback_px(tmp, self, context): - pass - ''' +import bpy +from bpy.props import * +from . base import InsertClassTemplateBase, insert_template +from .. utils.variable_name_conversion import (get_valid_variable_name, + get_lower_case_with_underscores, + get_separated_capitalized_words) + +operator_type_items = [ + ("NORMAL", "Normal", ""), + ("MODAL", "Modal", ""), + ("MODAL_DRAW", "Modal Draw", "") ] + +class InsertOperator(bpy.types.Operator, InsertClassTemplateBase): + bl_idname = "code_autocomplete.insert_operator" + bl_label = "Insert Operator" + bl_description = "" + + operator_type: EnumProperty(items = operator_type_items, default = "NORMAL") + + def execute(self, context): + if self.operator_type == "NORMAL": code = operator_template + if self.operator_type == "MODAL": code = modal_operator_template + if self.operator_type == "MODAL_DRAW": code = modal_operator_draw_template + changes = { + "CLASS_NAME" : get_valid_variable_name(self.class_name), + "ID_NAME" : "my_operator." + get_lower_case_with_underscores(self.class_name), + "LABEL" : get_separated_capitalized_words(self.class_name) } + insert_template(code, changes) + return {"FINISHED"} + +operator_template = '''class CLASS_NAME(bpy.types.Operator): + bl_idname = "ID_NAME" + bl_label = "LABEL" + bl_description = "" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + return {"FINISHED"} + ''' + +modal_operator_template = '''class CLASS_NAME(bpy.types.Operator): + bl_idname = "ID_NAME" + bl_label = "LABEL" + bl_description = "" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return True + + def invoke(self, context, event): + context.window_manager.modal_handler_add(self) + return {"RUNNING_MODAL"} + + def modal(self, context, event): + + if event.type == "LEFTMOUSE": + return {"FINISHED"} + + if event.type in {"RIGHTMOUSE", "ESC"}: + return {"CANCELLED"} + + return {"RUNNING_MODAL"} + ''' + +modal_operator_draw_template = '''class CLASS_NAME(bpy.types.Operator): + bl_idname = "ID_NAME" + bl_label = "LABEL" + bl_description = "" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return True + + def invoke(self, context, event): + args = (self, context) + self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL") + context.window_manager.modal_handler_add(self) + return {"RUNNING_MODAL"} + + def modal(self, context, event): + context.area.tag_redraw() + + if event.type == "LEFTMOUSE": + return self.finish() + + if event.type in {"RIGHTMOUSE", "ESC"}: + return self.finish() + + return {"RUNNING_MODAL"} + + def finish(self): + bpy.types.SpaceView3D.draw_handler_remove(self._handle, "WINDOW") + return {"FINISHED"} + + def draw_callback_px(tmp, self, context): + pass + ''' diff --git a/code_templates/insert_panel.py b/code_templates/insert_panel.py index 96a085f..9580c57 100644 --- a/code_templates/insert_panel.py +++ b/code_templates/insert_panel.py @@ -1,29 +1,29 @@ -import bpy -from . base import InsertClassTemplateBase, insert_template -from .. utils.variable_name_conversion import (get_valid_variable_name, - get_lower_case_with_underscores, - get_separated_capitalized_words) - -class InsertPanel(bpy.types.Operator, InsertClassTemplateBase): - bl_idname = "code_autocomplete.insert_panel" - bl_label = "Insert Panel" - bl_description = "" - - def execute(self, context): - changes = { - "CLASS_NAME" : get_valid_variable_name(self.class_name), - "ID_NAME" : get_lower_case_with_underscores(self.class_name), - "LABEL" : get_separated_capitalized_words(self.class_name) } - insert_template(panel_template, changes) - return {"FINISHED"} - -panel_template = '''class CLASS_NAME(bpy.types.Panel): - bl_idname = "ID_NAME" - bl_label = "LABEL" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" - bl_category = "category" - - def draw(self, context): - layout = self.layout - ''' +import bpy +from . base import InsertClassTemplateBase, insert_template +from .. utils.variable_name_conversion import (get_valid_variable_name, + get_lower_case_with_underscores, + get_separated_capitalized_words) + +class InsertPanel(bpy.types.Operator, InsertClassTemplateBase): + bl_idname = "code_autocomplete.insert_panel" + bl_label = "Insert Panel" + bl_description = "" + + def execute(self, context): + changes = { + "CLASS_NAME" : get_valid_variable_name(self.class_name), + "ID_NAME" : get_lower_case_with_underscores(self.class_name), + "LABEL" : get_separated_capitalized_words(self.class_name) } + insert_template(panel_template, changes) + return {"FINISHED"} + +panel_template = '''class CLASS_NAME(bpy.types.Panel): + bl_idname = "ID_NAME" + bl_label = "LABEL" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "category" + + def draw(self, context): + layout = self.layout + ''' diff --git a/code_templates/insert_register.py b/code_templates/insert_register.py index 8920d85..60773cc 100644 --- a/code_templates/insert_register.py +++ b/code_templates/insert_register.py @@ -1,21 +1,21 @@ -import bpy -from . base import InsertTemplateBase, insert_template - -class InsertRegister(bpy.types.Operator, InsertTemplateBase): - bl_idname = "code_autocomplete.insert_register" - bl_label = "Insert Keymap" - bl_description = "" - - def execute(self, context): - insert_template(register_template) - return {"FINISHED"} - -register_template = '''def register(): - bpy.utils.register_module(__name__) - -def unregister(): - bpy.utils.unregister_module(__name__) - -if __name__ == "__main__": - register() -''' +import bpy +from . base import InsertTemplateBase, insert_template + +class InsertRegister(bpy.types.Operator, InsertTemplateBase): + bl_idname = "code_autocomplete.insert_register" + bl_label = "Insert Keymap" + bl_description = "" + + def execute(self, context): + insert_template(register_template) + return {"FINISHED"} + +register_template = '''def register(): + bpy.utils.register_module(__name__) + +def unregister(): + bpy.utils.unregister_module(__name__) + +if __name__ == "__main__": + register() +''' From 15ab9c0cfb02644abe872b2577436c25bbf74857 Mon Sep 17 00:00:00 2001 From: tin2tin Date: Tue, 19 Nov 2019 09:21:55 +0100 Subject: [PATCH 3/3] Collected all classes in init I hope you know what to do about them - I don't... --- README.md | 6 +- __init__.py | 242 ++++--- developer_utils.py | 84 +-- graphics/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 202 bytes graphics/__pycache__/list_box.cpython-37.pyc | Bin 0 -> 3896 bytes graphics/__pycache__/rectangle.cpython-37.pyc | Bin 0 -> 3344 bytes graphics/__pycache__/text_box.cpython-37.pyc | Bin 0 -> 2637 bytes graphics/__pycache__/utils.cpython-37.pyc | Bin 0 -> 552 bytes graphics/list_box.py | 202 +++--- graphics/rectangle.py | 172 ++--- graphics/text_box.py | 128 ++-- graphics/utils.py | 18 +- quick_operators.py | 301 ++++---- settings.py | 144 ++-- text_block.py | 684 +++++++++--------- utils/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 199 bytes .../variable_name_conversion.cpython-37.pyc | Bin 0 -> 1542 bytes utils/variable_name_conversion.py | 72 +- 18 files changed, 1055 insertions(+), 998 deletions(-) create mode 100644 graphics/__pycache__/__init__.cpython-37.pyc create mode 100644 graphics/__pycache__/list_box.cpython-37.pyc create mode 100644 graphics/__pycache__/rectangle.cpython-37.pyc create mode 100644 graphics/__pycache__/text_box.cpython-37.pyc create mode 100644 graphics/__pycache__/utils.cpython-37.pyc create mode 100644 utils/__pycache__/__init__.cpython-37.pyc create mode 100644 utils/__pycache__/variable_name_conversion.cpython-37.pyc diff --git a/README.md b/README.md index c5954f0..3c1f05a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -Autocompletion, templates and addon development tools for the text editor. - -Manual: http://code-autocomplete-manual.readthedocs.org/en/latest/ +Autocompletion, templates and addon development tools for the text editor. + +Manual: http://code-autocomplete-manual.readthedocs.org/en/latest/ diff --git a/__init__.py b/__init__.py index f5e0c6b..da1208a 100644 --- a/__init__.py +++ b/__init__.py @@ -1,93 +1,149 @@ -''' -Copyright (C) 2016 Jacques Lucke -mail@jlucke.com - -Created by Jacques Lucke - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -''' - - - -bl_info = { - "name": "Code Autocomplete", - "description": "Autocompletion, templates and addon development tools for the text editor.", - "author": "Jacques Lucke", - "version": (2, 0, 0), - "blender": (2, 80, 0), - "location": "Text Editor", - "category": "Development" - } - - - -# load and reload submodules -################################## - -# append jedi package path to make 'import jedi' available -import os, sys -sys.path.append(os.path.join(__path__[0], "jedi")) - -import importlib -from . import developer_utils -importlib.reload(developer_utils) -modules = developer_utils.setup_addon_modules(__path__, __name__, "bpy" in locals()) - - - -# register -################################## - -import bpy - -addon_keymaps = [] -def register_keymaps(): - global addon_keymaps - wm = bpy.context.window_manager - km = wm.keyconfigs.addon.keymaps.new(name = "Text", space_type = "TEXT_EDITOR") - kmi = km.keymap_items.new("code_autocomplete.select_whole_string", type = "Y", value = "PRESS", ctrl = True) - kmi = km.keymap_items.new("wm.call_menu", type = "SPACE", value = "PRESS", ctrl = True) - kmi.properties.name = "code_autocomplete_insert_template_menu" - kmi = km.keymap_items.new("wm.call_menu", type = "TAB", value = "PRESS", ctrl = True) - kmi.properties.name = "code_autocomplete_select_text_block" - addon_keymaps.append(km) - -def unregister_keymaps(): - wm = bpy.context.window_manager - for km in addon_keymaps: - for kmi in km.keymap_items: - km.keymap_items.remove(kmi) - wm.keyconfigs.addon.keymaps.remove(km) - addon_keymaps.clear() - -from . addon_development import AddonDevelopmentSceneProperties -from . quick_operators import register_menus, unregister_menus -from . code_templates.base import draw_template_menu - -def register(): - bpy.utils.register_module(__name__) - register_keymaps() - register_menus() - bpy.types.TEXT_MT_templates.append(draw_template_menu) - bpy.types.Scene.addon_development = bpy.props.PointerProperty(name = "Addon Development", type = AddonDevelopmentSceneProperties) - - print("Registered Code Autocomplete with {} modules.".format(len(modules))) - -def unregister(): - bpy.utils.unregister_module(__name__) - unregister_keymaps() - unregister_menus() - bpy.types.TEXT_MT_templates.remove(draw_template_menu) - - print("Unregistered Code Autocomplete") +''' +Copyright (C) 2016 Jacques Lucke +mail@jlucke.com + +Created by Jacques Lucke + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +''' + + + +bl_info = { + "name": "Code Autocomplete", + "description": "Autocompletion, templates and addon development tools for the text editor.", + "author": "Jacques Lucke", + "version": (2, 0, 0), + "blender": (2, 80, 0), + "location": "Text Editor", + "category": "Development" + } + + + +# load and reload submodules +################################## + +# append jedi package path to make 'import jedi' available +import os, sys +sys.path.append(os.path.join(__path__[0], "jedi")) + +import importlib +from . import developer_utils +importlib.reload(developer_utils) +modules = developer_utils.setup_addon_modules(__path__, __name__, "bpy" in locals()) + + + +# register +################################## + +import bpy + +addon_keymaps = [] +def register_keymaps(): + global addon_keymaps + wm = bpy.context.window_manager + km = wm.keyconfigs.addon.keymaps.new(name = "Text", space_type = "TEXT_EDITOR") + kmi = km.keymap_items.new("code_autocomplete.select_whole_string", type = "Y", value = "PRESS", ctrl = True) + kmi = km.keymap_items.new("wm.call_menu", type = "SPACE", value = "PRESS", ctrl = True) + kmi.properties.name = "code_autocomplete_insert_template_menu" + kmi = km.keymap_items.new("wm.call_menu", type = "TAB", value = "PRESS", ctrl = True) + kmi.properties.name = "code_autocomplete_select_text_block" + addon_keymaps.append(km) + +def unregister_keymaps(): + wm = bpy.context.window_manager + for km in addon_keymaps: + for kmi in km.keymap_items: + km.keymap_items.remove(kmi) + wm.keyconfigs.addon.keymaps.remove(km) + addon_keymaps.clear() + +from . addon_development import AddonDevelopmentSceneProperties +from . quick_operators import register_menus, unregister_menus +from . code_templates.base import draw_template_menu + + +classes = ( + # code_autocomplete_insert_template_menu, + # code_autocomplete.build_script, + # code_autocomplete.correct_whitespaces, + # code_autocomplete.convert_addon_indentation, + # SolveWhitespaceInconsistency, + # SelectWholeString, + # ConvertFileIndentation, + SelectTextBlockMenu, + OpenTextBlock, + CompletionProviders , + ContextBoxProperties, + DescriptionBoxProperties, + AddonPreferences, + TextBlock, + Provider, + Completion, + JediCompletion, + JediCompletionProvider, + OperatorCompletion, + ParameterCompletion, + OperatorCompletionProvider, + WordCompletion, + WordCompletionProvider, + BlockEvent, + AutocompleteHandler, + ContextUI, + ActiveTextArea, + AddonDeveloperPanel, + AddonFilesPanel, + SetDirectoryVisibility, + RunAddon, + RestartBlender, + NewFile, + NewDirectory, + FileMenuOpener, + OpenFile, + OpenExternalFileBrowser, + RenameFile, + DeleteFile, + SaveFiles, + ExportAddon, + ConvertAddonIndentation, + FindExistingAddon, + MakeAddonNameValid, + CreateNewAddon, + AddonDevelopmentSceneProperties, + TextBox, + ListItem, + ListBox, + Rectangle, +) + +def register(): + for i in classes: + bpy.utils.register_class(i) + register_keymaps() + register_menus() + bpy.types.TEXT_MT_templates.append(draw_template_menu) + bpy.types.Scene.addon_development: bpy.props.PointerProperty(name = "Addon Development", type = AddonDevelopmentSceneProperties) + + print("Registered Code Autocomplete with {} modules.".format(len(modules))) + +def unregister(): + for i in classes: + bpy.utils.unregister_class(i) + unregister_keymaps() + unregister_menus() + bpy.types.TEXT_MT_templates.remove(draw_template_menu) + + print("Unregistered Code Autocomplete") diff --git a/developer_utils.py b/developer_utils.py index e031810..dcbe163 100644 --- a/developer_utils.py +++ b/developer_utils.py @@ -1,42 +1,42 @@ -import os -import sys -import pkgutil -import importlib - -def setup_addon_modules(path, package_name, reload): - """ - Imports and reloads all modules in this addon. - - path -- __path__ from __init__.py - package_name -- __name__ from __init__.py - - Individual modules can define a __reload_order_index__ property which - will be used to reload the modules in a specific order. The default is 0. - """ - def get_submodule_names(path = path[0], root = ""): - module_names = [] - for importer, module_name, is_package in pkgutil.iter_modules([path]): - if is_package: - sub_path = os.path.join(path, module_name) - sub_root = root + module_name + "." - module_names.extend(get_submodule_names(sub_path, sub_root)) - else: - module_names.append(root + module_name) - return module_names - - def import_submodules(names): - modules = [] - for name in names: - modules.append(importlib.import_module("." + name, package_name)) - return modules - - def reload_modules(modules): - modules.sort(key = lambda module: getattr(module, "__reload_order_index__", 0)) - for module in modules: - importlib.reload(module) - - names = get_submodule_names() - modules = import_submodules(names) - if reload: - reload_modules(modules) - return modules +import os +import sys +import pkgutil +import importlib + +def setup_addon_modules(path, package_name, reload): + """ + Imports and reloads all modules in this addon. + + path -- __path__ from __init__.py + package_name -- __name__ from __init__.py + + Individual modules can define a __reload_order_index__ property which + will be used to reload the modules in a specific order. The default is 0. + """ + def get_submodule_names(path = path[0], root = ""): + module_names = [] + for importer, module_name, is_package in pkgutil.iter_modules([path]): + if is_package: + sub_path = os.path.join(path, module_name) + sub_root = root + module_name + "." + module_names.extend(get_submodule_names(sub_path, sub_root)) + else: + module_names.append(root + module_name) + return module_names + + def import_submodules(names): + modules = [] + for name in names: + modules.append(importlib.import_module("." + name, package_name)) + return modules + + def reload_modules(modules): + modules.sort(key = lambda module: getattr(module, "__reload_order_index__", 0)) + for module in modules: + importlib.reload(module) + + names = get_submodule_names() + modules = import_submodules(names) + if reload: + reload_modules(modules) + return modules diff --git a/graphics/__pycache__/__init__.cpython-37.pyc b/graphics/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe9104fb2365272d0a1c548fbb4fb2d2a11f04dc GIT binary patch literal 202 zcmYL@u?oU47=%-BkV4`Uq}LCWClz z-yJtyTU8}XDsPW-&R51yMJlUg8YL)pWYc1QXFkt=d}uR=6KPKn6JhP#Rs;bD6oauH zw7s@U`^gTYRU#O)zX_Up)&NOwTp$oip(T*0G#4?T#KHACXz09f7b6KcH`WBsYj=H) O7Xzx;>t#OkPG(;L$vO7` literal 0 HcmV?d00001 diff --git a/graphics/__pycache__/list_box.cpython-37.pyc b/graphics/__pycache__/list_box.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44707ea1ffd7b3996adc6cdda3c26ecded378c3a GIT binary patch literal 3896 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 literal 0 HcmV?d00001 diff --git a/graphics/__pycache__/text_box.cpython-37.pyc b/graphics/__pycache__/text_box.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a246928098f0c81ef8f85768a90855c46e0fbeb5 GIT binary patch literal 2637 zcmaJ?&u<$=6rPz~+v~NRq-ki=7L_2C3iBhBs6YiFDQyH22MbDRCCk-n%m;)CsL~!8T3w&?Zn>rP+t9kQg-psr=?|tvhwOY+( z_}p)QzH{DT>^B>g7Y6$r^R8`Adf!EVyDPNY@4-akqkY8Y^(+8{qtY zCkooMA^!xJt7yqGNWjRPrjT5U`>bh^N$71^kVW)`OnI{?%hJWTBrCFt-jOvJGNns5 zgJF6t9=6F0`lu%b*U^$S5Xnc(V=0c`Cr(HU6iPs$1Qd!ui?RfYn}RyB3<`yyu4GVD zX=qj#iVim<{0eN{?qH1XuhsuyuHBlFbL0E-pZ)Ri@2j1M-_ZBVlYLl~&Ha!I zCz)K$A)?F_h4%0?kpf7)qU{t;P&ClaAWp(GjQhH9H}+XTOAee^rhF1qgd(F z6Eo|vl7SivTsmgVmp9^m>LuZBplxDxB?|k2w;6<;%~Y2LzLa6VgBaUM5N&+N6xs7Q zx&C(R(uzH`BZCZ@yb#wX{&IMCM_Hu z_mfsTmVxK*q;Wg$4Wb|oS{>yNHp6z(GM28#!^Ocp?RZ|;4^z)8!-QlGgz+MG(I)@h znK4op^W{g7A;*=_3}k)`v+sht+~**-t8I2*A?#RDJ=ed&Ph@ezOI&^QHL zy6%@o(yctILMBGSX5AX*r``e{y_y+M(->_=@^fsST_MSICV|cqkO(Ob0MRpm?YXgh z_+dH=$;o}b#_lY)#GbWhADuRnoq^pI+C37`DZ10rK0r!i7I4O_eIPj7es~*64loxQ zhm;|jhXzUp(DgZOtw$ROCqk}HLab(KjTQD&WfSMN{iy8`3KBh!WcMaur1Cu1wp9LI zUG7BJ32T=()CoGOMhDpK7}4TG^$d;pJw>6?t_*uYKLHjJT@Ly?y+HYCpbJJ(y`WB+ zMNf2T3kX-|%z?DV052o+m{iY$kb>ka2;=j@;rN9MK+N#7e1XrQKgD;eIULB~BPftx zD7_36s64@a0E6TQe2)(=q+-PPQAxIJb!o&+nCvY}z4#HM(YtJW>NYr34QuRZ&0XW( z!@uz<%HF!Is_1kf8AM^KEu@5ck>-WDs^Oo$({qqtL`!HV<5fPzt4wfL?9S%5coLjr zcPV1MxyvmufGr!tmmAm`fOk;|yH*#K5R~}hX}WbbQWE$gt5LVL7{0E>J+$Ex`X%)$^wn#+V(c1MjSNAiEGaU~z(QRh?gb)KAi9_>ZqAHL zQ;K}w0_GAmP2z&7VQL0^vpQM5a{txoBq~M|m{`DY5iKDwF~S2ErZ`mjEP5L6F65s4 zFK8e6l^y}g2;oA_)T5``J8P4xp-Ec2e5AqMQo3!C|W?Ug&9FM zKFG0ZH;x@d@u4b^oCoRPDtAs{Gg5&zjyP%*JHx&f+cvJpvkGFVXR%fhP)z};3oRBe zsh2T6c0Eed$L?|zCMgy6l~s(*1&eI=;;{=Fs4Ol=F|M1efXTOH?zD~nwAJ&onqmMn z#zC`aHYWxJxq4F|-hjFlQb{pnCnq04wrj=x|xmX;cAwTif@)wbj-hL*3NQvi4_8Ht{%{@(l7o2zjj_~ literal 0 HcmV?d00001 diff --git a/graphics/__pycache__/utils.cpython-37.pyc b/graphics/__pycache__/utils.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1ed59a634cd10b35ce22a7a76ae0b0b48a4b539 GIT binary patch literal 552 zcmYjOu}&i~5VhCIk_$4XpjgszGZCz{9z`POqeN1L@}Pb4@cRqju& zy6>TLOO-D`qGG&JfRX&{eLHXGz4fTy?=q_HzsK8W+Mm+Ht};5D(h6S)G}F9d6Q(mQ z{;~Ag5lr-=?I`qp8BcJ;dJD#ic6IdXWq#=ZyQD@GVi|lFKI15gm}t_$-@= z6~oq&vgvFsR(#23?Ir)5a*A>6I&%-zT`+%O!%0)*QT(Kld!?-r?lJB=55K@eT|$U? zcnApdxJQN*qK`s5Q)H8aEr0diI_Y^pQ>(4#_rK@cG^ELpyoUsgcX^LDTp|3Y^ghuI Z@j=*JKvKuf@!aY~1t-aHNPH*jh*yiugr5Ka literal 0 HcmV?d00001 diff --git a/graphics/list_box.py b/graphics/list_box.py index 8e9a49c..fe76f2a 100644 --- a/graphics/list_box.py +++ b/graphics/list_box.py @@ -1,101 +1,101 @@ -import blf -from bgl import * -from . utils import getDpi -from mathutils import Vector -from . rectangle import Rectangle - -class ListItem: - def __init__(self, text): - self.text = text - self.active = False - self.alignment = "LEFT" - self.data = None - self.offset = 0 - - -class ListBox: - def __init__(self): - self.items = [] - self.position = Vector((0, 0)) - self.width = 200 - self.line_height = 23 - self.font_size = 8 - self.padding = 5 - self.font = 1 - self.background_color = (1.0, 1.0, 1.0, 1.0) - self.background_border_color = (0.9, 0.76, 0.4, 1.0) - self.text_color = (0.1, 0.1, 0.1, 1.0) - self.active_item_color = (0.95, 0.95, 1.0, 1.0) - self.active_item_border_color = (1.0, 0.8, 0.5, 1.0) - self.calc_height() - - def contains(self, point): - background = self.get_background_rectangle() - return background.contains(point) - - def get_item_under_point(self, point): - for i, item in enumerate(self.items): - rec = self.get_item_rectangle(i) - if rec.contains(point): return item - - def draw(self): - self.calc_height() - self.draw_background() - self.draw_items() - - def draw_background(self): - background = self.get_background_rectangle() - background.draw() - - def get_background_rectangle(self): - self.calc_height() - - background = Rectangle( - x1 = self.position.x, - y1 = self.position.y, - x2 = self.position.x + self.width, - y2 = self.position.y - self.height ) - background.border_thickness = -1 - background.color = self.background_color - background.border_color = self.background_border_color - return background - - def calc_height(self): - self.height = len(self.items) * self.line_height + self.padding - - def draw_items(self): - for index in range(len(self.items)): - self.draw_item(index) - - def draw_item(self, index): - item = self.items[index] - item_rec = self.get_item_rectangle(index) - if item.active: - item_rec.draw() - self.draw_item_in_rectangle(item, item_rec) - - def get_item_rectangle(self, index): - item_rec = Rectangle() - item_rec.x1 = self.position.x - item_rec.y1 = self.position.y - index * self.line_height - self.padding / 2 - item_rec.x2 = self.position.x + self.width - item_rec.y2 = item_rec.y1 - self.line_height - item_rec.color = self.active_item_color - item_rec.border_color = self.active_item_border_color - item_rec.border_thickness = -1 - return item_rec - - def draw_item_in_rectangle(self, item, rectangle): - glColor4f(*self.text_color) - blf.size(self.font, self.font_size, int(getDpi())) - - if item.alignment == "LEFT": - x = rectangle.left + self.padding - if item.alignment == "CENTER": - x = rectangle.center.x - blf.dimensions(self.font, item.text)[0] / 2 - x += item.offset - - offset = blf.dimensions(self.font, "i")[1] / 2 - - blf.position(self.font, x, rectangle.center.y - offset, 0) - blf.draw(self.font, item.text) +import blf +from bgl import * +from . utils import getDpi +from mathutils import Vector +from . rectangle import Rectangle + +class ListItem: + def __init__(self, text): + self.text = text + self.active = False + self.alignment = "LEFT" + self.data = None + self.offset = 0 + + +class ListBox: + def __init__(self): + self.items = [] + self.position = Vector((0, 0)) + self.width = 200 + self.line_height = 23 + self.font_size = 8 + self.padding = 5 + self.font = 1 + self.background_color = (1.0, 1.0, 1.0, 1.0) + self.background_border_color = (0.9, 0.76, 0.4, 1.0) + self.text_color = (0.1, 0.1, 0.1, 1.0) + self.active_item_color = (0.95, 0.95, 1.0, 1.0) + self.active_item_border_color = (1.0, 0.8, 0.5, 1.0) + self.calc_height() + + def contains(self, point): + background = self.get_background_rectangle() + return background.contains(point) + + def get_item_under_point(self, point): + for i, item in enumerate(self.items): + rec = self.get_item_rectangle(i) + if rec.contains(point): return item + + def draw(self): + self.calc_height() + self.draw_background() + self.draw_items() + + def draw_background(self): + background = self.get_background_rectangle() + background.draw() + + def get_background_rectangle(self): + self.calc_height() + + background = Rectangle( + x1 = self.position.x, + y1 = self.position.y, + x2 = self.position.x + self.width, + y2 = self.position.y - self.height ) + background.border_thickness = -1 + background.color = self.background_color + background.border_color = self.background_border_color + return background + + def calc_height(self): + self.height = len(self.items) * self.line_height + self.padding + + def draw_items(self): + for index in range(len(self.items)): + self.draw_item(index) + + def draw_item(self, index): + item = self.items[index] + item_rec = self.get_item_rectangle(index) + if item.active: + item_rec.draw() + self.draw_item_in_rectangle(item, item_rec) + + def get_item_rectangle(self, index): + item_rec = Rectangle() + item_rec.x1 = self.position.x + item_rec.y1 = self.position.y - index * self.line_height - self.padding / 2 + item_rec.x2 = self.position.x + self.width + item_rec.y2 = item_rec.y1 - self.line_height + item_rec.color = self.active_item_color + item_rec.border_color = self.active_item_border_color + item_rec.border_thickness = -1 + return item_rec + + def draw_item_in_rectangle(self, item, rectangle): + glColor4f(*self.text_color) + blf.size(self.font, self.font_size, int(getDpi())) + + if item.alignment == "LEFT": + x = rectangle.left + self.padding + if item.alignment == "CENTER": + x = rectangle.center.x - blf.dimensions(self.font, item.text)[0] / 2 + x += item.offset + + offset = blf.dimensions(self.font, "i")[1] / 2 + + blf.position(self.font, x, rectangle.center.y - offset, 0) + blf.draw(self.font, item.text) diff --git a/graphics/rectangle.py b/graphics/rectangle.py index b405fcf..89686fc 100644 --- a/graphics/rectangle.py +++ b/graphics/rectangle.py @@ -1,86 +1,86 @@ -from bgl import * -from mathutils import Vector - -class Rectangle: - def __init__(self, x1 = 0, y1 = 0, x2 = 0, y2 = 0): - self.x1 = float(x1) - self.y1 = float(y1) - self.x2 = float(x2) - self.y2 = float(y2) - self.color = (0.8, 0.8, 0.8, 1.0) - self.border_color = (0.1, 0.1, 0.1, 1.0) - self.border_thickness = 0 - - @property - def width(self): - return abs(self.x1 - self.x2) - - @property - def height(self): - return abs(self.y1 - self.y2) - - @property - def left(self): - return min(self.x1, self.x2) - - @property - def right(self): - return max(self.x1, self.x2) - - @property - def top(self): - return max(self.y1, self.y2) - - @property - def bottom(self): - return min(self.y1, self.y2) - - @property - def center(self): - return Vector((self.center_x, self.center_y)) - - @property - def center_x(self): - return (self.x1 + self.x2) / 2 - - @property - def center_y(self): - return (self.y1 + self.y2) / 2 - - def contains(self, point): - return self.left <= point[0] <= self.right and self.bottom <= point[1] <= self.top - - def draw(self): - glColor4f(*self.color) - glEnable(GL_BLEND) - glBegin(GL_POLYGON) - glVertex2f(self.x1, self.y1) - glVertex2f(self.x2, self.y1) - glVertex2f(self.x2, self.y2) - glVertex2f(self.x1, self.y2) - glEnd() - - if self.border_thickness != 0: - self.drawBorder() - - def drawBorder(self): - thickness = self.border_thickness - thickness = min(abs(self.x1 - self.x2) / 2, abs(self.y1 - self.y2) / 2, thickness) - left, right = sorted([self.x1, self.x2]) - bottom, top = sorted([self.y1, self.y2]) - - if thickness > 0: - topBorder = Rectangle(left, top, right, top - thickness) - bottomBorder = Rectangle(left, bottom + thickness, right, bottom) - else: - topBorder = Rectangle(left + thickness, top, right - thickness, top - thickness) - bottomBorder = Rectangle(left + thickness, bottom + thickness, right - thickness, bottom) - leftBorder = Rectangle(left, top, left + thickness, bottom) - rightBorder = Rectangle(right - thickness, top, right, bottom) - - for border in (topBorder, bottomBorder, leftBorder, rightBorder): - border.color = self.border_color - border.draw() - - def __repr__(self): - return "({}, {}) - ({}, {})".format(self.x1, self.y1, self.x2, self.y2) +from bgl import * +from mathutils import Vector + +class Rectangle: + def __init__(self, x1 = 0, y1 = 0, x2 = 0, y2 = 0): + self.x1 = float(x1) + self.y1 = float(y1) + self.x2 = float(x2) + self.y2 = float(y2) + self.color = (0.8, 0.8, 0.8, 1.0) + self.border_color = (0.1, 0.1, 0.1, 1.0) + self.border_thickness = 0 + + @property + def width(self): + return abs(self.x1 - self.x2) + + @property + def height(self): + return abs(self.y1 - self.y2) + + @property + def left(self): + return min(self.x1, self.x2) + + @property + def right(self): + return max(self.x1, self.x2) + + @property + def top(self): + return max(self.y1, self.y2) + + @property + def bottom(self): + return min(self.y1, self.y2) + + @property + def center(self): + return Vector((self.center_x, self.center_y)) + + @property + def center_x(self): + return (self.x1 + self.x2) / 2 + + @property + def center_y(self): + return (self.y1 + self.y2) / 2 + + def contains(self, point): + return self.left <= point[0] <= self.right and self.bottom <= point[1] <= self.top + + def draw(self): + glColor4f(*self.color) + glEnable(GL_BLEND) + glBegin(GL_POLYGON) + glVertex2f(self.x1, self.y1) + glVertex2f(self.x2, self.y1) + glVertex2f(self.x2, self.y2) + glVertex2f(self.x1, self.y2) + glEnd() + + if self.border_thickness != 0: + self.drawBorder() + + def drawBorder(self): + thickness = self.border_thickness + thickness = min(abs(self.x1 - self.x2) / 2, abs(self.y1 - self.y2) / 2, thickness) + left, right = sorted([self.x1, self.x2]) + bottom, top = sorted([self.y1, self.y2]) + + if thickness > 0: + topBorder = Rectangle(left, top, right, top - thickness) + bottomBorder = Rectangle(left, bottom + thickness, right, bottom) + else: + topBorder = Rectangle(left + thickness, top, right - thickness, top - thickness) + bottomBorder = Rectangle(left + thickness, bottom + thickness, right - thickness, bottom) + leftBorder = Rectangle(left, top, left + thickness, bottom) + rightBorder = Rectangle(right - thickness, top, right, bottom) + + for border in (topBorder, bottomBorder, leftBorder, rightBorder): + border.color = self.border_color + border.draw() + + def __repr__(self): + return "({}, {}) - ({}, {})".format(self.x1, self.y1, self.x2, self.y2) diff --git a/graphics/text_box.py b/graphics/text_box.py index 21846bb..0af31ed 100644 --- a/graphics/text_box.py +++ b/graphics/text_box.py @@ -1,64 +1,64 @@ -import blf -import textwrap -from bgl import * -from . utils import getDpi -from mathutils import Vector -from . rectangle import Rectangle - -class TextBox: - def __init__(self): - self.text = "" - self.position = Vector((0, 0)) - self.width = 400 - self.background_color = (1.0, 1.0, 1.0, 1.0) - self.background_border_color = (0.9, 0.76, 0.4, 1.0) - self.text_color = (0.1, 0.1, 0.1, 1.0) - self.font_size = 8 - self.font = 1 - self.line_height = 23 - self.padding = 5 - - def draw(self): - blf.size(self.font, self.font_size, int(getDpi())) - - self.calc_lines() - background = self.get_background_rectangle() - background.draw() - - glColor4f(*self.text_color) - pos = self.position.copy() - pos.x += self.padding - pos.y -= self.padding - self.line_height / 2 - pos.y -= blf.dimensions(self.font, "i")[1] / 2 - for i, line in enumerate(self.lines): - pos.y -= self.line_height - blf.position(self.font, pos.x, pos.y, 0) - blf.draw(self.font, line) - - def calc_lines(self): - lines = self.text.split("\n") - while len(lines) > 0: - if lines[-1] != "": break - del lines[-1] - - self.lines = lines - - def get_background_rectangle(self): - self.calc_height() - self.calc_width() - background = Rectangle( - x1 = self.position.x, - y1 = self.position.y, - x2 = self.position.x + self.width, - y2 = self.position.y - self.height ) - background.border_thickness = -1 - background.color = self.background_color - background.border_color = self.background_border_color - return background - - def calc_height(self): - self.height = 2 * self.padding + self.line_height * len(self.lines) - - def calc_width(self): - widths = [blf.dimensions(self.font, line)[0] for line in self.lines] - self.width = max(widths) + 2 * self.padding +import blf +import textwrap +from bgl import * +from . utils import getDpi +from mathutils import Vector +from . rectangle import Rectangle + +class TextBox: + def __init__(self): + self.text = "" + self.position = Vector((0, 0)) + self.width = 400 + self.background_color = (1.0, 1.0, 1.0, 1.0) + self.background_border_color = (0.9, 0.76, 0.4, 1.0) + self.text_color = (0.1, 0.1, 0.1, 1.0) + self.font_size = 8 + self.font = 1 + self.line_height = 23 + self.padding = 5 + + def draw(self): + blf.size(self.font, self.font_size, int(getDpi())) + + self.calc_lines() + background = self.get_background_rectangle() + background.draw() + + glColor4f(*self.text_color) + pos = self.position.copy() + pos.x += self.padding + pos.y -= self.padding - self.line_height / 2 + pos.y -= blf.dimensions(self.font, "i")[1] / 2 + for i, line in enumerate(self.lines): + pos.y -= self.line_height + blf.position(self.font, pos.x, pos.y, 0) + blf.draw(self.font, line) + + def calc_lines(self): + lines = self.text.split("\n") + while len(lines) > 0: + if lines[-1] != "": break + del lines[-1] + + self.lines = lines + + def get_background_rectangle(self): + self.calc_height() + self.calc_width() + background = Rectangle( + x1 = self.position.x, + y1 = self.position.y, + x2 = self.position.x + self.width, + y2 = self.position.y - self.height ) + background.border_thickness = -1 + background.color = self.background_color + background.border_color = self.background_border_color + return background + + def calc_height(self): + self.height = 2 * self.padding + self.line_height * len(self.lines) + + def calc_width(self): + widths = [blf.dimensions(self.font, line)[0] for line in self.lines] + self.width = max(widths) + 2 * self.padding diff --git a/graphics/utils.py b/graphics/utils.py index 41993a4..766c8f8 100644 --- a/graphics/utils.py +++ b/graphics/utils.py @@ -1,9 +1,9 @@ -import bpy - -def getDpiFactor(): - return getDpi() / 72 - -def getDpi(): - systemPreferences = bpy.context.user_preferences.system - retinaFactor = getattr(systemPreferences, "pixel_size", 1) - return systemPreferences.dpi * retinaFactor +import bpy + +def getDpiFactor(): + return getDpi() / 72 + +def getDpi(): + systemPreferences = bpy.context.preferences.system + retinaFactor = getattr(systemPreferences, "pixel_size", 1) + return systemPreferences.dpi * retinaFactor diff --git a/quick_operators.py b/quick_operators.py index d0dde38..1132dce 100644 --- a/quick_operators.py +++ b/quick_operators.py @@ -1,150 +1,151 @@ -import bpy -import os -import re -from bpy.props import * -from . text_block import TextBlock - -class SolveWhitespaceInconsistency(bpy.types.Operator): - bl_idname = "code_autocomplete.correct_whitespaces" - bl_label = "Correct Whitespaces" - bl_description = "Convert whitespaces to spaces or tabs depending on what is set for this text block" - - def execute(self, context): - if context.edit_text.use_tabs_as_spaces: - bpy.ops.text.convert_whitespace(type = "SPACES") - else: - bpy.ops.text.convert_whitespace(type = "TABS") - return { "FINISHED" } - -class SelectWholeString(bpy.types.Operator): - bl_idname = "code_autocomplete.select_whole_string" - bl_label = "Select Whole String" - bl_description = "" - bl_options = {"REGISTER"} - - def execute(self, context): - text_block = TextBlock.get_active() - if not text_block: return {"CANCELLED"} - - line_text = text_block.current_line - character_index = text_block.current_character_index - - string_letter = text_block.get_string_definition_type(line_text, character_index) - if string_letter is None: return {"CANCELLED"} - start, end = text_block.get_range_surrounded_by_letter(line_text, string_letter, character_index) - if start != end: - text_block.set_selection_in_line(start, end) - - return {"FINISHED"} - - -class ConvertFileIndentation(bpy.types.Operator): - bl_idname = "code_autocomplete.convert_file_indentation" - bl_label = "Convert File Indentation" - bl_description = "" - bl_options = {"REGISTER"} - - path: StringProperty() - old_indentation: StringProperty(default = "\t") - new_indentation: StringProperty(default = " ") - - @classmethod - def poll(cls, context): - return True - - def execute(self, context): - if not os.path.exists(self.path): return {"CANCELLED"} - - old = self.old_indentation - new = self.new_indentation - - file = open(self.path, "r") - lines = file.readlines() - file.close() - - new_lines = [] - for line in lines: - new_line = "" - while line.startswith(old): - new_line += new - line = line[len(old):] - new_line += line.rstrip() - new_lines.append(new_line) - - file = open(self.path, "w") - file.write("\n".join(new_lines)) - file.close() - return {"FINISHED"} - - -class SelectTextBlockMenu(bpy.types.Menu): - bl_idname = "code_autocomplete_select_text_block" - bl_label = "Select Text Block" - - def draw(self, context): - layout = self.layout - - if len(bpy.data.texts) == 0: - layout.label("There are no texts in this file", icon = "INFO") - else: - for text in bpy.data.texts: - operator = layout.operator("code_autocomplete.open_text_block", text = text.name) - operator.name = text.name - -class OpenTextBlock(bpy.types.Operator): - bl_idname = "code_autocomplete.open_text_block" - bl_label = "Open Text Block" - bl_description = "" - bl_options = {"REGISTER"} - - name: StringProperty() - - @classmethod - def poll(cls, context): - return hasattr(context.space_data, "text") - - def execute(self, context): - context.space_data.text = bpy.data.texts[self.name] - return {"FINISHED"} - - -def right_click_menu_extension(self, context): - layout = self.layout - layout.separator() - layout.operator("text.comment") - layout.operator("text.uncomment") - - text_block = TextBlock.get_active() - if text_block: - line = text_block.current_line - match = re.match("def (\w+)\(\):", line) - if match: - function_name = match.group(1) - operator = layout.operator("code_autocomplete.execute_function") - operator.filepath = text_block.filepath - operator.function_name = function_name - - -def format_menu_extension(self, context): - text_block = TextBlock.get_active() - if text_block: - layout = self.layout - layout.operator("code_autocomplete.build_script") - layout.operator("code_autocomplete.correct_whitespaces") - props = layout.operator("code_autocomplete.convert_addon_indentation") - if text_block.use_tabs_as_spaces: - props.old_indentation = "\t" - props.new_indentation = " " - else: - props.old_indentation = " " - props.new_indentation = "\t" - - - -def register_menus(): - bpy.types.TEXT_MT_context_menu.append(right_click_menu_extension) - bpy.types.TEXT_MT_format.append(format_menu_extension) - -def unregister_menus(): - bpy.types.TEXT_MT_context_menu.remove(right_click_menu_extension) - bpy.types.TEXT_MT_format.remove(format_menu_extension) +import bpy +import os +import re +from bpy.props import * +from . text_block import TextBlock + +class SolveWhitespaceInconsistency(bpy.types.Operator): + bl_idname = "code_autocomplete.correct_whitespaces" + bl_label = "Correct Whitespaces" + bl_description = "Convert whitespaces to spaces or tabs depending on what is set for this text block" + + def execute(self, context): + if context.edit_text.use_tabs_as_spaces: + bpy.ops.text.convert_whitespace(type = "SPACES") + else: + bpy.ops.text.convert_whitespace(type = "TABS") + return { "FINISHED" } + +class SelectWholeString(bpy.types.Operator): + bl_idname = "code_autocomplete.select_whole_string" + bl_label = "Select Whole String" + bl_description = "" + bl_options = {"REGISTER"} + + def execute(self, context): + text_block = TextBlock.get_active() + if not text_block: return {"CANCELLED"} + + line_text = text_block.current_line + character_index = text_block.current_character_index + + string_letter = text_block.get_string_definition_type(line_text, character_index) + if string_letter is None: return {"CANCELLED"} + start, end = text_block.get_range_surrounded_by_letter(line_text, string_letter, character_index) + if start != end: + text_block.set_selection_in_line(start, end) + + return {"FINISHED"} + + +class ConvertFileIndentation(bpy.types.Operator): + bl_idname = "code_autocomplete.convert_file_indentation" + bl_label = "Convert File Indentation" + bl_description = "" + bl_options = {"REGISTER"} + + path: StringProperty() + old_indentation: StringProperty(default = "\t") + new_indentation: StringProperty(default = " ") + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + if not os.path.exists(self.path): return {"CANCELLED"} + + old = self.old_indentation + new = self.new_indentation + + file = open(self.path, "r") + lines = file.readlines() + file.close() + + new_lines = [] + for line in lines: + new_line = "" + while line.startswith(old): + new_line += new + line = line[len(old):] + new_line += line.rstrip() + new_lines.append(new_line) + + file = open(self.path, "w") + file.write("\n".join(new_lines)) + file.close() + return {"FINISHED"} + + +class SelectTextBlockMenu(bpy.types.Menu): + bl_idname = "code_autocomplete_select_text_block" + bl_label = "Select Text Block" + + def draw(self, context): + layout = self.layout + + if len(bpy.data.texts) == 0: + layout.label(text="There are no texts in this file", icon = "INFO") + else: + for text in bpy.data.texts: + operator = layout.operator("code_autocomplete.open_text_block", text = text.name) + operator.name = text.name + +class OpenTextBlock(bpy.types.Operator): + bl_idname = "code_autocomplete.open_text_block" + bl_label = "Open Text Block" + bl_description = "" + bl_options = {"REGISTER"} + + name: StringProperty() + + @classmethod + def poll(cls, context): + return hasattr(context.space_data, "text") + + def execute(self, context): + context.space_data.text = bpy.data.texts[self.name] + return {"FINISHED"} + + +def right_click_menu_extension(self, context): + layout = self.layout + layout.separator() + layout.operator("text.comment") + layout.operator("text.uncomment") + + text_block = TextBlock.get_active() + if text_block: + line = text_block.current_line + match = re.match("def (\w+)\(\):", line) + if match: + function_name = match.group(1) + operator = layout.operator("code_autocomplete.execute_function") + operator.filepath = text_block.filepath + operator.function_name = function_name + + +def format_menu_extension(self, context): + text_block = TextBlock.get_active() + if text_block: + layout = self.layout + layout.operator("code_autocomplete.build_script") + layout.operator("code_autocomplete.correct_whitespaces") + props = layout.operator("code_autocomplete.convert_addon_indentation") + if text_block.use_tabs_as_spaces: + props.old_indentation = "\t" + props.new_indentation = " " + else: + props.old_indentation = " " + props.new_indentation = "\t" + + + +def register_menus(): + bpy.types.TEXT_MT_context_menu.append(right_click_menu_extension) + bpy.types.TEXT_MT_format.append(format_menu_extension) + +def unregister_menus(): + bpy.types.TEXT_MT_context_menu.remove(right_click_menu_extension) + bpy.types.TEXT_MT_format.remove(format_menu_extension) + diff --git a/settings.py b/settings.py index fbc722c..503e46d 100644 --- a/settings.py +++ b/settings.py @@ -1,72 +1,72 @@ -import bpy -import os -from bpy.props import * - -addon_name = os.path.basename(os.path.dirname(__file__)) - - -def prop_changed(self, context): - for area in bpy.context.screen.areas: - if area.type == "TEXT_EDITOR": - area.tag_redraw() - -class CompletionProviders (bpy.types.PropertyGroup): - use_jedi_completion: BoolProperty(default = True, name = "Use Jedi Completion", - update = prop_changed, description = "Use the Jedi autocompletion library for python") - use_word_completion: BoolProperty(default = True, name = "Use Word Completion", - update = prop_changed, description = "The context box will also contain words that you already used in the file") - use_operator_completion: BoolProperty(default = True, name = "Use Operator Completion", - update = prop_changed, description = "Activate the autocompletion for calling operators (bpy.ops)") - -class ContextBoxProperties(bpy.types.PropertyGroup): - font_size: IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) - line_height: IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) - width: IntProperty(default = 200, name = "Width", min = 10, update = prop_changed) - padding: IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) - lines: IntProperty(default = 8, name = "Lines", min = 1, update = prop_changed) - -class DescriptionBoxProperties(bpy.types.PropertyGroup): - font_size: IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) - line_height: IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) - padding: IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) - - -class AddonPreferences(bpy.types.AddonPreferences): - bl_idname = addon_name - - completion_providers: PointerProperty(type = CompletionProviders) - context_box: PointerProperty(type = ContextBoxProperties) - description_box: PointerProperty(type = DescriptionBoxProperties) - - debug: BoolProperty(default = False, name = "Debug", - update = prop_changed, description = "Turn on to get some debug information from this addon") - - def draw(self, context): - layout = self.layout - - col = layout.column() - col.label("Completion Providers:") - col.prop(self.completion_providers, "use_jedi_completion", "Jedi") - col.prop(self.completion_providers, "use_word_completion", "Existing Words") - col.prop(self.completion_providers, "use_operator_completion", "Operators") - - row = layout.row() - col = row.column(align = True) - col.label("Context Box") - col.prop(self.context_box, "font_size") - col.prop(self.context_box, "line_height") - col.prop(self.context_box, "width") - col.prop(self.context_box, "padding") - col.prop(self.context_box, "lines") - - col = row.column(align = True) - col.label("Description Box") - col.prop(self.description_box, "font_size") - col.prop(self.description_box, "line_height") - col.prop(self.description_box, "padding") - - layout.prop(self, "debug") - -def get_preferences(): - addon = bpy.context.user_preferences.addons.get(addon_name) - return getattr(addon, "preferences", None) +import bpy +import os +from bpy.props import * + +addon_name = os.path.basename(os.path.dirname(__file__)) + + +def prop_changed(self, context): + for area in bpy.context.screen.areas: + if area.type == "TEXT_EDITOR": + area.tag_redraw() + +class CompletionProviders (bpy.types.PropertyGroup): + use_jedi_completion: BoolProperty(default = True, name = "Use Jedi Completion", + update = prop_changed, description = "Use the Jedi autocompletion library for python") + use_word_completion: BoolProperty(default = True, name = "Use Word Completion", + update = prop_changed, description = "The context box will also contain words that you already used in the file") + use_operator_completion: BoolProperty(default = True, name = "Use Operator Completion", + update = prop_changed, description = "Activate the autocompletion for calling operators (bpy.ops)") + +class ContextBoxProperties(bpy.types.PropertyGroup): + font_size: IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) + line_height: IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) + width: IntProperty(default = 200, name = "Width", min = 10, update = prop_changed) + padding: IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) + lines: IntProperty(default = 8, name = "Lines", min = 1, update = prop_changed) + +class DescriptionBoxProperties(bpy.types.PropertyGroup): + font_size: IntProperty(default = 12, name = "Font Size", min = 10, update = prop_changed) + line_height: IntProperty(default = 21, name = "Line Height", min = 5, update = prop_changed) + padding: IntProperty(default = 4, name = "Padding", min = 0, update = prop_changed) + + +class AddonPreferences(bpy.types.AddonPreferences): + bl_idname = addon_name + + completion_providers: PointerProperty(type = CompletionProviders) + context_box: PointerProperty(type = ContextBoxProperties) + description_box: PointerProperty(type = DescriptionBoxProperties) + + debug: BoolProperty(default = False, name = "Debug", + update = prop_changed, description = "Turn on to get some debug information from this addon") + + def draw(self, context): + layout = self.layout + + col = layout.column() + col.label(text="Completion Providers:") + col.prop(self.completion_providers, "use_jedi_completion", "Jedi") + col.prop(self.completion_providers, "use_word_completion", "Existing Words") + col.prop(self.completion_providers, "use_operator_completion", "Operators") + + row = layout.row() + col = row.column(align = True) + col.label(text="Context Box") + col.prop(self.context_box, "font_size") + col.prop(self.context_box, "line_height") + col.prop(self.context_box, "width") + col.prop(self.context_box, "padding") + col.prop(self.context_box, "lines") + + col = row.column(align = True) + col.label(text="Description Box") + col.prop(self.description_box, "font_size") + col.prop(self.description_box, "line_height") + col.prop(self.description_box, "padding") + + layout.prop(self, "debug") + +def get_preferences(): + addon = bpy.context.preferences.addons.get(addon_name) + return getattr(addon, "preferences", None) diff --git a/text_block.py b/text_block.py index 638e017..40fac64 100644 --- a/text_block.py +++ b/text_block.py @@ -1,342 +1,342 @@ -import bpy, re -from mathutils import Vector - -class TextBlock: - def __init__(self, text_block): - if text_block is None: raise AttributeError() - self.text_block = text_block - self.set_context() - - def set_context(self, window = None, area = None, region = None, space = None): - context = bpy.context - self.window = window if window else context.window - self.area = area if window else context.area - self.region = region if window else context.region - self.space = space if window else context.space_data - - @classmethod - def get_active(cls): - text = getattr(bpy.context.space_data, "text", None) - if text: return TextBlock(text) - return None - - @property - def filepath(self): - return self.text_block.filepath - - @property - def current_cursor_region_location(self): - space = bpy.context.space_data - line = self.current_line_index - character = self.current_character_index - location = space.region_location_from_cursor(line, character) - return Vector(location) - - @property - def use_tabs_as_spaces(self): - return self.text_block.use_tabs_as_spaces - - @property - def current_line(self): - return self.text_block.current_line.body - @current_line.setter - def current_line(self, text): - self.text_block.current_line.body = text - - @property - def cursor_position(self): - return self.current_line_index, self.current_character_index - @cursor_position.setter - def cursor_position(self, position): - self.current_line_index = position[0] - self.current_character_index = position[1] - - @property - def current_character_index(self): - return self.get_character_index() - @current_character_index.setter - def current_character_index(self, index): - self.set_cursor_position_horizontal(index) - - @property - def current_line_index(self): - return self.get_line_index() - @current_line_index.setter - def current_line_index(self, index): - self.set_cursor_position_vertical(index) - - @property - def line_amount(self): - return len(self.text_block.lines) - - @property - def text_before_cursor(self): - return self.current_line[:self.current_character_index] - - @property - def current_word(self): - return self.get_last_word(self.text_before_cursor) - - - @property - def lines(self): - return self.get_all_lines() - @lines.setter - def lines(self, lines): - cursor_position = self.cursor_position - text = "\n".join(lines) - self.text_block.from_string(text) - self.cursor_position = cursor_position - - def get_all_lines(self): - return list(self.iter_lines()) - - def iter_lines(self): - for line in self.text_block.lines: - yield line.body - - def set_line_text(self, line_index, text): - self.text_block.lines[line_index].body = text - - # 'bpy.context.sce' -> 'sce' - def get_last_word(self, text): - match = re.search("(?!\w*\W).*", text) - if match: return match.group() - return "" - - @property - def parents_of_current_word(self): - return self.get_parent_words(self.text_before_cursor) - - # 'bpy.context.sce' -> ['bpy', 'context'] - def get_parent_words(self, text): - parents = [] - text = self.text_before_cursor - while True: - parent = self.get_parent_word(text) - if parent is None: break - text = text[:-len(self.get_last_word(text))-1] - parents.append(parent) - parents.reverse() - return parents - - @property - def parent_of_current_word(self): - return self.get_parent_word(self.text_before_cursor) - - # 'bpy.context.sce' -> 'context' - def get_parent_word(self, text): - match = re.search("(\w+)\.(?!.*\W)", text) - if match: - return match.group(1) - return None - - @property - def text(self): - return self.text_block.as_string() - - def get_existing_words(self): - existing_words = [] - existing_parts = set(re.sub("[^\w]", " ", self.text).split()) - for part in existing_parts: - if not part.isdigit(): existing_words.append(part) - return existing_words - - def insert(self, text): - bpy.ops.text.insert(self.override, text = text) - - def get_current_text_after_pattern(self, pattern): - return self.get_text_after_pattern(pattern, self.text_before_cursor) - - def get_text_after_pattern(self, pattern, text): - match = self.get_last_match(pattern, text) - if match: - return text[match.end():] - - def get_last_match(self, pattern, text): - match = None - for match in re.finditer(pattern, text): pass - return match - - def search_pattern_in_current_line(self, pattern): - return re.search(pattern, self.current_line) - - def replace_current_word(self, new_word): - self.delete_current_word() - self.insert(new_word) - - def delete_current_word(self): - match = re.search("\w*$", self.text_before_cursor) - if match: - length = match.end() - match.start() - for i in range(length): - self.remove_character_before_cursor() - - # "this.is.a.test(type = 'myt" -> "this.is.a.test" - def get_current_function_path(self): - text = self.text_before_cursor - open_bracket_index = self.get_current_open_bracket_index(text) - if open_bracket_index == -1: return None - text_before = text[:open_bracket_index-1] - match = re.search(r"(\w[\w\.]+\w)$", text_before) - if match: - return match.group(1) - return None - - # "test = this.is.anoth" -> "this.is" | "test = this.is.anoth." -> "this.is.anoth" - def get_current_parent_path(self): - path = self.get_current_path() - if self.text_before_cursor.endswith("."): return path - match = re.search(r"([\w\.]+)\.(?!.*\.)", path) - if match: - return match.group(1) - return "" - - # "test = this.is.anoth" -> "this.is.anoth" - def get_current_path(self): - text_before = self.text_before_cursor - match = re.search("(\w[\w\.]+\w)\.?$", text_before) - if match: - return match.group(1) - return "" - - # " event.type = 't" -> "event.type" - def get_current_line_assign_variable_path(self): - text_before = self.text_before_cursor - match = re.fullmatch("\s*([\w\.]+)\s*=.*", text_before) - if match: - return match.group(1) - return None - - # "if event.type == "A" and event.value != 'RE" -> "event.value" - def get_current_compare_variable_path(self): - text_before = self.text_before_cursor - match = self.get_last_match("([\w\.]+)\s*(==|<|>|!=|(not )?in \[)", text_before) - if match: - return match.group(1) - return None - - def get_current_open_bracket_index(self, text): - close_bracket_counter = 0 - current_open_bracket_index = -1 - - for i, c in enumerate(reversed(text)): - if c == ")": close_bracket_counter += 1 - elif c == "(": - if close_bracket_counter > 0: close_bracket_counter -= 1 - else: - current_open_bracket_index = len(text) - i - break - return current_open_bracket_index - - def select_match_in_current_line(self, match): - if match: - self.set_selection_in_line(match.start() + 1, match.end() + 1) - - def delete_selection(self): - self.insert(" ") - self.remove_character_before_cursor() - - def get_string_definition_type(self, text, current_index): - string_letter = None - for i in range(current_index): - letter = text[i] - if letter == '"': - if string_letter == '"': - string_letter = None - elif string_letter is None: - string_letter = letter - if letter == "'": - if string_letter == "'": - string_letter = None - elif string_letter is None: - string_letter = letter - return string_letter - - def get_range_surrounded_by_letter(self, text, letter, current_index): - text_before = text[:current_index] - text_after = text[current_index:] - - start_index = text_before.rfind(letter) + 1 - end_index = text_after.find(letter) + len(text_before) - - if 0 < start_index < end_index: - return start_index+1, end_index+1 - return current_index, current_index - - def select_text_in_current_line(self, text): - line = self.current_line - start = line.find(text) - if start != -1: - end = start + len(text) - self.set_selection_in_line(start + 1, end + 1) - - def set_selection_in_line(self, start, end): - line = self.current_line_index - if start > end: start, end = end, start - self.set_selection(line, start, line, end) - - def set_selection(self, start_line, start_character, end_line, end_character): - self.set_cursor_position(start_line, start_character, select = False) - self.set_cursor_position(end_line, end_character, select = True) - - def set_cursor_position(self, line_index, character_index, select = False): - self.set_cursor_position_vertical(line_index, select) - self.set_cursor_position_horizontal(character_index, select) - - def set_cursor_position_horizontal(self, target_index, select = False): - self.move_cursor_to_line_end(select) - self.move_cursor_left_to_target_index(target_index, select) - - def move_cursor_left_to_target_index(self, target_index, select): - target_index = max(1, target_index) - while self.get_character_index(select) >= target_index: - self.move_cursor_left(select) - - def set_cursor_position_vertical(self, target_line, select = False): - move_function = self.move_cursor_up - if target_line > self.get_line_index(select): - move_function = self.move_cursor_down - move_amount = abs(self.current_line_index - target_line) - for i in range(move_amount): - move_function(select) - - def get_character_index(self, select = False): - if select: return self.text_block.select_end_character - return self.text_block.current_character - def get_line_index(self, select = False): - return self.text_block.current_line_index - - def move_cursor_to_line_begin(self, select = False): - self.move_cursor("LINE_BEGIN", select) - def move_cursor_to_line_end(self, select = False): - self.move_cursor("LINE_END", select) - - # note: this may change the character index more than one (if there is TAB) - def move_cursor_right(self, select = False): - self.move_cursor("NEXT_CHARACTER", select) - def move_cursor_left(self, select = False): - self.move_cursor("PREVIOUS_CHARACTER", select) - - def move_cursor_up(self, select = False): - self.move_cursor("PREVIOUS_LINE", select) - def move_cursor_down(self, select = False): - self.move_cursor("NEXT_LINE", select) - - def move_cursor(self, type, select = False): - if select: bpy.ops.text.move_select(self.override, type = type) - else: bpy.ops.text.move(self.override, type = type) - - def remove_character_before_cursor(self): - bpy.ops.text.delete(self.override, type = "PREVIOUS_CHARACTER") - - def line_break(self): - bpy.ops.text.line_break(self.override) - - @property - def override(self): - return {"edit_text" : self.text_block, - "area" : self.area, - "space_data" : self.space, - "region" : self.region, - "window" : self.window} +import bpy, re +from mathutils import Vector + +class TextBlock: + def __init__(self, text_block): + if text_block is None: raise AttributeError() + self.text_block = text_block + self.set_context() + + def set_context(self, window = None, area = None, region = None, space = None): + context = bpy.context + self.window = window if window else context.window + self.area = area if window else context.area + self.region = region if window else context.region + self.space = space if window else context.space_data + + @classmethod + def get_active(cls): + text = getattr(bpy.context.space_data, "text", None) + if text: return TextBlock(text) + return None + + @property + def filepath(self): + return self.text_block.filepath + + @property + def current_cursor_region_location(self): + space = bpy.context.space_data + line = self.current_line_index + character = self.current_character_index + location = space.region_location_from_cursor(line, character) + return Vector(location) + + @property + def use_tabs_as_spaces(self): + return self.text_block.use_tabs_as_spaces + + @property + def current_line(self): + return self.text_block.current_line.body + @current_line.setter + def current_line(self, text): + self.text_block.current_line.body = text + + @property + def cursor_position(self): + return self.current_line_index, self.current_character_index + @cursor_position.setter + def cursor_position(self, position): + self.current_line_index = position[0] + self.current_character_index = position[1] + + @property + def current_character_index(self): + return self.get_character_index() + @current_character_index.setter + def current_character_index(self, index): + self.set_cursor_position_horizontal(index) + + @property + def current_line_index(self): + return self.get_line_index() + @current_line_index.setter + def current_line_index(self, index): + self.set_cursor_position_vertical(index) + + @property + def line_amount(self): + return len(self.text_block.lines) + + @property + def text_before_cursor(self): + return self.current_line[:self.current_character_index] + + @property + def current_word(self): + return self.get_last_word(self.text_before_cursor) + + + @property + def lines(self): + return self.get_all_lines() + @lines.setter + def lines(self, lines): + cursor_position = self.cursor_position + text = "\n".join(lines) + self.text_block.from_string(text) + self.cursor_position = cursor_position + + def get_all_lines(self): + return list(self.iter_lines()) + + def iter_lines(self): + for line in self.text_block.lines: + yield line.body + + def set_line_text(self, line_index, text): + self.text_block.lines[line_index].body = text + + # 'bpy.context.sce' -> 'sce' + def get_last_word(self, text): + match = re.search("(?!\w*\W).*", text) + if match: return match.group() + return "" + + @property + def parents_of_current_word(self): + return self.get_parent_words(self.text_before_cursor) + + # 'bpy.context.sce' -> ['bpy', 'context'] + def get_parent_words(self, text): + parents = [] + text = self.text_before_cursor + while True: + parent = self.get_parent_word(text) + if parent is None: break + text = text[:-len(self.get_last_word(text))-1] + parents.append(parent) + parents.reverse() + return parents + + @property + def parent_of_current_word(self): + return self.get_parent_word(self.text_before_cursor) + + # 'bpy.context.sce' -> 'context' + def get_parent_word(self, text): + match = re.search("(\w+)\.(?!.*\W)", text) + if match: + return match.group(1) + return None + + @property + def text(self): + return self.text_block.as_string() + + def get_existing_words(self): + existing_words = [] + existing_parts = set(re.sub("[^\w]", " ", self.text).split()) + for part in existing_parts: + if not part.isdigit(): existing_words.append(part) + return existing_words + + def insert(self, text): + bpy.ops.text.insert(self.override, text = text) + + def get_current_text_after_pattern(self, pattern): + return self.get_text_after_pattern(pattern, self.text_before_cursor) + + def get_text_after_pattern(self, pattern, text): + match = self.get_last_match(pattern, text) + if match: + return text[match.end():] + + def get_last_match(self, pattern, text): + match = None + for match in re.finditer(pattern, text): pass + return match + + def search_pattern_in_current_line(self, pattern): + return re.search(pattern, self.current_line) + + def replace_current_word(self, new_word): + self.delete_current_word() + self.insert(new_word) + + def delete_current_word(self): + match = re.search("\w*$", self.text_before_cursor) + if match: + length = match.end() - match.start() + for i in range(length): + self.remove_character_before_cursor() + + # "this.is.a.test(type = 'myt" -> "this.is.a.test" + def get_current_function_path(self): + text = self.text_before_cursor + open_bracket_index = self.get_current_open_bracket_index(text) + if open_bracket_index == -1: return None + text_before = text[:open_bracket_index-1] + match = re.search(r"(\w[\w\.]+\w)$", text_before) + if match: + return match.group(1) + return None + + # "test = this.is.anoth" -> "this.is" | "test = this.is.anoth." -> "this.is.anoth" + def get_current_parent_path(self): + path = self.get_current_path() + if self.text_before_cursor.endswith("."): return path + match = re.search(r"([\w\.]+)\.(?!.*\.)", path) + if match: + return match.group(1) + return "" + + # "test = this.is.anoth" -> "this.is.anoth" + def get_current_path(self): + text_before = self.text_before_cursor + match = re.search("(\w[\w\.]+\w)\.?$", text_before) + if match: + return match.group(1) + return "" + + # " event.type = 't" -> "event.type" + def get_current_line_assign_variable_path(self): + text_before = self.text_before_cursor + match = re.fullmatch("\s*([\w\.]+)\s*=.*", text_before) + if match: + return match.group(1) + return None + + # "if event.type == "A" and event.value != 'RE" -> "event.value" + def get_current_compare_variable_path(self): + text_before = self.text_before_cursor + match = self.get_last_match("([\w\.]+)\s*(==|<|>|!=|(not )?in \[)", text_before) + if match: + return match.group(1) + return None + + def get_current_open_bracket_index(self, text): + close_bracket_counter = 0 + current_open_bracket_index = -1 + + for i, c in enumerate(reversed(text)): + if c == ")": close_bracket_counter += 1 + elif c == "(": + if close_bracket_counter > 0: close_bracket_counter -= 1 + else: + current_open_bracket_index = len(text) - i + break + return current_open_bracket_index + + def select_match_in_current_line(self, match): + if match: + self.set_selection_in_line(match.start() + 1, match.end() + 1) + + def delete_selection(self): + self.insert(" ") + self.remove_character_before_cursor() + + def get_string_definition_type(self, text, current_index): + string_letter = None + for i in range(current_index): + letter = text[i] + if letter == '"': + if string_letter == '"': + string_letter = None + elif string_letter is None: + string_letter = letter + if letter == "'": + if string_letter == "'": + string_letter = None + elif string_letter is None: + string_letter = letter + return string_letter + + def get_range_surrounded_by_letter(self, text, letter, current_index): + text_before = text[:current_index] + text_after = text[current_index:] + + start_index = text_before.rfind(letter) + 1 + end_index = text_after.find(letter) + len(text_before) + + if 0 < start_index < end_index: + return start_index+1, end_index+1 + return current_index, current_index + + def select_text_in_current_line(self, text): + line = self.current_line + start = line.find(text) + if start != -1: + end = start + len(text) + self.set_selection_in_line(start + 1, end + 1) + + def set_selection_in_line(self, start, end): + line = self.current_line_index + if start > end: start, end = end, start + self.set_selection(line, start, line, end) + + def set_selection(self, start_line, start_character, end_line, end_character): + self.set_cursor_position(start_line, start_character, select = False) + self.set_cursor_position(end_line, end_character, select = True) + + def set_cursor_position(self, line_index, character_index, select = False): + self.set_cursor_position_vertical(line_index, select) + self.set_cursor_position_horizontal(character_index, select) + + def set_cursor_position_horizontal(self, target_index, select = False): + self.move_cursor_to_line_end(select) + self.move_cursor_left_to_target_index(target_index, select) + + def move_cursor_left_to_target_index(self, target_index, select): + target_index = max(1, target_index) + while self.get_character_index(select) >= target_index: + self.move_cursor_left(select) + + def set_cursor_position_vertical(self, target_line, select = False): + move_function = self.move_cursor_up + if target_line > self.get_line_index(select): + move_function = self.move_cursor_down + move_amount = abs(self.current_line_index - target_line) + for i in range(move_amount): + move_function(select) + + def get_character_index(self, select = False): + if select: return self.text_block.select_end_character + return self.text_block.current_character + def get_line_index(self, select = False): + return self.text_block.current_line_index + + def move_cursor_to_line_begin(self, select = False): + self.move_cursor("LINE_BEGIN", select) + def move_cursor_to_line_end(self, select = False): + self.move_cursor("LINE_END", select) + + # note: this may change the character index more than one (if there is TAB) + def move_cursor_right(self, select = False): + self.move_cursor("NEXT_CHARACTER", select) + def move_cursor_left(self, select = False): + self.move_cursor("PREVIOUS_CHARACTER", select) + + def move_cursor_up(self, select = False): + self.move_cursor("PREVIOUS_LINE", select) + def move_cursor_down(self, select = False): + self.move_cursor("NEXT_LINE", select) + + def move_cursor(self, type, select = False): + if select: bpy.ops.text.move_select(self.override, type = type) + else: bpy.ops.text.move(self.override, type = type) + + def remove_character_before_cursor(self): + bpy.ops.text.delete(self.override, type = "PREVIOUS_CHARACTER") + + def line_break(self): + bpy.ops.text.line_break(self.override) + + @property + def override(self): + return {"edit_text" : self.text_block, + "area" : self.area, + "space_data" : self.space, + "region" : self.region, + "window" : self.window} diff --git a/utils/__pycache__/__init__.cpython-37.pyc b/utils/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53353cbf63419ae86dd8791663e433a8113b123e GIT binary patch literal 199 zcmZ?b<>g`k0-nW}V?p#|5CH>>K!yVl7qb9~6oz01O-8?!3`HPe1o5lD*(xTqIJKxa zrW8oVI2IJRB$gz`1m!2@X6B{GIOU|~rKA=qxaF7Tr6iVQ=I6nLVvO`GjADwDi!uvJ zienN}Qu6bPW0LbzQsWa#OY)QRa|?1(OHyM>OEPncW8&j8^D;}~CJ08#Yn{T{0Gt(f@n%{nG{N{xG#L1{J zP@Y3K4?##GX++kEWRf3}bt)ZMgOSO)oPv={S5Cv|NV-bA#xJlSD*?p;8jdySOVCXV zL`IINq>>e6$YDQU+2{V+gOYfxWLlNn+WIJUbIcdFKLum~Mjs z66(?xZBTs;^#6ovBM@nrLDKh?$21m;zJvmUJb_j>VT#0_z|^QzXpt!ixQ-5y6<+6I zdQ9iQ1)icWgZK-kzJd!^QB3-*aQuf4jA;_b>##D$5s8J}vhxclJ%Y>}u#fKxvP?GK z4S2zkX8LZy3jTxuKY^(*e_JpdO(z4!9zYpyGyQ6`CvE{bdBKL1l7gd+`>MxeR1XC{A@voF?AQ2o=ot{>xF?F@mW>;VVs=2Gx+p|LwjJ#hv?>md+7qBW zI6M#+W$+%HHp<}bF}KglmFJXGp|M4SN}i@l>lz29+F%WsySM^ci`v~@P2=NYpB!5u8q-_x=Y%O%!|E^H Ug?=LQNOcfW%L|ve+^xIyKie5q@Bjb+ literal 0 HcmV?d00001 diff --git a/utils/variable_name_conversion.py b/utils/variable_name_conversion.py index 6446051..0d118c1 100644 --- a/utils/variable_name_conversion.py +++ b/utils/variable_name_conversion.py @@ -1,36 +1,36 @@ -import re - -def get_valid_variable_name(name): - return re.sub("\W+", "", name) - -def get_lower_case_with_underscores(name): - words = get_words(name) - words = [word.lower() for word in words] - output = "_".join(words) - return output - -def get_separated_capitalized_words(name): - words = get_words(name) - words = [word.capitalize() for word in words] - output = " ".join(words) - return output - -def get_words(name): - words = [] - current_word = "" - for char in name: - if char.islower(): - current_word += char - if char.isupper(): - if current_word.isupper() or len(current_word) == 0: - current_word += char - else: - words.append(current_word) - current_word = char - if char == "_": - words.append(current_word) - current_word = "" - - words.append(current_word) - words = [word for word in words if len(word) > 0] - return words +import re + +def get_valid_variable_name(name): + return re.sub("\W+", "", name) + +def get_lower_case_with_underscores(name): + words = get_words(name) + words = [word.lower() for word in words] + output = "_".join(words) + return output + +def get_separated_capitalized_words(name): + words = get_words(name) + words = [word.capitalize() for word in words] + output = " ".join(words) + return output + +def get_words(name): + words = [] + current_word = "" + for char in name: + if char.islower(): + current_word += char + if char.isupper(): + if current_word.isupper() or len(current_word) == 0: + current_word += char + else: + words.append(current_word) + current_word = char + if char == "_": + words.append(current_word) + current_word = "" + + words.append(current_word) + words = [word for word in words if len(word) > 0] + return words