java-codebase-rag 0.12.0__py3-none-any.whl → 0.12.2__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 (77) hide show
  1. java_codebase_rag-0.12.2.dist-info/METADATA +35 -0
  2. java_codebase_rag-0.12.2.dist-info/RECORD +4 -0
  3. {java_codebase_rag-0.12.0.dist-info → java_codebase_rag-0.12.2.dist-info}/WHEEL +1 -1
  4. java_codebase_rag/_deprecation.py +0 -103
  5. java_codebase_rag/_fdlimit.py +0 -56
  6. java_codebase_rag/_stdio.py +0 -32
  7. java_codebase_rag/_version.py +0 -35
  8. java_codebase_rag/absence/__init__.py +0 -0
  9. java_codebase_rag/absence/absence_diagnosis.py +0 -700
  10. java_codebase_rag/absence/absence_types.py +0 -124
  11. java_codebase_rag/absence/absence_vocab.py +0 -460
  12. java_codebase_rag/analysis/__init__.py +0 -0
  13. java_codebase_rag/analysis/pr_analysis.py +0 -563
  14. java_codebase_rag/analysis/resolve_service.py +0 -740
  15. java_codebase_rag/ast/__init__.py +0 -0
  16. java_codebase_rag/ast/ast_java.py +0 -2847
  17. java_codebase_rag/ast/ast_kotlin.py +0 -1794
  18. java_codebase_rag/ast/brownfield_events.py +0 -58
  19. java_codebase_rag/ast/chunk_heuristics.py +0 -83
  20. java_codebase_rag/ast/language.py +0 -117
  21. java_codebase_rag/cli.py +0 -1215
  22. java_codebase_rag/cli_dispatch.py +0 -251
  23. java_codebase_rag/cli_format.py +0 -85
  24. java_codebase_rag/cli_progress.py +0 -94
  25. java_codebase_rag/config.py +0 -833
  26. java_codebase_rag/eval/__init__.py +0 -1
  27. java_codebase_rag/eval/ground_truth.py +0 -100
  28. java_codebase_rag/eval/metrics.py +0 -107
  29. java_codebase_rag/eval/runner.py +0 -556
  30. java_codebase_rag/graph/__init__.py +0 -0
  31. java_codebase_rag/graph/build_ast_graph.py +0 -4593
  32. java_codebase_rag/graph/graph_enrich.py +0 -1940
  33. java_codebase_rag/graph/graph_types.py +0 -224
  34. java_codebase_rag/graph/java_ontology.py +0 -465
  35. java_codebase_rag/graph/ladybug_queries.py +0 -2213
  36. java_codebase_rag/graph/path_filtering.py +0 -509
  37. java_codebase_rag/index/__init__.py +0 -0
  38. java_codebase_rag/index/java_index_flow_lancedb.py +0 -879
  39. java_codebase_rag/index/java_index_v1_common.py +0 -33
  40. java_codebase_rag/install_data/__init__.py +0 -0
  41. java_codebase_rag/install_data/agents/explorer-rag-cli.md +0 -110
  42. java_codebase_rag/install_data/agents/explorer-rag-enhanced.md +0 -152
  43. java_codebase_rag/install_data/skills/explore-codebase/SKILL.md +0 -165
  44. java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md +0 -107
  45. java_codebase_rag/installer.py +0 -2188
  46. java_codebase_rag/jrag.py +0 -4545
  47. java_codebase_rag/jrag_envelope.py +0 -1107
  48. java_codebase_rag/jrag_hints.py +0 -204
  49. java_codebase_rag/jrag_render.py +0 -926
  50. java_codebase_rag/lance_optimize.py +0 -264
  51. java_codebase_rag/mcp/__init__.py +0 -0
  52. java_codebase_rag/mcp/mcp_hints.py +0 -932
  53. java_codebase_rag/mcp/mcp_v2.py +0 -1916
  54. java_codebase_rag/mcp/server.py +0 -886
  55. java_codebase_rag/pipeline.py +0 -531
  56. java_codebase_rag/progress.py +0 -570
  57. java_codebase_rag/read_payloads.py +0 -781
  58. java_codebase_rag/search/__init__.py +0 -0
  59. java_codebase_rag/search/index_common.py +0 -10
  60. java_codebase_rag/search/search_lancedb.py +0 -1296
  61. java_codebase_rag/search/search_lexical.py +0 -449
  62. java_codebase_rag/search/search_scoring.py +0 -537
  63. java_codebase_rag/watch/__init__.py +0 -0
  64. java_codebase_rag/watch/client.py +0 -230
  65. java_codebase_rag/watch/daemon.py +0 -396
  66. java_codebase_rag/watch/lock.py +0 -201
  67. java_codebase_rag/watch/paths.py +0 -76
  68. java_codebase_rag/watch/protocol.py +0 -122
  69. java_codebase_rag/watch/server.py +0 -273
  70. java_codebase_rag/watch/warm.py +0 -105
  71. java_codebase_rag/watch/watcher.py +0 -394
  72. java_codebase_rag-0.12.0.dist-info/METADATA +0 -340
  73. java_codebase_rag-0.12.0.dist-info/RECORD +0 -75
  74. java_codebase_rag-0.12.0.dist-info/entry_points.txt +0 -5
  75. java_codebase_rag-0.12.0.dist-info/licenses/LICENSE +0 -21
  76. java_codebase_rag-0.12.0.dist-info/top_level.txt +0 -1
  77. /java_codebase_rag/__init__.py → /java_codebase_rag-0.12.2.dist-info/top_level.txt +0 -0
@@ -1,509 +0,0 @@
1
- """Layered path ignore rules for Java indexing and graph enrichment (B5).
2
-
3
- Resolution order (later overrides earlier; innermost nested wins among peers):
4
-
5
- 1. ``builtin_default`` — legacy ``COMMON_EXCLUDED_PATH_PATTERNS`` (gitignore-style).
6
- 2. ``project_root`` — ``<project>/.java-codebase-rag/ignore``.
7
- 3. ``nested`` — each ``<dir>/.java-codebase-rag/ignore`` along the path from project root
8
- to the file's parent (outer dirs first, inner dirs last).
9
- 4. ``gitignore`` — each ``.gitignore`` from project root down to the file's parent
10
- (when ``use_gitignore`` is true), using :class:`pathspec.GitIgnoreSpec`.
11
-
12
- Paths outside ``project_root`` are never ignored by this object.
13
- """
14
- from __future__ import annotations
15
-
16
- import fnmatch
17
- import os
18
- import warnings
19
- from collections.abc import Iterator, Sequence
20
- from dataclasses import dataclass
21
- from pathlib import Path
22
- from typing import overload
23
-
24
- from pathspec import GitIgnoreSpec
25
-
26
- from java_codebase_rag.ast.language import LANG_BACKENDS
27
-
28
- # Pruning for LocalFile sources: skip VCS, build outputs, dependency trees, and
29
- # test sources (we currently index prod Java only to keep the semantic index clean).
30
- # Also avoids EMFILE under default ulimits when the engine traverses in parallel.
31
- #
32
- # Note on build-output dir names: ``out``, ``build`` and ``target`` are also legal
33
- # Java package names (e.g. ``com.example.out.api``). The unconditional ``**/out/**``
34
- # pattern that previously lived here false-matched such packages and silently
35
- # dropped real source files. These dirs are now pruned only when they sit next to
36
- # a build-tool indicator (``pom.xml``, ``build.gradle``, ``build.gradle.kts``,
37
- # ``settings.gradle``, ``settings.gradle.kts``) — see ``_is_build_output_dir``
38
- # and ``BUILD_DIR_NAMES``. If you genuinely need to skip an arbitrary nested
39
- # directory, add a ``.java-codebase-rag/ignore`` entry at the project or subtree root.
40
- COMMON_EXCLUDED_PATH_PATTERNS: list[str] = [
41
- "**/.*",
42
- "**/.git/**",
43
- "**/.idea/**",
44
- "**/.venv/**",
45
- "**/node_modules/**",
46
- "**/*.class",
47
- "**/src/test/java/**",
48
- "**/src/test/resources/**",
49
- ]
50
-
51
- # Directory names that are pruned ONLY when they sit next to a build-tool indicator.
52
- # The check is ``parent_dir`` contains any of ``BUILD_TOOL_INDICATORS``.
53
- BUILD_DIR_NAMES: tuple[str, ...] = ("target", "build", "out")
54
-
55
- # Files whose presence in a directory marks it as a JVM build module. When one
56
- # of these sits next to a ``BUILD_DIR_NAMES`` entry, that entry is treated as
57
- # build output and pruned from the walk.
58
- BUILD_TOOL_INDICATORS: tuple[str, ...] = (
59
- "pom.xml",
60
- "build.gradle",
61
- "build.gradle.kts",
62
- "settings.gradle",
63
- "settings.gradle.kts",
64
- )
65
-
66
- # Directory names always pruned regardless of siblings (universal nuisance dirs;
67
- # never a legal package name in practice).
68
- UNCONDITIONAL_PRUNE_DIRS: frozenset[str] = frozenset({
69
- ".git",
70
- ".idea",
71
- ".venv",
72
- "node_modules",
73
- })
74
-
75
-
76
- def _is_build_output_dir(parent_dir: str, dirname: str) -> bool:
77
- """True iff ``<parent_dir>/<dirname>`` looks like a JVM build-output directory.
78
-
79
- A name in :data:`BUILD_DIR_NAMES` is build output only when its parent
80
- directory contains a build-tool indicator (Maven/Gradle marker file).
81
- Otherwise, names like ``out`` are treated as ordinary subdirectories so
82
- Java sources under packages such as ``com.example.out.api`` survive the walk.
83
- """
84
- if dirname not in BUILD_DIR_NAMES:
85
- return False
86
- try:
87
- with os.scandir(parent_dir) as it:
88
- siblings = {entry.name for entry in it}
89
- except OSError:
90
- return False
91
- return any(marker in siblings for marker in BUILD_TOOL_INDICATORS)
92
-
93
-
94
- def compile_excluded_glob_patterns(
95
- patterns: Sequence[str] | tuple[str, ...],
96
- ) -> list[str]:
97
- """Store exclude patterns in list form; same as ast-graph ``index`` compile step."""
98
- return list(patterns)
99
-
100
-
101
- def is_relative_path_excluded(rel_posix: str, exclude_globs: list[str]) -> bool:
102
- """True if a project-relative path matches an exclude glob (incl. ``**/<path>``)."""
103
- for pat in exclude_globs:
104
- if fnmatch.fnmatch(rel_posix, pat):
105
- return True
106
- if fnmatch.fnmatch(f"**/{rel_posix}", pat):
107
- return True
108
- return False
109
-
110
-
111
- @dataclass(frozen=True)
112
- class IgnoreLayer:
113
- """One ignore configuration anchored at ``root`` (patterns apply under this dir)."""
114
-
115
- root: Path
116
- spec: GitIgnoreSpec
117
- source: str
118
- ignore_file: Path | None = None
119
-
120
-
121
- def _read_ignore_lines(path: Path) -> list[str]:
122
- try:
123
- text = path.read_text(encoding="utf-8", errors="replace")
124
- except OSError:
125
- return []
126
- return text.splitlines()
127
-
128
-
129
- def _line_has_negation(lines: Sequence[str]) -> bool:
130
- for raw in lines:
131
- s = raw.strip()
132
- if not s or s.startswith("#"):
133
- continue
134
- if s.startswith("\\!"):
135
- continue
136
- if s.startswith("!"):
137
- return True
138
- return False
139
-
140
-
141
- def _scan_negation_any_bundle_ignore(project_root: Path) -> bool:
142
- """Return True if any ``.java-codebase-rag/ignore`` contains a negation (``!``) line.
143
-
144
- Runs one ``rglob`` at :class:`LayeredIgnore` construction. Fine for typical
145
- repos; very large monorepos pay a full-tree walk on every new ``LayeredIgnore``
146
- instance (same for :func:`_scan_negation_any_gitignore`).
147
- """
148
- root = project_root.resolve()
149
- try:
150
- for p in root.rglob(".java-codebase-rag"):
151
- if not p.is_dir():
152
- continue
153
- ign = p / "ignore"
154
- if ign.is_file() and _line_has_negation(_read_ignore_lines(ign)):
155
- return True
156
- except OSError:
157
- return False
158
- return False
159
-
160
-
161
- def _scan_negation_any_gitignore(project_root: Path) -> bool:
162
- """See :func:`_scan_negation_any_bundle_ignore` (also uses ``rglob``)."""
163
- root = project_root.resolve()
164
- try:
165
- for p in root.rglob(".gitignore"):
166
- if p.is_file() and _line_has_negation(_read_ignore_lines(p)):
167
- return True
168
- except OSError:
169
- return False
170
- return False
171
-
172
-
173
- def _prefix_line_to_project(
174
- prefix_posix: str,
175
- raw_line: str,
176
- ) -> str | None:
177
- """Map a gitignore line from a subdirectory anchor to project-root-relative."""
178
- line = raw_line.strip()
179
- if not line or line.startswith("#"):
180
- return None
181
- neg = line.startswith("!")
182
- body = line[1:] if neg else line
183
- if body.startswith("\\#") or body.startswith("\\!"):
184
- body = body[1:]
185
- anchored = body.startswith("/")
186
- if anchored:
187
- body = body[1:]
188
- if prefix_posix:
189
- mapped = f"{prefix_posix}/{body}" if body else prefix_posix
190
- else:
191
- mapped = body
192
- return f"!{mapped}" if neg else mapped
193
-
194
-
195
- def _mega_build_for_rel(
196
- self_root: Path,
197
- rel_project: str,
198
- *,
199
- use_gitignore: bool,
200
- builtin_lines: list[str],
201
- project_ignore_path: Path,
202
- project_lines: list[str] | None,
203
- ) -> tuple[list[str], list[tuple[str, Path | None, int, str]]]:
204
- """Mega gitignore lines (project-relative) + (source, file, line_no, pattern_text)."""
205
- mega: list[str] = []
206
- meta: list[tuple[str, Path | None, int, str]] = []
207
-
208
- def extend_builtin() -> None:
209
- for i, raw in enumerate(builtin_lines, start=1):
210
- s = raw.strip()
211
- if not s or s.startswith("#"):
212
- continue
213
- mega.append(raw.rstrip("\n"))
214
- meta.append(("builtin_default", None, i, s))
215
-
216
- def extend_file(source: str, path: Path, lines: Sequence[str]) -> None:
217
- for lineno, raw in enumerate(lines, start=1):
218
- s = raw.strip()
219
- if not s or s.startswith("#"):
220
- continue
221
- mega.append(raw.rstrip("\n"))
222
- meta.append((source, path, lineno, s))
223
-
224
- extend_builtin()
225
- if project_lines is not None:
226
- extend_file("project_root", project_ignore_path, project_lines)
227
-
228
- parts = Path(rel_project).parts
229
- dir_parts = parts[:-1] if len(parts) > 1 else ()
230
- for i in range(1, len(dir_parts) + 1):
231
- anchor = self_root.joinpath(*dir_parts[:i])
232
- nested_path = anchor / ".java-codebase-rag" / "ignore"
233
- if not nested_path.is_file():
234
- continue
235
- prefix = anchor.relative_to(self_root).as_posix()
236
- nlines = _read_ignore_lines(nested_path)
237
- for lineno, raw in enumerate(nlines, start=1):
238
- mapped = _prefix_line_to_project(prefix, raw)
239
- if mapped is None:
240
- continue
241
- mega.append(mapped)
242
- meta.append(("nested", nested_path, lineno, raw.strip()))
243
-
244
- if use_gitignore:
245
- for i in range(len(dir_parts) + 1):
246
- anchor = self_root if i == 0 else self_root.joinpath(*dir_parts[:i])
247
- git_path = anchor / ".gitignore"
248
- if not git_path.is_file():
249
- continue
250
- prefix = anchor.relative_to(self_root).as_posix() if i > 0 else ""
251
- glines = _read_ignore_lines(git_path)
252
- for lineno, raw in enumerate(glines, start=1):
253
- mapped = _prefix_line_to_project(prefix, raw)
254
- if mapped is None:
255
- continue
256
- mega.append(mapped)
257
- meta.append(("gitignore", git_path, lineno, raw.strip()))
258
-
259
- return mega, meta
260
-
261
-
262
- def _winning_row(
263
- rel: str,
264
- mega: list[str],
265
- meta: list[tuple[str, Path | None, int, str]],
266
- ) -> tuple[str, Path | None, int, str]:
267
- """The last rule line that changes the cumulative match result (git semantics)."""
268
- if not mega:
269
- return "builtin_default", None, 1, ""
270
- state = False
271
- last_idx = 0
272
- for i in range(len(mega)):
273
- cur = GitIgnoreSpec.from_lines(mega[: i + 1]).match_file(rel)
274
- if cur != state:
275
- last_idx = i
276
- state = cur
277
- return meta[last_idx]
278
-
279
-
280
- class LayeredIgnore:
281
- """Evaluate layered ignore rules anchored at a single project root."""
282
-
283
- def __init__(
284
- self,
285
- project_root: Path | str,
286
- *,
287
- use_gitignore: bool = True,
288
- builtin_patterns: Sequence[str] | None = None,
289
- ) -> None:
290
- self.project_root = Path(project_root).expanduser().resolve()
291
- self.use_gitignore = use_gitignore
292
- self._builtin_lines = (
293
- list(builtin_patterns)
294
- if builtin_patterns is not None
295
- else list(COMMON_EXCLUDED_PATH_PATTERNS)
296
- )
297
- self._project_ignore_path = self.project_root / ".java-codebase-rag" / "ignore"
298
- self._project_lines: list[str] | None = None
299
- if self._project_ignore_path.is_file():
300
- self._project_lines = _read_ignore_lines(self._project_ignore_path)
301
- self._permissive_coco_walk = (
302
- _scan_negation_any_bundle_ignore(self.project_root)
303
- or (use_gitignore and _scan_negation_any_gitignore(self.project_root))
304
- )
305
- self._mega_cache: dict[str, tuple[list[str], GitIgnoreSpec, list[tuple[str, Path | None, int, str]]]] = {}
306
-
307
- def cocoindex_excluded_patterns(self) -> list[str]:
308
- """Patterns for CocoIndex ``PatternFilePathMatcher.excluded_patterns``.
309
-
310
- Matches pre-B5 behaviour when no negation rules exist anywhere under the
311
- project that could un-ignore paths under pruned directories. Otherwise
312
- returns an empty list and callers must filter each path with
313
- :meth:`is_ignored`.
314
- """
315
- if self._permissive_coco_walk:
316
- return []
317
- return list(self._builtin_lines)
318
-
319
- def _rel_project(self, path: Path) -> str | None:
320
- try:
321
- return path.resolve().relative_to(self.project_root).as_posix()
322
- except ValueError:
323
- return None
324
-
325
- def _path_for_display(self, path: Path | None) -> str:
326
- """Project-relative POSIX path when under ``project_root``; else best-effort short path."""
327
- if path is None:
328
- return ""
329
- try:
330
- return path.resolve().relative_to(self.project_root).as_posix()
331
- except ValueError:
332
- try:
333
- return path.resolve().relative_to(Path.cwd()).as_posix()
334
- except ValueError:
335
- return path.as_posix()
336
-
337
- def _mega(self, rel_project: str) -> tuple[list[str], GitIgnoreSpec, list[tuple[str, Path | None, int, str]]]:
338
- # Cache by directory (parent of rel_project). _mega_build_for_rel reads only dir_parts,
339
- # so files in the same directory share the same mega/spec/meta tuple.
340
- cache_key = Path(rel_project).parent.as_posix()
341
- if cache_key in self._mega_cache:
342
- return self._mega_cache[cache_key]
343
- mega, meta = _mega_build_for_rel(
344
- self.project_root,
345
- rel_project,
346
- use_gitignore=self.use_gitignore,
347
- builtin_lines=self._builtin_lines,
348
- project_ignore_path=self._project_ignore_path,
349
- project_lines=self._project_lines,
350
- )
351
- result = (mega, GitIgnoreSpec.from_lines(mega), meta)
352
- self._mega_cache[cache_key] = result
353
- return result
354
-
355
- def is_ignored(self, path: Path) -> bool:
356
- """Return whether ``path`` is ignored by any configured layer.
357
-
358
- Boolean-only fast path for the per-file index walk. It deliberately does
359
- not compute *which* layer/source last matched: that attribution is
360
- O(rules²) via :func:`_winning_row` (one ``GitIgnoreSpec`` rebuild per
361
- rule prefix) and is only needed for ``diagnose-ignore``, so it lives in
362
- :meth:`diagnose_dict` and is never paid on the hot path.
363
- """
364
- rel = self._rel_project(path)
365
- if rel is None:
366
- return False
367
- mega, spec, _ = self._mega(rel)
368
- if not mega:
369
- return False
370
- return spec.match_file(rel)
371
-
372
- def diagnose(self, path: Path) -> str:
373
- """Human-readable, multi-line explanation of the ignore decision."""
374
- d = self.diagnose_dict(path)
375
- expl = d.get("explanation", "")
376
- layer = d.get("layer")
377
- ign = d.get("ignored")
378
- mp = d.get("matching_pattern")
379
- lines = [
380
- f"ignored={ign}",
381
- f"layer={layer!r}",
382
- f"matching_pattern={mp!r}",
383
- str(expl),
384
- ]
385
- return "\n".join(lines)
386
-
387
- def diagnose_dict(self, path: Path) -> dict[str, object]:
388
- """Structured diagnose payload for MCP ``diagnose_ignore``."""
389
- rel = self._rel_project(path)
390
- if rel is None:
391
- return {
392
- "ignored": False,
393
- "layer": None,
394
- "matching_pattern": None,
395
- "explanation": (
396
- f"Path {self._path_for_display(path)!r} is outside the configured "
397
- "project root — not ignored."
398
- ),
399
- }
400
- mega, spec, meta = self._mega(rel)
401
- if not mega:
402
- return {
403
- "ignored": False,
404
- "layer": None,
405
- "matching_pattern": None,
406
- "explanation": f"Path {rel!r} is not ignored by any configured layer.",
407
- }
408
- ignored = spec.match_file(rel)
409
- if not ignored:
410
- return {
411
- "ignored": False,
412
- "layer": None,
413
- "matching_pattern": None,
414
- "explanation": f"Path {rel!r} is not ignored by any configured layer.",
415
- }
416
- src, fp, ln, pat = _winning_row(rel, mega, meta)
417
- if fp is not None:
418
- expl = (
419
- f"Excluded by {self._path_for_display(fp)} ({src}) at line {ln}: {pat!r}"
420
- )
421
- else:
422
- expl = f"Excluded by builtin default ({src}) at builtin line {ln}: {pat!r}"
423
- return {
424
- "ignored": True,
425
- "layer": src,
426
- "matching_pattern": pat,
427
- "explanation": expl,
428
- }
429
-
430
-
431
- @overload
432
- def iter_source_files(root: Path, exclude_globs: list[str]) -> Iterator[Path]: ...
433
-
434
-
435
- @overload
436
- def iter_source_files(root: Path, *, ignore: LayeredIgnore) -> Iterator[Path]: ...
437
-
438
-
439
- def iter_source_files(
440
- root: Path,
441
- exclude_globs: list[str] | None = None,
442
- *,
443
- ignore: LayeredIgnore | None = None,
444
- ) -> Iterator[Path]:
445
- """Walk ``root`` for source files of any registered language backend.
446
-
447
- Yields files whose suffix is claimed by at least one backend in
448
- :data:`~java_codebase_rag.ast.language.LANG_BACKENDS` (``.java`` always;
449
- ``.kt`` when the ``tree-sitter-kotlin`` grammar imports). Pruning
450
- (``UNCONDITIONAL_PRUNE_DIRS`` + ``_is_build_output_dir``) and the layered
451
- ignore rules are unchanged from the former Java-only walk.
452
- """
453
- if exclude_globs is not None and ignore is not None:
454
- raise TypeError("pass either exclude_globs or ignore=, not both")
455
- if exclude_globs is not None:
456
- warnings.warn(
457
- "iter_source_files(root, exclude_globs) is deprecated; "
458
- "use iter_source_files(root, ignore=LayeredIgnore(root, ...)).",
459
- DeprecationWarning,
460
- stacklevel=2,
461
- )
462
- ignore_ctx = LayeredIgnore(root, builtin_patterns=exclude_globs, use_gitignore=False)
463
- elif ignore is not None:
464
- ignore_ctx = ignore
465
- else:
466
- ignore_ctx = LayeredIgnore(root)
467
- # Union of suffixes claimed by every registered backend (``.java`` always;
468
- # ``.kt`` when the Kotlin grammar imports). Computed once per call.
469
- known_suffixes: set[str] = set()
470
- for backend in LANG_BACKENDS.values():
471
- known_suffixes.update(backend.suffixes)
472
- root = root.resolve()
473
- for dirpath, dirnames, filenames in os.walk(root):
474
- # Universal nuisance dirs (VCS, IDE, deps) are pruned unconditionally.
475
- # Build-output dirs (``out`` / ``build`` / ``target``) are pruned only when
476
- # they sit alongside a build-tool indicator file — otherwise names like
477
- # ``out`` belong to a Java package (e.g. ``com.example.out.api``) and must
478
- # be walked. See ``_is_build_output_dir``.
479
- dirnames[:] = [
480
- d
481
- for d in dirnames
482
- if d not in UNCONDITIONAL_PRUNE_DIRS
483
- and not _is_build_output_dir(dirpath, d)
484
- ]
485
- for fn in filenames:
486
- # Literal byte-identity match (mirrors the legacy ``endswith``
487
- # behaviour): ``Path(".java").suffix`` is ``""``, so a dotfile named
488
- # ``.java`` would be dropped by a suffix-in-set check. ``endswith``
489
- # over the registered suffixes restores the exact old semantics.
490
- if not any(fn.endswith(s) for s in known_suffixes):
491
- continue
492
- p = Path(dirpath) / fn
493
- if ignore_ctx.is_ignored(p):
494
- continue
495
- yield p
496
-
497
-
498
- def iter_java_source_files(
499
- root: Path,
500
- exclude_globs: list[str] | None = None,
501
- *,
502
- ignore: LayeredIgnore | None = None,
503
- ) -> Iterator[Path]:
504
- """Deprecated alias for :func:`iter_source_files`.
505
-
506
- Kept so any import site not migrated to the new name continues to work; new
507
- call sites should use :func:`iter_source_files`.
508
- """
509
- return iter_source_files(root, exclude_globs, ignore=ignore)
File without changes