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.
Files changed (51) hide show
  1. plugins/c-header.yaml +89 -0
  2. plugins/gdscript.yaml +96 -0
  3. plugins/python.yaml +94 -0
  4. plugins/yaml.yaml +87 -0
  5. reveal/__init__.py +20 -0
  6. reveal/_archive_old_v0.1/analyzers/__init__.py +39 -0
  7. reveal/_archive_old_v0.1/analyzers/base.py +143 -0
  8. reveal/_archive_old_v0.1/analyzers/bash_analyzer.py +202 -0
  9. reveal/_archive_old_v0.1/analyzers/csharp_analyzer.py +167 -0
  10. reveal/_archive_old_v0.1/analyzers/gdscript_analyzer.py +201 -0
  11. reveal/_archive_old_v0.1/analyzers/go_analyzer.py +230 -0
  12. reveal/_archive_old_v0.1/analyzers/javascript_analyzer.py +272 -0
  13. reveal/_archive_old_v0.1/analyzers/json_analyzer.py +143 -0
  14. reveal/_archive_old_v0.1/analyzers/markdown_analyzer.py +116 -0
  15. reveal/_archive_old_v0.1/analyzers/php_analyzer.py +166 -0
  16. reveal/_archive_old_v0.1/analyzers/python_analyzer.py +118 -0
  17. reveal/_archive_old_v0.1/analyzers/rust_analyzer.py +202 -0
  18. reveal/_archive_old_v0.1/analyzers/sql_analyzer.py +534 -0
  19. reveal/_archive_old_v0.1/analyzers/text_analyzer.py +35 -0
  20. reveal/_archive_old_v0.1/analyzers/toml_analyzer.py +137 -0
  21. reveal/_archive_old_v0.1/analyzers/treesitter_base.py +276 -0
  22. reveal/_archive_old_v0.1/analyzers/yaml_analyzer.py +127 -0
  23. reveal/_archive_old_v0.1/breadcrumbs.py +147 -0
  24. reveal/_archive_old_v0.1/cli.py +445 -0
  25. reveal/_archive_old_v0.1/core.py +136 -0
  26. reveal/_archive_old_v0.1/detectors.py +29 -0
  27. reveal/_archive_old_v0.1/formatters.py +272 -0
  28. reveal/_archive_old_v0.1/grep_filter.py +85 -0
  29. reveal/_archive_old_v0.1/plugin_loader.py +178 -0
  30. reveal/_archive_old_v0.1/registry.py +286 -0
  31. reveal/analyzers/__init__.py +23 -0
  32. reveal/analyzers/go.py +13 -0
  33. reveal/analyzers/jupyter_analyzer.py +226 -0
  34. reveal/analyzers/markdown.py +79 -0
  35. reveal/analyzers/python.py +15 -0
  36. reveal/analyzers/rust.py +13 -0
  37. reveal/analyzers/yaml_json.py +110 -0
  38. reveal/base.py +193 -0
  39. reveal/main.py +216 -0
  40. reveal/tests/__init__.py +1 -0
  41. reveal/tests/test_json_yaml_line_numbers.py +238 -0
  42. reveal/tests/test_line_numbers.py +151 -0
  43. reveal/tests/test_toml_analyzer.py +220 -0
  44. reveal/tree_view.py +105 -0
  45. reveal/treesitter.py +282 -0
  46. reveal_cli-0.2.0.dist-info/METADATA +319 -0
  47. reveal_cli-0.2.0.dist-info/RECORD +51 -0
  48. reveal_cli-0.2.0.dist-info/WHEEL +5 -0
  49. reveal_cli-0.2.0.dist-info/entry_points.txt +2 -0
  50. reveal_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
  51. 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