Skip to content
Snippets Groups Projects
development_class_viewer.py 6.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Alfonso de Leon Serra's avatar
    Alfonso de Leon Serra committed
    # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  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 2
    #  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, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    bl_info = {
    
    Campbell Barton's avatar
    Campbell Barton committed
        "name": "Class Viewer",
        "author": "Mackraken", "batFinger"
    
        "blender": (2, 80, 0),
    
    Campbell Barton's avatar
    Campbell Barton committed
        "location": "Text Editor > Toolbar, Text Editor > Right Click",
        "warning": "",
    
        "description": "List classes and definitions of a text block",
    
        "doc_url": "https://sites.google.com/site/aleonserra/home/scripts/class-viewer",
    
        "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
    
    Campbell Barton's avatar
    Campbell Barton committed
        "category": "Development"}
    
    Alfonso de Leon Serra's avatar
    Alfonso de Leon Serra committed
    import bpy
    
    from bpy.types import (
            Operator,
            Menu,
            )
    from bpy.props import IntProperty
    
    # lists all defs, comment or classes
    # space = current text editor
    # sort = sort result
    # tipo = kind of struct to look for
    # escape = character right next to the class or def name
    # note: GPL license block is skipped from the comment entries now
    
    
    def getfunc(space, sort, tipo="def ", escape=""):
        defs, license_clean, return_lists = [], [], []
    
        if space.type == "TEXT_EDITOR" and space.text is not None:
            txt = space.text
            line_block_start, line_block_end = None, None
    
            for i, l in enumerate(txt.lines):
                try:
                    line = l.body
                except:
                    line = ""
                if tipo == "# ":
                    linex, line_c = "", ""
                    if tipo in line:
                        # find the license block
                        block_start = line.find("BEGIN GPL LICENSE BLOCK")
                        if block_start != -1:
                            line_block_start = i + 1
    
                        block_end = line.find("END GPL LICENSE BLOCK")
                        if block_end != -1:
                            line_block_end = i + 1
    
    Campbell Barton's avatar
    Campbell Barton committed
                        end = line.find(escape)
    
                        # use line partition, get the last tuple element as it is the comment
                        line_c = line.partition("#")[2]
                        # strip the spaces from the left if any and
                        # cut the comment at the 60 characters length max
                        linex = line_c[:60].lstrip() if len(line_c) > 60 else line_c.lstrip()
                        if end == 0:
                            func = linex
    
    Campbell Barton's avatar
    Campbell Barton committed
                        else:
    
                            func = linex.rstrip(escape)
    
                        defs.append([func, i + 1])
    
                elif line[0: len(tipo)] == tipo:
                    end = line.find(escape)
                    if end == 0:
                        func = line[len(tipo)::]
                    else:
                        func = line[len(tipo):end]
                    defs.append([func, i + 1])
    
            if line_block_start and line_block_end:
                # note : the slice should keep the first line of the license block
                license_clean = defs[line_block_start: line_block_end]
    
            return_lists = [line for line in defs if line not in license_clean] if license_clean else defs
            if sort:
                return_lists.sort()
    
        return return_lists
    
    
    def draw_menus(layout, space, tipo, escape):
    
        items = getfunc(space, 1, tipo, escape)
        if not items:
            layout.label(text="Not found", icon="INFO")
            return
    
        for it in items:
            layout.operator("text.jumptoline", text="{}".format(it[0])).line = it[1]
    
    
    class BaseCheckPoll():
    
    Campbell Barton's avatar
    Campbell Barton committed
        @classmethod
        def poll(cls, context):
    
            if context.area.spaces[0].type != "TEXT_EDITOR":
    
    Campbell Barton's avatar
    Campbell Barton committed
                return False
    
            else:
                return context.area.spaces[0].text is not None
    
    
    class TEXT_OT_Jumptoline(Operator, BaseCheckPoll):
        bl_label = "Jump"
        bl_idname = "text.jumptoline"
        bl_description = "Jump to line containing the text"
        __doc__ = "Jump to line containing the passed line integer. Used by the Class Viewer add-on"
    
    
        line: IntProperty(default=-1)
    
    Campbell Barton's avatar
    Campbell Barton committed
        def execute(self, context):
    
            if self.line == -1:
                self.report({'WARNING'}, "No valid line found. Operation Cancelled")
                return {'CANCELLED'}
    
            bpy.ops.text.jump(line=self.line)
            self.report({'INFO'}, "Jump to line: {}".format(self.line))
    
    Campbell Barton's avatar
    Campbell Barton committed
            return {'FINISHED'}
    
    class CommentsMenu(Menu, BaseCheckPoll):
    
    Campbell Barton's avatar
    Campbell Barton committed
        bl_idname = "OBJECT_MT_select_comments"
    
    Campbell Barton's avatar
    Campbell Barton committed
        def draw(self, context):
            layout = self.layout
            space = context.area.spaces[0]
    
            draw_menus(layout, space, "# ", "\n")
    
    
    class DefsMenu(Menu, BaseCheckPoll):
    
    Campbell Barton's avatar
    Campbell Barton committed
        bl_idname = "OBJECT_MT_select_defs"
    
    Campbell Barton's avatar
    Campbell Barton committed
        def draw(self, context):
            layout = self.layout
            space = context.area.spaces[0]
    
            draw_menus(layout, space, "def ", "(")
    
    
    class ClassesMenu(Menu, BaseCheckPoll):
    
    Campbell Barton's avatar
    Campbell Barton committed
        bl_idname = "OBJECT_MT_select_classes"
    
    Campbell Barton's avatar
    Campbell Barton committed
        def draw(self, context):
            layout = self.layout
            space = context.area.spaces[0]
    
            draw_menus(layout, space, "class ", "(")
    
    Campbell Barton's avatar
    Campbell Barton committed
    
    
    def menu_development_class_view(self, context):
        self.layout.separator()
    
        self.layout.menu("OBJECT_MT_select_comments", icon='SHORTDISPLAY')
        self.layout.menu("OBJECT_MT_select_defs", icon='FILE_TEXT')
        self.layout.menu("OBJECT_MT_select_classes", icon='SCRIPTPLUGINS')
    
    classes = (
        TEXT_OT_Jumptoline,
        ClassesMenu,
        DefsMenu,
        CommentsMenu,
        )
    
    Alfonso de Leon Serra's avatar
    Alfonso de Leon Serra committed
    
    
    def register():
    
        for cls in classes:
            bpy.utils.register_class(cls)
    
    
        bpy.types.TEXT_MT_context_menu.append(menu_development_class_view)
    
    Alfonso de Leon Serra's avatar
    Alfonso de Leon Serra committed
    
    def unregister():
    
        for cls in classes:
            bpy.utils.unregister_class(cls)
    
    
        bpy.types.TEXT_MT_context_menu.remove(menu_development_class_view)
    
    Alfonso de Leon Serra's avatar
    Alfonso de Leon Serra committed
    if __name__ == "__main__":
    
    Campbell Barton's avatar
    Campbell Barton committed
        register()