UI: Move Extensions repositories popover to header
[blender-addons-contrib.git] / development_class_viewer.py
blob841f81a823b39e2e00039b52bc9346697e0a4018
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "Class Viewer",
21 "author": "Mackraken", "batFinger"
22 "version": (0, 1, 3),
23 "blender": (2, 80, 0),
24 "location": "Text Editor > Toolbar, Text Editor > Right Click",
25 "warning": "",
26 "description": "List classes and definitions of a text block",
27 "doc_url": "https://sites.google.com/site/aleonserra/home/scripts/class-viewer",
28 "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
29 "category": "Development"}
32 import bpy
33 from bpy.types import (
34 Operator,
35 Menu,
37 from bpy.props import IntProperty
39 # lists all defs, comment or classes
40 # space = current text editor
41 # sort = sort result
42 # tipo = kind of struct to look for
43 # escape = character right next to the class or def name
44 # note: GPL license block is skipped from the comment entries now
47 def getfunc(space, sort, tipo="def ", escape=""):
48 defs, license_clean, return_lists = [], [], []
50 if space.type == "TEXT_EDITOR" and space.text is not None:
51 txt = space.text
52 line_block_start, line_block_end = None, None
54 for i, l in enumerate(txt.lines):
55 try:
56 line = l.body
57 except:
58 line = ""
59 if tipo == "# ":
60 linex, line_c = "", ""
61 if tipo in line:
62 # find the license block
63 block_start = line.find("BEGIN GPL LICENSE BLOCK")
64 if block_start != -1:
65 line_block_start = i + 1
67 block_end = line.find("END GPL LICENSE BLOCK")
68 if block_end != -1:
69 line_block_end = i + 1
71 end = line.find(escape)
72 # use line partition, get the last tuple element as it is the comment
73 line_c = line.partition("#")[2]
74 # strip the spaces from the left if any and
75 # cut the comment at the 60 characters length max
76 linex = line_c[:60].lstrip() if len(line_c) > 60 else line_c.lstrip()
77 if end == 0:
78 func = linex
79 else:
80 func = linex.rstrip(escape)
82 defs.append([func, i + 1])
84 elif line[0: len(tipo)] == tipo:
85 end = line.find(escape)
86 if end == 0:
87 func = line[len(tipo)::]
88 else:
89 func = line[len(tipo):end]
90 defs.append([func, i + 1])
92 if line_block_start and line_block_end:
93 # note : the slice should keep the first line of the license block
94 license_clean = defs[line_block_start: line_block_end]
96 return_lists = [line for line in defs if line not in license_clean] if license_clean else defs
97 if sort:
98 return_lists.sort()
100 return return_lists
103 def draw_menus(layout, space, tipo, escape):
105 items = getfunc(space, 1, tipo, escape)
106 if not items:
107 layout.label(text="Not found", icon="INFO")
108 return
110 for it in items:
111 layout.operator("text.jumptoline", text="{}".format(it[0])).line = it[1]
114 class BaseCheckPoll():
115 @classmethod
116 def poll(cls, context):
117 if context.area.spaces[0].type != "TEXT_EDITOR":
118 return False
119 else:
120 return context.area.spaces[0].text is not None
123 class TEXT_OT_Jumptoline(Operator, BaseCheckPoll):
124 bl_label = "Jump"
125 bl_idname = "text.jumptoline"
126 bl_description = "Jump to line containing the text"
127 __doc__ = "Jump to line containing the passed line integer. Used by the Class Viewer add-on"
129 line: IntProperty(default=-1)
131 def execute(self, context):
132 if self.line == -1:
133 self.report({'WARNING'}, "No valid line found. Operation Cancelled")
134 return {'CANCELLED'}
136 bpy.ops.text.jump(line=self.line)
137 self.report({'INFO'}, "Jump to line: {}".format(self.line))
139 return {'FINISHED'}
142 class CommentsMenu(Menu, BaseCheckPoll):
143 bl_idname = "OBJECT_MT_select_comments"
144 bl_label = "Comments"
146 def draw(self, context):
147 layout = self.layout
148 space = context.area.spaces[0]
149 draw_menus(layout, space, "# ", "\n")
152 class DefsMenu(Menu, BaseCheckPoll):
153 bl_idname = "OBJECT_MT_select_defs"
154 bl_label = "Definitions"
156 def draw(self, context):
157 layout = self.layout
158 space = context.area.spaces[0]
159 draw_menus(layout, space, "def ", "(")
162 class ClassesMenu(Menu, BaseCheckPoll):
163 bl_idname = "OBJECT_MT_select_classes"
164 bl_label = "Classes"
166 def draw(self, context):
167 layout = self.layout
168 space = context.area.spaces[0]
169 draw_menus(layout, space, "class ", "(")
172 def menu_development_class_view(self, context):
173 self.layout.separator()
175 self.layout.menu("OBJECT_MT_select_comments", icon='SHORTDISPLAY')
176 self.layout.menu("OBJECT_MT_select_defs", icon='FILE_TEXT')
177 self.layout.menu("OBJECT_MT_select_classes", icon='SCRIPTPLUGINS')
180 classes = (
181 TEXT_OT_Jumptoline,
182 ClassesMenu,
183 DefsMenu,
184 CommentsMenu,
188 def register():
189 for cls in classes:
190 bpy.utils.register_class(cls)
192 bpy.types.TEXT_MT_context_menu.append(menu_development_class_view)
195 def unregister():
196 for cls in classes:
197 bpy.utils.unregister_class(cls)
199 bpy.types.TEXT_MT_context_menu.remove(menu_development_class_view)
202 if __name__ == "__main__":
203 register()