204 lines
6.2 KiB
Python
204 lines
6.2 KiB
Python
# ##### 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 = {
|
|
"name": "Class Viewer",
|
|
"author": "Mackraken", "batFinger"
|
|
"version": (0, 1, 3),
|
|
"blender": (2, 80, 0),
|
|
"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/",
|
|
"category": "Development"}
|
|
|
|
|
|
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
|
|
|
|
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
|
|
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():
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if context.area.spaces[0].type != "TEXT_EDITOR":
|
|
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)
|
|
|
|
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))
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class CommentsMenu(Menu, BaseCheckPoll):
|
|
bl_idname = "OBJECT_MT_select_comments"
|
|
bl_label = "Comments"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
space = context.area.spaces[0]
|
|
draw_menus(layout, space, "# ", "\n")
|
|
|
|
|
|
class DefsMenu(Menu, BaseCheckPoll):
|
|
bl_idname = "OBJECT_MT_select_defs"
|
|
bl_label = "Definitions"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
space = context.area.spaces[0]
|
|
draw_menus(layout, space, "def ", "(")
|
|
|
|
|
|
class ClassesMenu(Menu, BaseCheckPoll):
|
|
bl_idname = "OBJECT_MT_select_classes"
|
|
bl_label = "Classes"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
space = context.area.spaces[0]
|
|
draw_menus(layout, space, "class ", "(")
|
|
|
|
|
|
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,
|
|
)
|
|
|
|
|
|
def register():
|
|
for cls in classes:
|
|
bpy.utils.register_class(cls)
|
|
|
|
bpy.types.TEXT_MT_context_menu.append(menu_development_class_view)
|
|
|
|
|
|
def unregister():
|
|
for cls in classes:
|
|
bpy.utils.unregister_class(cls)
|
|
|
|
bpy.types.TEXT_MT_context_menu.remove(menu_development_class_view)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register()
|