pi-lens 3.8.72 → 3.8.73
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.
- package/CHANGELOG.md +46 -0
- package/README.md +25 -24
- package/dist/clients/deps/js-yaml.js +1 -1
- package/dist/clients/dispatch/runners/ast-grep-napi.js +60 -7
- package/dist/clients/instance-registry.js +12 -1
- package/dist/clients/lsp/index.js +86 -14
- package/dist/index.js +3709 -3409
- package/dist/tools/lsp-diagnostics.js +126 -9
- package/docs/servercapabilities.md +1 -1
- package/package.json +8 -9
- package/rules/tree-sitter-queries/typescript/no-equality-in-for-condition.yml +1 -0
- package/rules/tree-sitter-queries/typescript/no-jump-in-finally.yml +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,50 @@ All notable changes to pi-lens will be documented in this file.
|
|
|
10
10
|
|
|
11
11
|
### Fixed
|
|
12
12
|
|
|
13
|
+
## [3.8.73] - 2026-07-28
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **`@types/js-yaml` removed; js-yaml 5 migration complete** (closes #391) — with
|
|
20
|
+
js-yaml 5.2.2 in (#843) the package ships its own declarations, so the stub
|
|
21
|
+
types dev-dependency is gone and the lockfile regenerated. This completes the
|
|
22
|
+
v5 migration started with the namespace-import compatibility pass below.
|
|
23
|
+
|
|
24
|
+
- **`js-yaml` imports are compatible with v5** (refs #391, #843) — use the
|
|
25
|
+
package's namespace exports so the central accessor and YAML rule tooling work
|
|
26
|
+
with both the current v4 dependency and the upcoming v5 release. `@types/js-yaml`
|
|
27
|
+
remains temporarily because js-yaml 4.3.0 needs its declarations; remove it
|
|
28
|
+
with the v5 Dependabot upgrade once the bundled declarations are active.
|
|
29
|
+
|
|
30
|
+
- **`build:dist` now compiles with TypeScript 7.0.2** (refs #809) — the dist emit pin moves from `typescript@6` to the same major as the dev dependency (#600); emitted output was verified byte-identical across all 328 files before the bump.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **ast-grep Gate-B now follows LSP liveness and launcher resolution** (refs #868) — the NAPI fallback no longer runs beside a healthy ast-grep LSP when the bundled native binary is resolvable but `ast-grep` is absent from PATH; it still engages when neither resolution path nor a live client is available.
|
|
35
|
+
|
|
36
|
+
- **NAPI fallback unsupported-rule skips no longer spam the session log** (refs #282) — the per-rule terminal lines (one for each of the ~30 non-jsts catalog rules) are replaced by a single aggregated `astgrep_napi_unsupported_rules_skipped` entry in `latency.log` carrying per-language counts and rule ids.
|
|
37
|
+
|
|
38
|
+
- **Missing tree-sitter defect-class metadata is now covered** (refs #863) — loop-termination and `finally` control-flow rules now declare their taxonomy classes, keeping the rule audit complete.
|
|
39
|
+
|
|
40
|
+
- **MCP smoke deadlines scale under CI load (refs #860)** — the shared MCP
|
|
41
|
+
test harness keeps its 20s local default, accepts the
|
|
42
|
+
`PI_LENS_TEST_TIMEOUT_SCALE` multiplier, and the analyze-graph smoke pays
|
|
43
|
+
server startup through a throwaway health call before its assertions.
|
|
44
|
+
|
|
45
|
+
- **TypeScript 7 compatibility fixes (refs #809)** — narrowed ast-grep search test details and made the LSP `workspace/applyEdit` response overload-compatible, unblocking Dependabot PR #600.
|
|
46
|
+
|
|
47
|
+
- **ast-grep NAPI fallback now runs TSX-tagged rules and reports unsupported-language skips** (refs #282) — `.tsx` files use the `tsx` grammar for rule-language scoping, preserving the exact TypeScript/JavaScript twin behavior from #657; rules for languages the fallback cannot evaluate are logged once per rule instead of disappearing as zero matches.
|
|
48
|
+
|
|
49
|
+
- **CSS server root-policy coverage is hermetic on Windows** (refs #855) — the
|
|
50
|
+
server-policy fixture now pins its nearest `package.json` inside the temporary
|
|
51
|
+
workspace, so a marker in the host user's profile cannot change the result.
|
|
52
|
+
|
|
53
|
+
- **Multi-client LSP shutdown no longer serializes the grace-period tail** (#851) — retiring clients now tear down concurrently while generation handoff still waits for every client to settle; instance-registry child removals serialize their read-modify-write without serializing process kills.
|
|
54
|
+
|
|
55
|
+
- **LSP singleton resets no longer overlap live server generations** (closes #850) — `resetLSPService()` still returns synchronously and may allocate a replacement service immediately, but that replacement's first spawn now waits for every older generation's asynchronous teardown to settle. Repeated resets destroy an intermediate waiting generation before it can spawn, and post-root/post-client-cleanup guards prevent a retired service from starting a late process outside `state.inFlight`. The handoff is cleared after its first wait, preserving normal within-generation warm reuse and its hot path; concurrent-secondary sessions still skip destructive reset through the existing ownership guard. New deterministic coverage holds the old client's shutdown open and proves current `master`'s pre-fix second spawn/three-generation accumulation cannot recur.
|
|
56
|
+
|
|
13
57
|
## [3.8.72] - 2026-07-26
|
|
14
58
|
|
|
15
59
|
### Added
|
|
@@ -42,6 +86,8 @@ All notable changes to pi-lens will be documented in this file.
|
|
|
42
86
|
|
|
43
87
|
### Changed
|
|
44
88
|
|
|
89
|
+
- **Explicit `lsp_diagnostics` file batches now expose a consolidated contract** (refs #837) — runtime path lists are normalized and rejected above the 100-file cap (never silently truncated), preserve input order, enforce bounded per-file deadlines and abort-aware completion, and return honest per-file outcomes (`clean`, `findings`, `unsupported`, `unavailable`, `failed`, `inconclusive`) with aggregate counts. Primary-language diagnostics remain separated from auxiliary findings, and incomplete batches are rendered as unconfirmed.
|
|
90
|
+
|
|
45
91
|
- **Incumbent LSP wait policy now has one process-neutral module boundary** (#822) — per-server strategies, tier classification, and capability-snapshot types moved under `clients/lsp/wait-policy/` with compatibility re-exports at the old paths; this is an internal refactor with no behavior change.
|
|
46
92
|
|
|
47
93
|
- **Review-graph file budget now tapers instead of scaling flatly or cliff-dropping above 1,000 files** (refs #775 R1) — `project-scale.ts`'s `getReviewGraphMaxFilesDerived` keeps today's exact 0.5× `maxProjectFiles` ratio (1,000 files at the 2,000 default base) unchanged for every project at or below a 4,000-file `maxProjectFiles` base, then tapers growth asymptotically toward a 5,000-file hard ceiling for larger bases instead of continuing linearly forever. The ceiling is bound FIRST by the review graph's own disk-persist circuit-breaker (`GRAPH_PERSIST_MAX_ELEMENTS_DEFAULT` = 200,000 graph elements in `review-graph/builder.ts` — above it the graph is never persisted, logged + skipped, so a repo over that line pays a full cold build every session instead of once): at ~30 elements/file for a symbol-dense repo, 5,000 files ≈ 150,000 elements, a ~25% margin below the persist cap (a 6,000-file ceiling considered during review left only ~10% margin — too little for a denser-than-average repo). SECOND and independently, it's consistent with the graph's measured per-file build cost (CHANGELOG 3.8.43: "2-4 s" full cold build at the then-1,000-file cap, ~2-4 ms/file; 5,000 files ≈ 10-20 s worst-case, one-time and disk-cached thereafter). New `.pi-lens.json#reviewGraph.maxFiles` knob (R2) lets a monorepo opt into a bigger graph explicitly, bypassing the taper (tolerant numeric parse, clamped to `[100, 20_000]`, warn-once on invalid — mirrors `maxProjectFiles`'s parsing); the pre-existing `PI_LENS_REVIEW_GRAPH_MAX_FILES` env override still wins outright over both. Truncation (files found over the cap) now always logs a `review_graph_size_skip` latency phase directly from `review-graph/builder.ts`'s skip branch (R3), independent of which caller triggered the build, instead of relying on `dispatch/integration.ts`'s cascade-only `graph_build` log — closing a silent-cap gap for the other four `buildOrUpdateGraph` call sites (`lens-map.ts`, `project-report.ts`, `mcp/analyze.ts`, `runtime-session.ts`, `dispatch/runners/tree-sitter.ts`). Documented in `docs/globalconfig.md`'s project-config section. New tests: `taperedReviewGraphMaxFiles` shape (below/at/above the linear boundary, half-gap-at-taper-scale property, ceiling asymptote, monotonicity), the config knob's parse/clamp/warn-once behavior, and the new latency phase.
|
package/README.md
CHANGED
|
@@ -79,8 +79,8 @@ Thanks goes to these wonderful people:
|
|
|
79
79
|
<!-- prettier-ignore-start -->
|
|
80
80
|
<!-- markdownlint-disable -->
|
|
81
81
|
<table>
|
|
82
|
-
<tbody>
|
|
83
|
-
<tr>
|
|
82
|
+
<tbody>
|
|
83
|
+
<tr>
|
|
84
84
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/apmantza"><img src="https://avatars.githubusercontent.com/u/247365598?v=4" width="100px;" alt=""/><br /><sub><b>Apostolos Mantzaris</b></sub></a><br /><a href="#code-apmantza" title="Code">💻</a> <a href="#doc-apmantza" title="Documentation">📖</a> <a href="#ideas-apmantza" title="Ideas & Planning">🤔</a> <a href="#maintenance-apmantza" title="Maintenance">🚧</a> <a href="#review-apmantza" title="Reviewed Pull Requests">👀</a></td>
|
|
85
85
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anh-chu"><img src="https://avatars.githubusercontent.com/u/34973633?v=4" width="100px;" alt=""/><br /><sub><b>Anh Chu</b></sub></a><br /><a href="#code-anh-chu" title="Code">💻</a></td>
|
|
86
86
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/loss-and-quick"><img src="https://avatars.githubusercontent.com/u/39405619?v=4" width="100px;" alt=""/><br /><sub><b>minicx</b></sub></a><br /><a href="#code-loss-and-quick" title="Code">💻</a></td>
|
|
@@ -88,7 +88,7 @@ Thanks goes to these wonderful people:
|
|
|
88
88
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tifandotme"><img src="https://avatars.githubusercontent.com/u/33323177?v=4" width="100px;" alt=""/><br /><sub><b>Tifan Dwi Avianto</b></sub></a><br /><a href="#code-tifandotme" title="Code">💻</a></td>
|
|
89
89
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amit-gshe"><img src="https://avatars.githubusercontent.com/u/7383028?v=4" width="100px;" alt=""/><br /><sub><b>Amit</b></sub></a><br /><a href="#code-amit-gshe" title="Code">💻</a> <a href="#bug-amit-gshe" title="Bug reports">🐛</a></td>
|
|
90
90
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fractiunate"><img src="https://avatars.githubusercontent.com/u/78024279?v=4" width="100px;" alt=""/><br /><sub><b>Fractiunate // David Rahäuser</b></sub></a><br /><a href="#code-fractiunate" title="Code">💻</a></td>
|
|
91
|
-
</tr
|
|
91
|
+
</tr>
|
|
92
92
|
<tr>
|
|
93
93
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vkarasen"><img src="https://avatars.githubusercontent.com/u/7932536?v=4" width="100px;" alt=""/><br /><sub><b>Vitali Karasenko</b></sub></a><br /><a href="#code-vkarasen" title="Code">💻</a></td>
|
|
94
94
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rfxlamia"><img src="https://avatars.githubusercontent.com/u/222023708?v=4" width="100px;" alt=""/><br /><sub><b>V</b></sub></a><br /><a href="#code-rfxlamia" title="Code">💻</a></td>
|
|
@@ -97,7 +97,7 @@ Thanks goes to these wonderful people:
|
|
|
97
97
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mehalter"><img src="https://avatars.githubusercontent.com/u/1591837?v=4" width="100px;" alt=""/><br /><sub><b>Micah Halter</b></sub></a><br /><a href="#code-mehalter" title="Code">💻</a></td>
|
|
98
98
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JulienEllie"><img src="https://avatars.githubusercontent.com/u/670518?v=4" width="100px;" alt=""/><br /><sub><b>Julien Ellie</b></sub></a><br /><a href="#code-JulienEllie" title="Code">💻</a></td>
|
|
99
99
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Istar-Eldritch"><img src="https://avatars.githubusercontent.com/u/3746468?v=4" width="100px;" alt=""/><br /><sub><b>Ruben Paz</b></sub></a><br /><a href="#code-Istar-Eldritch" title="Code">💻</a> <a href="#bug-Istar-Eldritch" title="Bug reports">🐛</a></td>
|
|
100
|
-
</tr
|
|
100
|
+
</tr>
|
|
101
101
|
<tr>
|
|
102
102
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cjunxiang"><img src="https://avatars.githubusercontent.com/u/26619858?v=4" width="100px;" alt=""/><br /><sub><b>C.Junxiang</b></sub></a><br /><a href="#code-cjunxiang" title="Code">💻</a> <a href="#bug-cjunxiang" title="Bug reports">🐛</a></td>
|
|
103
103
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/trvon"><img src="https://avatars.githubusercontent.com/u/6031322?v=4" width="100px;" alt=""/><br /><sub><b>Trevon</b></sub></a><br /><a href="#code-trvon" title="Code">💻</a></td>
|
|
@@ -106,7 +106,7 @@ Thanks goes to these wonderful people:
|
|
|
106
106
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/spyrosbazios"><img src="https://avatars.githubusercontent.com/u/37960233?v=4" width="100px;" alt=""/><br /><sub><b>spyrosbazios</b></sub></a><br /><a href="#code-spyrosbazios" title="Code">💻</a></td>
|
|
107
107
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Roman-Galeev"><img src="https://avatars.githubusercontent.com/u/40388226?v=4" width="100px;" alt=""/><br /><sub><b>Roman Galeev</b></sub></a><br /><a href="#code-Roman-Galeev" title="Code">💻</a></td>
|
|
108
108
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jerryfan"><img src="https://avatars.githubusercontent.com/u/2540814?v=4" width="100px;" alt=""/><br /><sub><b>jerryfan</b></sub></a><br /><a href="#code-jerryfan" title="Code">💻</a></td>
|
|
109
|
-
</tr
|
|
109
|
+
</tr>
|
|
110
110
|
<tr>
|
|
111
111
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shaDmx"><img src="https://avatars.githubusercontent.com/u/91132641?v=4" width="100px;" alt=""/><br /><sub><b>Max L.</b></sub></a><br /><a href="#code-shaDmx" title="Code">💻</a></td>
|
|
112
112
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/radge"><img src="https://avatars.githubusercontent.com/u/129205?v=4" width="100px;" alt=""/><br /><sub><b>David Ryan</b></sub></a><br /><a href="#code-radge" title="Code">💻</a></td>
|
|
@@ -115,90 +115,91 @@ Thanks goes to these wonderful people:
|
|
|
115
115
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/feoh"><img src="https://avatars.githubusercontent.com/u/330070?v=4" width="100px;" alt=""/><br /><sub><b>Chris Patti</b></sub></a><br /><a href="#code-feoh" title="Code">💻</a> <a href="#bug-feoh" title="Bug reports">🐛</a></td>
|
|
116
116
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chenxin-yan"><img src="https://avatars.githubusercontent.com/u/71162231?v=4" width="100px;" alt=""/><br /><sub><b>Chenxin Yan</b></sub></a><br /><a href="#code-chenxin-yan" title="Code">💻</a> <a href="#doc-chenxin-yan" title="Documentation">📖</a> <a href="#bug-chenxin-yan" title="Bug reports">🐛</a></td>
|
|
117
117
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/flowing-abyss"><img src="https://avatars.githubusercontent.com/u/98622217?v=4" width="100px;" alt=""/><br /><sub><b>flowing-abyss</b></sub></a><br /><a href="#code-flowing-abyss" title="Code">💻</a></td>
|
|
118
|
-
</tr
|
|
118
|
+
</tr>
|
|
119
119
|
<tr>
|
|
120
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shaochong-theoai"><img src="https://avatars.githubusercontent.com/u/305592060?v=4" width="100px;" alt=""/><br /><sub><b>shaochong-theoai</b></sub></a><br /><a href="#code-shaochong-theoai" title="Code">💻</a></td>
|
|
120
121
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuli500177"><img src="https://avatars.githubusercontent.com/u/62830942?v=4" width="100px;" alt=""/><br /><sub><b>Xu Yili</b></sub></a><br /><a href="#bug-xuli500177" title="Bug reports">🐛</a></td>
|
|
121
122
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ricardoraposo"><img src="https://avatars.githubusercontent.com/u/50217712?v=4" width="100px;" alt=""/><br /><sub><b>Ricardo Raposo</b></sub></a><br /><a href="#code-ricardoraposo" title="Code">💻</a></td>
|
|
122
123
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jsmestad"><img src="https://avatars.githubusercontent.com/u/16150?v=4" width="100px;" alt=""/><br /><sub><b>Justin Smestad</b></sub></a><br /><a href="#code-jsmestad" title="Code">💻</a></td>
|
|
123
124
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anasalsbey-glitch"><img src="https://avatars.githubusercontent.com/u/283609702?v=4" width="100px;" alt=""/><br /><sub><b>anasalsbey-glitch</b></sub></a><br /><a href="#code-anasalsbey-glitch" title="Code">💻</a> <a href="#bug-anasalsbey-glitch" title="Bug reports">🐛</a></td>
|
|
124
125
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PeraSite"><img src="https://avatars.githubusercontent.com/u/19837403?v=4" width="100px;" alt=""/><br /><sub><b>정제훈</b></sub></a><br /><a href="#code-PeraSite" title="Code">💻</a></td>
|
|
125
126
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/carolitascl"><img src="https://avatars.githubusercontent.com/u/26188349?v=4" width="100px;" alt=""/><br /><sub><b>Carolina</b></sub></a><br /><a href="#bug-carolitascl" title="Bug reports">🐛</a></td>
|
|
126
|
-
|
|
127
|
-
</tr><br />
|
|
127
|
+
</tr>
|
|
128
128
|
<tr>
|
|
129
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/leakedby"><img src="https://avatars.githubusercontent.com/u/4213260?v=4" width="100px;" alt=""/><br /><sub><b>LeakedBy</b></sub></a><br /><a href="#bug-leakedby" title="Bug reports">🐛</a></td>
|
|
129
130
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mdbooth"><img src="https://avatars.githubusercontent.com/u/1318691?v=4" width="100px;" alt=""/><br /><sub><b>Matthew Booth</b></sub></a><br /><a href="#bug-mdbooth" title="Bug reports">🐛</a></td>
|
|
130
131
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Wiedzmin89"><img src="https://avatars.githubusercontent.com/u/61706855?v=4" width="100px;" alt=""/><br /><sub><b>Wiedzmin89</b></sub></a><br /><a href="#ideas-Wiedzmin89" title="Ideas & Planning">🤔</a></td>
|
|
131
132
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/grodingo"><img src="https://avatars.githubusercontent.com/u/244184972?v=4" width="100px;" alt=""/><br /><sub><b>Virgile</b></sub></a><br /><a href="#bug-grodingo" title="Bug reports">🐛</a></td>
|
|
132
133
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/finnvyrn"><img src="https://avatars.githubusercontent.com/u/90801772?v=4" width="100px;" alt=""/><br /><sub><b>Finn</b></sub></a><br /><a href="#ideas-finnvyrn" title="Ideas & Planning">🤔</a></td>
|
|
133
134
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ywh555hhh"><img src="https://avatars.githubusercontent.com/u/121592812?v=4" width="100px;" alt=""/><br /><sub><b>Wayne E</b></sub></a><br /><a href="#bug-ywh555hhh" title="Bug reports">🐛</a></td>
|
|
134
135
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RimuruW"><img src="https://avatars.githubusercontent.com/u/59136309?v=4" width="100px;" alt=""/><br /><sub><b>RimuruW</b></sub></a><br /><a href="#ideas-RimuruW" title="Ideas & Planning">🤔</a></td>
|
|
135
|
-
|
|
136
|
-
</tr><br />
|
|
136
|
+
</tr>
|
|
137
137
|
<tr>
|
|
138
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Bjynt"><img src="https://avatars.githubusercontent.com/u/22177300?v=4" width="100px;" alt=""/><br /><sub><b>Bjynt</b></sub></a><br /><a href="#bug-Bjynt" title="Bug reports">🐛</a> <a href="#ideas-Bjynt" title="Ideas & Planning">🤔</a></td>
|
|
138
139
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pvtri96"><img src="https://avatars.githubusercontent.com/u/28696888?v=4" width="100px;" alt=""/><br /><sub><b>Tri Van Pham</b></sub></a><br /><a href="#bug-pvtri96" title="Bug reports">🐛</a></td>
|
|
139
140
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BingWuJ"><img src="https://avatars.githubusercontent.com/u/117666511?v=4" width="100px;" alt=""/><br /><sub><b>BingWuJ</b></sub></a><br /><a href="#bug-BingWuJ" title="Bug reports">🐛</a></td>
|
|
140
141
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tackleberrybey"><img src="https://avatars.githubusercontent.com/u/156954032?v=4" width="100px;" alt=""/><br /><sub><b>tackleberrybey</b></sub></a><br /><a href="#bug-tackleberrybey" title="Bug reports">🐛</a></td>
|
|
141
142
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TaterDoge"><img src="https://avatars.githubusercontent.com/u/121467933?v=4" width="100px;" alt=""/><br /><sub><b>Mariann Abshire</b></sub></a><br /><a href="#bug-TaterDoge" title="Bug reports">🐛</a></td>
|
|
142
143
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ProbabilityEngineer"><img src="https://avatars.githubusercontent.com/u/38498804?v=4" width="100px;" alt=""/><br /><sub><b>ProbabilityEngineer</b></sub></a><br /><a href="#ideas-ProbabilityEngineer" title="Ideas & Planning">🤔</a></td>
|
|
143
144
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LeonardoRick"><img src="https://avatars.githubusercontent.com/u/17517057?v=4" width="100px;" alt=""/><br /><sub><b>Leonardo Rick</b></sub></a><br /><a href="#bug-LeonardoRick" title="Bug reports">🐛</a> <a href="#ideas-LeonardoRick" title="Ideas & Planning">🤔</a></td>
|
|
144
|
-
|
|
145
|
-
</tr><br />
|
|
145
|
+
</tr>
|
|
146
146
|
<tr>
|
|
147
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kenbanks-peng"><img src="https://avatars.githubusercontent.com/u/26904200?v=4" width="100px;" alt=""/><br /><sub><b>Ken Banks</b></sub></a><br /><a href="#bug-kenbanks-peng" title="Bug reports">🐛</a></td>
|
|
147
148
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rstacruz"><img src="https://avatars.githubusercontent.com/u/74385?v=4" width="100px;" alt=""/><br /><sub><b>Rico Sta. Cruz</b></sub></a><br /><a href="#ideas-rstacruz" title="Ideas & Planning">🤔</a></td>
|
|
148
149
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/acidnik"><img src="https://avatars.githubusercontent.com/u/1227955?v=4" width="100px;" alt=""/><br /><sub><b>Nikita Bilous</b></sub></a><br /><a href="#bug-acidnik" title="Bug reports">🐛</a></td>
|
|
149
150
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/av1155"><img src="https://avatars.githubusercontent.com/u/117413846?v=4" width="100px;" alt=""/><br /><sub><b>Andrea Arturo Venti Fuentes</b></sub></a><br /><a href="#bug-av1155" title="Bug reports">🐛</a></td>
|
|
150
151
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dacec354"><img src="https://avatars.githubusercontent.com/u/90093629?v=4" width="100px;" alt=""/><br /><sub><b>dacec354</b></sub></a><br /><a href="#bug-dacec354" title="Bug reports">🐛</a></td>
|
|
151
152
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nodnarbnitram"><img src="https://avatars.githubusercontent.com/u/44812862?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Martin</b></sub></a><br /><a href="#bug-nodnarbnitram" title="Bug reports">🐛</a></td>
|
|
152
153
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alpertarhan"><img src="https://avatars.githubusercontent.com/u/50966980?v=4" width="100px;" alt=""/><br /><sub><b>Alper Tarhan</b></sub></a><br /><a href="#bug-alpertarhan" title="Bug reports">🐛</a></td>
|
|
153
|
-
|
|
154
|
-
</tr><br />
|
|
154
|
+
</tr>
|
|
155
155
|
<tr>
|
|
156
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asolopovas"><img src="https://avatars.githubusercontent.com/u/6893216?v=4" width="100px;" alt=""/><br /><sub><b>Andrius Solopovas</b></sub></a><br /><a href="#bug-asolopovas" title="Bug reports">🐛</a></td>
|
|
156
157
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GrahamJenkins"><img src="https://avatars.githubusercontent.com/u/6607975?v=4" width="100px;" alt=""/><br /><sub><b>Graham Jenkins</b></sub></a><br /><a href="#bug-GrahamJenkins" title="Bug reports">🐛</a></td>
|
|
157
158
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wings1848"><img src="https://avatars.githubusercontent.com/u/120104016?v=4" width="100px;" alt=""/><br /><sub><b>Wings Butterfly</b></sub></a><br /><a href="#bug-wings1848" title="Bug reports">🐛</a></td>
|
|
158
159
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JohannesKlauss"><img src="https://avatars.githubusercontent.com/u/6214415?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Klauss</b></sub></a><br /><a href="#ideas-JohannesKlauss" title="Ideas & Planning">🤔</a></td>
|
|
159
160
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tan-yong-sheng"><img src="https://avatars.githubusercontent.com/u/64836390?v=4" width="100px;" alt=""/><br /><sub><b>Tan Yong Sheng</b></sub></a><br /><a href="#ideas-tan-yong-sheng" title="Ideas & Planning">🤔</a></td>
|
|
160
161
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fmatray"><img src="https://avatars.githubusercontent.com/u/8267716?v=4" width="100px;" alt=""/><br /><sub><b>Frédéric</b></sub></a><br /><a href="#bug-fmatray" title="Bug reports">🐛</a></td>
|
|
161
162
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fuentesjr"><img src="https://avatars.githubusercontent.com/u/9240?v=4" width="100px;" alt=""/><br /><sub><b>Salvador Fuentes Jr</b></sub></a><br /><a href="#bug-fuentesjr" title="Bug reports">🐛</a></td>
|
|
162
|
-
|
|
163
|
-
</tr><br />
|
|
163
|
+
</tr>
|
|
164
164
|
<tr>
|
|
165
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Viterkim"><img src="https://avatars.githubusercontent.com/u/17838985?v=4" width="100px;" alt=""/><br /><sub><b>Viktor</b></sub></a><br /><a href="#bug-Viterkim" title="Bug reports">🐛</a></td>
|
|
165
166
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ortonomy"><img src="https://avatars.githubusercontent.com/u/6688676?v=4" width="100px;" alt=""/><br /><sub><b>Gregory Orton</b></sub></a><br /><a href="#bug-ortonomy" title="Bug reports">🐛</a></td>
|
|
166
167
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jimallen"><img src="https://avatars.githubusercontent.com/u/868773?v=4" width="100px;" alt=""/><br /><sub><b>Jim Allen</b></sub></a><br /><a href="#bug-jimallen" title="Bug reports">🐛</a></td>
|
|
167
168
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/quantfiction"><img src="https://avatars.githubusercontent.com/u/49965454?v=4" width="100px;" alt=""/><br /><sub><b>quantfiction</b></sub></a><br /><a href="#bug-quantfiction" title="Bug reports">🐛</a></td>
|
|
168
169
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Artawower"><img src="https://avatars.githubusercontent.com/u/16963833?v=4" width="100px;" alt=""/><br /><sub><b>Art</b></sub></a><br /><a href="#bug-Artawower" title="Bug reports">🐛</a></td>
|
|
169
170
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chozandrias76"><img src="https://avatars.githubusercontent.com/u/2087677?v=4" width="100px;" alt=""/><br /><sub><b>Colin Swenson-Healey</b></sub></a><br /><a href="#bug-chozandrias76" title="Bug reports">🐛</a></td>
|
|
170
171
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JustlyAI"><img src="https://avatars.githubusercontent.com/u/12634140?v=4" width="100px;" alt=""/><br /><sub><b>Laurent Wiesel</b></sub></a><br /><a href="#bug-JustlyAI" title="Bug reports">🐛</a></td>
|
|
171
|
-
|
|
172
|
-
</tr><br />
|
|
172
|
+
</tr>
|
|
173
173
|
<tr>
|
|
174
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/stark-bit"><img src="https://avatars.githubusercontent.com/u/44064758?v=4" width="100px;" alt=""/><br /><sub><b>Rei Starks</b></sub></a><br /><a href="#bug-stark-bit" title="Bug reports">🐛</a></td>
|
|
174
175
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/robertoecf"><img src="https://avatars.githubusercontent.com/u/54923863?v=4" width="100px;" alt=""/><br /><sub><b>Roberto Freitas</b></sub></a><br /><a href="#bug-robertoecf" title="Bug reports">🐛</a></td>
|
|
175
176
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tmttodd"><img src="https://avatars.githubusercontent.com/u/160077416?v=4" width="100px;" alt=""/><br /><sub><b>tmttodd</b></sub></a><br /><a href="#bug-tmttodd" title="Bug reports">🐛</a></td>
|
|
176
177
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/smaharnav"><img src="https://avatars.githubusercontent.com/u/263328627?v=4" width="100px;" alt=""/><br /><sub><b>smaharnav</b></sub></a><br /><a href="#bug-smaharnav" title="Bug reports">🐛</a></td>
|
|
177
178
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MzaGuille"><img src="https://avatars.githubusercontent.com/u/151482698?v=4" width="100px;" alt=""/><br /><sub><b>Mza.Guille</b></sub></a><br /><a href="#bug-MzaGuille" title="Bug reports">🐛</a></td>
|
|
178
179
|
<td align="center" valign="top" width="14.28%"><a href="http://jasonrimmer.com/"><img src="https://avatars.githubusercontent.com/u/629?v=4" width="100px;" alt=""/><br /><sub><b>Jason Rimmer</b></sub></a><br /><a href="#bug-jrimmer" title="Bug reports">🐛</a></td>
|
|
179
180
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ArthurHeymans"><img src="https://avatars.githubusercontent.com/u/15137817?v=4" width="100px;" alt=""/><br /><sub><b>Arthur Heymans</b></sub></a><br /><a href="#ideas-ArthurHeymans" title="Ideas & Planning">🤔</a></td>
|
|
180
|
-
|
|
181
|
-
</tr><br />
|
|
181
|
+
</tr>
|
|
182
182
|
<tr>
|
|
183
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Blue-B"><img src="https://avatars.githubusercontent.com/u/55532956?v=4" width="100px;" alt=""/><br /><sub><b>Blue-B</b></sub></a><br /><a href="#bug-Blue-B" title="Bug reports">🐛</a></td>
|
|
183
184
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bisko"><img src="https://avatars.githubusercontent.com/u/184938?v=4" width="100px;" alt=""/><br /><sub><b>Biser Perchinkov</b></sub></a><br /><a href="#bug-bisko" title="Bug reports">🐛</a></td>
|
|
184
185
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xificurC"><img src="https://avatars.githubusercontent.com/u/4800719?v=4" width="100px;" alt=""/><br /><sub><b>Peter Nagy</b></sub></a><br /><a href="#bug-xificurC" title="Bug reports">🐛</a></td>
|
|
185
186
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/orest-tokovenko-block"><img src="https://avatars.githubusercontent.com/u/190554467?v=4" width="100px;" alt=""/><br /><sub><b>Orest Tokovenko</b></sub></a><br /><a href="#bug-orest-tokovenko-block" title="Bug reports">🐛</a></td>
|
|
186
187
|
<td align="center" valign="top" width="14.28%"><a href="https://eisterman.dev/"><img src="https://avatars.githubusercontent.com/u/3028953?v=4" width="100px;" alt=""/><br /><sub><b>Federico Pasqua</b></sub></a><br /><a href="#bug-eisterman" title="Bug reports">🐛</a></td>
|
|
187
188
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ymek"><img src="https://avatars.githubusercontent.com/u/300569?v=4" width="100px;" alt=""/><br /><sub><b>Myke Stubbs</b></sub></a><br /><a href="#code-ymek" title="Code">💻</a> <a href="#bug-ymek" title="Bug reports">🐛</a></td>
|
|
188
189
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/black-snow"><img src="https://avatars.githubusercontent.com/u/579733?v=4" width="100px;" alt=""/><br /><sub><b>Ronald</b></sub></a><br /><a href="#bug-black-snow" title="Bug reports">🐛</a></td>
|
|
189
|
-
|
|
190
|
-
</tr><br />
|
|
190
|
+
</tr>
|
|
191
191
|
<tr>
|
|
192
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JSap0914"><img src="https://avatars.githubusercontent.com/u/116227558?v=4" width="100px;" alt=""/><br /><sub><b>JSap0914</b></sub></a><br /><a href="#code-JSap0914" title="Code">💻</a></td>
|
|
192
193
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AngriestBird"><img src="https://avatars.githubusercontent.com/u/12663269?v=4" width="100px;" alt=""/><br /><sub><b>Kenneth McCormick</b></sub></a><br /><a href="#bug-AngriestBird" title="Bug reports">🐛</a></td>
|
|
193
194
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/knthmn"><img src="https://avatars.githubusercontent.com/u/60438008?v=4" width="100px;" alt=""/><br /><sub><b>Kent Hou Man</b></sub></a><br /><a href="#code-knthmn" title="Code">💻</a> <a href="#bug-knthmn" title="Bug reports">🐛</a></td>
|
|
194
195
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronkyriesenbach"><img src="https://avatars.githubusercontent.com/u/12665860?v=4" width="100px;" alt=""/><br /><sub><b>Aaron Ky-Riesenbach</b></sub></a><br /><a href="#bug-aaronkyriesenbach" title="Bug reports">🐛</a></td>
|
|
195
196
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LovelyLoong"><img src="https://avatars.githubusercontent.com/u/54889641?v=4" width="100px;" alt=""/><br /><sub><b>可爱的龙</b></sub></a><br /><a href="#bug-LovelyLoong" title="Bug reports">🐛</a></td>
|
|
196
197
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pppobear"><img src="https://avatars.githubusercontent.com/u/21952175?v=4" width="100px;" alt=""/><br /><sub><b>pppobear</b></sub></a><br /><a href="#bug-pppobear" title="Bug reports">🐛</a></td>
|
|
198
|
+
<td align="center" valign="top" width="14.28%"><a href="https://neumie.dev/"><img src="https://avatars.githubusercontent.com/u/23665482?v=4" width="100px;" alt=""/><br /><sub><b>Jakub Neumann</b></sub></a><br /><a href="#code-neumie" title="Code">💻</a></td>
|
|
197
199
|
</tr>
|
|
198
|
-
</tbody>
|
|
200
|
+
</tbody>
|
|
199
201
|
</table>
|
|
200
202
|
|
|
201
|
-
|
|
202
203
|
<!-- markdownlint-restore -->
|
|
203
204
|
<!-- prettier-ignore-end -->
|
|
204
205
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Exposes both the default export (callers that do `import yaml from …`) and the
|
|
4
4
|
* named `dump`/`load` helpers, sourced from the one import.
|
|
5
5
|
*/
|
|
6
|
-
import yaml from "js-yaml";
|
|
6
|
+
import * as yaml from "js-yaml";
|
|
7
7
|
export default yaml;
|
|
8
8
|
export const dump = yaml.dump;
|
|
9
9
|
export const load = yaml.load;
|
|
@@ -10,9 +10,12 @@ import * as fs from "node:fs";
|
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { loadAstGrepNapi, } from "../../deps/ast-grep-napi.js";
|
|
12
12
|
import { getAstGrepRuleSources, } from "../../sgconfig.js";
|
|
13
|
+
import { logLatency } from "../../latency-logger.js";
|
|
13
14
|
import { hasEslintConfig } from "../../tool-policy.js";
|
|
14
15
|
import { enabledAuxiliaryLspServerIds } from "../auxiliary-lsp.js";
|
|
15
16
|
import { classifyDefect } from "../diagnostic-taxonomy.js";
|
|
17
|
+
import { isAuxiliaryLspAlive } from "../../lsp/index.js";
|
|
18
|
+
import { resolveAstGrepNativeExe } from "../../lsp/wait-policy/index.js";
|
|
16
19
|
import { PRIORITY } from "../priorities.js";
|
|
17
20
|
import { calculateRuleComplexity, isOverlyBroadPattern, isStructuredRule, loadYamlRules, loadYamlRulesFresh, MAX_BLOCKING_RULE_COMPLEXITY, } from "./yaml-rule-parser.js";
|
|
18
21
|
// Lazy load the napi package
|
|
@@ -123,22 +126,26 @@ export function canHandle(filePath) {
|
|
|
123
126
|
/**
|
|
124
127
|
* The TypeScript grammar is a syntactic superset of JavaScript, so a
|
|
125
128
|
* `JavaScript`-tagged rule using generic node kinds (`variable_declarator`,
|
|
126
|
-
* `assignment_expression`, …) still matches against a parsed `.ts
|
|
127
|
-
*
|
|
129
|
+
* `assignment_expression`, …) still matches against a parsed `.ts` root
|
|
130
|
+
* — and vice versa isn't an issue since JS files never parse
|
|
128
131
|
* TS-only syntax, but a `TypeScript`-tagged rule with a plain-JS-compatible
|
|
129
132
|
* body would equally double-fire alongside a `JavaScript` twin on a `.ts`
|
|
130
133
|
* file. Without this, `language:` reads as a real filter but isn't one for
|
|
131
134
|
* ts↔js pairs, so twin rules sharing a base name (e.g. `hardcoded-url` /
|
|
132
135
|
* `hardcoded-url-js`) both match the same construct in the SAME runner
|
|
133
|
-
* invocation (#657).
|
|
134
|
-
*
|
|
136
|
+
* invocation (#657). TSX is deliberately its own grammar here: the primary
|
|
137
|
+
* ast-grep CLI/LSP also treats `tsx` as distinct from `typescript`, so a
|
|
138
|
+
* language-tagged rule only runs on the exact grammar it names. Returns
|
|
139
|
+
* undefined for extensions this scoping doesn't apply to (css/html), where
|
|
140
|
+
* no filtering is added.
|
|
135
141
|
*/
|
|
136
142
|
export function ruleLanguageForFile(filePath) {
|
|
137
143
|
const ext = path.extname(filePath).toLowerCase();
|
|
138
144
|
switch (ext) {
|
|
139
145
|
case ".ts":
|
|
140
|
-
case ".tsx":
|
|
141
146
|
return "typescript";
|
|
147
|
+
case ".tsx":
|
|
148
|
+
return "tsx";
|
|
142
149
|
case ".js":
|
|
143
150
|
case ".jsx":
|
|
144
151
|
return "javascript";
|
|
@@ -213,10 +220,32 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
|
|
|
213
220
|
const maxTotalDiagnostics = options.maxTotalDiagnostics ?? MAX_TOTAL_DIAGNOSTICS;
|
|
214
221
|
const blockingOnly = options.blockingOnly === true;
|
|
215
222
|
const log = options.log;
|
|
223
|
+
const unsupportedLanguageLog = options.unsupportedLanguageLog ?? new Set();
|
|
216
224
|
const diagnostics = [];
|
|
217
225
|
const seenRuleIds = new Set();
|
|
218
226
|
const suppressLinterOverlap = kind === "jsts" && hasEslintConfig(cwd);
|
|
219
227
|
const fileLang = ruleLanguageForFile(filePath);
|
|
228
|
+
// Unsupported-language skips are expected in bulk (every non-jsts rule in the
|
|
229
|
+
// catalog, e.g. ~30 Python rules) — aggregate them into ONE latency-log entry
|
|
230
|
+
// per evaluation instead of per-rule terminal lines (#282 follow-up).
|
|
231
|
+
const newlyUnsupported = new Map();
|
|
232
|
+
const flushUnsupportedRuleSkips = () => {
|
|
233
|
+
if (newlyUnsupported.size === 0)
|
|
234
|
+
return;
|
|
235
|
+
logLatency({
|
|
236
|
+
type: "phase",
|
|
237
|
+
phase: "astgrep_napi_unsupported_rules_skipped",
|
|
238
|
+
filePath,
|
|
239
|
+
durationMs: 0,
|
|
240
|
+
metadata: {
|
|
241
|
+
skippedByLanguage: Object.fromEntries(Array.from(newlyUnsupported.entries(), ([language, ruleIds]) => [
|
|
242
|
+
language,
|
|
243
|
+
{ count: ruleIds.length, ruleIds },
|
|
244
|
+
])),
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
newlyUnsupported.clear();
|
|
248
|
+
};
|
|
220
249
|
// Shared with the raw sgconfig materializer so both surfaces walk the same
|
|
221
250
|
// workspace-rooted sources in the same precedence order.
|
|
222
251
|
const ruleSources = getAstGrepRuleSources(options.projectRoot ?? cwd);
|
|
@@ -233,6 +262,7 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
|
|
|
233
262
|
}
|
|
234
263
|
const duplicates = duplicateRuleIds(rules);
|
|
235
264
|
if (appendDuplicateRuleDiagnostics(diagnostics, seenRuleIds, duplicates, source, filePath, maxTotalDiagnostics)) {
|
|
265
|
+
flushUnsupportedRuleSkips();
|
|
236
266
|
return diagnostics;
|
|
237
267
|
}
|
|
238
268
|
const duplicateSet = new Set(duplicates);
|
|
@@ -258,7 +288,16 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
|
|
|
258
288
|
continue;
|
|
259
289
|
}
|
|
260
290
|
const lang = rule.language?.toLowerCase();
|
|
261
|
-
if (lang &&
|
|
291
|
+
if (lang &&
|
|
292
|
+
lang !== "typescript" &&
|
|
293
|
+
lang !== "tsx" &&
|
|
294
|
+
lang !== "javascript") {
|
|
295
|
+
if (!unsupportedLanguageLog.has(rule.id)) {
|
|
296
|
+
unsupportedLanguageLog.add(rule.id);
|
|
297
|
+
const ids = newlyUnsupported.get(lang) ?? [];
|
|
298
|
+
ids.push(rule.id);
|
|
299
|
+
newlyUnsupported.set(lang, ids);
|
|
300
|
+
}
|
|
262
301
|
continue;
|
|
263
302
|
}
|
|
264
303
|
// Scope TypeScript/JavaScript-tagged rules to the file's actual
|
|
@@ -338,6 +377,7 @@ export function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}
|
|
|
338
377
|
}
|
|
339
378
|
}
|
|
340
379
|
}
|
|
380
|
+
flushUnsupportedRuleSkips();
|
|
341
381
|
return diagnostics;
|
|
342
382
|
}
|
|
343
383
|
// --- Runner Definition ---
|
|
@@ -357,7 +397,20 @@ const astGrepNapiRunner = {
|
|
|
357
397
|
// report against the LSP's `tool: ast-grep` diagnostics. Resume ONLY as the
|
|
358
398
|
// fallback when the binary is absent / can't spawn (Gate B).
|
|
359
399
|
const astGrepLspEnabled = enabledAuxiliaryLspServerIds((f) => ctx.pi?.getFlag?.(f)).includes("ast-grep");
|
|
360
|
-
|
|
400
|
+
// Gate B asks whether the LSP will handle this file, not whether a bare
|
|
401
|
+
// `ast-grep` command happens to be on PATH. The launcher first tries the
|
|
402
|
+
// platform-native package binary, then PATH; mirror that resolution here.
|
|
403
|
+
// A live client covers the already-warm case, while a resolvable binary
|
|
404
|
+
// covers the cold case before the LSP has spawned for this root.
|
|
405
|
+
const astGrepLspAlive = astGrepLspEnabled
|
|
406
|
+
? await isAuxiliaryLspAlive("ast-grep", ctx.filePath)
|
|
407
|
+
: false;
|
|
408
|
+
let astGrepBinaryResolvable = false;
|
|
409
|
+
if (astGrepLspEnabled && !astGrepLspAlive) {
|
|
410
|
+
astGrepBinaryResolvable =
|
|
411
|
+
Boolean(resolveAstGrepNativeExe()) || (await ctx.hasTool("ast-grep"));
|
|
412
|
+
}
|
|
413
|
+
if (astGrepLspEnabled && (astGrepLspAlive || astGrepBinaryResolvable)) {
|
|
361
414
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
362
415
|
}
|
|
363
416
|
const sgModule = await loadSg();
|
|
@@ -226,8 +226,19 @@ export async function recordLspChild(entry) {
|
|
|
226
226
|
}
|
|
227
227
|
await writeRegistryAsync(file);
|
|
228
228
|
}
|
|
229
|
+
// LSP client shutdown intentionally does not await registry removal (the
|
|
230
|
+
// process-exiting path must stay non-blocking), but concurrent removals still
|
|
231
|
+
// need to serialize their read-modify-write sequence or siblings can be lost
|
|
232
|
+
// to last-writer-wins. Process kills remain fully concurrent; this queue only
|
|
233
|
+
// covers the small best-effort registry mutation.
|
|
234
|
+
let lspChildRemovalTail = Promise.resolve();
|
|
229
235
|
/** Remove an LSP child (by pid) from this process's entry. */
|
|
230
|
-
export
|
|
236
|
+
export function removeLspChild(pid) {
|
|
237
|
+
const removal = lspChildRemovalTail.then(() => removeLspChildNow(pid));
|
|
238
|
+
lspChildRemovalTail = removal.catch(() => { });
|
|
239
|
+
return removal;
|
|
240
|
+
}
|
|
241
|
+
async function removeLspChildNow(pid) {
|
|
231
242
|
if (!isInstanceRegistryEnabled())
|
|
232
243
|
return;
|
|
233
244
|
const selfPid = process.pid;
|
|
@@ -440,7 +440,16 @@ export class LSPService {
|
|
|
440
440
|
notifyWriteBackpressureStreak = new Map();
|
|
441
441
|
/** True after shutdown() has been called; blocks new operations */
|
|
442
442
|
isDestroyed = false;
|
|
443
|
-
|
|
443
|
+
/**
|
|
444
|
+
* #850: teardown completion for every singleton generation retired before
|
|
445
|
+
* this service was published. Only replacement services receive one; direct
|
|
446
|
+
* `new LSPService()` callers and the first singleton generation stay hot-path
|
|
447
|
+
* identical. Cleared after the first completed wait so warm reuse never pays
|
|
448
|
+
* a permanent promise/microtask tax.
|
|
449
|
+
*/
|
|
450
|
+
generationHandoff;
|
|
451
|
+
constructor(generationHandoff) {
|
|
452
|
+
this.generationHandoff = generationHandoff;
|
|
444
453
|
this.state = {
|
|
445
454
|
clients: new Map(),
|
|
446
455
|
servers: new Map(),
|
|
@@ -738,9 +747,38 @@ export class LSPService {
|
|
|
738
747
|
}
|
|
739
748
|
return undefined;
|
|
740
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* Read-only liveness check for one server/file pair. Unlike
|
|
752
|
+
* `getClientForFile`, this never creates or warms a client; it only resolves
|
|
753
|
+
* the server's root and checks the already-connected client map.
|
|
754
|
+
*/
|
|
755
|
+
async isServerAliveForFile(serverId, filePath) {
|
|
756
|
+
if (this.checkDestroyed())
|
|
757
|
+
return false;
|
|
758
|
+
for (const server of getServersForFileWithConfig(filePath)) {
|
|
759
|
+
if (server.id !== serverId)
|
|
760
|
+
continue;
|
|
761
|
+
const root = await server.root(filePath);
|
|
762
|
+
if (!root)
|
|
763
|
+
continue;
|
|
764
|
+
const key = `${server.id}:${normalizeMapKey(root)}`;
|
|
765
|
+
if (this.state.clients.get(key)?.isAlive())
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
741
770
|
async ensureClientForServer(filePath, server) {
|
|
771
|
+
const handoff = this.generationHandoff;
|
|
772
|
+
if (handoff) {
|
|
773
|
+
await handoff;
|
|
774
|
+
if (this.generationHandoff === handoff) {
|
|
775
|
+
this.generationHandoff = undefined;
|
|
776
|
+
}
|
|
777
|
+
if (this.checkDestroyed())
|
|
778
|
+
return undefined;
|
|
779
|
+
}
|
|
742
780
|
const root = await server.root(filePath);
|
|
743
|
-
if (!root)
|
|
781
|
+
if (!root || this.checkDestroyed())
|
|
744
782
|
return undefined;
|
|
745
783
|
const allowInstall = this.shouldAllowInstall(filePath, root);
|
|
746
784
|
const normalizedRoot = normalizeMapKey(root);
|
|
@@ -831,6 +869,11 @@ export class LSPService {
|
|
|
831
869
|
if (inFlight) {
|
|
832
870
|
return inFlight;
|
|
833
871
|
}
|
|
872
|
+
// `server.root()` and a dead client's shutdown above are both async. A
|
|
873
|
+
// reset during either gap may have completed without seeing this request in
|
|
874
|
+
// state.inFlight; never let that retired generation start a late spawn.
|
|
875
|
+
if (this.checkDestroyed())
|
|
876
|
+
return undefined;
|
|
834
877
|
const spawnPromise = this.spawnClient(server, root, key, filePath, allowInstall);
|
|
835
878
|
this.state.inFlight.set(key, spawnPromise);
|
|
836
879
|
try {
|
|
@@ -3186,14 +3229,13 @@ export class LSPService {
|
|
|
3186
3229
|
processExiting: !!options.processExiting,
|
|
3187
3230
|
},
|
|
3188
3231
|
});
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
}
|
|
3232
|
+
// Start every client teardown before awaiting any of them. A non-fast
|
|
3233
|
+
// process-tree kill can spend its grace period per client, so awaiting in
|
|
3234
|
+
// map order makes the reset tail O(clientCount * grace) instead of the
|
|
3235
|
+
// maximum individual teardown. allSettled preserves the per-client
|
|
3236
|
+
// best-effort contract: one failure must not prevent other clients from
|
|
3237
|
+
// finishing, and the caller still waits for every teardown to settle.
|
|
3238
|
+
await Promise.allSettled(Array.from(this.state.clients.values(), (client) => Promise.resolve().then(() => client.shutdown(options))));
|
|
3197
3239
|
this.state.clients.clear();
|
|
3198
3240
|
this.state.broken.clear();
|
|
3199
3241
|
this.workspaceProbeLogged.clear();
|
|
@@ -3245,17 +3287,47 @@ export class LSPService {
|
|
|
3245
3287
|
}
|
|
3246
3288
|
// --- Singleton Instance ---
|
|
3247
3289
|
let globalLSPService = null;
|
|
3290
|
+
/**
|
|
3291
|
+
* #850: all singleton generations whose teardown is still pending. A new
|
|
3292
|
+
* generation may be allocated synchronously, but its first spawn waits on this
|
|
3293
|
+
* handoff so two generations can never own the same server/root concurrently.
|
|
3294
|
+
*/
|
|
3295
|
+
let globalLSPGenerationHandoff;
|
|
3248
3296
|
export function getLSPService() {
|
|
3249
3297
|
if (!globalLSPService) {
|
|
3250
|
-
globalLSPService = new LSPService();
|
|
3298
|
+
globalLSPService = new LSPService(globalLSPGenerationHandoff);
|
|
3251
3299
|
}
|
|
3252
3300
|
return globalLSPService;
|
|
3253
3301
|
}
|
|
3302
|
+
/**
|
|
3303
|
+
* Cross-layer liveness seam for dispatch-side auxiliary gates. This is a
|
|
3304
|
+
* liveness read only: it does not spawn, wait for initialization, or probe a
|
|
3305
|
+
* binary.
|
|
3306
|
+
*/
|
|
3307
|
+
export async function isAuxiliaryLspAlive(serverId, filePath) {
|
|
3308
|
+
return getLSPService().isServerAliveForFile(serverId, filePath);
|
|
3309
|
+
}
|
|
3254
3310
|
export function resetLSPService(options = {}) {
|
|
3255
|
-
|
|
3256
|
-
globalLSPService.shutdown(options).catch(() => { });
|
|
3257
|
-
}
|
|
3311
|
+
const retiringService = globalLSPService;
|
|
3258
3312
|
globalLSPService = null;
|
|
3313
|
+
if (!retiringService)
|
|
3314
|
+
return;
|
|
3315
|
+
// shutdown() marks the service destroyed synchronously before its first
|
|
3316
|
+
// await. Include both that teardown and every earlier pending generation:
|
|
3317
|
+
// repeated resets may retire a replacement that is itself still waiting on
|
|
3318
|
+
// its predecessor. allSettled keeps teardown best-effort without ever
|
|
3319
|
+
// rejecting (and therefore permanently poisoning) the next generation.
|
|
3320
|
+
const teardown = retiringService.shutdown(options);
|
|
3321
|
+
const pending = globalLSPGenerationHandoff
|
|
3322
|
+
? [globalLSPGenerationHandoff, teardown]
|
|
3323
|
+
: [teardown];
|
|
3324
|
+
const handoff = Promise.allSettled(pending).then(() => undefined);
|
|
3325
|
+
globalLSPGenerationHandoff = handoff;
|
|
3326
|
+
void handoff.then(() => {
|
|
3327
|
+
if (globalLSPGenerationHandoff === handoff) {
|
|
3328
|
+
globalLSPGenerationHandoff = undefined;
|
|
3329
|
+
}
|
|
3330
|
+
});
|
|
3259
3331
|
}
|
|
3260
3332
|
/**
|
|
3261
3333
|
* Test-only: exposes the async workspace-diagnostics file walk so its
|