glimmervoid 0.0.0-stage → 0.30.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/LICENSE +48 -0
- package/README.md +195 -2
- package/dist/bin/glimmervoid.js +275 -0
- package/dist/chunks/adapters-Dho1VSY5.js +2 -0
- package/dist/chunks/adapters-zPDx1Xox.js +575 -0
- package/dist/chunks/agent-api-cli-D0MmtEdz.js +71 -0
- package/dist/chunks/agent-api-core-Dz0r21y9.js +103 -0
- package/dist/chunks/agent-setup-cli-B5Atxr3F.js +139 -0
- package/dist/chunks/ascii-figure-core-pDAwtlKO.js +45 -0
- package/dist/chunks/child-process-safe-DRGsp0TE.js +69 -0
- package/dist/chunks/child-process-safe-DvQjXsgz.js +2 -0
- package/dist/chunks/config-path-core-DFt-TemS.js +38 -0
- package/dist/chunks/config-store-CC8T3okC.js +2 -0
- package/dist/chunks/config-store-mm7MgzoL.js +1412 -0
- package/dist/chunks/contracts-DGkKNh9W.js +3020 -0
- package/dist/chunks/grok-DuU8xVLZ.js +436 -0
- package/dist/chunks/hook-command-core-DclEoE9e.js +14 -0
- package/dist/chunks/hook-relay-core-BHfYFnYV.js +88 -0
- package/dist/chunks/hook-tools-pSJia7ks.js +74 -0
- package/dist/chunks/ingest-core-DWI1Mbu4.js +342 -0
- package/dist/chunks/ingest-editor-core-BuI1gMF_.js +445 -0
- package/dist/chunks/loopback-post-C7C22eYi.js +79 -0
- package/dist/chunks/main-CuIZQPhZ.js +43559 -0
- package/dist/chunks/node-pty-preflight-CK1knGIR.js +162 -0
- package/dist/chunks/pair-cli-w-gCAF-1.js +110 -0
- package/dist/chunks/path-doctor-C23VAqeu.js +63 -0
- package/dist/chunks/refocus-core-BKQKllq7.js +38 -0
- package/dist/chunks/relay-stdin-CpHvLu6M.js +20 -0
- package/dist/chunks/remote-config-C2ytQwrs.js +525 -0
- package/dist/chunks/rtk-hook-core-Bl6gYHPN.js +133 -0
- package/dist/chunks/rtk-resolver-CLo-gZzw.js +21 -0
- package/dist/chunks/rtk-resolver-CMQAidlI.js +2 -0
- package/dist/chunks/runtime-paths-Ci_ksSkj.js +75 -0
- package/dist/chunks/runtime-paths-JLrZ3wyi.js +2 -0
- package/dist/chunks/sandbox-deps-Dd5-kZnf.js +67 -0
- package/dist/chunks/session-Cez8aIWg.js +164 -0
- package/dist/chunks/spawn-command-Cc40FiZI.js +134 -0
- package/dist/chunks/visions-cli-BXQFIBCX.js +98 -0
- package/dist/chunks/visions-setup-CvVnOe1r.js +1753 -0
- package/dist/client/assets/favicon-CKygeLwW.svg +7 -0
- package/dist/client/assets/index-B1MFijjb.css +1 -0
- package/dist/client/assets/index-CU1iPPdn.js +249 -0
- package/dist/client/index.html +108 -0
- package/dist/package.json +3 -0
- package/dist/scripts/postinstall-path-check.js +36 -0
- package/dist/server/index.js +92 -0
- package/dist/session/command-hook-relay.js +16 -0
- package/dist/session/hook-relay.js +24 -0
- package/dist/session/hook-tool-relay.js +107 -0
- package/dist/session/statusline-relay.js +82 -0
- package/dist/session/visions-relay.js +627 -0
- package/dist/tools/vscode-visions/extension.js +289 -0
- package/dist/tools/vscode-visions/lsp-convert.js +80 -0
- package/dist/tools/vscode-visions/package.json +32 -0
- package/dist/tools/vscode-visions/visions-lsp-core.js +112 -0
- package/package.json +96 -4
- package/scripts/postinstall.mjs +4 -0
- package/scripts/prepare-build.js +38 -0
- package/scripts/recover-handoff.mjs +82 -0
|
@@ -0,0 +1,1753 @@
|
|
|
1
|
+
import { a as packageRoot, i as extensionDir, n as cliPath, o as relayPath, t as bundled } from "./runtime-paths-Ci_ksSkj.js";
|
|
2
|
+
import { i as execSync, n as execFileAsync } from "./child-process-safe-DRGsp0TE.js";
|
|
3
|
+
import { r as resolvePathCommandMatches } from "./spawn-command-Cc40FiZI.js";
|
|
4
|
+
import { d as scrubText, n as SOURCE_DEFAULTS } from "./ingest-core-DWI1Mbu4.js";
|
|
5
|
+
import { stripTypeScriptTypes } from "node:module";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import crypto from "node:crypto";
|
|
10
|
+
var KIND = "file-change";
|
|
11
|
+
var DEFAULT_BATCH_MS = SOURCE_DEFAULTS.fs.batchMs;
|
|
12
|
+
var MAX_FILES_PER_BATCH = SOURCE_DEFAULTS.fs.digestQuota;
|
|
13
|
+
var UNTRACKED_KEY_CHARS = 16;
|
|
14
|
+
var MAX_ROOT_ENTRIES = 32;
|
|
15
|
+
var MAX_SAMPLE_FILES = 5;
|
|
16
|
+
var MAX_REL_PATH_CHARS = 200;
|
|
17
|
+
var IGNORED_DIR_NAMES = Object.freeze([
|
|
18
|
+
".git",
|
|
19
|
+
"node_modules",
|
|
20
|
+
".glimmervoid",
|
|
21
|
+
".cache",
|
|
22
|
+
".parcel-cache",
|
|
23
|
+
".turbo",
|
|
24
|
+
".next",
|
|
25
|
+
".nuxt",
|
|
26
|
+
".svelte-kit",
|
|
27
|
+
".gradle",
|
|
28
|
+
".tox",
|
|
29
|
+
".venv",
|
|
30
|
+
".pytest_cache",
|
|
31
|
+
".mypy_cache",
|
|
32
|
+
".ruff_cache",
|
|
33
|
+
".idea",
|
|
34
|
+
"__pycache__",
|
|
35
|
+
"dist",
|
|
36
|
+
"build",
|
|
37
|
+
"target",
|
|
38
|
+
"coverage"
|
|
39
|
+
]);
|
|
40
|
+
var IGNORED_FILE_SUFFIXES = Object.freeze([
|
|
41
|
+
"~",
|
|
42
|
+
".swp",
|
|
43
|
+
".swx",
|
|
44
|
+
".swo",
|
|
45
|
+
".tmp",
|
|
46
|
+
".temp",
|
|
47
|
+
".lock"
|
|
48
|
+
]);
|
|
49
|
+
var IGNORED_FILE_NAMES = Object.freeze([
|
|
50
|
+
".DS_Store",
|
|
51
|
+
"Thumbs.db",
|
|
52
|
+
"desktop.ini",
|
|
53
|
+
"4913"
|
|
54
|
+
]);
|
|
55
|
+
var DAEMON_CONFIG_SIBLINGS = Object.freeze([
|
|
56
|
+
"usage-lanes.json",
|
|
57
|
+
"usage-warehouse.json",
|
|
58
|
+
"usage-budget-state.json",
|
|
59
|
+
"pr-review-state.json",
|
|
60
|
+
"team-review-state.json",
|
|
61
|
+
"pairings.json",
|
|
62
|
+
"pairings-seen.json",
|
|
63
|
+
"litellm-pricing.json",
|
|
64
|
+
"update-check.json",
|
|
65
|
+
"recordings",
|
|
66
|
+
"uploads",
|
|
67
|
+
"memory"
|
|
68
|
+
]);
|
|
69
|
+
var ACTIVE_SESSION_STATES = Object.freeze([
|
|
70
|
+
"INITIALIZING",
|
|
71
|
+
"STARTING",
|
|
72
|
+
"RUNNING",
|
|
73
|
+
"WAITING",
|
|
74
|
+
"IDLE",
|
|
75
|
+
"COMPLETE"
|
|
76
|
+
]);
|
|
77
|
+
var CHANGE_KINDS = Object.freeze([
|
|
78
|
+
"create",
|
|
79
|
+
"update",
|
|
80
|
+
"delete"
|
|
81
|
+
]);
|
|
82
|
+
var CHANGE_VERBS = Object.freeze({
|
|
83
|
+
create: "created",
|
|
84
|
+
update: "updated",
|
|
85
|
+
delete: "deleted"
|
|
86
|
+
});
|
|
87
|
+
function toPosix(value) {
|
|
88
|
+
return String(value == null ? "" : value).split("\\").join("/");
|
|
89
|
+
}
|
|
90
|
+
function nonEmptyString(value) {
|
|
91
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
92
|
+
}
|
|
93
|
+
function buildIgnorePatterns(extraDirNames = []) {
|
|
94
|
+
const names = [];
|
|
95
|
+
for (const raw of [...IGNORED_DIR_NAMES, ...Array.isArray(extraDirNames) ? extraDirNames : []]) {
|
|
96
|
+
const name = nonEmptyString(raw);
|
|
97
|
+
if (!name || names.includes(name)) continue;
|
|
98
|
+
names.push(name);
|
|
99
|
+
}
|
|
100
|
+
const patterns = [];
|
|
101
|
+
for (const name of names) patterns.push(name, `**/${name}`, `**/${name}/**`);
|
|
102
|
+
for (const suffix of IGNORED_FILE_SUFFIXES) patterns.push(`**/*${suffix}`);
|
|
103
|
+
for (const name of IGNORED_FILE_NAMES) patterns.push(`**/${name}`);
|
|
104
|
+
return patterns;
|
|
105
|
+
}
|
|
106
|
+
function hasIgnoredSegment(relPath) {
|
|
107
|
+
for (const segment of toPosix(relPath).split("/")) {
|
|
108
|
+
if (!segment) continue;
|
|
109
|
+
if (IGNORED_DIR_NAMES.includes(segment)) return true;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
function isIgnoredFileName(base) {
|
|
114
|
+
if (IGNORED_FILE_NAMES.includes(base)) return true;
|
|
115
|
+
return IGNORED_FILE_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
116
|
+
}
|
|
117
|
+
function daemonWriteRules(configPath) {
|
|
118
|
+
const resolvedPath = nonEmptyString(configPath);
|
|
119
|
+
if (!resolvedPath) return null;
|
|
120
|
+
const configFile = path.resolve(resolvedPath);
|
|
121
|
+
const dir = path.dirname(configFile);
|
|
122
|
+
return {
|
|
123
|
+
paths: [configFile, ...DAEMON_CONFIG_SIBLINGS.map((name) => path.join(dir, name))],
|
|
124
|
+
dir,
|
|
125
|
+
prefix: `${path.basename(configFile)}.`
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function isDaemonDerivedSibling(rules, absolutePath) {
|
|
129
|
+
const resolved = path.resolve(absolutePath);
|
|
130
|
+
if (foldCase(path.dirname(resolved)) !== foldCase(rules.dir)) return false;
|
|
131
|
+
return foldCase(path.basename(resolved)).startsWith(foldCase(rules.prefix));
|
|
132
|
+
}
|
|
133
|
+
function foldCase(value) {
|
|
134
|
+
return process.platform === "win32" ? String(value).toLowerCase() : String(value);
|
|
135
|
+
}
|
|
136
|
+
function isPathInside(parent, child) {
|
|
137
|
+
const from = nonEmptyString(parent);
|
|
138
|
+
const to = nonEmptyString(child);
|
|
139
|
+
if (!from || !to) return false;
|
|
140
|
+
const resolvedParent = path.resolve(from);
|
|
141
|
+
const resolvedChild = path.resolve(to);
|
|
142
|
+
if (foldCase(resolvedParent) === foldCase(resolvedChild)) return true;
|
|
143
|
+
const relative = path.relative(resolvedParent, resolvedChild);
|
|
144
|
+
if (!relative || path.isAbsolute(relative)) return false;
|
|
145
|
+
return !toPosix(relative).startsWith("../");
|
|
146
|
+
}
|
|
147
|
+
function relativeWithin(root, absolutePath) {
|
|
148
|
+
const from = nonEmptyString(root);
|
|
149
|
+
const to = nonEmptyString(absolutePath);
|
|
150
|
+
if (!from || !to) return null;
|
|
151
|
+
const relative = path.relative(path.resolve(from), path.resolve(to));
|
|
152
|
+
if (!relative || path.isAbsolute(relative)) return null;
|
|
153
|
+
const posix = toPosix(relative);
|
|
154
|
+
if (posix.startsWith("../")) return null;
|
|
155
|
+
return posix;
|
|
156
|
+
}
|
|
157
|
+
function isIgnoredChange({ relPath, absolutePath = null, daemonRules = null } = {}) {
|
|
158
|
+
const relative = nonEmptyString(relPath);
|
|
159
|
+
if (!relative) return true;
|
|
160
|
+
if (hasIgnoredSegment(relative)) return true;
|
|
161
|
+
const base = toPosix(relative).split("/").pop();
|
|
162
|
+
if (base && isIgnoredFileName(base)) return true;
|
|
163
|
+
const absolute = nonEmptyString(absolutePath);
|
|
164
|
+
if (!absolute || !daemonRules) return false;
|
|
165
|
+
if (daemonRules.paths.some((daemonPath) => isPathInside(daemonPath, absolute))) return true;
|
|
166
|
+
return isDaemonDerivedSibling(daemonRules, absolute);
|
|
167
|
+
}
|
|
168
|
+
function normalizeRoots(raw) {
|
|
169
|
+
if (!Array.isArray(raw)) return [];
|
|
170
|
+
const roots = [];
|
|
171
|
+
for (const entry of raw) {
|
|
172
|
+
const value = nonEmptyString(entry);
|
|
173
|
+
if (!value || roots.includes(value)) continue;
|
|
174
|
+
roots.push(value);
|
|
175
|
+
if (roots.length >= MAX_ROOT_ENTRIES) break;
|
|
176
|
+
}
|
|
177
|
+
return roots;
|
|
178
|
+
}
|
|
179
|
+
function dedupeRoots(roots) {
|
|
180
|
+
const named = [];
|
|
181
|
+
for (const entry of Array.isArray(roots) ? roots : []) {
|
|
182
|
+
const value = nonEmptyString(entry);
|
|
183
|
+
if (!value) continue;
|
|
184
|
+
if (named.some((kept) => foldCase(path.resolve(kept)) === foldCase(path.resolve(value)))) continue;
|
|
185
|
+
named.push(value);
|
|
186
|
+
}
|
|
187
|
+
named.sort((left, right) => left.length - right.length || left.localeCompare(right));
|
|
188
|
+
const kept = [];
|
|
189
|
+
for (const root of named) {
|
|
190
|
+
if (kept.some((outer) => isPathInside(outer, root))) continue;
|
|
191
|
+
kept.push(root);
|
|
192
|
+
}
|
|
193
|
+
return kept;
|
|
194
|
+
}
|
|
195
|
+
function deriveSessionRoots(session) {
|
|
196
|
+
const dirs = [];
|
|
197
|
+
for (const dir of [session?.path, session?.worktreeDir]) {
|
|
198
|
+
const value = nonEmptyString(dir);
|
|
199
|
+
if (!value || dirs.includes(value)) continue;
|
|
200
|
+
dirs.push(value);
|
|
201
|
+
}
|
|
202
|
+
return dirs;
|
|
203
|
+
}
|
|
204
|
+
function isActiveSessionState(state) {
|
|
205
|
+
return ACTIVE_SESSION_STATES.includes(nonEmptyString(state) || "");
|
|
206
|
+
}
|
|
207
|
+
function createBatch() {
|
|
208
|
+
return {
|
|
209
|
+
files: /* @__PURE__ */ new Map(),
|
|
210
|
+
untracked: /* @__PURE__ */ new Set(),
|
|
211
|
+
floored: false
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function untrackedKey(relPath) {
|
|
215
|
+
return crypto.createHash("sha1").update(relPath, "utf8").digest("hex").slice(0, UNTRACKED_KEY_CHARS);
|
|
216
|
+
}
|
|
217
|
+
function truncatePath(relPath) {
|
|
218
|
+
if (relPath.length <= MAX_REL_PATH_CHARS) return relPath;
|
|
219
|
+
const scrubbed = scrubText(relPath);
|
|
220
|
+
if (scrubbed.length <= MAX_REL_PATH_CHARS) return scrubbed;
|
|
221
|
+
return `${scrubbed.slice(0, MAX_REL_PATH_CHARS)}...`;
|
|
222
|
+
}
|
|
223
|
+
function normalizeChangeKind(type) {
|
|
224
|
+
const kind = nonEmptyString(type);
|
|
225
|
+
if (!kind || !CHANGE_KINDS.includes(kind)) return null;
|
|
226
|
+
return kind;
|
|
227
|
+
}
|
|
228
|
+
function mergeChange(previous, next) {
|
|
229
|
+
if (!previous) return next;
|
|
230
|
+
if (previous === "create" && next === "delete") return null;
|
|
231
|
+
if (next === "delete") return "delete";
|
|
232
|
+
if (previous === "create") return "create";
|
|
233
|
+
return "update";
|
|
234
|
+
}
|
|
235
|
+
function recordChange(batch, relPath, type) {
|
|
236
|
+
const kind = normalizeChangeKind(type);
|
|
237
|
+
const relative = nonEmptyString(relPath);
|
|
238
|
+
if (!kind || !relative) return false;
|
|
239
|
+
const key = truncatePath(relative);
|
|
240
|
+
if (batch.files.has(key)) {
|
|
241
|
+
const merged = mergeChange(batch.files.get(key), kind);
|
|
242
|
+
if (merged === null) batch.files.delete(key);
|
|
243
|
+
if (merged !== null) batch.files.set(key, merged);
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
if (batch.files.size >= 2e3) {
|
|
247
|
+
const digest = untrackedKey(key);
|
|
248
|
+
if (batch.untracked.has(digest)) return true;
|
|
249
|
+
if (batch.untracked.size >= 1e4) {
|
|
250
|
+
batch.floored = true;
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
batch.untracked.add(digest);
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
batch.files.set(key, kind);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
function batchSize(batch) {
|
|
260
|
+
return batch.files.size + batch.untracked.size;
|
|
261
|
+
}
|
|
262
|
+
function fileChangeEvent({ root, relPath, change, now }) {
|
|
263
|
+
return {
|
|
264
|
+
source: "fs",
|
|
265
|
+
kind: KIND,
|
|
266
|
+
ts: now,
|
|
267
|
+
scope: {
|
|
268
|
+
root,
|
|
269
|
+
sessionId: null
|
|
270
|
+
},
|
|
271
|
+
summary: `${CHANGE_VERBS[change]} ${relPath}`,
|
|
272
|
+
detail: {
|
|
273
|
+
path: relPath,
|
|
274
|
+
change
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function countByKind(batch) {
|
|
279
|
+
const counts = {
|
|
280
|
+
create: 0,
|
|
281
|
+
update: 0,
|
|
282
|
+
delete: 0
|
|
283
|
+
};
|
|
284
|
+
for (const change of batch.files.values()) counts[change] += 1;
|
|
285
|
+
return counts;
|
|
286
|
+
}
|
|
287
|
+
function burstSummary(batch) {
|
|
288
|
+
const counts = countByKind(batch);
|
|
289
|
+
const parts = [];
|
|
290
|
+
if (counts.create) parts.push(`${counts.create} created`);
|
|
291
|
+
if (counts.update) parts.push(`${counts.update} updated`);
|
|
292
|
+
if (counts.delete) parts.push(`${counts.delete} deleted`);
|
|
293
|
+
const total = batchSize(batch);
|
|
294
|
+
const tail = parts.length > 0 ? `: ${parts.join(", ")}` : "";
|
|
295
|
+
return {
|
|
296
|
+
summary: `${batch.floored ? `at least ${total}` : `${total}`} files changed${tail}`,
|
|
297
|
+
counts,
|
|
298
|
+
total
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function sampleOf(batch) {
|
|
302
|
+
return [...batch.files.keys()].slice(0, MAX_SAMPLE_FILES).join(", ");
|
|
303
|
+
}
|
|
304
|
+
function decideFsEvents(batch, { root = null, now = 0 } = {}) {
|
|
305
|
+
const total = batchSize(batch);
|
|
306
|
+
if (total === 0) return [];
|
|
307
|
+
if (total <= MAX_FILES_PER_BATCH && batch.untracked.size === 0) {
|
|
308
|
+
const events = [];
|
|
309
|
+
for (const [relPath, change] of batch.files) events.push(fileChangeEvent({
|
|
310
|
+
root,
|
|
311
|
+
relPath,
|
|
312
|
+
change,
|
|
313
|
+
now
|
|
314
|
+
}));
|
|
315
|
+
return events;
|
|
316
|
+
}
|
|
317
|
+
const { summary, counts } = burstSummary(batch);
|
|
318
|
+
return [{
|
|
319
|
+
source: "fs",
|
|
320
|
+
kind: KIND,
|
|
321
|
+
ts: now,
|
|
322
|
+
scope: {
|
|
323
|
+
root,
|
|
324
|
+
sessionId: null
|
|
325
|
+
},
|
|
326
|
+
summary,
|
|
327
|
+
detail: {
|
|
328
|
+
files: total,
|
|
329
|
+
atLeast: batch.floored,
|
|
330
|
+
created: counts.create,
|
|
331
|
+
updated: counts.update,
|
|
332
|
+
deleted: counts.delete,
|
|
333
|
+
sample: sampleOf(batch)
|
|
334
|
+
}
|
|
335
|
+
}];
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region server/lane-log.ts
|
|
339
|
+
var MAX_ONCE_KEYS = 256;
|
|
340
|
+
function renderFieldValue(value) {
|
|
341
|
+
if (typeof value !== "string") return String(value);
|
|
342
|
+
if (value !== "" && !/[ ="\u0000-\u001f\u007f]/.test(value)) return value;
|
|
343
|
+
return JSON.stringify(value);
|
|
344
|
+
}
|
|
345
|
+
function renderLine(prefix, message, fields) {
|
|
346
|
+
const renderedFields = Object.entries(fields ?? {}).flatMap(([key, value]) => {
|
|
347
|
+
if (value === null || value === void 0) return [];
|
|
348
|
+
return `${key}=${renderFieldValue(value)}`;
|
|
349
|
+
});
|
|
350
|
+
if (renderedFields.length === 0) return `${prefix} ${message}`;
|
|
351
|
+
return `${prefix} ${message} ${renderedFields.join(" ")}`;
|
|
352
|
+
}
|
|
353
|
+
function createLaneLog({ prefix = "", logger = console, debugFlag = false } = {}) {
|
|
354
|
+
const onceKeys = /* @__PURE__ */ new Set();
|
|
355
|
+
function note(message, fields) {
|
|
356
|
+
if (!logger || typeof logger.log !== "function") return;
|
|
357
|
+
logger.log(renderLine(prefix, message, fields));
|
|
358
|
+
}
|
|
359
|
+
function warn(message, fields) {
|
|
360
|
+
if (!logger || typeof logger.warn !== "function") return;
|
|
361
|
+
logger.warn(renderLine(prefix, message, fields));
|
|
362
|
+
}
|
|
363
|
+
function warnOnce(key, message, fields) {
|
|
364
|
+
if (onceKeys.has(key)) return;
|
|
365
|
+
onceKeys.add(key);
|
|
366
|
+
const [oldestKey] = onceKeys;
|
|
367
|
+
if (onceKeys.size > MAX_ONCE_KEYS && oldestKey !== void 0) onceKeys.delete(oldestKey);
|
|
368
|
+
warn(message, fields);
|
|
369
|
+
}
|
|
370
|
+
function isDebug() {
|
|
371
|
+
if (typeof debugFlag !== "function") return debugFlag === true;
|
|
372
|
+
try {
|
|
373
|
+
return debugFlag() === true;
|
|
374
|
+
} catch {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function debugNote(buildMessage, buildFields) {
|
|
379
|
+
if (!isDebug()) return;
|
|
380
|
+
note(buildMessage(), buildFields?.());
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
note,
|
|
384
|
+
warn,
|
|
385
|
+
warnOnce,
|
|
386
|
+
debugNote
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region server/core/editor-extension-core.ts
|
|
391
|
+
var EDITOR_CANDIDATES = [
|
|
392
|
+
{
|
|
393
|
+
command: "codium",
|
|
394
|
+
label: "VSCodium",
|
|
395
|
+
macAppBundleName: "VSCodium"
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
command: "code",
|
|
399
|
+
label: "VS Code",
|
|
400
|
+
macAppBundleName: "Visual Studio Code"
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
command: "code-insiders",
|
|
404
|
+
label: "VS Code Insiders",
|
|
405
|
+
macAppBundleName: "Visual Studio Code - Insiders"
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
command: "cursor",
|
|
409
|
+
label: "Cursor",
|
|
410
|
+
macAppBundleName: "Cursor"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
command: "windsurf",
|
|
414
|
+
label: "Windsurf",
|
|
415
|
+
macAppBundleName: "Windsurf"
|
|
416
|
+
}
|
|
417
|
+
];
|
|
418
|
+
var MACOS_BUNDLE_CLI_DIR = "Contents/Resources/app/bin";
|
|
419
|
+
function editorBundleCandidatePaths(candidate, { platform, homeDir = null }) {
|
|
420
|
+
if (platform !== "darwin") return [];
|
|
421
|
+
const bundleName = candidate.macAppBundleName;
|
|
422
|
+
if (!bundleName) return [];
|
|
423
|
+
const applicationDirs = ["/Applications"];
|
|
424
|
+
if (homeDir) applicationDirs.push(path.posix.join(homeDir, "Applications"));
|
|
425
|
+
return applicationDirs.map((applicationDir) => path.posix.join(applicationDir, `${bundleName}.app`, MACOS_BUNDLE_CLI_DIR, candidate.command));
|
|
426
|
+
}
|
|
427
|
+
function editorLabelFor(command, platform) {
|
|
428
|
+
const byCommand = EDITOR_CANDIDATES.find((candidate) => candidate.command === command);
|
|
429
|
+
if (byCommand) return byCommand.label;
|
|
430
|
+
const basename = (platform === "win32" ? path.win32 : path.posix).basename(command);
|
|
431
|
+
return EDITOR_CANDIDATES.find((candidate) => candidate.command === basename)?.label || command;
|
|
432
|
+
}
|
|
433
|
+
function isAbsoluteEditorPath(requested, platform) {
|
|
434
|
+
return (platform === "win32" ? path.win32 : path.posix).isAbsolute(requested);
|
|
435
|
+
}
|
|
436
|
+
function resolveEditorPathsFor({ platform, homeDir = null, matchesOnPath, exists }) {
|
|
437
|
+
const resolved = {};
|
|
438
|
+
for (const candidate of EDITOR_CANDIDATES) {
|
|
439
|
+
const [firstOnPath] = matchesOnPath(candidate.command);
|
|
440
|
+
const commandPath = firstOnPath || editorBundleCandidatePaths(candidate, {
|
|
441
|
+
platform,
|
|
442
|
+
homeDir
|
|
443
|
+
}).find(exists);
|
|
444
|
+
if (!commandPath) continue;
|
|
445
|
+
resolved[candidate.command] = commandPath;
|
|
446
|
+
}
|
|
447
|
+
return resolved;
|
|
448
|
+
}
|
|
449
|
+
function decideEditorTargets({ requested = null, resolvedByCommand = {}, probe }) {
|
|
450
|
+
if (requested) {
|
|
451
|
+
const label = editorLabelFor(requested, probe.platform);
|
|
452
|
+
if (isAbsoluteEditorPath(requested, probe.platform)) {
|
|
453
|
+
if (!probe.exists(requested)) return {
|
|
454
|
+
targets: [],
|
|
455
|
+
reason: `editor path does not exist on disk: ${requested}`
|
|
456
|
+
};
|
|
457
|
+
return {
|
|
458
|
+
targets: [{
|
|
459
|
+
command: requested,
|
|
460
|
+
label,
|
|
461
|
+
commandPath: requested
|
|
462
|
+
}],
|
|
463
|
+
reason: "requested"
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const commandPath = resolvedByCommand[requested];
|
|
467
|
+
if (!commandPath) return {
|
|
468
|
+
targets: [],
|
|
469
|
+
reason: `editor not found on PATH: ${requested}`
|
|
470
|
+
};
|
|
471
|
+
return {
|
|
472
|
+
targets: [{
|
|
473
|
+
command: requested,
|
|
474
|
+
label,
|
|
475
|
+
commandPath
|
|
476
|
+
}],
|
|
477
|
+
reason: "requested"
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
const targets = [];
|
|
481
|
+
for (const candidate of EDITOR_CANDIDATES) {
|
|
482
|
+
const commandPath = resolvedByCommand[candidate.command];
|
|
483
|
+
if (!commandPath) continue;
|
|
484
|
+
targets.push({
|
|
485
|
+
command: candidate.command,
|
|
486
|
+
label: candidate.label,
|
|
487
|
+
commandPath
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
if (targets.length === 0) return {
|
|
491
|
+
targets: [],
|
|
492
|
+
reason: "no VS Code family editor found on PATH or on disk"
|
|
493
|
+
};
|
|
494
|
+
return {
|
|
495
|
+
targets,
|
|
496
|
+
reason: "detected"
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
function relayStamp(relayPath) {
|
|
500
|
+
return `${JSON.stringify({ relayPath }, null, 2)}\n`;
|
|
501
|
+
}
|
|
502
|
+
function visionsExtensionFiles({ manifestJson, extensionJs, convertJs, lspCoreJs, relayPath }) {
|
|
503
|
+
return [
|
|
504
|
+
{
|
|
505
|
+
path: "package.json",
|
|
506
|
+
data: manifestJson
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
path: "extension.js",
|
|
510
|
+
data: extensionJs
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
path: "lsp-convert.js",
|
|
514
|
+
data: convertJs
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
path: "visions-lsp-core.js",
|
|
518
|
+
data: lspCoreJs
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
path: "relay-path.json",
|
|
522
|
+
data: relayStamp(relayPath)
|
|
523
|
+
}
|
|
524
|
+
];
|
|
525
|
+
}
|
|
526
|
+
function parseInstalledExtensions(stdout) {
|
|
527
|
+
return String(stdout || "").split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== "");
|
|
528
|
+
}
|
|
529
|
+
function isExtensionInstalled(stdout, extensionId) {
|
|
530
|
+
const wanted = String(extensionId).toLowerCase();
|
|
531
|
+
return parseInstalledExtensions(stdout).some((line) => line.toLowerCase() === wanted);
|
|
532
|
+
}
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region server/core/editor-setup-core.ts
|
|
535
|
+
function relayInvocation({ glimmervoidOnPath = true, cliPath = "", nodePath = "node" } = {}) {
|
|
536
|
+
if (glimmervoidOnPath) return {
|
|
537
|
+
command: "glimmervoid",
|
|
538
|
+
args: ["visions", "relay"]
|
|
539
|
+
};
|
|
540
|
+
return {
|
|
541
|
+
command: nodePath,
|
|
542
|
+
args: [
|
|
543
|
+
cliPath,
|
|
544
|
+
"visions",
|
|
545
|
+
"relay"
|
|
546
|
+
]
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function invocationParts(invocation) {
|
|
550
|
+
return [invocation.command, ...invocation.args];
|
|
551
|
+
}
|
|
552
|
+
function commandLine(invocation) {
|
|
553
|
+
return invocationParts(invocation).join(" ");
|
|
554
|
+
}
|
|
555
|
+
function jsonCommandArray(invocation) {
|
|
556
|
+
return JSON.stringify(invocationParts(invocation));
|
|
557
|
+
}
|
|
558
|
+
function neovimSnippet(invocation) {
|
|
559
|
+
return [
|
|
560
|
+
"vim.api.nvim_create_autocmd('FileType', {",
|
|
561
|
+
" pattern = 'markdown',",
|
|
562
|
+
" callback = function()",
|
|
563
|
+
` vim.lsp.start({ name = 'glimmervoid-visions', cmd = { ${invocationParts(invocation).map((part) => `'${part}'`).join(", ")} } })`,
|
|
564
|
+
" end,",
|
|
565
|
+
"})"
|
|
566
|
+
].join("\n");
|
|
567
|
+
}
|
|
568
|
+
function helixSnippet(invocation) {
|
|
569
|
+
return [
|
|
570
|
+
"[language-server.glimmervoid-visions]",
|
|
571
|
+
`command = "${invocation.command}"`,
|
|
572
|
+
`args = ${JSON.stringify(invocation.args)}`,
|
|
573
|
+
"",
|
|
574
|
+
"[[language]]",
|
|
575
|
+
"name = \"markdown\"",
|
|
576
|
+
"language-servers = [\"marksman\", \"glimmervoid-visions\"]"
|
|
577
|
+
].join("\n");
|
|
578
|
+
}
|
|
579
|
+
function emacsSnippet(invocation) {
|
|
580
|
+
return [
|
|
581
|
+
"(with-eval-after-load 'eglot",
|
|
582
|
+
" (add-to-list 'eglot-server-programs",
|
|
583
|
+
` '(markdown-mode . (${invocationParts(invocation).map((part) => `"${part}"`).join(" ")}))))`
|
|
584
|
+
].join("\n");
|
|
585
|
+
}
|
|
586
|
+
function sublimeSnippet(invocation) {
|
|
587
|
+
return [
|
|
588
|
+
"{",
|
|
589
|
+
" \"clients\": {",
|
|
590
|
+
" \"glimmervoid-visions\": {",
|
|
591
|
+
" \"enabled\": true,",
|
|
592
|
+
` "command": ${jsonCommandArray(invocation)},`,
|
|
593
|
+
" \"selector\": \"text.html.markdown\"",
|
|
594
|
+
" }",
|
|
595
|
+
" }",
|
|
596
|
+
"}"
|
|
597
|
+
].join("\n");
|
|
598
|
+
}
|
|
599
|
+
function kateSnippet(invocation) {
|
|
600
|
+
return [
|
|
601
|
+
"{",
|
|
602
|
+
" \"servers\": {",
|
|
603
|
+
" \"markdown\": {",
|
|
604
|
+
` "command": ${jsonCommandArray(invocation)},`,
|
|
605
|
+
" \"highlightingModeRegex\": \"^Markdown$\"",
|
|
606
|
+
" }",
|
|
607
|
+
" }",
|
|
608
|
+
"}"
|
|
609
|
+
].join("\n");
|
|
610
|
+
}
|
|
611
|
+
function genericSnippet(invocation) {
|
|
612
|
+
return `command: ${commandLine(invocation)}\ntransport: stdio\nlanguages: markdown`;
|
|
613
|
+
}
|
|
614
|
+
var EDITOR_RECIPES = [
|
|
615
|
+
{
|
|
616
|
+
id: "vscode",
|
|
617
|
+
label: "VS Code, VSCodium, Cursor, Windsurf",
|
|
618
|
+
where: "installed for you",
|
|
619
|
+
build: () => "glimmervoid visions install"
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
id: "neovim",
|
|
623
|
+
label: "Neovim",
|
|
624
|
+
where: "init.lua (or any file it loads)",
|
|
625
|
+
build: neovimSnippet
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
id: "helix",
|
|
629
|
+
label: "Helix",
|
|
630
|
+
where: "~/.config/helix/languages.toml",
|
|
631
|
+
build: helixSnippet
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
id: "emacs",
|
|
635
|
+
label: "Emacs (eglot)",
|
|
636
|
+
where: "init.el",
|
|
637
|
+
build: emacsSnippet
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
id: "sublime",
|
|
641
|
+
label: "Sublime Text (LSP package)",
|
|
642
|
+
where: "Preferences: LSP Settings",
|
|
643
|
+
build: sublimeSnippet
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
id: "kate",
|
|
647
|
+
label: "Kate",
|
|
648
|
+
where: "Settings > LSP Client > User Server Settings",
|
|
649
|
+
build: kateSnippet
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
id: "jetbrains",
|
|
653
|
+
label: "JetBrains IDEs (LSP4IJ plugin)",
|
|
654
|
+
where: "Settings > Language Servers > New > Command",
|
|
655
|
+
build: commandLine
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
id: "other",
|
|
659
|
+
label: "Any other LSP client",
|
|
660
|
+
where: "wherever it registers a language server",
|
|
661
|
+
build: genericSnippet
|
|
662
|
+
}
|
|
663
|
+
];
|
|
664
|
+
function recipeIds() {
|
|
665
|
+
return EDITOR_RECIPES.map((recipe) => recipe.id);
|
|
666
|
+
}
|
|
667
|
+
function buildSetupGuide({ editorId = null, invocation }) {
|
|
668
|
+
const recipes = editorId ? EDITOR_RECIPES.filter((recipe) => recipe.id === editorId) : EDITOR_RECIPES;
|
|
669
|
+
if (recipes.length === 0) return {
|
|
670
|
+
ok: false,
|
|
671
|
+
reason: `unknown editor: ${editorId}`,
|
|
672
|
+
sections: []
|
|
673
|
+
};
|
|
674
|
+
return {
|
|
675
|
+
ok: true,
|
|
676
|
+
reason: "ok",
|
|
677
|
+
sections: recipes.map((recipe) => ({
|
|
678
|
+
id: recipe.id,
|
|
679
|
+
label: recipe.label,
|
|
680
|
+
where: recipe.where,
|
|
681
|
+
snippet: recipe.build(invocation)
|
|
682
|
+
}))
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
//#endregion
|
|
686
|
+
//#region server/core/visions-defaults-core.ts
|
|
687
|
+
var IMPLIED_INGEST = Object.freeze({
|
|
688
|
+
enabled: true,
|
|
689
|
+
sources: Object.freeze({
|
|
690
|
+
fs: Object.freeze({ enabled: true }),
|
|
691
|
+
git: Object.freeze({ enabled: true }),
|
|
692
|
+
editor: Object.freeze({ enabled: true })
|
|
693
|
+
})
|
|
694
|
+
});
|
|
695
|
+
function isPlainObject(value) {
|
|
696
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
697
|
+
}
|
|
698
|
+
function impliedIngestChanges(config) {
|
|
699
|
+
if (!isPlainObject(config.ingest)) return [{
|
|
700
|
+
path: ["ingest"],
|
|
701
|
+
value: JSON.parse(JSON.stringify(IMPLIED_INGEST)),
|
|
702
|
+
why: "visions needs machine context"
|
|
703
|
+
}];
|
|
704
|
+
const changes = [];
|
|
705
|
+
if (config.ingest.enabled !== true && config.ingest.enabled !== false) changes.push({
|
|
706
|
+
path: ["ingest", "enabled"],
|
|
707
|
+
value: true,
|
|
708
|
+
why: "visions needs machine context"
|
|
709
|
+
});
|
|
710
|
+
if (config.ingest.enabled === false) return changes;
|
|
711
|
+
const sources = isPlainObject(config.ingest.sources) ? config.ingest.sources : {};
|
|
712
|
+
for (const [name, value] of Object.entries(IMPLIED_INGEST.sources)) {
|
|
713
|
+
if (isPlainObject(sources[name])) continue;
|
|
714
|
+
changes.push({
|
|
715
|
+
path: [
|
|
716
|
+
"ingest",
|
|
717
|
+
"sources",
|
|
718
|
+
name
|
|
719
|
+
],
|
|
720
|
+
value: JSON.parse(JSON.stringify(value)),
|
|
721
|
+
why: `visions implies the ${name} source`
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
return changes;
|
|
725
|
+
}
|
|
726
|
+
function decideImpliedDefaults(config) {
|
|
727
|
+
if (!config) return { changes: [] };
|
|
728
|
+
const visions = config.visions;
|
|
729
|
+
if (!isPlainObject(visions) || visions.enabled !== true) return { changes: [] };
|
|
730
|
+
const changes = impliedIngestChanges(config);
|
|
731
|
+
if (!isPlainObject(visions.dispatch)) changes.push({
|
|
732
|
+
path: ["visions", "dispatch"],
|
|
733
|
+
value: { enabled: true },
|
|
734
|
+
why: "visions implies its model dispatch"
|
|
735
|
+
});
|
|
736
|
+
return { changes };
|
|
737
|
+
}
|
|
738
|
+
function applyChanges(config, changes) {
|
|
739
|
+
for (const change of changes) {
|
|
740
|
+
let cursor = config;
|
|
741
|
+
for (const key of change.path.slice(0, -1)) {
|
|
742
|
+
const existing = cursor[key];
|
|
743
|
+
if (isPlainObject(existing)) {
|
|
744
|
+
cursor = existing;
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
const created = {};
|
|
748
|
+
cursor[key] = created;
|
|
749
|
+
cursor = created;
|
|
750
|
+
}
|
|
751
|
+
cursor[change.path[change.path.length - 1]] = change.value;
|
|
752
|
+
}
|
|
753
|
+
return config;
|
|
754
|
+
}
|
|
755
|
+
//#endregion
|
|
756
|
+
//#region server/core/vsix-core.ts
|
|
757
|
+
var CRC_TABLE = buildCrcTable();
|
|
758
|
+
var LOCAL_HEADER_SIG = 67324752;
|
|
759
|
+
var CENTRAL_HEADER_SIG = 33639248;
|
|
760
|
+
var END_OF_CENTRAL_SIG = 101010256;
|
|
761
|
+
var STORED = 0;
|
|
762
|
+
var ZIP_VERSION = 20;
|
|
763
|
+
var UTF8_FLAG = 2048;
|
|
764
|
+
var DOS_TIME = 0;
|
|
765
|
+
var DOS_DATE = 33;
|
|
766
|
+
function buildCrcTable() {
|
|
767
|
+
const table = /* @__PURE__ */ new Uint32Array(256);
|
|
768
|
+
for (let index = 0; index < 256; index += 1) {
|
|
769
|
+
let value = index;
|
|
770
|
+
for (let bit = 0; bit < 8; bit += 1) value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
771
|
+
table[index] = value >>> 0;
|
|
772
|
+
}
|
|
773
|
+
return table;
|
|
774
|
+
}
|
|
775
|
+
function crc32(buffer) {
|
|
776
|
+
let crc = 4294967295;
|
|
777
|
+
for (let index = 0; index < buffer.length; index += 1) crc = CRC_TABLE[(crc ^ buffer[index]) & 255] ^ crc >>> 8;
|
|
778
|
+
return (crc ^ 4294967295) >>> 0;
|
|
779
|
+
}
|
|
780
|
+
function localHeader(entry, nameBytes) {
|
|
781
|
+
const header = Buffer.alloc(30);
|
|
782
|
+
header.writeUInt32LE(LOCAL_HEADER_SIG, 0);
|
|
783
|
+
header.writeUInt16LE(ZIP_VERSION, 4);
|
|
784
|
+
header.writeUInt16LE(UTF8_FLAG, 6);
|
|
785
|
+
header.writeUInt16LE(STORED, 8);
|
|
786
|
+
header.writeUInt16LE(DOS_TIME, 10);
|
|
787
|
+
header.writeUInt16LE(DOS_DATE, 12);
|
|
788
|
+
header.writeUInt32LE(entry.crc, 14);
|
|
789
|
+
header.writeUInt32LE(entry.size, 18);
|
|
790
|
+
header.writeUInt32LE(entry.size, 22);
|
|
791
|
+
header.writeUInt16LE(nameBytes.length, 26);
|
|
792
|
+
header.writeUInt16LE(0, 28);
|
|
793
|
+
return header;
|
|
794
|
+
}
|
|
795
|
+
function centralHeader(entry, nameBytes) {
|
|
796
|
+
const header = Buffer.alloc(46);
|
|
797
|
+
header.writeUInt32LE(CENTRAL_HEADER_SIG, 0);
|
|
798
|
+
header.writeUInt16LE(ZIP_VERSION, 4);
|
|
799
|
+
header.writeUInt16LE(ZIP_VERSION, 6);
|
|
800
|
+
header.writeUInt16LE(UTF8_FLAG, 8);
|
|
801
|
+
header.writeUInt16LE(STORED, 10);
|
|
802
|
+
header.writeUInt16LE(DOS_TIME, 12);
|
|
803
|
+
header.writeUInt16LE(DOS_DATE, 14);
|
|
804
|
+
header.writeUInt32LE(entry.crc, 16);
|
|
805
|
+
header.writeUInt32LE(entry.size, 20);
|
|
806
|
+
header.writeUInt32LE(entry.size, 24);
|
|
807
|
+
header.writeUInt16LE(nameBytes.length, 28);
|
|
808
|
+
header.writeUInt16LE(0, 30);
|
|
809
|
+
header.writeUInt16LE(0, 32);
|
|
810
|
+
header.writeUInt16LE(0, 34);
|
|
811
|
+
header.writeUInt16LE(0, 36);
|
|
812
|
+
header.writeUInt32LE(0, 38);
|
|
813
|
+
header.writeUInt32LE(entry.offset, 42);
|
|
814
|
+
return header;
|
|
815
|
+
}
|
|
816
|
+
function buildZip(files) {
|
|
817
|
+
const chunks = [];
|
|
818
|
+
const central = [];
|
|
819
|
+
let offset = 0;
|
|
820
|
+
for (const file of files) {
|
|
821
|
+
const nameBytes = Buffer.from(file.path, "utf8");
|
|
822
|
+
const data = Buffer.isBuffer(file.data) ? file.data : Buffer.from(String(file.data), "utf8");
|
|
823
|
+
const entry = {
|
|
824
|
+
crc: crc32(data),
|
|
825
|
+
size: data.length,
|
|
826
|
+
offset
|
|
827
|
+
};
|
|
828
|
+
const header = localHeader(entry, nameBytes);
|
|
829
|
+
chunks.push(header, nameBytes, data);
|
|
830
|
+
offset += header.length + nameBytes.length + data.length;
|
|
831
|
+
central.push({
|
|
832
|
+
entry,
|
|
833
|
+
nameBytes
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
const centralOffset = offset;
|
|
837
|
+
let centralSize = 0;
|
|
838
|
+
for (const { entry, nameBytes } of central) {
|
|
839
|
+
const header = centralHeader(entry, nameBytes);
|
|
840
|
+
chunks.push(header, nameBytes);
|
|
841
|
+
centralSize += header.length + nameBytes.length;
|
|
842
|
+
}
|
|
843
|
+
const end = Buffer.alloc(22);
|
|
844
|
+
end.writeUInt32LE(END_OF_CENTRAL_SIG, 0);
|
|
845
|
+
end.writeUInt16LE(0, 4);
|
|
846
|
+
end.writeUInt16LE(0, 6);
|
|
847
|
+
end.writeUInt16LE(central.length, 8);
|
|
848
|
+
end.writeUInt16LE(central.length, 10);
|
|
849
|
+
end.writeUInt32LE(centralSize, 12);
|
|
850
|
+
end.writeUInt32LE(centralOffset, 16);
|
|
851
|
+
end.writeUInt16LE(0, 20);
|
|
852
|
+
chunks.push(end);
|
|
853
|
+
return Buffer.concat(chunks);
|
|
854
|
+
}
|
|
855
|
+
function escapeXml(value) {
|
|
856
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
857
|
+
}
|
|
858
|
+
function contentTypesXml() {
|
|
859
|
+
return [
|
|
860
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\"?>",
|
|
861
|
+
"<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">",
|
|
862
|
+
"<Default Extension=\"json\" ContentType=\"application/json\"/>",
|
|
863
|
+
"<Default Extension=\"js\" ContentType=\"application/javascript\"/>",
|
|
864
|
+
"<Default Extension=\"md\" ContentType=\"text/markdown\"/>",
|
|
865
|
+
"<Default Extension=\"vsixmanifest\" ContentType=\"text/xml\"/>",
|
|
866
|
+
"</Types>"
|
|
867
|
+
].join("");
|
|
868
|
+
}
|
|
869
|
+
function vsixManifestXml({ id, publisher, version, displayName, description, engine }) {
|
|
870
|
+
return [
|
|
871
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\"?>",
|
|
872
|
+
"<PackageManifest Version=\"2.0.0\" xmlns=\"http://schemas.microsoft.com/developer/vsx-schema/2011\">",
|
|
873
|
+
"<Metadata>",
|
|
874
|
+
`<Identity Language="en-US" Id="${escapeXml(id)}" Version="${escapeXml(version)}" Publisher="${escapeXml(publisher)}"/>`,
|
|
875
|
+
`<DisplayName>${escapeXml(displayName)}</DisplayName>`,
|
|
876
|
+
`<Description xml:space="preserve">${escapeXml(description)}</Description>`,
|
|
877
|
+
"<Tags></Tags>",
|
|
878
|
+
"<Categories>Other</Categories>",
|
|
879
|
+
"<GalleryFlags>Public</GalleryFlags>",
|
|
880
|
+
"<Properties>",
|
|
881
|
+
`<Property Id="Microsoft.VisualStudio.Code.Engine" Value="${escapeXml(engine)}"/>`,
|
|
882
|
+
"<Property Id=\"Microsoft.VisualStudio.Code.ExtensionDependencies\" Value=\"\"/>",
|
|
883
|
+
"<Property Id=\"Microsoft.VisualStudio.Code.ExtensionPack\" Value=\"\"/>",
|
|
884
|
+
"<Property Id=\"Microsoft.VisualStudio.Code.ExtensionKind\" Value=\"workspace\"/>",
|
|
885
|
+
"</Properties>",
|
|
886
|
+
"</Metadata>",
|
|
887
|
+
"<Installation><InstallationTarget Id=\"Microsoft.VisualStudio.Code\"/></Installation>",
|
|
888
|
+
"<Dependencies/>",
|
|
889
|
+
"<Assets><Asset Type=\"Microsoft.VisualStudio.Code.Manifest\" Path=\"extension/package.json\" Addressable=\"true\"/></Assets>",
|
|
890
|
+
"</PackageManifest>"
|
|
891
|
+
].join("");
|
|
892
|
+
}
|
|
893
|
+
function buildVsix({ manifest, extensionFiles = [] }) {
|
|
894
|
+
const files = [{
|
|
895
|
+
path: "extension.vsixmanifest",
|
|
896
|
+
data: vsixManifestXml({
|
|
897
|
+
id: manifest.name,
|
|
898
|
+
publisher: manifest.publisher,
|
|
899
|
+
version: manifest.version,
|
|
900
|
+
displayName: manifest.displayName || manifest.name,
|
|
901
|
+
description: manifest.description || manifest.displayName || manifest.name,
|
|
902
|
+
engine: manifest.engines?.vscode || "*"
|
|
903
|
+
})
|
|
904
|
+
}, {
|
|
905
|
+
path: "[Content_Types].xml",
|
|
906
|
+
data: contentTypesXml()
|
|
907
|
+
}];
|
|
908
|
+
for (const file of extensionFiles) files.push({
|
|
909
|
+
path: `extension/${file.path}`,
|
|
910
|
+
data: file.data
|
|
911
|
+
});
|
|
912
|
+
return buildZip(files);
|
|
913
|
+
}
|
|
914
|
+
function extensionIdOf(manifest) {
|
|
915
|
+
return `${manifest.publisher}.${manifest.name}`;
|
|
916
|
+
}
|
|
917
|
+
//#endregion
|
|
918
|
+
//#region server/core/editor-wire-core.ts
|
|
919
|
+
var MARKER_NAME = "glimmervoid-visions";
|
|
920
|
+
var SERVER_ID = "glimmervoid-visions";
|
|
921
|
+
var HELIX_BEGIN = `# >>> ${MARKER_NAME}`;
|
|
922
|
+
var HELIX_END = `# <<< ${MARKER_NAME}`;
|
|
923
|
+
var EMACS_BEGIN = `;; >>> ${MARKER_NAME}`;
|
|
924
|
+
var EMACS_END = `;; <<< ${MARKER_NAME}`;
|
|
925
|
+
var UNCHANGED = "unchanged";
|
|
926
|
+
function parts(invocation) {
|
|
927
|
+
return [invocation.command, ...invocation.args];
|
|
928
|
+
}
|
|
929
|
+
function splitMarked(text, begin, end) {
|
|
930
|
+
const startAt = text.indexOf(begin);
|
|
931
|
+
if (startAt === -1) return {
|
|
932
|
+
hasBlock: false,
|
|
933
|
+
before: text,
|
|
934
|
+
after: ""
|
|
935
|
+
};
|
|
936
|
+
const endAt = text.indexOf(end, startAt);
|
|
937
|
+
if (endAt === -1) return {
|
|
938
|
+
hasBlock: false,
|
|
939
|
+
unterminated: true,
|
|
940
|
+
before: text,
|
|
941
|
+
after: ""
|
|
942
|
+
};
|
|
943
|
+
return {
|
|
944
|
+
hasBlock: true,
|
|
945
|
+
before: text.slice(0, startAt),
|
|
946
|
+
after: text.slice(endAt + end.length)
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
function appendBlock(text, block) {
|
|
950
|
+
const body = text.trimEnd();
|
|
951
|
+
return `${body}${body === "" ? "" : "\n\n"}${block}\n`;
|
|
952
|
+
}
|
|
953
|
+
function replaceMarkedBlock(text, begin, end, block) {
|
|
954
|
+
const split = splitMarked(text, begin, end);
|
|
955
|
+
if (split.unterminated) return {
|
|
956
|
+
text,
|
|
957
|
+
changed: false,
|
|
958
|
+
reason: "unterminated-block"
|
|
959
|
+
};
|
|
960
|
+
if (!split.hasBlock) return {
|
|
961
|
+
text: appendBlock(text, block),
|
|
962
|
+
changed: true,
|
|
963
|
+
reason: "appended"
|
|
964
|
+
};
|
|
965
|
+
const merged = `${split.before}${block}${split.after}`;
|
|
966
|
+
if (merged === text) return {
|
|
967
|
+
text,
|
|
968
|
+
changed: false,
|
|
969
|
+
reason: UNCHANGED
|
|
970
|
+
};
|
|
971
|
+
return {
|
|
972
|
+
text: merged,
|
|
973
|
+
changed: true,
|
|
974
|
+
reason: "updated"
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
function neovimDropIn(invocation) {
|
|
978
|
+
return [
|
|
979
|
+
`-- Written by glimmervoid; edit glimmervoid's config, not this file. Delete it to unwire ${MARKER_NAME}.`,
|
|
980
|
+
"vim.api.nvim_create_autocmd('FileType', {",
|
|
981
|
+
" pattern = 'markdown',",
|
|
982
|
+
` callback = function()`,
|
|
983
|
+
` vim.lsp.start({ name = '${SERVER_ID}', cmd = { ${parts(invocation).map((part) => `'${part}'`).join(", ")} } })`,
|
|
984
|
+
" end,",
|
|
985
|
+
"})",
|
|
986
|
+
""
|
|
987
|
+
].join("\n");
|
|
988
|
+
}
|
|
989
|
+
function helixServerLines(invocation) {
|
|
990
|
+
return [
|
|
991
|
+
HELIX_BEGIN,
|
|
992
|
+
`[language-server.${SERVER_ID}]`,
|
|
993
|
+
`command = "${invocation.command}"`,
|
|
994
|
+
`args = ${JSON.stringify(invocation.args)}`
|
|
995
|
+
];
|
|
996
|
+
}
|
|
997
|
+
function helixServerBlock(invocation) {
|
|
998
|
+
return [...helixServerLines(invocation), HELIX_END].join("\n");
|
|
999
|
+
}
|
|
1000
|
+
function helixFullBlock(invocation) {
|
|
1001
|
+
return [
|
|
1002
|
+
...helixServerLines(invocation),
|
|
1003
|
+
"",
|
|
1004
|
+
"[[language]]",
|
|
1005
|
+
"name = \"markdown\"",
|
|
1006
|
+
`language-servers = ["marksman", "${SERVER_ID}"]`,
|
|
1007
|
+
HELIX_END
|
|
1008
|
+
].join("\n");
|
|
1009
|
+
}
|
|
1010
|
+
function addToHelixMarkdown(text) {
|
|
1011
|
+
const lines = text.split("\n");
|
|
1012
|
+
let inMarkdown = false;
|
|
1013
|
+
let sawMarkdownLanguage = false;
|
|
1014
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1015
|
+
const line = lines[index].trim();
|
|
1016
|
+
if (line === "[[language]]") {
|
|
1017
|
+
inMarkdown = false;
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
if (line.startsWith("[") && line !== "[[language]]") {
|
|
1021
|
+
inMarkdown = false;
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
if (/^name\s*=\s*"markdown"\s*$/.test(line)) {
|
|
1025
|
+
inMarkdown = true;
|
|
1026
|
+
sawMarkdownLanguage = true;
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
if (!inMarkdown || !/^language-servers\s*=\s*\[/.test(line)) continue;
|
|
1030
|
+
if (line.includes(`"glimmervoid-visions"`)) return {
|
|
1031
|
+
lines,
|
|
1032
|
+
sawMarkdownLanguage,
|
|
1033
|
+
changed: false
|
|
1034
|
+
};
|
|
1035
|
+
const isEmptyArray = /\[\s*\]\s*$/.test(line);
|
|
1036
|
+
lines[index] = isEmptyArray ? lines[index].replace(/\[\s*\]\s*$/, `["${SERVER_ID}"]`) : lines[index].replace(/\]\s*$/, `, "${SERVER_ID}"]`);
|
|
1037
|
+
return {
|
|
1038
|
+
lines,
|
|
1039
|
+
sawMarkdownLanguage,
|
|
1040
|
+
changed: true
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
return {
|
|
1044
|
+
lines,
|
|
1045
|
+
sawMarkdownLanguage,
|
|
1046
|
+
changed: false
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
function helixMerge(existingText, invocation) {
|
|
1050
|
+
const text = typeof existingText === "string" ? existingText : "";
|
|
1051
|
+
const split = splitMarked(text, HELIX_BEGIN, HELIX_END);
|
|
1052
|
+
if (split.unterminated) return {
|
|
1053
|
+
text,
|
|
1054
|
+
changed: false,
|
|
1055
|
+
reason: "unterminated-block"
|
|
1056
|
+
};
|
|
1057
|
+
const beforeEdit = addToHelixMarkdown(split.before);
|
|
1058
|
+
const afterEdit = addToHelixMarkdown(split.after);
|
|
1059
|
+
const block = beforeEdit.sawMarkdownLanguage || afterEdit.sawMarkdownLanguage ? helixServerBlock(invocation) : helixFullBlock(invocation);
|
|
1060
|
+
const before = beforeEdit.lines.join("\n");
|
|
1061
|
+
const after = afterEdit.lines.join("\n");
|
|
1062
|
+
if (split.hasBlock) {
|
|
1063
|
+
const merged = `${before}${block}${after}`;
|
|
1064
|
+
if (merged === text) return {
|
|
1065
|
+
text,
|
|
1066
|
+
changed: false,
|
|
1067
|
+
reason: UNCHANGED
|
|
1068
|
+
};
|
|
1069
|
+
return {
|
|
1070
|
+
text: merged,
|
|
1071
|
+
changed: true,
|
|
1072
|
+
reason: "updated"
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
const merged = appendBlock(`${before}${after}`, block);
|
|
1076
|
+
if (merged === text) return {
|
|
1077
|
+
text,
|
|
1078
|
+
changed: false,
|
|
1079
|
+
reason: UNCHANGED
|
|
1080
|
+
};
|
|
1081
|
+
return {
|
|
1082
|
+
text: merged,
|
|
1083
|
+
changed: true,
|
|
1084
|
+
reason: "appended"
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
function removeFromHelixMarkdown(text) {
|
|
1088
|
+
const lines = text.split("\n");
|
|
1089
|
+
let changed = false;
|
|
1090
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1091
|
+
if (!/^\s*language-servers\s*=\s*\[/.test(lines[index]) || !lines[index].includes(`"glimmervoid-visions"`)) continue;
|
|
1092
|
+
lines[index] = lines[index].replace(new RegExp(`\\s*,\\s*"${SERVER_ID}"|"${SERVER_ID}"\\s*,\\s*|"${SERVER_ID}"`), "");
|
|
1093
|
+
changed = true;
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
text: lines.join("\n"),
|
|
1097
|
+
changed
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
function helixRemove(existingText) {
|
|
1101
|
+
const text = typeof existingText === "string" ? existingText : "";
|
|
1102
|
+
const split = splitMarked(text, HELIX_BEGIN, HELIX_END);
|
|
1103
|
+
if (split.unterminated) return {
|
|
1104
|
+
text,
|
|
1105
|
+
changed: false,
|
|
1106
|
+
reason: "unterminated-block"
|
|
1107
|
+
};
|
|
1108
|
+
const cleaned = removeFromHelixMarkdown(split.hasBlock ? `${split.before.trimEnd()}\n${split.after.replace(/^\n+/, "")}` : text);
|
|
1109
|
+
if (cleaned.text === text) return {
|
|
1110
|
+
text,
|
|
1111
|
+
changed: false,
|
|
1112
|
+
reason: UNCHANGED
|
|
1113
|
+
};
|
|
1114
|
+
return {
|
|
1115
|
+
text: cleaned.text,
|
|
1116
|
+
changed: true,
|
|
1117
|
+
reason: "removed"
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
function emacsRemove(existingText) {
|
|
1121
|
+
const text = typeof existingText === "string" ? existingText : "";
|
|
1122
|
+
const split = splitMarked(text, EMACS_BEGIN, EMACS_END);
|
|
1123
|
+
if (split.unterminated) return {
|
|
1124
|
+
text,
|
|
1125
|
+
changed: false,
|
|
1126
|
+
reason: "unterminated-block"
|
|
1127
|
+
};
|
|
1128
|
+
if (!split.hasBlock) return {
|
|
1129
|
+
text,
|
|
1130
|
+
changed: false,
|
|
1131
|
+
reason: UNCHANGED
|
|
1132
|
+
};
|
|
1133
|
+
return {
|
|
1134
|
+
text: `${split.before.trimEnd()}\n${split.after.replace(/^\n+/, "")}`,
|
|
1135
|
+
changed: true,
|
|
1136
|
+
reason: "removed"
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
function jsonSettingsRemove(existingText, { path: keyPath }) {
|
|
1140
|
+
const parsed = parseJsonSettings(existingText);
|
|
1141
|
+
if (!parsed.ok) return {
|
|
1142
|
+
text: existingText,
|
|
1143
|
+
changed: false,
|
|
1144
|
+
reason: parsed.reason
|
|
1145
|
+
};
|
|
1146
|
+
let cursor = parsed.value;
|
|
1147
|
+
for (const key of keyPath.slice(0, -1)) {
|
|
1148
|
+
if (!cursor[key] || typeof cursor[key] !== "object") return {
|
|
1149
|
+
text: existingText,
|
|
1150
|
+
changed: false,
|
|
1151
|
+
reason: UNCHANGED
|
|
1152
|
+
};
|
|
1153
|
+
cursor = cursor[key];
|
|
1154
|
+
}
|
|
1155
|
+
const lastKey = keyPath[keyPath.length - 1];
|
|
1156
|
+
if (!(lastKey in cursor)) return {
|
|
1157
|
+
text: existingText,
|
|
1158
|
+
changed: false,
|
|
1159
|
+
reason: UNCHANGED
|
|
1160
|
+
};
|
|
1161
|
+
delete cursor[lastKey];
|
|
1162
|
+
return {
|
|
1163
|
+
text: `${JSON.stringify(parsed.value, null, 2)}\n`,
|
|
1164
|
+
changed: true,
|
|
1165
|
+
reason: "removed"
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
function emacsMerge(existingText, invocation) {
|
|
1169
|
+
return replaceMarkedBlock(typeof existingText === "string" ? existingText : "", EMACS_BEGIN, EMACS_END, [
|
|
1170
|
+
EMACS_BEGIN,
|
|
1171
|
+
";; Written by glimmervoid. Delete this block to unwire glimmervoid-visions.",
|
|
1172
|
+
"(with-eval-after-load 'eglot",
|
|
1173
|
+
" (add-to-list 'eglot-server-programs",
|
|
1174
|
+
` '(markdown-mode . (${parts(invocation).map((part) => `"${part}"`).join(" ")}))))`,
|
|
1175
|
+
EMACS_END
|
|
1176
|
+
].join("\n"));
|
|
1177
|
+
}
|
|
1178
|
+
function parseJsonSettings(existingText) {
|
|
1179
|
+
const text = typeof existingText === "string" ? existingText.trim() : "";
|
|
1180
|
+
if (text === "") return {
|
|
1181
|
+
ok: true,
|
|
1182
|
+
value: {}
|
|
1183
|
+
};
|
|
1184
|
+
try {
|
|
1185
|
+
const value = JSON.parse(text);
|
|
1186
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {
|
|
1187
|
+
ok: false,
|
|
1188
|
+
reason: "not-an-object"
|
|
1189
|
+
};
|
|
1190
|
+
return {
|
|
1191
|
+
ok: true,
|
|
1192
|
+
value
|
|
1193
|
+
};
|
|
1194
|
+
} catch {
|
|
1195
|
+
return {
|
|
1196
|
+
ok: false,
|
|
1197
|
+
reason: "unparseable"
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
function jsonSettingsMerge(existingText, { path: keyPath, value }) {
|
|
1202
|
+
const parsed = parseJsonSettings(existingText);
|
|
1203
|
+
if (!parsed.ok) return {
|
|
1204
|
+
text: existingText,
|
|
1205
|
+
changed: false,
|
|
1206
|
+
reason: parsed.reason
|
|
1207
|
+
};
|
|
1208
|
+
const settings = parsed.value;
|
|
1209
|
+
let cursor = settings;
|
|
1210
|
+
for (const key of keyPath.slice(0, -1)) {
|
|
1211
|
+
if (!cursor[key] || typeof cursor[key] !== "object" || Array.isArray(cursor[key])) cursor[key] = {};
|
|
1212
|
+
cursor = cursor[key];
|
|
1213
|
+
}
|
|
1214
|
+
const lastKey = keyPath[keyPath.length - 1];
|
|
1215
|
+
if (JSON.stringify(cursor[lastKey]) === JSON.stringify(value)) return {
|
|
1216
|
+
text: existingText,
|
|
1217
|
+
changed: false,
|
|
1218
|
+
reason: UNCHANGED
|
|
1219
|
+
};
|
|
1220
|
+
cursor[lastKey] = value;
|
|
1221
|
+
return {
|
|
1222
|
+
text: `${JSON.stringify(settings, null, 2)}\n`,
|
|
1223
|
+
changed: true,
|
|
1224
|
+
reason: "merged"
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
function sublimeSettings(invocation) {
|
|
1228
|
+
return {
|
|
1229
|
+
path: ["clients", SERVER_ID],
|
|
1230
|
+
value: {
|
|
1231
|
+
enabled: true,
|
|
1232
|
+
command: parts(invocation),
|
|
1233
|
+
selector: "text.html.markdown"
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
function kateSettings(invocation) {
|
|
1238
|
+
return {
|
|
1239
|
+
path: ["servers", "markdown"],
|
|
1240
|
+
value: {
|
|
1241
|
+
command: parts(invocation),
|
|
1242
|
+
highlightingModeRegex: "^Markdown$"
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
//#endregion
|
|
1247
|
+
//#region server/editor-wire.ts
|
|
1248
|
+
function configHome(homeDir, env) {
|
|
1249
|
+
if (env.XDG_CONFIG_HOME) return env.XDG_CONFIG_HOME;
|
|
1250
|
+
return path.join(homeDir, ".config");
|
|
1251
|
+
}
|
|
1252
|
+
function sublimeUserDir(homeDir, platform, env) {
|
|
1253
|
+
if (platform === "darwin") return path.join(homeDir, "Library", "Application Support", "Sublime Text", "Packages", "User");
|
|
1254
|
+
if (platform === "win32") return path.join(env.APPDATA || path.join(homeDir, "AppData", "Roaming"), "Sublime Text", "Packages", "User");
|
|
1255
|
+
return path.join(configHome(homeDir, env), "sublime-text", "Packages", "User");
|
|
1256
|
+
}
|
|
1257
|
+
function neovimConfigDir(homeDir, platform, env) {
|
|
1258
|
+
if (platform === "win32") return path.join(env.LOCALAPPDATA || path.join(homeDir, "AppData", "Local"), "nvim");
|
|
1259
|
+
return path.join(configHome(homeDir, env), "nvim");
|
|
1260
|
+
}
|
|
1261
|
+
function helixConfigDir(homeDir, platform, env) {
|
|
1262
|
+
if (platform === "win32") return path.join(env.APPDATA || path.join(homeDir, "AppData", "Roaming"), "helix");
|
|
1263
|
+
return path.join(configHome(homeDir, env), "helix");
|
|
1264
|
+
}
|
|
1265
|
+
function exists(filePath) {
|
|
1266
|
+
try {
|
|
1267
|
+
return fs.existsSync(filePath);
|
|
1268
|
+
} catch {
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
function onPath(command, { platform, exec }) {
|
|
1273
|
+
return resolvePathCommandMatches(command, {
|
|
1274
|
+
platform,
|
|
1275
|
+
exec
|
|
1276
|
+
}).length > 0;
|
|
1277
|
+
}
|
|
1278
|
+
function editorTargets({ homeDir = os.homedir(), platform = process.platform, env = process.env, exec = execSync } = {}) {
|
|
1279
|
+
const nvimDir = neovimConfigDir(homeDir, platform, env);
|
|
1280
|
+
const helixDir = helixConfigDir(homeDir, platform, env);
|
|
1281
|
+
const kateDir = path.join(configHome(homeDir, env), "kate", "lspclient");
|
|
1282
|
+
const sublimeDir = sublimeUserDir(homeDir, platform, env);
|
|
1283
|
+
const emacsInits = [path.join(homeDir, ".emacs.d", "init.el"), path.join(configHome(homeDir, env), "emacs", "init.el")];
|
|
1284
|
+
const targets = [];
|
|
1285
|
+
if (exists(nvimDir) || onPath("nvim", {
|
|
1286
|
+
platform,
|
|
1287
|
+
exec
|
|
1288
|
+
})) targets.push({
|
|
1289
|
+
id: "neovim",
|
|
1290
|
+
label: "Neovim",
|
|
1291
|
+
filePath: path.join(nvimDir, "plugin", "glimmervoid-visions.lua"),
|
|
1292
|
+
owned: true,
|
|
1293
|
+
create: true
|
|
1294
|
+
});
|
|
1295
|
+
if (exists(helixDir) || onPath("hx", {
|
|
1296
|
+
platform,
|
|
1297
|
+
exec
|
|
1298
|
+
})) targets.push({
|
|
1299
|
+
id: "helix",
|
|
1300
|
+
label: "Helix",
|
|
1301
|
+
filePath: path.join(helixDir, "languages.toml"),
|
|
1302
|
+
merge: helixMerge,
|
|
1303
|
+
unmerge: helixRemove,
|
|
1304
|
+
create: true
|
|
1305
|
+
});
|
|
1306
|
+
if (exists(kateDir) || onPath("kate", {
|
|
1307
|
+
platform,
|
|
1308
|
+
exec
|
|
1309
|
+
})) targets.push({
|
|
1310
|
+
id: "kate",
|
|
1311
|
+
label: "Kate",
|
|
1312
|
+
filePath: path.join(kateDir, "settings.json"),
|
|
1313
|
+
json: kateSettings,
|
|
1314
|
+
create: true
|
|
1315
|
+
});
|
|
1316
|
+
if (exists(sublimeDir)) targets.push({
|
|
1317
|
+
id: "sublime",
|
|
1318
|
+
label: "Sublime Text",
|
|
1319
|
+
filePath: path.join(sublimeDir, "LSP.sublime-settings"),
|
|
1320
|
+
json: sublimeSettings,
|
|
1321
|
+
create: true
|
|
1322
|
+
});
|
|
1323
|
+
const emacsInit = emacsInits.find((candidate) => exists(candidate));
|
|
1324
|
+
if (emacsInit) targets.push({
|
|
1325
|
+
id: "emacs",
|
|
1326
|
+
label: "Emacs",
|
|
1327
|
+
filePath: emacsInit,
|
|
1328
|
+
merge: emacsMerge,
|
|
1329
|
+
unmerge: emacsRemove,
|
|
1330
|
+
create: false
|
|
1331
|
+
});
|
|
1332
|
+
return targets;
|
|
1333
|
+
}
|
|
1334
|
+
function readIfPresent(filePath) {
|
|
1335
|
+
try {
|
|
1336
|
+
return fs.readFileSync(filePath, "utf8");
|
|
1337
|
+
} catch {
|
|
1338
|
+
return null;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
function backupOnce(filePath, existingText) {
|
|
1342
|
+
const backupPath = `${filePath}.glimmervoid.bak`;
|
|
1343
|
+
if (existingText === null || exists(backupPath)) return null;
|
|
1344
|
+
fs.writeFileSync(backupPath, existingText);
|
|
1345
|
+
return backupPath;
|
|
1346
|
+
}
|
|
1347
|
+
function planFor(target, existingText, invocation) {
|
|
1348
|
+
if (target.owned) {
|
|
1349
|
+
const text = neovimDropIn(invocation);
|
|
1350
|
+
if (existingText === text) return {
|
|
1351
|
+
text,
|
|
1352
|
+
changed: false,
|
|
1353
|
+
reason: "unchanged"
|
|
1354
|
+
};
|
|
1355
|
+
return {
|
|
1356
|
+
text,
|
|
1357
|
+
changed: true,
|
|
1358
|
+
reason: existingText === null ? "created" : "updated"
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
if (target.merge) return target.merge(existingText, invocation);
|
|
1362
|
+
return jsonSettingsMerge(existingText ?? "", target.json(invocation));
|
|
1363
|
+
}
|
|
1364
|
+
function unwirePlanFor(target, existingText) {
|
|
1365
|
+
if (target.owned) return {
|
|
1366
|
+
text: null,
|
|
1367
|
+
changed: existingText !== null,
|
|
1368
|
+
reason: existingText === null ? "unchanged" : "deleted"
|
|
1369
|
+
};
|
|
1370
|
+
if (target.unmerge) return target.unmerge(existingText);
|
|
1371
|
+
return jsonSettingsRemove(existingText ?? "", target.json({
|
|
1372
|
+
command: "",
|
|
1373
|
+
args: []
|
|
1374
|
+
}));
|
|
1375
|
+
}
|
|
1376
|
+
function applyPlan(target, existingText, plan, { dryRun }) {
|
|
1377
|
+
if (!plan.changed) return {
|
|
1378
|
+
...outcome(target),
|
|
1379
|
+
action: plan.reason === "unchanged" ? "unchanged" : "skipped",
|
|
1380
|
+
reason: plan.reason
|
|
1381
|
+
};
|
|
1382
|
+
if (dryRun) return {
|
|
1383
|
+
...outcome(target),
|
|
1384
|
+
action: "would-write",
|
|
1385
|
+
reason: plan.reason
|
|
1386
|
+
};
|
|
1387
|
+
try {
|
|
1388
|
+
if (plan.text === null) {
|
|
1389
|
+
fs.rmSync(target.filePath, { force: true });
|
|
1390
|
+
return {
|
|
1391
|
+
...outcome(target),
|
|
1392
|
+
action: "removed",
|
|
1393
|
+
reason: plan.reason
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
fs.mkdirSync(path.dirname(target.filePath), { recursive: true });
|
|
1397
|
+
backupOnce(target.filePath, existingText);
|
|
1398
|
+
fs.writeFileSync(target.filePath, plan.text);
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
return {
|
|
1401
|
+
...outcome(target),
|
|
1402
|
+
action: "failed",
|
|
1403
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
return {
|
|
1407
|
+
...outcome(target),
|
|
1408
|
+
action: plan.text === null ? "removed" : "wrote",
|
|
1409
|
+
reason: plan.reason
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
function unwireEditor(target, { dryRun = false } = {}) {
|
|
1413
|
+
const existingText = readIfPresent(target.filePath);
|
|
1414
|
+
if (existingText === null && !target.owned) return {
|
|
1415
|
+
...outcome(target),
|
|
1416
|
+
action: "skipped",
|
|
1417
|
+
reason: "file-absent"
|
|
1418
|
+
};
|
|
1419
|
+
return applyPlan(target, existingText, unwirePlanFor(target, existingText), { dryRun });
|
|
1420
|
+
}
|
|
1421
|
+
function wireEditor(target, invocation, { dryRun = false } = {}) {
|
|
1422
|
+
const existingText = readIfPresent(target.filePath);
|
|
1423
|
+
if (existingText === null && !target.create) return {
|
|
1424
|
+
...outcome(target),
|
|
1425
|
+
action: "skipped",
|
|
1426
|
+
reason: "file-absent"
|
|
1427
|
+
};
|
|
1428
|
+
return applyPlan(target, existingText, planFor(target, existingText, invocation), { dryRun });
|
|
1429
|
+
}
|
|
1430
|
+
function outcome(target) {
|
|
1431
|
+
return {
|
|
1432
|
+
id: target.id,
|
|
1433
|
+
label: target.label,
|
|
1434
|
+
filePath: target.filePath
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
function wireEditors({ invocation, dryRun = false, targets = null, ...detection }) {
|
|
1438
|
+
return (targets || editorTargets(detection)).map((target) => wireEditor(target, invocation, { dryRun }));
|
|
1439
|
+
}
|
|
1440
|
+
function unwireEditors({ dryRun = false, targets = null, ...detection } = {}) {
|
|
1441
|
+
return (targets || editorTargets(detection)).map((target) => unwireEditor(target, { dryRun }));
|
|
1442
|
+
}
|
|
1443
|
+
//#endregion
|
|
1444
|
+
//#region server/visions-setup.ts
|
|
1445
|
+
var EXTENSION_DIR = extensionDir;
|
|
1446
|
+
var RELAY_PATH = relayPath("visions-relay");
|
|
1447
|
+
var LSP_CORE_PATH = bundled ? path.join(EXTENSION_DIR, "visions-lsp-core.js") : path.join(packageRoot, "server", "core", "visions-lsp-core.ts");
|
|
1448
|
+
var CLI_PATH = cliPath;
|
|
1449
|
+
var EDITOR_TIMEOUT_MS = 6e4;
|
|
1450
|
+
function underTestRunner(env) {
|
|
1451
|
+
return typeof env.NODE_TEST_CONTEXT === "string" && env.NODE_TEST_CONTEXT !== "";
|
|
1452
|
+
}
|
|
1453
|
+
function errorMessage(error) {
|
|
1454
|
+
return error instanceof Error ? error.message : String(error);
|
|
1455
|
+
}
|
|
1456
|
+
function commandFailureDetail(error) {
|
|
1457
|
+
const failure = error ?? {};
|
|
1458
|
+
return String(failure.stderr || failure.message || error).trim().split("\n").pop();
|
|
1459
|
+
}
|
|
1460
|
+
function packedSource(filePath) {
|
|
1461
|
+
return stripTypeScriptTypes(fs.readFileSync(filePath, "utf8"), { mode: "strip" });
|
|
1462
|
+
}
|
|
1463
|
+
function packedLspCore(filePath) {
|
|
1464
|
+
const stripped = packedSource(filePath);
|
|
1465
|
+
const exportStatement = /^export \{([^}]*)\};/m;
|
|
1466
|
+
if (!exportStatement.test(stripped)) throw new Error(`${filePath} has no single-line export block to rewrite for the extension host`);
|
|
1467
|
+
return stripped.replace(exportStatement, "module.exports = {$1};");
|
|
1468
|
+
}
|
|
1469
|
+
function packedExtensionSources() {
|
|
1470
|
+
if (bundled) return {
|
|
1471
|
+
extensionJs: fs.readFileSync(path.join(EXTENSION_DIR, "extension.js"), "utf8"),
|
|
1472
|
+
convertJs: fs.readFileSync(path.join(EXTENSION_DIR, "lsp-convert.js"), "utf8"),
|
|
1473
|
+
lspCoreJs: fs.readFileSync(LSP_CORE_PATH, "utf8")
|
|
1474
|
+
};
|
|
1475
|
+
return {
|
|
1476
|
+
extensionJs: packedSource(path.join(EXTENSION_DIR, "extension.ts")),
|
|
1477
|
+
convertJs: packedSource(path.join(EXTENSION_DIR, "lsp-convert.ts")),
|
|
1478
|
+
lspCoreJs: packedLspCore(LSP_CORE_PATH)
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
function packVsix() {
|
|
1482
|
+
const manifestJson = fs.readFileSync(path.join(EXTENSION_DIR, "package.json"), "utf8");
|
|
1483
|
+
const manifest = JSON.parse(manifestJson);
|
|
1484
|
+
return {
|
|
1485
|
+
manifest,
|
|
1486
|
+
vsix: buildVsix({
|
|
1487
|
+
manifest,
|
|
1488
|
+
extensionFiles: visionsExtensionFiles({
|
|
1489
|
+
manifestJson,
|
|
1490
|
+
...packedExtensionSources(),
|
|
1491
|
+
relayPath: RELAY_PATH
|
|
1492
|
+
})
|
|
1493
|
+
})
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
function missingInstallFiles() {
|
|
1497
|
+
return [RELAY_PATH, LSP_CORE_PATH].filter((filePath) => !fs.existsSync(filePath));
|
|
1498
|
+
}
|
|
1499
|
+
function resolvedEditorPaths({ platform = process.platform, exec = execSync, homeDir = os.homedir(), exists = fs.existsSync } = {}) {
|
|
1500
|
+
return resolveEditorPathsFor({
|
|
1501
|
+
platform,
|
|
1502
|
+
homeDir,
|
|
1503
|
+
matchesOnPath: (command) => resolvePathCommandMatches(command, {
|
|
1504
|
+
platform,
|
|
1505
|
+
exec
|
|
1506
|
+
}),
|
|
1507
|
+
exists
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
function relayInvocationOptions({ platform = process.platform, exec = execSync } = {}) {
|
|
1511
|
+
const onPath = resolvePathCommandMatches("glimmervoid", {
|
|
1512
|
+
platform,
|
|
1513
|
+
exec
|
|
1514
|
+
}).length > 0;
|
|
1515
|
+
return {
|
|
1516
|
+
chosen: relayInvocation({
|
|
1517
|
+
glimmervoidOnPath: onPath,
|
|
1518
|
+
cliPath: CLI_PATH,
|
|
1519
|
+
nodePath: process.execPath
|
|
1520
|
+
}),
|
|
1521
|
+
absolute: relayInvocation({
|
|
1522
|
+
glimmervoidOnPath: false,
|
|
1523
|
+
cliPath: CLI_PATH,
|
|
1524
|
+
nodePath: process.execPath
|
|
1525
|
+
}),
|
|
1526
|
+
onPath
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
function resolveRelayInvocation(options = {}) {
|
|
1530
|
+
return relayInvocationOptions(options).chosen;
|
|
1531
|
+
}
|
|
1532
|
+
async function editorExtensions(commandPath) {
|
|
1533
|
+
try {
|
|
1534
|
+
const { stdout } = await execFileAsync(commandPath, ["--list-extensions"], { timeout: EDITOR_TIMEOUT_MS });
|
|
1535
|
+
return stdout;
|
|
1536
|
+
} catch (error) {
|
|
1537
|
+
return `${(error ?? {}).stdout || ""}`;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
async function installInto(target, vsixPath, extensionId) {
|
|
1541
|
+
try {
|
|
1542
|
+
await execFileAsync(target.commandPath, [
|
|
1543
|
+
"--install-extension",
|
|
1544
|
+
vsixPath,
|
|
1545
|
+
"--force"
|
|
1546
|
+
], { timeout: EDITOR_TIMEOUT_MS });
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
return {
|
|
1549
|
+
...target,
|
|
1550
|
+
ok: false,
|
|
1551
|
+
detail: commandFailureDetail(error)
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
if (!isExtensionInstalled(await editorExtensions(target.commandPath), extensionId)) return {
|
|
1555
|
+
...target,
|
|
1556
|
+
ok: false,
|
|
1557
|
+
detail: "the editor reported no error but does not list the extension"
|
|
1558
|
+
};
|
|
1559
|
+
return {
|
|
1560
|
+
...target,
|
|
1561
|
+
ok: true,
|
|
1562
|
+
detail: "installed"
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
async function uninstallFrom(target, extensionId) {
|
|
1566
|
+
if (!isExtensionInstalled(await editorExtensions(target.commandPath), extensionId)) return {
|
|
1567
|
+
...target,
|
|
1568
|
+
ok: true,
|
|
1569
|
+
detail: "not installed"
|
|
1570
|
+
};
|
|
1571
|
+
try {
|
|
1572
|
+
await execFileAsync(target.commandPath, ["--uninstall-extension", extensionId], { timeout: EDITOR_TIMEOUT_MS });
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
return {
|
|
1575
|
+
...target,
|
|
1576
|
+
ok: false,
|
|
1577
|
+
detail: commandFailureDetail(error)
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
return {
|
|
1581
|
+
...target,
|
|
1582
|
+
ok: true,
|
|
1583
|
+
detail: "removed"
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
async function installExtensions({ requested = null, resolvedByCommand = null, platform = process.platform, exists = fs.existsSync } = {}) {
|
|
1587
|
+
const { targets, reason } = decideEditorTargets({
|
|
1588
|
+
requested,
|
|
1589
|
+
resolvedByCommand: resolvedByCommand || resolvedEditorPaths({
|
|
1590
|
+
platform,
|
|
1591
|
+
exists
|
|
1592
|
+
}),
|
|
1593
|
+
probe: {
|
|
1594
|
+
platform,
|
|
1595
|
+
exists
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
if (targets.length === 0) return {
|
|
1599
|
+
targets: [],
|
|
1600
|
+
reason,
|
|
1601
|
+
results: []
|
|
1602
|
+
};
|
|
1603
|
+
const { manifest, vsix } = packVsix();
|
|
1604
|
+
const extensionId = extensionIdOf(manifest);
|
|
1605
|
+
const vsixPath = path.join(os.tmpdir(), `${extensionId}-${manifest.version}.vsix`);
|
|
1606
|
+
fs.writeFileSync(vsixPath, vsix);
|
|
1607
|
+
const results = [];
|
|
1608
|
+
for (const target of targets) results.push(await installInto(target, vsixPath, extensionId));
|
|
1609
|
+
return {
|
|
1610
|
+
targets,
|
|
1611
|
+
reason,
|
|
1612
|
+
results
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
async function uninstallExtensions({ resolvedByCommand = null, platform = process.platform, exists = fs.existsSync } = {}) {
|
|
1616
|
+
const { targets } = decideEditorTargets({
|
|
1617
|
+
resolvedByCommand: resolvedByCommand || resolvedEditorPaths({
|
|
1618
|
+
platform,
|
|
1619
|
+
exists
|
|
1620
|
+
}),
|
|
1621
|
+
probe: {
|
|
1622
|
+
platform,
|
|
1623
|
+
exists
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
const { manifest } = packVsix();
|
|
1627
|
+
const extensionId = extensionIdOf(manifest);
|
|
1628
|
+
const results = [];
|
|
1629
|
+
for (const target of targets) results.push(await uninstallFrom(target, extensionId));
|
|
1630
|
+
return {
|
|
1631
|
+
targets,
|
|
1632
|
+
results
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
async function wireEverything({ requested = null, dryRun = false } = {}) {
|
|
1636
|
+
const missing = missingInstallFiles();
|
|
1637
|
+
if (missing.length > 0) return {
|
|
1638
|
+
ok: false,
|
|
1639
|
+
reason: `missing from this install: ${missing.join(", ")}`,
|
|
1640
|
+
extensions: {
|
|
1641
|
+
targets: [],
|
|
1642
|
+
reason: "not-run",
|
|
1643
|
+
results: []
|
|
1644
|
+
},
|
|
1645
|
+
files: []
|
|
1646
|
+
};
|
|
1647
|
+
const invocation = resolveRelayInvocation();
|
|
1648
|
+
return {
|
|
1649
|
+
ok: true,
|
|
1650
|
+
reason: "ok",
|
|
1651
|
+
invocation,
|
|
1652
|
+
extensions: await installExtensions({ requested }),
|
|
1653
|
+
files: wireEditors({
|
|
1654
|
+
invocation,
|
|
1655
|
+
dryRun
|
|
1656
|
+
})
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
async function unwireEverything() {
|
|
1660
|
+
return {
|
|
1661
|
+
extensions: await uninstallExtensions(),
|
|
1662
|
+
files: unwireEditors()
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
function createVisionsSetup({ getConfig = () => ({}), configStore = null, logger = console, debug = false, env = process.env, onConfigChanged = null, wire = wireEverything, unwire = unwireEverything } = {}) {
|
|
1666
|
+
const { note, warn } = createLaneLog({
|
|
1667
|
+
prefix: "[visions-setup]",
|
|
1668
|
+
logger,
|
|
1669
|
+
debugFlag: debug
|
|
1670
|
+
});
|
|
1671
|
+
let appliedState = false;
|
|
1672
|
+
let inFlight = null;
|
|
1673
|
+
function isEnabled() {
|
|
1674
|
+
return (getConfig()?.visions)?.enabled === true;
|
|
1675
|
+
}
|
|
1676
|
+
function reportFiles(files) {
|
|
1677
|
+
for (const file of files) {
|
|
1678
|
+
if (file.action === "unchanged" || file.action === "skipped") continue;
|
|
1679
|
+
if (file.action === "failed") {
|
|
1680
|
+
warn(`${file.label}: ${file.reason} (${file.filePath})`);
|
|
1681
|
+
continue;
|
|
1682
|
+
}
|
|
1683
|
+
note(`${file.label}: ${file.action} ${file.filePath}`);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
function reportConfigChanged() {
|
|
1687
|
+
if (typeof onConfigChanged !== "function") return;
|
|
1688
|
+
Promise.resolve().then(onConfigChanged).catch((error) => warn(`lane rebuild failed: ${errorMessage(error)}`));
|
|
1689
|
+
}
|
|
1690
|
+
function isEditorWiringRefused() {
|
|
1691
|
+
return underTestRunner(env) && wire === wireEverything && unwire === unwireEverything;
|
|
1692
|
+
}
|
|
1693
|
+
function isConfigWriteRefused() {
|
|
1694
|
+
if (!underTestRunner(env)) return false;
|
|
1695
|
+
const configPath = configStore?.configPath;
|
|
1696
|
+
if (typeof configPath !== "string" || !configPath) return false;
|
|
1697
|
+
return isPathInside(os.homedir(), configPath) && !isPathInside(os.tmpdir(), configPath);
|
|
1698
|
+
}
|
|
1699
|
+
function writeImpliedDefaults() {
|
|
1700
|
+
if (!configStore || isConfigWriteRefused()) return null;
|
|
1701
|
+
if (decideImpliedDefaults(getConfig()).changes.length === 0) return null;
|
|
1702
|
+
const written = [];
|
|
1703
|
+
const saved = configStore.save((freshConfig) => {
|
|
1704
|
+
const changes = decideImpliedDefaults(freshConfig).changes;
|
|
1705
|
+
written.push(...changes);
|
|
1706
|
+
applyChanges(freshConfig, changes);
|
|
1707
|
+
});
|
|
1708
|
+
if (!saved || written.length === 0) return null;
|
|
1709
|
+
applyChanges(getConfig(), written);
|
|
1710
|
+
for (const change of written) note(`config: ${change.path.join(".")} on (${change.why})`);
|
|
1711
|
+
reportConfigChanged();
|
|
1712
|
+
return saved;
|
|
1713
|
+
}
|
|
1714
|
+
async function apply(enabled) {
|
|
1715
|
+
if (!enabled) {
|
|
1716
|
+
if (isEditorWiringRefused()) return null;
|
|
1717
|
+
const report = await unwire();
|
|
1718
|
+
reportFiles(report.files);
|
|
1719
|
+
for (const result of report.extensions.results) note(`${result.label}: extension ${result.detail}`);
|
|
1720
|
+
return report;
|
|
1721
|
+
}
|
|
1722
|
+
writeImpliedDefaults();
|
|
1723
|
+
if (isEditorWiringRefused()) return null;
|
|
1724
|
+
const report = await wire({});
|
|
1725
|
+
if (!report.ok) {
|
|
1726
|
+
warn(report.reason);
|
|
1727
|
+
return report;
|
|
1728
|
+
}
|
|
1729
|
+
reportFiles(report.files);
|
|
1730
|
+
for (const result of report.extensions.results) {
|
|
1731
|
+
if (result.ok) note(`${result.label}: extension ${result.detail}`);
|
|
1732
|
+
if (!result.ok) warn(`${result.label}: extension install failed: ${result.detail}`);
|
|
1733
|
+
}
|
|
1734
|
+
return report;
|
|
1735
|
+
}
|
|
1736
|
+
async function maybeApply() {
|
|
1737
|
+
const enabled = isEnabled();
|
|
1738
|
+
if (appliedState === enabled) return null;
|
|
1739
|
+
if (inFlight) return inFlight;
|
|
1740
|
+
appliedState = enabled;
|
|
1741
|
+
inFlight = apply(enabled).catch((error) => {
|
|
1742
|
+
appliedState = null;
|
|
1743
|
+
warn(`editor wiring failed: ${errorMessage(error)}`);
|
|
1744
|
+
return null;
|
|
1745
|
+
}).finally(() => {
|
|
1746
|
+
inFlight = null;
|
|
1747
|
+
});
|
|
1748
|
+
return inFlight;
|
|
1749
|
+
}
|
|
1750
|
+
return { maybeApply };
|
|
1751
|
+
}
|
|
1752
|
+
//#endregion
|
|
1753
|
+
export { deriveSessionRoots as C, recordChange as D, normalizeRoots as E, relativeWithin as O, dedupeRoots as S, isIgnoredChange as T, DEFAULT_BATCH_MS as _, relayInvocationOptions as a, daemonWriteRules as b, unwireEverything as c, extensionIdOf as d, buildSetupGuide as f, createLaneLog as g, isExtensionInstalled as h, packVsix as i, wireEverything as l, recipeIds as m, createVisionsSetup as n, resolveRelayInvocation as o, commandLine as p, editorExtensions as r, resolvedEditorPaths as s, RELAY_PATH as t, editorTargets as u, buildIgnorePatterns as v, isActiveSessionState as w, decideFsEvents as x, createBatch as y };
|