coc-vscode-loader 1.1.1 → 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/README.md CHANGED
@@ -24,17 +24,25 @@ npm install coc-vscode-loader
24
24
  | Key | Action |
25
25
  |-----|--------|
26
26
  | `I` | Install mode (button highlight) |
27
- | `U` | Update all installed packages |
27
+ | `U` | Update all installed packages (max 3 concurrent) |
28
28
  | `C` | Check for remote updates (git ls-remote commit compare) |
29
29
  | `Z` | Uninstall all installed packages (with confirmation) |
30
+ | `D` | Cleanup orphaned packages (installed but removed from registry) |
30
31
  | `H` | Home (reset all state) |
31
32
  | `?` | Help |
32
33
  | `i` | Install package under cursor |
33
34
  | `u` | Update package under cursor |
34
- | `X` / `x` | Uninstall package under cursor |
35
+ | `X` | Uninstall package under cursor |
36
+ | `R` | Reinstall package under cursor |
37
+ | `x` | Toggle mark package for batch operations |
38
+ | `f` | Cycle filter: all → installed → available |
39
+ | `s` | Cycle sort: default → name → status → type |
40
+ | `gg` | Jump to first package |
41
+ | `G` | Jump to last package |
35
42
  | `<CR>` | Toggle details (commit / type / source) or install log |
36
43
  | `/` | Search filter |
37
- | `q` / `<Esc>` | Close (auto `:CocRestart` if changes detected) |
44
+ | `q` | Close (auto `:CocRestart` if changes detected) |
45
+ | `<Esc>` | Help→Search→Clear marks→Cancel|Close |
38
46
 
39
47
  ## Commands
40
48
 
@@ -44,28 +52,33 @@ npm install coc-vscode-loader
44
52
  | `:CocCommand loader.install <name>` | Install a package |
45
53
  | `:CocCommand loader.uninstall <name>` | Uninstall a package |
46
54
  | `:CocCommand loader.update <name>` | Update a package |
55
+ | `:CocCommand loader.reinstall <name>` | Reinstall a package (uninstall + install) |
47
56
  | `:CocCommand loader.uninstallAll` | Uninstall all (with confirmation) |
48
57
  | `:CocCommand loader.updateRegistry` | Fetch latest registry from remote |
49
58
 
50
59
  ## Features
51
60
 
52
61
  - **Real conversion pipeline** — git clone → converter → npm install → esbuild → register to coc
53
- - **Auto-fetch registry** — remote registry fetched in background on startup, no manual refresh needed
62
+ - **Auto-fetch registry** — remote registry fetched in background when TUI opens, no manual refresh needed
54
63
  - **Incremental cache** — source/ keeps git repo, updates via git pull only
55
64
  - **Commit tracking** — records commit SHA after install, visible in detail view
56
65
  - **Update check** — `C` key compares against remote HEAD, shows `↑` when outdated
57
66
  - **Auto restart** — `:CocRestart` triggered automatically on close when changes detected
58
67
  - **Manual registry update** — `:CocCommand loader.updateRegistry` also available for re-fetch
59
68
  - **Install logs** — real command output per step, expandable
69
+ - **Mark & batch** — `x` toggle mark, visual indicator, `D` clean orphaned packages
70
+ - **Filter & sort** — `f` cycle view filter, `s` cycle sort order (name/status/type)
71
+ - **Concurrency limit** — max 3 parallel operations for `U` (Update All)
72
+ - **Desktop notifications** — `showInformationMessage` on install/update/uninstall complete
60
73
 
61
74
  ## Architecture
62
75
 
63
76
  | File | Description |
64
77
  |------|-------------|
65
- | `src/index.ts` | Plugin entry + 7 CocCommands |
78
+ | `src/index.ts` | Plugin entry + 8 CocCommands |
66
79
  | `src/tui.ts` | TUI window management + rendering + key dispatch |
67
80
  | `src/state.ts` | State management (debounced rendering) |
68
- | `src/registry.ts` | Built-in registry + remote update cache |
81
+ | `src/registry.ts` | Remote registry fetch + disk cache |
69
82
  | `src/pipeline.ts` | Real install/update/uninstall flow (git + npx tsx + npm + node + cp) |
70
83
  | `src/renderer.ts` | LineBuffer render engine (inspired by lazy.nvim) |
71
84
 
Binary file
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,21 +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
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
+ }
45
130
  function loadCache() {
46
131
  try {
47
132
  if (fs.existsSync(CACHE_PATH)) {
@@ -52,6 +137,16 @@ function loadCache() {
52
137
  return null;
53
138
  }
54
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
+ }
55
150
  const res = await fetch(REMOTE_REGISTRY_URL);
56
151
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
57
152
  const data = await res.json();
@@ -61,10 +156,21 @@ async function updateRegistry() {
61
156
  cached = data;
62
157
  return data.length;
63
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
+ }
64
169
  function getAllPackages() {
65
- if (cached) return cached;
66
- cached = loadCache();
67
- return cached || [];
170
+ if (!cached) {
171
+ cached = loadCache() || [];
172
+ }
173
+ return cached.filter((p) => !p.minPluginVersion || satisfiesVersion(p.minPluginVersion));
68
174
  }
69
175
  function getPackage(name) {
70
176
  return getAllPackages().find((p) => p.name === name);
@@ -102,10 +208,11 @@ function createInitialState() {
102
208
  commitDate,
103
209
  progressLog: [],
104
210
  expanded: false,
105
- logExpanded: false
211
+ logExpanded: false,
212
+ marked: false
106
213
  };
107
214
  });
108
- 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" };
109
216
  }
110
217
  var StateManager = class {
111
218
  constructor(initial) {
@@ -144,8 +251,11 @@ var StateManager = class {
144
251
  }
145
252
  pkg.status = status;
146
253
  if (extra?.progress !== void 0) pkg.progress = extra.progress;
147
- if (extra?.logEntry !== void 0) pkg.progressLog.push(extra.logEntry);
148
- 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
+ }
149
259
  if (extra?.error !== void 0) pkg.error = extra.error;
150
260
  if (status === "installed" || status === "not-installed") {
151
261
  pkg.progress = void 0;
@@ -175,6 +285,21 @@ var StateManager = class {
175
285
  s.viewFilter = filter;
176
286
  });
177
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
+ }
178
303
  setStatusMessage(msg) {
179
304
  this.mutate((s) => {
180
305
  s.statusMessage = msg;
@@ -203,14 +328,39 @@ var StateManager = class {
203
328
  pkgs = pkgs.filter((p) => p.status === "installed");
204
329
  }
205
330
  const q = this.state.searchQuery.toLowerCase();
206
- if (!q) return pkgs;
207
- return pkgs.filter(
208
- (p) => p.info.name.toLowerCase().includes(q) || p.info.displayName.toLowerCase().includes(q) || p.info.description.toLowerCase().includes(q)
209
- );
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;
210
346
  }
211
347
  getPackage(name) {
212
348
  return this.state.packages.find((p) => p.info.name === name);
213
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
+ }
214
364
  refreshPackages() {
215
365
  this.mutate((s) => {
216
366
  const updated = getAllPackages();
@@ -226,7 +376,8 @@ var StateManager = class {
226
376
  status: isInstalled(info.name) ? "installed" : "not-installed",
227
377
  progressLog: [],
228
378
  expanded: false,
229
- logExpanded: false
379
+ logExpanded: false,
380
+ marked: false
230
381
  };
231
382
  });
232
383
  });
@@ -234,10 +385,11 @@ var StateManager = class {
234
385
  };
235
386
 
236
387
  // src/tui.ts
237
- var import_coc = require("coc.nvim");
388
+ var import_coc2 = require("coc.nvim");
238
389
 
239
390
  // src/pipeline.ts
240
391
  var import_child_process = require("child_process");
392
+ var import_coc = require("coc.nvim");
241
393
  var path3 = __toESM(require("path"));
242
394
  var fs3 = __toESM(require("fs"));
243
395
  var os3 = __toESM(require("os"));
@@ -256,12 +408,9 @@ function pluginDir(name) {
256
408
  }
257
409
  function converterCliPath() {
258
410
  const base = path3.resolve(__dirname, "..");
259
- const cwd = process.cwd();
260
411
  const candidates = [
261
412
  path3.join(base, "converter", "src", "cli.ts"),
262
- path3.join(base, "..", "converter", "src", "cli.ts"),
263
- path3.join(cwd, "converter", "src", "cli.ts"),
264
- path3.join(cwd, "..", "converter", "src", "cli.ts")
413
+ path3.join(base, "..", "converter", "src", "cli.ts")
265
414
  ];
266
415
  for (const p of candidates) {
267
416
  if (fs3.existsSync(p)) return p;
@@ -270,37 +419,43 @@ function converterCliPath() {
270
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/"
271
420
  );
272
421
  }
422
+ var CMD_TIMEOUT = 3e5;
273
423
  async function run(cmd, args, cwd, onLine) {
274
424
  return new Promise((resolve2, reject) => {
275
425
  const child = (0, import_child_process.spawn)(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"], shell: true });
276
- 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);
277
430
  const handler = (data) => {
278
431
  const text = data.toString();
279
- lines.push(text);
280
- onLine(text);
432
+ onLine?.(text);
281
433
  };
282
434
  child.stdout.on("data", handler);
283
435
  child.stderr.on("data", handler);
284
436
  child.on("close", (code) => {
437
+ clearTimeout(timer);
285
438
  if (code === 0) resolve2();
286
439
  else reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
287
440
  });
288
- child.on("error", reject);
441
+ child.on("error", (e) => {
442
+ clearTimeout(timer);
443
+ reject(e);
444
+ });
289
445
  });
290
446
  }
291
447
  async function downloadSource(info, name, onProgress) {
292
448
  const srcDir = sourceDir(name);
293
449
  const cache = cacheDir(name);
294
450
  const repoUrl = `https://github.com/${info.source.repo}.git`;
451
+ const log = (chunk) => onProgress(1, 5, chunk.trim(), "");
295
452
  if (fs3.existsSync(srcDir)) {
296
453
  onProgress(1, 5, "Updating source...", `git -C ${srcDir} pull`);
297
- await run("git", ["-C", srcDir, "pull"], cache, () => {
298
- });
454
+ await run("git", ["-C", srcDir, "pull"], cache, log);
299
455
  } else {
300
456
  onProgress(1, 5, "Cloning repository...", `git clone --depth=1 ${repoUrl}`);
301
457
  fs3.mkdirSync(cache, { recursive: true });
302
- await run("git", ["clone", "--depth=1", repoUrl, srcDir], cache, () => {
303
- });
458
+ await run("git", ["clone", "--depth=1", repoUrl, srcDir], cache, log);
304
459
  }
305
460
  return info.source.subdir ? path3.join(srcDir, info.source.subdir) : srcDir;
306
461
  }
@@ -309,51 +464,152 @@ async function convertSource(inputDir, name, onProgress) {
309
464
  if (fs3.existsSync(build)) fs3.rmSync(build, { recursive: true });
310
465
  const cli = converterCliPath();
311
466
  onProgress(2, 5, "Converting...", `converter convert ${inputDir} -o ${build}`);
312
- await run("npx", ["tsx", cli, "convert", inputDir, "-o", build], cacheDir(name), () => {
313
- });
467
+ const log = (chunk) => onProgress(2, 5, chunk.trim(), "");
468
+ await run("npx", ["tsx", cli, "convert", inputDir, "-o", build], cacheDir(name), log);
314
469
  }
315
470
  async function buildPackage(name, inputDir, info, onProgress) {
316
471
  const build = buildDir(name);
472
+ const npmLog = (chunk) => onProgress(3, 5, chunk.trim(), "");
317
473
  onProgress(3, 5, "Installing dependencies...", "npm install --legacy-peer-deps");
318
- await run("npm", ["install", "--legacy-peer-deps"], build, () => {
319
- });
474
+ await run("npm", ["install", "--legacy-peer-deps"], build, npmLog);
320
475
  onProgress(3, 5, "Running postinstall...", "npm run postinstall");
321
- await run("npm", ["run", "postinstall", "--if-present"], build, () => {
322
- }).catch(() => {
476
+ await run("npm", ["run", "postinstall", "--if-present"], build, npmLog).catch(() => {
323
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
+ }
324
507
  const serverDir = path3.join(inputDir, "server");
325
508
  if (fs3.existsSync(serverDir) && fs3.existsSync(path3.join(serverDir, "package.json"))) {
326
509
  onProgress(3, 5, "Installing server dependencies...", `npm install in ${serverDir}`);
327
- await run("npm", ["install", "--legacy-peer-deps"], serverDir, () => {
328
- });
510
+ await run("npm", ["install", "--legacy-peer-deps"], serverDir, npmLog);
329
511
  const destServer = path3.join(build, "server");
330
512
  if (fs3.existsSync(destServer)) fs3.rmSync(destServer, { recursive: true });
331
513
  fs3.cpSync(serverDir, destServer, { recursive: true });
332
514
  }
333
515
  onProgress(4, 5, "Building...", "node esbuild.mjs");
334
- await run("node", ["esbuild.mjs"], build, () => {
335
- });
516
+ const buildLog = (chunk) => onProgress(4, 5, chunk.trim(), "");
517
+ await run("node", ["esbuild.mjs"], build, buildLog);
336
518
  if (info.serverBinary) {
337
519
  const sb = info.serverBinary;
338
520
  onProgress(4, 5, "Downloading language server...", `fetching ${sb.repo}`);
339
521
  try {
340
- const tagData = await runWithOutput("gh", ["api", `repos/${sb.repo}/releases/latest`, "--jq", ".tag_name"], os3.homedir());
341
- const version = tagData.replace(/^v/, "");
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
+ };
342
536
  const arch2 = os3.arch() === "arm64" ? "arm64" : "x64";
343
537
  const platform = process.platform === "win32" ? "win32" : process.platform === "darwin" ? "darwin" : "linux";
344
- const filename = sb.asset.replace("{{version}}", version).replace("{{platform}}", platform).replace("{{arch}}", arch2);
345
- const url = `https://github.com/${sb.repo}/releases/download/${version}/${filename}`;
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}`;
346
542
  onProgress(4, 5, "Downloading...", `curl ${filename}`);
347
- await run("curl", ["-sSL", url, "-o", path3.join(build, filename)], build, () => {
348
- });
349
- onProgress(4, 5, "Extracting...", `tar xzf ${filename}`);
543
+ await run("curl", ["-#SL", url, "-o", path3.join(build, filename)], build);
544
+ onProgress(4, 5, "Extracting...", filename);
350
545
  const serverDir2 = path3.join(build, "server");
351
546
  fs3.mkdirSync(serverDir2, { recursive: true });
352
- await run("tar", ["xzf", filename, "-C", serverDir2], build, () => {
353
- });
354
- fs3.rmSync(path3.join(build, filename));
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
+ }
355
612
  } catch {
356
- onProgress(4, 5, "Warning: server download failed", "install server binary manually");
357
613
  }
358
614
  }
359
615
  }
@@ -379,10 +635,10 @@ async function installToCoc(name, onProgress) {
379
635
  function metaPath(name) {
380
636
  return path3.join(cacheDir(name), "meta.json");
381
637
  }
382
- function saveMeta(name) {
638
+ async function saveMeta(name) {
383
639
  const srcDir = sourceDir(name);
384
640
  try {
385
- const log = (0, import_child_process.execSync)(`git -C "${srcDir}" log -1 --format="%h|%s|%ar"`, { encoding: "utf-8" }).toString().trim();
641
+ const log = await runWithOutput("git", ["-C", srcDir, "log", "-1", "--format=%h|%s|%ar"], srcDir);
386
642
  const [commit, msg, date] = log.split("|");
387
643
  fs3.writeFileSync(metaPath(name), JSON.stringify({ commit, msg, date, updatedAt: Date.now() }, null, 2));
388
644
  } catch {
@@ -408,9 +664,10 @@ async function installPackage(state, name) {
408
664
  await convertSource(input, name, prog);
409
665
  await buildPackage(name, input, info, prog);
410
666
  await installToCoc(name, prog);
411
- saveMeta(name);
667
+ await saveMeta(name);
412
668
  state.setDirty();
413
669
  state.setPackageStatus(name, "installed");
670
+ import_coc.window.showInformationMessage(`coc-${name} installed`);
414
671
  try {
415
672
  const meta = JSON.parse(fs3.readFileSync(metaPath(name), "utf-8"));
416
673
  if (meta.commit) {
@@ -453,6 +710,7 @@ async function uninstallPackage(state, name) {
453
710
  }
454
711
  state.setPackageStatus(name, "not-installed");
455
712
  state.setDirty();
713
+ import_coc.window.showInformationMessage(`coc-${name} uninstalled`);
456
714
  } catch (e) {
457
715
  state.setPackageStatus(name, "failed", { error: e.message });
458
716
  }
@@ -477,9 +735,10 @@ async function updatePackage(state, name) {
477
735
  await convertSource(input, name, prog);
478
736
  await buildPackage(name, input, info, prog);
479
737
  await installToCoc(name, prog);
480
- saveMeta(name);
738
+ await saveMeta(name);
481
739
  state.setDirty();
482
740
  state.setPackageStatus(name, "installed");
741
+ import_coc.window.showInformationMessage(`coc-${name} installed`);
483
742
  try {
484
743
  const meta = JSON.parse(fs3.readFileSync(metaPath(name), "utf-8"));
485
744
  if (meta.commit) {
@@ -502,6 +761,10 @@ async function updatePackage(state, name) {
502
761
  async function runWithOutput(cmd, args, cwd) {
503
762
  return new Promise((resolve2, reject) => {
504
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);
505
768
  let out = "";
506
769
  child.stdout.on("data", (d) => {
507
770
  out += d.toString();
@@ -509,10 +772,27 @@ async function runWithOutput(cmd, args, cwd) {
509
772
  child.stderr.on("data", (d) => {
510
773
  out += d.toString();
511
774
  });
512
- child.on("close", (code) => code === 0 ? resolve2(out.trim()) : reject(new Error(`exit ${code}`)));
513
- 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
+ });
514
783
  });
515
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
+ }
516
796
  async function checkUpdates(state) {
517
797
  const s = state.getState();
518
798
  const results = {};
@@ -612,8 +892,16 @@ var LineBuffer = class {
612
892
  };
613
893
 
614
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
+ })();
615
903
  var HELP_TEXT = [
616
- " 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`,
617
905
  "",
618
906
  " Keymaps:",
619
907
  " i Install package under cursor",
@@ -621,10 +909,18 @@ var HELP_TEXT = [
621
909
  " U Update all installed packages",
622
910
  " C Check for updates from remote",
623
911
  " X Uninstall package under cursor",
912
+ " R Reinstall package under cursor",
624
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",
625
920
  " <Enter> Toggle expand/collapse details",
626
921
  " / Search filter",
627
- " q / <Esc> Close window",
922
+ " q Close window",
923
+ " <Esc> Help\u2192Search\u2192Marks\u2192Busy guard\u2192Close",
628
924
  "",
629
925
  " " + "\u2500".repeat(40),
630
926
  "",
@@ -652,13 +948,20 @@ var TUI = class {
652
948
  i: "i",
653
949
  u: "u",
654
950
  X: "X",
655
- 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"
656
959
  };
657
960
  this.rendering = false;
658
961
  this.state = state;
659
962
  }
660
963
  async open() {
661
- const nvim = import_coc.workspace.nvim;
964
+ const nvim = import_coc2.workspace.nvim;
662
965
  this.ns = await nvim.createNamespace("coc-loader");
663
966
  await nvim.command("highlight default link CocConverterTitle Title");
664
967
  await nvim.command("highlight default link CocConverterPill Visual");
@@ -703,7 +1006,7 @@ var TUI = class {
703
1006
  this.render();
704
1007
  });
705
1008
  this.disposables.push(
706
- import_coc.workspace.registerAutocmd({
1009
+ import_coc2.workspace.registerAutocmd({
707
1010
  event: "WinEnter",
708
1011
  request: true,
709
1012
  callback: async () => {
@@ -727,10 +1030,14 @@ var TUI = class {
727
1030
  `);
728
1031
  }
729
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
+ });
730
1037
  await this.render();
731
1038
  }
732
1039
  async getCursorLine0() {
733
- const nvim = import_coc.workspace.nvim;
1040
+ const nvim = import_coc2.workspace.nvim;
734
1041
  const cursor = await nvim.call("nvim_win_get_cursor", [this.winid]);
735
1042
  return cursor[0] - 1;
736
1043
  }
@@ -760,6 +1067,16 @@ var TUI = class {
760
1067
  this.state.setSearchQuery("");
761
1068
  return;
762
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
+ }
763
1080
  this.close();
764
1081
  return;
765
1082
  }
@@ -769,24 +1086,60 @@ var TUI = class {
769
1086
  }
770
1087
  if (id === "slash") {
771
1088
  try {
772
- const q = await import_coc.workspace.nvim.call("input", ["Search: ", ""]);
1089
+ const q = await import_coc2.workspace.nvim.call("input", ["Search: ", ""]);
773
1090
  if (q) this.state.setSearchQuery(q);
774
1091
  } catch {
775
1092
  }
776
1093
  return;
777
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
+ }
778
1130
  if (id === "U") {
779
1131
  const installed = s.packages.filter((p) => p.status === "installed");
780
1132
  if (installed.length === 0) return;
781
1133
  this.state.setActivePill("U");
782
- 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));
783
1136
  this.state.setActivePill(null);
784
1137
  return;
785
1138
  }
786
1139
  if (id === "Z") {
787
1140
  const installed = s.packages.filter((p) => p.status === "installed");
788
1141
  if (installed.length === 0) return;
789
- 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?`);
790
1143
  if (ok) {
791
1144
  for (const pkg of installed) uninstallPackage(this.state, pkg.info.name);
792
1145
  }
@@ -802,6 +1155,10 @@ var TUI = class {
802
1155
  if (!pkgName) return;
803
1156
  const entry = this.state.getPackage(pkgName);
804
1157
  if (!entry) return;
1158
+ if (id === "x") {
1159
+ this.state.toggleMark(pkgName);
1160
+ return;
1161
+ }
805
1162
  if (id === "i" && entry.status === "not-installed") {
806
1163
  await installPackage(this.state, pkgName);
807
1164
  return;
@@ -814,6 +1171,11 @@ var TUI = class {
814
1171
  uninstallPackage(this.state, pkgName);
815
1172
  return;
816
1173
  }
1174
+ if (id === "R" && entry.status === "installed") {
1175
+ await uninstallPackage(this.state, pkgName);
1176
+ await installPackage(this.state, pkgName);
1177
+ return;
1178
+ }
817
1179
  if (id === "cr") {
818
1180
  if (this.logLineSet.has(line0)) {
819
1181
  this.state.toggleLog(pkgName);
@@ -824,7 +1186,7 @@ var TUI = class {
824
1186
  }
825
1187
  }
826
1188
  async setupKeymaps() {
827
- const buf = import_coc.workspace.nvim.createBuffer(this.bufnr);
1189
+ const buf = import_coc2.workspace.nvim.createBuffer(this.bufnr);
828
1190
  const entries = [
829
1191
  ["q", "q"],
830
1192
  ["<Esc>", "esc"],
@@ -838,7 +1200,13 @@ var TUI = class {
838
1200
  ["H", "H"],
839
1201
  ["u", "u"],
840
1202
  ["X", "X"],
841
- ["x", "X"],
1203
+ ["R", "R"],
1204
+ ["f", "f"],
1205
+ ["s", "s"],
1206
+ ["x", "x"],
1207
+ ["D", "D"],
1208
+ ["gg", "gg"],
1209
+ ["G", "G"],
842
1210
  ["<CR>", "cr"]
843
1211
  ];
844
1212
  for (const [vimKey, id] of entries) {
@@ -857,20 +1225,20 @@ var TUI = class {
857
1225
  this.disposables = [];
858
1226
  if (this.winid) {
859
1227
  try {
860
- 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]);
861
1229
  } catch {
862
1230
  }
863
1231
  this.winid = 0;
864
1232
  }
865
1233
  if (needRestart) {
866
- import_coc.workspace.nvim.command("CocRestart", true);
1234
+ import_coc2.workspace.nvim.command("CocRestart", true);
867
1235
  }
868
1236
  }
869
1237
  async render() {
870
1238
  if (!this.winid || this.rendering) return;
871
1239
  this.rendering = true;
872
1240
  try {
873
- const nvim = import_coc.workspace.nvim;
1241
+ const nvim = import_coc2.workspace.nvim;
874
1242
  const state = this.state.getState();
875
1243
  const filtered = this.state.getFilteredPackages();
876
1244
  const result = state.showHelp ? this.renderHelp() : this.renderPackageList(state, filtered);
@@ -896,7 +1264,7 @@ var TUI = class {
896
1264
  renderHelp() {
897
1265
  const header = [
898
1266
  "",
899
- " coc-loader v0.1",
1267
+ ` coc-loader v${VERSION}`,
900
1268
  " press ? help | / search | q quit",
901
1269
  " " + "\u2500".repeat(50),
902
1270
  ""
@@ -938,7 +1306,13 @@ var TUI = class {
938
1306
  buf.highlight(/\([IU?C]\)/g, "CocConverterKey");
939
1307
  buf.nl();
940
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";
941
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");
942
1316
  if (state.statusMessage) {
943
1317
  buf.append(" \xB7 ");
944
1318
  buf.append(state.statusMessage, "Comment");
@@ -961,6 +1335,10 @@ var TUI = class {
961
1335
  if (filtered.length === 0 && state.searchQuery) {
962
1336
  buf.nl("no matching packages");
963
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");
964
1342
  const result = buf.render(2);
965
1343
  return { lines: result.lines, pkgLineMap, logLines: logSet, highlights: result.highlights };
966
1344
  }
@@ -969,7 +1347,12 @@ var TUI = class {
969
1347
  const iconHl = entry.status === "installed" ? "CocConverterInstalled" : entry.status === "failed" ? "ErrorMsg" : "CocConverterAvailable";
970
1348
  const pkgLine = buf.currentLine();
971
1349
  pkgLineMap.set(pkgLine, entry.info.name);
972
- buf.append(" ");
1350
+ if (entry.marked) {
1351
+ buf.append("\u25B8", "CocConverterKey");
1352
+ buf.append(" ");
1353
+ } else {
1354
+ buf.append(" ");
1355
+ }
973
1356
  buf.append(icon, iconHl);
974
1357
  buf.append(" ");
975
1358
  buf.append(entry.info.displayName);
@@ -989,15 +1372,17 @@ var TUI = class {
989
1372
  }
990
1373
  if (entry.expanded) {
991
1374
  buf.nl();
992
- for (const text of [
1375
+ const extras = [
993
1376
  entry.info.description,
994
1377
  `type ${entry.info.type}`,
995
1378
  entry.commit ? `commit ${entry.commit}` : null,
996
1379
  `source ${sourceStr(entry.info.source)}`,
997
1380
  `languages ${entry.info.languages.join(", ")}`,
998
1381
  `categories ${entry.info.categories.join(", ")}`,
999
- `homepage ${entry.info.url}`
1000
- ].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)) {
1001
1386
  const ln = buf.currentLine();
1002
1387
  buf.nl(` ${text}`);
1003
1388
  pkgLineMap.set(ln, entry.info.name);
@@ -1033,9 +1418,6 @@ var TUI = class {
1033
1418
  }
1034
1419
  buf.nl();
1035
1420
  }
1036
- hl(line, hlGroup, colStart, colEnd) {
1037
- this.hlLines.push({ line, hlGroup, colStart, colEnd });
1038
- }
1039
1421
  isOpen() {
1040
1422
  return this.winid !== 0;
1041
1423
  }
@@ -1051,7 +1433,7 @@ var currentTUI = null;
1051
1433
  async function activate(context) {
1052
1434
  const state = new StateManager(createInitialState());
1053
1435
  context.subscriptions.push(
1054
- import_coc2.commands.registerCommand("loader.open", async () => {
1436
+ import_coc3.commands.registerCommand("loader.open", async () => {
1055
1437
  if (currentTUI && currentTUI.isOpen()) {
1056
1438
  await currentTUI.close();
1057
1439
  }
@@ -1060,67 +1442,67 @@ async function activate(context) {
1060
1442
  })
1061
1443
  );
1062
1444
  context.subscriptions.push(
1063
- import_coc2.commands.registerCommand("loader.install", async (name) => {
1445
+ import_coc3.commands.registerCommand("loader.install", async (name) => {
1064
1446
  if (!name) {
1065
- name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1447
+ name = await import_coc3.workspace.nvim.call("input", ["Plugin name: ", ""]);
1066
1448
  if (!name) return;
1067
1449
  }
1068
1450
  const pkg = state.getPackage(name);
1069
1451
  if (!pkg) {
1070
- import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1452
+ import_coc3.window.showInformationMessage(`Unknown package: ${name}`);
1071
1453
  return;
1072
1454
  }
1073
1455
  if (pkg.status === "installed") {
1074
- import_coc2.window.showInformationMessage(`${name} is already installed`);
1456
+ import_coc3.window.showInformationMessage(`${name} is already installed`);
1075
1457
  return;
1076
1458
  }
1077
1459
  await installPackage(state, name);
1078
1460
  })
1079
1461
  );
1080
1462
  context.subscriptions.push(
1081
- import_coc2.commands.registerCommand("loader.uninstall", async (name) => {
1463
+ import_coc3.commands.registerCommand("loader.uninstall", async (name) => {
1082
1464
  if (!name) {
1083
- name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1465
+ name = await import_coc3.workspace.nvim.call("input", ["Plugin name: ", ""]);
1084
1466
  if (!name) return;
1085
1467
  }
1086
1468
  const pkg = state.getPackage(name);
1087
1469
  if (!pkg) {
1088
- import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1470
+ import_coc3.window.showInformationMessage(`Unknown package: ${name}`);
1089
1471
  return;
1090
1472
  }
1091
1473
  if (pkg.status !== "installed") {
1092
- import_coc2.window.showInformationMessage(`${name} is not installed`);
1474
+ import_coc3.window.showInformationMessage(`${name} is not installed`);
1093
1475
  return;
1094
1476
  }
1095
1477
  uninstallPackage(state, name);
1096
1478
  })
1097
1479
  );
1098
1480
  context.subscriptions.push(
1099
- import_coc2.commands.registerCommand("loader.update", async (name) => {
1481
+ import_coc3.commands.registerCommand("loader.update", async (name) => {
1100
1482
  if (!name) {
1101
- name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1483
+ name = await import_coc3.workspace.nvim.call("input", ["Plugin name: ", ""]);
1102
1484
  if (!name) return;
1103
1485
  }
1104
1486
  const pkg = state.getPackage(name);
1105
1487
  if (!pkg) {
1106
- import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1488
+ import_coc3.window.showInformationMessage(`Unknown package: ${name}`);
1107
1489
  return;
1108
1490
  }
1109
1491
  if (pkg.status !== "installed") {
1110
- import_coc2.window.showInformationMessage(`${name} is not installed`);
1492
+ import_coc3.window.showInformationMessage(`${name} is not installed`);
1111
1493
  return;
1112
1494
  }
1113
1495
  await updatePackage(state, name);
1114
1496
  })
1115
1497
  );
1116
1498
  context.subscriptions.push(
1117
- import_coc2.commands.registerCommand("loader.uninstallAll", async () => {
1499
+ import_coc3.commands.registerCommand("loader.uninstallAll", async () => {
1118
1500
  const installed = state.getState().packages.filter((p) => p.status === "installed");
1119
1501
  if (installed.length === 0) {
1120
- import_coc2.window.showInformationMessage("No packages installed");
1502
+ import_coc3.window.showInformationMessage("No packages installed");
1121
1503
  return;
1122
1504
  }
1123
- 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?`);
1124
1506
  if (ok) {
1125
1507
  for (const pkg of installed) {
1126
1508
  uninstallPackage(state, pkg.info.name);
@@ -1129,25 +1511,42 @@ async function activate(context) {
1129
1511
  })
1130
1512
  );
1131
1513
  context.subscriptions.push(
1132
- 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 () => {
1133
1534
  try {
1134
1535
  const count = await updateRegistry();
1135
- 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.`);
1136
1537
  } catch (e) {
1137
- import_coc2.window.showErrorMessage(`Registry update failed: ${e.message}`);
1538
+ import_coc3.window.showErrorMessage(`Registry update failed: ${e.message}`);
1138
1539
  }
1139
1540
  })
1140
1541
  );
1141
1542
  context.subscriptions.push(
1142
- import_coc2.commands.registerCommand("loader._dispatch", async (key) => {
1543
+ import_coc3.commands.registerCommand("loader._dispatch", async (key) => {
1143
1544
  if (currentTUI) {
1144
1545
  await currentTUI.handleKey(key);
1145
1546
  }
1146
1547
  })
1147
1548
  );
1148
- updateRegistry().then(() => state.refreshPackages()).catch(() => {
1149
- });
1150
- import_coc2.window.showInformationMessage("coc-loader activated! Use :CocCommand loader.open");
1549
+ import_coc3.window.showInformationMessage("coc-loader activated! Use :CocCommand loader.open");
1151
1550
  }
1152
1551
  // Annotate the CommonJS export names for ESM import in node:
1153
1552
  0 && (module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coc-vscode-loader",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Run VS Code extensions seamlessly in coc.nvim",
5
5
  "main": "lib/index.js",
6
6
  "keywords": [