coc-vscode-loader 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +69 -0
  2. package/lib/index.js +1078 -0
  3. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # coc-vscode-loader
2
+
3
+ VS Code extension → coc.nvim plugin loader with TUI package manager.
4
+
5
+ Install/update/uninstall converted VS Code extensions via a floating terminal UI.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ cd ~/.config/coc/extensions
11
+ npm install /path/to/coc-vscode-loader
12
+ # or :CocInstall /path/to/coc-vscode-loader
13
+ ```
14
+
15
+ ## TUI Keymaps
16
+
17
+ | Key | Action |
18
+ |-----|--------|
19
+ | `I` | Install mode (button highlight) |
20
+ | `U` | Update all installed packages |
21
+ | `C` | Check for remote updates (git ls-remote commit compare) |
22
+ | `Z` | Uninstall all installed packages (with confirmation) |
23
+ | `H` | Home (reset all state) |
24
+ | `?` | Help |
25
+ | `i` | Install package under cursor |
26
+ | `u` | Update package under cursor |
27
+ | `X` / `x` | Uninstall package under cursor |
28
+ | `<CR>` | Toggle details (commit / type / source) or install log |
29
+ | `/` | Search filter |
30
+ | `q` / `<Esc>` | Close (auto `:CocRestart` if changes detected) |
31
+
32
+ ## Commands
33
+
34
+ | Command | Action |
35
+ |---------|--------|
36
+ | `:CocCommand loader.open` | Open TUI |
37
+ | `:CocCommand loader.install <name>` | Install a package |
38
+ | `:CocCommand loader.uninstall <name>` | Uninstall a package |
39
+ | `:CocCommand loader.update <name>` | Update a package |
40
+ | `:CocCommand loader.uninstallAll` | Uninstall all (with confirmation) |
41
+ | `:CocCommand loader.updateRegistry` | Fetch latest registry from remote |
42
+
43
+ ## Features
44
+
45
+ - **Real conversion pipeline** — git clone → converter → npm install → esbuild → register to coc
46
+ - **Incremental cache** — source/ keeps git repo, updates via git pull only
47
+ - **Commit tracking** — records commit SHA after install, visible in detail view
48
+ - **Update check** — `C` key compares against remote HEAD, shows `↑` when outdated
49
+ - **Auto restart** — `:CocRestart` triggered automatically on close when changes detected
50
+ - **Registry hot-reload** — `:CocCommand loader.updateRegistry` fetches remote registry
51
+ - **Install logs** — real command output per step, expandable
52
+
53
+ ## Architecture
54
+
55
+ | File | Description |
56
+ |------|-------------|
57
+ | `src/index.ts` | Plugin entry + 7 CocCommands |
58
+ | `src/tui.ts` | TUI window management + rendering + key dispatch |
59
+ | `src/state.ts` | State management (debounced rendering) |
60
+ | `src/registry.ts` | Built-in registry + remote update cache |
61
+ | `src/pipeline.ts` | Real install/update/uninstall flow (git + npx tsx + npm + node + cp) |
62
+ | `src/renderer.ts` | LineBuffer render engine (inspired by lazy.nvim) |
63
+
64
+ ## Build
65
+
66
+ ```bash
67
+ npm install
68
+ npm run build # esbuild → lib/index.js
69
+ ```
package/lib/index.js ADDED
@@ -0,0 +1,1078 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ activate: () => activate
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_coc2 = require("coc.nvim");
37
+
38
+ // src/registry.ts
39
+ var path = __toESM(require("path"));
40
+ var fs = __toESM(require("fs"));
41
+ var os = __toESM(require("os"));
42
+ var REMOTE_REGISTRY_URL = "https://raw.githubusercontent.com/coc-plugin/coc-vscode-registry/main/registry.json";
43
+ 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
+ var cached = null;
77
+ function loadCache() {
78
+ try {
79
+ if (fs.existsSync(CACHE_PATH)) {
80
+ return JSON.parse(fs.readFileSync(CACHE_PATH, "utf-8"));
81
+ }
82
+ } catch {
83
+ }
84
+ return null;
85
+ }
86
+ async function updateRegistry() {
87
+ const res = await fetch(REMOTE_REGISTRY_URL);
88
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
89
+ const data = await res.json();
90
+ if (!Array.isArray(data)) throw new Error("Invalid registry format");
91
+ fs.mkdirSync(path.dirname(CACHE_PATH), { recursive: true });
92
+ fs.writeFileSync(CACHE_PATH, JSON.stringify(data, null, 2));
93
+ cached = data;
94
+ return data.length;
95
+ }
96
+ function getAllPackages() {
97
+ if (cached) return cached;
98
+ cached = loadCache();
99
+ return cached || BUILTIN_REGISTRY;
100
+ }
101
+ function getPackage(name) {
102
+ return getAllPackages().find((p) => p.name === name);
103
+ }
104
+
105
+ // src/state.ts
106
+ var path2 = __toESM(require("path"));
107
+ var fs2 = __toESM(require("fs"));
108
+ var os2 = __toESM(require("os"));
109
+ function isInstalled(name) {
110
+ return fs2.existsSync(path2.join(os2.homedir(), ".config", "coc", "extensions", "node_modules", `coc-${name}`));
111
+ }
112
+ function createInitialState() {
113
+ const packages = getAllPackages().map((info) => {
114
+ const installed = isInstalled(info.name);
115
+ let commit;
116
+ if (installed) {
117
+ try {
118
+ const meta = JSON.parse(
119
+ fs2.readFileSync(path2.join(os2.homedir(), ".config", "coc", "converter-cache", info.name, "meta.json"), "utf-8")
120
+ );
121
+ commit = meta.commit || void 0;
122
+ } catch {
123
+ }
124
+ }
125
+ return {
126
+ info,
127
+ status: installed ? "installed" : "not-installed",
128
+ commit,
129
+ progressLog: [],
130
+ expanded: false,
131
+ logExpanded: false
132
+ };
133
+ });
134
+ return { packages, searchQuery: "", showHelp: false, activePill: null, dirty: false, viewFilter: "all" };
135
+ }
136
+ var StateManager = class {
137
+ constructor(initial) {
138
+ this.listeners = /* @__PURE__ */ new Set();
139
+ this.scheduled = false;
140
+ this.state = initial;
141
+ }
142
+ getState() {
143
+ return this.state;
144
+ }
145
+ subscribe(fn) {
146
+ this.listeners.add(fn);
147
+ return () => this.listeners.delete(fn);
148
+ }
149
+ mutate(fn) {
150
+ fn(this.state);
151
+ this.notify();
152
+ }
153
+ notify() {
154
+ if (this.scheduled) return;
155
+ this.scheduled = true;
156
+ const state = this.state;
157
+ process.nextTick(() => {
158
+ this.scheduled = false;
159
+ for (const fn of this.listeners) {
160
+ fn(state);
161
+ }
162
+ });
163
+ }
164
+ setPackageStatus(name, status, extra) {
165
+ this.mutate((s) => {
166
+ const pkg = s.packages.find((p) => p.info.name === name);
167
+ if (pkg) {
168
+ pkg.status = status;
169
+ 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);
172
+ if (extra?.error !== void 0) pkg.error = extra.error;
173
+ if (status === "installed" || status === "not-installed") {
174
+ pkg.progress = void 0;
175
+ }
176
+ }
177
+ });
178
+ }
179
+ toggleExpand(name) {
180
+ this.mutate((s) => {
181
+ const pkg = s.packages.find((p) => p.info.name === name);
182
+ if (pkg) pkg.expanded = !pkg.expanded;
183
+ });
184
+ }
185
+ toggleLog(name) {
186
+ this.mutate((s) => {
187
+ const pkg = s.packages.find((p) => p.info.name === name);
188
+ if (pkg) pkg.logExpanded = !pkg.logExpanded;
189
+ });
190
+ }
191
+ toggleHelp() {
192
+ this.mutate((s) => {
193
+ s.showHelp = !s.showHelp;
194
+ });
195
+ }
196
+ setViewFilter(filter) {
197
+ this.mutate((s) => {
198
+ s.viewFilter = filter;
199
+ });
200
+ }
201
+ setStatusMessage(msg) {
202
+ this.mutate((s) => {
203
+ s.statusMessage = msg;
204
+ });
205
+ }
206
+ setDirty() {
207
+ this.mutate((s) => {
208
+ s.dirty = true;
209
+ });
210
+ }
211
+ setActivePill(pill) {
212
+ this.mutate((s) => {
213
+ s.activePill = pill;
214
+ });
215
+ }
216
+ setSearchQuery(query) {
217
+ this.mutate((s) => {
218
+ s.searchQuery = query;
219
+ });
220
+ }
221
+ getFilteredPackages() {
222
+ let pkgs = this.state.packages;
223
+ if (this.state.viewFilter === "not-installed") {
224
+ pkgs = pkgs.filter((p) => p.status === "not-installed");
225
+ } else if (this.state.viewFilter === "installed") {
226
+ pkgs = pkgs.filter((p) => p.status === "installed");
227
+ }
228
+ 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
+ );
233
+ }
234
+ getPackage(name) {
235
+ return this.state.packages.find((p) => p.info.name === name);
236
+ }
237
+ };
238
+
239
+ // src/tui.ts
240
+ var import_coc = require("coc.nvim");
241
+
242
+ // src/pipeline.ts
243
+ var import_child_process = require("child_process");
244
+ var path3 = __toESM(require("path"));
245
+ var fs3 = __toESM(require("fs"));
246
+ var os3 = __toESM(require("os"));
247
+ var CACHE_ROOT = path3.join(os3.homedir(), ".config", "coc", "converter-cache");
248
+ function cacheDir(name) {
249
+ return path3.join(CACHE_ROOT, name);
250
+ }
251
+ function sourceDir(name) {
252
+ return path3.join(cacheDir(name), "source");
253
+ }
254
+ function buildDir(name) {
255
+ return path3.join(cacheDir(name), "build");
256
+ }
257
+ function pluginDir(name) {
258
+ return path3.join(os3.homedir(), ".config", "coc", "extensions", "node_modules", `coc-${name}`);
259
+ }
260
+ function converterCliPath() {
261
+ const base = path3.resolve(__dirname, "..");
262
+ const candidates = [
263
+ path3.join(base, "..", "converter", "src", "cli.ts"),
264
+ path3.join(base, "..", "..", "converter", "src", "cli.ts"),
265
+ path3.join(base, "..", "..", "..", "converter", "src", "cli.ts")
266
+ ];
267
+ for (const p of candidates) {
268
+ if (fs3.existsSync(p)) return p;
269
+ }
270
+ throw new Error(
271
+ "converter CLI not found. Please set $COC_CONVERTER_PATH to the converter/ directory, or ensure it is at the same level as coc-converter/"
272
+ );
273
+ }
274
+ async function run(cmd, args, cwd, onLine) {
275
+ return new Promise((resolve2, reject) => {
276
+ const child = (0, import_child_process.spawn)(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"], shell: true });
277
+ const lines = [];
278
+ const handler = (data) => {
279
+ const text = data.toString();
280
+ lines.push(text);
281
+ onLine(text);
282
+ };
283
+ child.stdout.on("data", handler);
284
+ child.stderr.on("data", handler);
285
+ child.on("close", (code) => {
286
+ if (code === 0) resolve2();
287
+ else reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
288
+ });
289
+ child.on("error", reject);
290
+ });
291
+ }
292
+ async function downloadSource(info, name, onProgress) {
293
+ const srcDir = sourceDir(name);
294
+ const cache = cacheDir(name);
295
+ const repoUrl = `https://github.com/${info.source.repo}.git`;
296
+ if (fs3.existsSync(srcDir)) {
297
+ onProgress(1, 5, "Updating source...", `git -C ${srcDir} pull`);
298
+ await run("git", ["-C", srcDir, "pull"], cache, () => {
299
+ });
300
+ } else {
301
+ onProgress(1, 5, "Cloning repository...", `git clone --depth=1 ${repoUrl}`);
302
+ fs3.mkdirSync(cache, { recursive: true });
303
+ await run("git", ["clone", "--depth=1", repoUrl, srcDir], cache, () => {
304
+ });
305
+ }
306
+ return info.source.subdir ? path3.join(srcDir, info.source.subdir) : srcDir;
307
+ }
308
+ async function convertSource(inputDir, name, onProgress) {
309
+ const build = buildDir(name);
310
+ if (fs3.existsSync(build)) fs3.rmSync(build, { recursive: true });
311
+ const cli = converterCliPath();
312
+ onProgress(2, 5, "Converting...", `converter convert ${inputDir} -o ${build}`);
313
+ await run("npx", ["tsx", cli, "convert", inputDir, "-o", build], cacheDir(name), () => {
314
+ });
315
+ }
316
+ async function buildPackage(name, onProgress) {
317
+ const build = buildDir(name);
318
+ onProgress(3, 5, "Installing dependencies...", "npm install --legacy-peer-deps");
319
+ await run("npm", ["install", "--legacy-peer-deps"], build, () => {
320
+ });
321
+ onProgress(4, 5, "Building...", "node esbuild.mjs");
322
+ await run("node", ["esbuild.mjs"], build, () => {
323
+ });
324
+ }
325
+ function extensionsPkgPath() {
326
+ return path3.join(os3.homedir(), ".config", "coc", "extensions", "package.json");
327
+ }
328
+ async function installToCoc(name, onProgress) {
329
+ const src = buildDir(name);
330
+ const dest = pluginDir(name);
331
+ onProgress(5, 5, "Installing to coc...", `copy to ${dest} + register in extensions/package.json`);
332
+ if (fs3.existsSync(dest)) fs3.rmSync(dest, { recursive: true });
333
+ fs3.mkdirSync(path3.dirname(dest), { recursive: true });
334
+ fs3.cpSync(src, dest, { recursive: true });
335
+ const pkgPath = extensionsPkgPath();
336
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
337
+ const depName = `coc-${name}`;
338
+ if (!pkg.dependencies[depName]) {
339
+ pkg.dependencies[depName] = `file:${dest}`;
340
+ pkg.lastUpdate = Date.now();
341
+ fs3.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
342
+ }
343
+ }
344
+ function metaPath(name) {
345
+ return path3.join(cacheDir(name), "meta.json");
346
+ }
347
+ function saveMeta(name) {
348
+ const srcDir = sourceDir(name);
349
+ try {
350
+ const commit = (0, import_child_process.execSync)("git", ["-C", srcDir, "rev-parse", "--short", "HEAD"], { encoding: "utf-8" }).toString().trim();
351
+ fs3.writeFileSync(metaPath(name), JSON.stringify({ commit, updatedAt: Date.now() }, null, 2));
352
+ } catch {
353
+ }
354
+ }
355
+ async function installPackage(state, name) {
356
+ const info = getPackage(name);
357
+ if (!info) {
358
+ state.setPackageStatus(name, "failed", { error: `Unknown package: ${name}` });
359
+ return;
360
+ }
361
+ const prog = (step, total, msg, cmd) => {
362
+ state.setPackageStatus(name, "installing", {
363
+ progress: `[${step}/${total}] ${msg}`,
364
+ logEntry: `[${step}/${total}] ${msg}
365
+ $ ${cmd}`,
366
+ appendLog: true
367
+ });
368
+ };
369
+ state.setPackageStatus(name, "installing", { progress: "Starting..." });
370
+ try {
371
+ const input = await downloadSource(info, name, prog);
372
+ await convertSource(input, name, prog);
373
+ await buildPackage(name, prog);
374
+ await installToCoc(name, prog);
375
+ saveMeta(name);
376
+ state.setDirty();
377
+ state.setPackageStatus(name, "installed");
378
+ } catch (e) {
379
+ state.setPackageStatus(name, "failed", { error: e.message });
380
+ }
381
+ }
382
+ async function uninstallPackage(state, name) {
383
+ state.setPackageStatus(name, "uninstalling", { progress: "[1/3] Removing from coc..." });
384
+ try {
385
+ const dest = pluginDir(name);
386
+ if (fs3.existsSync(dest)) {
387
+ fs3.rmSync(dest, { recursive: true });
388
+ }
389
+ state.setPackageStatus(name, "uninstalling", { progress: "[2/3] Removing from package.json..." });
390
+ const pkgPath = extensionsPkgPath();
391
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
392
+ const depName = `coc-${name}`;
393
+ if (pkg.dependencies[depName]) {
394
+ delete pkg.dependencies[depName];
395
+ pkg.lastUpdate = Date.now();
396
+ fs3.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
397
+ }
398
+ state.setPackageStatus(name, "uninstalling", { progress: "[3/3] Removing cache..." });
399
+ const cache = cacheDir(name);
400
+ if (fs3.existsSync(cache)) {
401
+ fs3.rmSync(cache, { recursive: true });
402
+ }
403
+ state.setPackageStatus(name, "not-installed");
404
+ state.setDirty();
405
+ } catch (e) {
406
+ state.setPackageStatus(name, "failed", { error: e.message });
407
+ }
408
+ }
409
+ async function updatePackage(state, name) {
410
+ const info = getPackage(name);
411
+ if (!info) {
412
+ state.setPackageStatus(name, "failed", { error: `Unknown package: ${name}` });
413
+ return;
414
+ }
415
+ const prog = (step, total, msg, cmd) => {
416
+ state.setPackageStatus(name, "updating", {
417
+ progress: `[${step}/${total}] ${msg}`,
418
+ logEntry: `[${step}/${total}] ${msg}
419
+ $ ${cmd}`,
420
+ appendLog: true
421
+ });
422
+ };
423
+ state.setPackageStatus(name, "updating", { progress: "Starting..." });
424
+ try {
425
+ const input = await downloadSource(info, name, prog);
426
+ await convertSource(input, name, prog);
427
+ await buildPackage(name, prog);
428
+ await installToCoc(name, prog);
429
+ saveMeta(name);
430
+ state.setDirty();
431
+ state.setPackageStatus(name, "installed");
432
+ } catch (e) {
433
+ state.setPackageStatus(name, "failed", { error: e.message });
434
+ }
435
+ }
436
+ async function runWithOutput(cmd, args, cwd) {
437
+ return new Promise((resolve2, reject) => {
438
+ const child = (0, import_child_process.spawn)(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"], shell: true });
439
+ let out = "";
440
+ child.stdout.on("data", (d) => {
441
+ out += d.toString();
442
+ });
443
+ child.stderr.on("data", (d) => {
444
+ out += d.toString();
445
+ });
446
+ child.on("close", (code) => code === 0 ? resolve2(out.trim()) : reject(new Error(`exit ${code}`)));
447
+ child.on("error", reject);
448
+ });
449
+ }
450
+ async function checkUpdates(state) {
451
+ const s = state.getState();
452
+ const results = {};
453
+ state.setStatusMessage("Checking for updates...");
454
+ for (const pkg of s.packages) {
455
+ if (pkg.status !== "installed" || !pkg.commit) continue;
456
+ state.setStatusMessage(`Checking ${pkg.info.displayName}...`);
457
+ try {
458
+ const out = await runWithOutput("git", ["ls-remote", `https://github.com/${pkg.info.source.repo}.git`, "HEAD"], os3.homedir());
459
+ const remote = out.split(" ")[0];
460
+ if (remote) results[pkg.info.name] = remote.substring(0, 7) !== pkg.commit;
461
+ } catch {
462
+ }
463
+ }
464
+ const updateCount = Object.values(results).filter(Boolean).length;
465
+ state.mutate((s2) => {
466
+ for (const p of s2.packages) {
467
+ if (results[p.info.name] !== void 0) p.hasUpdate = results[p.info.name];
468
+ }
469
+ s2.statusMessage = void 0;
470
+ });
471
+ if (updateCount > 0) {
472
+ state.setStatusMessage(`Found ${updateCount} package(s) with updates. Use 'u' to update.`);
473
+ setTimeout(() => state.setStatusMessage(), 5e3);
474
+ } else {
475
+ state.setStatusMessage("All packages up to date.");
476
+ setTimeout(() => state.setStatusMessage(), 3e3);
477
+ }
478
+ }
479
+
480
+ // src/renderer.ts
481
+ function byteLen(s) {
482
+ return Buffer.from(s).length;
483
+ }
484
+ var LineBuffer = class {
485
+ constructor() {
486
+ this.lines = [[]];
487
+ this.li = 0;
488
+ this.patternHls = [];
489
+ }
490
+ append(text, hl) {
491
+ this.lines[this.li].push({ text, hl });
492
+ return this;
493
+ }
494
+ nl(text, hl) {
495
+ if (text !== void 0) this.append(text, hl);
496
+ this.li++;
497
+ this.lines[this.li] = [];
498
+ return this;
499
+ }
500
+ lineCount() {
501
+ return this.lines.length;
502
+ }
503
+ currentLine() {
504
+ return this.li;
505
+ }
506
+ highlight(pattern, hlGroup) {
507
+ const segs = this.lines[this.li];
508
+ let full = "";
509
+ for (const seg of segs) full += seg.text;
510
+ pattern.lastIndex = 0;
511
+ let m;
512
+ while ((m = pattern.exec(full)) !== null) {
513
+ const colStart = byteLen(full.slice(0, m.index));
514
+ const colEnd = colStart + byteLen(m[0]);
515
+ this.patternHls.push({ line: this.li, hlGroup, colStart, colEnd });
516
+ }
517
+ return this;
518
+ }
519
+ render(padding = 0) {
520
+ const lines = [];
521
+ const highlights = [];
522
+ for (let li = 0; li < this.lines.length; li++) {
523
+ const segs = this.lines[li];
524
+ let full = "";
525
+ for (const seg of segs) {
526
+ const colStart = byteLen(full) + padding;
527
+ full += seg.text;
528
+ const colEnd = byteLen(full) + padding;
529
+ if (seg.hl) {
530
+ highlights.push({ line: li, hlGroup: seg.hl, colStart, colEnd });
531
+ }
532
+ }
533
+ if (padding > 0 && full.length > 0) {
534
+ full = " ".repeat(padding) + full;
535
+ }
536
+ lines.push(full);
537
+ }
538
+ for (const h of this.patternHls) {
539
+ h.colStart += padding;
540
+ h.colEnd += padding;
541
+ highlights.push(h);
542
+ }
543
+ this.patternHls = [];
544
+ return { lines, highlights };
545
+ }
546
+ };
547
+
548
+ // src/tui.ts
549
+ var HELP_TEXT = [
550
+ " coc-loader \u2014 VS Code extension \u2192 coc.nvim plugin converter",
551
+ "",
552
+ " Keymaps:",
553
+ " i Install package under cursor",
554
+ " u Update package under cursor",
555
+ " U Update all installed packages",
556
+ " C Check for updates from remote",
557
+ " X Uninstall package under cursor",
558
+ " Z Uninstall all installed packages (with confirm)",
559
+ " <Enter> Toggle expand/collapse details",
560
+ " / Search filter",
561
+ " q / <Esc> Close window",
562
+ "",
563
+ " " + "\u2500".repeat(40),
564
+ "",
565
+ " Plugin types:",
566
+ " ts-bridge Depends on TypeScript LSP bridge (e.g. Volar)",
567
+ " pure-lsp Standard LSP protocol (e.g. Prisma, ESLint)",
568
+ " direct-api Direct coc.nvim API calls (e.g. HTML CSS Support)"
569
+ ];
570
+ var TUI = class {
571
+ constructor(state) {
572
+ this.bufnr = 0;
573
+ this.winid = 0;
574
+ this.ns = 0;
575
+ this.disposables = [];
576
+ this.unsubscribe = null;
577
+ this.pkgLineMap = /* @__PURE__ */ new Map();
578
+ this.logLineSet = /* @__PURE__ */ new Set();
579
+ this.keyMap = {
580
+ q: "q",
581
+ esc: "<Esc>",
582
+ question: "?",
583
+ slash: "/",
584
+ U: "U",
585
+ Z: "Z",
586
+ i: "i",
587
+ u: "u",
588
+ X: "X",
589
+ cr: "<CR>"
590
+ };
591
+ this.rendering = false;
592
+ this.state = state;
593
+ }
594
+ async open() {
595
+ const nvim = import_coc.workspace.nvim;
596
+ this.ns = await nvim.createNamespace("coc-loader");
597
+ await nvim.command("highlight default link CocConverterTitle Title");
598
+ await nvim.command("highlight default link CocConverterPill Visual");
599
+ await nvim.command("highlight default link CocConverterPillActive IncSearch");
600
+ await nvim.command("highlight CocConverterKey guifg=#569CD6 guibg=NONE ctermbg=NONE");
601
+ await nvim.command("highlight default link CocConverterInstalled String");
602
+ await nvim.command("highlight default link CocConverterAvailable Comment");
603
+ await nvim.command("highlight default link CocConverterType Type");
604
+ await nvim.command("highlight default link CocConverterSection Title");
605
+ await nvim.command("highlight default link CocConverterTotal Identifier");
606
+ const buf = await nvim.createNewBuffer(false, true);
607
+ this.bufnr = buf.id;
608
+ const editorLines = await nvim.call("nvim_get_option", ["lines"]);
609
+ const editorCols = await nvim.call("nvim_get_option", ["columns"]);
610
+ const height = Math.min(Math.floor(editorLines * 0.85), 40);
611
+ const width = Math.min(Math.floor(editorCols * 0.85), 120);
612
+ const row = Math.max(Math.floor((editorLines - height) / 2), 0);
613
+ const col = Math.max(Math.floor((editorCols - width) / 2), 0);
614
+ const win = await nvim.openFloatWindow(buf, true, {
615
+ relative: "editor",
616
+ width,
617
+ height,
618
+ row,
619
+ col,
620
+ border: "none",
621
+ style: "minimal"
622
+ });
623
+ this.winid = win.id;
624
+ await nvim.call("nvim_buf_set_option", [this.bufnr, "modifiable", false]);
625
+ await nvim.call("nvim_buf_set_option", [this.bufnr, "bufhidden", "wipe"]);
626
+ await nvim.call("nvim_buf_set_option", [this.bufnr, "buftype", "nofile"]);
627
+ await nvim.call("nvim_buf_set_option", [this.bufnr, "swapfile", false]);
628
+ await nvim.call("nvim_buf_set_option", [this.bufnr, "undolevels", -1]);
629
+ await nvim.call("nvim_win_set_option", [this.winid, "cursorline", true]);
630
+ await nvim.call("nvim_win_set_option", [this.winid, "number", false]);
631
+ await nvim.call("nvim_win_set_option", [this.winid, "relativenumber", false]);
632
+ await nvim.call("nvim_win_set_option", [this.winid, "wrap", false]);
633
+ await nvim.call("nvim_win_set_option", [this.winid, "signcolumn", "no"]);
634
+ await nvim.call("nvim_win_set_option", [this.winid, "spell", false]);
635
+ await nvim.call("nvim_win_set_option", [this.winid, "foldenable", false]);
636
+ this.unsubscribe = this.state.subscribe(() => {
637
+ this.render();
638
+ });
639
+ this.disposables.push(
640
+ import_coc.workspace.registerAutocmd({
641
+ event: "WinEnter",
642
+ request: true,
643
+ callback: async () => {
644
+ if (!this.winid) return;
645
+ const curWin = await nvim.call("win_getid");
646
+ if (curWin === this.winid) return;
647
+ const curBuf = await nvim.call("winbufnr", [curWin]);
648
+ const bt = await nvim.call("getbufvar", [curBuf, "&buftype"]);
649
+ if (bt !== "nofile" && bt !== "prompt") {
650
+ this.close();
651
+ }
652
+ }
653
+ })
654
+ );
655
+ const exists = await nvim.call("exists", ["*CocConverterDispatch"]);
656
+ if (exists === 0) {
657
+ await nvim.command(`
658
+ function! CocConverterDispatch(key) abort
659
+ execute 'CocCommand loader._dispatch ' . a:key
660
+ endfunction
661
+ `);
662
+ }
663
+ await this.setupKeymaps();
664
+ await this.render();
665
+ }
666
+ async getCursorLine0() {
667
+ const nvim = import_coc.workspace.nvim;
668
+ const cursor = await nvim.call("nvim_win_get_cursor", [this.winid]);
669
+ return cursor[0] - 1;
670
+ }
671
+ async handleKey(id) {
672
+ const line0 = await this.getCursorLine0();
673
+ const s = this.state.getState();
674
+ if (id === "q") {
675
+ this.close();
676
+ return;
677
+ }
678
+ if (id === "I") {
679
+ this.state.setActivePill("I");
680
+ return;
681
+ }
682
+ if (id === "H") {
683
+ this.state.setSearchQuery("");
684
+ this.state.setActivePill(null);
685
+ if (s.showHelp) this.state.toggleHelp();
686
+ return;
687
+ }
688
+ if (id === "esc") {
689
+ if (s.showHelp) {
690
+ this.state.toggleHelp();
691
+ return;
692
+ }
693
+ if (s.searchQuery) {
694
+ this.state.setSearchQuery("");
695
+ return;
696
+ }
697
+ this.close();
698
+ return;
699
+ }
700
+ if (id === "question") {
701
+ this.state.toggleHelp();
702
+ return;
703
+ }
704
+ if (id === "slash") {
705
+ try {
706
+ const q = await import_coc.workspace.nvim.call("input", ["Search: ", ""]);
707
+ if (q) this.state.setSearchQuery(q);
708
+ } catch {
709
+ }
710
+ return;
711
+ }
712
+ if (id === "U") {
713
+ const installed = s.packages.filter((p) => p.status === "installed");
714
+ if (installed.length === 0) return;
715
+ this.state.setActivePill("U");
716
+ await Promise.all(installed.map((p) => updatePackage(this.state, p.info.name)));
717
+ this.state.setActivePill(null);
718
+ return;
719
+ }
720
+ if (id === "Z") {
721
+ const installed = s.packages.filter((p) => p.status === "installed");
722
+ if (installed.length === 0) return;
723
+ const ok = await import_coc.window.showPrompt(`Uninstall all ${installed.length} packages?`);
724
+ if (ok) {
725
+ for (const pkg of installed) uninstallPackage(this.state, pkg.info.name);
726
+ }
727
+ return;
728
+ }
729
+ if (id === "C") {
730
+ this.state.setActivePill("C");
731
+ await checkUpdates(this.state);
732
+ this.state.setActivePill(null);
733
+ return;
734
+ }
735
+ const pkgName = this.pkgLineMap.get(line0);
736
+ if (!pkgName) return;
737
+ const entry = this.state.getPackage(pkgName);
738
+ if (!entry) return;
739
+ if (id === "i" && entry.status === "not-installed") {
740
+ await installPackage(this.state, pkgName);
741
+ return;
742
+ }
743
+ if (id === "u" && entry.status === "installed") {
744
+ await updatePackage(this.state, pkgName);
745
+ return;
746
+ }
747
+ if (id === "X" && entry.status === "installed") {
748
+ uninstallPackage(this.state, pkgName);
749
+ return;
750
+ }
751
+ if (id === "cr") {
752
+ if (this.logLineSet.has(line0)) {
753
+ this.state.toggleLog(pkgName);
754
+ } else {
755
+ this.state.toggleExpand(pkgName);
756
+ }
757
+ return;
758
+ }
759
+ }
760
+ async setupKeymaps() {
761
+ const buf = import_coc.workspace.nvim.createBuffer(this.bufnr);
762
+ const entries = [
763
+ ["q", "q"],
764
+ ["<Esc>", "esc"],
765
+ ["?", "question"],
766
+ ["/", "slash"],
767
+ ["U", "U"],
768
+ ["Z", "Z"],
769
+ ["C", "C"],
770
+ ["i", "i"],
771
+ ["I", "I"],
772
+ ["H", "H"],
773
+ ["u", "u"],
774
+ ["X", "X"],
775
+ ["x", "X"],
776
+ ["<CR>", "cr"]
777
+ ];
778
+ for (const [vimKey, id] of entries) {
779
+ buf.setKeymap("n", vimKey, `<Cmd>call CocConverterDispatch("${id}")<CR>`, { silent: true, nowait: true });
780
+ }
781
+ }
782
+ async close() {
783
+ const needRestart = this.state.getState().dirty;
784
+ if (this.unsubscribe) {
785
+ this.unsubscribe();
786
+ this.unsubscribe = null;
787
+ }
788
+ for (const d of this.disposables) {
789
+ d.dispose();
790
+ }
791
+ this.disposables = [];
792
+ if (this.winid) {
793
+ try {
794
+ await import_coc.workspace.nvim.call("nvim_win_close", [this.winid, true]);
795
+ } catch {
796
+ }
797
+ this.winid = 0;
798
+ }
799
+ if (needRestart) {
800
+ import_coc.workspace.nvim.command("CocRestart", true);
801
+ }
802
+ }
803
+ async render() {
804
+ if (!this.winid || this.rendering) return;
805
+ this.rendering = true;
806
+ try {
807
+ const nvim = import_coc.workspace.nvim;
808
+ const state = this.state.getState();
809
+ const filtered = this.state.getFilteredPackages();
810
+ const result = state.showHelp ? this.renderHelp() : this.renderPackageList(state, filtered);
811
+ nvim.pauseNotification();
812
+ nvim.call("nvim_buf_set_option", [this.bufnr, "modifiable", true], true);
813
+ nvim.call("nvim_buf_clear_namespace", [this.bufnr, this.ns, 0, -1], true);
814
+ nvim.call("nvim_buf_set_lines", [this.bufnr, 0, -1, false, result.lines], true);
815
+ nvim.call("nvim_buf_set_option", [this.bufnr, "modifiable", false], true);
816
+ for (const h of result.highlights) {
817
+ nvim.call("nvim_buf_set_extmark", [this.bufnr, this.ns, h.line, h.colStart, {
818
+ end_col: h.colEnd,
819
+ hl_group: h.hlGroup,
820
+ hl_mode: "combine"
821
+ }], true);
822
+ }
823
+ await nvim.resumeNotification();
824
+ this.pkgLineMap = result.pkgLineMap;
825
+ this.logLineSet = result.logLines;
826
+ } finally {
827
+ this.rendering = false;
828
+ }
829
+ }
830
+ renderHelp() {
831
+ const header = [
832
+ "",
833
+ " coc-loader v0.1",
834
+ " press ? help | / search | q quit",
835
+ " " + "\u2500".repeat(50),
836
+ ""
837
+ ];
838
+ return { lines: [...header, ...HELP_TEXT, "", " q to return"], pkgLineMap: /* @__PURE__ */ new Map(), logLines: /* @__PURE__ */ new Set(), highlights: [] };
839
+ }
840
+ statusLabel(status) {
841
+ switch (status) {
842
+ case "installed":
843
+ return "[installed]";
844
+ case "not-installed":
845
+ return "[not installed]";
846
+ case "installing":
847
+ return "[installing]";
848
+ case "updating":
849
+ return "[updating]";
850
+ case "uninstalling":
851
+ return "[uninstalling]";
852
+ case "failed":
853
+ return "[failed]";
854
+ default:
855
+ return "";
856
+ }
857
+ }
858
+ renderPackageList(state, filtered) {
859
+ const pkgLineMap = /* @__PURE__ */ new Map();
860
+ const logSet = /* @__PURE__ */ new Set();
861
+ this.pkgLineMap = pkgLineMap;
862
+ this.logLineSet = logSet;
863
+ const buf = new LineBuffer();
864
+ buf.nl();
865
+ const needHome = !!(state.showHelp || state.searchQuery);
866
+ buf.append("coc-loader(H)", needHome ? "CocConverterPillActive" : "CocConverterTitle");
867
+ for (const [name, key] of [["Install", "I"], ["Update", "U"], ["Check", "C"], ["Help", "?"]]) {
868
+ buf.append(" ");
869
+ const isActive = key === "?" && state.showHelp || key === "I" && state.activePill === "I" || key === "U" && state.activePill === "U" || key === "C" && state.activePill === "C";
870
+ buf.append(`${name}(${key})`, isActive ? "CocConverterPillActive" : "CocConverterPill");
871
+ }
872
+ buf.highlight(/\([IU?C]\)/g, "CocConverterKey");
873
+ buf.nl();
874
+ buf.nl();
875
+ buf.append(`Total: ${filtered.length} packages`, "CocConverterTotal");
876
+ if (state.statusMessage) {
877
+ buf.append(" \xB7 ");
878
+ buf.append(state.statusMessage, "Comment");
879
+ }
880
+ buf.nl();
881
+ buf.nl();
882
+ const installed = filtered.filter((e) => e.status !== "not-installed");
883
+ const available = filtered.filter((e) => e.status === "not-installed");
884
+ const section = (title, entries) => {
885
+ if (entries.length === 0) return;
886
+ buf.nl(`${title}`);
887
+ for (const e of entries) {
888
+ this.renderEntry(buf, pkgLineMap, logSet, e);
889
+ }
890
+ buf.nl();
891
+ buf.nl();
892
+ };
893
+ section(`Installed (${installed.length})`, installed);
894
+ section(`Available (${available.length})`, available);
895
+ if (filtered.length === 0 && state.searchQuery) {
896
+ buf.nl("no matching packages");
897
+ }
898
+ const result = buf.render(2);
899
+ return { lines: result.lines, pkgLineMap, logLines: logSet, highlights: result.highlights };
900
+ }
901
+ renderEntry(buf, pkgLineMap, logSet, entry) {
902
+ const icon = entry.status === "installed" ? "\u25CF" : entry.status === "failed" ? "\u2717" : "\u25CB";
903
+ const iconHl = entry.status === "installed" ? "CocConverterInstalled" : entry.status === "failed" ? "ErrorMsg" : "CocConverterAvailable";
904
+ const pkgLine = buf.currentLine();
905
+ pkgLineMap.set(pkgLine, entry.info.name);
906
+ buf.append(" ");
907
+ buf.append(icon, iconHl);
908
+ buf.append(" ");
909
+ buf.append(entry.info.displayName);
910
+ buf.append(" ");
911
+ buf.append(entry.info.type, "CocConverterType");
912
+ if (entry.hasUpdate) {
913
+ buf.append(" \u2191", "CocConverterKey");
914
+ }
915
+ if (entry.expanded) {
916
+ buf.nl();
917
+ for (const text of [
918
+ entry.info.description,
919
+ `type ${entry.info.type}`,
920
+ entry.commit ? `commit ${entry.commit}` : null,
921
+ `source ${sourceStr(entry.info.source)}`,
922
+ `languages ${entry.info.languages.join(", ")}`,
923
+ `categories ${entry.info.categories.join(", ")}`,
924
+ `homepage ${entry.info.url}`
925
+ ].filter(Boolean)) {
926
+ const ln = buf.currentLine();
927
+ buf.nl(` ${text}`);
928
+ pkgLineMap.set(ln, entry.info.name);
929
+ }
930
+ }
931
+ if (entry.progress) {
932
+ buf.nl();
933
+ if (entry.logExpanded) {
934
+ const ln = buf.currentLine();
935
+ buf.nl(` \u25BC Install log:`);
936
+ logSet.add(ln);
937
+ pkgLineMap.set(ln, entry.info.name);
938
+ for (const log of entry.progressLog) {
939
+ for (const l of log.split("\n")) {
940
+ const ln2 = buf.currentLine();
941
+ buf.nl(` ${l}`);
942
+ logSet.add(ln2);
943
+ pkgLineMap.set(ln2, entry.info.name);
944
+ }
945
+ }
946
+ } else {
947
+ const ln = buf.currentLine();
948
+ buf.nl(` \u25B6 ${entry.progress}`);
949
+ logSet.add(ln);
950
+ pkgLineMap.set(ln, entry.info.name);
951
+ }
952
+ }
953
+ if (entry.error) {
954
+ buf.nl();
955
+ const ln = buf.currentLine();
956
+ buf.nl(` \u2717 ${entry.error}`);
957
+ pkgLineMap.set(ln, entry.info.name);
958
+ }
959
+ buf.nl();
960
+ }
961
+ hl(line, hlGroup, colStart, colEnd) {
962
+ this.hlLines.push({ line, hlGroup, colStart, colEnd });
963
+ }
964
+ isOpen() {
965
+ return this.winid !== 0;
966
+ }
967
+ };
968
+ function sourceStr(source) {
969
+ if (source.type === "github") return `github:${source.repo}${source.subdir ? "/" + source.subdir : ""}`;
970
+ if (source.type === "npm") return `npm:${source.package}`;
971
+ return source.type;
972
+ }
973
+
974
+ // src/index.ts
975
+ var currentTUI = null;
976
+ async function activate(context) {
977
+ const state = new StateManager(createInitialState());
978
+ context.subscriptions.push(
979
+ import_coc2.commands.registerCommand("loader.open", async () => {
980
+ if (currentTUI && currentTUI.isOpen()) {
981
+ await currentTUI.close();
982
+ }
983
+ currentTUI = new TUI(state);
984
+ await currentTUI.open();
985
+ })
986
+ );
987
+ context.subscriptions.push(
988
+ import_coc2.commands.registerCommand("loader.install", async (name) => {
989
+ if (!name) {
990
+ name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
991
+ if (!name) return;
992
+ }
993
+ const pkg = state.getPackage(name);
994
+ if (!pkg) {
995
+ import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
996
+ return;
997
+ }
998
+ if (pkg.status === "installed") {
999
+ import_coc2.window.showInformationMessage(`${name} is already installed`);
1000
+ return;
1001
+ }
1002
+ await installPackage(state, name);
1003
+ })
1004
+ );
1005
+ context.subscriptions.push(
1006
+ import_coc2.commands.registerCommand("loader.uninstall", async (name) => {
1007
+ if (!name) {
1008
+ name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1009
+ if (!name) return;
1010
+ }
1011
+ const pkg = state.getPackage(name);
1012
+ if (!pkg) {
1013
+ import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1014
+ return;
1015
+ }
1016
+ if (pkg.status !== "installed") {
1017
+ import_coc2.window.showInformationMessage(`${name} is not installed`);
1018
+ return;
1019
+ }
1020
+ uninstallPackage(state, name);
1021
+ })
1022
+ );
1023
+ context.subscriptions.push(
1024
+ import_coc2.commands.registerCommand("loader.update", async (name) => {
1025
+ if (!name) {
1026
+ name = await import_coc2.workspace.nvim.call("input", ["Plugin name: ", ""]);
1027
+ if (!name) return;
1028
+ }
1029
+ const pkg = state.getPackage(name);
1030
+ if (!pkg) {
1031
+ import_coc2.window.showInformationMessage(`Unknown package: ${name}`);
1032
+ return;
1033
+ }
1034
+ if (pkg.status !== "installed") {
1035
+ import_coc2.window.showInformationMessage(`${name} is not installed`);
1036
+ return;
1037
+ }
1038
+ await updatePackage(state, name);
1039
+ })
1040
+ );
1041
+ context.subscriptions.push(
1042
+ import_coc2.commands.registerCommand("loader.uninstallAll", async () => {
1043
+ const installed = state.getState().packages.filter((p) => p.status === "installed");
1044
+ if (installed.length === 0) {
1045
+ import_coc2.window.showInformationMessage("No packages installed");
1046
+ return;
1047
+ }
1048
+ const ok = await import_coc2.window.showPrompt(`Uninstall all ${installed.length} packages?`);
1049
+ if (ok) {
1050
+ for (const pkg of installed) {
1051
+ uninstallPackage(state, pkg.info.name);
1052
+ }
1053
+ }
1054
+ })
1055
+ );
1056
+ context.subscriptions.push(
1057
+ import_coc2.commands.registerCommand("loader.updateRegistry", async () => {
1058
+ try {
1059
+ const count = await updateRegistry();
1060
+ import_coc2.window.showInformationMessage(`Registry updated: ${count} packages available. Restart coc to apply.`);
1061
+ } catch (e) {
1062
+ import_coc2.window.showErrorMessage(`Registry update failed: ${e.message}`);
1063
+ }
1064
+ })
1065
+ );
1066
+ context.subscriptions.push(
1067
+ import_coc2.commands.registerCommand("loader._dispatch", async (key) => {
1068
+ if (currentTUI) {
1069
+ await currentTUI.handleKey(key);
1070
+ }
1071
+ })
1072
+ );
1073
+ import_coc2.window.showInformationMessage("coc-loader activated! Use :CocCommand loader.open");
1074
+ }
1075
+ // Annotate the CommonJS export names for ESM import in node:
1076
+ 0 && (module.exports = {
1077
+ activate
1078
+ });
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "coc-vscode-loader",
3
+ "version": "1.0.0",
4
+ "description": "Run VS Code extensions seamlessly in coc.nvim",
5
+ "main": "lib/index.js",
6
+ "keywords": [
7
+ "coc.nvim",
8
+ "vscode",
9
+ "neovim",
10
+ "lsp",
11
+ "plugin-loader",
12
+ "vim"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/coc-plugin/coc-vscode-loader.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/coc-plugin/coc-vscode-loader/issues"
20
+ },
21
+ "homepage": "https://github.com/coc-plugin/coc-vscode-loader",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "coc": ">= 0.0.80"
25
+ },
26
+ "scripts": {
27
+ "build": "node esbuild.mjs",
28
+ "prepare": "node esbuild.mjs"
29
+ },
30
+ "devDependencies": {
31
+ "coc.nvim": "^0.0.83-next.18",
32
+ "esbuild": "^0.25.0",
33
+ "typescript": "^5.3.3"
34
+ },
35
+ "activationEvents": [
36
+ "onCommand:loader.open",
37
+ "onCommand:loader._dispatch"
38
+ ],
39
+ "contributes": {
40
+ "commands": [
41
+ { "command": "loader.open", "title": "Open VS Code extension loader" },
42
+ { "command": "loader.install", "title": "Install a VS Code extension" },
43
+ { "command": "loader.uninstall", "title": "Uninstall a package" },
44
+ { "command": "loader.update", "title": "Update a package" },
45
+ { "command": "loader.uninstallAll", "title": "Uninstall all packages" },
46
+ { "command": "loader.updateRegistry", "title": "Update package registry" },
47
+ { "command": "loader._dispatch", "title": "" }
48
+ ]
49
+ }
50
+ }