tree-sitter-analyzer 1.9.17.1__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 (149) hide show
  1. tree_sitter_analyzer/__init__.py +132 -0
  2. tree_sitter_analyzer/__main__.py +11 -0
  3. tree_sitter_analyzer/api.py +853 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +12 -0
  6. tree_sitter_analyzer/cli/argument_validator.py +89 -0
  7. tree_sitter_analyzer/cli/commands/__init__.py +26 -0
  8. tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
  9. tree_sitter_analyzer/cli/commands/base_command.py +181 -0
  10. tree_sitter_analyzer/cli/commands/default_command.py +18 -0
  11. tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
  12. tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
  13. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
  14. tree_sitter_analyzer/cli/commands/query_command.py +109 -0
  15. tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
  16. tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
  17. tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
  18. tree_sitter_analyzer/cli/commands/table_command.py +414 -0
  19. tree_sitter_analyzer/cli/info_commands.py +124 -0
  20. tree_sitter_analyzer/cli_main.py +472 -0
  21. tree_sitter_analyzer/constants.py +85 -0
  22. tree_sitter_analyzer/core/__init__.py +15 -0
  23. tree_sitter_analyzer/core/analysis_engine.py +580 -0
  24. tree_sitter_analyzer/core/cache_service.py +333 -0
  25. tree_sitter_analyzer/core/engine.py +585 -0
  26. tree_sitter_analyzer/core/parser.py +293 -0
  27. tree_sitter_analyzer/core/query.py +605 -0
  28. tree_sitter_analyzer/core/query_filter.py +200 -0
  29. tree_sitter_analyzer/core/query_service.py +340 -0
  30. tree_sitter_analyzer/encoding_utils.py +530 -0
  31. tree_sitter_analyzer/exceptions.py +747 -0
  32. tree_sitter_analyzer/file_handler.py +246 -0
  33. tree_sitter_analyzer/formatters/__init__.py +1 -0
  34. tree_sitter_analyzer/formatters/base_formatter.py +201 -0
  35. tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
  36. tree_sitter_analyzer/formatters/formatter_config.py +197 -0
  37. tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
  38. tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
  39. tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
  40. tree_sitter_analyzer/formatters/go_formatter.py +368 -0
  41. tree_sitter_analyzer/formatters/html_formatter.py +498 -0
  42. tree_sitter_analyzer/formatters/java_formatter.py +423 -0
  43. tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
  44. tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
  45. tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
  46. tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
  47. tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
  48. tree_sitter_analyzer/formatters/php_formatter.py +301 -0
  49. tree_sitter_analyzer/formatters/python_formatter.py +830 -0
  50. tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
  51. tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
  52. tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
  53. tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
  54. tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
  55. tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
  56. tree_sitter_analyzer/interfaces/__init__.py +9 -0
  57. tree_sitter_analyzer/interfaces/cli.py +535 -0
  58. tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
  59. tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
  60. tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
  61. tree_sitter_analyzer/language_detector.py +553 -0
  62. tree_sitter_analyzer/language_loader.py +271 -0
  63. tree_sitter_analyzer/languages/__init__.py +10 -0
  64. tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
  65. tree_sitter_analyzer/languages/css_plugin.py +449 -0
  66. tree_sitter_analyzer/languages/go_plugin.py +836 -0
  67. tree_sitter_analyzer/languages/html_plugin.py +496 -0
  68. tree_sitter_analyzer/languages/java_plugin.py +1299 -0
  69. tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
  70. tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
  71. tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
  72. tree_sitter_analyzer/languages/php_plugin.py +862 -0
  73. tree_sitter_analyzer/languages/python_plugin.py +1636 -0
  74. tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
  75. tree_sitter_analyzer/languages/rust_plugin.py +673 -0
  76. tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
  77. tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
  78. tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
  79. tree_sitter_analyzer/legacy_table_formatter.py +860 -0
  80. tree_sitter_analyzer/mcp/__init__.py +34 -0
  81. tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
  82. tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
  83. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
  84. tree_sitter_analyzer/mcp/server.py +869 -0
  85. tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
  86. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
  87. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
  88. tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
  89. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
  90. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
  91. tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
  92. tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
  93. tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
  94. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
  95. tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
  96. tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
  97. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
  98. tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
  99. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
  100. tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
  101. tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
  102. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
  103. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
  104. tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
  105. tree_sitter_analyzer/models.py +840 -0
  106. tree_sitter_analyzer/mypy_current_errors.txt +2 -0
  107. tree_sitter_analyzer/output_manager.py +255 -0
  108. tree_sitter_analyzer/platform_compat/__init__.py +3 -0
  109. tree_sitter_analyzer/platform_compat/adapter.py +324 -0
  110. tree_sitter_analyzer/platform_compat/compare.py +224 -0
  111. tree_sitter_analyzer/platform_compat/detector.py +67 -0
  112. tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
  113. tree_sitter_analyzer/platform_compat/profiles.py +217 -0
  114. tree_sitter_analyzer/platform_compat/record.py +55 -0
  115. tree_sitter_analyzer/platform_compat/recorder.py +155 -0
  116. tree_sitter_analyzer/platform_compat/report.py +92 -0
  117. tree_sitter_analyzer/plugins/__init__.py +280 -0
  118. tree_sitter_analyzer/plugins/base.py +647 -0
  119. tree_sitter_analyzer/plugins/manager.py +384 -0
  120. tree_sitter_analyzer/project_detector.py +328 -0
  121. tree_sitter_analyzer/queries/__init__.py +27 -0
  122. tree_sitter_analyzer/queries/csharp.py +216 -0
  123. tree_sitter_analyzer/queries/css.py +615 -0
  124. tree_sitter_analyzer/queries/go.py +275 -0
  125. tree_sitter_analyzer/queries/html.py +543 -0
  126. tree_sitter_analyzer/queries/java.py +402 -0
  127. tree_sitter_analyzer/queries/javascript.py +724 -0
  128. tree_sitter_analyzer/queries/kotlin.py +192 -0
  129. tree_sitter_analyzer/queries/markdown.py +258 -0
  130. tree_sitter_analyzer/queries/php.py +95 -0
  131. tree_sitter_analyzer/queries/python.py +859 -0
  132. tree_sitter_analyzer/queries/ruby.py +92 -0
  133. tree_sitter_analyzer/queries/rust.py +223 -0
  134. tree_sitter_analyzer/queries/sql.py +555 -0
  135. tree_sitter_analyzer/queries/typescript.py +871 -0
  136. tree_sitter_analyzer/queries/yaml.py +236 -0
  137. tree_sitter_analyzer/query_loader.py +272 -0
  138. tree_sitter_analyzer/security/__init__.py +22 -0
  139. tree_sitter_analyzer/security/boundary_manager.py +277 -0
  140. tree_sitter_analyzer/security/regex_checker.py +297 -0
  141. tree_sitter_analyzer/security/validator.py +599 -0
  142. tree_sitter_analyzer/table_formatter.py +782 -0
  143. tree_sitter_analyzer/utils/__init__.py +53 -0
  144. tree_sitter_analyzer/utils/logging.py +433 -0
  145. tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
  146. tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
  147. tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
  148. tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
  149. tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ File Handler Module
4
+
5
+ This module provides file reading functionality with encoding detection and fallback.
6
+ """
7
+
8
+ import itertools
9
+ from pathlib import Path
10
+
11
+ from .encoding_utils import read_file_safe, read_file_safe_streaming
12
+ from .utils import setup_logger
13
+
14
+ # Set up logger for this module
15
+ logger = setup_logger(__name__)
16
+
17
+
18
+ def log_error(message: str, *args: object, **kwargs: object) -> None:
19
+ """Log error message"""
20
+ logger.error(message, *args, **kwargs) # type: ignore[arg-type]
21
+
22
+
23
+ def log_info(message: str, *args: object, **kwargs: object) -> None:
24
+ """Log info message"""
25
+ logger.info(message, *args, **kwargs) # type: ignore[arg-type]
26
+
27
+
28
+ def log_warning(message: str, *args: object, **kwargs: object) -> None:
29
+ """Log warning message"""
30
+ logger.warning(message, *args, **kwargs) # type: ignore[arg-type]
31
+
32
+
33
+ def detect_language_from_extension(file_path: str) -> str:
34
+ """
35
+ Detect programming language from file extension
36
+
37
+ Args:
38
+ file_path: File path to analyze
39
+
40
+ Returns:
41
+ Language name or 'unknown' if not recognized
42
+ """
43
+ extension = Path(file_path).suffix.lower()
44
+
45
+ extension_map = {
46
+ ".java": "java",
47
+ ".py": "python",
48
+ ".js": "javascript",
49
+ ".ts": "typescript",
50
+ ".cpp": "cpp",
51
+ ".c": "c",
52
+ ".h": "c",
53
+ ".hpp": "cpp",
54
+ ".cs": "csharp",
55
+ ".go": "go",
56
+ ".rs": "rust",
57
+ ".rb": "ruby",
58
+ ".php": "php",
59
+ ".kt": "kotlin",
60
+ ".scala": "scala",
61
+ ".swift": "swift",
62
+ }
63
+
64
+ return extension_map.get(extension, "unknown")
65
+
66
+
67
+ def read_file_with_fallback(file_path: str) -> bytes | None:
68
+ """
69
+ Read file with encoding fallback using unified encoding utilities
70
+
71
+ Args:
72
+ file_path: Path to the file to read
73
+
74
+ Returns:
75
+ File content as bytes, or None if file doesn't exist
76
+ """
77
+ # Check file existence first
78
+ file_obj = Path(file_path)
79
+ if not file_obj.exists():
80
+ log_error(f"File does not exist: {file_path}")
81
+ return None
82
+
83
+ try:
84
+ content, detected_encoding = read_file_safe(file_path)
85
+ log_info(
86
+ f"Successfully read file {file_path} with encoding: {detected_encoding}"
87
+ )
88
+ return content.encode("utf-8")
89
+
90
+ except Exception as e:
91
+ log_error(f"Failed to read file {file_path}: {e}")
92
+ return None
93
+
94
+
95
+ def read_file_partial(
96
+ file_path: str,
97
+ start_line: int,
98
+ end_line: int | None = None,
99
+ start_column: int | None = None,
100
+ end_column: int | None = None,
101
+ ) -> str | None:
102
+ """
103
+ Read partial file content by line/column range using streaming for memory efficiency.
104
+
105
+ Performance: Uses streaming approach for 150x speedup on large files.
106
+ Only loads requested lines into memory instead of entire file.
107
+
108
+ Args:
109
+ file_path: Path to file
110
+ start_line: Start line (1-based)
111
+ end_line: End line (1-based, None means EOF)
112
+ start_column: Start column (0-based, optional)
113
+ end_column: End column (0-based, optional)
114
+
115
+ Returns:
116
+ Selected content string, or None on error
117
+ """
118
+ # Check file existence
119
+ file_obj = Path(file_path)
120
+ if not file_obj.exists():
121
+ log_error(f"File does not exist: {file_path}")
122
+ return None
123
+
124
+ # Parameter validation
125
+ if start_line < 1:
126
+ log_error(f"Invalid start_line: {start_line}. Line numbers start from 1.")
127
+ return None
128
+
129
+ if end_line is not None and end_line < start_line:
130
+ log_error(f"Invalid range: end_line ({end_line}) < start_line ({start_line})")
131
+ return None
132
+
133
+ try:
134
+ # Use streaming approach for memory efficiency
135
+ with read_file_safe_streaming(file_path) as f:
136
+ # Convert to 0-based indexing
137
+ start_idx = start_line - 1
138
+ end_idx = end_line - 1 if end_line is not None else None
139
+
140
+ # Use itertools.islice for efficient line selection
141
+ if end_idx is not None:
142
+ # Read specific range
143
+ selected_lines_iter = itertools.islice(f, start_idx, end_idx + 1)
144
+ else:
145
+ # Read from start_line to end of file
146
+ selected_lines_iter = itertools.islice(f, start_idx, None)
147
+
148
+ # Convert iterator to list for processing
149
+ selected_lines = list(selected_lines_iter)
150
+
151
+ # Check if we got any lines
152
+ if not selected_lines:
153
+ # Check if start_line is beyond file length by counting lines
154
+ with read_file_safe_streaming(file_path) as f_count:
155
+ total_lines = sum(1 for _ in f_count)
156
+
157
+ if start_idx >= total_lines:
158
+ log_warning(
159
+ f"start_line ({start_line}) exceeds file length ({total_lines})"
160
+ )
161
+ return ""
162
+ else:
163
+ # File might be empty or other issue
164
+ return ""
165
+
166
+ # Handle column range if specified
167
+ if start_column is not None or end_column is not None:
168
+ processed_lines = []
169
+ for i, line in enumerate(selected_lines):
170
+ # Strip newline for processing
171
+ line_content = line.rstrip("\r\n")
172
+
173
+ if i == 0 and start_column is not None:
174
+ # First line: apply start_column
175
+ line_content = (
176
+ line_content[start_column:]
177
+ if start_column < len(line_content)
178
+ else ""
179
+ )
180
+
181
+ if i == len(selected_lines) - 1 and end_column is not None:
182
+ # Last line: apply end_column
183
+ if i == 0 and start_column is not None:
184
+ # Single line: apply both start and end columns
185
+ col_end = (
186
+ end_column - start_column
187
+ if end_column >= start_column
188
+ else 0
189
+ )
190
+ line_content = line_content[:col_end] if col_end > 0 else ""
191
+ else:
192
+ line_content = (
193
+ line_content[:end_column]
194
+ if end_column < len(line_content)
195
+ else line_content
196
+ )
197
+
198
+ # Preserve original newline (except last line)
199
+ if i < len(selected_lines) - 1:
200
+ # Detect original newline char of the line
201
+ original_line = selected_lines[i]
202
+ if original_line.endswith("\r\n"):
203
+ line_content += "\r\n"
204
+ elif original_line.endswith("\n"):
205
+ line_content += "\n"
206
+ elif original_line.endswith("\r"):
207
+ line_content += "\r"
208
+
209
+ processed_lines.append(line_content)
210
+
211
+ result = "".join(processed_lines)
212
+ else:
213
+ # No column range: join lines directly
214
+ result = "".join(selected_lines)
215
+
216
+ # Calculate end line for logging
217
+ actual_end_line = end_line or (start_line + len(selected_lines) - 1)
218
+
219
+ log_info(
220
+ f"Successfully read partial file {file_path}: "
221
+ f"lines {start_line}-{actual_end_line}"
222
+ f"{f', columns {start_column}-{end_column}' if start_column is not None or end_column is not None else ''}"
223
+ )
224
+
225
+ return result
226
+
227
+ except Exception as e:
228
+ log_error(f"Failed to read partial file {file_path}: {e}")
229
+ return None
230
+
231
+
232
+ def read_file_lines_range(
233
+ file_path: str, start_line: int, end_line: int | None = None
234
+ ) -> str | None:
235
+ """
236
+ 指定した行番号範囲でファイルの一部を読み込み(列指定なし)
237
+
238
+ Args:
239
+ file_path: 読み込むファイルのパス
240
+ start_line: 開始行番号(1ベース)
241
+ end_line: 終了行番号(Noneの場合はファイル末尾まで、1ベース)
242
+
243
+ Returns:
244
+ 指定範囲のファイル内容(文字列)、エラーの場合はNone
245
+ """
246
+ return read_file_partial(file_path, start_line, end_line)
@@ -0,0 +1 @@
1
+ # Language-specific formatters
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Base formatter for language-specific formatting.
4
+ """
5
+
6
+ import csv
7
+ import io
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any
10
+
11
+
12
+ class BaseFormatter(ABC):
13
+ """Base class for language-specific formatters"""
14
+
15
+ @abstractmethod
16
+ def __init__(self) -> None:
17
+ pass
18
+
19
+ @abstractmethod
20
+ def format_summary(self, analysis_result: dict[str, Any]) -> str:
21
+ """Format summary output"""
22
+ pass
23
+
24
+ @abstractmethod
25
+ def format_structure(self, analysis_result: dict[str, Any]) -> str:
26
+ """Format structure analysis output"""
27
+ pass
28
+
29
+ @abstractmethod
30
+ def format_advanced(
31
+ self, analysis_result: dict[str, Any], output_format: str = "json"
32
+ ) -> str:
33
+ """Format advanced analysis output"""
34
+ pass
35
+
36
+ @abstractmethod
37
+ def format_table(
38
+ self, analysis_result: dict[str, Any], table_type: str = "full"
39
+ ) -> str:
40
+ """Format table output"""
41
+ pass
42
+
43
+
44
+ class BaseTableFormatter(ABC):
45
+ """Base class for language-specific table formatters"""
46
+
47
+ def __init__(self, format_type: str = "full"):
48
+ self.format_type = format_type
49
+
50
+ def _get_platform_newline(self) -> str:
51
+ """Get platform-specific newline code"""
52
+ import os
53
+
54
+ return "\r\n" if os.name == "nt" else "\n" # Windows uses \r\n, others use \n
55
+
56
+ def _convert_to_platform_newlines(self, text: str) -> str:
57
+ """Convert regular \n to platform-specific newline code"""
58
+ platform_newline = self._get_platform_newline()
59
+ if platform_newline != "\n":
60
+ return text.replace("\n", platform_newline)
61
+ return text
62
+
63
+ def format_structure(self, structure_data: dict[str, Any]) -> str:
64
+ """Format structure data in table format"""
65
+ if self.format_type == "full":
66
+ result = self._format_full_table(structure_data)
67
+ elif self.format_type == "compact":
68
+ result = self._format_compact_table(structure_data)
69
+ elif self.format_type == "csv":
70
+ result = self._format_csv(structure_data)
71
+ else:
72
+ raise ValueError(f"Unsupported format type: {self.format_type}")
73
+
74
+ # Finally convert to platform-specific newline code
75
+ if self.format_type == "csv":
76
+ return result
77
+
78
+ return self._convert_to_platform_newlines(result)
79
+
80
+ @abstractmethod
81
+ def _format_full_table(self, data: dict[str, Any]) -> str:
82
+ """Full table format (language-specific implementation)"""
83
+ pass
84
+
85
+ @abstractmethod
86
+ def _format_compact_table(self, data: dict[str, Any]) -> str:
87
+ """Compact table format (language-specific implementation)"""
88
+ pass
89
+
90
+ def _format_csv(self, data: dict[str, Any]) -> str:
91
+ """CSV format (common implementation)"""
92
+ output = io.StringIO()
93
+ writer = csv.writer(output, lineterminator="\n")
94
+
95
+ # Header
96
+ writer.writerow(
97
+ ["Type", "Name", "Signature", "Visibility", "Lines", "Complexity", "Doc"]
98
+ )
99
+
100
+ # Fields
101
+ for field in data.get("fields", []):
102
+ writer.writerow(
103
+ [
104
+ "Field",
105
+ str(field.get("name", "")),
106
+ f"{str(field.get('name', ''))}:{str(field.get('type', ''))}",
107
+ str(field.get("visibility", "")),
108
+ f"{field.get('line_range', {}).get('start', 0)}-{field.get('line_range', {}).get('end', 0)}",
109
+ "",
110
+ self._clean_csv_text(
111
+ self._extract_doc_summary(str(field.get("javadoc", "")))
112
+ ),
113
+ ]
114
+ )
115
+
116
+ # Methods
117
+ for method in data.get("methods", []):
118
+ writer.writerow(
119
+ [
120
+ "Constructor" if method.get("is_constructor", False) else "Method",
121
+ str(method.get("name", "")),
122
+ self._clean_csv_text(self._create_full_signature(method)),
123
+ str(method.get("visibility", "")),
124
+ f"{method.get('line_range', {}).get('start', 0)}-{method.get('line_range', {}).get('end', 0)}",
125
+ method.get("complexity_score", 0),
126
+ self._clean_csv_text(
127
+ self._extract_doc_summary(str(method.get("javadoc", "")))
128
+ ),
129
+ ]
130
+ )
131
+
132
+ csv_content = output.getvalue()
133
+ csv_content = csv_content.replace("\r\n", "\n").replace("\r", "\n")
134
+ csv_content = csv_content.rstrip("\n")
135
+ output.close()
136
+
137
+ return csv_content
138
+
139
+ # Common helper methods
140
+ def _create_full_signature(self, method: dict[str, Any]) -> str:
141
+ """Create complete method signature"""
142
+ params = method.get("parameters", [])
143
+ param_strs = []
144
+ for param in params:
145
+ if isinstance(param, dict):
146
+ param_type = str(param.get("type", "Object"))
147
+ param_name = str(param.get("name", "param"))
148
+ param_strs.append(f"{param_name}:{param_type}")
149
+ else:
150
+ param_strs.append(str(param))
151
+
152
+ params_str = ", ".join(param_strs)
153
+ return_type = str(method.get("return_type", "void"))
154
+
155
+ modifiers = []
156
+ if method.get("is_static", False):
157
+ modifiers.append("[static]")
158
+
159
+ modifier_str = " ".join(modifiers)
160
+ signature = f"({params_str}):{return_type}"
161
+
162
+ if modifier_str:
163
+ signature += f" {modifier_str}"
164
+
165
+ return signature
166
+
167
+ def _convert_visibility(self, visibility: str) -> str:
168
+ """Convert visibility to symbol"""
169
+ mapping = {"public": "+", "private": "-", "protected": "#", "package": "~"}
170
+ return mapping.get(visibility, visibility)
171
+
172
+ def _extract_doc_summary(self, javadoc: str) -> str:
173
+ """Extract summary from documentation"""
174
+ if not javadoc:
175
+ return "-"
176
+
177
+ # Remove comment symbols
178
+ clean_doc = (
179
+ javadoc.replace("/**", "").replace("*/", "").replace("*", "").strip()
180
+ )
181
+
182
+ # Get first line
183
+ lines = clean_doc.split("\n")
184
+ first_line = lines[0].strip()
185
+
186
+ # Truncate if too long
187
+ if len(first_line) > 50:
188
+ first_line = first_line[:47] + "..."
189
+
190
+ return first_line.replace("|", "\\|").replace("\n", " ")
191
+
192
+ def _clean_csv_text(self, text: str) -> str:
193
+ """Text cleaning for CSV format"""
194
+ if not text:
195
+ return ""
196
+
197
+ cleaned = text.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
198
+ cleaned = " ".join(cleaned.split())
199
+ cleaned = cleaned.replace('"', '""')
200
+
201
+ return cleaned