@mrclrchtr/supi-code-intelligence 4.9.0 → 4.10.0
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/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +155 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +70 -34
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +104 -5
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +10 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +4 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +106 -2
- package/package.json +5 -5
- package/src/analysis/refactor/apply.ts +14 -19
- package/src/analysis/refactor/position.ts +47 -0
- package/src/analysis/refactor/safety.ts +18 -10
- package/src/analysis/search/ast-scan-timing.ts +0 -2
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// and notification/request dispatching through vscode-jsonrpc's MessageConnection.
|
|
4
4
|
|
|
5
5
|
import type { Readable, Writable } from "node:stream";
|
|
6
|
+
import { startDebugTimer } from "@mrclrchtr/supi-core/debug";
|
|
6
7
|
import {
|
|
7
8
|
CancellationTokenSource,
|
|
8
9
|
createMessageConnection,
|
|
@@ -20,6 +21,9 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
20
21
|
export type NotificationHandler = (method: string, params: unknown) => void;
|
|
21
22
|
export type RequestHandler = (method: string, params: unknown) => Promise<unknown> | unknown;
|
|
22
23
|
|
|
24
|
+
type RequestMethodClass = "diagnostic" | "lifecycle" | "other" | "refactor" | "semantic";
|
|
25
|
+
type RequestOutcome = "cancelled" | "completed" | "failed" | "timed-out";
|
|
26
|
+
|
|
23
27
|
/** Re-export ResponseError so callers don't need a separate vscode-jsonrpc import. */
|
|
24
28
|
const JsonRpcRequestError = ResponseError;
|
|
25
29
|
|
|
@@ -83,14 +87,23 @@ export class JsonRpcClient {
|
|
|
83
87
|
params?: unknown,
|
|
84
88
|
options?: { timeoutMs?: number },
|
|
85
89
|
): Promise<unknown> {
|
|
90
|
+
const timeoutMs = options?.timeoutMs ?? this.timeoutMs;
|
|
91
|
+
const methodClass = classifyRequestMethod(method);
|
|
92
|
+
const timer = startDebugTimer();
|
|
86
93
|
if (this.closed || !this.connection) {
|
|
94
|
+
recordRequestTiming(timer, {
|
|
95
|
+
methodClass,
|
|
96
|
+
outcome: "cancelled",
|
|
97
|
+
timeoutMs,
|
|
98
|
+
timedOut: false,
|
|
99
|
+
cancelled: true,
|
|
100
|
+
});
|
|
87
101
|
return Promise.reject(new Error("JSON-RPC client is closed"));
|
|
88
102
|
}
|
|
89
103
|
|
|
90
|
-
const timeoutMs = options?.timeoutMs ?? this.timeoutMs;
|
|
91
104
|
const tokenSource = new CancellationTokenSource();
|
|
92
|
-
|
|
93
105
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
106
|
+
let timedOut = false;
|
|
94
107
|
const timeoutError = new Error(`Request ${method} timed out after ${timeoutMs}ms`);
|
|
95
108
|
|
|
96
109
|
const request = this.connection.sendRequest(method, params, tokenSource.token);
|
|
@@ -106,13 +119,43 @@ export class JsonRpcClient {
|
|
|
106
119
|
request,
|
|
107
120
|
new Promise<never>((_resolve, reject) => {
|
|
108
121
|
timeout = setTimeout(() => {
|
|
122
|
+
timedOut = true;
|
|
109
123
|
tokenSource.cancel();
|
|
110
124
|
reject(timeoutError);
|
|
111
125
|
}, timeoutMs);
|
|
112
126
|
}),
|
|
113
|
-
])
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
])
|
|
128
|
+
.then(
|
|
129
|
+
(result) => {
|
|
130
|
+
recordRequestTiming(timer, {
|
|
131
|
+
methodClass,
|
|
132
|
+
outcome: "completed",
|
|
133
|
+
timeoutMs,
|
|
134
|
+
timedOut: false,
|
|
135
|
+
cancelled: false,
|
|
136
|
+
});
|
|
137
|
+
return result;
|
|
138
|
+
},
|
|
139
|
+
(error: unknown) => {
|
|
140
|
+
const cancelled = timedOut || this.closed || isCancellationError(error);
|
|
141
|
+
const outcome: RequestOutcome = timedOut
|
|
142
|
+
? "timed-out"
|
|
143
|
+
: cancelled
|
|
144
|
+
? "cancelled"
|
|
145
|
+
: "failed";
|
|
146
|
+
recordRequestTiming(timer, {
|
|
147
|
+
methodClass,
|
|
148
|
+
outcome,
|
|
149
|
+
timeoutMs,
|
|
150
|
+
timedOut,
|
|
151
|
+
cancelled,
|
|
152
|
+
});
|
|
153
|
+
throw error;
|
|
154
|
+
},
|
|
155
|
+
)
|
|
156
|
+
.finally(() => {
|
|
157
|
+
if (timeout !== undefined) clearTimeout(timeout);
|
|
158
|
+
});
|
|
116
159
|
|
|
117
160
|
// Prevent unhandled rejection when dispose() cancels requests
|
|
118
161
|
promise.catch(() => {});
|
|
@@ -142,3 +185,59 @@ export class JsonRpcClient {
|
|
|
142
185
|
}
|
|
143
186
|
}
|
|
144
187
|
}
|
|
188
|
+
|
|
189
|
+
const SEMANTIC_REQUESTS = new Set([
|
|
190
|
+
"textDocument/definition",
|
|
191
|
+
"textDocument/documentSymbol",
|
|
192
|
+
"textDocument/hover",
|
|
193
|
+
"textDocument/implementation",
|
|
194
|
+
"textDocument/references",
|
|
195
|
+
"workspace/symbol",
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
/** Classify requests into bounded groups without retaining the raw method. */
|
|
199
|
+
function classifyRequestMethod(method: string): RequestMethodClass {
|
|
200
|
+
if (method === "initialize" || method === "shutdown") return "lifecycle";
|
|
201
|
+
if (method === "textDocument/diagnostic" || method === "workspace/diagnostic") {
|
|
202
|
+
return "diagnostic";
|
|
203
|
+
}
|
|
204
|
+
if (method === "textDocument/codeAction" || method === "textDocument/rename") {
|
|
205
|
+
return "refactor";
|
|
206
|
+
}
|
|
207
|
+
return SEMANTIC_REQUESTS.has(method) ? "semantic" : "other";
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function isCancellationError(error: unknown): boolean {
|
|
211
|
+
if (!(error instanceof Error)) return false;
|
|
212
|
+
const code = (error as Error & { code?: unknown }).code;
|
|
213
|
+
return (
|
|
214
|
+
code === -32_800 ||
|
|
215
|
+
code === -32_802 ||
|
|
216
|
+
error.name === "CancellationError" ||
|
|
217
|
+
/\bcancell?ed\b/i.test(error.message)
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
interface RequestTimingObservation {
|
|
222
|
+
readonly methodClass: RequestMethodClass;
|
|
223
|
+
readonly outcome: RequestOutcome;
|
|
224
|
+
readonly timeoutMs: number;
|
|
225
|
+
readonly timedOut: boolean;
|
|
226
|
+
readonly cancelled: boolean;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function recordRequestTiming(
|
|
230
|
+
timer: ReturnType<typeof startDebugTimer>,
|
|
231
|
+
observation: RequestTimingObservation,
|
|
232
|
+
): void {
|
|
233
|
+
timer.finish(
|
|
234
|
+
() => ({
|
|
235
|
+
source: "lsp",
|
|
236
|
+
level: "debug",
|
|
237
|
+
category: "request.timing",
|
|
238
|
+
message: `LSP ${observation.methodClass} request ${observation.outcome}`,
|
|
239
|
+
data: { ...observation },
|
|
240
|
+
}),
|
|
241
|
+
"request",
|
|
242
|
+
);
|
|
243
|
+
}
|
|
@@ -96,6 +96,16 @@ if (state.kind === "ready") {
|
|
|
96
96
|
}
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
## Structural performance baseline
|
|
100
|
+
|
|
101
|
+
Run the stable outline fixture on the same machine and dependency versions:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pnpm --filter @mrclrchtr/supi-tree-sitter bench:structural
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The benchmark reports a cold session parser baseline and a repeated parser baseline. Debug capture is active, so the baseline includes the internal timing observation cost. The benchmark records measurements but does not set a pass or fail threshold.
|
|
108
|
+
|
|
99
109
|
## Source
|
|
100
110
|
|
|
101
111
|
- `src/api.ts` — public library entrypoint
|
|
@@ -46,13 +46,21 @@ export interface DebugTimer {
|
|
|
46
46
|
* names are accumulated. Event data reserves the `timing` field. When Debug is
|
|
47
47
|
* disabled at start, this returns a no-op timer and does not read the clock.
|
|
48
48
|
* Pass a factory to `finish()` to avoid event-data construction when disabled.
|
|
49
|
+
* Clock, event-construction, and registry failures are isolated from the
|
|
50
|
+
* measured operation and make the timer a no-op.
|
|
49
51
|
*/
|
|
50
52
|
export function startDebugTimer(options: DebugTimerOptions = {}): DebugTimer {
|
|
51
53
|
if (!isDebugRegistryEnabled()) return DISABLED_DEBUG_TIMER;
|
|
52
54
|
const now = options.now ?? performance.now.bind(performance);
|
|
53
|
-
|
|
55
|
+
let startedAt: number;
|
|
56
|
+
try {
|
|
57
|
+
startedAt = now();
|
|
58
|
+
} catch {
|
|
59
|
+
return DISABLED_DEBUG_TIMER;
|
|
60
|
+
}
|
|
54
61
|
let previousAt = startedAt;
|
|
55
62
|
let finished = false;
|
|
63
|
+
let failed = false;
|
|
56
64
|
const phases = new Map<string, number>();
|
|
57
65
|
|
|
58
66
|
const markAt = (phase: string, current: number): void => {
|
|
@@ -65,30 +73,35 @@ export function startDebugTimer(options: DebugTimerOptions = {}): DebugTimer {
|
|
|
65
73
|
return {
|
|
66
74
|
enabled: true,
|
|
67
75
|
mark(phase) {
|
|
68
|
-
if (finished) return;
|
|
69
|
-
|
|
76
|
+
if (finished || failed) return;
|
|
77
|
+
try {
|
|
78
|
+
markAt(phase, now());
|
|
79
|
+
} catch {
|
|
80
|
+
failed = true;
|
|
81
|
+
}
|
|
70
82
|
},
|
|
71
83
|
finish(input, finalPhase) {
|
|
72
|
-
if (finished) return null;
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
if (finished || failed) return null;
|
|
85
|
+
finished = true;
|
|
86
|
+
try {
|
|
87
|
+
if (!isDebugRegistryEnabled()) return null;
|
|
88
|
+
const completedAt = now();
|
|
89
|
+
if (finalPhase) markAt(finalPhase, completedAt);
|
|
90
|
+
const phasesMs = Object.fromEntries(
|
|
91
|
+
[...phases.entries()].map(([name, value]) => [name, duration(value)]),
|
|
92
|
+
);
|
|
93
|
+
const timing: DebugTiming = {
|
|
94
|
+
durationMs: duration(completedAt - startedAt),
|
|
95
|
+
phasesMs,
|
|
96
|
+
};
|
|
97
|
+
const eventInput = typeof input === "function" ? input() : input;
|
|
98
|
+
return recordDebugEvent({
|
|
99
|
+
...eventInput,
|
|
100
|
+
data: { ...eventInput.data, timing },
|
|
101
|
+
});
|
|
102
|
+
} catch {
|
|
75
103
|
return null;
|
|
76
104
|
}
|
|
77
|
-
const completedAt = now();
|
|
78
|
-
if (finalPhase) markAt(finalPhase, completedAt);
|
|
79
|
-
finished = true;
|
|
80
|
-
const phasesMs = Object.fromEntries(
|
|
81
|
-
[...phases.entries()].map(([name, value]) => [name, duration(value)]),
|
|
82
|
-
);
|
|
83
|
-
const timing: DebugTiming = {
|
|
84
|
-
durationMs: duration(completedAt - startedAt),
|
|
85
|
-
phasesMs,
|
|
86
|
-
};
|
|
87
|
-
const eventInput = typeof input === "function" ? input() : input;
|
|
88
|
-
return recordDebugEvent({
|
|
89
|
-
...eventInput,
|
|
90
|
-
data: { ...eventInput.data, timing },
|
|
91
|
-
});
|
|
92
105
|
},
|
|
93
106
|
};
|
|
94
107
|
}
|