@formspec/ts-plugin 0.1.0-alpha.21 → 0.1.0-alpha.23
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/README.md +56 -0
- package/dist/__tests__/downstream-authoring-host.test.d.ts +2 -0
- package/dist/__tests__/downstream-authoring-host.test.d.ts.map +1 -0
- package/dist/__tests__/semantic-service.test.d.ts +2 -0
- package/dist/__tests__/semantic-service.test.d.ts.map +1 -0
- package/dist/index.cjs +486 -277
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +483 -271
- package/dist/index.js.map +1 -1
- package/dist/perf-utils.d.ts +3 -0
- package/dist/perf-utils.d.ts.map +1 -0
- package/dist/reference-host-example.d.ts +14 -0
- package/dist/reference-host-example.d.ts.map +1 -0
- package/dist/semantic-service.d.ts +116 -0
- package/dist/semantic-service.d.ts.map +1 -0
- package/dist/service.d.ts +38 -34
- package/dist/service.d.ts.map +1 -1
- package/dist/ts-plugin-alpha.d.ts +470 -0
- package/dist/ts-plugin-beta.d.ts +470 -0
- package/dist/ts-plugin-internal.d.ts +470 -0
- package/dist/ts-plugin.d.ts +458 -0
- package/dist/workspace.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,36 +1,31 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION as FORMSPEC_ANALYSIS_PROTOCOL_VERSION4,
|
|
4
|
+
FORMSPEC_ANALYSIS_SCHEMA_VERSION as FORMSPEC_ANALYSIS_SCHEMA_VERSION2
|
|
5
|
+
} from "@formspec/analysis/protocol";
|
|
6
|
+
|
|
1
7
|
// src/service.ts
|
|
2
8
|
import fs from "fs/promises";
|
|
3
9
|
import net from "net";
|
|
4
10
|
import "typescript";
|
|
5
11
|
import {
|
|
6
|
-
FORMSPEC_ANALYSIS_PROTOCOL_VERSION as
|
|
7
|
-
computeFormSpecTextHash,
|
|
12
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION as FORMSPEC_ANALYSIS_PROTOCOL_VERSION3,
|
|
8
13
|
isFormSpecSemanticQuery
|
|
9
14
|
} from "@formspec/analysis/protocol";
|
|
10
|
-
import
|
|
11
|
-
buildFormSpecAnalysisFileSnapshot,
|
|
12
|
-
createFormSpecPerformanceRecorder,
|
|
13
|
-
findDeclarationForCommentOffset,
|
|
14
|
-
getCommentHoverInfoAtOffset,
|
|
15
|
-
getSemanticCommentCompletionContextAtOffset,
|
|
16
|
-
getFormSpecPerformanceNow,
|
|
17
|
-
getSubjectType,
|
|
18
|
-
optionalMeasure,
|
|
19
|
-
resolveDeclarationPlacement,
|
|
20
|
-
serializeCompletionContext,
|
|
21
|
-
serializeHoverInfo
|
|
22
|
-
} from "@formspec/analysis/internal";
|
|
15
|
+
import "@formspec/analysis/internal";
|
|
23
16
|
|
|
24
17
|
// src/workspace.ts
|
|
25
18
|
import os from "os";
|
|
26
19
|
import path from "path";
|
|
27
20
|
import {
|
|
28
21
|
FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
29
|
-
FORMSPEC_ANALYSIS_SCHEMA_VERSION
|
|
22
|
+
FORMSPEC_ANALYSIS_SCHEMA_VERSION
|
|
23
|
+
} from "@formspec/analysis/protocol";
|
|
24
|
+
import {
|
|
30
25
|
getFormSpecManifestPath,
|
|
31
26
|
getFormSpecWorkspaceId,
|
|
32
27
|
getFormSpecWorkspaceRuntimeDirectory
|
|
33
|
-
} from "@formspec/analysis/
|
|
28
|
+
} from "@formspec/analysis/internal";
|
|
34
29
|
function getFormSpecWorkspaceRuntimePaths(workspaceRoot, platform = process.platform, userScope = getFormSpecUserScope()) {
|
|
35
30
|
const workspaceId = getFormSpecWorkspaceId(workspaceRoot);
|
|
36
31
|
const runtimeDirectory = getFormSpecWorkspaceRuntimeDirectory(workspaceRoot);
|
|
@@ -79,112 +74,154 @@ function sanitizeScopeSegment(value) {
|
|
|
79
74
|
return sanitized.length > 0 ? sanitized : "formspec";
|
|
80
75
|
}
|
|
81
76
|
|
|
77
|
+
// src/semantic-service.ts
|
|
78
|
+
import "typescript";
|
|
79
|
+
import {
|
|
80
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION as FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
81
|
+
computeFormSpecTextHash
|
|
82
|
+
} from "@formspec/analysis/protocol";
|
|
83
|
+
import {
|
|
84
|
+
buildFormSpecAnalysisFileSnapshot,
|
|
85
|
+
createFormSpecPerformanceRecorder,
|
|
86
|
+
findDeclarationForCommentOffset,
|
|
87
|
+
getCommentHoverInfoAtOffset,
|
|
88
|
+
getSemanticCommentCompletionContextAtOffset,
|
|
89
|
+
getFormSpecPerformanceNow,
|
|
90
|
+
getSubjectType,
|
|
91
|
+
optionalMeasure,
|
|
92
|
+
resolveDeclarationPlacement,
|
|
93
|
+
serializeCompletionContext,
|
|
94
|
+
serializeHoverInfo
|
|
95
|
+
} from "@formspec/analysis/internal";
|
|
96
|
+
|
|
82
97
|
// src/constants.ts
|
|
83
98
|
var FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES = 256 * 1024;
|
|
84
99
|
var FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS = 3e4;
|
|
85
100
|
var FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS = 50;
|
|
86
101
|
var FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS = 250;
|
|
87
|
-
var FORM_SPEC_PLUGIN_PERFORMANCE_EVENT = {
|
|
88
|
-
handleQuery: "plugin.handleQuery"
|
|
89
|
-
};
|
|
90
102
|
|
|
91
|
-
// src/
|
|
92
|
-
|
|
103
|
+
// src/perf-utils.ts
|
|
104
|
+
function formatPerformanceEvent(event) {
|
|
105
|
+
const detailEntries = Object.entries(event.detail ?? {}).map(([key, value]) => `${key}=${String(value)}`).join(" ");
|
|
106
|
+
return `${event.durationMs.toFixed(1)}ms ${event.name}${detailEntries === "" ? "" : ` ${detailEntries}`}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/semantic-service.ts
|
|
110
|
+
var STATS_ONLY_EVENT_NAMES = /* @__PURE__ */ new Set([
|
|
111
|
+
"analysis.syntheticCheckBatch.cacheHit",
|
|
112
|
+
"analysis.narrowSyntheticCheckBatch.cacheHit",
|
|
113
|
+
"analysis.syntheticCheckBatch.cacheMiss",
|
|
114
|
+
"analysis.narrowSyntheticCheckBatch.cacheMiss",
|
|
115
|
+
"analysis.syntheticCheckBatch.createProgram",
|
|
116
|
+
"analysis.narrowSyntheticCheckBatch.createProgram"
|
|
117
|
+
]);
|
|
118
|
+
var StatsOnlyPerformanceRecorder = class {
|
|
119
|
+
mutableEvents = [];
|
|
120
|
+
get events() {
|
|
121
|
+
return this.mutableEvents;
|
|
122
|
+
}
|
|
123
|
+
measure(name, detail, callback) {
|
|
124
|
+
const result = callback();
|
|
125
|
+
if (STATS_ONLY_EVENT_NAMES.has(name)) {
|
|
126
|
+
this.mutableEvents.push({
|
|
127
|
+
name,
|
|
128
|
+
durationMs: 0,
|
|
129
|
+
...detail === void 0 ? {} : { detail }
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
record(event) {
|
|
135
|
+
if (STATS_ONLY_EVENT_NAMES.has(event.name)) {
|
|
136
|
+
this.mutableEvents.push(event);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var FormSpecSemanticService = class {
|
|
93
141
|
constructor(options) {
|
|
94
142
|
this.options = options;
|
|
95
|
-
this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);
|
|
96
|
-
this.manifest = createFormSpecAnalysisManifest(
|
|
97
|
-
options.workspaceRoot,
|
|
98
|
-
options.typescriptVersion,
|
|
99
|
-
Date.now()
|
|
100
|
-
);
|
|
101
143
|
}
|
|
102
|
-
manifest;
|
|
103
|
-
runtimePaths;
|
|
104
144
|
snapshotCache = /* @__PURE__ */ new Map();
|
|
105
145
|
refreshTimers = /* @__PURE__ */ new Map();
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
146
|
+
stats = {
|
|
147
|
+
queryTotals: {
|
|
148
|
+
completion: 0,
|
|
149
|
+
hover: 0,
|
|
150
|
+
diagnostics: 0,
|
|
151
|
+
fileSnapshot: 0
|
|
152
|
+
},
|
|
153
|
+
queryPathTotals: {
|
|
154
|
+
diagnostics: { cold: 0, warm: 0 },
|
|
155
|
+
fileSnapshot: { cold: 0, warm: 0 }
|
|
156
|
+
},
|
|
157
|
+
fileSnapshotCacheHits: 0,
|
|
158
|
+
fileSnapshotCacheMisses: 0,
|
|
159
|
+
syntheticBatchCacheHits: 0,
|
|
160
|
+
syntheticBatchCacheMisses: 0,
|
|
161
|
+
syntheticCompileCount: 0,
|
|
162
|
+
syntheticCompileApplications: 0
|
|
163
|
+
};
|
|
164
|
+
/** Resolves semantic completion context for a comment cursor position. */
|
|
165
|
+
getCompletionContext(filePath, offset) {
|
|
166
|
+
this.stats.queryTotals.completion += 1;
|
|
167
|
+
return this.runMeasured(
|
|
168
|
+
"semantic.getCompletionContext",
|
|
169
|
+
{ filePath, offset },
|
|
170
|
+
(performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
|
|
171
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
172
|
+
sourceHash: context.sourceHash,
|
|
173
|
+
context: serializeCompletionContext(
|
|
174
|
+
getSemanticCommentCompletionContextAtOffset(context.sourceFile.text, offset, {
|
|
175
|
+
checker: context.checker,
|
|
176
|
+
...context.placement === null ? {} : { placement: context.placement },
|
|
177
|
+
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
178
|
+
})
|
|
179
|
+
)
|
|
180
|
+
}))
|
|
181
|
+
);
|
|
109
182
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
const newlineIndex = buffer.indexOf("\n");
|
|
141
|
-
if (newlineIndex < 0) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const payload = buffer.slice(0, newlineIndex);
|
|
145
|
-
const remaining = buffer.slice(newlineIndex + 1);
|
|
146
|
-
if (remaining.trim().length > 0) {
|
|
147
|
-
this.options.logger?.info(
|
|
148
|
-
`[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
buffer = remaining;
|
|
152
|
-
this.respondToSocket(socket, payload);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
await new Promise((resolve, reject) => {
|
|
156
|
-
const handleError = (error) => {
|
|
157
|
-
reject(error);
|
|
183
|
+
/** Resolves semantic hover payload for a comment cursor position. */
|
|
184
|
+
getHover(filePath, offset) {
|
|
185
|
+
this.stats.queryTotals.hover += 1;
|
|
186
|
+
return this.runMeasured(
|
|
187
|
+
"semantic.getHover",
|
|
188
|
+
{ filePath, offset },
|
|
189
|
+
(performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
|
|
190
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
191
|
+
sourceHash: context.sourceHash,
|
|
192
|
+
hover: serializeHoverInfo(
|
|
193
|
+
getCommentHoverInfoAtOffset(context.sourceFile.text, offset, {
|
|
194
|
+
checker: context.checker,
|
|
195
|
+
...context.placement === null ? {} : { placement: context.placement },
|
|
196
|
+
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
197
|
+
})
|
|
198
|
+
)
|
|
199
|
+
}))
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
/** Returns canonical FormSpec diagnostics for a file in the current host program. */
|
|
203
|
+
getDiagnostics(filePath) {
|
|
204
|
+
this.stats.queryTotals.diagnostics += 1;
|
|
205
|
+
return this.runMeasured("semantic.getDiagnostics", { filePath }, (performance2) => {
|
|
206
|
+
const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance2);
|
|
207
|
+
this.recordQueryPath("diagnostics", cacheState);
|
|
208
|
+
return {
|
|
209
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
210
|
+
sourceHash: snapshot.sourceHash,
|
|
211
|
+
diagnostics: snapshot.diagnostics
|
|
158
212
|
};
|
|
159
|
-
this.server?.once("error", handleError);
|
|
160
|
-
this.server?.listen(this.runtimePaths.endpoint.address, () => {
|
|
161
|
-
this.server?.off("error", handleError);
|
|
162
|
-
resolve();
|
|
163
|
-
});
|
|
164
213
|
});
|
|
165
|
-
await this.writeManifest();
|
|
166
214
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (server?.listening === true) {
|
|
176
|
-
await new Promise((resolve, reject) => {
|
|
177
|
-
server.close((error) => {
|
|
178
|
-
if (error === void 0) {
|
|
179
|
-
resolve();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
reject(error);
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
await this.cleanupRuntimeArtifacts();
|
|
215
|
+
/** Returns the full serialized semantic snapshot for a file. */
|
|
216
|
+
getFileSnapshot(filePath) {
|
|
217
|
+
this.stats.queryTotals.fileSnapshot += 1;
|
|
218
|
+
return this.runMeasured("semantic.getFileSnapshot", { filePath }, (performance2) => {
|
|
219
|
+
const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance2);
|
|
220
|
+
this.recordQueryPath("fileSnapshot", cacheState);
|
|
221
|
+
return snapshot;
|
|
222
|
+
});
|
|
187
223
|
}
|
|
224
|
+
/** Schedules a debounced background refresh for the file snapshot cache. */
|
|
188
225
|
scheduleSnapshotRefresh(filePath) {
|
|
189
226
|
const existing = this.refreshTimers.get(filePath);
|
|
190
227
|
if (existing !== void 0) {
|
|
@@ -192,7 +229,7 @@ var FormSpecPluginService = class {
|
|
|
192
229
|
}
|
|
193
230
|
const timer = setTimeout(() => {
|
|
194
231
|
try {
|
|
195
|
-
this.getFileSnapshot(filePath
|
|
232
|
+
this.getFileSnapshot(filePath);
|
|
196
233
|
} catch (error) {
|
|
197
234
|
this.options.logger?.info(
|
|
198
235
|
`[FormSpec] Failed to refresh semantic snapshot for ${filePath}: ${String(error)}`
|
|
@@ -200,76 +237,58 @@ var FormSpecPluginService = class {
|
|
|
200
237
|
}
|
|
201
238
|
this.refreshTimers.delete(filePath);
|
|
202
239
|
}, this.options.snapshotDebounceMs ?? FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS);
|
|
240
|
+
timer.unref();
|
|
203
241
|
this.refreshTimers.set(filePath, timer);
|
|
204
242
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
FORM_SPEC_PLUGIN_PERFORMANCE_EVENT.handleQuery,
|
|
210
|
-
{
|
|
211
|
-
kind: query.kind,
|
|
212
|
-
...query.kind === "health" ? {} : { filePath: query.filePath }
|
|
213
|
-
},
|
|
214
|
-
() => this.executeQuery(query, performance)
|
|
215
|
-
);
|
|
216
|
-
if (performance !== void 0) {
|
|
217
|
-
this.logPerformanceEvents(performance.events);
|
|
218
|
-
}
|
|
219
|
-
return response;
|
|
220
|
-
}
|
|
221
|
-
respondToSocket(socket, payload) {
|
|
222
|
-
try {
|
|
223
|
-
const query = JSON.parse(payload);
|
|
224
|
-
if (!isFormSpecSemanticQuery(query)) {
|
|
225
|
-
throw new Error("Invalid FormSpec semantic query payload");
|
|
226
|
-
}
|
|
227
|
-
const response = this.handleQuery(query);
|
|
228
|
-
socket.end(`${JSON.stringify(response)}
|
|
229
|
-
`);
|
|
230
|
-
} catch (error) {
|
|
231
|
-
socket.end(
|
|
232
|
-
`${JSON.stringify({
|
|
233
|
-
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
234
|
-
kind: "error",
|
|
235
|
-
error: error instanceof Error ? error.message : String(error)
|
|
236
|
-
})}
|
|
237
|
-
`
|
|
238
|
-
);
|
|
243
|
+
/** Clears pending timers and cached semantic snapshots. */
|
|
244
|
+
dispose() {
|
|
245
|
+
for (const timer of this.refreshTimers.values()) {
|
|
246
|
+
clearTimeout(timer);
|
|
239
247
|
}
|
|
248
|
+
this.refreshTimers.clear();
|
|
249
|
+
this.snapshotCache.clear();
|
|
240
250
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
251
|
+
/** Returns a copy of the current performance and cache counters. */
|
|
252
|
+
getStats() {
|
|
253
|
+
return {
|
|
254
|
+
queryTotals: { ...this.stats.queryTotals },
|
|
255
|
+
queryPathTotals: {
|
|
256
|
+
diagnostics: { ...this.stats.queryPathTotals.diagnostics },
|
|
257
|
+
fileSnapshot: { ...this.stats.queryPathTotals.fileSnapshot }
|
|
258
|
+
},
|
|
259
|
+
fileSnapshotCacheHits: this.stats.fileSnapshotCacheHits,
|
|
260
|
+
fileSnapshotCacheMisses: this.stats.fileSnapshotCacheMisses,
|
|
261
|
+
syntheticBatchCacheHits: this.stats.syntheticBatchCacheHits,
|
|
262
|
+
syntheticBatchCacheMisses: this.stats.syntheticBatchCacheMisses,
|
|
263
|
+
syntheticCompileCount: this.stats.syntheticCompileCount,
|
|
264
|
+
syntheticCompileApplications: this.stats.syntheticCompileApplications
|
|
265
|
+
};
|
|
246
266
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
267
|
+
runMeasured(name, detail, fn) {
|
|
268
|
+
const performance2 = this.options.enablePerformanceLogging === true ? createFormSpecPerformanceRecorder() : new StatsOnlyPerformanceRecorder();
|
|
269
|
+
const result = optionalMeasure(performance2, name, detail, () => fn(performance2));
|
|
270
|
+
this.updateStatsFromPerformanceEvents(performance2.events);
|
|
271
|
+
if (this.options.enablePerformanceLogging === true) {
|
|
272
|
+
this.logPerformanceEvents(name, performance2.events);
|
|
251
273
|
}
|
|
274
|
+
return result;
|
|
252
275
|
}
|
|
253
|
-
withCommentQueryContext(filePath, offset,
|
|
276
|
+
withCommentQueryContext(filePath, offset, performance2, handler) {
|
|
254
277
|
return optionalMeasure(
|
|
255
|
-
|
|
256
|
-
"
|
|
278
|
+
performance2,
|
|
279
|
+
"semantic.resolveCommentQueryContext",
|
|
257
280
|
{
|
|
258
281
|
filePath,
|
|
259
282
|
offset
|
|
260
283
|
},
|
|
261
284
|
() => {
|
|
262
|
-
const environment = this.getSourceEnvironment(filePath,
|
|
285
|
+
const environment = this.getSourceEnvironment(filePath, performance2);
|
|
263
286
|
if (environment === null) {
|
|
264
|
-
return
|
|
265
|
-
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
266
|
-
kind: "error",
|
|
267
|
-
error: `Unable to resolve TypeScript source file for ${filePath}`
|
|
268
|
-
};
|
|
287
|
+
return null;
|
|
269
288
|
}
|
|
270
289
|
const declaration = optionalMeasure(
|
|
271
|
-
|
|
272
|
-
"
|
|
290
|
+
performance2,
|
|
291
|
+
"semantic.findDeclarationForCommentOffset",
|
|
273
292
|
{
|
|
274
293
|
filePath,
|
|
275
294
|
offset
|
|
@@ -277,14 +296,14 @@ var FormSpecPluginService = class {
|
|
|
277
296
|
() => findDeclarationForCommentOffset(environment.sourceFile, offset)
|
|
278
297
|
);
|
|
279
298
|
const placement = declaration === null ? null : optionalMeasure(
|
|
280
|
-
|
|
281
|
-
"
|
|
299
|
+
performance2,
|
|
300
|
+
"semantic.resolveDeclarationPlacement",
|
|
282
301
|
void 0,
|
|
283
302
|
() => resolveDeclarationPlacement(declaration)
|
|
284
303
|
);
|
|
285
304
|
const subjectType = declaration === null ? void 0 : optionalMeasure(
|
|
286
|
-
|
|
287
|
-
"
|
|
305
|
+
performance2,
|
|
306
|
+
"semantic.getSubjectType",
|
|
288
307
|
void 0,
|
|
289
308
|
() => getSubjectType(declaration, environment.checker)
|
|
290
309
|
);
|
|
@@ -297,11 +316,12 @@ var FormSpecPluginService = class {
|
|
|
297
316
|
}
|
|
298
317
|
);
|
|
299
318
|
}
|
|
300
|
-
|
|
319
|
+
getFileSnapshotWithCacheState(filePath, performance2) {
|
|
301
320
|
const startedAt = getFormSpecPerformanceNow();
|
|
302
|
-
const environment = this.getSourceEnvironment(filePath,
|
|
321
|
+
const environment = this.getSourceEnvironment(filePath, performance2);
|
|
303
322
|
if (environment === null) {
|
|
304
|
-
|
|
323
|
+
this.stats.fileSnapshotCacheMisses += 1;
|
|
324
|
+
const snapshot2 = {
|
|
305
325
|
filePath,
|
|
306
326
|
sourceHash: "",
|
|
307
327
|
generatedAt: this.getNow().toISOString(),
|
|
@@ -309,131 +329,83 @@ var FormSpecPluginService = class {
|
|
|
309
329
|
diagnostics: [
|
|
310
330
|
{
|
|
311
331
|
code: "MISSING_SOURCE_FILE",
|
|
332
|
+
category: "infrastructure",
|
|
312
333
|
message: `Unable to resolve TypeScript source file for ${filePath}`,
|
|
313
334
|
range: { start: 0, end: 0 },
|
|
314
|
-
severity: "warning"
|
|
335
|
+
severity: "warning",
|
|
336
|
+
relatedLocations: [],
|
|
337
|
+
data: {
|
|
338
|
+
filePath
|
|
339
|
+
}
|
|
315
340
|
}
|
|
316
341
|
]
|
|
317
342
|
};
|
|
318
|
-
|
|
319
|
-
name: "
|
|
343
|
+
performance2.record({
|
|
344
|
+
name: "semantic.getFileSnapshot.result",
|
|
320
345
|
durationMs: getFormSpecPerformanceNow() - startedAt,
|
|
321
346
|
detail: {
|
|
322
347
|
filePath,
|
|
323
348
|
cache: "missing-source"
|
|
324
349
|
}
|
|
325
350
|
});
|
|
326
|
-
return
|
|
351
|
+
return {
|
|
352
|
+
snapshot: snapshot2,
|
|
353
|
+
cacheState: "missing-source"
|
|
354
|
+
};
|
|
327
355
|
}
|
|
328
356
|
const cached = this.snapshotCache.get(filePath);
|
|
329
357
|
if (cached?.sourceHash === environment.sourceHash) {
|
|
330
|
-
|
|
331
|
-
|
|
358
|
+
this.stats.fileSnapshotCacheHits += 1;
|
|
359
|
+
performance2.record({
|
|
360
|
+
name: "semantic.getFileSnapshot.result",
|
|
332
361
|
durationMs: getFormSpecPerformanceNow() - startedAt,
|
|
333
362
|
detail: {
|
|
334
363
|
filePath,
|
|
335
364
|
cache: "hit"
|
|
336
365
|
}
|
|
337
366
|
});
|
|
338
|
-
return
|
|
367
|
+
return {
|
|
368
|
+
snapshot: cached.snapshot,
|
|
369
|
+
cacheState: "hit"
|
|
370
|
+
};
|
|
339
371
|
}
|
|
372
|
+
this.stats.fileSnapshotCacheMisses += 1;
|
|
340
373
|
const snapshot = buildFormSpecAnalysisFileSnapshot(environment.sourceFile, {
|
|
341
374
|
checker: environment.checker,
|
|
342
375
|
now: () => this.getNow(),
|
|
343
|
-
|
|
376
|
+
performance: performance2
|
|
344
377
|
});
|
|
345
378
|
this.snapshotCache.set(filePath, {
|
|
346
379
|
sourceHash: environment.sourceHash,
|
|
347
380
|
snapshot
|
|
348
381
|
});
|
|
349
|
-
|
|
350
|
-
name: "
|
|
382
|
+
performance2.record({
|
|
383
|
+
name: "semantic.getFileSnapshot.result",
|
|
351
384
|
durationMs: getFormSpecPerformanceNow() - startedAt,
|
|
352
385
|
detail: {
|
|
353
386
|
filePath,
|
|
354
387
|
cache: "miss"
|
|
355
388
|
}
|
|
356
389
|
});
|
|
357
|
-
return
|
|
390
|
+
return {
|
|
391
|
+
snapshot,
|
|
392
|
+
cacheState: "miss"
|
|
393
|
+
};
|
|
358
394
|
}
|
|
359
395
|
getNow() {
|
|
360
396
|
return this.options.now?.() ?? /* @__PURE__ */ new Date();
|
|
361
397
|
}
|
|
362
|
-
|
|
363
|
-
switch (query.kind) {
|
|
364
|
-
case "health":
|
|
365
|
-
return {
|
|
366
|
-
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
367
|
-
kind: "health",
|
|
368
|
-
manifest: this.manifest
|
|
369
|
-
};
|
|
370
|
-
case "completion":
|
|
371
|
-
return this.withCommentQueryContext(
|
|
372
|
-
query.filePath,
|
|
373
|
-
query.offset,
|
|
374
|
-
(context) => ({
|
|
375
|
-
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
376
|
-
kind: "completion",
|
|
377
|
-
sourceHash: context.sourceHash,
|
|
378
|
-
context: serializeCompletionContext(
|
|
379
|
-
getSemanticCommentCompletionContextAtOffset(context.sourceFile.text, query.offset, {
|
|
380
|
-
checker: context.checker,
|
|
381
|
-
...context.placement === null ? {} : { placement: context.placement },
|
|
382
|
-
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
383
|
-
})
|
|
384
|
-
)
|
|
385
|
-
}),
|
|
386
|
-
performance
|
|
387
|
-
);
|
|
388
|
-
case "hover":
|
|
389
|
-
return this.withCommentQueryContext(
|
|
390
|
-
query.filePath,
|
|
391
|
-
query.offset,
|
|
392
|
-
(context) => ({
|
|
393
|
-
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
394
|
-
kind: "hover",
|
|
395
|
-
sourceHash: context.sourceHash,
|
|
396
|
-
hover: serializeHoverInfo(
|
|
397
|
-
getCommentHoverInfoAtOffset(context.sourceFile.text, query.offset, {
|
|
398
|
-
checker: context.checker,
|
|
399
|
-
...context.placement === null ? {} : { placement: context.placement },
|
|
400
|
-
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
401
|
-
})
|
|
402
|
-
)
|
|
403
|
-
}),
|
|
404
|
-
performance
|
|
405
|
-
);
|
|
406
|
-
case "diagnostics": {
|
|
407
|
-
const snapshot = this.getFileSnapshot(query.filePath, performance);
|
|
408
|
-
return {
|
|
409
|
-
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
410
|
-
kind: "diagnostics",
|
|
411
|
-
sourceHash: snapshot.sourceHash,
|
|
412
|
-
diagnostics: snapshot.diagnostics
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
case "file-snapshot":
|
|
416
|
-
return {
|
|
417
|
-
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION2,
|
|
418
|
-
kind: "file-snapshot",
|
|
419
|
-
snapshot: this.getFileSnapshot(query.filePath, performance)
|
|
420
|
-
};
|
|
421
|
-
default: {
|
|
422
|
-
throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
getSourceEnvironment(filePath, performance) {
|
|
398
|
+
getSourceEnvironment(filePath, performance2) {
|
|
427
399
|
return optionalMeasure(
|
|
428
|
-
|
|
429
|
-
"
|
|
400
|
+
performance2,
|
|
401
|
+
"semantic.getSourceEnvironment",
|
|
430
402
|
{
|
|
431
403
|
filePath
|
|
432
404
|
},
|
|
433
405
|
() => {
|
|
434
406
|
const program = optionalMeasure(
|
|
435
|
-
|
|
436
|
-
"
|
|
407
|
+
performance2,
|
|
408
|
+
"semantic.sourceEnvironment.getProgram",
|
|
437
409
|
void 0,
|
|
438
410
|
() => this.options.getProgram()
|
|
439
411
|
);
|
|
@@ -441,8 +413,8 @@ var FormSpecPluginService = class {
|
|
|
441
413
|
return null;
|
|
442
414
|
}
|
|
443
415
|
const sourceFile = optionalMeasure(
|
|
444
|
-
|
|
445
|
-
"
|
|
416
|
+
performance2,
|
|
417
|
+
"semantic.sourceEnvironment.getSourceFile",
|
|
446
418
|
void 0,
|
|
447
419
|
() => program.getSourceFile(filePath)
|
|
448
420
|
);
|
|
@@ -450,14 +422,14 @@ var FormSpecPluginService = class {
|
|
|
450
422
|
return null;
|
|
451
423
|
}
|
|
452
424
|
const checker = optionalMeasure(
|
|
453
|
-
|
|
454
|
-
"
|
|
425
|
+
performance2,
|
|
426
|
+
"semantic.sourceEnvironment.getTypeChecker",
|
|
455
427
|
void 0,
|
|
456
428
|
() => program.getTypeChecker()
|
|
457
429
|
);
|
|
458
430
|
const sourceHash = optionalMeasure(
|
|
459
|
-
|
|
460
|
-
"
|
|
431
|
+
performance2,
|
|
432
|
+
"semantic.sourceEnvironment.computeTextHash",
|
|
461
433
|
void 0,
|
|
462
434
|
() => computeFormSpecTextHash(sourceFile.text)
|
|
463
435
|
);
|
|
@@ -469,19 +441,38 @@ var FormSpecPluginService = class {
|
|
|
469
441
|
}
|
|
470
442
|
);
|
|
471
443
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
444
|
+
recordQueryPath(kind, cacheState) {
|
|
445
|
+
if (cacheState === "hit") {
|
|
446
|
+
this.stats.queryPathTotals[kind].warm += 1;
|
|
475
447
|
return;
|
|
476
448
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
449
|
+
this.stats.queryPathTotals[kind].cold += 1;
|
|
450
|
+
}
|
|
451
|
+
updateStatsFromPerformanceEvents(events) {
|
|
452
|
+
for (const event of events) {
|
|
453
|
+
if (event.name === "analysis.syntheticCheckBatch.cacheHit" || event.name === "analysis.narrowSyntheticCheckBatch.cacheHit") {
|
|
454
|
+
this.stats.syntheticBatchCacheHits += 1;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (event.name === "analysis.syntheticCheckBatch.cacheMiss" || event.name === "analysis.narrowSyntheticCheckBatch.cacheMiss") {
|
|
458
|
+
this.stats.syntheticBatchCacheMisses += 1;
|
|
459
|
+
const applicationCount = event.detail?.["applicationCount"];
|
|
460
|
+
if (typeof applicationCount === "number") {
|
|
461
|
+
this.stats.syntheticCompileApplications += applicationCount;
|
|
462
|
+
}
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if (event.name === "analysis.syntheticCheckBatch.createProgram" || event.name === "analysis.narrowSyntheticCheckBatch.createProgram") {
|
|
466
|
+
this.stats.syntheticCompileCount += 1;
|
|
483
467
|
}
|
|
484
468
|
}
|
|
469
|
+
}
|
|
470
|
+
logPerformanceEvents(rootEventName, events) {
|
|
471
|
+
const logger = this.options.logger;
|
|
472
|
+
if (logger === void 0 || events.length === 0) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const rootEvent = [...events].reverse().find((event) => event.name === rootEventName);
|
|
485
476
|
if (rootEvent === void 0) {
|
|
486
477
|
return;
|
|
487
478
|
}
|
|
@@ -489,18 +480,234 @@ var FormSpecPluginService = class {
|
|
|
489
480
|
if (rootEvent.durationMs < thresholdMs) {
|
|
490
481
|
return;
|
|
491
482
|
}
|
|
492
|
-
const sortedHotspots = [...events].filter((event) => event.name !==
|
|
483
|
+
const sortedHotspots = [...events].filter((event) => event.name !== rootEventName).sort((left, right) => right.durationMs - left.durationMs).slice(0, 8);
|
|
493
484
|
const lines = [
|
|
494
|
-
`[FormSpec][perf] ${
|
|
485
|
+
`[FormSpec][perf] ${formatPerformanceEvent(rootEvent)}`,
|
|
495
486
|
...sortedHotspots.map((event) => ` ${formatPerformanceEvent(event)}`)
|
|
496
487
|
];
|
|
497
488
|
logger.info(lines.join("\n"));
|
|
498
489
|
}
|
|
499
490
|
};
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
491
|
+
|
|
492
|
+
// src/service.ts
|
|
493
|
+
var FormSpecPluginService = class {
|
|
494
|
+
constructor(options) {
|
|
495
|
+
this.options = options;
|
|
496
|
+
this.semanticService = new FormSpecSemanticService(options);
|
|
497
|
+
this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);
|
|
498
|
+
this.manifest = createFormSpecAnalysisManifest(
|
|
499
|
+
options.workspaceRoot,
|
|
500
|
+
options.typescriptVersion,
|
|
501
|
+
Date.now()
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
manifest;
|
|
505
|
+
runtimePaths;
|
|
506
|
+
semanticService;
|
|
507
|
+
server = null;
|
|
508
|
+
getManifest() {
|
|
509
|
+
return this.manifest;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Returns the underlying semantic service used by this reference wrapper.
|
|
513
|
+
*
|
|
514
|
+
* @public
|
|
515
|
+
*/
|
|
516
|
+
getSemanticService() {
|
|
517
|
+
return this.semanticService;
|
|
518
|
+
}
|
|
519
|
+
async start() {
|
|
520
|
+
if (this.server !== null) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
await fs.mkdir(this.runtimePaths.runtimeDirectory, { recursive: true });
|
|
524
|
+
if (this.runtimePaths.endpoint.kind === "unix-socket") {
|
|
525
|
+
await fs.rm(this.runtimePaths.endpoint.address, { force: true });
|
|
526
|
+
}
|
|
527
|
+
this.server = net.createServer((socket) => {
|
|
528
|
+
let buffer = "";
|
|
529
|
+
socket.setEncoding("utf8");
|
|
530
|
+
socket.setTimeout(FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS, () => {
|
|
531
|
+
this.options.logger?.info(
|
|
532
|
+
`[FormSpec] Closing idle semantic query socket for ${this.runtimePaths.workspaceRoot}`
|
|
533
|
+
);
|
|
534
|
+
socket.destroy();
|
|
535
|
+
});
|
|
536
|
+
socket.on("data", (chunk) => {
|
|
537
|
+
buffer += String(chunk);
|
|
538
|
+
if (buffer.length > FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES) {
|
|
539
|
+
socket.end(
|
|
540
|
+
`${JSON.stringify({
|
|
541
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION3,
|
|
542
|
+
kind: "error",
|
|
543
|
+
error: `FormSpec semantic query exceeded ${String(FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES)} bytes`
|
|
544
|
+
})}
|
|
545
|
+
`
|
|
546
|
+
);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
550
|
+
if (newlineIndex < 0) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const payload = buffer.slice(0, newlineIndex);
|
|
554
|
+
const remaining = buffer.slice(newlineIndex + 1);
|
|
555
|
+
if (remaining.trim().length > 0) {
|
|
556
|
+
this.options.logger?.info(
|
|
557
|
+
`[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
buffer = remaining;
|
|
561
|
+
this.respondToSocket(socket, payload);
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
await new Promise((resolve, reject) => {
|
|
565
|
+
const handleError = (error) => {
|
|
566
|
+
reject(error);
|
|
567
|
+
};
|
|
568
|
+
this.server?.once("error", handleError);
|
|
569
|
+
this.server?.listen(this.runtimePaths.endpoint.address, () => {
|
|
570
|
+
this.server?.off("error", handleError);
|
|
571
|
+
resolve();
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
await this.writeManifest();
|
|
575
|
+
}
|
|
576
|
+
async stop() {
|
|
577
|
+
this.semanticService.dispose();
|
|
578
|
+
const server = this.server;
|
|
579
|
+
this.server = null;
|
|
580
|
+
if (server?.listening === true) {
|
|
581
|
+
await new Promise((resolve, reject) => {
|
|
582
|
+
server.close((error) => {
|
|
583
|
+
if (error === void 0) {
|
|
584
|
+
resolve();
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
reject(error);
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
await this.cleanupRuntimeArtifacts();
|
|
592
|
+
}
|
|
593
|
+
scheduleSnapshotRefresh(filePath) {
|
|
594
|
+
this.semanticService.scheduleSnapshotRefresh(filePath);
|
|
595
|
+
}
|
|
596
|
+
handleQuery(query) {
|
|
597
|
+
if (this.options.enablePerformanceLogging === true) {
|
|
598
|
+
const startedAt = performance.now();
|
|
599
|
+
const response = this.executeQuery(query);
|
|
600
|
+
this.logQueryDuration(query, performance.now() - startedAt);
|
|
601
|
+
return response;
|
|
602
|
+
}
|
|
603
|
+
return this.executeQuery(query);
|
|
604
|
+
}
|
|
605
|
+
executeQuery(query) {
|
|
606
|
+
switch (query.kind) {
|
|
607
|
+
case "health":
|
|
608
|
+
return {
|
|
609
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION3,
|
|
610
|
+
kind: "health",
|
|
611
|
+
manifest: this.manifest
|
|
612
|
+
};
|
|
613
|
+
case "completion": {
|
|
614
|
+
const result = this.semanticService.getCompletionContext(query.filePath, query.offset);
|
|
615
|
+
if (result === null) {
|
|
616
|
+
return {
|
|
617
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION3,
|
|
618
|
+
kind: "error",
|
|
619
|
+
error: `Unable to resolve TypeScript source file for ${query.filePath}`
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
...result,
|
|
624
|
+
kind: "completion"
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
case "hover": {
|
|
628
|
+
const result = this.semanticService.getHover(query.filePath, query.offset);
|
|
629
|
+
if (result === null) {
|
|
630
|
+
return {
|
|
631
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION3,
|
|
632
|
+
kind: "error",
|
|
633
|
+
error: `Unable to resolve TypeScript source file for ${query.filePath}`
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
...result,
|
|
638
|
+
kind: "hover"
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
case "diagnostics": {
|
|
642
|
+
const result = this.semanticService.getDiagnostics(query.filePath);
|
|
643
|
+
return {
|
|
644
|
+
...result,
|
|
645
|
+
kind: "diagnostics"
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
case "file-snapshot":
|
|
649
|
+
return {
|
|
650
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION3,
|
|
651
|
+
kind: "file-snapshot",
|
|
652
|
+
snapshot: this.semanticService.getFileSnapshot(query.filePath)
|
|
653
|
+
};
|
|
654
|
+
default: {
|
|
655
|
+
throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
respondToSocket(socket, payload) {
|
|
660
|
+
try {
|
|
661
|
+
const query = JSON.parse(payload);
|
|
662
|
+
if (!isFormSpecSemanticQuery(query)) {
|
|
663
|
+
throw new Error("Invalid FormSpec semantic query payload");
|
|
664
|
+
}
|
|
665
|
+
const response = this.handleQuery(query);
|
|
666
|
+
socket.end(`${JSON.stringify(response)}
|
|
667
|
+
`);
|
|
668
|
+
} catch (error) {
|
|
669
|
+
socket.end(
|
|
670
|
+
`${JSON.stringify({
|
|
671
|
+
protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION3,
|
|
672
|
+
kind: "error",
|
|
673
|
+
error: error instanceof Error ? error.message : String(error)
|
|
674
|
+
})}
|
|
675
|
+
`
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
async writeManifest() {
|
|
680
|
+
const tempManifestPath = `${this.runtimePaths.manifestPath}.tmp`;
|
|
681
|
+
await fs.writeFile(tempManifestPath, `${JSON.stringify(this.manifest, null, 2)}
|
|
682
|
+
`, "utf8");
|
|
683
|
+
await fs.rename(tempManifestPath, this.runtimePaths.manifestPath);
|
|
684
|
+
}
|
|
685
|
+
async cleanupRuntimeArtifacts() {
|
|
686
|
+
await fs.rm(this.runtimePaths.manifestPath, { force: true });
|
|
687
|
+
if (this.runtimePaths.endpoint.kind === "unix-socket") {
|
|
688
|
+
await fs.rm(this.runtimePaths.endpoint.address, { force: true });
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
logQueryDuration(query, durationMs) {
|
|
692
|
+
const logger = this.options.logger;
|
|
693
|
+
if (logger === void 0) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const thresholdMs = this.options.performanceLogThresholdMs ?? FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS;
|
|
697
|
+
if (durationMs < thresholdMs) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const event = {
|
|
701
|
+
name: "plugin.handleQuery",
|
|
702
|
+
durationMs,
|
|
703
|
+
detail: {
|
|
704
|
+
kind: query.kind,
|
|
705
|
+
...query.kind === "health" ? {} : { filePath: query.filePath }
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
logger.info(`[FormSpec][perf] ${formatPerformanceEvent(event)}`);
|
|
709
|
+
}
|
|
710
|
+
};
|
|
504
711
|
function createLanguageServiceProxy(languageService, semanticService) {
|
|
505
712
|
const wrapWithSnapshotRefresh = (fn) => {
|
|
506
713
|
return (fileName, ...args) => {
|
|
@@ -609,11 +816,16 @@ function init(modules) {
|
|
|
609
816
|
return {
|
|
610
817
|
create(info) {
|
|
611
818
|
const service = getOrCreateService(info, typescriptVersion);
|
|
612
|
-
return createLanguageServiceProxy(info.languageService, service);
|
|
819
|
+
return createLanguageServiceProxy(info.languageService, service.getSemanticService());
|
|
613
820
|
}
|
|
614
821
|
};
|
|
615
822
|
}
|
|
616
823
|
export {
|
|
824
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION4 as FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
825
|
+
FORMSPEC_ANALYSIS_SCHEMA_VERSION2 as FORMSPEC_ANALYSIS_SCHEMA_VERSION,
|
|
826
|
+
FormSpecPluginService,
|
|
827
|
+
FormSpecSemanticService,
|
|
828
|
+
createLanguageServiceProxy,
|
|
617
829
|
init
|
|
618
830
|
};
|
|
619
831
|
//# sourceMappingURL=index.js.map
|