tree-sitter-analyzer 1.6.1__py3-none-any.whl → 1.7.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.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +1 -1
- tree_sitter_analyzer/formatters/formatter_factory.py +3 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +113 -13
- tree_sitter_analyzer/formatters/python_formatter.py +57 -15
- tree_sitter_analyzer/formatters/typescript_formatter.py +432 -0
- tree_sitter_analyzer/language_detector.py +1 -1
- tree_sitter_analyzer/languages/java_plugin.py +68 -7
- tree_sitter_analyzer/languages/javascript_plugin.py +43 -1
- tree_sitter_analyzer/languages/python_plugin.py +157 -49
- tree_sitter_analyzer/languages/typescript_plugin.py +1729 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +10 -0
- tree_sitter_analyzer/mcp/server.py +73 -18
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +21 -1
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +36 -17
- tree_sitter_analyzer/project_detector.py +6 -8
- tree_sitter_analyzer/queries/javascript.py +1 -1
- tree_sitter_analyzer/queries/typescript.py +630 -10
- tree_sitter_analyzer/utils.py +26 -5
- {tree_sitter_analyzer-1.6.1.dist-info → tree_sitter_analyzer-1.7.0.dist-info}/METADATA +76 -55
- {tree_sitter_analyzer-1.6.1.dist-info → tree_sitter_analyzer-1.7.0.dist-info}/RECORD +22 -20
- {tree_sitter_analyzer-1.6.1.dist-info → tree_sitter_analyzer-1.7.0.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-1.6.1.dist-info → tree_sitter_analyzer-1.7.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TypeScript-specific table formatter.
|
|
4
|
+
|
|
5
|
+
Provides specialized formatting for TypeScript code analysis results,
|
|
6
|
+
handling TypeScript-specific features like interfaces, type aliases, enums,
|
|
7
|
+
generics, decorators, and modern JavaScript features with type annotations.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from .base_formatter import BaseTableFormatter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TypeScriptTableFormatter(BaseTableFormatter):
|
|
16
|
+
"""Table formatter specialized for TypeScript"""
|
|
17
|
+
|
|
18
|
+
def format(self, data: dict[str, Any]) -> str:
|
|
19
|
+
"""Format data using the configured format type"""
|
|
20
|
+
return self.format_structure(data)
|
|
21
|
+
|
|
22
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
23
|
+
"""Full table format for TypeScript"""
|
|
24
|
+
lines = []
|
|
25
|
+
|
|
26
|
+
# Header - TypeScript (module/file based)
|
|
27
|
+
file_path = data.get("file_path", "Unknown")
|
|
28
|
+
file_name = file_path.split("/")[-1].split("\\")[-1]
|
|
29
|
+
module_name = (
|
|
30
|
+
file_name.replace(".ts", "").replace(".tsx", "").replace(".d.ts", "")
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Check if this is a module (has exports, classes, interfaces, or functions)
|
|
34
|
+
exports = data.get("exports", [])
|
|
35
|
+
classes = data.get("classes", [])
|
|
36
|
+
interfaces = data.get("interfaces", [])
|
|
37
|
+
functions = data.get("functions", [])
|
|
38
|
+
is_module = len(exports) > 0 or len(classes) > 0 or len(interfaces) > 0 or len(functions) > 0
|
|
39
|
+
is_declaration_file = file_name.endswith(".d.ts")
|
|
40
|
+
is_tsx = file_name.endswith(".tsx")
|
|
41
|
+
|
|
42
|
+
if is_declaration_file:
|
|
43
|
+
lines.append(f"# Declaration File: {module_name}")
|
|
44
|
+
elif is_tsx:
|
|
45
|
+
lines.append(f"# TSX Module: {module_name}")
|
|
46
|
+
elif is_module:
|
|
47
|
+
lines.append(f"# TypeScript Module: {module_name}")
|
|
48
|
+
else:
|
|
49
|
+
lines.append(f"# TypeScript Script: {module_name}")
|
|
50
|
+
lines.append("")
|
|
51
|
+
|
|
52
|
+
# Imports
|
|
53
|
+
imports = data.get("imports", [])
|
|
54
|
+
if imports:
|
|
55
|
+
lines.append("## Imports")
|
|
56
|
+
lines.append("```typescript")
|
|
57
|
+
for imp in imports:
|
|
58
|
+
import_statement = imp.get("statement", "")
|
|
59
|
+
if not import_statement:
|
|
60
|
+
# Construct import statement from parts
|
|
61
|
+
source = imp.get("source", "")
|
|
62
|
+
name = imp.get("name", "")
|
|
63
|
+
is_type_import = imp.get("is_type_import", False)
|
|
64
|
+
if name and source:
|
|
65
|
+
type_prefix = "type " if is_type_import else ""
|
|
66
|
+
import_statement = f"import {type_prefix}{name} from {source};"
|
|
67
|
+
lines.append(import_statement)
|
|
68
|
+
lines.append("```")
|
|
69
|
+
lines.append("")
|
|
70
|
+
|
|
71
|
+
# Module Info
|
|
72
|
+
stats = data.get("statistics", {})
|
|
73
|
+
classes = data.get("classes", [])
|
|
74
|
+
interfaces = [c for c in classes if c.get("class_type") == "interface"]
|
|
75
|
+
type_aliases = [c for c in classes if c.get("class_type") == "type"]
|
|
76
|
+
enums = [c for c in classes if c.get("class_type") == "enum"]
|
|
77
|
+
actual_classes = [c for c in classes if c.get("class_type") in ["class", "abstract_class"]]
|
|
78
|
+
|
|
79
|
+
lines.append("## Module Info")
|
|
80
|
+
lines.append("| Property | Value |")
|
|
81
|
+
lines.append("|----------|-------|")
|
|
82
|
+
lines.append(f"| File | {file_name} |")
|
|
83
|
+
lines.append(f"| Type | {'Declaration File' if is_declaration_file else 'TSX Module' if is_tsx else 'TypeScript Module' if is_module else 'TypeScript Script'} |")
|
|
84
|
+
lines.append(f"| Functions | {stats.get('function_count', 0)} |")
|
|
85
|
+
lines.append(f"| Classes | {len(actual_classes)} |")
|
|
86
|
+
lines.append(f"| Interfaces | {len(interfaces)} |")
|
|
87
|
+
lines.append(f"| Type Aliases | {len(type_aliases)} |")
|
|
88
|
+
lines.append(f"| Enums | {len(enums)} |")
|
|
89
|
+
lines.append(f"| Variables | {stats.get('variable_count', 0)} |")
|
|
90
|
+
lines.append(f"| Exports | {len(exports)} |")
|
|
91
|
+
lines.append("")
|
|
92
|
+
|
|
93
|
+
# Interfaces (TypeScript specific)
|
|
94
|
+
if interfaces:
|
|
95
|
+
lines.append("## Interfaces")
|
|
96
|
+
lines.append("| Interface | Extends | Lines | Properties | Methods | Generics |")
|
|
97
|
+
lines.append("|-----------|---------|-------|------------|---------|----------|")
|
|
98
|
+
|
|
99
|
+
for interface in interfaces:
|
|
100
|
+
name = str(interface.get("name", "Unknown"))
|
|
101
|
+
extends = ", ".join(interface.get("interfaces", [])) or "-"
|
|
102
|
+
line_range = interface.get("line_range", {})
|
|
103
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
104
|
+
|
|
105
|
+
# Count properties and methods within the interface
|
|
106
|
+
interface_properties = [
|
|
107
|
+
v for v in data.get("variables", [])
|
|
108
|
+
if line_range.get("start", 0) <= v.get("line_range", {}).get("start", 0) <= line_range.get("end", 0)
|
|
109
|
+
and v.get("declaration_kind") == "property_signature"
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
interface_methods = [
|
|
113
|
+
m for m in data.get("methods", [])
|
|
114
|
+
if line_range.get("start", 0) <= m.get("line_range", {}).get("start", 0) <= line_range.get("end", 0)
|
|
115
|
+
and m.get("is_signature", False)
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
generics = ", ".join(interface.get("generics", [])) or "-"
|
|
119
|
+
|
|
120
|
+
lines.append(f"| {name} | {extends} | {lines_str} | {len(interface_properties)} | {len(interface_methods)} | {generics} |")
|
|
121
|
+
lines.append("")
|
|
122
|
+
|
|
123
|
+
# Type Aliases (TypeScript specific)
|
|
124
|
+
if type_aliases:
|
|
125
|
+
lines.append("## Type Aliases")
|
|
126
|
+
lines.append("| Type | Lines | Generics | Definition |")
|
|
127
|
+
lines.append("|------|-------|----------|------------|")
|
|
128
|
+
|
|
129
|
+
for type_alias in type_aliases:
|
|
130
|
+
name = str(type_alias.get("name", "Unknown"))
|
|
131
|
+
line_range = type_alias.get("line_range", {})
|
|
132
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
133
|
+
generics = ", ".join(type_alias.get("generics", [])) or "-"
|
|
134
|
+
|
|
135
|
+
# Extract type definition from raw text
|
|
136
|
+
raw_text = type_alias.get("raw_text", "")
|
|
137
|
+
if "=" in raw_text:
|
|
138
|
+
definition = raw_text.split("=", 1)[1].strip().rstrip(";")[:50]
|
|
139
|
+
if len(definition) > 47:
|
|
140
|
+
definition = definition[:47] + "..."
|
|
141
|
+
else:
|
|
142
|
+
definition = "-"
|
|
143
|
+
|
|
144
|
+
lines.append(f"| {name} | {lines_str} | {generics} | {definition} |")
|
|
145
|
+
lines.append("")
|
|
146
|
+
|
|
147
|
+
# Enums (TypeScript specific)
|
|
148
|
+
if enums:
|
|
149
|
+
lines.append("## Enums")
|
|
150
|
+
lines.append("| Enum | Lines | Values |")
|
|
151
|
+
lines.append("|------|-------|--------|")
|
|
152
|
+
|
|
153
|
+
for enum in enums:
|
|
154
|
+
name = str(enum.get("name", "Unknown"))
|
|
155
|
+
line_range = enum.get("line_range", {})
|
|
156
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
157
|
+
|
|
158
|
+
# Count enum values (simplified)
|
|
159
|
+
raw_text = enum.get("raw_text", "")
|
|
160
|
+
value_count = raw_text.count(",") + 1 if raw_text.count("{") > 0 else 0
|
|
161
|
+
|
|
162
|
+
lines.append(f"| {name} | {lines_str} | {value_count} |")
|
|
163
|
+
lines.append("")
|
|
164
|
+
|
|
165
|
+
# Classes
|
|
166
|
+
if actual_classes:
|
|
167
|
+
lines.append("## Classes")
|
|
168
|
+
lines.append("| Class | Type | Extends | Implements | Lines | Methods | Properties | Generics |")
|
|
169
|
+
lines.append("|-------|------|---------|------------|-------|---------|------------|----------|")
|
|
170
|
+
|
|
171
|
+
for class_info in actual_classes:
|
|
172
|
+
name = str(class_info.get("name", "Unknown"))
|
|
173
|
+
class_type = class_info.get("class_type", "class")
|
|
174
|
+
extends = str(class_info.get("superclass", "")) or "-"
|
|
175
|
+
implements = ", ".join(class_info.get("interfaces", [])) or "-"
|
|
176
|
+
line_range = class_info.get("line_range", {})
|
|
177
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
178
|
+
generics = ", ".join(class_info.get("generics", [])) or "-"
|
|
179
|
+
|
|
180
|
+
# Count methods within the class
|
|
181
|
+
class_methods = [
|
|
182
|
+
m for m in data.get("functions", [])
|
|
183
|
+
if (line_range.get("start", 0) <= m.get("line_range", {}).get("start", 0) <= line_range.get("end", 0)
|
|
184
|
+
and m.get("is_method", False)
|
|
185
|
+
and not m.get("is_signature", False))
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
# Count properties (class fields)
|
|
189
|
+
class_properties = [
|
|
190
|
+
v for v in data.get("variables", [])
|
|
191
|
+
if line_range.get("start", 0) <= v.get("line_range", {}).get("start", 0) <= line_range.get("end", 0)
|
|
192
|
+
and v.get("declaration_kind") == "property"
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
lines.append(f"| {name} | {class_type} | {extends} | {implements} | {lines_str} | {len(class_methods)} | {len(class_properties)} | {generics} |")
|
|
196
|
+
lines.append("")
|
|
197
|
+
|
|
198
|
+
# Functions
|
|
199
|
+
functions = data.get("functions", [])
|
|
200
|
+
if functions:
|
|
201
|
+
lines.append("## Functions")
|
|
202
|
+
lines.append("| Function | Type | Return Type | Parameters | Async | Generic | Lines | Complexity |")
|
|
203
|
+
lines.append("|----------|------|-------------|------------|-------|---------|-------|------------|")
|
|
204
|
+
|
|
205
|
+
for func in functions:
|
|
206
|
+
name = str(func.get("name", "Unknown"))
|
|
207
|
+
func_type = "arrow" if func.get("is_arrow") else "method" if func.get("is_method") else "function"
|
|
208
|
+
return_type = str(func.get("return_type", "any"))
|
|
209
|
+
params = func.get("parameters", [])
|
|
210
|
+
param_count = len(params)
|
|
211
|
+
is_async = "✓" if func.get("is_async") else ""
|
|
212
|
+
has_generics = "✓" if func.get("generics") else ""
|
|
213
|
+
line_range = func.get("line_range", {})
|
|
214
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
215
|
+
complexity = func.get("complexity_score", 1)
|
|
216
|
+
|
|
217
|
+
lines.append(f"| {name} | {func_type} | {return_type} | {param_count} | {is_async} | {has_generics} | {lines_str} | {complexity} |")
|
|
218
|
+
lines.append("")
|
|
219
|
+
|
|
220
|
+
# Variables/Properties
|
|
221
|
+
variables = data.get("variables", [])
|
|
222
|
+
if variables:
|
|
223
|
+
lines.append("## Variables & Properties")
|
|
224
|
+
lines.append("| Name | Type | Kind | Visibility | Static | Optional | Lines |")
|
|
225
|
+
lines.append("|------|------|------|------------|--------|----------|-------|")
|
|
226
|
+
|
|
227
|
+
for var in variables:
|
|
228
|
+
name = str(var.get("name", "Unknown"))
|
|
229
|
+
var_type = str(var.get("variable_type", "any"))
|
|
230
|
+
kind = var.get("declaration_kind", "variable")
|
|
231
|
+
visibility = var.get("visibility", "public")
|
|
232
|
+
is_static = "✓" if var.get("is_static") else ""
|
|
233
|
+
is_optional = "✓" if var.get("is_optional") else ""
|
|
234
|
+
line_range = var.get("line_range", {})
|
|
235
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
236
|
+
|
|
237
|
+
lines.append(f"| {name} | {var_type} | {kind} | {visibility} | {is_static} | {is_optional} | {lines_str} |")
|
|
238
|
+
lines.append("")
|
|
239
|
+
|
|
240
|
+
# Exports
|
|
241
|
+
if exports:
|
|
242
|
+
lines.append("## Exports")
|
|
243
|
+
lines.append("| Export | Type | Default |")
|
|
244
|
+
lines.append("|--------|------|---------|")
|
|
245
|
+
|
|
246
|
+
for export in exports:
|
|
247
|
+
names = export.get("names", [])
|
|
248
|
+
export_type = export.get("type", "unknown")
|
|
249
|
+
is_default = "✓" if export.get("is_default") else ""
|
|
250
|
+
|
|
251
|
+
for name in names:
|
|
252
|
+
lines.append(f"| {name} | {export_type} | {is_default} |")
|
|
253
|
+
lines.append("")
|
|
254
|
+
|
|
255
|
+
return "\n".join(lines)
|
|
256
|
+
|
|
257
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
258
|
+
"""Compact table format for TypeScript"""
|
|
259
|
+
lines = []
|
|
260
|
+
|
|
261
|
+
# Header
|
|
262
|
+
file_path = data.get("file_path", "Unknown")
|
|
263
|
+
file_name = file_path.split("/")[-1].split("\\")[-1]
|
|
264
|
+
lines.append(f"# {file_name}")
|
|
265
|
+
lines.append("")
|
|
266
|
+
|
|
267
|
+
# Summary
|
|
268
|
+
stats = data.get("statistics", {})
|
|
269
|
+
classes = data.get("classes", [])
|
|
270
|
+
functions = data.get("functions", [])
|
|
271
|
+
variables = data.get("variables", [])
|
|
272
|
+
|
|
273
|
+
interfaces = len([c for c in classes if c.get("class_type") == "interface"])
|
|
274
|
+
type_aliases = len([c for c in classes if c.get("class_type") == "type"])
|
|
275
|
+
enums = len([c for c in classes if c.get("class_type") == "enum"])
|
|
276
|
+
actual_classes = len([c for c in classes if c.get("class_type") in ["class", "abstract_class"]])
|
|
277
|
+
|
|
278
|
+
lines.append("## Summary")
|
|
279
|
+
lines.append(f"- **Classes**: {actual_classes}")
|
|
280
|
+
lines.append(f"- **Interfaces**: {interfaces}")
|
|
281
|
+
lines.append(f"- **Type Aliases**: {type_aliases}")
|
|
282
|
+
lines.append(f"- **Enums**: {enums}")
|
|
283
|
+
lines.append(f"- **Functions**: {len(functions)}")
|
|
284
|
+
lines.append(f"- **Variables**: {len(variables)}")
|
|
285
|
+
lines.append("")
|
|
286
|
+
|
|
287
|
+
# Main elements
|
|
288
|
+
if classes:
|
|
289
|
+
lines.append("## Types")
|
|
290
|
+
for class_info in classes:
|
|
291
|
+
name = class_info.get("name", "Unknown")
|
|
292
|
+
class_type = class_info.get("class_type", "class")
|
|
293
|
+
line_range = class_info.get("line_range", {})
|
|
294
|
+
lines_str = f"L{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
295
|
+
lines.append(f"- **{name}** ({class_type}) - {lines_str}")
|
|
296
|
+
lines.append("")
|
|
297
|
+
|
|
298
|
+
if functions:
|
|
299
|
+
lines.append("## Functions")
|
|
300
|
+
for func in functions:
|
|
301
|
+
name = func.get("name", "Unknown")
|
|
302
|
+
return_type = func.get("return_type", "any")
|
|
303
|
+
line_range = func.get("line_range", {})
|
|
304
|
+
lines_str = f"L{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
305
|
+
async_marker = " (async)" if func.get("is_async") else ""
|
|
306
|
+
lines.append(f"- **{name}**(): {return_type}{async_marker} - {lines_str}")
|
|
307
|
+
lines.append("")
|
|
308
|
+
|
|
309
|
+
return "\n".join(lines)
|
|
310
|
+
|
|
311
|
+
def _format_csv(self, data: dict[str, Any]) -> str:
|
|
312
|
+
"""CSV format for TypeScript"""
|
|
313
|
+
lines = []
|
|
314
|
+
|
|
315
|
+
# Header
|
|
316
|
+
lines.append("Type,Name,Kind,Return/Type,Lines,Visibility,Static,Async,Generic")
|
|
317
|
+
|
|
318
|
+
# Classes, interfaces, types, enums
|
|
319
|
+
classes = data.get("classes", [])
|
|
320
|
+
for class_info in classes:
|
|
321
|
+
name = class_info.get("name", "")
|
|
322
|
+
class_type = class_info.get("class_type", "class")
|
|
323
|
+
line_range = class_info.get("line_range", {})
|
|
324
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
325
|
+
visibility = class_info.get("visibility", "public")
|
|
326
|
+
is_static = "true" if class_info.get("is_static") else "false"
|
|
327
|
+
has_generics = "true" if class_info.get("generics") else "false"
|
|
328
|
+
|
|
329
|
+
lines.append(f"Class,{name},{class_type},,{lines_str},{visibility},{is_static},,{has_generics}")
|
|
330
|
+
|
|
331
|
+
# Functions
|
|
332
|
+
functions = data.get("functions", [])
|
|
333
|
+
for func in functions:
|
|
334
|
+
name = func.get("name", "")
|
|
335
|
+
func_type = "arrow" if func.get("is_arrow") else "method" if func.get("is_method") else "function"
|
|
336
|
+
return_type = func.get("return_type", "any")
|
|
337
|
+
line_range = func.get("line_range", {})
|
|
338
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
339
|
+
visibility = func.get("visibility", "public")
|
|
340
|
+
is_static = "true" if func.get("is_static") else "false"
|
|
341
|
+
is_async = "true" if func.get("is_async") else "false"
|
|
342
|
+
has_generics = "true" if func.get("generics") else "false"
|
|
343
|
+
|
|
344
|
+
lines.append(f"Function,{name},{func_type},{return_type},{lines_str},{visibility},{is_static},{is_async},{has_generics}")
|
|
345
|
+
|
|
346
|
+
# Variables
|
|
347
|
+
variables = data.get("variables", [])
|
|
348
|
+
for var in variables:
|
|
349
|
+
name = var.get("name", "")
|
|
350
|
+
kind = var.get("declaration_kind", "variable")
|
|
351
|
+
var_type = var.get("variable_type", "any")
|
|
352
|
+
line_range = var.get("line_range", {})
|
|
353
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
354
|
+
visibility = var.get("visibility", "public")
|
|
355
|
+
is_static = "true" if var.get("is_static") else "false"
|
|
356
|
+
|
|
357
|
+
lines.append(f"Variable,{name},{kind},{var_type},{lines_str},{visibility},{is_static},,")
|
|
358
|
+
|
|
359
|
+
return "\n".join(lines)
|
|
360
|
+
|
|
361
|
+
def _get_element_type_name(self, element: dict[str, Any]) -> str:
|
|
362
|
+
"""Get human-readable type name for TypeScript elements"""
|
|
363
|
+
element_type = element.get("element_type", "unknown")
|
|
364
|
+
|
|
365
|
+
if element_type == "class":
|
|
366
|
+
class_type = element.get("class_type", "class")
|
|
367
|
+
if class_type == "interface":
|
|
368
|
+
return "Interface"
|
|
369
|
+
elif class_type == "type":
|
|
370
|
+
return "Type Alias"
|
|
371
|
+
elif class_type == "enum":
|
|
372
|
+
return "Enum"
|
|
373
|
+
elif class_type == "abstract_class":
|
|
374
|
+
return "Abstract Class"
|
|
375
|
+
else:
|
|
376
|
+
return "Class"
|
|
377
|
+
elif element_type == "function":
|
|
378
|
+
if element.get("is_arrow"):
|
|
379
|
+
return "Arrow Function"
|
|
380
|
+
elif element.get("is_method"):
|
|
381
|
+
return "Method"
|
|
382
|
+
elif element.get("is_constructor"):
|
|
383
|
+
return "Constructor"
|
|
384
|
+
else:
|
|
385
|
+
return "Function"
|
|
386
|
+
elif element_type == "variable":
|
|
387
|
+
kind = element.get("declaration_kind", "variable")
|
|
388
|
+
if kind == "property":
|
|
389
|
+
return "Property"
|
|
390
|
+
elif kind == "property_signature":
|
|
391
|
+
return "Property Signature"
|
|
392
|
+
else:
|
|
393
|
+
return "Variable"
|
|
394
|
+
elif element_type == "import":
|
|
395
|
+
return "Import"
|
|
396
|
+
else:
|
|
397
|
+
return element_type.title()
|
|
398
|
+
|
|
399
|
+
def _format_element_details(self, element: dict[str, Any]) -> str:
|
|
400
|
+
"""Format TypeScript-specific element details"""
|
|
401
|
+
details = []
|
|
402
|
+
|
|
403
|
+
# Type annotations
|
|
404
|
+
if element.get("has_type_annotations"):
|
|
405
|
+
details.append("typed")
|
|
406
|
+
|
|
407
|
+
# Generics
|
|
408
|
+
if element.get("generics"):
|
|
409
|
+
generics = ", ".join(element.get("generics", []))
|
|
410
|
+
details.append(f"<{generics}>")
|
|
411
|
+
|
|
412
|
+
# Visibility
|
|
413
|
+
visibility = element.get("visibility")
|
|
414
|
+
if visibility and visibility != "public":
|
|
415
|
+
details.append(visibility)
|
|
416
|
+
|
|
417
|
+
# Modifiers
|
|
418
|
+
if element.get("is_static"):
|
|
419
|
+
details.append("static")
|
|
420
|
+
if element.get("is_async"):
|
|
421
|
+
details.append("async")
|
|
422
|
+
if element.get("is_abstract"):
|
|
423
|
+
details.append("abstract")
|
|
424
|
+
if element.get("is_optional"):
|
|
425
|
+
details.append("optional")
|
|
426
|
+
|
|
427
|
+
# Framework specific
|
|
428
|
+
framework = element.get("framework_type")
|
|
429
|
+
if framework:
|
|
430
|
+
details.append(f"{framework}")
|
|
431
|
+
|
|
432
|
+
return " ".join(details) if details else ""
|
|
@@ -201,9 +201,12 @@ class JavaElementExtractor(ElementExtractor):
|
|
|
201
201
|
import_name = static_match.group(1)
|
|
202
202
|
if import_content.endswith(".*"):
|
|
203
203
|
import_name = import_name.replace(".*", "")
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
|
|
205
|
+
# For static imports, extract the class name (remove method/field name)
|
|
206
|
+
parts = import_name.split(".")
|
|
207
|
+
if len(parts) > 1:
|
|
208
|
+
# Remove the last part (method/field name) to get class name
|
|
209
|
+
import_name = ".".join(parts[:-1])
|
|
207
210
|
|
|
208
211
|
imports.append(
|
|
209
212
|
Import(
|
|
@@ -255,6 +258,10 @@ class JavaElementExtractor(ElementExtractor):
|
|
|
255
258
|
packages: list[Package] = []
|
|
256
259
|
|
|
257
260
|
# Extract package declaration
|
|
261
|
+
if tree is None or tree.root_node is None:
|
|
262
|
+
log_debug("Tree or root_node is None, returning empty packages list")
|
|
263
|
+
return packages
|
|
264
|
+
|
|
258
265
|
for child in tree.root_node.children:
|
|
259
266
|
if child.type == "package_declaration":
|
|
260
267
|
package_info = self._extract_package_element(child)
|
|
@@ -1054,10 +1061,12 @@ class JavaElementExtractor(ElementExtractor):
|
|
|
1054
1061
|
# Handle wildcard case
|
|
1055
1062
|
if import_content.endswith(".*"):
|
|
1056
1063
|
import_name = import_name.replace(".*", "")
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1064
|
+
|
|
1065
|
+
# For static imports, extract the class name (remove method/field name)
|
|
1066
|
+
parts = import_name.split(".")
|
|
1067
|
+
if len(parts) > 1:
|
|
1068
|
+
# Remove the last part (method/field name) to get class name
|
|
1069
|
+
import_name = ".".join(parts[:-1])
|
|
1061
1070
|
|
|
1062
1071
|
return Import(
|
|
1063
1072
|
name=import_name,
|
|
@@ -1099,6 +1108,20 @@ class JavaElementExtractor(ElementExtractor):
|
|
|
1099
1108
|
log_error(f"Unexpected error in import extraction: {e}")
|
|
1100
1109
|
return None
|
|
1101
1110
|
|
|
1111
|
+
def extract_elements(self, tree: "tree_sitter.Tree", source_code: str) -> list:
|
|
1112
|
+
"""Extract elements from source code using tree-sitter AST"""
|
|
1113
|
+
elements = []
|
|
1114
|
+
|
|
1115
|
+
try:
|
|
1116
|
+
elements.extend(self.extract_functions(tree, source_code))
|
|
1117
|
+
elements.extend(self.extract_classes(tree, source_code))
|
|
1118
|
+
elements.extend(self.extract_variables(tree, source_code))
|
|
1119
|
+
elements.extend(self.extract_imports(tree, source_code))
|
|
1120
|
+
except Exception as e:
|
|
1121
|
+
log_error(f"Failed to extract elements: {e}")
|
|
1122
|
+
|
|
1123
|
+
return elements
|
|
1124
|
+
|
|
1102
1125
|
|
|
1103
1126
|
class JavaPlugin(LanguagePlugin):
|
|
1104
1127
|
"""Java language plugin for the new architecture"""
|
|
@@ -1107,6 +1130,12 @@ class JavaPlugin(LanguagePlugin):
|
|
|
1107
1130
|
"""Initialize the Java plugin"""
|
|
1108
1131
|
super().__init__()
|
|
1109
1132
|
self._language_cache: tree_sitter.Language | None = None
|
|
1133
|
+
self._extractor: Optional[JavaElementExtractor] = None
|
|
1134
|
+
|
|
1135
|
+
# Legacy attributes for backward compatibility with tests
|
|
1136
|
+
self.language = "java"
|
|
1137
|
+
self.extractor = self.create_extractor()
|
|
1138
|
+
self.supported_extensions = self.get_file_extensions()
|
|
1110
1139
|
|
|
1111
1140
|
def get_language_name(self) -> str:
|
|
1112
1141
|
"""Return the name of the programming language this plugin supports"""
|
|
@@ -1120,6 +1149,12 @@ class JavaPlugin(LanguagePlugin):
|
|
|
1120
1149
|
"""Create and return an element extractor for this language"""
|
|
1121
1150
|
return JavaElementExtractor()
|
|
1122
1151
|
|
|
1152
|
+
def get_extractor(self) -> ElementExtractor:
|
|
1153
|
+
"""Get the cached extractor instance, creating it if necessary"""
|
|
1154
|
+
if self._extractor is None:
|
|
1155
|
+
self._extractor = JavaElementExtractor()
|
|
1156
|
+
return self._extractor
|
|
1157
|
+
|
|
1123
1158
|
def get_tree_sitter_language(self) -> Optional["tree_sitter.Language"]:
|
|
1124
1159
|
"""Get the Tree-sitter language object for Java"""
|
|
1125
1160
|
if self._language_cache is None:
|
|
@@ -1270,3 +1305,29 @@ class JavaPlugin(LanguagePlugin):
|
|
|
1270
1305
|
success=False,
|
|
1271
1306
|
error_message=str(e),
|
|
1272
1307
|
)
|
|
1308
|
+
|
|
1309
|
+
def extract_elements(self, tree: "tree_sitter.Tree", source_code: str) -> dict[str, list[CodeElement]]:
|
|
1310
|
+
"""Legacy method for backward compatibility with tests"""
|
|
1311
|
+
if not tree or not tree.root_node:
|
|
1312
|
+
return {
|
|
1313
|
+
"packages": [],
|
|
1314
|
+
"functions": [],
|
|
1315
|
+
"classes": [],
|
|
1316
|
+
"variables": [],
|
|
1317
|
+
"imports": [],
|
|
1318
|
+
"annotations": []
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
extractor = self.create_extractor()
|
|
1322
|
+
|
|
1323
|
+
# Extract all types of elements and return as dictionary
|
|
1324
|
+
result = {
|
|
1325
|
+
"packages": extractor.extract_packages(tree, source_code),
|
|
1326
|
+
"functions": extractor.extract_functions(tree, source_code),
|
|
1327
|
+
"classes": extractor.extract_classes(tree, source_code),
|
|
1328
|
+
"variables": extractor.extract_variables(tree, source_code),
|
|
1329
|
+
"imports": extractor.extract_imports(tree, source_code),
|
|
1330
|
+
"annotations": extractor.extract_annotations(tree, source_code)
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
return result
|
|
@@ -558,7 +558,8 @@ class JavaScriptElementExtractor(ElementExtractor):
|
|
|
558
558
|
elif child.type == "class_heritage":
|
|
559
559
|
# Extract extends clause
|
|
560
560
|
heritage_text = self._get_node_text_optimized(child)
|
|
561
|
-
|
|
561
|
+
# Support both simple names (Component) and dotted names (React.Component)
|
|
562
|
+
match = re.search(r"extends\s+([\w.]+)", heritage_text)
|
|
562
563
|
if match:
|
|
563
564
|
superclass = match.group(1)
|
|
564
565
|
|
|
@@ -1217,6 +1218,20 @@ class JavaScriptElementExtractor(ElementExtractor):
|
|
|
1217
1218
|
else:
|
|
1218
1219
|
return "unknown"
|
|
1219
1220
|
|
|
1221
|
+
def extract_elements(self, tree: "tree_sitter.Tree", source_code: str) -> list:
|
|
1222
|
+
"""Extract elements from source code using tree-sitter AST"""
|
|
1223
|
+
elements = []
|
|
1224
|
+
|
|
1225
|
+
try:
|
|
1226
|
+
elements.extend(self.extract_functions(tree, source_code))
|
|
1227
|
+
elements.extend(self.extract_classes(tree, source_code))
|
|
1228
|
+
elements.extend(self.extract_variables(tree, source_code))
|
|
1229
|
+
elements.extend(self.extract_imports(tree, source_code))
|
|
1230
|
+
except Exception as e:
|
|
1231
|
+
log_error(f"Failed to extract elements: {e}")
|
|
1232
|
+
|
|
1233
|
+
return elements
|
|
1234
|
+
|
|
1220
1235
|
def _get_variable_kind(self, var_data: dict | str) -> str:
|
|
1221
1236
|
"""Get variable declaration kind from variable data or raw text"""
|
|
1222
1237
|
if isinstance(var_data, dict):
|
|
@@ -1344,6 +1359,11 @@ class JavaScriptPlugin(LanguagePlugin):
|
|
|
1344
1359
|
def __init__(self) -> None:
|
|
1345
1360
|
self._extractor = JavaScriptElementExtractor()
|
|
1346
1361
|
self._language: tree_sitter.Language | None = None
|
|
1362
|
+
|
|
1363
|
+
# Legacy compatibility attributes for tests
|
|
1364
|
+
self.language = "javascript"
|
|
1365
|
+
self.extractor = self._extractor
|
|
1366
|
+
self.supported_extensions = [".js", ".mjs", ".jsx", ".es6", ".es", ".cjs"]
|
|
1347
1367
|
|
|
1348
1368
|
@property
|
|
1349
1369
|
def language_name(self) -> str:
|
|
@@ -1495,3 +1515,25 @@ class JavaScriptPlugin(LanguagePlugin):
|
|
|
1495
1515
|
success=False,
|
|
1496
1516
|
error_message=str(e),
|
|
1497
1517
|
)
|
|
1518
|
+
|
|
1519
|
+
def extract_elements(self, tree: "tree_sitter.Tree", source_code: str) -> dict:
|
|
1520
|
+
"""Extract elements from source code using tree-sitter AST"""
|
|
1521
|
+
try:
|
|
1522
|
+
if tree is None or not hasattr(tree, 'root_node') or tree.root_node is None:
|
|
1523
|
+
return {"functions": [], "classes": [], "variables": [], "imports": [], "exports": []}
|
|
1524
|
+
|
|
1525
|
+
functions = self._extractor.extract_functions(tree, source_code)
|
|
1526
|
+
classes = self._extractor.extract_classes(tree, source_code)
|
|
1527
|
+
variables = self._extractor.extract_variables(tree, source_code)
|
|
1528
|
+
imports = self._extractor.extract_imports(tree, source_code)
|
|
1529
|
+
|
|
1530
|
+
return {
|
|
1531
|
+
"functions": functions,
|
|
1532
|
+
"classes": classes,
|
|
1533
|
+
"variables": variables,
|
|
1534
|
+
"imports": imports,
|
|
1535
|
+
"exports": [] # TODO: Implement exports extraction
|
|
1536
|
+
}
|
|
1537
|
+
except Exception as e:
|
|
1538
|
+
log_error(f"Failed to extract elements: {e}")
|
|
1539
|
+
return {"functions": [], "classes": [], "variables": [], "imports": [], "exports": []}
|