@saifymatteo/pi-x-ide 0.0.0-stage → 1.20.2
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 +328 -0
- package/CONTRIBUTING.md +119 -0
- package/CONTRIBUTING.zh-CN.md +106 -0
- package/LICENSE +201 -0
- package/README.md +33 -2
- package/README.zh-CN.md +34 -0
- package/RELEASE.md +213 -0
- package/config.example.json +9 -0
- package/dist/src/pi/chunks/chunk-4RHJAL4S.js +200 -0
- package/dist/src/pi/chunks/chunk-UUXVCTOS.js +819 -0
- package/dist/src/pi/chunks/config-ui-LO3DVQFC.js +8073 -0
- package/dist/src/pi/chunks/runtime-services-BVQBAAWT.js +19569 -0
- package/dist/src/pi/commands.js +126 -0
- package/dist/src/pi/config-ui.js +127 -0
- package/dist/src/pi/connection.js +237 -0
- package/dist/src/pi/context.js +30 -0
- package/dist/src/pi/diagnostic-renderer.js +326 -0
- package/dist/src/pi/diagnostic-types.js +1 -0
- package/dist/src/pi/diagnostics.js +104 -0
- package/dist/src/pi/discovery.js +135 -0
- package/dist/src/pi/effect-boundary.js +5 -0
- package/dist/src/pi/ide-host.js +68 -0
- package/dist/src/pi/index.js +636 -0
- package/dist/src/pi/install.js +308 -0
- package/dist/src/pi/reconnect.js +57 -0
- package/dist/src/pi/runtime-loader.js +38 -0
- package/dist/src/pi/runtime-services.js +356 -0
- package/dist/src/pi/safety.js +61 -0
- package/dist/src/pi/selection.js +18 -0
- package/dist/src/pi/state.js +13 -0
- package/dist/src/pi/ui.js +304 -0
- package/dist/src/pi/zed.js +409 -0
- package/dist/src/shared/config-options.js +45 -0
- package/dist/src/shared/config.js +183 -0
- package/dist/src/shared/display-width.js +99 -0
- package/dist/src/shared/effect-errors.js +36 -0
- package/dist/src/shared/effect-runtime.js +36 -0
- package/dist/src/shared/effect-schema.js +120 -0
- package/dist/src/shared/errors.js +48 -0
- package/dist/src/shared/format.js +92 -0
- package/dist/src/shared/ide-server.js +167 -0
- package/dist/src/shared/jsonrpc-guard.js +12 -0
- package/dist/src/shared/lock-file-heartbeat.js +58 -0
- package/dist/src/shared/lock-file.js +50 -0
- package/dist/src/shared/paths.js +79 -0
- package/dist/src/shared/platform.js +57 -0
- package/dist/src/shared/protocol.js +3 -0
- package/dist/src/shared/schema.js +37 -0
- package/dist/src/shared/version.js +1 -0
- package/dist/src/shared/ws.js +8 -0
- package/docs/explanation/architecture.md +123 -0
- package/docs/explanation/discovery.md +113 -0
- package/docs/explanation/effect.md +127 -0
- package/docs/how-to/install-jetbrains.md +58 -0
- package/docs/how-to/install-neovim.md +138 -0
- package/docs/how-to/install-vscode.md +75 -0
- package/docs/how-to/install-zed.md +70 -0
- package/docs/how-to/troubleshoot-connection.md +103 -0
- package/docs/how-to/use-wsl2.md +90 -0
- package/docs/reference/commands.md +59 -0
- package/docs/reference/configuration.md +101 -0
- package/docs/reference/feature-parity.md +26 -0
- package/docs/reference/protocol.md +71 -0
- package/docs/specs/ide-protocol.md +215 -0
- package/docs/tutorials/get-started.md +133 -0
- package/docs/zh-CN/explanation/architecture.md +93 -0
- package/docs/zh-CN/explanation/discovery.md +71 -0
- package/docs/zh-CN/explanation/effect.md +90 -0
- package/docs/zh-CN/how-to/install-jetbrains.md +49 -0
- package/docs/zh-CN/how-to/install-neovim.md +126 -0
- package/docs/zh-CN/how-to/install-vscode.md +67 -0
- package/docs/zh-CN/how-to/install-zed.md +56 -0
- package/docs/zh-CN/how-to/troubleshoot-connection.md +85 -0
- package/docs/zh-CN/how-to/use-wsl2.md +75 -0
- package/docs/zh-CN/reference/commands.md +54 -0
- package/docs/zh-CN/reference/configuration.md +94 -0
- package/docs/zh-CN/reference/feature-parity.md +21 -0
- package/docs/zh-CN/reference/protocol.md +54 -0
- package/docs/zh-CN/tutorials/get-started.md +101 -0
- package/package.json +86 -4
- package/schemas/config.json +77 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { logExtensionError } from "../shared/errors.js";
|
|
2
|
+
import { formatRangeMention } from "../shared/format.js";
|
|
3
|
+
import { hasDirectWorkspaceMatch } from "../shared/paths.js";
|
|
4
|
+
import { IdeConnection, IdeConnectionTimeoutError } from "./connection.js";
|
|
5
|
+
import { discoverIdeCandidates } from "./discovery.js";
|
|
6
|
+
import { handleDiagnosticFixRequested } from "./diagnostics.js";
|
|
7
|
+
import { discoverInstallCandidates, installIdeExtension, isAutoInstallEnabled, selectAutoInstallCandidate, } from "./install.js";
|
|
8
|
+
import { resetReconnectState, scheduleReconnect, stopReconnectScheduling } from "./reconnect.js";
|
|
9
|
+
import { containPiError } from "./safety.js";
|
|
10
|
+
import { clearLatestSelection, setLatestSelection } from "./selection.js";
|
|
11
|
+
import { clearIdeUi, updateIdeUi } from "./ui.js";
|
|
12
|
+
import { startZedPolling, stopZedPolling } from "./zed.js";
|
|
13
|
+
const INSTALL_RECONNECT_RETRY_MS = 1_500;
|
|
14
|
+
const INSTALL_RECONNECT_TIMEOUT_MS = 15_000;
|
|
15
|
+
let activePi;
|
|
16
|
+
function formatConnectTimeoutMessage(error) {
|
|
17
|
+
return `${error.message}.`;
|
|
18
|
+
}
|
|
19
|
+
export async function startSession(pi, runtime, ctx) {
|
|
20
|
+
activePi = pi;
|
|
21
|
+
runtime.ctx = ctx;
|
|
22
|
+
runtime.cwd = ctx.cwd;
|
|
23
|
+
const generation = runtime.sessionGeneration;
|
|
24
|
+
stopZedPolling(runtime);
|
|
25
|
+
stopReconnectScheduling(runtime);
|
|
26
|
+
if (!runtime.enabled) {
|
|
27
|
+
runtime.connectionStatus = "disabled";
|
|
28
|
+
updateIdeUi(runtime, ctx);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
void maybeAutoInstallAndReconnect(runtime, ctx, generation);
|
|
32
|
+
await connectAutoWithZedFallback(runtime, ctx, generation);
|
|
33
|
+
}
|
|
34
|
+
export function shutdownSession(runtime, ctx) {
|
|
35
|
+
runtime.ctx = ctx;
|
|
36
|
+
stopZedPolling(runtime);
|
|
37
|
+
stopReconnectScheduling(runtime);
|
|
38
|
+
const connection = runtime.connection;
|
|
39
|
+
runtime.connection = undefined;
|
|
40
|
+
connection?.disconnect();
|
|
41
|
+
clearIdeUi(runtime, ctx);
|
|
42
|
+
runtime.ctx = undefined;
|
|
43
|
+
return Promise.resolve();
|
|
44
|
+
}
|
|
45
|
+
export async function refreshCandidates(runtime, ctx) {
|
|
46
|
+
runtime.ctx = ctx;
|
|
47
|
+
runtime.cwd = ctx.cwd;
|
|
48
|
+
runtime.candidates = await discoverIdeCandidates({ cwd: ctx.cwd });
|
|
49
|
+
return runtime.candidates;
|
|
50
|
+
}
|
|
51
|
+
export async function connectAuto(runtime, ctx, pi) {
|
|
52
|
+
if (pi)
|
|
53
|
+
activePi = pi;
|
|
54
|
+
await connectAutoWithZedFallback(runtime, ctx);
|
|
55
|
+
}
|
|
56
|
+
export async function connectCandidate(runtime, candidate, ctx, options = {}, pi) {
|
|
57
|
+
if (pi)
|
|
58
|
+
activePi = pi;
|
|
59
|
+
await connectCandidateInternal(runtime, candidate, ctx, options);
|
|
60
|
+
}
|
|
61
|
+
export function disconnect(runtime, ctx, disabled = false) {
|
|
62
|
+
runtime.ctx = ctx;
|
|
63
|
+
stopZedPolling(runtime);
|
|
64
|
+
stopReconnectScheduling(runtime);
|
|
65
|
+
const connection = runtime.connection;
|
|
66
|
+
runtime.connection = undefined;
|
|
67
|
+
connection?.disconnect();
|
|
68
|
+
resetReconnectState(runtime);
|
|
69
|
+
runtime.enabled = !disabled;
|
|
70
|
+
runtime.connectedServer = undefined;
|
|
71
|
+
runtime.connectionStatus = disabled ? "disabled" : "disconnected";
|
|
72
|
+
runtime.connectionMessage = disabled ? "IDE integration disabled." : "Disconnected.";
|
|
73
|
+
if (disabled) {
|
|
74
|
+
runtime.latestSelection = undefined;
|
|
75
|
+
runtime.latestSelectionKey = undefined;
|
|
76
|
+
runtime.turnSelection = undefined;
|
|
77
|
+
runtime.attachState = "idle";
|
|
78
|
+
}
|
|
79
|
+
updateIdeUi(runtime, ctx);
|
|
80
|
+
return Promise.resolve();
|
|
81
|
+
}
|
|
82
|
+
export async function installExtension(runtime, ctx) {
|
|
83
|
+
runtime.ctx = ctx;
|
|
84
|
+
runtime.cwd = ctx.cwd;
|
|
85
|
+
const candidates = await discoverInstallCandidates({ includeLowConfidence: true });
|
|
86
|
+
if (candidates.length === 0) {
|
|
87
|
+
ctx.ui.notify("No supported VS Code-family IDE CLI found. Install VS Code, Cursor, or Windsurf CLI first.", "warning");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const candidate = await selectManualInstallCandidate(candidates, ctx);
|
|
91
|
+
if (!candidate)
|
|
92
|
+
return;
|
|
93
|
+
runtime.enabled = true;
|
|
94
|
+
if (!candidate.needsInstall) {
|
|
95
|
+
ctx.ui.notify(`${candidate.label} already has Pi x IDE ${candidate.installedVersion ?? candidate.targetVersion}.`, "info");
|
|
96
|
+
await connectAutoInternal(runtime, ctx);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
ctx.ui.notify(`${installActionLabel(candidate)} Pi x IDE extension for ${candidate.label}...`, "info");
|
|
100
|
+
const result = await installIdeExtension(candidate, runtime);
|
|
101
|
+
if (!result.success) {
|
|
102
|
+
ctx.ui.notify(`Failed to install Pi x IDE extension for ${candidate.label}: ${describeInstallError(result.error, result.stderr)}`, "warning");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
ctx.ui.notify(`Pi x IDE extension installed for ${candidate.label}. Trying to connect...`, "info");
|
|
106
|
+
const connected = await retryConnectAfterInstall(runtime, ctx, runtime.sessionGeneration);
|
|
107
|
+
if (!connected && runtime.enabled) {
|
|
108
|
+
ctx.ui.notify(`Pi x IDE extension installed for ${candidate.label}. If Pi does not connect automatically, reload the IDE window and run /ide auto.`, "warning");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function maybeAutoInstallAndReconnect(runtime, ctx, generation) {
|
|
112
|
+
try {
|
|
113
|
+
if (!isAutoInstallEnabled(process.env, { projectDir: ctx.cwd ?? runtime.cwd }) ||
|
|
114
|
+
!isInstallSessionActive(runtime, generation)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const candidates = await discoverInstallCandidates();
|
|
118
|
+
const candidate = selectAutoInstallCandidate(candidates);
|
|
119
|
+
if (!candidate)
|
|
120
|
+
return;
|
|
121
|
+
if (!isInstallSessionActive(runtime, generation))
|
|
122
|
+
return;
|
|
123
|
+
if (!candidate.needsInstall)
|
|
124
|
+
return;
|
|
125
|
+
notifyInstall(ctx, `Installing Pi x IDE extension for ${candidate.label}...`, "info");
|
|
126
|
+
const result = await installIdeExtension(candidate, runtime);
|
|
127
|
+
if (!isInstallSessionActive(runtime, generation))
|
|
128
|
+
return;
|
|
129
|
+
if (!result.success) {
|
|
130
|
+
notifyInstall(ctx, `Failed to install Pi x IDE extension for ${candidate.label}: ${describeInstallError(result.error, result.stderr)}`, "warning");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
notifyInstall(ctx, `Pi x IDE extension installed for ${candidate.label}. Trying to connect...`, "info");
|
|
134
|
+
const connected = await retryConnectAfterInstall(runtime, ctx, generation);
|
|
135
|
+
if (!isInstallSessionActive(runtime, generation))
|
|
136
|
+
return;
|
|
137
|
+
if (connected && runtime.connectionStatus === "connected") {
|
|
138
|
+
notifyInstall(ctx, `Pi x IDE extension installed for ${candidate.label}. Connected!`, "info");
|
|
139
|
+
}
|
|
140
|
+
else if (!connected) {
|
|
141
|
+
notifyInstall(ctx, `Pi x IDE extension installed for ${candidate.label}. If Pi does not connect automatically, reload the IDE window and run /ide.`, "warning");
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
notifyInstall(ctx, `Pi x IDE extension installed for ${candidate.label}. Connection attempt completed. Run /ide to retry if needed.`, "warning");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
if (isInstallSessionActive(runtime, generation)) {
|
|
149
|
+
notifyInstall(ctx, `Failed to auto-install Pi x IDE extension: ${errorMessage(error)}`, "warning");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function selectManualInstallCandidate(candidates, ctx) {
|
|
154
|
+
if (candidates.length === 1)
|
|
155
|
+
return candidates[0];
|
|
156
|
+
const labels = candidates.map((candidate, index) => `${index + 1}. ${formatInstallCandidate(candidate)}`);
|
|
157
|
+
const choice = await ctx.ui.select("Select IDE to install Pi x IDE", labels);
|
|
158
|
+
if (!choice)
|
|
159
|
+
return undefined;
|
|
160
|
+
return candidates[labels.indexOf(choice)];
|
|
161
|
+
}
|
|
162
|
+
async function retryConnectAfterInstall(runtime, ctx, generation) {
|
|
163
|
+
const deadline = Date.now() + INSTALL_RECONNECT_TIMEOUT_MS;
|
|
164
|
+
while (Date.now() < deadline) {
|
|
165
|
+
if (!isInstallSessionActive(runtime, generation))
|
|
166
|
+
return false;
|
|
167
|
+
await sleep(INSTALL_RECONNECT_RETRY_MS);
|
|
168
|
+
if (!isInstallSessionActive(runtime, generation))
|
|
169
|
+
return false;
|
|
170
|
+
const candidates = await refreshCandidates(runtime, ctx);
|
|
171
|
+
if (candidates.length === 0)
|
|
172
|
+
continue;
|
|
173
|
+
await connectAutoInternal(runtime, ctx);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
function isInstallSessionActive(runtime, generation) {
|
|
179
|
+
return runtime.enabled && runtime.sessionGeneration === generation;
|
|
180
|
+
}
|
|
181
|
+
function formatInstallCandidate(candidate) {
|
|
182
|
+
const version = candidate.installedVersion ? `installed ${candidate.installedVersion}` : "not installed";
|
|
183
|
+
const status = candidate.reason === "current" ? "up to date" : `${installActionLabel(candidate).toLowerCase()} required`;
|
|
184
|
+
return `${candidate.label} — ${version}, target ${candidate.targetVersion}, ${status} (${candidate.cliPath})`;
|
|
185
|
+
}
|
|
186
|
+
function installActionLabel(candidate) {
|
|
187
|
+
if (candidate.reason === "outdated")
|
|
188
|
+
return "Updating";
|
|
189
|
+
if (candidate.reason === "unknown")
|
|
190
|
+
return "Installing or updating";
|
|
191
|
+
return "Installing";
|
|
192
|
+
}
|
|
193
|
+
function notifyInstall(ctx, message, level) {
|
|
194
|
+
try {
|
|
195
|
+
if (!ctx.hasUI)
|
|
196
|
+
return;
|
|
197
|
+
ctx.ui.notify(message, level);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
logExtensionError("IDE install notification", error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function describeInstallError(error, stderr) {
|
|
204
|
+
const message = error || stderr.trim();
|
|
205
|
+
return message || "unknown error";
|
|
206
|
+
}
|
|
207
|
+
function errorMessage(error) {
|
|
208
|
+
return error instanceof Error ? error.message : String(error);
|
|
209
|
+
}
|
|
210
|
+
function sleep(ms) {
|
|
211
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
212
|
+
}
|
|
213
|
+
async function connectAutoWithZedFallback(runtime, ctx, generation = runtime.sessionGeneration, options = {}) {
|
|
214
|
+
await connectAutoInternal(runtime, ctx, options);
|
|
215
|
+
if (runtime.connectionStatus !== "connected") {
|
|
216
|
+
startZedPolling(runtime, ctx, { generation });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function connectAutoInternal(runtime, ctx, options = {}) {
|
|
220
|
+
runtime.enabled = true;
|
|
221
|
+
const candidates = await refreshCandidates(runtime, ctx);
|
|
222
|
+
const candidate = candidates.find((entry) => isAutoConnectCandidate(entry, ctx.cwd));
|
|
223
|
+
if (!candidate) {
|
|
224
|
+
if (options.resetReconnectState !== false)
|
|
225
|
+
resetReconnectState(runtime);
|
|
226
|
+
runtime.connectionStatus = "disconnected";
|
|
227
|
+
runtime.connectionMessage =
|
|
228
|
+
candidates.length > 0
|
|
229
|
+
? "No IDE lock file matches the current workspace. Use /ide to connect manually."
|
|
230
|
+
: "No matching IDE lock files found.";
|
|
231
|
+
runtime.currentCandidate = undefined;
|
|
232
|
+
runtime.connectedServer = undefined;
|
|
233
|
+
updateIdeUi(runtime, ctx);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
await connectCandidateInternal(runtime, candidate, ctx, options);
|
|
237
|
+
}
|
|
238
|
+
function isAutoConnectCandidate(candidate, cwd) {
|
|
239
|
+
return hasDirectWorkspaceMatch(candidate.lock.workspaceFolders, cwd);
|
|
240
|
+
}
|
|
241
|
+
async function connectCandidateInternal(runtime, candidate, ctx, options = {}) {
|
|
242
|
+
if (options.resetReconnectState !== false)
|
|
243
|
+
resetReconnectState(runtime);
|
|
244
|
+
runtime.ctx = ctx;
|
|
245
|
+
runtime.cwd = ctx.cwd;
|
|
246
|
+
runtime.enabled = true;
|
|
247
|
+
stopZedPolling(runtime);
|
|
248
|
+
stopReconnectScheduling(runtime);
|
|
249
|
+
const previous = runtime.connection;
|
|
250
|
+
runtime.connection = undefined;
|
|
251
|
+
previous?.disconnect();
|
|
252
|
+
runtime.currentCandidate = candidate;
|
|
253
|
+
runtime.connectedServer = undefined;
|
|
254
|
+
runtime.connectionStatus = "connecting";
|
|
255
|
+
runtime.connectionMessage = `Connecting to ${candidate.lock.name} at ${candidate.lock.host}:${candidate.lock.port}`;
|
|
256
|
+
updateIdeUi(runtime, ctx);
|
|
257
|
+
const generation = runtime.sessionGeneration;
|
|
258
|
+
const connectionRef = {};
|
|
259
|
+
const connection = new IdeConnection(candidate, ctx.cwd, createConnectionCallbacks(activePi, runtime, () => connectionRef.current, generation));
|
|
260
|
+
connectionRef.current = connection;
|
|
261
|
+
runtime.connection = connection;
|
|
262
|
+
try {
|
|
263
|
+
await connection.connect();
|
|
264
|
+
if (runtime.connection === connection && runtime.connectionStatus === "connecting") {
|
|
265
|
+
runtime.connectionStatus = "connected";
|
|
266
|
+
runtime.connectionMessage = undefined;
|
|
267
|
+
resetReconnectState(runtime);
|
|
268
|
+
updateIdeUi(runtime, ctx);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (runtime.connection === connection) {
|
|
273
|
+
runtime.connection = undefined;
|
|
274
|
+
runtime.connectionStatus = "error";
|
|
275
|
+
runtime.connectionMessage =
|
|
276
|
+
error instanceof IdeConnectionTimeoutError ? formatConnectTimeoutMessage(error) : errorMessage(error);
|
|
277
|
+
updateIdeUi(runtime, ctx);
|
|
278
|
+
if (!(error instanceof IdeConnectionTimeoutError))
|
|
279
|
+
scheduleIdeReconnect(runtime);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function createConnectionCallbacks(pi, runtime, getConnection, generation) {
|
|
284
|
+
return {
|
|
285
|
+
onConnected: (server) => {
|
|
286
|
+
if (!isCurrentConnection(runtime, getConnection(), generation))
|
|
287
|
+
return;
|
|
288
|
+
runtime.connectedServer = server;
|
|
289
|
+
runtime.connectionStatus = "connected";
|
|
290
|
+
runtime.connectionMessage = undefined;
|
|
291
|
+
resetReconnectState(runtime);
|
|
292
|
+
updateIdeUi(runtime);
|
|
293
|
+
},
|
|
294
|
+
onDisconnected: (reason) => {
|
|
295
|
+
if (!isCurrentConnection(runtime, getConnection(), generation))
|
|
296
|
+
return;
|
|
297
|
+
runtime.connection = undefined;
|
|
298
|
+
runtime.connectedServer = undefined;
|
|
299
|
+
runtime.connectionStatus = runtime.enabled ? "disconnected" : "disabled";
|
|
300
|
+
runtime.connectionMessage = reason;
|
|
301
|
+
updateIdeUi(runtime);
|
|
302
|
+
if (runtime.enabled)
|
|
303
|
+
scheduleIdeReconnect(runtime);
|
|
304
|
+
},
|
|
305
|
+
onSelectionChanged: (snapshot) => {
|
|
306
|
+
if (!isCurrentConnection(runtime, getConnection(), generation))
|
|
307
|
+
return;
|
|
308
|
+
setLatestSelection(runtime, snapshot);
|
|
309
|
+
},
|
|
310
|
+
onSelectionCleared: () => {
|
|
311
|
+
if (!isCurrentConnection(runtime, getConnection(), generation))
|
|
312
|
+
return;
|
|
313
|
+
clearLatestSelection(runtime);
|
|
314
|
+
},
|
|
315
|
+
onAtMentioned: (params) => {
|
|
316
|
+
if (!isCurrentConnection(runtime, getConnection(), generation))
|
|
317
|
+
return;
|
|
318
|
+
handleAtMentioned(runtime, params);
|
|
319
|
+
},
|
|
320
|
+
onDiagnosticFixRequested: (params) => {
|
|
321
|
+
if (!pi || !isCurrentConnection(runtime, getConnection(), generation))
|
|
322
|
+
return;
|
|
323
|
+
handleDiagnosticFixRequested(pi, runtime, params);
|
|
324
|
+
},
|
|
325
|
+
onError: (error) => {
|
|
326
|
+
if (!isCurrentConnection(runtime, getConnection(), generation))
|
|
327
|
+
return;
|
|
328
|
+
containPiError(runtime, "IDE connection error", error);
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function isCurrentConnection(runtime, connection, generation = runtime.sessionGeneration) {
|
|
333
|
+
return runtime.sessionGeneration === generation && !!connection && runtime.connection === connection;
|
|
334
|
+
}
|
|
335
|
+
function scheduleIdeReconnect(runtime) {
|
|
336
|
+
scheduleReconnect(runtime, async (generation) => {
|
|
337
|
+
const ctx = runtime.ctx;
|
|
338
|
+
if (!ctx || !runtime.enabled || runtime.sessionGeneration !== generation)
|
|
339
|
+
return;
|
|
340
|
+
try {
|
|
341
|
+
await connectAutoWithZedFallback(runtime, ctx, runtime.sessionGeneration, { resetReconnectState: false });
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
containPiError(runtime, "IDE reconnect", error);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function handleAtMentioned(runtime, params) {
|
|
349
|
+
setLatestSelection(runtime, params);
|
|
350
|
+
const ctx = runtime.ctx;
|
|
351
|
+
if (!ctx?.hasUI)
|
|
352
|
+
return;
|
|
353
|
+
const text = params.rangeText || formatRangeMention(params, { cwd: ctx.cwd });
|
|
354
|
+
ctx.ui.pasteToEditor(text + "\n");
|
|
355
|
+
ctx.ui.notify(`Attached ${text}`, "info");
|
|
356
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { errorMessage, formatExtensionError, isStaleContextError, logExtensionError, safeRun, safeRunAsync, setExtensionErrorReporter, } from "../shared/errors.js";
|
|
2
|
+
import { updateIdeUi } from "./ui.js";
|
|
3
|
+
function deliverPiError(scope, error, ctx) {
|
|
4
|
+
try {
|
|
5
|
+
if (!ctx?.hasUI)
|
|
6
|
+
return false;
|
|
7
|
+
ctx.ui.notify(formatExtensionError(scope, error), "error");
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function notifyPiError(scope, error, ctx) {
|
|
15
|
+
if (isStaleContextError(error))
|
|
16
|
+
return;
|
|
17
|
+
deliverPiError(scope, error, ctx);
|
|
18
|
+
}
|
|
19
|
+
export function flushPendingPiErrors(runtime) {
|
|
20
|
+
if (runtime.pendingExtensionErrors.length === 0)
|
|
21
|
+
return;
|
|
22
|
+
const pending = runtime.pendingExtensionErrors;
|
|
23
|
+
runtime.pendingExtensionErrors = [];
|
|
24
|
+
for (const item of pending) {
|
|
25
|
+
if (isStaleContextError(item.error))
|
|
26
|
+
continue;
|
|
27
|
+
if (!deliverPiError(item.scope, item.error, runtime.ctx)) {
|
|
28
|
+
runtime.pendingExtensionErrors.push(item);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function bindPiUiContext(runtime, ctx) {
|
|
33
|
+
runtime.ctx = ctx;
|
|
34
|
+
flushPendingPiErrors(runtime);
|
|
35
|
+
}
|
|
36
|
+
function reportPiExtensionError(runtime, scope, error) {
|
|
37
|
+
if (isStaleContextError(error))
|
|
38
|
+
return;
|
|
39
|
+
if (deliverPiError(scope, error, runtime.ctx))
|
|
40
|
+
return;
|
|
41
|
+
runtime.pendingExtensionErrors.push({ scope, error });
|
|
42
|
+
}
|
|
43
|
+
export function installPiErrorReporter(runtime) {
|
|
44
|
+
setExtensionErrorReporter((scope, error) => {
|
|
45
|
+
reportPiExtensionError(runtime, scope, error);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export function containPiError(runtime, scope, error, ctx = runtime.ctx) {
|
|
49
|
+
if (ctx)
|
|
50
|
+
bindPiUiContext(runtime, ctx);
|
|
51
|
+
logExtensionError(scope, error);
|
|
52
|
+
runtime.connectionStatus = "error";
|
|
53
|
+
runtime.connectionMessage = `${scope}: ${errorMessage(error)}`;
|
|
54
|
+
safeRun(`${scope} update UI after error`, () => updateIdeUi(runtime, ctx ?? runtime.ctx));
|
|
55
|
+
}
|
|
56
|
+
export function runPiBoundary(scope, runtime, action, ctx) {
|
|
57
|
+
return safeRun(scope, action, (error) => containPiError(runtime, scope, error, ctx));
|
|
58
|
+
}
|
|
59
|
+
export async function runPiBoundaryAsync(scope, runtime, action, ctx) {
|
|
60
|
+
return safeRunAsync(scope, action, (error) => containPiError(runtime, scope, error, ctx));
|
|
61
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { snapshotKey } from "../shared/format.js";
|
|
2
|
+
import { updateIdeUi } from "./ui.js";
|
|
3
|
+
export function setLatestSelection(runtime, snapshot, ctx) {
|
|
4
|
+
const key = snapshotKey(snapshot);
|
|
5
|
+
runtime.latestSelection = snapshot;
|
|
6
|
+
if (runtime.latestSelectionKey !== key) {
|
|
7
|
+
runtime.latestSelectionKey = key;
|
|
8
|
+
runtime.attachState = "pending";
|
|
9
|
+
}
|
|
10
|
+
updateIdeUi(runtime, ctx);
|
|
11
|
+
}
|
|
12
|
+
export function clearLatestSelection(runtime, ctx) {
|
|
13
|
+
runtime.latestSelection = undefined;
|
|
14
|
+
runtime.latestSelectionKey = undefined;
|
|
15
|
+
runtime.turnSelection = undefined;
|
|
16
|
+
runtime.attachState = "idle";
|
|
17
|
+
updateIdeUi(runtime, ctx);
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function createRuntime() {
|
|
2
|
+
return {
|
|
3
|
+
enabled: true,
|
|
4
|
+
candidates: [],
|
|
5
|
+
connectionStatus: "idle",
|
|
6
|
+
attachState: "idle",
|
|
7
|
+
reconnectAttempts: 0,
|
|
8
|
+
installingIdeIds: new Set(),
|
|
9
|
+
sessionGeneration: 0,
|
|
10
|
+
sessionStarting: false,
|
|
11
|
+
pendingExtensionErrors: [],
|
|
12
|
+
};
|
|
13
|
+
}
|