tree-sitter-analyzer 0.1.2__py3-none-any.whl → 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.
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/cli/commands/advanced_command.py +10 -10
- tree_sitter_analyzer/cli/commands/base_command.py +11 -11
- tree_sitter_analyzer/cli/commands/partial_read_command.py +12 -12
- tree_sitter_analyzer/cli/commands/structure_command.py +13 -13
- tree_sitter_analyzer/cli/commands/summary_command.py +8 -8
- tree_sitter_analyzer/cli_main.py +6 -6
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +5 -5
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +9 -9
- tree_sitter_analyzer/table_formatter.py +26 -26
- tree_sitter_analyzer-0.2.0.dist-info/METADATA +331 -0
- {tree_sitter_analyzer-0.1.2.dist-info → tree_sitter_analyzer-0.2.0.dist-info}/RECORD +14 -14
- tree_sitter_analyzer-0.1.2.dist-info/METADATA +0 -444
- {tree_sitter_analyzer-0.1.2.dist-info → tree_sitter_analyzer-0.2.0.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.1.2.dist-info → tree_sitter_analyzer-0.2.0.dist-info}/entry_points.txt +0 -0
tree_sitter_analyzer/__init__.py
CHANGED
|
@@ -37,7 +37,7 @@ class AdvancedCommand(BaseCommand):
|
|
|
37
37
|
"node_count": analysis_result.node_count,
|
|
38
38
|
"language": analysis_result.language,
|
|
39
39
|
}
|
|
40
|
-
output_section("
|
|
40
|
+
output_section("Statistics")
|
|
41
41
|
if self.args.output_format == "json":
|
|
42
42
|
output_json(stats)
|
|
43
43
|
else:
|
|
@@ -46,7 +46,7 @@ class AdvancedCommand(BaseCommand):
|
|
|
46
46
|
|
|
47
47
|
def _output_full_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
48
48
|
"""Output full analysis results."""
|
|
49
|
-
output_section("
|
|
49
|
+
output_section("Advanced Analysis Results")
|
|
50
50
|
if self.args.output_format == "json":
|
|
51
51
|
result_dict = {
|
|
52
52
|
"file_path": analysis_result.file_path,
|
|
@@ -72,17 +72,17 @@ class AdvancedCommand(BaseCommand):
|
|
|
72
72
|
|
|
73
73
|
def _output_text_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
74
74
|
"""Output analysis in text format."""
|
|
75
|
-
output_data(f"
|
|
76
|
-
output_data(f"
|
|
77
|
-
output_data(f"
|
|
75
|
+
output_data(f"File: {analysis_result.file_path}")
|
|
76
|
+
output_data(f"Package: (default)")
|
|
77
|
+
output_data(f"Lines: {analysis_result.line_count}")
|
|
78
78
|
|
|
79
79
|
element_counts = {}
|
|
80
80
|
for element in analysis_result.elements:
|
|
81
81
|
element_type = getattr(element, "__class__", type(element)).__name__
|
|
82
82
|
element_counts[element_type] = element_counts.get(element_type, 0) + 1
|
|
83
83
|
|
|
84
|
-
output_data(f"
|
|
85
|
-
output_data(f"
|
|
86
|
-
output_data(f"
|
|
87
|
-
output_data(f"
|
|
88
|
-
output_data(f"
|
|
84
|
+
output_data(f"Classes: {element_counts.get('Class', 0)}")
|
|
85
|
+
output_data(f"Methods: {element_counts.get('Function', 0)}")
|
|
86
|
+
output_data(f"Fields: {element_counts.get('Variable', 0)}")
|
|
87
|
+
output_data(f"Imports: {element_counts.get('Import', 0)}")
|
|
88
|
+
output_data(f"Annotations: 0")
|
|
@@ -34,13 +34,13 @@ class BaseCommand(ABC):
|
|
|
34
34
|
def validate_file(self) -> bool:
|
|
35
35
|
"""Validate input file exists and is accessible."""
|
|
36
36
|
if not hasattr(self.args, "file_path") or not self.args.file_path:
|
|
37
|
-
output_error("ERROR:
|
|
37
|
+
output_error("ERROR: File path not specified.")
|
|
38
38
|
return False
|
|
39
39
|
|
|
40
40
|
import os
|
|
41
41
|
|
|
42
42
|
if not os.path.exists(self.args.file_path):
|
|
43
|
-
output_error(f"ERROR:
|
|
43
|
+
output_error(f"ERROR: File not found: {self.args.file_path}")
|
|
44
44
|
return False
|
|
45
45
|
|
|
46
46
|
return True
|
|
@@ -50,18 +50,18 @@ class BaseCommand(ABC):
|
|
|
50
50
|
if hasattr(self.args, "language") and self.args.language:
|
|
51
51
|
target_language = self.args.language.lower()
|
|
52
52
|
if (not hasattr(self.args, "table") or not self.args.table) and (not hasattr(self.args, "quiet") or not self.args.quiet):
|
|
53
|
-
output_info(f"INFO:
|
|
53
|
+
output_info(f"INFO: Language explicitly specified: {target_language}")
|
|
54
54
|
else:
|
|
55
55
|
target_language = detect_language_from_file(self.args.file_path)
|
|
56
56
|
if target_language == "unknown":
|
|
57
57
|
output_error(
|
|
58
|
-
f"ERROR:
|
|
58
|
+
f"ERROR: Could not determine language for file '{self.args.file_path}'."
|
|
59
59
|
)
|
|
60
60
|
return None
|
|
61
61
|
else:
|
|
62
62
|
if (not hasattr(self.args, "table") or not self.args.table) and (not hasattr(self.args, "quiet") or not self.args.quiet):
|
|
63
63
|
output_info(
|
|
64
|
-
f"INFO:
|
|
64
|
+
f"INFO: Language auto-detected from extension: {target_language}"
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
# Language support validation
|
|
@@ -69,7 +69,7 @@ class BaseCommand(ABC):
|
|
|
69
69
|
if target_language != "java":
|
|
70
70
|
if (not hasattr(self.args, "table") or not self.args.table) and (not hasattr(self.args, "quiet") or not self.args.quiet):
|
|
71
71
|
output_info(
|
|
72
|
-
"INFO: Java
|
|
72
|
+
"INFO: Trying with Java analysis engine. May not work correctly."
|
|
73
73
|
)
|
|
74
74
|
target_language = "java" # Fallback
|
|
75
75
|
|
|
@@ -89,10 +89,10 @@ class BaseCommand(ABC):
|
|
|
89
89
|
end_column=getattr(self.args, 'end_column', None)
|
|
90
90
|
)
|
|
91
91
|
if partial_content is None:
|
|
92
|
-
output_error("ERROR:
|
|
92
|
+
output_error("ERROR: Failed to read file partially")
|
|
93
93
|
return None
|
|
94
94
|
except Exception as e:
|
|
95
|
-
output_error(f"ERROR:
|
|
95
|
+
output_error(f"ERROR: Failed to read file partially: {e}")
|
|
96
96
|
return None
|
|
97
97
|
|
|
98
98
|
request = AnalysisRequest(
|
|
@@ -109,13 +109,13 @@ class BaseCommand(ABC):
|
|
|
109
109
|
if analysis_result
|
|
110
110
|
else "Unknown error"
|
|
111
111
|
)
|
|
112
|
-
output_error(f"ERROR:
|
|
112
|
+
output_error(f"ERROR: Analysis failed: {error_msg}")
|
|
113
113
|
return None
|
|
114
114
|
|
|
115
115
|
return analysis_result
|
|
116
116
|
|
|
117
117
|
except Exception as e:
|
|
118
|
-
output_error(f"ERROR:
|
|
118
|
+
output_error(f"ERROR: An error occurred during analysis: {e}")
|
|
119
119
|
return None
|
|
120
120
|
|
|
121
121
|
def execute(self) -> int:
|
|
@@ -138,7 +138,7 @@ class BaseCommand(ABC):
|
|
|
138
138
|
try:
|
|
139
139
|
return asyncio.run(self.execute_async(language))
|
|
140
140
|
except Exception as e:
|
|
141
|
-
output_error(f"ERROR:
|
|
141
|
+
output_error(f"ERROR: An error occurred during command execution: {e}")
|
|
142
142
|
return 1
|
|
143
143
|
|
|
144
144
|
@abstractmethod
|
|
@@ -28,14 +28,14 @@ class PartialReadCommand(BaseCommand):
|
|
|
28
28
|
"""Validate input file exists and is accessible."""
|
|
29
29
|
if not hasattr(self.args, "file_path") or not self.args.file_path:
|
|
30
30
|
from ...output_manager import output_error
|
|
31
|
-
output_error("ERROR:
|
|
31
|
+
output_error("ERROR: File path not specified.")
|
|
32
32
|
return False
|
|
33
33
|
|
|
34
34
|
import os
|
|
35
35
|
|
|
36
36
|
if not os.path.exists(self.args.file_path):
|
|
37
37
|
from ...output_manager import output_error
|
|
38
|
-
output_error(f"ERROR:
|
|
38
|
+
output_error(f"ERROR: File not found: {self.args.file_path}")
|
|
39
39
|
return False
|
|
40
40
|
|
|
41
41
|
return True
|
|
@@ -54,17 +54,17 @@ class PartialReadCommand(BaseCommand):
|
|
|
54
54
|
# Validate partial read arguments
|
|
55
55
|
if not self.args.start_line:
|
|
56
56
|
from ...output_manager import output_error
|
|
57
|
-
output_error("ERROR: --start-line
|
|
57
|
+
output_error("ERROR: --start-line is required")
|
|
58
58
|
return 1
|
|
59
59
|
|
|
60
60
|
if self.args.start_line < 1:
|
|
61
61
|
from ...output_manager import output_error
|
|
62
|
-
output_error("ERROR: --start-line
|
|
62
|
+
output_error("ERROR: --start-line must be 1 or greater")
|
|
63
63
|
return 1
|
|
64
64
|
|
|
65
65
|
if self.args.end_line and self.args.end_line < self.args.start_line:
|
|
66
66
|
from ...output_manager import output_error
|
|
67
|
-
output_error("ERROR: --end-line
|
|
67
|
+
output_error("ERROR: --end-line must be greater than or equal to --start-line")
|
|
68
68
|
return 1
|
|
69
69
|
|
|
70
70
|
# Read partial content
|
|
@@ -79,7 +79,7 @@ class PartialReadCommand(BaseCommand):
|
|
|
79
79
|
|
|
80
80
|
if partial_content is None:
|
|
81
81
|
from ...output_manager import output_error
|
|
82
|
-
output_error("ERROR:
|
|
82
|
+
output_error("ERROR: Failed to read file partially")
|
|
83
83
|
return 1
|
|
84
84
|
|
|
85
85
|
# Output the result
|
|
@@ -88,7 +88,7 @@ class PartialReadCommand(BaseCommand):
|
|
|
88
88
|
|
|
89
89
|
except Exception as e:
|
|
90
90
|
from ...output_manager import output_error
|
|
91
|
-
output_error(f"ERROR:
|
|
91
|
+
output_error(f"ERROR: Failed to read file partially: {e}")
|
|
92
92
|
return 1
|
|
93
93
|
|
|
94
94
|
def _output_partial_content(self, content: str) -> None:
|
|
@@ -107,7 +107,7 @@ class PartialReadCommand(BaseCommand):
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
# Build range info for header
|
|
110
|
-
range_info = f"
|
|
110
|
+
range_info = f"Line {self.args.start_line}"
|
|
111
111
|
if hasattr(self.args, 'end_line') and self.args.end_line:
|
|
112
112
|
range_info += f"-{self.args.end_line}"
|
|
113
113
|
|
|
@@ -119,10 +119,10 @@ class PartialReadCommand(BaseCommand):
|
|
|
119
119
|
output_json(result_data)
|
|
120
120
|
else:
|
|
121
121
|
# Human-readable format with header
|
|
122
|
-
output_section("
|
|
123
|
-
output_data(f"
|
|
124
|
-
output_data(f"
|
|
125
|
-
output_data(f"
|
|
122
|
+
output_section("Partial Read Result")
|
|
123
|
+
output_data(f"File: {self.args.file_path}")
|
|
124
|
+
output_data(f"Range: {range_info}")
|
|
125
|
+
output_data(f"Characters read: {len(content)}")
|
|
126
126
|
output_data("") # Empty line for separation
|
|
127
127
|
|
|
128
128
|
# Output the actual content
|
|
@@ -27,7 +27,7 @@ class StructureCommand(BaseCommand):
|
|
|
27
27
|
|
|
28
28
|
def _output_structure_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
29
29
|
"""Output structure analysis results with appropriate Japanese header."""
|
|
30
|
-
output_section("
|
|
30
|
+
output_section("Structure Analysis Results")
|
|
31
31
|
|
|
32
32
|
# Convert to legacy structure format expected by tests
|
|
33
33
|
structure_dict = self._convert_to_legacy_format(analysis_result)
|
|
@@ -91,31 +91,31 @@ class StructureCommand(BaseCommand):
|
|
|
91
91
|
|
|
92
92
|
def _output_text_format(self, structure_dict: dict) -> None:
|
|
93
93
|
"""Output structure analysis in human-readable text format."""
|
|
94
|
-
output_data(f"
|
|
95
|
-
output_data(f"
|
|
94
|
+
output_data(f"File: {structure_dict['file_path']}")
|
|
95
|
+
output_data(f"Language: {structure_dict['language']}")
|
|
96
96
|
|
|
97
97
|
if structure_dict['package']:
|
|
98
|
-
output_data(f"
|
|
98
|
+
output_data(f"Package: {structure_dict['package']['name']}")
|
|
99
99
|
|
|
100
100
|
stats = structure_dict['statistics']
|
|
101
|
-
output_data(f"
|
|
102
|
-
output_data(f"
|
|
103
|
-
output_data(f"
|
|
104
|
-
output_data(f"
|
|
105
|
-
output_data(f"
|
|
106
|
-
output_data(f"
|
|
101
|
+
output_data(f"Statistics:")
|
|
102
|
+
output_data(f" Classes: {stats['class_count']}")
|
|
103
|
+
output_data(f" Methods: {stats['method_count']}")
|
|
104
|
+
output_data(f" Fields: {stats['field_count']}")
|
|
105
|
+
output_data(f" Imports: {stats['import_count']}")
|
|
106
|
+
output_data(f" Total lines: {stats['total_lines']}")
|
|
107
107
|
|
|
108
108
|
if structure_dict['classes']:
|
|
109
|
-
output_data("
|
|
109
|
+
output_data("Classes:")
|
|
110
110
|
for cls in structure_dict['classes']:
|
|
111
111
|
output_data(f" - {cls['name']}")
|
|
112
112
|
|
|
113
113
|
if structure_dict['methods']:
|
|
114
|
-
output_data("
|
|
114
|
+
output_data("Methods:")
|
|
115
115
|
for method in structure_dict['methods']:
|
|
116
116
|
output_data(f" - {method['name']}")
|
|
117
117
|
|
|
118
118
|
if structure_dict['fields']:
|
|
119
|
-
output_data("
|
|
119
|
+
output_data("Fields:")
|
|
120
120
|
for field in structure_dict['fields']:
|
|
121
121
|
output_data(f" - {field['name']}")
|
|
@@ -27,7 +27,7 @@ class SummaryCommand(BaseCommand):
|
|
|
27
27
|
|
|
28
28
|
def _output_summary_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
29
29
|
"""Output summary analysis results."""
|
|
30
|
-
output_section("
|
|
30
|
+
output_section("Summary Results")
|
|
31
31
|
|
|
32
32
|
# Get summary types from args (default: classes,methods)
|
|
33
33
|
summary_types = getattr(self.args, 'summary', 'classes,methods')
|
|
@@ -75,19 +75,19 @@ class SummaryCommand(BaseCommand):
|
|
|
75
75
|
|
|
76
76
|
def _output_text_format(self, summary_data: dict, requested_types: list) -> None:
|
|
77
77
|
"""Output summary in human-readable text format."""
|
|
78
|
-
output_data(f"
|
|
79
|
-
output_data(f"
|
|
78
|
+
output_data(f"File: {summary_data['file_path']}")
|
|
79
|
+
output_data(f"Language: {summary_data['language']}")
|
|
80
80
|
|
|
81
81
|
for element_type in requested_types:
|
|
82
82
|
if element_type in summary_data['summary']:
|
|
83
83
|
elements = summary_data['summary'][element_type]
|
|
84
84
|
type_name_map = {
|
|
85
|
-
'classes': '
|
|
86
|
-
'methods': '
|
|
87
|
-
'fields': '
|
|
88
|
-
'imports': '
|
|
85
|
+
'classes': 'Classes',
|
|
86
|
+
'methods': 'Methods',
|
|
87
|
+
'fields': 'Fields',
|
|
88
|
+
'imports': 'Imports'
|
|
89
89
|
}
|
|
90
90
|
type_name = type_name_map.get(element_type, element_type)
|
|
91
|
-
output_data(f"\n{type_name} ({len(elements)}
|
|
91
|
+
output_data(f"\n{type_name} ({len(elements)} items):")
|
|
92
92
|
for element in elements:
|
|
93
93
|
output_data(f" - {element['name']}")
|
tree_sitter_analyzer/cli_main.py
CHANGED
|
@@ -183,23 +183,23 @@ def handle_special_commands(args: argparse.Namespace) -> Optional[int]:
|
|
|
183
183
|
# Validate partial read options
|
|
184
184
|
if hasattr(args, 'partial_read') and args.partial_read:
|
|
185
185
|
if args.start_line is None:
|
|
186
|
-
output_error("ERROR: --start-line
|
|
186
|
+
output_error("ERROR: --start-line is required")
|
|
187
187
|
return 1
|
|
188
188
|
|
|
189
189
|
if args.start_line < 1:
|
|
190
|
-
output_error("ERROR: --start-line
|
|
190
|
+
output_error("ERROR: --start-line must be 1 or greater")
|
|
191
191
|
return 1
|
|
192
192
|
|
|
193
193
|
if args.end_line and args.end_line < args.start_line:
|
|
194
|
-
output_error("ERROR: --end-line
|
|
194
|
+
output_error("ERROR: --end-line must be greater than or equal to --start-line")
|
|
195
195
|
return 1
|
|
196
196
|
|
|
197
197
|
if args.start_column is not None and args.start_column < 0:
|
|
198
|
-
output_error("ERROR: --start-column
|
|
198
|
+
output_error("ERROR: --start-column must be 0 or greater")
|
|
199
199
|
return 1
|
|
200
200
|
|
|
201
201
|
if args.end_column is not None and args.end_column < 0:
|
|
202
|
-
output_error("ERROR: --end-column
|
|
202
|
+
output_error("ERROR: --end-column must be 0 or greater")
|
|
203
203
|
return 1
|
|
204
204
|
|
|
205
205
|
# Query language commands
|
|
@@ -258,7 +258,7 @@ def main() -> None:
|
|
|
258
258
|
sys.exit(exit_code)
|
|
259
259
|
else:
|
|
260
260
|
if not args.file_path:
|
|
261
|
-
output_error("ERROR:
|
|
261
|
+
output_error("ERROR: File path not specified.")
|
|
262
262
|
else:
|
|
263
263
|
output_error("ERROR: 実行可能なコマンドが指定されていません。")
|
|
264
264
|
parser.print_help()
|
|
@@ -158,16 +158,16 @@ class ReadPartialTool:
|
|
|
158
158
|
json_output = json.dumps(result_data, indent=2, ensure_ascii=False)
|
|
159
159
|
|
|
160
160
|
# Build range info for header
|
|
161
|
-
range_info = f"
|
|
161
|
+
range_info = f"Line {start_line}"
|
|
162
162
|
if end_line:
|
|
163
163
|
range_info += f"-{end_line}"
|
|
164
164
|
|
|
165
165
|
# Build CLI-compatible output with header and JSON (without log message)
|
|
166
166
|
cli_output = (
|
|
167
|
-
f"---
|
|
168
|
-
f"
|
|
169
|
-
f"
|
|
170
|
-
f"
|
|
167
|
+
f"--- Partial Read Result ---\n"
|
|
168
|
+
f"File: {file_path}\n"
|
|
169
|
+
f"Range: {range_info}\n"
|
|
170
|
+
f"Characters read: {len(content)}\n"
|
|
171
171
|
f"{json_output}"
|
|
172
172
|
)
|
|
173
173
|
|
|
@@ -39,7 +39,7 @@ class TableFormatTool:
|
|
|
39
39
|
|
|
40
40
|
def get_tool_schema(self) -> Dict[str, Any]:
|
|
41
41
|
"""
|
|
42
|
-
Get the MCP tool schema for
|
|
42
|
+
Get the MCP tool schema for analyze_code_structure.
|
|
43
43
|
|
|
44
44
|
Returns:
|
|
45
45
|
Dictionary containing the tool schema
|
|
@@ -255,7 +255,7 @@ class TableFormatTool:
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
async def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
258
|
-
"""Execute
|
|
258
|
+
"""Execute code structure analysis tool."""
|
|
259
259
|
try:
|
|
260
260
|
# Validate arguments first
|
|
261
261
|
if "file_path" not in args:
|
|
@@ -275,7 +275,7 @@ class TableFormatTool:
|
|
|
275
275
|
|
|
276
276
|
# Use performance monitoring
|
|
277
277
|
monitor = get_performance_monitor()
|
|
278
|
-
with monitor.measure_operation("
|
|
278
|
+
with monitor.measure_operation("code_structure_analysis"):
|
|
279
279
|
# Analyze structure using the unified analysis engine
|
|
280
280
|
request = AnalysisRequest(
|
|
281
281
|
file_path=file_path,
|
|
@@ -328,12 +328,12 @@ class TableFormatTool:
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
except Exception as e:
|
|
331
|
-
self.logger.error(f"Error in
|
|
331
|
+
self.logger.error(f"Error in code structure analysis tool: {e}")
|
|
332
332
|
raise
|
|
333
333
|
|
|
334
334
|
def get_tool_definition(self) -> Any:
|
|
335
335
|
"""
|
|
336
|
-
Get the MCP tool definition for
|
|
336
|
+
Get the MCP tool definition for analyze_code_structure.
|
|
337
337
|
|
|
338
338
|
Returns:
|
|
339
339
|
Tool definition object compatible with MCP server
|
|
@@ -342,15 +342,15 @@ class TableFormatTool:
|
|
|
342
342
|
from mcp.types import Tool
|
|
343
343
|
|
|
344
344
|
return Tool(
|
|
345
|
-
name="
|
|
346
|
-
description="
|
|
345
|
+
name="analyze_code_structure",
|
|
346
|
+
description="Analyze code structure and generate detailed overview tables (classes, methods, fields) for large files",
|
|
347
347
|
inputSchema=self.get_tool_schema(),
|
|
348
348
|
)
|
|
349
349
|
except ImportError:
|
|
350
350
|
# Fallback for when MCP is not available
|
|
351
351
|
return {
|
|
352
|
-
"name": "
|
|
353
|
-
"description": "
|
|
352
|
+
"name": "analyze_code_structure",
|
|
353
|
+
"description": "Analyze code structure and generate detailed overview tables (classes, methods, fields) for large files",
|
|
354
354
|
"inputSchema": self.get_tool_schema(),
|
|
355
355
|
}
|
|
356
356
|
|
|
@@ -15,7 +15,7 @@ from .models import AnalysisResult
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class TableFormatter:
|
|
18
|
-
"""
|
|
18
|
+
"""Table formatter for code analysis results"""
|
|
19
19
|
|
|
20
20
|
def __init__(self, format_type: str = "full", language: str = "java", include_javadoc: bool = False):
|
|
21
21
|
self.format_type = format_type
|
|
@@ -23,17 +23,17 @@ class TableFormatter:
|
|
|
23
23
|
self.include_javadoc = include_javadoc
|
|
24
24
|
|
|
25
25
|
def _get_platform_newline(self) -> str:
|
|
26
|
-
"""
|
|
26
|
+
"""Get platform-specific newline character"""
|
|
27
27
|
return os.linesep
|
|
28
28
|
|
|
29
29
|
def _convert_to_platform_newlines(self, text: str) -> str:
|
|
30
|
-
"""
|
|
30
|
+
"""Convert standard \\n to platform-specific newline characters"""
|
|
31
31
|
if os.linesep != "\n":
|
|
32
32
|
return text.replace("\n", os.linesep)
|
|
33
33
|
return text
|
|
34
34
|
|
|
35
35
|
def format_structure(self, structure_data: Dict[str, Any]) -> str:
|
|
36
|
-
"""
|
|
36
|
+
"""Format structure data as table"""
|
|
37
37
|
if self.format_type == "full":
|
|
38
38
|
result = self._format_full_table(structure_data)
|
|
39
39
|
elif self.format_type == "compact":
|
|
@@ -43,18 +43,18 @@ class TableFormatter:
|
|
|
43
43
|
else:
|
|
44
44
|
raise ValueError(f"Unsupported format type: {self.format_type}")
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
# CSV
|
|
46
|
+
# Finally convert to platform-specific newline characters
|
|
47
|
+
# Skip newline conversion for CSV format (newline control is handled within _format_csv)
|
|
48
48
|
if self.format_type == "csv":
|
|
49
49
|
return result
|
|
50
50
|
|
|
51
51
|
return self._convert_to_platform_newlines(result)
|
|
52
52
|
|
|
53
53
|
def _format_full_table(self, data: Dict[str, Any]) -> str:
|
|
54
|
-
"""
|
|
54
|
+
"""Full table format"""
|
|
55
55
|
lines = []
|
|
56
56
|
|
|
57
|
-
#
|
|
57
|
+
# Header - use filename when multiple classes exist
|
|
58
58
|
classes = data.get("classes", [])
|
|
59
59
|
if classes is None:
|
|
60
60
|
classes = []
|
|
@@ -201,10 +201,10 @@ class TableFormatter:
|
|
|
201
201
|
return "\n".join(lines)
|
|
202
202
|
|
|
203
203
|
def _format_compact_table(self, data: Dict[str, Any]) -> str:
|
|
204
|
-
"""
|
|
204
|
+
"""Compact table format"""
|
|
205
205
|
lines = []
|
|
206
206
|
|
|
207
|
-
#
|
|
207
|
+
# Header
|
|
208
208
|
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
209
209
|
classes = data.get("classes", [])
|
|
210
210
|
if classes is None:
|
|
@@ -255,11 +255,11 @@ class TableFormatter:
|
|
|
255
255
|
return "\n".join(lines)
|
|
256
256
|
|
|
257
257
|
def _format_csv(self, data: Dict[str, Any]) -> str:
|
|
258
|
-
"""CSV
|
|
258
|
+
"""CSV format"""
|
|
259
259
|
output = io.StringIO()
|
|
260
|
-
writer = csv.writer(output, lineterminator="\n") #
|
|
260
|
+
writer = csv.writer(output, lineterminator="\n") # Explicitly specify newline character
|
|
261
261
|
|
|
262
|
-
#
|
|
262
|
+
# Header
|
|
263
263
|
writer.writerow(
|
|
264
264
|
["Type", "Name", "Signature", "Visibility", "Lines", "Complexity", "Doc"]
|
|
265
265
|
)
|
|
@@ -296,9 +296,9 @@ class TableFormatter:
|
|
|
296
296
|
]
|
|
297
297
|
)
|
|
298
298
|
|
|
299
|
-
# CSV
|
|
299
|
+
# Completely control CSV output newlines
|
|
300
300
|
csv_content = output.getvalue()
|
|
301
|
-
#
|
|
301
|
+
# Unify all newline patterns and remove trailing newlines
|
|
302
302
|
csv_content = csv_content.replace("\r\n", "\n").replace("\r", "\n")
|
|
303
303
|
csv_content = csv_content.rstrip("\n")
|
|
304
304
|
output.close()
|
|
@@ -411,38 +411,38 @@ class TableFormatter:
|
|
|
411
411
|
if not javadoc:
|
|
412
412
|
return "-"
|
|
413
413
|
|
|
414
|
-
#
|
|
414
|
+
# Remove comment symbols
|
|
415
415
|
clean_doc = (
|
|
416
416
|
javadoc.replace("/**", "").replace("*/", "").replace("*", "").strip()
|
|
417
417
|
)
|
|
418
418
|
|
|
419
|
-
#
|
|
419
|
+
# Get first line (use standard \\n only)
|
|
420
420
|
lines = clean_doc.split("\n")
|
|
421
421
|
first_line = lines[0].strip()
|
|
422
422
|
|
|
423
|
-
#
|
|
423
|
+
# Truncate if too long
|
|
424
424
|
if len(first_line) > 50:
|
|
425
425
|
first_line = first_line[:47] + "..."
|
|
426
426
|
|
|
427
|
-
# Markdown
|
|
427
|
+
# Escape characters that cause problems in Markdown tables (use standard \\n only)
|
|
428
428
|
return first_line.replace("|", "\\|").replace("\n", " ")
|
|
429
429
|
|
|
430
430
|
def _clean_csv_text(self, text: str) -> str:
|
|
431
|
-
"""CSV
|
|
431
|
+
"""Text cleaning for CSV format"""
|
|
432
432
|
if not text:
|
|
433
433
|
return ""
|
|
434
434
|
|
|
435
|
-
#
|
|
435
|
+
# Replace all newline characters with spaces
|
|
436
436
|
cleaned = text.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
|
|
437
|
-
#
|
|
437
|
+
# Convert consecutive spaces to single space
|
|
438
438
|
cleaned = " ".join(cleaned.split())
|
|
439
|
-
# CSV
|
|
440
|
-
cleaned = cleaned.replace('"', '""') #
|
|
439
|
+
# Escape characters that cause problems in CSV
|
|
440
|
+
cleaned = cleaned.replace('"', '""') # Escape double quotes
|
|
441
441
|
|
|
442
442
|
return cleaned
|
|
443
443
|
|
|
444
444
|
|
|
445
445
|
def create_table_formatter(format_type: str, language: str = "java", include_javadoc: bool = False):
|
|
446
|
-
"""
|
|
447
|
-
#
|
|
446
|
+
"""Create table formatter (using new factory)"""
|
|
447
|
+
# Create TableFormatter directly (for JavaDoc support)
|
|
448
448
|
return TableFormatter(format_type, language, include_javadoc)
|