coc-vscode-loader 1.2.5 → 1.2.6
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 +4 -2
- package/assets/tui-preview.png +0 -0
- package/converter/README.md +28 -14
- package/converter/package-lock.json +2 -2
- package/converter/package.json +1 -1
- package/converter/src/convert.ts +2 -0
- package/converter/src/steps/index.ts +2 -0
- package/converter/src/steps/snippets.ts +101 -0
- package/converter/src/types.ts +7 -1
- package/lib/index.js +70 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,8 +37,9 @@ npm install coc-vscode-loader
|
|
|
37
37
|
| `x` | Toggle mark package for batch operations |
|
|
38
38
|
| `f` | Cycle filter: all → installed → available |
|
|
39
39
|
| `s` | Cycle sort: default → name → status → type |
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
40
|
+
| `[` / `]` | Previous / next page |
|
|
41
|
+
| `gg` | Jump to first page |
|
|
42
|
+
| `G` | Jump to last page |
|
|
42
43
|
| `<CR>` | Toggle details (commit / type / source) or install log |
|
|
43
44
|
| `/` | Search filter |
|
|
44
45
|
| `q` | Close (auto `:CocRestart` if changes detected) |
|
|
@@ -60,6 +61,7 @@ npm install coc-vscode-loader
|
|
|
60
61
|
|
|
61
62
|
- **Real conversion pipeline** — git clone → converter → npm install → esbuild → register to coc
|
|
62
63
|
- **Auto-fetch registry** — remote registry fetched in background when TUI opens, no manual refresh needed
|
|
64
|
+
- **Pagination** — `[`/`]` prev/next page, 50 packages per page, handles 5000+ registry entries
|
|
63
65
|
- **Incremental cache** — source/ keeps git repo, updates via git pull only
|
|
64
66
|
- **Commit tracking** — records commit SHA after install, visible in detail view
|
|
65
67
|
- **Update check** — `C` key compares against remote HEAD, shows `↑` when outdated
|
package/assets/tui-preview.png
CHANGED
|
Binary file
|
package/converter/README.md
CHANGED
|
@@ -18,11 +18,25 @@ cd ~/.config/coc/extensions && npm install /path/to/coc-ext
|
|
|
18
18
|
|
|
19
19
|
## Verified conversions
|
|
20
20
|
|
|
21
|
-
| Plugin | Type |
|
|
22
|
-
|
|
23
|
-
| Volar (Vue) | TS bridge |
|
|
24
|
-
| Prisma | Pure LSP |
|
|
25
|
-
| HTML CSS Support | Direct API |
|
|
21
|
+
| Plugin | Type | Notes |
|
|
22
|
+
|--------|------|-------|
|
|
23
|
+
| Volar (Vue) | TS bridge | Requires modified coc-tsserver |
|
|
24
|
+
| Prisma | Pure LSP | Auto-detects bin entry |
|
|
25
|
+
| HTML CSS Support | Direct API | Handles API differences |
|
|
26
|
+
| Deno | Pure LSP | Binary server download |
|
|
27
|
+
| TOML (Taplo) | Pure LSP | Binary server download |
|
|
28
|
+
| Ansible | Pure LSP | npm package server + pip install |
|
|
29
|
+
| YAML | Pure LSP | npm package server |
|
|
30
|
+
| Tailwind CSS | Pure LSP | npm package server, bin entry |
|
|
31
|
+
| Biome | Pure LSP | Binary server download |
|
|
32
|
+
| Stylelint | Pure LSP | npm package server |
|
|
33
|
+
| Prettier | Direct API | Source transforms |
|
|
34
|
+
| Svelte | Pure LSP | npm package server |
|
|
35
|
+
| Astro | Pure LSP | npm package server |
|
|
36
|
+
| Lua | Pure LSP | npm package server |
|
|
37
|
+
| gitignore | Direct API | Source transforms |
|
|
38
|
+
|
|
39
|
+
See the [registry](https://github.com/coc-plugin/coc-vscode-registry) for the full list and latest status.
|
|
26
40
|
|
|
27
41
|
### Plugin types
|
|
28
42
|
|
|
@@ -77,21 +91,21 @@ const PRESETS = {
|
|
|
77
91
|
`convert.ts` only calls `getActivePresets()` + `generateBridgeCode()`, it never touches bridge logic directly.
|
|
78
92
|
Adding a new bridge type = add a new preset in `presets.ts`, no changes to main flow.
|
|
79
93
|
|
|
80
|
-
See [
|
|
94
|
+
See [../docs/converter-design-v2.md](../docs/converter-design-v2.md).
|
|
81
95
|
|
|
82
96
|
## File structure
|
|
83
97
|
|
|
84
98
|
| File | Lines | Description |
|
|
85
99
|
|------|-------|-------------|
|
|
86
|
-
| `src/cli.ts` |
|
|
87
|
-
| `src/convert.ts` |
|
|
88
|
-
| `src/scanner.ts` |
|
|
89
|
-
| `src/transforms/import-mapping.ts` |
|
|
100
|
+
| `src/cli.ts` | 59 | CLI entry |
|
|
101
|
+
| `src/convert.ts` | 461 | Main flow + template generation + API replacement |
|
|
102
|
+
| `src/scanner.ts` | 52 | API scanner + plugin classification |
|
|
103
|
+
| `src/transforms/import-mapping.ts` | 193 | Import replacement |
|
|
90
104
|
| `src/transforms/language-client.ts` | 48 | LanguageClient adaptation |
|
|
91
|
-
| `src/transforms/class-to-factory.ts` |
|
|
92
|
-
| `src/transforms/provider-register.ts` |
|
|
93
|
-
| `src/transforms/enum-offset.ts` |
|
|
94
|
-
| **Total** | **~
|
|
105
|
+
| `src/transforms/class-to-factory.ts` | 53 | new Xxx() → Xxx.create() |
|
|
106
|
+
| `src/transforms/provider-register.ts` | 61 | Provider registration signature fixes |
|
|
107
|
+
| `src/transforms/enum-offset.ts` | 32 | Enum value offset annotations |
|
|
108
|
+
| **Total** | **~959** | |
|
|
95
109
|
|
|
96
110
|
## Handled API differences
|
|
97
111
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "converter",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "converter",
|
|
9
|
-
"version": "1.2.
|
|
9
|
+
"version": "1.2.6",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"commander": "^15.0.0",
|
|
12
12
|
"ts-morph": "^28.0.0",
|
package/converter/package.json
CHANGED
package/converter/src/convert.ts
CHANGED
|
@@ -326,6 +326,7 @@ export async function convert(opts: ConvertOptions): Promise<void> {
|
|
|
326
326
|
command: c.command,
|
|
327
327
|
title: c.title,
|
|
328
328
|
})) || undefined,
|
|
329
|
+
snippets: origPkg.contributes?.snippets || undefined,
|
|
329
330
|
...(tsPlugins.length > 0 ? {
|
|
330
331
|
typescriptServerPlugins: tsPlugins.map(p => ({
|
|
331
332
|
...p,
|
|
@@ -338,6 +339,7 @@ export async function convert(opts: ConvertOptions): Promise<void> {
|
|
|
338
339
|
// Clean null fields
|
|
339
340
|
if (!pkg.contributes?.configuration) delete (pkg.contributes as any).configuration
|
|
340
341
|
if (!pkg.contributes?.commands) delete (pkg.contributes as any).commands
|
|
342
|
+
if (!pkg.contributes?.snippets) delete (pkg.contributes as any).snippets
|
|
341
343
|
if (Object.keys(pkg.contributes).length === 0) delete (pkg as any).contributes
|
|
342
344
|
|
|
343
345
|
fs.writeFileSync(path.join(output, 'package.json'), JSON.stringify(pkg, null, 2))
|
|
@@ -3,6 +3,7 @@ import { languageClientGenerator } from './language-client.js'
|
|
|
3
3
|
import { sourceGenerator } from './source.js'
|
|
4
4
|
import { bridgeGenerator } from './bridge.js'
|
|
5
5
|
import { markUnsupportedGenerator } from './mark-unsupported.js'
|
|
6
|
+
import { snippetsGenerator } from './snippets.js'
|
|
6
7
|
|
|
7
8
|
const REGISTRY: Record<string, StepGenerator> = {}
|
|
8
9
|
|
|
@@ -27,3 +28,4 @@ registerGenerator(languageClientGenerator)
|
|
|
27
28
|
registerGenerator(sourceGenerator)
|
|
28
29
|
registerGenerator(bridgeGenerator)
|
|
29
30
|
registerGenerator(markUnsupportedGenerator)
|
|
31
|
+
registerGenerator(snippetsGenerator)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as fs from 'fs'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import { StepGenerator, StepContext, SnippetsStep, StepResult } from '../types.js'
|
|
4
|
+
|
|
5
|
+
export const snippetsGenerator: StepGenerator = {
|
|
6
|
+
type: 'snippets',
|
|
7
|
+
|
|
8
|
+
generate(ctx: StepContext, step: any): StepResult {
|
|
9
|
+
const ss = step as SnippetsStep
|
|
10
|
+
const { input, output, origPkg, verbose } = ctx
|
|
11
|
+
|
|
12
|
+
const contributedSnippets: Array<{ language: string; path: string }> =
|
|
13
|
+
origPkg.contributes?.snippets || []
|
|
14
|
+
|
|
15
|
+
if (contributedSnippets.length === 0 && !ss.languages) {
|
|
16
|
+
throw new Error('snippets step: source package.json has no contributes.snippets, and no languages specified in step config')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Collect unique (sourcePath → [languages]) mappings
|
|
20
|
+
const fileToLanguages = new Map<string, string[]>()
|
|
21
|
+
if (ss.languages) {
|
|
22
|
+
for (const lang of ss.languages) {
|
|
23
|
+
const entry = contributedSnippets.find(s => s.language === lang)
|
|
24
|
+
if (entry) {
|
|
25
|
+
const langs = fileToLanguages.get(entry.path) || []
|
|
26
|
+
langs.push(lang)
|
|
27
|
+
fileToLanguages.set(entry.path, langs)
|
|
28
|
+
} else {
|
|
29
|
+
const defaultPath = `./snippets/${lang}.json`
|
|
30
|
+
const fp = path.join(input, defaultPath)
|
|
31
|
+
if (fs.existsSync(fp)) {
|
|
32
|
+
const langs = fileToLanguages.get(defaultPath) || []
|
|
33
|
+
langs.push(lang)
|
|
34
|
+
fileToLanguages.set(defaultPath, langs)
|
|
35
|
+
} else if (verbose) {
|
|
36
|
+
console.warn(` snippets: no snippet file found for language "${lang}", skipping`)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
for (const s of contributedSnippets) {
|
|
42
|
+
const langs = fileToLanguages.get(s.path) || []
|
|
43
|
+
langs.push(s.language)
|
|
44
|
+
fileToLanguages.set(s.path, langs)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (fileToLanguages.size === 0) {
|
|
49
|
+
throw new Error('snippets step: no snippet files found to copy')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Create output directories and copy files to original relative paths
|
|
53
|
+
const srcDir = path.join(output, 'src')
|
|
54
|
+
fs.mkdirSync(srcDir, { recursive: true })
|
|
55
|
+
|
|
56
|
+
const generatedFiles: Array<{ path: string; content: string }> = []
|
|
57
|
+
let copiedCount = 0
|
|
58
|
+
const allLanguages: string[] = []
|
|
59
|
+
|
|
60
|
+
for (const [sourceRelPath, languages] of fileToLanguages) {
|
|
61
|
+
const sourceFile = path.join(input, sourceRelPath)
|
|
62
|
+
if (!fs.existsSync(sourceFile)) {
|
|
63
|
+
if (verbose) console.warn(` snippets: source file not found: ${sourceFile}, skipping`)
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
const dest = path.join(output, sourceRelPath)
|
|
67
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true })
|
|
68
|
+
fs.copyFileSync(sourceFile, dest)
|
|
69
|
+
copiedCount++
|
|
70
|
+
allLanguages.push(...languages)
|
|
71
|
+
if (verbose) console.log(` snippets: copied ${sourceRelPath} (${languages.join(', ')})`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (copiedCount === 0) {
|
|
75
|
+
throw new Error('snippets step: no snippet files were copied')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Generate empty src/index.ts
|
|
79
|
+
const indexContent = `\
|
|
80
|
+
import { ExtensionContext } from 'coc.nvim'
|
|
81
|
+
|
|
82
|
+
export function activate(context: ExtensionContext): void {
|
|
83
|
+
// coc-snippets discovers snippets via package.json's contributes.snippets
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
generatedFiles.push({ path: 'src/index.ts', content: indexContent })
|
|
87
|
+
|
|
88
|
+
const activationEvents = [...new Set(allLanguages)].map(l => `onLanguage:${l}`)
|
|
89
|
+
|
|
90
|
+
if (verbose) {
|
|
91
|
+
console.log(` snippets: ${copiedCount} files, ${new Set(allLanguages).size} languages`)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
generatedFiles,
|
|
96
|
+
entryPoint: 'src/index.ts',
|
|
97
|
+
keepDeps: {},
|
|
98
|
+
activationEvents,
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
}
|
package/converter/src/types.ts
CHANGED
|
@@ -73,7 +73,13 @@ export interface MarkUnsupportedStep {
|
|
|
73
73
|
verbose?: boolean
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export
|
|
76
|
+
export interface SnippetsStep {
|
|
77
|
+
type: 'snippets'
|
|
78
|
+
/** Optional: override languages to generate (default: read from source package.json's contributes.snippets) */
|
|
79
|
+
languages?: string[]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type ConvertStep = LanguageClientStep | SourceStep | BridgeStep | MarkUnsupportedStep | SnippetsStep
|
|
77
83
|
|
|
78
84
|
// ---- Step execution ----
|
|
79
85
|
|
package/lib/index.js
CHANGED
|
@@ -39,7 +39,7 @@ var require_package = __commonJS({
|
|
|
39
39
|
"package.json"(exports2, module2) {
|
|
40
40
|
module2.exports = {
|
|
41
41
|
name: "coc-vscode-loader",
|
|
42
|
-
version: "1.2.
|
|
42
|
+
version: "1.2.6",
|
|
43
43
|
description: "Run VS Code extensions seamlessly in coc.nvim",
|
|
44
44
|
main: "lib/index.js",
|
|
45
45
|
keywords: [
|
|
@@ -180,7 +180,7 @@ async function fetchRegistryJSON(url) {
|
|
|
180
180
|
} catch {
|
|
181
181
|
}
|
|
182
182
|
return new Promise((resolve2, reject) => {
|
|
183
|
-
(0, import_child_process.execFile)("curl", ["-sL", url], { encoding: "utf-8", maxBuffer:
|
|
183
|
+
(0, import_child_process.execFile)("curl", ["-sL", "--compressed", url], { encoding: "utf-8", maxBuffer: 20 * 1024 * 1024 }, (err, stdout) => {
|
|
184
184
|
if (err) reject(new Error(`curl failed: ${err.message}`));
|
|
185
185
|
else {
|
|
186
186
|
try {
|
|
@@ -239,6 +239,7 @@ var os2 = __toESM(require("os"));
|
|
|
239
239
|
function isInstalled(name) {
|
|
240
240
|
return fs2.existsSync(path2.join(os2.homedir(), ".config", "coc", "extensions", "node_modules", `coc-${name}`));
|
|
241
241
|
}
|
|
242
|
+
var PAGE_SIZE = 50;
|
|
242
243
|
function createInitialState() {
|
|
243
244
|
const packages = getAllPackages().map((info) => {
|
|
244
245
|
const installed = isInstalled(info.name);
|
|
@@ -268,7 +269,7 @@ function createInitialState() {
|
|
|
268
269
|
marked: false
|
|
269
270
|
};
|
|
270
271
|
});
|
|
271
|
-
return { packages, searchQuery: "", showHelp: false, activePill: null, dirty: false, viewFilter: "all", sortBy: "default" };
|
|
272
|
+
return { packages, searchQuery: "", showHelp: false, activePill: null, dirty: false, viewFilter: "all", sortBy: "default", currentPage: 0 };
|
|
272
273
|
}
|
|
273
274
|
var StateManager = class {
|
|
274
275
|
constructor(initial) {
|
|
@@ -339,21 +340,25 @@ var StateManager = class {
|
|
|
339
340
|
setViewFilter(filter) {
|
|
340
341
|
this.mutate((s) => {
|
|
341
342
|
s.viewFilter = filter;
|
|
343
|
+
s.currentPage = 0;
|
|
342
344
|
});
|
|
343
345
|
}
|
|
344
346
|
cycleViewFilter() {
|
|
345
347
|
this.mutate((s) => {
|
|
346
348
|
s.viewFilter = s.viewFilter === "all" ? "installed" : s.viewFilter === "installed" ? "not-installed" : "all";
|
|
349
|
+
s.currentPage = 0;
|
|
347
350
|
});
|
|
348
351
|
}
|
|
349
352
|
setSortBy(sortBy) {
|
|
350
353
|
this.mutate((s) => {
|
|
351
354
|
s.sortBy = sortBy;
|
|
355
|
+
s.currentPage = 0;
|
|
352
356
|
});
|
|
353
357
|
}
|
|
354
358
|
cycleSortBy() {
|
|
355
359
|
this.mutate((s) => {
|
|
356
360
|
s.sortBy = s.sortBy === "default" ? "name" : s.sortBy === "name" ? "status" : s.sortBy === "status" ? "type" : "default";
|
|
361
|
+
s.currentPage = 0;
|
|
357
362
|
});
|
|
358
363
|
}
|
|
359
364
|
setStatusMessage(msg) {
|
|
@@ -374,6 +379,12 @@ var StateManager = class {
|
|
|
374
379
|
setSearchQuery(query) {
|
|
375
380
|
this.mutate((s) => {
|
|
376
381
|
s.searchQuery = query;
|
|
382
|
+
s.currentPage = 0;
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
setPage(n) {
|
|
386
|
+
this.mutate((s) => {
|
|
387
|
+
s.currentPage = n;
|
|
377
388
|
});
|
|
378
389
|
}
|
|
379
390
|
getFilteredPackages() {
|
|
@@ -1028,8 +1039,9 @@ var HELP_TEXT = [
|
|
|
1028
1039
|
" x Toggle mark",
|
|
1029
1040
|
" f Cycle filter: all \u2192 installed \u2192 not-installed",
|
|
1030
1041
|
" s Cycle sort: default \u2192 name \u2192 status \u2192 type",
|
|
1031
|
-
"
|
|
1032
|
-
"
|
|
1042
|
+
" [/] Previous/next page",
|
|
1043
|
+
" gg Jump to first page",
|
|
1044
|
+
" G Jump to last page",
|
|
1033
1045
|
" <Enter> Toggle expand/collapse details",
|
|
1034
1046
|
" / Search filter",
|
|
1035
1047
|
" q Close window",
|
|
@@ -1068,10 +1080,14 @@ var TUI = class {
|
|
|
1068
1080
|
x: "x",
|
|
1069
1081
|
D: "D",
|
|
1070
1082
|
gg: "gg",
|
|
1071
|
-
G: "G"
|
|
1083
|
+
G: "G",
|
|
1084
|
+
"[": "pageup",
|
|
1085
|
+
"]": "pagedown"
|
|
1072
1086
|
};
|
|
1073
1087
|
this.rendering = false;
|
|
1074
1088
|
this.pendingRender = false;
|
|
1089
|
+
this.lastPage = -1;
|
|
1090
|
+
this.scrollToFirst = false;
|
|
1075
1091
|
this.state = state;
|
|
1076
1092
|
}
|
|
1077
1093
|
async open() {
|
|
@@ -1219,16 +1235,29 @@ var TUI = class {
|
|
|
1219
1235
|
return;
|
|
1220
1236
|
}
|
|
1221
1237
|
if (id === "gg") {
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
await import_coc2.workspace.nvim.call("nvim_win_set_cursor", [this.winid, [firstLine + 1, 0]]);
|
|
1225
|
-
}
|
|
1238
|
+
this.state.setPage(0);
|
|
1239
|
+
this.scrollToFirst = true;
|
|
1226
1240
|
return;
|
|
1227
1241
|
}
|
|
1228
1242
|
if (id === "G") {
|
|
1229
|
-
const
|
|
1230
|
-
|
|
1231
|
-
|
|
1243
|
+
const filtered = this.state.getFilteredPackages();
|
|
1244
|
+
const totalPages = Math.ceil(filtered.length / PAGE_SIZE) || 1;
|
|
1245
|
+
this.state.setPage(totalPages - 1);
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
if (id === "pagedown") {
|
|
1249
|
+
const filtered = this.state.getFilteredPackages();
|
|
1250
|
+
const totalPages = Math.ceil(filtered.length / PAGE_SIZE) || 1;
|
|
1251
|
+
const s2 = this.state.getState();
|
|
1252
|
+
if (s2.currentPage < totalPages - 1) {
|
|
1253
|
+
this.state.setPage(s2.currentPage + 1);
|
|
1254
|
+
}
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
if (id === "pageup") {
|
|
1258
|
+
const s2 = this.state.getState();
|
|
1259
|
+
if (s2.currentPage > 0) {
|
|
1260
|
+
this.state.setPage(s2.currentPage - 1);
|
|
1232
1261
|
}
|
|
1233
1262
|
return;
|
|
1234
1263
|
}
|
|
@@ -1325,6 +1354,8 @@ var TUI = class {
|
|
|
1325
1354
|
["D", "D"],
|
|
1326
1355
|
["gg", "gg"],
|
|
1327
1356
|
["G", "G"],
|
|
1357
|
+
["[", "pageup"],
|
|
1358
|
+
["]", "pagedown"],
|
|
1328
1359
|
["<CR>", "cr"]
|
|
1329
1360
|
];
|
|
1330
1361
|
for (const [vimKey, id] of entries) {
|
|
@@ -1378,6 +1409,15 @@ var TUI = class {
|
|
|
1378
1409
|
}], true);
|
|
1379
1410
|
}
|
|
1380
1411
|
await nvim.resumeNotification();
|
|
1412
|
+
const shouldScroll = this.scrollToFirst || state.currentPage !== this.lastPage;
|
|
1413
|
+
this.scrollToFirst = false;
|
|
1414
|
+
this.lastPage = state.currentPage;
|
|
1415
|
+
if (!state.showHelp && shouldScroll) {
|
|
1416
|
+
const firstPkgLine = Math.min(...result.pkgLineMap.keys());
|
|
1417
|
+
if (isFinite(firstPkgLine)) {
|
|
1418
|
+
await nvim.call("nvim_win_set_cursor", [this.winid, [firstPkgLine + 1, 0]]);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1381
1421
|
this.pkgLineMap = result.pkgLineMap;
|
|
1382
1422
|
this.logLineSet = result.logLines;
|
|
1383
1423
|
} finally {
|
|
@@ -1443,26 +1483,29 @@ var TUI = class {
|
|
|
1443
1483
|
}
|
|
1444
1484
|
buf.nl();
|
|
1445
1485
|
buf.nl();
|
|
1446
|
-
const
|
|
1447
|
-
const
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
buf.
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1486
|
+
const totalPages = Math.ceil(filtered.length / PAGE_SIZE) || 1;
|
|
1487
|
+
const page = Math.min(state.currentPage, totalPages - 1);
|
|
1488
|
+
const start = page * PAGE_SIZE;
|
|
1489
|
+
const pageItems = filtered.slice(start, start + PAGE_SIZE);
|
|
1490
|
+
const end = Math.min(start + PAGE_SIZE, filtered.length);
|
|
1491
|
+
if (totalPages > 1) {
|
|
1492
|
+
buf.append(`Page ${page + 1}/${totalPages} \xB7 ${start + 1}\u2013${end} of ${filtered.length}`, "CocConverterTotal");
|
|
1493
|
+
} else {
|
|
1494
|
+
buf.append(`${filtered.length} packages`, "CocConverterTotal");
|
|
1495
|
+
}
|
|
1496
|
+
buf.nl();
|
|
1497
|
+
buf.nl();
|
|
1498
|
+
for (const e of pageItems) {
|
|
1499
|
+
this.renderEntry(buf, pkgLineMap, logSet, e);
|
|
1500
|
+
}
|
|
1459
1501
|
if (filtered.length === 0 && state.searchQuery) {
|
|
1460
1502
|
buf.nl("no matching packages");
|
|
1461
1503
|
}
|
|
1462
1504
|
buf.nl();
|
|
1463
1505
|
buf.append(" " + "\u2500".repeat(50), "Comment");
|
|
1464
1506
|
buf.nl();
|
|
1465
|
-
|
|
1507
|
+
const pageNav = totalPages > 1 ? ` [ prev ] next` : "";
|
|
1508
|
+
buf.append(` ${filtered.length} packages \xB7 ${filterLabel} \xB7 ${sortLabel}${pageNav}`, "Comment");
|
|
1466
1509
|
const result = buf.render(2);
|
|
1467
1510
|
return { lines: result.lines, pkgLineMap, logLines: logSet, highlights: result.highlights };
|
|
1468
1511
|
}
|