reveal-cli 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plugins/c-header.yaml +89 -0
- plugins/gdscript.yaml +96 -0
- plugins/python.yaml +94 -0
- plugins/yaml.yaml +87 -0
- reveal/__init__.py +20 -0
- reveal/_archive_old_v0.1/analyzers/__init__.py +39 -0
- reveal/_archive_old_v0.1/analyzers/base.py +143 -0
- reveal/_archive_old_v0.1/analyzers/bash_analyzer.py +202 -0
- reveal/_archive_old_v0.1/analyzers/csharp_analyzer.py +167 -0
- reveal/_archive_old_v0.1/analyzers/gdscript_analyzer.py +201 -0
- reveal/_archive_old_v0.1/analyzers/go_analyzer.py +230 -0
- reveal/_archive_old_v0.1/analyzers/javascript_analyzer.py +272 -0
- reveal/_archive_old_v0.1/analyzers/json_analyzer.py +143 -0
- reveal/_archive_old_v0.1/analyzers/markdown_analyzer.py +116 -0
- reveal/_archive_old_v0.1/analyzers/php_analyzer.py +166 -0
- reveal/_archive_old_v0.1/analyzers/python_analyzer.py +118 -0
- reveal/_archive_old_v0.1/analyzers/rust_analyzer.py +202 -0
- reveal/_archive_old_v0.1/analyzers/sql_analyzer.py +534 -0
- reveal/_archive_old_v0.1/analyzers/text_analyzer.py +35 -0
- reveal/_archive_old_v0.1/analyzers/toml_analyzer.py +137 -0
- reveal/_archive_old_v0.1/analyzers/treesitter_base.py +276 -0
- reveal/_archive_old_v0.1/analyzers/yaml_analyzer.py +127 -0
- reveal/_archive_old_v0.1/breadcrumbs.py +147 -0
- reveal/_archive_old_v0.1/cli.py +445 -0
- reveal/_archive_old_v0.1/core.py +136 -0
- reveal/_archive_old_v0.1/detectors.py +29 -0
- reveal/_archive_old_v0.1/formatters.py +272 -0
- reveal/_archive_old_v0.1/grep_filter.py +85 -0
- reveal/_archive_old_v0.1/plugin_loader.py +178 -0
- reveal/_archive_old_v0.1/registry.py +286 -0
- reveal/analyzers/__init__.py +23 -0
- reveal/analyzers/go.py +13 -0
- reveal/analyzers/jupyter_analyzer.py +226 -0
- reveal/analyzers/markdown.py +79 -0
- reveal/analyzers/python.py +15 -0
- reveal/analyzers/rust.py +13 -0
- reveal/analyzers/yaml_json.py +110 -0
- reveal/base.py +193 -0
- reveal/main.py +216 -0
- reveal/tests/__init__.py +1 -0
- reveal/tests/test_json_yaml_line_numbers.py +238 -0
- reveal/tests/test_line_numbers.py +151 -0
- reveal/tests/test_toml_analyzer.py +220 -0
- reveal/tree_view.py +105 -0
- reveal/treesitter.py +282 -0
- reveal_cli-0.2.0.dist-info/METADATA +319 -0
- reveal_cli-0.2.0.dist-info/RECORD +51 -0
- reveal_cli-0.2.0.dist-info/WHEEL +5 -0
- reveal_cli-0.2.0.dist-info/entry_points.txt +2 -0
- reveal_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
- reveal_cli-0.2.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bash/Shell script analyzer using tree-sitter.
|
|
3
|
+
|
|
4
|
+
Extracts:
|
|
5
|
+
- Functions
|
|
6
|
+
- Variables
|
|
7
|
+
- Exported variables
|
|
8
|
+
- Source/include statements
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Dict, Any, List
|
|
12
|
+
from .treesitter_base import TreeSitterAnalyzer, TREE_SITTER_AVAILABLE
|
|
13
|
+
from ..registry import register
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@register(['.sh', '.bash', '.zsh'], name='Bash', icon='🐚')
|
|
17
|
+
class BashAnalyzer(TreeSitterAnalyzer):
|
|
18
|
+
"""
|
|
19
|
+
Analyzer for Bash/Shell script files.
|
|
20
|
+
|
|
21
|
+
Features:
|
|
22
|
+
- Function definitions
|
|
23
|
+
- Variable assignments
|
|
24
|
+
- Exported variables
|
|
25
|
+
- Source statements
|
|
26
|
+
|
|
27
|
+
All positions are exact (tree-sitter preserves source locations).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
language_name = 'bash'
|
|
31
|
+
|
|
32
|
+
def __init__(self, lines: List[str], **kwargs):
|
|
33
|
+
"""Initialize Bash analyzer."""
|
|
34
|
+
self.node_type_map = {
|
|
35
|
+
'function': 'function_definition',
|
|
36
|
+
}
|
|
37
|
+
super().__init__(lines, **kwargs)
|
|
38
|
+
|
|
39
|
+
def extract_custom(self) -> Dict[str, List[Dict[str, Any]]]:
|
|
40
|
+
"""Extract Bash-specific items."""
|
|
41
|
+
return {
|
|
42
|
+
'variables': self._extract_variables(),
|
|
43
|
+
'exports': self._extract_exports(),
|
|
44
|
+
'sources': self._extract_sources(),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def _extract_variables(self) -> List[Dict[str, Any]]:
|
|
48
|
+
"""Extract variable assignments."""
|
|
49
|
+
if not self.tree:
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
variables = []
|
|
53
|
+
cursor = self.tree.walk()
|
|
54
|
+
|
|
55
|
+
def visit(cursor):
|
|
56
|
+
node = cursor.node
|
|
57
|
+
|
|
58
|
+
if node.type == 'variable_assignment':
|
|
59
|
+
# Get variable name
|
|
60
|
+
name_node = node.child_by_field_name('name')
|
|
61
|
+
if name_node:
|
|
62
|
+
line = node.start_point[0] + 1
|
|
63
|
+
if self.in_focus_range(line):
|
|
64
|
+
variables.append({
|
|
65
|
+
'name': name_node.text.decode('utf-8'),
|
|
66
|
+
'line': line
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
# Recurse
|
|
70
|
+
if cursor.goto_first_child():
|
|
71
|
+
visit(cursor)
|
|
72
|
+
while cursor.goto_next_sibling():
|
|
73
|
+
visit(cursor)
|
|
74
|
+
cursor.goto_parent()
|
|
75
|
+
|
|
76
|
+
visit(cursor)
|
|
77
|
+
return variables
|
|
78
|
+
|
|
79
|
+
def _extract_exports(self) -> List[Dict[str, Any]]:
|
|
80
|
+
"""Extract export statements."""
|
|
81
|
+
if not self.tree:
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
exports = []
|
|
85
|
+
cursor = self.tree.walk()
|
|
86
|
+
|
|
87
|
+
def visit(cursor):
|
|
88
|
+
node = cursor.node
|
|
89
|
+
|
|
90
|
+
if node.type == 'declaration_command':
|
|
91
|
+
# Check if it's an export
|
|
92
|
+
for child in node.children:
|
|
93
|
+
if child.type == 'variable_assignment':
|
|
94
|
+
name_node = child.child_by_field_name('name')
|
|
95
|
+
if name_node:
|
|
96
|
+
line = node.start_point[0] + 1
|
|
97
|
+
if self.in_focus_range(line):
|
|
98
|
+
exports.append({
|
|
99
|
+
'name': name_node.text.decode('utf-8'),
|
|
100
|
+
'line': line
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
# Recurse
|
|
104
|
+
if cursor.goto_first_child():
|
|
105
|
+
visit(cursor)
|
|
106
|
+
while cursor.goto_next_sibling():
|
|
107
|
+
visit(cursor)
|
|
108
|
+
cursor.goto_parent()
|
|
109
|
+
|
|
110
|
+
visit(cursor)
|
|
111
|
+
return exports
|
|
112
|
+
|
|
113
|
+
def _extract_sources(self) -> List[Dict[str, Any]]:
|
|
114
|
+
"""Extract source/. statements."""
|
|
115
|
+
if not self.tree:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
sources = []
|
|
119
|
+
cursor = self.tree.walk()
|
|
120
|
+
|
|
121
|
+
def visit(cursor):
|
|
122
|
+
node = cursor.node
|
|
123
|
+
|
|
124
|
+
# Look for source command or . command
|
|
125
|
+
if node.type == 'command':
|
|
126
|
+
command_name = node.child_by_field_name('name')
|
|
127
|
+
if command_name:
|
|
128
|
+
cmd_text = command_name.text.decode('utf-8')
|
|
129
|
+
if cmd_text in ('source', '.'):
|
|
130
|
+
# Get the sourced file
|
|
131
|
+
full_text = node.text.decode('utf-8').strip()
|
|
132
|
+
line = node.start_point[0] + 1
|
|
133
|
+
if self.in_focus_range(line):
|
|
134
|
+
sources.append({
|
|
135
|
+
'name': full_text,
|
|
136
|
+
'line': line
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
# Recurse
|
|
140
|
+
if cursor.goto_first_child():
|
|
141
|
+
visit(cursor)
|
|
142
|
+
while cursor.goto_next_sibling():
|
|
143
|
+
visit(cursor)
|
|
144
|
+
cursor.goto_parent()
|
|
145
|
+
|
|
146
|
+
visit(cursor)
|
|
147
|
+
return sources
|
|
148
|
+
|
|
149
|
+
def format_structure(self, structure: Dict[str, Any]) -> List[str]:
|
|
150
|
+
"""
|
|
151
|
+
Custom formatting for Bash structure output.
|
|
152
|
+
|
|
153
|
+
Groups items logically:
|
|
154
|
+
1. Source statements
|
|
155
|
+
2. Functions
|
|
156
|
+
3. Exported variables
|
|
157
|
+
4. Variables
|
|
158
|
+
"""
|
|
159
|
+
if not TREE_SITTER_AVAILABLE or 'error' in structure:
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
lines = []
|
|
163
|
+
|
|
164
|
+
# Sources
|
|
165
|
+
sources = structure.get('sources', [])
|
|
166
|
+
if sources:
|
|
167
|
+
lines.append(f"\nSource statements ({len(sources)}):")
|
|
168
|
+
for item in sources[:10]:
|
|
169
|
+
loc = self.format_location(item['line'])
|
|
170
|
+
lines.append(f" {loc:30} {item['name']}")
|
|
171
|
+
if len(sources) > 10:
|
|
172
|
+
lines.append(f" ... and {len(sources) - 10} more")
|
|
173
|
+
|
|
174
|
+
# Functions
|
|
175
|
+
functions = structure.get('functions', [])
|
|
176
|
+
if functions:
|
|
177
|
+
lines.append(f"\nFunctions ({len(functions)}):")
|
|
178
|
+
for item in functions:
|
|
179
|
+
loc = self.format_location(item['line'])
|
|
180
|
+
lines.append(f" {loc:30} {item['name']}()")
|
|
181
|
+
|
|
182
|
+
# Exports
|
|
183
|
+
exports = structure.get('exports', [])
|
|
184
|
+
if exports:
|
|
185
|
+
lines.append(f"\nExported variables ({len(exports)}):")
|
|
186
|
+
for item in exports[:15]:
|
|
187
|
+
loc = self.format_location(item['line'])
|
|
188
|
+
lines.append(f" {loc:30} {item['name']}")
|
|
189
|
+
if len(exports) > 15:
|
|
190
|
+
lines.append(f" ... and {len(exports) - 15} more")
|
|
191
|
+
|
|
192
|
+
# Variables
|
|
193
|
+
variables = structure.get('variables', [])
|
|
194
|
+
if variables:
|
|
195
|
+
lines.append(f"\nVariables ({len(variables)}):")
|
|
196
|
+
for item in variables[:20]:
|
|
197
|
+
loc = self.format_location(item['line'])
|
|
198
|
+
lines.append(f" {loc:30} {item['name']}")
|
|
199
|
+
if len(variables) > 20:
|
|
200
|
+
lines.append(f" ... and {len(variables) - 20} more")
|
|
201
|
+
|
|
202
|
+
return lines if lines else None
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
C# file analyzer using tree-sitter.
|
|
3
|
+
|
|
4
|
+
Extracts C# structure including:
|
|
5
|
+
- Namespaces
|
|
6
|
+
- Classes
|
|
7
|
+
- Interfaces
|
|
8
|
+
- Structs
|
|
9
|
+
- Enums
|
|
10
|
+
- Methods
|
|
11
|
+
- Properties
|
|
12
|
+
- Using statements (imports)
|
|
13
|
+
|
|
14
|
+
Demonstrates the power of tree-sitter for enterprise languages.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from typing import Dict, Any, List
|
|
18
|
+
from .treesitter_base import TreeSitterAnalyzer, TREE_SITTER_AVAILABLE
|
|
19
|
+
from ..registry import register
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@register(['.cs'], name='C#', icon='🔷')
|
|
23
|
+
class CSharpAnalyzer(TreeSitterAnalyzer):
|
|
24
|
+
"""
|
|
25
|
+
Analyzer for C# source files.
|
|
26
|
+
|
|
27
|
+
Features:
|
|
28
|
+
- Full OOP structure (classes, interfaces, methods)
|
|
29
|
+
- Namespace detection
|
|
30
|
+
- Using statements
|
|
31
|
+
- Properties and fields
|
|
32
|
+
- Delegates and events
|
|
33
|
+
|
|
34
|
+
All positions are exact (tree-sitter preserves source locations).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
# Set language for tree-sitter
|
|
38
|
+
language_name = 'c_sharp'
|
|
39
|
+
|
|
40
|
+
def __init__(self, lines: List[str], **kwargs):
|
|
41
|
+
"""Initialize C# analyzer."""
|
|
42
|
+
# Map reveal's generic names to C#'s tree-sitter node types
|
|
43
|
+
self.node_type_map = {
|
|
44
|
+
'function': 'method_declaration',
|
|
45
|
+
'class': 'class_declaration',
|
|
46
|
+
'import': 'using_directive',
|
|
47
|
+
}
|
|
48
|
+
super().__init__(lines, **kwargs)
|
|
49
|
+
|
|
50
|
+
def extract_custom(self) -> Dict[str, List[Dict[str, Any]]]:
|
|
51
|
+
"""Extract C#-specific items."""
|
|
52
|
+
return {
|
|
53
|
+
'namespaces': self._extract_nodes('namespace_declaration', name_field='name'),
|
|
54
|
+
'interfaces': self._extract_nodes('interface_declaration', name_field='name'),
|
|
55
|
+
'structs': self._extract_nodes('struct_declaration', name_field='name'),
|
|
56
|
+
'enums': self._extract_nodes('enum_declaration', name_field='name'),
|
|
57
|
+
'properties': self._extract_nodes('property_declaration', name_field='name'),
|
|
58
|
+
'delegates': self._extract_nodes('delegate_declaration', name_field='name'),
|
|
59
|
+
'events': self._extract_nodes('event_declaration', name_field='name'),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def format_structure(self, structure: Dict[str, Any]) -> List[str]:
|
|
63
|
+
"""
|
|
64
|
+
Custom formatting for C# structure output.
|
|
65
|
+
|
|
66
|
+
Groups items logically:
|
|
67
|
+
1. Namespaces
|
|
68
|
+
2. Using statements
|
|
69
|
+
3. Types (classes, interfaces, structs, enums)
|
|
70
|
+
4. Members (methods, properties, events)
|
|
71
|
+
"""
|
|
72
|
+
if not TREE_SITTER_AVAILABLE or 'error' in structure:
|
|
73
|
+
return None # Use default formatting
|
|
74
|
+
|
|
75
|
+
lines = []
|
|
76
|
+
|
|
77
|
+
# Namespaces
|
|
78
|
+
namespaces = structure.get('namespaces', [])
|
|
79
|
+
if namespaces:
|
|
80
|
+
lines.append(f"\nNamespaces ({len(namespaces)}):")
|
|
81
|
+
for item in namespaces:
|
|
82
|
+
loc = self.format_location(item['line'])
|
|
83
|
+
lines.append(f" {loc:30} {item['name']}")
|
|
84
|
+
|
|
85
|
+
# Using statements
|
|
86
|
+
imports = structure.get('imports', [])
|
|
87
|
+
if imports:
|
|
88
|
+
lines.append(f"\nUsing statements ({len(imports)}):")
|
|
89
|
+
for item in imports[:10]: # Limit display
|
|
90
|
+
loc = self.format_location(item['line'])
|
|
91
|
+
# Clean up "using System;" -> "System"
|
|
92
|
+
name = item['name'].replace('using ', '').replace(';', '').strip()
|
|
93
|
+
lines.append(f" {loc:30} {name}")
|
|
94
|
+
if len(imports) > 10:
|
|
95
|
+
lines.append(f" ... and {len(imports) - 10} more")
|
|
96
|
+
|
|
97
|
+
# Types section
|
|
98
|
+
classes = structure.get('classes', [])
|
|
99
|
+
interfaces = structure.get('interfaces', [])
|
|
100
|
+
structs = structure.get('structs', [])
|
|
101
|
+
enums = structure.get('enums', [])
|
|
102
|
+
|
|
103
|
+
type_count = len(classes) + len(interfaces) + len(structs) + len(enums)
|
|
104
|
+
if type_count > 0:
|
|
105
|
+
lines.append(f"\nTypes ({type_count}):")
|
|
106
|
+
|
|
107
|
+
if classes:
|
|
108
|
+
lines.append(f" Classes ({len(classes)}):")
|
|
109
|
+
for item in classes:
|
|
110
|
+
loc = self.format_location(item['line'])
|
|
111
|
+
lines.append(f" {loc:28} {item['name']}")
|
|
112
|
+
|
|
113
|
+
if interfaces:
|
|
114
|
+
lines.append(f" Interfaces ({len(interfaces)}):")
|
|
115
|
+
for item in interfaces:
|
|
116
|
+
loc = self.format_location(item['line'])
|
|
117
|
+
lines.append(f" {loc:28} {item['name']}")
|
|
118
|
+
|
|
119
|
+
if structs:
|
|
120
|
+
lines.append(f" Structs ({len(structs)}):")
|
|
121
|
+
for item in structs:
|
|
122
|
+
loc = self.format_location(item['line'])
|
|
123
|
+
lines.append(f" {loc:28} {item['name']}")
|
|
124
|
+
|
|
125
|
+
if enums:
|
|
126
|
+
lines.append(f" Enums ({len(enums)}):")
|
|
127
|
+
for item in enums:
|
|
128
|
+
loc = self.format_location(item['line'])
|
|
129
|
+
lines.append(f" {loc:28} {item['name']}")
|
|
130
|
+
|
|
131
|
+
# Methods
|
|
132
|
+
methods = structure.get('functions', [])
|
|
133
|
+
if methods:
|
|
134
|
+
lines.append(f"\nMethods ({len(methods)}):")
|
|
135
|
+
for item in methods[:20]: # Limit display
|
|
136
|
+
loc = self.format_location(item['line'])
|
|
137
|
+
lines.append(f" {loc:30} {item['name']}()")
|
|
138
|
+
if len(methods) > 20:
|
|
139
|
+
lines.append(f" ... and {len(methods) - 20} more")
|
|
140
|
+
|
|
141
|
+
# Properties
|
|
142
|
+
properties = structure.get('properties', [])
|
|
143
|
+
if properties:
|
|
144
|
+
lines.append(f"\nProperties ({len(properties)}):")
|
|
145
|
+
for item in properties[:15]: # Limit display
|
|
146
|
+
loc = self.format_location(item['line'])
|
|
147
|
+
lines.append(f" {loc:30} {item['name']}")
|
|
148
|
+
if len(properties) > 15:
|
|
149
|
+
lines.append(f" ... and {len(properties) - 15} more")
|
|
150
|
+
|
|
151
|
+
# Events
|
|
152
|
+
events = structure.get('events', [])
|
|
153
|
+
if events:
|
|
154
|
+
lines.append(f"\nEvents ({len(events)}):")
|
|
155
|
+
for item in events:
|
|
156
|
+
loc = self.format_location(item['line'])
|
|
157
|
+
lines.append(f" {loc:30} {item['name']}")
|
|
158
|
+
|
|
159
|
+
# Delegates
|
|
160
|
+
delegates = structure.get('delegates', [])
|
|
161
|
+
if delegates:
|
|
162
|
+
lines.append(f"\nDelegates ({len(delegates)}):")
|
|
163
|
+
for item in delegates:
|
|
164
|
+
loc = self.format_location(item['line'])
|
|
165
|
+
lines.append(f" {loc:30} {item['name']}")
|
|
166
|
+
|
|
167
|
+
return lines if lines else None
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""GDScript file analyzer for Godot Engine."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Dict, Any, List
|
|
5
|
+
from .base import BaseAnalyzer
|
|
6
|
+
from ..registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@register(['.gd'], name='GDScript', icon='🎮')
|
|
10
|
+
class GDScriptAnalyzer(BaseAnalyzer):
|
|
11
|
+
"""Analyzer for GDScript files (Godot Engine)"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, lines: List[str], **kwargs):
|
|
14
|
+
super().__init__(lines, **kwargs)
|
|
15
|
+
|
|
16
|
+
def analyze_structure(self) -> Dict[str, Any]:
|
|
17
|
+
"""Analyze GDScript structure."""
|
|
18
|
+
extends_class = None
|
|
19
|
+
class_name = None
|
|
20
|
+
signals = []
|
|
21
|
+
exports = []
|
|
22
|
+
functions = []
|
|
23
|
+
constants = []
|
|
24
|
+
variables = []
|
|
25
|
+
|
|
26
|
+
for i, line in enumerate(self.lines, 1):
|
|
27
|
+
stripped = line.strip()
|
|
28
|
+
|
|
29
|
+
# extends declaration
|
|
30
|
+
if stripped.startswith('extends '):
|
|
31
|
+
extends_class = stripped[8:].strip()
|
|
32
|
+
|
|
33
|
+
# class_name declaration
|
|
34
|
+
elif stripped.startswith('class_name '):
|
|
35
|
+
class_name = stripped[11:].strip()
|
|
36
|
+
|
|
37
|
+
# signal definitions
|
|
38
|
+
elif stripped.startswith('signal '):
|
|
39
|
+
signal_match = re.match(r'signal\s+(\w+)', stripped)
|
|
40
|
+
if signal_match:
|
|
41
|
+
signals.append({'name': signal_match.group(1), 'line': i})
|
|
42
|
+
|
|
43
|
+
# @export variables
|
|
44
|
+
elif stripped.startswith('@export'):
|
|
45
|
+
# Look for variable on same line or next line
|
|
46
|
+
var_match = re.search(r'var\s+(\w+)', stripped)
|
|
47
|
+
if var_match:
|
|
48
|
+
exports.append({'name': var_match.group(1), 'line': i})
|
|
49
|
+
elif i < len(self.lines):
|
|
50
|
+
next_line = self.lines[i].strip()
|
|
51
|
+
var_match = re.search(r'var\s+(\w+)', next_line)
|
|
52
|
+
if var_match:
|
|
53
|
+
exports.append({'name': var_match.group(1), 'line': i + 1})
|
|
54
|
+
|
|
55
|
+
# const declarations
|
|
56
|
+
elif stripped.startswith('const '):
|
|
57
|
+
const_match = re.match(r'const\s+(\w+)', stripped)
|
|
58
|
+
if const_match:
|
|
59
|
+
constants.append({'name': const_match.group(1), 'line': i})
|
|
60
|
+
|
|
61
|
+
# var declarations (not exported)
|
|
62
|
+
elif stripped.startswith('var ') and not any(e['line'] == i for e in exports):
|
|
63
|
+
var_match = re.match(r'var\s+(\w+)', stripped)
|
|
64
|
+
if var_match:
|
|
65
|
+
variables.append({'name': var_match.group(1), 'line': i})
|
|
66
|
+
|
|
67
|
+
# function definitions
|
|
68
|
+
elif stripped.startswith('func '):
|
|
69
|
+
func_match = re.match(r'func\s+(\w+)', stripped)
|
|
70
|
+
if func_match:
|
|
71
|
+
func_name = func_match.group(1)
|
|
72
|
+
# Mark special Godot lifecycle functions
|
|
73
|
+
is_lifecycle = func_name in ['_ready', '_process', '_physics_process',
|
|
74
|
+
'_input', '_unhandled_input', '_enter_tree',
|
|
75
|
+
'_exit_tree', '_draw', '_notification']
|
|
76
|
+
functions.append({
|
|
77
|
+
'name': func_name,
|
|
78
|
+
'line': i,
|
|
79
|
+
'lifecycle': is_lifecycle
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
'extends': extends_class,
|
|
84
|
+
'class_name': class_name,
|
|
85
|
+
'signals': signals,
|
|
86
|
+
'exports': exports,
|
|
87
|
+
'constants': constants,
|
|
88
|
+
'variables': variables,
|
|
89
|
+
'functions': functions
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def generate_preview(self) -> List[tuple[int, str]]:
|
|
93
|
+
"""Generate GDScript preview showing key elements."""
|
|
94
|
+
preview = []
|
|
95
|
+
|
|
96
|
+
for i, line in enumerate(self.lines, 1):
|
|
97
|
+
stripped = line.strip()
|
|
98
|
+
|
|
99
|
+
# Include extends/class_name
|
|
100
|
+
if stripped.startswith('extends ') or stripped.startswith('class_name '):
|
|
101
|
+
preview.append((i, line))
|
|
102
|
+
|
|
103
|
+
# Include comments at the top (documentation)
|
|
104
|
+
elif stripped.startswith('#') and i <= 10:
|
|
105
|
+
preview.append((i, line))
|
|
106
|
+
|
|
107
|
+
# Include signal definitions
|
|
108
|
+
elif stripped.startswith('signal '):
|
|
109
|
+
preview.append((i, line))
|
|
110
|
+
|
|
111
|
+
# Include @export declarations
|
|
112
|
+
elif stripped.startswith('@export'):
|
|
113
|
+
preview.append((i, line))
|
|
114
|
+
# Include the variable line too
|
|
115
|
+
if i < len(self.lines) and self.lines[i].strip().startswith('var '):
|
|
116
|
+
preview.append((i + 1, self.lines[i]))
|
|
117
|
+
|
|
118
|
+
# Include const declarations
|
|
119
|
+
elif stripped.startswith('const '):
|
|
120
|
+
preview.append((i, line))
|
|
121
|
+
|
|
122
|
+
# Include function signatures
|
|
123
|
+
elif stripped.startswith('func '):
|
|
124
|
+
preview.append((i, line))
|
|
125
|
+
# Include function docstring if present
|
|
126
|
+
if i < len(self.lines):
|
|
127
|
+
next_line = self.lines[i].strip()
|
|
128
|
+
if next_line.startswith('#'):
|
|
129
|
+
preview.append((i + 1, self.lines[i]))
|
|
130
|
+
|
|
131
|
+
# Sort by line number and limit to first 40 lines
|
|
132
|
+
preview = sorted(list(set(preview)), key=lambda x: x[0])
|
|
133
|
+
return preview[:40]
|
|
134
|
+
|
|
135
|
+
def format_structure(self, structure: Dict[str, Any]) -> List[str]:
|
|
136
|
+
"""Format GDScript structure output."""
|
|
137
|
+
output = []
|
|
138
|
+
|
|
139
|
+
# Class inheritance
|
|
140
|
+
if structure['extends']:
|
|
141
|
+
output.append(f"Extends: {structure['extends']}")
|
|
142
|
+
if structure['class_name']:
|
|
143
|
+
output.append(f"Class Name: {structure['class_name']}")
|
|
144
|
+
|
|
145
|
+
if structure['extends'] or structure['class_name']:
|
|
146
|
+
output.append("")
|
|
147
|
+
|
|
148
|
+
# Signals
|
|
149
|
+
if structure['signals']:
|
|
150
|
+
output.append("Signals:")
|
|
151
|
+
for sig in structure['signals']:
|
|
152
|
+
loc = self.format_location(sig['line'])
|
|
153
|
+
output.append(f" {loc:<30} signal {sig['name']}")
|
|
154
|
+
output.append("")
|
|
155
|
+
|
|
156
|
+
# Exported variables
|
|
157
|
+
if structure['exports']:
|
|
158
|
+
output.append("Exported Variables:")
|
|
159
|
+
for exp in structure['exports']:
|
|
160
|
+
loc = self.format_location(exp['line'])
|
|
161
|
+
output.append(f" {loc:<30} @export var {exp['name']}")
|
|
162
|
+
output.append("")
|
|
163
|
+
|
|
164
|
+
# Constants
|
|
165
|
+
if structure['constants']:
|
|
166
|
+
output.append("Constants:")
|
|
167
|
+
for const in structure['constants']:
|
|
168
|
+
loc = self.format_location(const['line'])
|
|
169
|
+
output.append(f" {loc:<30} const {const['name']}")
|
|
170
|
+
output.append("")
|
|
171
|
+
|
|
172
|
+
# Variables
|
|
173
|
+
if structure['variables']:
|
|
174
|
+
output.append("Variables:")
|
|
175
|
+
for var in structure['variables'][:10]: # Limit to first 10
|
|
176
|
+
loc = self.format_location(var['line'])
|
|
177
|
+
output.append(f" {loc:<30} var {var['name']}")
|
|
178
|
+
if len(structure['variables']) > 10:
|
|
179
|
+
output.append(f" ... and {len(structure['variables']) - 10} more")
|
|
180
|
+
output.append("")
|
|
181
|
+
|
|
182
|
+
# Functions
|
|
183
|
+
if structure['functions']:
|
|
184
|
+
output.append("Functions:")
|
|
185
|
+
lifecycle = [f for f in structure['functions'] if f.get('lifecycle')]
|
|
186
|
+
regular = [f for f in structure['functions'] if not f.get('lifecycle')]
|
|
187
|
+
|
|
188
|
+
if lifecycle:
|
|
189
|
+
output.append(" Lifecycle:")
|
|
190
|
+
for func in lifecycle:
|
|
191
|
+
loc = self.format_location(func['line'])
|
|
192
|
+
output.append(f" {loc:<30} func {func['name']}()")
|
|
193
|
+
|
|
194
|
+
if regular:
|
|
195
|
+
if lifecycle:
|
|
196
|
+
output.append(" Custom:")
|
|
197
|
+
for func in regular:
|
|
198
|
+
loc = self.format_location(func['line'])
|
|
199
|
+
output.append(f" {loc:<30} func {func['name']}()")
|
|
200
|
+
|
|
201
|
+
return output
|