coc-vscode-loader 1.1.0 → 1.1.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/lib/index.js CHANGED
@@ -5,6 +5,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ try {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ } catch (e) {
12
+ throw mod = 0, e;
13
+ }
14
+ };
8
15
  var __export = (target, all) => {
9
16
  for (var name in all)
10
17
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,53 +34,99 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
34
  ));
28
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
36
 
37
+ // package.json
38
+ var require_package = __commonJS({
39
+ "package.json"(exports2, module2) {
40
+ module2.exports = {
41
+ name: "coc-vscode-loader",
42
+ version: "1.1.2",
43
+ description: "Run VS Code extensions seamlessly in coc.nvim",
44
+ main: "lib/index.js",
45
+ keywords: [
46
+ "coc.nvim",
47
+ "vscode",
48
+ "neovim",
49
+ "lsp",
50
+ "plugin-loader",
51
+ "vim"
52
+ ],
53
+ repository: {
54
+ type: "git",
55
+ url: "git+https://github.com/coc-plugin/coc-vscode-loader.git"
56
+ },
57
+ bugs: {
58
+ url: "https://github.com/coc-plugin/coc-vscode-loader/issues"
59
+ },
60
+ homepage: "https://www.npmjs.com/package/coc-vscode-loader",
61
+ license: "MIT",
62
+ engines: {
63
+ coc: ">= 0.0.80"
64
+ },
65
+ scripts: {
66
+ "bundle-converter": "rm -rf converter && cp -r ../converter ./converter && cd converter && npm install --legacy-peer-deps",
67
+ build: "npm run bundle-converter && node esbuild.mjs",
68
+ prepare: "node esbuild.mjs"
69
+ },
70
+ devDependencies: {
71
+ "coc.nvim": "^0.0.83-next.18",
72
+ esbuild: "^0.28.1",
73
+ typescript: "^6.0.3"
74
+ },
75
+ activationEvents: [
76
+ "onCommand:loader.open",
77
+ "onCommand:loader.install",
78
+ "onCommand:loader.uninstall",
79
+ "onCommand:loader.update",
80
+ "onCommand:loader.uninstallAll",
81
+ "onCommand:loader.updateRegistry",
82
+ "onCommand:loader._dispatch"
83
+ ],
84
+ contributes: {
85
+ commands: [
86
+ { command: "loader.open", title: "Open VS Code extension loader" },
87
+ { command: "loader.install", title: "Install a VS Code extension" },
88
+ { command: "loader.uninstall", title: "Uninstall a package" },
89
+ { command: "loader.update", title: "Update a package" },
90
+ { command: "loader.uninstallAll", title: "Uninstall all packages" },
91
+ { command: "loader.updateRegistry", title: "Update package registry" },
92
+ { command: "loader._dispatch", title: "" }
93
+ ]
94
+ }
95
+ };
96
+ }
97
+ });
98
+
30
99
  // src/index.ts
31
100
  var index_exports = {};
32
101
  __export(index_exports, {
33
102
  activate: () => activate
34
103
  });
35
104
  module.exports = __toCommonJS(index_exports);
36
- var import_coc2 = require("coc.nvim");
105
+ var import_coc3 = require("coc.nvim");
37
106
 
38
107
  // src/registry.ts
39
108
  var path = __toESM(require("path"));
40
109
  var fs = __toESM(require("fs"));
41
110
  var os = __toESM(require("os"));
111
+ function pluginVersion() {
112
+ try {
113
+ return require_package().version;
114
+ } catch {
115
+ return "0.0.0";
116
+ }
117
+ }
42
118
  var REMOTE_REGISTRY_URL = "https://raw.githubusercontent.com/coc-plugin/coc-vscode-registry/main/registry.json";
43
119
  var CACHE_PATH = path.join(os.homedir(), ".config", "coc", "converter-cache", "registry.json");
44
- var BUILTIN_REGISTRY = [
45
- {
46
- name: "volar",
47
- displayName: "Volar (Vue)",
48
- description: "Vue language support \u2014 template/script/style IntelliSense",
49
- type: "ts-bridge",
50
- source: { type: "github", repo: "vuejs/language-tools", subdir: "extensions/vscode" },
51
- url: "https://github.com/vuejs/language-tools",
52
- languages: ["vue"],
53
- categories: ["LSP", "TypeScript"]
54
- },
55
- {
56
- name: "prisma",
57
- displayName: "Prisma",
58
- description: "Prisma schema language support \u2014 syntax highlight, lint, format",
59
- type: "pure-lsp",
60
- source: { type: "github", repo: "prisma/language-tools", subdir: "packages/vscode" },
61
- url: "https://github.com/prisma/language-tools",
62
- languages: ["prisma"],
63
- categories: ["LSP"]
64
- },
65
- {
66
- name: "html-css-support",
67
- displayName: "HTML CSS Support",
68
- description: "CSS class name completion for HTML attributes",
69
- type: "direct-api",
70
- source: { type: "github", repo: "ecmel/vscode-html-css" },
71
- url: "https://github.com/ecmel/vscode-html-css",
72
- languages: ["html", "css"],
73
- categories: ["Completion"]
74
- }
75
- ];
76
120
  var cached = null;
121
+ function getLocalRegistryPath() {
122
+ try {
123
+ const pluginDir2 = path.dirname(fs.realpathSync(__dirname));
124
+ const local = path.join(pluginDir2, "..", "coc-vscode-registry", "registry.json");
125
+ if (fs.existsSync(local)) return local;
126
+ } catch {
127
+ }
128
+ return null;
129
+ }
77
130
  function loadCache() {
78
131
  try {
79
132
  if (fs.existsSync(CACHE_PATH)) {
@@ -84,6 +137,16 @@ function loadCache() {
84
137
  return null;
85
138
  }
86
139
  async function updateRegistry() {
140
+ const localPath = process.env.COC_REGISTRY_PATH || getLocalRegistryPath();
141
+ if (localPath) {
142
+ if (!fs.existsSync(localPath)) throw new Error(`Local registry not found: ${localPath}`);
143
+ const data2 = JSON.parse(fs.readFileSync(localPath, "utf-8"));
144
+ if (!Array.isArray(data2)) throw new Error("Invalid registry format");
145
+ fs.mkdirSync(path.dirname(CACHE_PATH), { recursive: true });
146
+ fs.writeFileSync(CACHE_PATH, JSON.stringify(data2, null, 2));
147
+ cached = data2;
148
+ return data2.length;
149
+ }
87
150
  const res = await fetch(REMOTE_REGISTRY_URL);
88
151
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
89
152
  const data = await res.json();
@@ -93,10 +156,21 @@ async function updateRegistry() {
93
156
  cached = data;
94
157
  return data.length;
95
158
  }
159
+ function satisfiesVersion(required) {
160
+ const a = pluginVersion().split(".").map(Number);
161
+ const b = required.split(".").map(Number);
162
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
163
+ const va = a[i] || 0, vb = b[i] || 0;
164
+ if (va > vb) return true;
165
+ if (va < vb) return false;
166
+ }
167
+ return true;
168
+ }
96
169
  function getAllPackages() {
97
- if (cached) return cached;
98
- cached = loadCache();
99
- return cached || BUILTIN_REGISTRY;
170
+ if (!cached) {
171
+ cached = loadCache() || [];
172
+ }
173
+ return cached.filter((p) => !p.minPluginVersion || satisfiesVersion(p.minPluginVersion));
100
174
  }
101
175
  function getPackage(name) {
102
176
  return getAllPackages().find((p) => p.name === name);
@@ -113,12 +187,16 @@ function createInitialState() {
113
187
  const packages = getAllPackages().map((info) => {
114
188
  const installed = isInstalled(info.name);
115
189
  let commit;
190
+ let commitMsg;
191
+ let commitDate;
116
192
  if (installed) {
117
193
  try {
118
194
  const meta = JSON.parse(
119
195
  fs2.readFileSync(path2.join(os2.homedir(), ".config", "coc", "converter-cache", info.name, "meta.json"), "utf-8")
120
196
  );
121
197
  commit = meta.commit || void 0;
198
+ commitMsg = meta.msg || void 0;
199
+ commitDate = meta.date || void 0;
122
200
  } catch {
123
201
  }
124
202
  }
@@ -126,12 +204,15 @@ function createInitialState() {
126
204
  info,
127
205
  status: installed ? "installed" : "not-installed",
128
206
  commit,
207
+ commitMsg,
208
+ commitDate,
129
209
  progressLog: [],
130
210
  expanded: false,
131
- logExpanded: false
211
+ logExpanded: false,
212
+ marked: false
132
213
  };
133
214
  });
134
- return { packages, searchQuery: "", showHelp: false, activePill: null, dirty: false, viewFilter: "all" };
215
+ return { packages, searchQuery: "", showHelp: false, activePill: null, dirty: false, viewFilter: "all", sortBy: "default" };
135
216
  }
136
217
  var StateManager = class {
137
218
  constructor(initial) {
@@ -165,10 +246,16 @@ var StateManager = class {
165
246
  this.mutate((s) => {
166
247
  const pkg = s.packages.find((p) => p.info.name === name);
167
248
  if (pkg) {
249
+ if (status === "installing" || status === "updating" || status === "uninstalling") {
250
+ pkg.progressLog = [];
251
+ }
168
252
  pkg.status = status;
169
253
  if (extra?.progress !== void 0) pkg.progress = extra.progress;
170
- if (extra?.logEntry !== void 0) pkg.progressLog.push(extra.logEntry);
171
- if (extra?.appendLog && extra?.progress !== void 0) pkg.progressLog.push(extra.progress);
254
+ if (extra?.logEntry !== void 0) {
255
+ pkg.progressLog.push(extra.logEntry);
256
+ } else if (extra?.progress !== void 0) {
257
+ pkg.progressLog.push(extra.progress);
258
+ }
172
259
  if (extra?.error !== void 0) pkg.error = extra.error;
173
260
  if (status === "installed" || status === "not-installed") {
174
261
  pkg.progress = void 0;
@@ -198,6 +285,21 @@ var StateManager = class {
198
285
  s.viewFilter = filter;
199
286
  });
200
287
  }
288
+ cycleViewFilter() {
289
+ this.mutate((s) => {
290
+ s.viewFilter = s.viewFilter === "all" ? "installed" : s.viewFilter === "installed" ? "not-installed" : "all";
291
+ });
292
+ }
293
+ setSortBy(sortBy) {
294
+ this.mutate((s) => {
295
+ s.sortBy = sortBy;
296
+ });
297
+ }
298
+ cycleSortBy() {
299
+ this.mutate((s) => {
300
+ s.sortBy = s.sortBy === "default" ? "name" : s.sortBy === "name" ? "status" : s.sortBy === "status" ? "type" : "default";
301
+ });
302
+ }
201
303
  setStatusMessage(msg) {
202
304
  this.mutate((s) => {
203
305
  s.statusMessage = msg;
@@ -226,21 +328,68 @@ var StateManager = class {
226
328
  pkgs = pkgs.filter((p) => p.status === "installed");
227
329
  }
228
330
  const q = this.state.searchQuery.toLowerCase();
229
- if (!q) return pkgs;
230
- return pkgs.filter(
231
- (p) => p.info.name.toLowerCase().includes(q) || p.info.displayName.toLowerCase().includes(q) || p.info.description.toLowerCase().includes(q)
232
- );
331
+ if (q) {
332
+ pkgs = pkgs.filter(
333
+ (p) => p.info.name.toLowerCase().includes(q) || p.info.displayName.toLowerCase().includes(q) || p.info.description.toLowerCase().includes(q)
334
+ );
335
+ }
336
+ const sortBy = this.state.sortBy;
337
+ if (sortBy === "name") {
338
+ pkgs = [...pkgs].sort((a, b) => a.info.name.localeCompare(b.info.name));
339
+ } else if (sortBy === "status") {
340
+ const order = { installed: 0, installing: 1, updating: 2, uninstalling: 3, failed: 4, "not-installed": 5 };
341
+ pkgs = [...pkgs].sort((a, b) => (order[a.status] ?? 9) - (order[b.status] ?? 9));
342
+ } else if (sortBy === "type") {
343
+ pkgs = [...pkgs].sort((a, b) => a.info.type.localeCompare(b.info.type));
344
+ }
345
+ return pkgs;
233
346
  }
234
347
  getPackage(name) {
235
348
  return this.state.packages.find((p) => p.info.name === name);
236
349
  }
350
+ toggleMark(name) {
351
+ this.mutate((s) => {
352
+ const pkg = s.packages.find((p) => p.info.name === name);
353
+ if (pkg) pkg.marked = !pkg.marked;
354
+ });
355
+ }
356
+ clearMarks() {
357
+ this.mutate((s) => {
358
+ for (const p of s.packages) p.marked = false;
359
+ });
360
+ }
361
+ getMarkedNames() {
362
+ return this.state.packages.filter((p) => p.marked).map((p) => p.info.name);
363
+ }
364
+ refreshPackages() {
365
+ this.mutate((s) => {
366
+ const updated = getAllPackages();
367
+ const oldMap = new Map(s.packages.map((p) => [p.info.name, p]));
368
+ s.packages = updated.map((info) => {
369
+ const old = oldMap.get(info.name);
370
+ if (old) {
371
+ old.info = info;
372
+ return old;
373
+ }
374
+ return {
375
+ info,
376
+ status: isInstalled(info.name) ? "installed" : "not-installed",
377
+ progressLog: [],
378
+ expanded: false,
379
+ logExpanded: false,
380
+ marked: false
381
+ };
382
+ });
383
+ });
384
+ }
237
385
  };
238
386
 
239
387
  // src/tui.ts
240
- var import_coc = require("coc.nvim");
388
+ var import_coc2 = require("coc.nvim");
241
389
 
242
390
  // src/pipeline.ts
243
391
  var import_child_process = require("child_process");
392
+ var import_coc = require("coc.nvim");
244
393
  var path3 = __toESM(require("path"));
245
394
  var fs3 = __toESM(require("fs"));
246
395
  var os3 = __toESM(require("os"));
@@ -259,12 +408,9 @@ function pluginDir(name) {
259
408
  }
260
409
  function converterCliPath() {
261
410
  const base = path3.resolve(__dirname, "..");
262
- const cwd = process.cwd();
263
411
  const candidates = [
264
412
  path3.join(base, "converter", "src", "cli.ts"),
265
- path3.join(base, "..", "converter", "src", "cli.ts"),
266
- path3.join(cwd, "converter", "src", "cli.ts"),
267
- path3.join(cwd, "..", "converter", "src", "cli.ts")
413
+ path3.join(base, "..", "converter", "src", "cli.ts")
268
414
  ];
269
415
  for (const p of candidates) {
270
416
  if (fs3.existsSync(p)) return p;
@@ -273,37 +419,43 @@ function converterCliPath() {
273
419
  "converter CLI not found. Please set $COC_CONVERTER_PATH to the converter/ directory, or ensure it is at the same level as coc-converter/"
274
420
  );
275
421
  }
422
+ var CMD_TIMEOUT = 3e5;
276
423
  async function run(cmd, args, cwd, onLine) {
277
424
  return new Promise((resolve2, reject) => {
278
425
  const child = (0, import_child_process.spawn)(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"], shell: true });
279
- const lines = [];
426
+ const timer = setTimeout(() => {
427
+ child.kill("SIGTERM");
428
+ reject(new Error(`Timed out after ${CMD_TIMEOUT / 1e3}s: ${cmd} ${args.join(" ")}`));
429
+ }, CMD_TIMEOUT);
280
430
  const handler = (data) => {
281
431
  const text = data.toString();
282
- lines.push(text);
283
- onLine(text);
432
+ onLine?.(text);
284
433
  };
285
434
  child.stdout.on("data", handler);
286
435
  child.stderr.on("data", handler);
287
436
  child.on("close", (code) => {
437
+ clearTimeout(timer);
288
438
  if (code === 0) resolve2();
289
439
  else reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
290
440
  });
291
- child.on("error", reject);
441
+ child.on("error", (e) => {
442
+ clearTimeout(timer);
443
+ reject(e);
444
+ });
292
445
  });
293
446
  }
294
447
  async function downloadSource(info, name, onProgress) {
295
448
  const srcDir = sourceDir(name);
296
449
  const cache = cacheDir(name);
297
450
  const repoUrl = `https://github.com/${info.source.repo}.git`;
451
+ const log = (chunk) => onProgress(1, 5, chunk.trim(), "");
298
452
  if (fs3.existsSync(srcDir)) {
299
453
  onProgress(1, 5, "Updating source...", `git -C ${srcDir} pull`);
300
- await run("git", ["-C", srcDir, "pull"], cache, () => {
301
- });
454
+ await run("git", ["-C", srcDir, "pull"], cache, log);
302
455
  } else {
303
456
  onProgress(1, 5, "Cloning repository...", `git clone --depth=1 ${repoUrl}`);
304
457
  fs3.mkdirSync(cache, { recursive: true });
305
- await run("git", ["clone", "--depth=1", repoUrl, srcDir], cache, () => {
306
- });
458
+ await run("git", ["clone", "--depth=1", repoUrl, srcDir], cache, log);
307
459
  }
308
460
  return info.source.subdir ? path3.join(srcDir, info.source.subdir) : srcDir;
309
461
  }
@@ -312,17 +464,154 @@ async function convertSource(inputDir, name, onProgress) {
312
464
  if (fs3.existsSync(build)) fs3.rmSync(build, { recursive: true });
313
465
  const cli = converterCliPath();
314
466
  onProgress(2, 5, "Converting...", `converter convert ${inputDir} -o ${build}`);
315
- await run("npx", ["tsx", cli, "convert", inputDir, "-o", build], cacheDir(name), () => {
316
- });
467
+ const log = (chunk) => onProgress(2, 5, chunk.trim(), "");
468
+ await run("npx", ["tsx", cli, "convert", inputDir, "-o", build], cacheDir(name), log);
317
469
  }
318
- async function buildPackage(name, onProgress) {
470
+ async function buildPackage(name, inputDir, info, onProgress) {
319
471
  const build = buildDir(name);
472
+ const npmLog = (chunk) => onProgress(3, 5, chunk.trim(), "");
320
473
  onProgress(3, 5, "Installing dependencies...", "npm install --legacy-peer-deps");
321
- await run("npm", ["install", "--legacy-peer-deps"], build, () => {
474
+ await run("npm", ["install", "--legacy-peer-deps"], build, npmLog);
475
+ onProgress(3, 5, "Running postinstall...", "npm run postinstall");
476
+ await run("npm", ["run", "postinstall", "--if-present"], build, npmLog).catch(() => {
322
477
  });
478
+ if (info.pipPackages?.length) {
479
+ const pipLog = (chunk) => onProgress(3, 5, chunk.trim(), "");
480
+ const pythonPaths = [
481
+ "/opt/homebrew/bin/python3",
482
+ "/usr/local/bin/python3",
483
+ "/usr/bin/python3",
484
+ "python3"
485
+ ];
486
+ let pythonBin = "";
487
+ for (const p of pythonPaths) {
488
+ if (p === "python3") {
489
+ try {
490
+ await run("python3", ["--version"], build);
491
+ pythonBin = "python3";
492
+ break;
493
+ } catch {
494
+ continue;
495
+ }
496
+ } else if (fs3.existsSync(p)) {
497
+ pythonBin = p;
498
+ break;
499
+ }
500
+ }
501
+ if (!pythonBin) throw new Error("python3 not found, cannot install pip packages: " + info.pipPackages.join(", "));
502
+ const pipArgs = ["-m", "pip", "install"];
503
+ if (process.platform === "linux") pipArgs.push("--break-system-packages");
504
+ onProgress(3, 5, "Installing pip packages...", `${pythonBin} -m pip install ${info.pipPackages.join(" ")}`);
505
+ await run(pythonBin, pipArgs.concat(...info.pipPackages), build, pipLog);
506
+ }
507
+ const serverDir = path3.join(inputDir, "server");
508
+ if (fs3.existsSync(serverDir) && fs3.existsSync(path3.join(serverDir, "package.json"))) {
509
+ onProgress(3, 5, "Installing server dependencies...", `npm install in ${serverDir}`);
510
+ await run("npm", ["install", "--legacy-peer-deps"], serverDir, npmLog);
511
+ const destServer = path3.join(build, "server");
512
+ if (fs3.existsSync(destServer)) fs3.rmSync(destServer, { recursive: true });
513
+ fs3.cpSync(serverDir, destServer, { recursive: true });
514
+ }
323
515
  onProgress(4, 5, "Building...", "node esbuild.mjs");
324
- await run("node", ["esbuild.mjs"], build, () => {
325
- });
516
+ const buildLog = (chunk) => onProgress(4, 5, chunk.trim(), "");
517
+ await run("node", ["esbuild.mjs"], build, buildLog);
518
+ if (info.serverBinary) {
519
+ const sb = info.serverBinary;
520
+ onProgress(4, 5, "Downloading language server...", `fetching ${sb.repo}`);
521
+ try {
522
+ const tagRes = await fetch(`https://api.github.com/repos/${sb.repo}/releases/latest`);
523
+ if (!tagRes.ok) throw new Error(`GitHub API: HTTP ${tagRes.status}`);
524
+ const tagData = await tagRes.json();
525
+ const tag = tagData.tag_name;
526
+ const version = tag.replace(/^v/, "");
527
+ const archMap = {
528
+ arm64: "aarch64",
529
+ x64: "x86_64"
530
+ };
531
+ const platformMap = {
532
+ darwin: "apple-darwin",
533
+ linux: "unknown-linux-gnu",
534
+ win32: "pc-windows-msvc"
535
+ };
536
+ const arch2 = os3.arch() === "arm64" ? "arm64" : "x64";
537
+ const platform = process.platform === "win32" ? "win32" : process.platform === "darwin" ? "darwin" : "linux";
538
+ const rawArch = archMap[arch2] || arch2;
539
+ const rustTarget = `${rawArch}-${platformMap[platform] || platform}`;
540
+ const filename = sb.asset.replace(/\{\{version}}/g, version).replace(/\{\{platform}}/g, platform).replace(/\{\{arch}}/g, arch2).replace(/\{\{raw-arch}}/g, rawArch).replace(/\{\{rust-target}}/g, rustTarget);
541
+ const url = `https://github.com/${sb.repo}/releases/download/${tag}/${filename}`;
542
+ onProgress(4, 5, "Downloading...", `curl ${filename}`);
543
+ await run("curl", ["-#SL", url, "-o", path3.join(build, filename)], build);
544
+ onProgress(4, 5, "Extracting...", filename);
545
+ const serverDir2 = path3.join(build, "server");
546
+ fs3.mkdirSync(serverDir2, { recursive: true });
547
+ if (filename.endsWith(".zip")) {
548
+ await run("unzip", ["-o", filename, "-d", serverDir2], build);
549
+ } else if (filename.endsWith(".gz") && !filename.endsWith(".tar.gz")) {
550
+ const outName = filename.replace(/\.gz$/, "");
551
+ await run("gunzip", [filename], build);
552
+ fs3.renameSync(path3.join(build, outName), path3.join(serverDir2, outName));
553
+ } else {
554
+ await run("tar", ["xzf", filename, "-C", serverDir2], build);
555
+ }
556
+ try {
557
+ fs3.readdirSync(serverDir2).forEach((f) => {
558
+ fs3.chmodSync(path3.join(serverDir2, f), 493);
559
+ });
560
+ } catch {
561
+ }
562
+ if (sb.binaryPath || filename.match(/\.(zip|gz)$/)) {
563
+ const archivePath = path3.join(build, filename);
564
+ if (fs3.existsSync(archivePath)) fs3.rmSync(archivePath);
565
+ }
566
+ const indexPath = path3.join(build, "lib", "index.js");
567
+ if (fs3.existsSync(indexPath)) {
568
+ const binPath = (sb.binaryPath || sb.asset.split(/-?\{\{/)[0]).replace(/\{\{version}}/g, version).replace(/\{\{platform}}/g, platform).replace(/\{\{arch}}/g, arch2).replace(/\{\{raw-arch}}/g, rawArch).replace(/\{\{rust-target}}/g, rustTarget);
569
+ let code = fs3.readFileSync(indexPath, "utf-8");
570
+ const svrArgs = sb.args?.length ? JSON.stringify(sb.args) : "[]";
571
+ code = code.replace(
572
+ /\{ module:\s*serverModule,\s*transport:\s*\w+\.TransportKind\.\w+\s*\}/,
573
+ `{ command: serverModule, args: ${svrArgs} }`
574
+ );
575
+ const serverPath = `require('path').join(__dirname, '..', 'server', '${binPath}')`;
576
+ code = code.replace(
577
+ /try\s*\{[^}]*?require\.resolve\([^)]+\)\s*;?\s*\}\s*catch\s*\{\s*\}/g,
578
+ `try { serverModule = ${serverPath} } catch {}`
579
+ );
580
+ fs3.writeFileSync(indexPath, code);
581
+ }
582
+ } catch (e) {
583
+ onProgress(4, 5, `Warning: serverBinary setup failed (${e.message})`, "install server binary manually");
584
+ }
585
+ }
586
+ const docSelPath = path3.join(build, "lib", "index.js");
587
+ if (fs3.existsSync(docSelPath)) {
588
+ let code = fs3.readFileSync(docSelPath, "utf-8");
589
+ const langSelector = info.languages.map((l) => `{ scheme: "file", language: "${l}" }`).join(", ");
590
+ code = code.replace(
591
+ /documentSelector:\s*\[\s*\{[^}]*?language:\s*['"][^'"]*['"][^}]*\}\s*\]/,
592
+ `documentSelector: [${langSelector}]`
593
+ );
594
+ code = code.replace(/serverModule\s*=\s*require\("path"\)\.join\(_dir,\s*_entry\);\s*/g, "");
595
+ code = code.replace(
596
+ /client\.start\(\);/g,
597
+ "client.start().catch(() => {/* init may complete async */});"
598
+ );
599
+ fs3.writeFileSync(docSelPath, code);
600
+ }
601
+ const pkgPath = path3.join(build, "package.json");
602
+ if (fs3.existsSync(pkgPath)) {
603
+ try {
604
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
605
+ const events = pkg.activationEvents || [];
606
+ const langEvents = info.languages.map((l) => `onLanguage:${l}`);
607
+ const newEvents = events.filter((e) => !e.startsWith("onLanguage:")).concat(langEvents);
608
+ if (newEvents.length > 0 && JSON.stringify(newEvents) !== JSON.stringify(events)) {
609
+ pkg.activationEvents = newEvents;
610
+ fs3.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
611
+ }
612
+ } catch {
613
+ }
614
+ }
326
615
  }
327
616
  function extensionsPkgPath() {
328
617
  return path3.join(os3.homedir(), ".config", "coc", "extensions", "package.json");
@@ -346,11 +635,12 @@ async function installToCoc(name, onProgress) {
346
635
  function metaPath(name) {
347
636
  return path3.join(cacheDir(name), "meta.json");
348
637
  }
349
- function saveMeta(name) {
638
+ async function saveMeta(name) {
350
639
  const srcDir = sourceDir(name);
351
640
  try {
352
- const commit = (0, import_child_process.execSync)("git", ["-C", srcDir, "rev-parse", "--short", "HEAD"], { encoding: "utf-8" }).toString().trim();
353
- fs3.writeFileSync(metaPath(name), JSON.stringify({ commit, updatedAt: Date.now() }, null, 2));
641
+ const log = await runWithOutput("git", ["-C", srcDir, "log", "-1", "--format=%h|%s|%ar"], srcDir);
642
+ const [commit, msg, date] = log.split("|");
643
+ fs3.writeFileSync(metaPath(name), JSON.stringify({ commit, msg, date, updatedAt: Date.now() }, null, 2));
354
644
  } catch {
355
645
  }
356
646
  }
@@ -372,11 +662,27 @@ async function installPackage(state, name) {
372
662
  try {
373
663
  const input = await downloadSource(info, name, prog);
374
664
  await convertSource(input, name, prog);
375
- await buildPackage(name, prog);
665
+ await buildPackage(name, input, info, prog);
376
666
  await installToCoc(name, prog);
377
- saveMeta(name);
667
+ await saveMeta(name);
378
668
  state.setDirty();
379
669
  state.setPackageStatus(name, "installed");
670
+ import_coc.window.showInformationMessage(`coc-${name} installed`);
671
+ try {
672
+ const meta = JSON.parse(fs3.readFileSync(metaPath(name), "utf-8"));
673
+ if (meta.commit) {
674
+ state.mutate((s) => {
675
+ const p = s.packages.find((p2) => p2.info.name === name);
676
+ if (p) {
677
+ p.commit = meta.commit;
678
+ p.commitMsg = meta.msg;
679
+ p.commitDate = meta.date;
680
+ p.updated = true;
681
+ }
682
+ });
683
+ }
684
+ } catch {
685
+ }
380
686
  } catch (e) {
381
687
  state.setPackageStatus(name, "failed", { error: e.message });
382
688
  }
@@ -404,6 +710,7 @@ async function uninstallPackage(state, name) {
404
710
  }
405
711
  state.setPackageStatus(name, "not-installed");
406
712
  state.setDirty();
713
+ import_coc.window.showInformationMessage(`coc-${name} uninstalled`);
407
714
  } catch (e) {
408
715
  state.setPackageStatus(name, "failed", { error: e.message });
409
716
  }
@@ -426,11 +733,27 @@ async function updatePackage(state, name) {
426
733
  try {
427
734
  const input = await downloadSource(info, name, prog);
428
735
  await convertSource(input, name, prog);
429
- await buildPackage(name, prog);
736
+ await buildPackage(name, input, info, prog);
430
737
  await installToCoc(name, prog);
431
- saveMeta(name);
738
+ await saveMeta(name);
432
739
  state.setDirty();
433
740
  state.setPackageStatus(name, "installed");
741
+ import_coc.window.showInformationMessage(`coc-${name} installed`);
742
+ try {
743
+ const meta = JSON.parse(fs3.readFileSync(metaPath(name), "utf-8"));
744
+ if (meta.commit) {
745
+ state.mutate((s) => {
746
+ const p = s.packages.find((p2) => p2.info.name === name);
747
+ if (p) {
748
+ p.commit = meta.commit;
749
+ p.commitMsg = meta.msg;
750
+ p.commitDate = meta.date;
751
+ p.updated = true;
752
+ }
753
+ });
754
+ }
755
+ } catch {
756
+ }
434
757
  } catch (e) {
435
758
  state.setPackageStatus(name, "failed", { error: e.message });
436
759
  }
@@ -438,6 +761,10 @@ async function updatePackage(state, name) {
438
761
  async function runWithOutput(cmd, args, cwd) {
439
762
  return new Promise((resolve2, reject) => {
440
763
  const child = (0, import_child_process.spawn)(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"], shell: true });
764
+ const timer = setTimeout(() => {
765
+ child.kill("SIGTERM");
766
+ reject(new Error(`Timed out after ${CMD_TIMEOUT / 1e3}s: ${cmd} ${args.join(" ")}`));
767
+ }, CMD_TIMEOUT);
441
768
  let out = "";
442
769
  child.stdout.on("data", (d) => {
443
770
  out += d.toString();
@@ -445,10 +772,27 @@ async function runWithOutput(cmd, args, cwd) {
445
772
  child.stderr.on("data", (d) => {
446
773
  out += d.toString();
447
774
  });
448
- child.on("close", (code) => code === 0 ? resolve2(out.trim()) : reject(new Error(`exit ${code}`)));
449
- child.on("error", reject);
775
+ child.on("close", (code) => {
776
+ clearTimeout(timer);
777
+ code === 0 ? resolve2(out.trim()) : reject(new Error(`exit ${code}`));
778
+ });
779
+ child.on("error", (e) => {
780
+ clearTimeout(timer);
781
+ reject(e);
782
+ });
450
783
  });
451
784
  }
785
+ async function runConcurrent(items, fn, concurrency = 3) {
786
+ const pool = /* @__PURE__ */ new Set();
787
+ for (const item of items) {
788
+ const p = fn(item).finally(() => pool.delete(p));
789
+ pool.add(p);
790
+ if (pool.size >= concurrency) {
791
+ await Promise.race(pool);
792
+ }
793
+ }
794
+ await Promise.all(pool);
795
+ }
452
796
  async function checkUpdates(state) {
453
797
  const s = state.getState();
454
798
  const results = {};
@@ -548,8 +892,16 @@ var LineBuffer = class {
548
892
  };
549
893
 
550
894
  // src/tui.ts
895
+ var VERSION = (() => {
896
+ try {
897
+ const pkg = JSON.parse(require("fs").readFileSync(require("path").join(__dirname, "..", "package.json"), "utf-8"));
898
+ return pkg.version;
899
+ } catch {
900
+ return "0.0.0";
901
+ }
902
+ })();
551
903
  var HELP_TEXT = [
552
- " coc-loader \u2014 VS Code extension \u2192 coc.nvim plugin converter",
904
+ ` coc-loader v${VERSION} \u2014 VS Code extension \u2192 coc.nvim plugin converter`,
553
905
  "",
554
906
  " Keymaps:",
555
907
  " i Install package under cursor",
@@ -557,10 +909,18 @@ var HELP_TEXT = [
557
909
  " U Update all installed packages",
558
910
  " C Check for updates from remote",
559
911
  " X Uninstall package under cursor",
912
+ " R Reinstall package under cursor",
560
913
  " Z Uninstall all installed packages (with confirm)",
914
+ " D Clean up orphaned packages",
915
+ " x Toggle mark",
916
+ " f Cycle filter: all \u2192 installed \u2192 not-installed",
917
+ " s Cycle sort: default \u2192 name \u2192 status \u2192 type",
918
+ " gg Jump to first package",
919
+ " G Jump to last package",
561
920
  " <Enter> Toggle expand/collapse details",
562
921
  " / Search filter",
563
- " q / <Esc> Close window",
922
+ " q Close window",
923
+ " <Esc> Help\u2192Search\u2192Marks\u2192Busy guard\u2192Close",
564
924
  "",
565
925
  " " + "\u2500".repeat(40),
566
926
  "",
@@ -588,13 +948,20 @@ var TUI = class {
588
948
  i: "i",
589
949
  u: "u",
590
950
  X: "X",
591
- cr: "<CR>"
951
+ R: "R",
952
+ cr: "<CR>",
953
+ f: "f",
954
+ s: "s",
955
+ x: "x",
956
+ D: "D",
957
+ gg: "gg",
958
+ G: "G"
592
959
  };
593
960
  this.rendering = false;
594
961
  this.state = state;
595
962
  }
596
963
  async open() {
597
- const nvim = import_coc.workspace.nvim;
964
+ const nvim = import_coc2.workspace.nvim;
598
965
  this.ns = await nvim.createNamespace("coc-loader");
599
966
  await nvim.command("highlight default link CocConverterTitle Title");
600
967
  await nvim.command("highlight default link CocConverterPill Visual");
@@ -639,7 +1006,7 @@ var TUI = class {
639
1006
  this.render();
640
1007
  });
641
1008
  this.disposables.push(
642
- import_coc.workspace.registerAutocmd({
1009
+ import_coc2.workspace.registerAutocmd({
643
1010
  event: "WinEnter",
644
1011
  request: true,
645
1012
  callback: async () => {
@@ -663,10 +1030,14 @@ var TUI = class {
663
1030
  `);
664
1031
  }
665
1032
  await this.setupKeymaps();
1033
+ updateRegistry().then(() => this.state.refreshPackages()).catch(() => {
1034
+ this.state.setStatusMessage("Failed to fetch remote registry (offline?)");
1035
+ setTimeout(() => this.state.setStatusMessage(), 5e3);
1036
+ });
666
1037
  await this.render();
667
1038
  }
668
1039
  async getCursorLine0() {
669
- const nvim = import_coc.workspace.nvim;
1040
+ const nvim = import_coc2.workspace.nvim;
670
1041
  const cursor = await nvim.call("nvim_win_get_cursor", [this.winid]);
671
1042
  return cursor[0] - 1;
672
1043
  }
@@ -696,6 +1067,16 @@ var TUI = class {
696
1067
  this.state.setSearchQuery("");
697
1068
  return;
698
1069
  }
1070
+ const hasMarks = s.packages.some((p) => p.marked);
1071
+ if (hasMarks) {
1072
+ this.state.clearMarks();
1073
+ return;
1074
+ }
1075
+ const busy = s.packages.some((p) => ["installing", "updating", "uninstalling"].includes(p.status));
1076
+ if (busy) {
1077
+ import_coc2.window.showInformationMessage("Operation in progress, wait for it to finish");
1078
+ return;
1079
+ }
699
1080
  this.close();
700
1081
  return;
701
1082
  }
@@ -705,24 +1086,60 @@ var TUI = class {
705
1086
  }
706
1087
  if (id === "slash") {
707
1088
  try {
708
- const q = await import_coc.workspace.nvim.call("input", ["Search: ", ""]);
1089
+ const q = await import_coc2.workspace.nvim.call("input", ["Search: ", ""]);
709
1090
  if (q) this.state.setSearchQuery(q);
710
1091
  } catch {
711
1092
  }
712
1093
  return;
713
1094
  }
1095
+ if (id === "f") {
1096
+ this.state.cycleViewFilter();
1097
+ return;
1098
+ }
1099
+ if (id === "s") {
1100
+ this.state.cycleSortBy();
1101
+ return;
1102
+ }
1103
+ if (id === "gg") {
1104
+ const firstLine = Math.min(...this.pkgLineMap.keys());
1105
+ if (isFinite(firstLine)) {
1106
+ await import_coc2.workspace.nvim.call("nvim_win_set_cursor", [this.winid, [firstLine + 1, 0]]);
1107
+ }
1108
+ return;
1109
+ }
1110
+ if (id === "G") {
1111
+ const lastLine = Math.max(...this.pkgLineMap.keys());
1112
+ if (isFinite(lastLine)) {
1113
+ await import_coc2.workspace.nvim.call("nvim_win_set_cursor", [this.winid, [lastLine + 1, 0]]);
1114
+ }
1115
+ return;
1116
+ }
1117
+ if (id === "D") {
1118
+ const installed = s.packages.filter((p) => p.status === "installed");
1119
+ const removed = installed.filter((p) => !getPackage(p.info.name));
1120
+ if (removed.length === 0) {
1121
+ import_coc2.window.showInformationMessage("No orphaned packages found");
1122
+ return;
1123
+ }
1124
+ const ok = await import_coc2.window.showPrompt(`Uninstall ${removed.length} orphaned package(s)?`);
1125
+ if (ok) {
1126
+ for (const p of removed) uninstallPackage(this.state, p.info.name);
1127
+ }
1128
+ return;
1129
+ }
714
1130
  if (id === "U") {
715
1131
  const installed = s.packages.filter((p) => p.status === "installed");
716
1132
  if (installed.length === 0) return;
717
1133
  this.state.setActivePill("U");
718
- await Promise.all(installed.map((p) => updatePackage(this.state, p.info.name)));
1134
+ const names = installed.map((p) => p.info.name);
1135
+ await runConcurrent(names, (name) => updatePackage(this.state, name));
719
1136
  this.state.setActivePill(null);
720
1137
  return;
721
1138
  }
722
1139
  if (id === "Z") {
723
1140
  const installed = s.packages.filter((p) => p.status === "installed");
724
1141
  if (installed.length === 0) return;
725
- const ok = await import_coc.window.showPrompt(`Uninstall all ${installed.length} packages?`);
1142
+ const ok = await import_coc2.window.showPrompt(`Uninstall all ${installed.length} packages?`);
726
1143
  if (ok) {
727
1144
  for (const pkg of installed) uninstallPackage(this.state, pkg.info.name);
728
1145
  }
@@ -738,6 +1155,10 @@ var TUI = class {
738
1155
  if (!pkgName) return;
739
1156
  const entry = this.state.getPackage(pkgName);
740
1157
  if (!entry) return;
1158
+ if (id === "x") {
1159
+ this.state.toggleMark(pkgName);
1160
+ return;
1161
+ }
741
1162
  if (id === "i" && entry.status === "not-installed") {
742
1163
  await installPackage(this.state, pkgName);
743
1164
  return;
@@ -750,6 +1171,11 @@ var TUI = class {
750
1171
  uninstallPackage(this.state, pkgName);
751
1172
  return;
752
1173
  }
1174
+ if (id === "R" && entry.status === "installed") {
1175
+ await uninstallPackage(this.state, pkgName);
1176
+ await installPackage(this.state, pkgName);
1177
+ return;
1178
+ }
753
1179
  if (id === "cr") {
754
1180
  if (this.logLineSet.has(line0)) {
755
1181
  this.state.toggleLog(pkgName);
@@ -760,7 +1186,7 @@ var TUI = class {
760
1186
  }
761
1187
  }
762
1188
  async setupKeymaps() {
763
- const buf = import_coc.workspace.nvim.createBuffer(this.bufnr);
1189
+ const buf = import_coc2.workspace.nvim.createBuffer(this.bufnr);
764
1190
  const entries = [
765
1191
  ["q", "q"],
766
1192
  ["<Esc>", "esc"],
@@ -774,7 +1200,13 @@ var TUI = class {
774
1200
  ["H", "H"],
775
1201
  ["u", "u"],
776
1202
  ["X", "X"],
777
- ["x", "X"],
1203
+ ["R", "R"],
1204
+ ["f", "f"],
1205
+ ["s", "s"],
1206
+ ["x", "x"],
1207
+ ["D", "D"],
1208
+ ["gg", "gg"],
1209
+ ["G", "G"],
778
1210
  ["<CR>", "cr"]
779
1211
  ];
780
1212
  for (const [vimKey, id] of entries) {
@@ -793,20 +1225,20 @@ var TUI = class {
793
1225
  this.disposables = [];
794
1226
  if (this.winid) {
795
1227
  try {
796
- await import_coc.workspace.nvim.call("nvim_win_close", [this.winid, true]);
1228
+ await import_coc2.workspace.nvim.call("nvim_win_close", [this.winid, true]);
797
1229
  } catch {
798
1230
  }
799
1231
  this.winid = 0;
800
1232
  }
801
1233
  if (needRestart) {
802
- import_coc.workspace.nvim.command("CocRestart", true);
1234
+ import_coc2.workspace.nvim.command("CocRestart", true);
803
1235
  }
804
1236
  }
805
1237
  async render() {
806
1238
  if (!this.winid || this.rendering) return;
807
1239
  this.rendering = true;
808
1240
  try {
809
- const nvim = import_coc.workspace.nvim;
1241
+ const nvim = import_coc2.workspace.nvim;
810
1242
  const state = this.state.getState();
811
1243
  const filtered = this.state.getFilteredPackages();
812
1244
  const result = state.showHelp ? this.renderHelp() : this.renderPackageList(state, filtered);
@@ -832,7 +1264,7 @@ var TUI = class {
832
1264
  renderHelp() {
833
1265
  const header = [
834
1266
  "",
835
- " coc-loader v0.1",
1267
+ ` coc-loader v${VERSION}`,
836
1268
  " press ? help | / search | q quit",
837
1269
  " " + "\u2500".repeat(50),
838
1270
  ""
@@ -874,7 +1306,13 @@ var TUI = class {
874
1306
  buf.highlight(/\([IU?C]\)/g, "CocConverterKey");
875
1307
  buf.nl();
876
1308
  buf.nl();
1309
+ const filterLabel = state.viewFilter === "all" ? "All" : state.viewFilter === "installed" ? "Installed" : "Available";
1310
+ const sortLabel = state.sortBy === "default" ? "Default" : state.sortBy === "name" ? "Name" : state.sortBy === "status" ? "Status" : "Type";
877
1311
  buf.append(`Total: ${filtered.length} packages`, "CocConverterTotal");
1312
+ buf.append(` | `);
1313
+ buf.append(`F:${filterLabel}(f)`, "CocConverterPill");
1314
+ buf.append(` `);
1315
+ buf.append(`S:${sortLabel}(s)`, "CocConverterPill");
878
1316
  if (state.statusMessage) {
879
1317
  buf.append(" \xB7 ");
880
1318
  buf.append(state.statusMessage, "Comment");
@@ -897,6 +1335,10 @@ var TUI = class {
897
1335
  if (filtered.length === 0 && state.searchQuery) {
898
1336
  buf.nl("no matching packages");
899
1337
  }
1338
+ buf.nl();
1339
+ buf.append(" " + "\u2500".repeat(50), "Comment");
1340
+ buf.nl();
1341
+ buf.append(` ${filtered.length} packages \xB7 ${filterLabel} \xB7 ${sortLabel} order`, "Comment");
900
1342
  const result = buf.render(2);
901
1343
  return { lines: result.lines, pkgLineMap, logLines: logSet, highlights: result.highlights };
902
1344
  }
@@ -905,7 +1347,12 @@ var TUI = class {
905
1347
  const iconHl = entry.status === "installed" ? "CocConverterInstalled" : entry.status === "failed" ? "ErrorMsg" : "CocConverterAvailable";
906
1348
  const pkgLine = buf.currentLine();
907
1349
  pkgLineMap.set(pkgLine, entry.info.name);
908
- buf.append(" ");
1350
+ if (entry.marked) {
1351
+ buf.append("\u25B8", "CocConverterKey");
1352
+ buf.append(" ");
1353
+ } else {
1354
+ buf.append(" ");
1355
+ }
909
1356
  buf.append(icon, iconHl);
910
1357
  buf.append(" ");
911
1358
  buf.append(entry.info.displayName);
@@ -914,17 +1361,28 @@ var TUI = class {
914
1361
  if (entry.hasUpdate) {
915
1362
  buf.append(" \u2191", "CocConverterKey");
916
1363
  }
1364
+ if (entry.updated && entry.commit && entry.commitMsg) {
1365
+ buf.nl();
1366
+ const ln = buf.currentLine();
1367
+ buf.append(` ${entry.commit} ${entry.commitMsg}`, "Comment");
1368
+ if (entry.commitDate) {
1369
+ buf.append(` (${entry.commitDate})`, "Comment");
1370
+ }
1371
+ pkgLineMap.set(ln, entry.info.name);
1372
+ }
917
1373
  if (entry.expanded) {
918
1374
  buf.nl();
919
- for (const text of [
1375
+ const extras = [
920
1376
  entry.info.description,
921
1377
  `type ${entry.info.type}`,
922
1378
  entry.commit ? `commit ${entry.commit}` : null,
923
1379
  `source ${sourceStr(entry.info.source)}`,
924
1380
  `languages ${entry.info.languages.join(", ")}`,
925
1381
  `categories ${entry.info.categories.join(", ")}`,
926
- `homepage ${entry.info.url}`
927
- ].filter(Boolean)) {
1382
+ `homepage ${entry.info.url}`,
1383
+ entry.info.serverBinary ? `server ${entry.info.serverBinary.repo} (binary release)` : null
1384
+ ];
1385
+ for (const text of extras.filter(Boolean)) {
928
1386
  const ln = buf.currentLine();
929
1387
  buf.nl(` ${text}`);
930
1388
  pkgLineMap.set(ln, entry.info.name);
@@ -960,9 +1418,6 @@ var TUI = class {
960
1418
  }
961
1419
  buf.nl();
962
1420
  }
963
- hl(line, hlGroup, colStart, colEnd) {
964
- this.hlLines.push({ line, hlGroup, colStart, colEnd });
965
- }
966
1421
  isOpen() {
967
1422
  return this.winid !== 0;
968
1423
  }
@@ -978,7 +1433,7 @@ var currentTUI = null;
978
1433
  async function activate(context) {
979
1434
  const state = new StateManager(createInitialState());
980
1435
  context.subscriptions.push(
981
- import_coc2.commands.registerCommand("loader.open", async () => {
1436
+ import_coc3.commands.registerCommand("loader.open", async () => {
982
1437
  if (currentTUI && currentTUI.isOpen()) {
983
1438
  await currentTUI.close();
984
1439
  }
@@ -987,67 +1442,67 @@ async function activate(context) {
987
1442
  })
988
1443
  );
989
1444
  context.subscriptions.push(
990
- import_coc2.commands.registerCommand("loader.install", async (name) => {
1445
+ import_coc3.commands.registerCommand("loader.install", async (name) => {
991
1446
  if (!name) {
992
- name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1447
+ name = await import_coc3.workspace.nvim.call("input", ["Plugin name: ", ""]);
993
1448
  if (!name) return;
994
1449
  }
995
1450
  const pkg = state.getPackage(name);
996
1451
  if (!pkg) {
997
- import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1452
+ import_coc3.window.showInformationMessage(`Unknown package: ${name}`);
998
1453
  return;
999
1454
  }
1000
1455
  if (pkg.status === "installed") {
1001
- import_coc2.window.showInformationMessage(`${name} is already installed`);
1456
+ import_coc3.window.showInformationMessage(`${name} is already installed`);
1002
1457
  return;
1003
1458
  }
1004
1459
  await installPackage(state, name);
1005
1460
  })
1006
1461
  );
1007
1462
  context.subscriptions.push(
1008
- import_coc2.commands.registerCommand("loader.uninstall", async (name) => {
1463
+ import_coc3.commands.registerCommand("loader.uninstall", async (name) => {
1009
1464
  if (!name) {
1010
- name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1465
+ name = await import_coc3.workspace.nvim.call("input", ["Plugin name: ", ""]);
1011
1466
  if (!name) return;
1012
1467
  }
1013
1468
  const pkg = state.getPackage(name);
1014
1469
  if (!pkg) {
1015
- import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1470
+ import_coc3.window.showInformationMessage(`Unknown package: ${name}`);
1016
1471
  return;
1017
1472
  }
1018
1473
  if (pkg.status !== "installed") {
1019
- import_coc2.window.showInformationMessage(`${name} is not installed`);
1474
+ import_coc3.window.showInformationMessage(`${name} is not installed`);
1020
1475
  return;
1021
1476
  }
1022
1477
  uninstallPackage(state, name);
1023
1478
  })
1024
1479
  );
1025
1480
  context.subscriptions.push(
1026
- import_coc2.commands.registerCommand("loader.update", async (name) => {
1481
+ import_coc3.commands.registerCommand("loader.update", async (name) => {
1027
1482
  if (!name) {
1028
- name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1483
+ name = await import_coc3.workspace.nvim.call("input", ["Plugin name: ", ""]);
1029
1484
  if (!name) return;
1030
1485
  }
1031
1486
  const pkg = state.getPackage(name);
1032
1487
  if (!pkg) {
1033
- import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1488
+ import_coc3.window.showInformationMessage(`Unknown package: ${name}`);
1034
1489
  return;
1035
1490
  }
1036
1491
  if (pkg.status !== "installed") {
1037
- import_coc2.window.showInformationMessage(`${name} is not installed`);
1492
+ import_coc3.window.showInformationMessage(`${name} is not installed`);
1038
1493
  return;
1039
1494
  }
1040
1495
  await updatePackage(state, name);
1041
1496
  })
1042
1497
  );
1043
1498
  context.subscriptions.push(
1044
- import_coc2.commands.registerCommand("loader.uninstallAll", async () => {
1499
+ import_coc3.commands.registerCommand("loader.uninstallAll", async () => {
1045
1500
  const installed = state.getState().packages.filter((p) => p.status === "installed");
1046
1501
  if (installed.length === 0) {
1047
- import_coc2.window.showInformationMessage("No packages installed");
1502
+ import_coc3.window.showInformationMessage("No packages installed");
1048
1503
  return;
1049
1504
  }
1050
- const ok = await import_coc2.window.showPrompt(`Uninstall all ${installed.length} packages?`);
1505
+ const ok = await import_coc3.window.showPrompt(`Uninstall all ${installed.length} packages?`);
1051
1506
  if (ok) {
1052
1507
  for (const pkg of installed) {
1053
1508
  uninstallPackage(state, pkg.info.name);
@@ -1056,23 +1511,42 @@ async function activate(context) {
1056
1511
  })
1057
1512
  );
1058
1513
  context.subscriptions.push(
1059
- import_coc2.commands.registerCommand("loader.updateRegistry", async () => {
1514
+ import_coc3.commands.registerCommand("loader.reinstall", async (name) => {
1515
+ if (!name) {
1516
+ name = await import_coc3.workspace.nvim.call("input", ["Plugin name: ", ""]);
1517
+ if (!name) return;
1518
+ }
1519
+ const pkg = state.getPackage(name);
1520
+ if (!pkg) {
1521
+ import_coc3.window.showInformationMessage(`Unknown package: ${name}`);
1522
+ return;
1523
+ }
1524
+ if (pkg.status !== "installed") {
1525
+ import_coc3.window.showInformationMessage(`${name} is not installed`);
1526
+ return;
1527
+ }
1528
+ await uninstallPackage(state, name);
1529
+ await installPackage(state, name);
1530
+ })
1531
+ );
1532
+ context.subscriptions.push(
1533
+ import_coc3.commands.registerCommand("loader.updateRegistry", async () => {
1060
1534
  try {
1061
1535
  const count = await updateRegistry();
1062
- import_coc2.window.showInformationMessage(`Registry updated: ${count} packages available. Restart coc to apply.`);
1536
+ import_coc3.window.showInformationMessage(`Registry updated: ${count} packages available. Restart coc to apply.`);
1063
1537
  } catch (e) {
1064
- import_coc2.window.showErrorMessage(`Registry update failed: ${e.message}`);
1538
+ import_coc3.window.showErrorMessage(`Registry update failed: ${e.message}`);
1065
1539
  }
1066
1540
  })
1067
1541
  );
1068
1542
  context.subscriptions.push(
1069
- import_coc2.commands.registerCommand("loader._dispatch", async (key) => {
1543
+ import_coc3.commands.registerCommand("loader._dispatch", async (key) => {
1070
1544
  if (currentTUI) {
1071
1545
  await currentTUI.handleKey(key);
1072
1546
  }
1073
1547
  })
1074
1548
  );
1075
- import_coc2.window.showInformationMessage("coc-loader activated! Use :CocCommand loader.open");
1549
+ import_coc3.window.showInformationMessage("coc-loader activated! Use :CocCommand loader.open");
1076
1550
  }
1077
1551
  // Annotate the CommonJS export names for ESM import in node:
1078
1552
  0 && (module.exports = {