tree-sitter-analyzer 0.2.0__py3-none-any.whl → 0.3.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 +133 -121
- tree_sitter_analyzer/__main__.py +11 -12
- tree_sitter_analyzer/api.py +531 -539
- tree_sitter_analyzer/cli/__init__.py +39 -39
- tree_sitter_analyzer/cli/__main__.py +12 -13
- tree_sitter_analyzer/cli/commands/__init__.py +26 -27
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
- tree_sitter_analyzer/cli/commands/base_command.py +160 -155
- tree_sitter_analyzer/cli/commands/default_command.py +18 -19
- tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
- tree_sitter_analyzer/cli/commands/query_command.py +81 -82
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
- tree_sitter_analyzer/cli/commands/table_command.py +232 -233
- tree_sitter_analyzer/cli/info_commands.py +120 -121
- tree_sitter_analyzer/cli_main.py +277 -276
- tree_sitter_analyzer/core/__init__.py +15 -20
- tree_sitter_analyzer/core/analysis_engine.py +591 -574
- tree_sitter_analyzer/core/cache_service.py +320 -330
- tree_sitter_analyzer/core/engine.py +557 -560
- tree_sitter_analyzer/core/parser.py +293 -288
- tree_sitter_analyzer/core/query.py +494 -502
- tree_sitter_analyzer/encoding_utils.py +458 -460
- tree_sitter_analyzer/exceptions.py +337 -340
- tree_sitter_analyzer/file_handler.py +217 -222
- tree_sitter_analyzer/formatters/__init__.py +1 -1
- tree_sitter_analyzer/formatters/base_formatter.py +167 -168
- tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
- tree_sitter_analyzer/formatters/java_formatter.py +287 -270
- tree_sitter_analyzer/formatters/python_formatter.py +255 -235
- tree_sitter_analyzer/interfaces/__init__.py +9 -10
- tree_sitter_analyzer/interfaces/cli.py +528 -557
- tree_sitter_analyzer/interfaces/cli_adapter.py +322 -319
- tree_sitter_analyzer/interfaces/mcp_adapter.py +180 -170
- tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
- tree_sitter_analyzer/java_analyzer.py +218 -219
- tree_sitter_analyzer/language_detector.py +398 -400
- tree_sitter_analyzer/language_loader.py +224 -228
- tree_sitter_analyzer/languages/__init__.py +10 -11
- tree_sitter_analyzer/languages/java_plugin.py +1129 -1113
- tree_sitter_analyzer/languages/python_plugin.py +737 -712
- tree_sitter_analyzer/mcp/__init__.py +31 -32
- tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +212 -213
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +560 -550
- tree_sitter_analyzer/mcp/server.py +333 -345
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +621 -557
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +242 -245
- tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
- tree_sitter_analyzer/mcp/utils/__init__.py +105 -106
- tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
- tree_sitter_analyzer/models.py +470 -481
- tree_sitter_analyzer/output_manager.py +261 -264
- tree_sitter_analyzer/plugins/__init__.py +333 -334
- tree_sitter_analyzer/plugins/base.py +477 -446
- tree_sitter_analyzer/plugins/java_plugin.py +608 -625
- tree_sitter_analyzer/plugins/javascript_plugin.py +446 -439
- tree_sitter_analyzer/plugins/manager.py +362 -355
- tree_sitter_analyzer/plugins/plugin_loader.py +85 -83
- tree_sitter_analyzer/plugins/python_plugin.py +606 -598
- tree_sitter_analyzer/plugins/registry.py +374 -366
- tree_sitter_analyzer/queries/__init__.py +26 -27
- tree_sitter_analyzer/queries/java.py +391 -394
- tree_sitter_analyzer/queries/javascript.py +148 -149
- tree_sitter_analyzer/queries/python.py +285 -286
- tree_sitter_analyzer/queries/typescript.py +229 -230
- tree_sitter_analyzer/query_loader.py +254 -260
- tree_sitter_analyzer/table_formatter.py +468 -448
- tree_sitter_analyzer/utils.py +277 -277
- {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/METADATA +21 -6
- tree_sitter_analyzer-0.3.0.dist-info/RECORD +77 -0
- tree_sitter_analyzer-0.2.0.dist-info/RECORD +0 -77
- {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,448 +1,468 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
result = self.
|
|
41
|
-
elif self.format_type == "
|
|
42
|
-
result = self.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
lines.append("
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
lines.append(
|
|
123
|
-
lines.append(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
lines.append(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
lines.append("")
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
]
|
|
172
|
-
if
|
|
173
|
-
lines.append("##
|
|
174
|
-
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
175
|
-
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
176
|
-
|
|
177
|
-
for method in
|
|
178
|
-
lines.append(self._format_method_row(method))
|
|
179
|
-
lines.append("")
|
|
180
|
-
|
|
181
|
-
#
|
|
182
|
-
|
|
183
|
-
m
|
|
184
|
-
for m in (data.get("methods") or [])
|
|
185
|
-
if not m.get("is_constructor", False)
|
|
186
|
-
and str(m.get("visibility")) == "
|
|
187
|
-
]
|
|
188
|
-
if
|
|
189
|
-
lines.append("##
|
|
190
|
-
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
191
|
-
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
192
|
-
|
|
193
|
-
for method in
|
|
194
|
-
lines.append(self._format_method_row(method))
|
|
195
|
-
lines.append("")
|
|
196
|
-
|
|
197
|
-
#
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
lines
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
lines
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
lines.append("")
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
#
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
str(
|
|
291
|
-
f"{
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
return
|
|
325
|
-
|
|
326
|
-
def
|
|
327
|
-
"""
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
""
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
#
|
|
383
|
-
if
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
#
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Table Formatter for Tree-sitter Analyzer
|
|
4
|
+
|
|
5
|
+
Provides table-formatted output for Java code analysis results.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import csv
|
|
9
|
+
import io
|
|
10
|
+
import os
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TableFormatter:
|
|
15
|
+
"""Table formatter for code analysis results"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
format_type: str = "full",
|
|
20
|
+
language: str = "java",
|
|
21
|
+
include_javadoc: bool = False,
|
|
22
|
+
):
|
|
23
|
+
self.format_type = format_type
|
|
24
|
+
self.language = language
|
|
25
|
+
self.include_javadoc = include_javadoc
|
|
26
|
+
|
|
27
|
+
def _get_platform_newline(self) -> str:
|
|
28
|
+
"""Get platform-specific newline character"""
|
|
29
|
+
return os.linesep
|
|
30
|
+
|
|
31
|
+
def _convert_to_platform_newlines(self, text: str) -> str:
|
|
32
|
+
"""Convert standard \\n to platform-specific newline characters"""
|
|
33
|
+
if os.linesep != "\n":
|
|
34
|
+
return text.replace("\n", os.linesep)
|
|
35
|
+
return text
|
|
36
|
+
|
|
37
|
+
def format_structure(self, structure_data: dict[str, Any]) -> str:
|
|
38
|
+
"""Format structure data as table"""
|
|
39
|
+
if self.format_type == "full":
|
|
40
|
+
result = self._format_full_table(structure_data)
|
|
41
|
+
elif self.format_type == "compact":
|
|
42
|
+
result = self._format_compact_table(structure_data)
|
|
43
|
+
elif self.format_type == "csv":
|
|
44
|
+
result = self._format_csv(structure_data)
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError(f"Unsupported format type: {self.format_type}")
|
|
47
|
+
|
|
48
|
+
# Finally convert to platform-specific newline characters
|
|
49
|
+
# Skip newline conversion for CSV format (newline control is handled within _format_csv)
|
|
50
|
+
if self.format_type == "csv":
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
return self._convert_to_platform_newlines(result)
|
|
54
|
+
|
|
55
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
56
|
+
"""Full table format"""
|
|
57
|
+
lines = []
|
|
58
|
+
|
|
59
|
+
# Header - use filename when multiple classes exist
|
|
60
|
+
classes = data.get("classes", [])
|
|
61
|
+
if classes is None:
|
|
62
|
+
classes = []
|
|
63
|
+
if len(classes) > 1:
|
|
64
|
+
# 複数クラスがある場合はファイル名を使用
|
|
65
|
+
file_name = data.get("file_path", "Unknown").split("/")[-1].split("\\")[-1]
|
|
66
|
+
lines.append(f"# {file_name}")
|
|
67
|
+
else:
|
|
68
|
+
# 単一クラスの場合は従来通り
|
|
69
|
+
class_name = classes[0].get("name", "Unknown") if classes else "Unknown"
|
|
70
|
+
lines.append(
|
|
71
|
+
f"# {(data.get('package') or {}).get('name', 'unknown')}.{class_name}"
|
|
72
|
+
)
|
|
73
|
+
lines.append("")
|
|
74
|
+
|
|
75
|
+
# Imports
|
|
76
|
+
imports = data.get("imports", [])
|
|
77
|
+
if imports:
|
|
78
|
+
lines.append("## Imports")
|
|
79
|
+
lines.append(f"```{self.language}")
|
|
80
|
+
for imp in imports:
|
|
81
|
+
lines.append(str(imp.get("statement", "")))
|
|
82
|
+
lines.append("```")
|
|
83
|
+
lines.append("")
|
|
84
|
+
|
|
85
|
+
# Class Info - 複数クラスに対応
|
|
86
|
+
classes = data.get("classes", [])
|
|
87
|
+
if classes is None:
|
|
88
|
+
classes = []
|
|
89
|
+
if len(classes) > 1:
|
|
90
|
+
lines.append("## Classes")
|
|
91
|
+
lines.append("| Class | Type | Visibility | Lines | Methods | Fields |")
|
|
92
|
+
lines.append("|-------|------|------------|-------|---------|--------|")
|
|
93
|
+
|
|
94
|
+
for class_info in classes:
|
|
95
|
+
name = str(class_info.get("name", "Unknown"))
|
|
96
|
+
class_type = str(class_info.get("type", "class"))
|
|
97
|
+
visibility = str(class_info.get("visibility", "public"))
|
|
98
|
+
line_range = class_info.get("line_range", {})
|
|
99
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
100
|
+
|
|
101
|
+
# このクラスのメソッド数とフィールド数を計算
|
|
102
|
+
class_methods = [
|
|
103
|
+
m
|
|
104
|
+
for m in data.get("methods", [])
|
|
105
|
+
if line_range.get("start", 0)
|
|
106
|
+
<= m.get("line_range", {}).get("start", 0)
|
|
107
|
+
<= line_range.get("end", 0)
|
|
108
|
+
]
|
|
109
|
+
class_fields = [
|
|
110
|
+
f
|
|
111
|
+
for f in data.get("fields", [])
|
|
112
|
+
if line_range.get("start", 0)
|
|
113
|
+
<= f.get("line_range", {}).get("start", 0)
|
|
114
|
+
<= line_range.get("end", 0)
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
lines.append(
|
|
118
|
+
f"| {name} | {class_type} | {visibility} | {lines_str} | {len(class_methods)} | {len(class_fields)} |"
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
# 単一クラスの場合は従来通り
|
|
122
|
+
lines.append("## Class Info")
|
|
123
|
+
lines.append("| Property | Value |")
|
|
124
|
+
lines.append("|----------|-------|")
|
|
125
|
+
|
|
126
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
127
|
+
class_info = data.get("classes", [{}])[0] if data.get("classes") else {}
|
|
128
|
+
stats = data.get("statistics") or {}
|
|
129
|
+
|
|
130
|
+
lines.append(f"| Package | {package_name} |")
|
|
131
|
+
lines.append(f"| Type | {str(class_info.get('type', 'class'))} |")
|
|
132
|
+
lines.append(
|
|
133
|
+
f"| Visibility | {str(class_info.get('visibility', 'public'))} |"
|
|
134
|
+
)
|
|
135
|
+
lines.append(
|
|
136
|
+
f"| Lines | {class_info.get('line_range', {}).get('start', 0)}-{class_info.get('line_range', {}).get('end', 0)} |"
|
|
137
|
+
)
|
|
138
|
+
lines.append(f"| Total Methods | {stats.get('method_count', 0)} |")
|
|
139
|
+
lines.append(f"| Total Fields | {stats.get('field_count', 0)} |")
|
|
140
|
+
|
|
141
|
+
lines.append("")
|
|
142
|
+
|
|
143
|
+
# Fields
|
|
144
|
+
fields = data.get("fields", [])
|
|
145
|
+
if fields is None:
|
|
146
|
+
fields = []
|
|
147
|
+
if fields:
|
|
148
|
+
lines.append("## Fields")
|
|
149
|
+
lines.append("| Name | Type | Vis | Modifiers | Line | Doc |")
|
|
150
|
+
lines.append("|------|------|-----|-----------|------|-----|")
|
|
151
|
+
|
|
152
|
+
for field in fields:
|
|
153
|
+
name = str(field.get("name", ""))
|
|
154
|
+
field_type = str(field.get("type", ""))
|
|
155
|
+
visibility = self._convert_visibility(str(field.get("visibility", "")))
|
|
156
|
+
modifiers = ",".join([str(m) for m in field.get("modifiers", [])])
|
|
157
|
+
line = field.get("line_range", {}).get("start", 0)
|
|
158
|
+
if self.include_javadoc:
|
|
159
|
+
doc = self._extract_doc_summary(str(field.get("javadoc", "")))
|
|
160
|
+
else:
|
|
161
|
+
doc = "-"
|
|
162
|
+
|
|
163
|
+
lines.append(
|
|
164
|
+
f"| {name} | {field_type} | {visibility} | {modifiers} | {line} | {doc} |"
|
|
165
|
+
)
|
|
166
|
+
lines.append("")
|
|
167
|
+
|
|
168
|
+
# Constructor
|
|
169
|
+
constructors = [
|
|
170
|
+
m for m in (data.get("methods") or []) if m.get("is_constructor", False)
|
|
171
|
+
]
|
|
172
|
+
if constructors:
|
|
173
|
+
lines.append("## Constructor")
|
|
174
|
+
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
175
|
+
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
176
|
+
|
|
177
|
+
for method in constructors:
|
|
178
|
+
lines.append(self._format_method_row(method))
|
|
179
|
+
lines.append("")
|
|
180
|
+
|
|
181
|
+
# Public Methods
|
|
182
|
+
public_methods = [
|
|
183
|
+
m
|
|
184
|
+
for m in (data.get("methods") or [])
|
|
185
|
+
if not m.get("is_constructor", False)
|
|
186
|
+
and str(m.get("visibility")) == "public"
|
|
187
|
+
]
|
|
188
|
+
if public_methods:
|
|
189
|
+
lines.append("## Public Methods")
|
|
190
|
+
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
191
|
+
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
192
|
+
|
|
193
|
+
for method in public_methods:
|
|
194
|
+
lines.append(self._format_method_row(method))
|
|
195
|
+
lines.append("")
|
|
196
|
+
|
|
197
|
+
# Private Methods
|
|
198
|
+
private_methods = [
|
|
199
|
+
m
|
|
200
|
+
for m in (data.get("methods") or [])
|
|
201
|
+
if not m.get("is_constructor", False)
|
|
202
|
+
and str(m.get("visibility")) == "private"
|
|
203
|
+
]
|
|
204
|
+
if private_methods:
|
|
205
|
+
lines.append("## Private Methods")
|
|
206
|
+
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
207
|
+
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
208
|
+
|
|
209
|
+
for method in private_methods:
|
|
210
|
+
lines.append(self._format_method_row(method))
|
|
211
|
+
lines.append("")
|
|
212
|
+
|
|
213
|
+
# 末尾の空行を削除
|
|
214
|
+
while lines and lines[-1] == "":
|
|
215
|
+
lines.pop()
|
|
216
|
+
|
|
217
|
+
return "\n".join(lines)
|
|
218
|
+
|
|
219
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
220
|
+
"""Compact table format"""
|
|
221
|
+
lines = []
|
|
222
|
+
|
|
223
|
+
# Header
|
|
224
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
225
|
+
classes = data.get("classes", [])
|
|
226
|
+
if classes is None:
|
|
227
|
+
classes = []
|
|
228
|
+
class_name = classes[0].get("name", "Unknown") if classes else "Unknown"
|
|
229
|
+
lines.append(f"# {package_name}.{class_name}")
|
|
230
|
+
lines.append("")
|
|
231
|
+
|
|
232
|
+
# 基本情報
|
|
233
|
+
stats = data.get("statistics") or {}
|
|
234
|
+
lines.append("## Info")
|
|
235
|
+
lines.append("| Property | Value |")
|
|
236
|
+
lines.append("|----------|-------|")
|
|
237
|
+
lines.append(f"| Package | {package_name} |")
|
|
238
|
+
lines.append(f"| Methods | {stats.get('method_count', 0)} |")
|
|
239
|
+
lines.append(f"| Fields | {stats.get('field_count', 0)} |")
|
|
240
|
+
lines.append("")
|
|
241
|
+
|
|
242
|
+
# メソッド(簡略版)
|
|
243
|
+
methods = data.get("methods", [])
|
|
244
|
+
if methods is None:
|
|
245
|
+
methods = []
|
|
246
|
+
if methods:
|
|
247
|
+
lines.append("## Methods")
|
|
248
|
+
lines.append("| Method | Sig | V | L | Cx | Doc |")
|
|
249
|
+
lines.append("|--------|-----|---|---|----|----|")
|
|
250
|
+
|
|
251
|
+
for method in methods:
|
|
252
|
+
name = str(method.get("name", ""))
|
|
253
|
+
signature = self._create_compact_signature(method)
|
|
254
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
255
|
+
line_range = method.get("line_range", {})
|
|
256
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
257
|
+
complexity = method.get("complexity_score", 0)
|
|
258
|
+
doc = self._clean_csv_text(
|
|
259
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
lines.append(
|
|
263
|
+
f"| {name} | {signature} | {visibility} | {lines_str} | {complexity} | {doc} |"
|
|
264
|
+
)
|
|
265
|
+
lines.append("")
|
|
266
|
+
|
|
267
|
+
# 末尾の空行を削除
|
|
268
|
+
while lines and lines[-1] == "":
|
|
269
|
+
lines.pop()
|
|
270
|
+
|
|
271
|
+
return "\n".join(lines)
|
|
272
|
+
|
|
273
|
+
def _format_csv(self, data: dict[str, Any]) -> str:
|
|
274
|
+
"""CSV format"""
|
|
275
|
+
output = io.StringIO()
|
|
276
|
+
writer = csv.writer(
|
|
277
|
+
output, lineterminator="\n"
|
|
278
|
+
) # Explicitly specify newline character
|
|
279
|
+
|
|
280
|
+
# Header
|
|
281
|
+
writer.writerow(
|
|
282
|
+
["Type", "Name", "Signature", "Visibility", "Lines", "Complexity", "Doc"]
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# フィールド
|
|
286
|
+
for field in data.get("fields", []):
|
|
287
|
+
writer.writerow(
|
|
288
|
+
[
|
|
289
|
+
"Field",
|
|
290
|
+
str(field.get("name", "")),
|
|
291
|
+
f"{str(field.get('name', ''))}:{str(field.get('type', ''))}",
|
|
292
|
+
str(field.get("visibility", "")),
|
|
293
|
+
f"{field.get('line_range', {}).get('start', 0)}-{field.get('line_range', {}).get('end', 0)}",
|
|
294
|
+
"",
|
|
295
|
+
self._clean_csv_text(
|
|
296
|
+
self._extract_doc_summary(str(field.get("javadoc", "")))
|
|
297
|
+
),
|
|
298
|
+
]
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# メソッド
|
|
302
|
+
for method in data.get("methods", []):
|
|
303
|
+
writer.writerow(
|
|
304
|
+
[
|
|
305
|
+
"Constructor" if method.get("is_constructor", False) else "Method",
|
|
306
|
+
str(method.get("name", "")),
|
|
307
|
+
self._clean_csv_text(self._create_full_signature(method)),
|
|
308
|
+
str(method.get("visibility", "")),
|
|
309
|
+
f"{method.get('line_range', {}).get('start', 0)}-{method.get('line_range', {}).get('end', 0)}",
|
|
310
|
+
method.get("complexity_score", 0),
|
|
311
|
+
self._clean_csv_text(
|
|
312
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
313
|
+
),
|
|
314
|
+
]
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Completely control CSV output newlines
|
|
318
|
+
csv_content = output.getvalue()
|
|
319
|
+
# Unify all newline patterns and remove trailing newlines
|
|
320
|
+
csv_content = csv_content.replace("\r\n", "\n").replace("\r", "\n")
|
|
321
|
+
csv_content = csv_content.rstrip("\n")
|
|
322
|
+
output.close()
|
|
323
|
+
|
|
324
|
+
return csv_content
|
|
325
|
+
|
|
326
|
+
def _format_method_row(self, method: dict[str, Any]) -> str:
|
|
327
|
+
"""メソッド行のフォーマット"""
|
|
328
|
+
name = str(method.get("name", ""))
|
|
329
|
+
signature = self._create_full_signature(method)
|
|
330
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
331
|
+
line_range = method.get("line_range", {})
|
|
332
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
333
|
+
cols_str = "5-6" # デフォルト値(実際の実装では正確な値を取得)
|
|
334
|
+
complexity = method.get("complexity_score", 0)
|
|
335
|
+
if self.include_javadoc:
|
|
336
|
+
doc = self._clean_csv_text(
|
|
337
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
338
|
+
)
|
|
339
|
+
else:
|
|
340
|
+
doc = "-"
|
|
341
|
+
|
|
342
|
+
return f"| {name} | {signature} | {visibility} | {lines_str} | {cols_str} | {complexity} | {doc} |"
|
|
343
|
+
|
|
344
|
+
def _create_full_signature(self, method: dict[str, Any]) -> str:
|
|
345
|
+
"""完全なメソッドシグネチャを作成"""
|
|
346
|
+
params = method.get("parameters", [])
|
|
347
|
+
param_strs = []
|
|
348
|
+
for param in params:
|
|
349
|
+
param_type = str(param.get("type", "Object"))
|
|
350
|
+
param_name = str(param.get("name", "param"))
|
|
351
|
+
param_strs.append(f"{param_name}:{param_type}")
|
|
352
|
+
|
|
353
|
+
params_str = ", ".join(param_strs)
|
|
354
|
+
return_type = str(method.get("return_type", "void"))
|
|
355
|
+
|
|
356
|
+
modifiers = []
|
|
357
|
+
if method.get("is_static", False):
|
|
358
|
+
modifiers.append("[static]")
|
|
359
|
+
|
|
360
|
+
modifier_str = " ".join(modifiers)
|
|
361
|
+
signature = f"({params_str}):{return_type}"
|
|
362
|
+
|
|
363
|
+
if modifier_str:
|
|
364
|
+
signature += f" {modifier_str}"
|
|
365
|
+
|
|
366
|
+
return signature
|
|
367
|
+
|
|
368
|
+
def _create_compact_signature(self, method: dict[str, Any]) -> str:
|
|
369
|
+
"""コンパクトなメソッドシグネチャを作成"""
|
|
370
|
+
params = method.get("parameters", [])
|
|
371
|
+
param_types = [self._shorten_type(p.get("type", "O")) for p in params]
|
|
372
|
+
params_str = ",".join(param_types)
|
|
373
|
+
return_type = self._shorten_type(method.get("return_type", "void"))
|
|
374
|
+
|
|
375
|
+
return f"({params_str}):{return_type}"
|
|
376
|
+
|
|
377
|
+
def _shorten_type(self, type_name: Any) -> str:
|
|
378
|
+
"""型名を短縮"""
|
|
379
|
+
if type_name is None:
|
|
380
|
+
return "O"
|
|
381
|
+
|
|
382
|
+
# Convert non-string types to string
|
|
383
|
+
if not isinstance(type_name, str):
|
|
384
|
+
type_name = str(type_name)
|
|
385
|
+
|
|
386
|
+
type_mapping = {
|
|
387
|
+
"String": "S",
|
|
388
|
+
"int": "i",
|
|
389
|
+
"long": "l",
|
|
390
|
+
"double": "d",
|
|
391
|
+
"boolean": "b",
|
|
392
|
+
"void": "void",
|
|
393
|
+
"Object": "O",
|
|
394
|
+
"Exception": "E",
|
|
395
|
+
"SQLException": "SE",
|
|
396
|
+
"IllegalArgumentException": "IAE",
|
|
397
|
+
"RuntimeException": "RE",
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
# Map<String,Object> -> M<S,O>
|
|
401
|
+
if "Map<" in type_name:
|
|
402
|
+
return (
|
|
403
|
+
type_name.replace("Map<", "M<")
|
|
404
|
+
.replace("String", "S")
|
|
405
|
+
.replace("Object", "O")
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# List<String> -> L<S>
|
|
409
|
+
if "List<" in type_name:
|
|
410
|
+
return type_name.replace("List<", "L<").replace("String", "S")
|
|
411
|
+
|
|
412
|
+
# String[] -> S[]
|
|
413
|
+
if "[]" in type_name:
|
|
414
|
+
base_type = type_name.replace("[]", "")
|
|
415
|
+
if base_type:
|
|
416
|
+
return type_mapping.get(base_type, base_type[0].upper()) + "[]"
|
|
417
|
+
else:
|
|
418
|
+
return "O[]"
|
|
419
|
+
|
|
420
|
+
return type_mapping.get(type_name, type_name)
|
|
421
|
+
|
|
422
|
+
def _convert_visibility(self, visibility: str) -> str:
|
|
423
|
+
"""可視性を記号に変換"""
|
|
424
|
+
mapping = {"public": "+", "private": "-", "protected": "#", "package": "~"}
|
|
425
|
+
return mapping.get(visibility, visibility)
|
|
426
|
+
|
|
427
|
+
def _extract_doc_summary(self, javadoc: str) -> str:
|
|
428
|
+
"""JavaDocから要約を抽出"""
|
|
429
|
+
if not javadoc:
|
|
430
|
+
return "-"
|
|
431
|
+
|
|
432
|
+
# Remove comment symbols
|
|
433
|
+
clean_doc = (
|
|
434
|
+
javadoc.replace("/**", "").replace("*/", "").replace("*", "").strip()
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
# Get first line (use standard \\n only)
|
|
438
|
+
lines = clean_doc.split("\n")
|
|
439
|
+
first_line = lines[0].strip()
|
|
440
|
+
|
|
441
|
+
# Truncate if too long
|
|
442
|
+
if len(first_line) > 50:
|
|
443
|
+
first_line = first_line[:47] + "..."
|
|
444
|
+
|
|
445
|
+
# Escape characters that cause problems in Markdown tables (use standard \\n only)
|
|
446
|
+
return first_line.replace("|", "\\|").replace("\n", " ")
|
|
447
|
+
|
|
448
|
+
def _clean_csv_text(self, text: str) -> str:
|
|
449
|
+
"""Text cleaning for CSV format"""
|
|
450
|
+
if not text:
|
|
451
|
+
return ""
|
|
452
|
+
|
|
453
|
+
# Replace all newline characters with spaces
|
|
454
|
+
cleaned = text.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
|
|
455
|
+
# Convert consecutive spaces to single space
|
|
456
|
+
cleaned = " ".join(cleaned.split())
|
|
457
|
+
# Escape characters that cause problems in CSV
|
|
458
|
+
cleaned = cleaned.replace('"', '""') # Escape double quotes
|
|
459
|
+
|
|
460
|
+
return cleaned
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def create_table_formatter(
|
|
464
|
+
format_type: str, language: str = "java", include_javadoc: bool = False
|
|
465
|
+
):
|
|
466
|
+
"""Create table formatter (using new factory)"""
|
|
467
|
+
# Create TableFormatter directly (for JavaDoc support)
|
|
468
|
+
return TableFormatter(format_type, language, include_javadoc)
|