codexa 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. codexa-0.4.0.dist-info/METADATA +650 -0
  2. codexa-0.4.0.dist-info/RECORD +189 -0
  3. codexa-0.4.0.dist-info/WHEEL +5 -0
  4. codexa-0.4.0.dist-info/entry_points.txt +2 -0
  5. codexa-0.4.0.dist-info/licenses/LICENSE +21 -0
  6. codexa-0.4.0.dist-info/top_level.txt +1 -0
  7. semantic_code_intelligence/__init__.py +5 -0
  8. semantic_code_intelligence/analysis/__init__.py +21 -0
  9. semantic_code_intelligence/analysis/ai_features.py +351 -0
  10. semantic_code_intelligence/bridge/__init__.py +28 -0
  11. semantic_code_intelligence/bridge/context_provider.py +245 -0
  12. semantic_code_intelligence/bridge/protocol.py +167 -0
  13. semantic_code_intelligence/bridge/server.py +348 -0
  14. semantic_code_intelligence/bridge/vscode.py +271 -0
  15. semantic_code_intelligence/ci/__init__.py +13 -0
  16. semantic_code_intelligence/ci/hooks.py +98 -0
  17. semantic_code_intelligence/ci/hotspots.py +272 -0
  18. semantic_code_intelligence/ci/impact.py +246 -0
  19. semantic_code_intelligence/ci/metrics.py +591 -0
  20. semantic_code_intelligence/ci/pr.py +412 -0
  21. semantic_code_intelligence/ci/quality.py +557 -0
  22. semantic_code_intelligence/ci/templates.py +164 -0
  23. semantic_code_intelligence/ci/trace.py +224 -0
  24. semantic_code_intelligence/cli/__init__.py +0 -0
  25. semantic_code_intelligence/cli/commands/__init__.py +0 -0
  26. semantic_code_intelligence/cli/commands/ask_cmd.py +153 -0
  27. semantic_code_intelligence/cli/commands/benchmark_cmd.py +303 -0
  28. semantic_code_intelligence/cli/commands/chat_cmd.py +252 -0
  29. semantic_code_intelligence/cli/commands/ci_gen_cmd.py +74 -0
  30. semantic_code_intelligence/cli/commands/context_cmd.py +120 -0
  31. semantic_code_intelligence/cli/commands/cross_refactor_cmd.py +113 -0
  32. semantic_code_intelligence/cli/commands/deps_cmd.py +91 -0
  33. semantic_code_intelligence/cli/commands/docs_cmd.py +101 -0
  34. semantic_code_intelligence/cli/commands/doctor_cmd.py +147 -0
  35. semantic_code_intelligence/cli/commands/evolve_cmd.py +171 -0
  36. semantic_code_intelligence/cli/commands/explain_cmd.py +112 -0
  37. semantic_code_intelligence/cli/commands/gate_cmd.py +135 -0
  38. semantic_code_intelligence/cli/commands/grep_cmd.py +234 -0
  39. semantic_code_intelligence/cli/commands/hotspots_cmd.py +119 -0
  40. semantic_code_intelligence/cli/commands/impact_cmd.py +131 -0
  41. semantic_code_intelligence/cli/commands/index_cmd.py +138 -0
  42. semantic_code_intelligence/cli/commands/init_cmd.py +152 -0
  43. semantic_code_intelligence/cli/commands/investigate_cmd.py +163 -0
  44. semantic_code_intelligence/cli/commands/languages_cmd.py +101 -0
  45. semantic_code_intelligence/cli/commands/lsp_cmd.py +49 -0
  46. semantic_code_intelligence/cli/commands/mcp_cmd.py +50 -0
  47. semantic_code_intelligence/cli/commands/metrics_cmd.py +264 -0
  48. semantic_code_intelligence/cli/commands/models_cmd.py +157 -0
  49. semantic_code_intelligence/cli/commands/plugin_cmd.py +275 -0
  50. semantic_code_intelligence/cli/commands/pr_summary_cmd.py +178 -0
  51. semantic_code_intelligence/cli/commands/quality_cmd.py +208 -0
  52. semantic_code_intelligence/cli/commands/refactor_cmd.py +103 -0
  53. semantic_code_intelligence/cli/commands/review_cmd.py +88 -0
  54. semantic_code_intelligence/cli/commands/search_cmd.py +236 -0
  55. semantic_code_intelligence/cli/commands/serve_cmd.py +117 -0
  56. semantic_code_intelligence/cli/commands/suggest_cmd.py +100 -0
  57. semantic_code_intelligence/cli/commands/summary_cmd.py +78 -0
  58. semantic_code_intelligence/cli/commands/tool_cmd.py +282 -0
  59. semantic_code_intelligence/cli/commands/trace_cmd.py +123 -0
  60. semantic_code_intelligence/cli/commands/tui_cmd.py +58 -0
  61. semantic_code_intelligence/cli/commands/viz_cmd.py +127 -0
  62. semantic_code_intelligence/cli/commands/watch_cmd.py +72 -0
  63. semantic_code_intelligence/cli/commands/web_cmd.py +61 -0
  64. semantic_code_intelligence/cli/commands/workspace_cmd.py +250 -0
  65. semantic_code_intelligence/cli/main.py +65 -0
  66. semantic_code_intelligence/cli/router.py +92 -0
  67. semantic_code_intelligence/config/__init__.py +0 -0
  68. semantic_code_intelligence/config/settings.py +260 -0
  69. semantic_code_intelligence/context/__init__.py +19 -0
  70. semantic_code_intelligence/context/engine.py +429 -0
  71. semantic_code_intelligence/context/memory.py +253 -0
  72. semantic_code_intelligence/daemon/__init__.py +1 -0
  73. semantic_code_intelligence/daemon/watcher.py +515 -0
  74. semantic_code_intelligence/docs/__init__.py +1080 -0
  75. semantic_code_intelligence/embeddings/__init__.py +0 -0
  76. semantic_code_intelligence/embeddings/enhanced.py +131 -0
  77. semantic_code_intelligence/embeddings/generator.py +149 -0
  78. semantic_code_intelligence/embeddings/model_registry.py +100 -0
  79. semantic_code_intelligence/evolution/__init__.py +1 -0
  80. semantic_code_intelligence/evolution/budget_guard.py +111 -0
  81. semantic_code_intelligence/evolution/commit_manager.py +88 -0
  82. semantic_code_intelligence/evolution/context_builder.py +131 -0
  83. semantic_code_intelligence/evolution/engine.py +249 -0
  84. semantic_code_intelligence/evolution/patch_generator.py +229 -0
  85. semantic_code_intelligence/evolution/task_selector.py +214 -0
  86. semantic_code_intelligence/evolution/test_runner.py +111 -0
  87. semantic_code_intelligence/indexing/__init__.py +0 -0
  88. semantic_code_intelligence/indexing/chunker.py +174 -0
  89. semantic_code_intelligence/indexing/parallel.py +86 -0
  90. semantic_code_intelligence/indexing/scanner.py +146 -0
  91. semantic_code_intelligence/indexing/semantic_chunker.py +337 -0
  92. semantic_code_intelligence/llm/__init__.py +62 -0
  93. semantic_code_intelligence/llm/cache.py +219 -0
  94. semantic_code_intelligence/llm/cached_provider.py +145 -0
  95. semantic_code_intelligence/llm/conversation.py +190 -0
  96. semantic_code_intelligence/llm/cross_refactor.py +272 -0
  97. semantic_code_intelligence/llm/investigation.py +274 -0
  98. semantic_code_intelligence/llm/mock_provider.py +77 -0
  99. semantic_code_intelligence/llm/ollama_provider.py +122 -0
  100. semantic_code_intelligence/llm/openai_provider.py +100 -0
  101. semantic_code_intelligence/llm/provider.py +92 -0
  102. semantic_code_intelligence/llm/rate_limiter.py +164 -0
  103. semantic_code_intelligence/llm/reasoning.py +438 -0
  104. semantic_code_intelligence/llm/safety.py +110 -0
  105. semantic_code_intelligence/llm/streaming.py +251 -0
  106. semantic_code_intelligence/lsp/__init__.py +609 -0
  107. semantic_code_intelligence/mcp/__init__.py +393 -0
  108. semantic_code_intelligence/parsing/__init__.py +19 -0
  109. semantic_code_intelligence/parsing/parser.py +375 -0
  110. semantic_code_intelligence/plugins/__init__.py +255 -0
  111. semantic_code_intelligence/plugins/examples/__init__.py +1 -0
  112. semantic_code_intelligence/plugins/examples/code_quality.py +73 -0
  113. semantic_code_intelligence/plugins/examples/search_annotator.py +56 -0
  114. semantic_code_intelligence/scalability/__init__.py +205 -0
  115. semantic_code_intelligence/search/__init__.py +0 -0
  116. semantic_code_intelligence/search/formatter.py +123 -0
  117. semantic_code_intelligence/search/grep.py +361 -0
  118. semantic_code_intelligence/search/hybrid_search.py +170 -0
  119. semantic_code_intelligence/search/keyword_search.py +311 -0
  120. semantic_code_intelligence/search/section_expander.py +103 -0
  121. semantic_code_intelligence/services/__init__.py +0 -0
  122. semantic_code_intelligence/services/indexing_service.py +630 -0
  123. semantic_code_intelligence/services/search_service.py +269 -0
  124. semantic_code_intelligence/storage/__init__.py +0 -0
  125. semantic_code_intelligence/storage/chunk_hash_store.py +86 -0
  126. semantic_code_intelligence/storage/hash_store.py +66 -0
  127. semantic_code_intelligence/storage/index_manifest.py +85 -0
  128. semantic_code_intelligence/storage/index_stats.py +138 -0
  129. semantic_code_intelligence/storage/query_history.py +160 -0
  130. semantic_code_intelligence/storage/symbol_registry.py +209 -0
  131. semantic_code_intelligence/storage/vector_store.py +297 -0
  132. semantic_code_intelligence/tests/__init__.py +0 -0
  133. semantic_code_intelligence/tests/test_ai_features.py +351 -0
  134. semantic_code_intelligence/tests/test_chunker.py +119 -0
  135. semantic_code_intelligence/tests/test_cli.py +188 -0
  136. semantic_code_intelligence/tests/test_config.py +154 -0
  137. semantic_code_intelligence/tests/test_context.py +381 -0
  138. semantic_code_intelligence/tests/test_embeddings.py +73 -0
  139. semantic_code_intelligence/tests/test_endtoend.py +1142 -0
  140. semantic_code_intelligence/tests/test_enhanced_embeddings.py +92 -0
  141. semantic_code_intelligence/tests/test_hash_store.py +79 -0
  142. semantic_code_intelligence/tests/test_logging.py +55 -0
  143. semantic_code_intelligence/tests/test_new_cli.py +138 -0
  144. semantic_code_intelligence/tests/test_parser.py +495 -0
  145. semantic_code_intelligence/tests/test_phase10.py +355 -0
  146. semantic_code_intelligence/tests/test_phase11.py +593 -0
  147. semantic_code_intelligence/tests/test_phase12.py +375 -0
  148. semantic_code_intelligence/tests/test_phase13.py +663 -0
  149. semantic_code_intelligence/tests/test_phase14.py +568 -0
  150. semantic_code_intelligence/tests/test_phase15.py +814 -0
  151. semantic_code_intelligence/tests/test_phase16.py +792 -0
  152. semantic_code_intelligence/tests/test_phase17.py +815 -0
  153. semantic_code_intelligence/tests/test_phase18.py +934 -0
  154. semantic_code_intelligence/tests/test_phase19.py +986 -0
  155. semantic_code_intelligence/tests/test_phase20.py +2753 -0
  156. semantic_code_intelligence/tests/test_phase20b.py +2058 -0
  157. semantic_code_intelligence/tests/test_phase20c.py +962 -0
  158. semantic_code_intelligence/tests/test_phase21.py +428 -0
  159. semantic_code_intelligence/tests/test_phase22.py +799 -0
  160. semantic_code_intelligence/tests/test_phase23.py +783 -0
  161. semantic_code_intelligence/tests/test_phase24.py +715 -0
  162. semantic_code_intelligence/tests/test_phase25.py +496 -0
  163. semantic_code_intelligence/tests/test_phase26.py +251 -0
  164. semantic_code_intelligence/tests/test_phase27.py +531 -0
  165. semantic_code_intelligence/tests/test_phase8.py +592 -0
  166. semantic_code_intelligence/tests/test_phase9.py +643 -0
  167. semantic_code_intelligence/tests/test_plugins.py +293 -0
  168. semantic_code_intelligence/tests/test_priority_features.py +727 -0
  169. semantic_code_intelligence/tests/test_router.py +41 -0
  170. semantic_code_intelligence/tests/test_scalability.py +138 -0
  171. semantic_code_intelligence/tests/test_scanner.py +125 -0
  172. semantic_code_intelligence/tests/test_search.py +160 -0
  173. semantic_code_intelligence/tests/test_semantic_chunker.py +255 -0
  174. semantic_code_intelligence/tests/test_tools.py +182 -0
  175. semantic_code_intelligence/tests/test_vector_store.py +151 -0
  176. semantic_code_intelligence/tests/test_watcher.py +211 -0
  177. semantic_code_intelligence/tools/__init__.py +442 -0
  178. semantic_code_intelligence/tools/executor.py +232 -0
  179. semantic_code_intelligence/tools/protocol.py +200 -0
  180. semantic_code_intelligence/tui/__init__.py +454 -0
  181. semantic_code_intelligence/utils/__init__.py +0 -0
  182. semantic_code_intelligence/utils/logging.py +112 -0
  183. semantic_code_intelligence/version.py +3 -0
  184. semantic_code_intelligence/web/__init__.py +11 -0
  185. semantic_code_intelligence/web/api.py +289 -0
  186. semantic_code_intelligence/web/server.py +397 -0
  187. semantic_code_intelligence/web/ui.py +659 -0
  188. semantic_code_intelligence/web/visualize.py +226 -0
  189. semantic_code_intelligence/workspace/__init__.py +427 -0
@@ -0,0 +1,593 @@
1
+ """Tests for Phase 11 — Multi-Language Parsing Expansion.
2
+
3
+ Covers: language detection, tree-sitter grammar loading, and symbol
4
+ extraction for TypeScript, TSX, C++, C#, Ruby, and PHP.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import pytest
10
+
11
+ from semantic_code_intelligence.parsing.parser import (
12
+ EXTENSION_TO_LANGUAGE,
13
+ FUNCTION_NODE_TYPES,
14
+ CLASS_NODE_TYPES,
15
+ IMPORT_NODE_TYPES,
16
+ detect_language,
17
+ get_language,
18
+ parse_file,
19
+ )
20
+
21
+
22
+ # =========================================================================
23
+ # Language detection tests
24
+ # =========================================================================
25
+
26
+
27
+ class TestNewLanguageDetection:
28
+ """Verify file extensions map to the correct language names."""
29
+
30
+ def test_typescript(self):
31
+ assert detect_language("app.ts") == "typescript"
32
+
33
+ def test_tsx(self):
34
+ assert detect_language("Component.tsx") == "tsx"
35
+
36
+ def test_cpp_extensions(self):
37
+ assert detect_language("main.cpp") == "cpp"
38
+ assert detect_language("util.cc") == "cpp"
39
+ assert detect_language("header.hpp") == "cpp"
40
+ assert detect_language("types.h") == "cpp"
41
+
42
+ def test_csharp(self):
43
+ assert detect_language("Program.cs") == "csharp"
44
+
45
+ def test_ruby(self):
46
+ assert detect_language("app.rb") == "ruby"
47
+
48
+ def test_php(self):
49
+ assert detect_language("index.php") == "php"
50
+
51
+ def test_case_insensitive(self):
52
+ assert detect_language("APP.TS") == "typescript"
53
+ assert detect_language("MAIN.CPP") == "cpp"
54
+ assert detect_language("PROG.CS") == "csharp"
55
+ assert detect_language("APP.RB") == "ruby"
56
+ assert detect_language("PAGE.PHP") == "php"
57
+
58
+
59
+ # =========================================================================
60
+ # Grammar loading tests
61
+ # =========================================================================
62
+
63
+
64
+ class TestGrammarLoading:
65
+ """Verify tree-sitter grammars load without errors."""
66
+
67
+ @pytest.mark.parametrize("lang", [
68
+ "typescript", "tsx", "cpp", "csharp", "ruby", "php",
69
+ ])
70
+ def test_load_language(self, lang):
71
+ result = get_language(lang)
72
+ assert result is not None, f"Failed to load grammar for {lang}"
73
+
74
+ def test_unknown_language_returns_none(self):
75
+ assert get_language("brainfuck") is None
76
+
77
+
78
+ # =========================================================================
79
+ # Node type mapping coverage
80
+ # =========================================================================
81
+
82
+
83
+ class TestNodeTypeMappings:
84
+ """Ensure all new languages have entries in every mapping dict."""
85
+
86
+ @pytest.mark.parametrize("lang", [
87
+ "typescript", "tsx", "cpp", "csharp", "ruby", "php",
88
+ ])
89
+ def test_function_types(self, lang):
90
+ assert lang in FUNCTION_NODE_TYPES
91
+ assert len(FUNCTION_NODE_TYPES[lang]) > 0
92
+
93
+ @pytest.mark.parametrize("lang", [
94
+ "typescript", "tsx", "cpp", "csharp", "ruby", "php",
95
+ ])
96
+ def test_class_types(self, lang):
97
+ assert lang in CLASS_NODE_TYPES
98
+ assert len(CLASS_NODE_TYPES[lang]) > 0
99
+
100
+ @pytest.mark.parametrize("lang", [
101
+ "typescript", "tsx", "cpp", "csharp", "ruby", "php",
102
+ ])
103
+ def test_import_types(self, lang):
104
+ assert lang in IMPORT_NODE_TYPES
105
+ assert len(IMPORT_NODE_TYPES[lang]) > 0
106
+
107
+
108
+ # =========================================================================
109
+ # TypeScript parsing
110
+ # =========================================================================
111
+
112
+ TYPESCRIPT_CODE = """\
113
+ import { useState } from 'react';
114
+
115
+ interface Config {
116
+ host: string;
117
+ port: number;
118
+ }
119
+
120
+ class Server {
121
+ constructor(private config: Config) {}
122
+
123
+ start(): void {
124
+ console.log('starting');
125
+ }
126
+ }
127
+
128
+ function createServer(config: Config): Server {
129
+ return new Server(config);
130
+ }
131
+
132
+ const helper = (x: number): number => x * 2;
133
+ """
134
+
135
+
136
+ class TestTypeScriptParsing:
137
+ @pytest.fixture(autouse=True)
138
+ def setup(self):
139
+ self.symbols = parse_file("server.ts", TYPESCRIPT_CODE)
140
+ self.kinds = {s.kind for s in self.symbols}
141
+ self.names = {s.name for s in self.symbols}
142
+
143
+ def test_detects_import(self):
144
+ imports = [s for s in self.symbols if s.kind == "import"]
145
+ assert len(imports) >= 1
146
+
147
+ def test_detects_interface(self):
148
+ classes = [s for s in self.symbols if s.kind == "class"]
149
+ class_names = {s.name for s in classes}
150
+ assert "Config" in class_names
151
+
152
+ def test_detects_class(self):
153
+ classes = [s for s in self.symbols if s.kind == "class"]
154
+ class_names = {s.name for s in classes}
155
+ assert "Server" in class_names
156
+
157
+ def test_detects_function(self):
158
+ fns = [s for s in self.symbols if s.kind == "function"]
159
+ fn_names = {s.name for s in fns}
160
+ assert "createServer" in fn_names
161
+
162
+ def test_detects_method(self):
163
+ methods = [s for s in self.symbols if s.kind == "method"]
164
+ method_names = {s.name for s in methods}
165
+ assert "start" in method_names
166
+
167
+
168
+ # =========================================================================
169
+ # TSX parsing
170
+ # =========================================================================
171
+
172
+ TSX_CODE = """\
173
+ import React from 'react';
174
+
175
+ interface Props {
176
+ title: string;
177
+ }
178
+
179
+ class Header extends React.Component<Props> {
180
+ render() {
181
+ return <h1>{this.props.title}</h1>;
182
+ }
183
+ }
184
+
185
+ function App(): JSX.Element {
186
+ return <Header title="Hello" />;
187
+ }
188
+ """
189
+
190
+
191
+ class TestTSXParsing:
192
+ @pytest.fixture(autouse=True)
193
+ def setup(self):
194
+ self.symbols = parse_file("App.tsx", TSX_CODE)
195
+
196
+ def test_has_symbols(self):
197
+ assert len(self.symbols) > 0
198
+
199
+ def test_detects_class(self):
200
+ classes = [s for s in self.symbols if s.kind == "class"]
201
+ assert any(s.name == "Header" for s in classes)
202
+
203
+ def test_detects_function(self):
204
+ fns = [s for s in self.symbols if s.kind == "function"]
205
+ assert any(s.name == "App" for s in fns)
206
+
207
+
208
+ # =========================================================================
209
+ # C++ parsing
210
+ # =========================================================================
211
+
212
+ CPP_CODE = """\
213
+ #include <iostream>
214
+ #include <string>
215
+
216
+ class Animal {
217
+ public:
218
+ Animal(std::string name) : name_(name) {}
219
+ virtual void speak() const = 0;
220
+ private:
221
+ std::string name_;
222
+ };
223
+
224
+ struct Point {
225
+ double x, y;
226
+ };
227
+
228
+ void greet(const std::string& name) {
229
+ std::cout << "Hello, " << name << std::endl;
230
+ }
231
+
232
+ int main() {
233
+ greet("world");
234
+ return 0;
235
+ }
236
+ """
237
+
238
+
239
+ class TestCppParsing:
240
+ @pytest.fixture(autouse=True)
241
+ def setup(self):
242
+ self.symbols = parse_file("main.cpp", CPP_CODE)
243
+
244
+ def test_has_symbols(self):
245
+ assert len(self.symbols) > 0
246
+
247
+ def test_detects_include(self):
248
+ imports = [s for s in self.symbols if s.kind == "import"]
249
+ assert len(imports) >= 2
250
+
251
+ def test_detects_class(self):
252
+ classes = [s for s in self.symbols if s.kind == "class"]
253
+ class_names = {s.name for s in classes}
254
+ assert "Animal" in class_names
255
+
256
+ def test_detects_struct(self):
257
+ classes = [s for s in self.symbols if s.kind == "class"]
258
+ class_names = {s.name for s in classes}
259
+ assert "Point" in class_names
260
+
261
+ def test_detects_function(self):
262
+ fns = [s for s in self.symbols if s.kind == "function"]
263
+ fn_names = {s.name for s in fns}
264
+ assert "greet" in fn_names
265
+ assert "main" in fn_names
266
+
267
+
268
+ # =========================================================================
269
+ # C# parsing
270
+ # =========================================================================
271
+
272
+ CSHARP_CODE = """\
273
+ using System;
274
+ using System.Collections.Generic;
275
+
276
+ namespace MyApp
277
+ {
278
+ interface IGreeter
279
+ {
280
+ void Greet(string name);
281
+ }
282
+
283
+ class Greeter : IGreeter
284
+ {
285
+ public Greeter() { }
286
+
287
+ public void Greet(string name)
288
+ {
289
+ Console.WriteLine($"Hello, {name}!");
290
+ }
291
+
292
+ public static int Add(int a, int b)
293
+ {
294
+ return a + b;
295
+ }
296
+ }
297
+
298
+ enum Color
299
+ {
300
+ Red,
301
+ Green,
302
+ Blue
303
+ }
304
+ }
305
+ """
306
+
307
+
308
+ class TestCSharpParsing:
309
+ @pytest.fixture(autouse=True)
310
+ def setup(self):
311
+ self.symbols = parse_file("Program.cs", CSHARP_CODE)
312
+
313
+ def test_has_symbols(self):
314
+ assert len(self.symbols) > 0
315
+
316
+ def test_detects_using(self):
317
+ imports = [s for s in self.symbols if s.kind == "import"]
318
+ assert len(imports) >= 2
319
+
320
+ def test_detects_interface(self):
321
+ classes = [s for s in self.symbols if s.kind == "class"]
322
+ class_names = {s.name for s in classes}
323
+ assert "IGreeter" in class_names
324
+
325
+ def test_detects_class(self):
326
+ classes = [s for s in self.symbols if s.kind == "class"]
327
+ class_names = {s.name for s in classes}
328
+ assert "Greeter" in class_names
329
+
330
+ def test_detects_enum(self):
331
+ classes = [s for s in self.symbols if s.kind == "class"]
332
+ class_names = {s.name for s in classes}
333
+ assert "Color" in class_names
334
+
335
+ def test_detects_method(self):
336
+ methods = [s for s in self.symbols if s.kind == "method"]
337
+ method_names = {s.name for s in methods}
338
+ assert "Greet" in method_names
339
+
340
+
341
+ # =========================================================================
342
+ # Ruby parsing
343
+ # =========================================================================
344
+
345
+ RUBY_CODE = """\
346
+ require 'json'
347
+ require_relative 'helpers'
348
+
349
+ module Serializable
350
+ def to_json
351
+ JSON.generate(to_h)
352
+ end
353
+ end
354
+
355
+ class User
356
+ include Serializable
357
+
358
+ attr_reader :name, :age
359
+
360
+ def initialize(name, age)
361
+ @name = name
362
+ @age = age
363
+ end
364
+
365
+ def greet
366
+ puts "Hello, #{@name}"
367
+ end
368
+
369
+ def self.create(name, age)
370
+ new(name, age)
371
+ end
372
+
373
+ def to_h
374
+ { name: @name, age: @age }
375
+ end
376
+ end
377
+
378
+ def standalone_function(x)
379
+ x * 2
380
+ end
381
+ """
382
+
383
+
384
+ class TestRubyParsing:
385
+ @pytest.fixture(autouse=True)
386
+ def setup(self):
387
+ self.symbols = parse_file("app.rb", RUBY_CODE)
388
+
389
+ def test_has_symbols(self):
390
+ assert len(self.symbols) > 0
391
+
392
+ def test_detects_require(self):
393
+ imports = [s for s in self.symbols if s.kind == "import"]
394
+ assert len(imports) >= 2
395
+ import_text = " ".join(s.body for s in imports)
396
+ assert "json" in import_text
397
+ assert "helpers" in import_text
398
+
399
+ def test_does_not_treat_include_as_import(self):
400
+ """include is a method call but not require/require_relative."""
401
+ imports = [s for s in self.symbols if s.kind == "import"]
402
+ for imp in imports:
403
+ assert "include" not in imp.name.split()[0]
404
+
405
+ def test_detects_module(self):
406
+ classes = [s for s in self.symbols if s.kind == "class"]
407
+ class_names = {s.name for s in classes}
408
+ assert "Serializable" in class_names
409
+
410
+ def test_detects_class(self):
411
+ classes = [s for s in self.symbols if s.kind == "class"]
412
+ class_names = {s.name for s in classes}
413
+ assert "User" in class_names
414
+
415
+ def test_detects_method(self):
416
+ methods = [s for s in self.symbols if s.kind == "method"]
417
+ method_names = {s.name for s in methods}
418
+ assert "initialize" in method_names
419
+ assert "greet" in method_names
420
+
421
+ def test_detects_standalone_function(self):
422
+ fns = [s for s in self.symbols if s.kind == "function"]
423
+ fn_names = {s.name for s in fns}
424
+ assert "standalone_function" in fn_names
425
+
426
+
427
+ # =========================================================================
428
+ # PHP parsing
429
+ # =========================================================================
430
+
431
+ PHP_CODE = """\
432
+ <?php
433
+
434
+ use App\\Models\\User;
435
+
436
+ interface Cacheable
437
+ {
438
+ public function cacheKey(): string;
439
+ }
440
+
441
+ trait Timestampable
442
+ {
443
+ public function createdAt(): string
444
+ {
445
+ return $this->created_at;
446
+ }
447
+ }
448
+
449
+ class UserService implements Cacheable
450
+ {
451
+ use Timestampable;
452
+
453
+ public function __construct(
454
+ private readonly string $name
455
+ ) {}
456
+
457
+ public function cacheKey(): string
458
+ {
459
+ return "user_" . $this->name;
460
+ }
461
+
462
+ public static function create(string $name): self
463
+ {
464
+ return new self($name);
465
+ }
466
+ }
467
+
468
+ function helper(int $x): int
469
+ {
470
+ return $x * 2;
471
+ }
472
+ """
473
+
474
+
475
+ class TestPHPParsing:
476
+ @pytest.fixture(autouse=True)
477
+ def setup(self):
478
+ self.symbols = parse_file("index.php", PHP_CODE)
479
+
480
+ def test_has_symbols(self):
481
+ assert len(self.symbols) > 0
482
+
483
+ def test_detects_use(self):
484
+ imports = [s for s in self.symbols if s.kind == "import"]
485
+ assert len(imports) >= 1
486
+
487
+ def test_detects_interface(self):
488
+ classes = [s for s in self.symbols if s.kind == "class"]
489
+ class_names = {s.name for s in classes}
490
+ assert "Cacheable" in class_names
491
+
492
+ def test_detects_trait(self):
493
+ classes = [s for s in self.symbols if s.kind == "class"]
494
+ class_names = {s.name for s in classes}
495
+ assert "Timestampable" in class_names
496
+
497
+ def test_detects_class(self):
498
+ classes = [s for s in self.symbols if s.kind == "class"]
499
+ class_names = {s.name for s in classes}
500
+ assert "UserService" in class_names
501
+
502
+ def test_detects_method(self):
503
+ methods = [s for s in self.symbols if s.kind == "method"]
504
+ method_names = {s.name for s in methods}
505
+ assert "cacheKey" in method_names
506
+
507
+ def test_detects_function(self):
508
+ fns = [s for s in self.symbols if s.kind == "function"]
509
+ fn_names = {s.name for s in fns}
510
+ assert "helper" in fn_names
511
+
512
+
513
+ # =========================================================================
514
+ # Semantic chunker integration
515
+ # =========================================================================
516
+
517
+
518
+ class TestSemanticChunkerIntegration:
519
+ """Verify the semantic chunker works with new languages."""
520
+
521
+ @pytest.mark.parametrize("ext,code", [
522
+ ("server.ts", TYPESCRIPT_CODE),
523
+ ("App.tsx", TSX_CODE),
524
+ ("main.cpp", CPP_CODE),
525
+ ("Program.cs", CSHARP_CODE),
526
+ ("app.rb", RUBY_CODE),
527
+ ("index.php", PHP_CODE),
528
+ ])
529
+ def test_semantic_chunking(self, ext, code):
530
+ from semantic_code_intelligence.indexing.semantic_chunker import semantic_chunk_code
531
+ chunks = semantic_chunk_code(code, ext, chunk_size=2048)
532
+ assert len(chunks) > 0
533
+ # At least some chunks should have symbol metadata
534
+ symbol_chunks = [c for c in chunks if c.symbol_name]
535
+ assert len(symbol_chunks) > 0
536
+
537
+
538
+ # =========================================================================
539
+ # Edge cases
540
+ # =========================================================================
541
+
542
+
543
+ class TestEdgeCases:
544
+ def test_empty_typescript_file(self):
545
+ symbols = parse_file("empty.ts", "")
546
+ assert symbols == []
547
+
548
+ def test_empty_cpp_file(self):
549
+ symbols = parse_file("empty.cpp", "")
550
+ assert symbols == []
551
+
552
+ def test_empty_ruby_file(self):
553
+ symbols = parse_file("empty.rb", "")
554
+ assert symbols == []
555
+
556
+ def test_minimal_typescript(self):
557
+ symbols = parse_file("min.ts", "const x = 1;")
558
+ assert isinstance(symbols, list)
559
+
560
+ def test_minimal_cpp(self):
561
+ symbols = parse_file("min.cpp", "int main() { return 0; }")
562
+ fns = [s for s in symbols if s.kind == "function"]
563
+ assert any(s.name == "main" for s in fns)
564
+
565
+ def test_minimal_csharp(self):
566
+ symbols = parse_file("min.cs", "using System;")
567
+ imports = [s for s in symbols if s.kind == "import"]
568
+ assert len(imports) >= 1
569
+
570
+ def test_minimal_ruby(self):
571
+ symbols = parse_file("min.rb", "def hello; end")
572
+ fns = [s for s in symbols if s.kind == "function"]
573
+ assert any(s.name == "hello" for s in fns)
574
+
575
+ def test_minimal_php(self):
576
+ code = "<?php\nfunction test() { return 1; }\n"
577
+ symbols = parse_file("min.php", code)
578
+ fns = [s for s in symbols if s.kind == "function"]
579
+ assert any(s.name == "test" for s in fns)
580
+
581
+ def test_symbol_line_numbers(self):
582
+ """Symbol start_line should be >= 1."""
583
+ for code, path in [
584
+ (TYPESCRIPT_CODE, "t.ts"),
585
+ (CPP_CODE, "t.cpp"),
586
+ (CSHARP_CODE, "t.cs"),
587
+ (RUBY_CODE, "t.rb"),
588
+ (PHP_CODE, "t.php"),
589
+ ]:
590
+ symbols = parse_file(path, code)
591
+ for s in symbols:
592
+ assert s.start_line >= 1, f"{path}: {s.name} has start_line {s.start_line}"
593
+ assert s.end_line >= s.start_line