@dungle-scrubs/tallow 0.9.3 → 0.9.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/dist/cli.js +7 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/interactive-mode-patch.d.ts +24 -10
- package/dist/interactive-mode-patch.d.ts.map +1 -1
- package/dist/interactive-mode-patch.js +285 -148
- package/dist/interactive-mode-patch.js.map +1 -1
- package/dist/interactive-reset.d.ts +49 -0
- package/dist/interactive-reset.d.ts.map +1 -0
- package/dist/interactive-reset.js +40 -0
- package/dist/interactive-reset.js.map +1 -0
- package/dist/pi-tui-editor-patch.d.ts +10 -0
- package/dist/pi-tui-editor-patch.d.ts.map +1 -0
- package/dist/pi-tui-editor-patch.js +159 -0
- package/dist/pi-tui-editor-patch.js.map +1 -0
- package/dist/pi-tui-patch.d.ts +2 -0
- package/dist/pi-tui-patch.d.ts.map +1 -0
- package/dist/pi-tui-patch.js +563 -0
- package/dist/pi-tui-patch.js.map +1 -0
- package/dist/pi-tui-settings-list-patch.d.ts +11 -0
- package/dist/pi-tui-settings-list-patch.d.ts.map +1 -0
- package/dist/pi-tui-settings-list-patch.js +38 -0
- package/dist/pi-tui-settings-list-patch.js.map +1 -0
- package/dist/reset-diagnostics.d.ts +69 -0
- package/dist/reset-diagnostics.d.ts.map +1 -0
- package/dist/reset-diagnostics.js +41 -0
- package/dist/reset-diagnostics.js.map +1 -0
- package/dist/sdk.d.ts +5 -21
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +180 -149
- package/dist/sdk.js.map +1 -1
- package/dist/workspace-transition-interactive.d.ts +1 -0
- package/dist/workspace-transition-interactive.d.ts.map +1 -1
- package/dist/workspace-transition-interactive.js +7 -17
- package/dist/workspace-transition-interactive.js.map +1 -1
- package/extensions/__integration__/audit-findings.test.ts +6 -16
- package/extensions/__integration__/teams-runtime.test.ts +4 -1
- package/extensions/_icons/index.ts +2 -4
- package/extensions/_shared/__tests__/image-metadata.test.ts +33 -0
- package/extensions/_shared/__tests__/terminal-links.test.ts +18 -0
- package/extensions/_shared/image-metadata.ts +99 -0
- package/extensions/_shared/inline-preview.ts +1 -1
- package/extensions/_shared/pid-registry.ts +0 -1
- package/extensions/_shared/terminal-links.ts +22 -0
- package/extensions/ask-user-question-tool/index.ts +0 -3
- package/extensions/clear/__tests__/clear.test.ts +270 -3
- package/extensions/command-expansion/index.ts +1 -1
- package/extensions/context-files/index.ts +5 -1
- package/extensions/context-fork/__tests__/context-fork.test.ts +94 -1
- package/extensions/context-fork/extension.json +1 -1
- package/extensions/context-fork/index.ts +32 -0
- package/extensions/edit-tool-enhanced/index.ts +2 -1
- package/extensions/hooks/index.ts +33 -11
- package/extensions/loop/index.ts +14 -1
- package/extensions/lsp/index.ts +64 -13
- package/extensions/lsp/package.json +2 -2
- package/extensions/permissions/__tests__/permissions.test.ts +4 -4
- package/extensions/random-spinner/index.ts +7 -642
- package/extensions/read-tool-enhanced/index.ts +6 -8
- package/extensions/render-stabilizer/__tests__/render-stabilizer.test.ts +4 -5
- package/extensions/render-stabilizer/index.ts +6 -6
- package/extensions/show-system-prompt/__tests__/show-system-prompt.test.ts +1 -1
- package/extensions/slash-command-bridge/__tests__/slash-command-bridge.test.ts +26 -0
- package/extensions/slash-command-bridge/index.ts +14 -2
- package/extensions/subagent-tool/index.ts +1 -1
- package/extensions/subagent-tool/model-resolver.ts +274 -7
- package/extensions/tasks/__tests__/state-ui.test.ts +3 -3
- package/extensions/tasks/__tests__/widget-subagents.test.ts +2 -2
- package/extensions/tasks/commands/register-tasks-extension.ts +10 -10
- package/extensions/tasks/state/index.ts +1 -1
- package/extensions/tasks/ui/index.ts +2 -7
- package/extensions/teams-tool/tools/register-extension.ts +1 -3
- package/extensions/web-search-tool/index.ts +2 -1
- package/extensions/write-tool-enhanced/__tests__/write-tool-enhanced.test.ts +21 -6
- package/extensions/write-tool-enhanced/index.ts +2 -1
- package/node_modules/@mariozechner/pi-tui/README.md +56 -34
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts +18 -13
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js +182 -113
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js +3 -3
- package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts +45 -36
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.js +489 -325
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts +1 -99
- package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/image.js +17 -192
- package/node_modules/@mariozechner/pi-tui/dist/components/image.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/input.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/input.js +57 -60
- package/node_modules/@mariozechner/pi-tui/dist/components/input.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts +2 -69
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.js +5 -102
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js +111 -53
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts +19 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js +78 -67
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts +0 -2
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js +12 -23
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/index.d.ts +8 -10
- package/node_modules/@mariozechner/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/index.js +6 -9
- package/node_modules/@mariozechner/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts +108 -238
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.js +108 -365
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts +33 -48
- package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keys.js +239 -155
- package/node_modules/@mariozechner/pi-tui/dist/keys.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts +14 -94
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js +44 -186
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts +13 -58
- package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal.js +78 -111
- package/node_modules/@mariozechner/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts +24 -110
- package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/tui.js +188 -435
- package/node_modules/@mariozechner/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts +0 -18
- package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/utils.js +251 -119
- package/node_modules/@mariozechner/pi-tui/dist/utils.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/package.json +6 -6
- package/node_modules/@mariozechner/pi-tui/src/__tests__/__snapshots__/render.test.ts.snap +3 -40
- package/node_modules/@mariozechner/pi-tui/src/__tests__/image-component.test.ts +71 -81
- package/node_modules/@mariozechner/pi-tui/src/__tests__/render.test.ts +0 -33
- package/node_modules/@mariozechner/pi-tui/src/__tests__/terminal-image.test.ts +93 -334
- package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-render-scheduling.test.ts +1 -1
- package/node_modules/@mariozechner/pi-tui/src/__tests__/utils.test.ts +11 -196
- package/node_modules/@mariozechner/pi-tui/src/autocomplete.ts +228 -142
- package/node_modules/@mariozechner/pi-tui/src/components/cancellable-loader.ts +3 -3
- package/node_modules/@mariozechner/pi-tui/src/components/editor.ts +624 -390
- package/node_modules/@mariozechner/pi-tui/src/components/image.ts +17 -227
- package/node_modules/@mariozechner/pi-tui/src/components/input.ts +71 -63
- package/node_modules/@mariozechner/pi-tui/src/components/loader.ts +5 -137
- package/node_modules/@mariozechner/pi-tui/src/components/markdown.ts +143 -52
- package/node_modules/@mariozechner/pi-tui/src/components/select-list.ts +136 -70
- package/node_modules/@mariozechner/pi-tui/src/components/settings-list.ts +11 -23
- package/node_modules/@mariozechner/pi-tui/src/index.ts +17 -36
- package/node_modules/@mariozechner/pi-tui/src/keybindings.ts +148 -421
- package/node_modules/@mariozechner/pi-tui/src/keys.ts +253 -181
- package/node_modules/@mariozechner/pi-tui/src/terminal-image.ts +51 -252
- package/node_modules/@mariozechner/pi-tui/src/terminal.ts +78 -133
- package/node_modules/@mariozechner/pi-tui/src/tui.ts +202 -478
- package/node_modules/@mariozechner/pi-tui/src/utils.ts +289 -125
- package/node_modules/@mariozechner/pi-tui/tsconfig.build.json +1 -0
- package/package.json +13 -13
- package/packages/tallow-tui/node_modules/@types/mime-types/README.md +8 -2
- package/packages/tallow-tui/node_modules/@types/mime-types/index.d.ts +6 -0
- package/packages/tallow-tui/node_modules/@types/mime-types/package.json +9 -3
- package/packages/tallow-tui/node_modules/get-east-asian-width/lookup-data.js +18 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/lookup.js +116 -384
- package/packages/tallow-tui/node_modules/get-east-asian-width/package.json +5 -4
- package/packages/tallow-tui/node_modules/get-east-asian-width/utilities.js +24 -0
- package/packages/tallow-tui/node_modules/marked/README.md +5 -4
- package/packages/tallow-tui/node_modules/marked/bin/main.js +10 -8
- package/packages/tallow-tui/node_modules/marked/bin/marked.js +2 -1
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.ts +156 -125
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js +67 -2179
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js.map +3 -3
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js +67 -2201
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js.map +3 -3
- package/packages/tallow-tui/node_modules/marked/man/marked.1 +4 -2
- package/packages/tallow-tui/node_modules/marked/man/marked.1.md +2 -1
- package/packages/tallow-tui/node_modules/marked/package.json +26 -34
- package/runtime/model-metadata-overrides.ts +10 -1
- package/runtime/pid-schema.ts +26 -6
- package/skills/tallow-expert/SKILL.md +1 -3
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts +0 -32
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.js +0 -46
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts +0 -52
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js +0 -89
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts +0 -14
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js +0 -55
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-change-listener.test.ts +0 -121
- package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-ghost-text.test.ts +0 -112
- package/node_modules/@mariozechner/pi-tui/src/__tests__/mouse-events.test.ts +0 -134
- package/node_modules/@mariozechner/pi-tui/src/__tests__/settings-list.test.ts +0 -49
- package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-diff-regression.test.ts +0 -555
- package/node_modules/@mariozechner/pi-tui/src/border-styles.ts +0 -60
- package/node_modules/@mariozechner/pi-tui/src/components/bordered-box.ts +0 -113
- package/node_modules/@mariozechner/pi-tui/src/test-utils/capability-env.ts +0 -56
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs +0 -2211
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs.map +0 -7
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.cts +0 -728
- package/packages/tallow-tui/node_modules/marked/marked.min.js +0 -69
|
@@ -1,51 +1,73 @@
|
|
|
1
1
|
# @mariozechner/pi-tui (Tallow fork)
|
|
2
2
|
|
|
3
|
-
Tallow
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Tallow vendors a local fork of `@mariozechner/pi-tui` to keep a small set
|
|
4
|
+
of UI primitives that upstream still does not expose in the form tallow
|
|
5
|
+
needs.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This is **not** a tiny historical snapshot anymore. The authoritative
|
|
8
|
+
upstream reference for the current reduction effort is
|
|
9
|
+
`@mariozechner/pi-tui@0.67.68`.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Why the fork existed
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let extensions set global defaults at session start.
|
|
13
|
+
The original fork was created because tallow needed a few TUI primitives
|
|
14
|
+
that extension APIs could not provide directly:
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
- configurable loader defaults
|
|
17
|
+
- border styles and bordered boxes
|
|
18
|
+
- earlier input interception hooks
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
`BorderedBox` component wraps content in a full border with optional
|
|
20
|
-
title, padding, and color functions.
|
|
20
|
+
Since then, upstream added several features that used to be local, so the
|
|
21
|
+
current fork is larger than the original rationale justifies.
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
## Long-term keep surface
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
consume input. Used by the which-key overlay extension.
|
|
25
|
+
The fork reduction effort is shrinking the long-term delta to only the
|
|
26
|
+
smallest set of proven tallow requirements:
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
- border styles
|
|
29
|
+
- loader global defaults and hide sentinel
|
|
30
|
+
- editor ghost-text and change-listener APIs
|
|
31
|
+
- minimal reset/render primitives still required by tallow
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
Everything else is treated as revert-or-extract unless proven necessary.
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
## Authoritative audit
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
| `tui.ts` | Input middleware array + add/remove methods |
|
|
37
|
+
Do not trust this README alone for the current delta. The canonical
|
|
38
|
+
status document is `docs/research/pi-tui-fork-audit.md`, generated and
|
|
39
|
+
validated by the audit script:
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
```bash
|
|
42
|
+
node scripts/audit-pi-tui-fork.mjs
|
|
43
|
+
```
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|------|---------|
|
|
43
|
-
| `border-styles.ts` | BorderStyle interface + presets |
|
|
44
|
-
| `components/bordered-box.ts` | Box component with configurable borders |
|
|
45
|
+
To regenerate the human-readable audit note:
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
```bash
|
|
48
|
+
node scripts/audit-pi-tui-fork.mjs \
|
|
49
|
+
--write-markdown docs/research/pi-tui-fork-audit.md
|
|
50
|
+
```
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
The generated report classifies each changed runtime file as one of:
|
|
53
|
+
|
|
54
|
+
- `keep`
|
|
55
|
+
- `extract`
|
|
56
|
+
- `upstream`
|
|
57
|
+
- `revert`
|
|
58
|
+
|
|
59
|
+
## Current ownership rules
|
|
60
|
+
|
|
61
|
+
- `packages/tallow-tui` should own generic TUI primitives only
|
|
62
|
+
- tallow session/reset behavior belongs in `src/interactive-*.ts`, not in
|
|
63
|
+
the fork
|
|
64
|
+
- application helpers should move to tallow or upstream rather than stay
|
|
65
|
+
in the fork by accident
|
|
66
|
+
|
|
67
|
+
## Sync approach
|
|
68
|
+
|
|
69
|
+
1. audit local-vs-upstream runtime deltas
|
|
70
|
+
2. revert low-risk files first
|
|
71
|
+
3. extract tallow-owned helpers out of the fork
|
|
72
|
+
4. keep only the justified primitive surface
|
|
73
|
+
5. shrink high-risk render/input deltas last with PTY regression tests
|
|
@@ -3,16 +3,22 @@ export interface AutocompleteItem {
|
|
|
3
3
|
label: string;
|
|
4
4
|
description?: string;
|
|
5
5
|
}
|
|
6
|
+
type Awaitable<T> = T | Promise<T>;
|
|
6
7
|
export interface SlashCommand {
|
|
7
8
|
name: string;
|
|
8
9
|
description?: string;
|
|
9
|
-
|
|
10
|
+
argumentHint?: string;
|
|
11
|
+
getArgumentCompletions?(argumentPrefix: string): Awaitable<AutocompleteItem[] | null>;
|
|
12
|
+
}
|
|
13
|
+
export interface AutocompleteSuggestions {
|
|
14
|
+
items: AutocompleteItem[];
|
|
15
|
+
prefix: string;
|
|
10
16
|
}
|
|
11
17
|
export interface AutocompleteProvider {
|
|
12
|
-
getSuggestions(lines: string[], cursorLine: number, cursorCol: number
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} | null
|
|
18
|
+
getSuggestions(lines: string[], cursorLine: number, cursorCol: number, options: {
|
|
19
|
+
signal: AbortSignal;
|
|
20
|
+
force?: boolean;
|
|
21
|
+
}): Promise<AutocompleteSuggestions | null>;
|
|
16
22
|
applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
|
|
17
23
|
lines: string[];
|
|
18
24
|
cursorLine: number;
|
|
@@ -24,10 +30,10 @@ export declare class CombinedAutocompleteProvider implements AutocompleteProvide
|
|
|
24
30
|
private basePath;
|
|
25
31
|
private fdPath;
|
|
26
32
|
constructor(commands?: (SlashCommand | AutocompleteItem)[], basePath?: string, fdPath?: string | null);
|
|
27
|
-
getSuggestions(lines: string[], cursorLine: number, cursorCol: number
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} | null
|
|
33
|
+
getSuggestions(lines: string[], cursorLine: number, cursorCol: number, options: {
|
|
34
|
+
signal: AbortSignal;
|
|
35
|
+
force?: boolean;
|
|
36
|
+
}): Promise<AutocompleteSuggestions | null>;
|
|
31
37
|
applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
|
|
32
38
|
lines: string[];
|
|
33
39
|
cursorLine: number;
|
|
@@ -36,13 +42,12 @@ export declare class CombinedAutocompleteProvider implements AutocompleteProvide
|
|
|
36
42
|
private extractAtPrefix;
|
|
37
43
|
private extractPathPrefix;
|
|
38
44
|
private expandHomePath;
|
|
45
|
+
private resolveScopedFuzzyQuery;
|
|
46
|
+
private scopedPathForDisplay;
|
|
39
47
|
private getFileSuggestions;
|
|
40
48
|
private scoreEntry;
|
|
41
49
|
private getFuzzyFileSuggestions;
|
|
42
|
-
getForceFileSuggestions(lines: string[], cursorLine: number, cursorCol: number): {
|
|
43
|
-
items: AutocompleteItem[];
|
|
44
|
-
prefix: string;
|
|
45
|
-
} | null;
|
|
46
50
|
shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean;
|
|
47
51
|
}
|
|
52
|
+
export {};
|
|
48
53
|
//# sourceMappingURL=autocomplete.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../src/autocomplete.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../src/autocomplete.ts"],"names":[],"mappings":"AA8NA,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEnC,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,sBAAsB,CAAC,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IAGpC,cAAc,CACb,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/C,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;IAI3C,eAAe,CACd,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,MAAM,GACZ;QACF,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KAClB,CAAC;CACF;AAGD,qBAAa,4BAA6B,YAAW,oBAAoB;IACxE,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAgB;gBAG7B,QAAQ,GAAE,CAAC,YAAY,GAAG,gBAAgB,CAAC,EAAO,EAClD,QAAQ,GAAE,MAAsB,EAChC,MAAM,GAAE,MAAM,GAAG,IAAW;IAOvB,cAAc,CACnB,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/C,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAsF1C,eAAe,CACd,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,MAAM,GACZ;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAwF7D,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,kBAAkB;IAyI1B,OAAO,CAAC,UAAU;YAuBJ,uBAAuB;IA6DrC,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;CAW5F"}
|
|
@@ -1,9 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
import { readdirSync, statSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { basename, dirname, join } from "node:path";
|
|
5
5
|
import { fuzzyFilter } from "./fuzzy.js";
|
|
6
6
|
const PATH_DELIMITERS = new Set([" ", "\t", '"', "'", "="]);
|
|
7
|
+
function toDisplayPath(value) {
|
|
8
|
+
return value.replace(/\\/g, "/");
|
|
9
|
+
}
|
|
10
|
+
function escapeRegex(value) {
|
|
11
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
12
|
+
}
|
|
13
|
+
function buildFdPathQuery(query) {
|
|
14
|
+
const normalized = toDisplayPath(query);
|
|
15
|
+
if (!normalized.includes("/")) {
|
|
16
|
+
return normalized;
|
|
17
|
+
}
|
|
18
|
+
const hasTrailingSeparator = normalized.endsWith("/");
|
|
19
|
+
const trimmed = normalized.replace(/^\/+|\/+$/g, "");
|
|
20
|
+
if (!trimmed) {
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
const separatorPattern = "[\\\\/]";
|
|
24
|
+
const segments = trimmed
|
|
25
|
+
.split("/")
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map((segment) => escapeRegex(segment));
|
|
28
|
+
if (segments.length === 0) {
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
let pattern = segments.join(separatorPattern);
|
|
32
|
+
if (hasTrailingSeparator) {
|
|
33
|
+
pattern += separatorPattern;
|
|
34
|
+
}
|
|
35
|
+
return pattern;
|
|
36
|
+
}
|
|
7
37
|
function findLastDelimiter(text) {
|
|
8
38
|
for (let i = text.length - 1; i >= 0; i -= 1) {
|
|
9
39
|
if (PATH_DELIMITERS.has(text[i] ?? "")) {
|
|
@@ -67,7 +97,7 @@ function buildCompletionValue(path, options) {
|
|
|
67
97
|
return `${openQuote}${path}${closeQuote}`;
|
|
68
98
|
}
|
|
69
99
|
// Use fd to walk directory tree (fast, respects .gitignore)
|
|
70
|
-
function walkDirectoryWithFd(baseDir, fdPath, query, maxResults) {
|
|
100
|
+
async function walkDirectoryWithFd(baseDir, fdPath, query, maxResults, signal) {
|
|
71
101
|
const args = [
|
|
72
102
|
"--base-directory",
|
|
73
103
|
baseDir,
|
|
@@ -86,35 +116,63 @@ function walkDirectoryWithFd(baseDir, fdPath, query, maxResults) {
|
|
|
86
116
|
"--exclude",
|
|
87
117
|
".git/**",
|
|
88
118
|
];
|
|
89
|
-
// Add query as pattern if provided
|
|
90
119
|
if (query) {
|
|
91
|
-
args.push(query);
|
|
120
|
+
args.push(buildFdPathQuery(query));
|
|
92
121
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
98
|
-
if (result.status !== 0 || !result.stdout) {
|
|
99
|
-
return [];
|
|
100
|
-
}
|
|
101
|
-
const lines = result.stdout.trim().split("\n").filter(Boolean);
|
|
102
|
-
const results = [];
|
|
103
|
-
for (const line of lines) {
|
|
104
|
-
const normalizedPath = line.endsWith("/") ? line.slice(0, -1) : line;
|
|
105
|
-
if (normalizedPath === ".git" ||
|
|
106
|
-
normalizedPath.startsWith(".git/") ||
|
|
107
|
-
normalizedPath.includes("/.git/")) {
|
|
108
|
-
continue;
|
|
122
|
+
return await new Promise((resolve) => {
|
|
123
|
+
if (signal.aborted) {
|
|
124
|
+
resolve([]);
|
|
125
|
+
return;
|
|
109
126
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
results.push({
|
|
113
|
-
path: line,
|
|
114
|
-
isDirectory,
|
|
127
|
+
const child = spawn(fdPath, args, {
|
|
128
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
115
129
|
});
|
|
116
|
-
|
|
117
|
-
|
|
130
|
+
let stdout = "";
|
|
131
|
+
let resolved = false;
|
|
132
|
+
const finish = (results) => {
|
|
133
|
+
if (resolved)
|
|
134
|
+
return;
|
|
135
|
+
resolved = true;
|
|
136
|
+
signal.removeEventListener("abort", onAbort);
|
|
137
|
+
resolve(results);
|
|
138
|
+
};
|
|
139
|
+
const onAbort = () => {
|
|
140
|
+
if (child.exitCode === null) {
|
|
141
|
+
child.kill("SIGKILL");
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
145
|
+
child.stdout.setEncoding("utf-8");
|
|
146
|
+
child.stdout.on("data", (chunk) => {
|
|
147
|
+
stdout += chunk;
|
|
148
|
+
});
|
|
149
|
+
child.on("error", () => {
|
|
150
|
+
finish([]);
|
|
151
|
+
});
|
|
152
|
+
child.on("close", (code) => {
|
|
153
|
+
if (signal.aborted || code !== 0 || !stdout) {
|
|
154
|
+
finish([]);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
158
|
+
const results = [];
|
|
159
|
+
for (const line of lines) {
|
|
160
|
+
const displayLine = toDisplayPath(line);
|
|
161
|
+
const hasTrailingSeparator = displayLine.endsWith("/");
|
|
162
|
+
const normalizedPath = hasTrailingSeparator ? displayLine.slice(0, -1) : displayLine;
|
|
163
|
+
if (normalizedPath === ".git" ||
|
|
164
|
+
normalizedPath.startsWith(".git/") ||
|
|
165
|
+
normalizedPath.includes("/.git/")) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
results.push({
|
|
169
|
+
path: displayLine,
|
|
170
|
+
isDirectory: hasTrailingSeparator,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
finish(results);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
118
176
|
}
|
|
119
177
|
// Combined provider that handles both slash commands and file paths
|
|
120
178
|
export class CombinedAutocompleteProvider {
|
|
@@ -126,15 +184,15 @@ export class CombinedAutocompleteProvider {
|
|
|
126
184
|
this.basePath = basePath;
|
|
127
185
|
this.fdPath = fdPath;
|
|
128
186
|
}
|
|
129
|
-
getSuggestions(lines, cursorLine, cursorCol) {
|
|
187
|
+
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
130
188
|
const currentLine = lines[cursorLine] || "";
|
|
131
189
|
const textBeforeCursor = currentLine.slice(0, cursorCol);
|
|
132
|
-
// Check for @ file reference (fuzzy search) - must be after a delimiter or at start
|
|
133
190
|
const atPrefix = this.extractAtPrefix(textBeforeCursor);
|
|
134
191
|
if (atPrefix) {
|
|
135
192
|
const { rawPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);
|
|
136
|
-
const suggestions = this.getFuzzyFileSuggestions(rawPrefix, {
|
|
137
|
-
isQuotedPrefix
|
|
193
|
+
const suggestions = await this.getFuzzyFileSuggestions(rawPrefix, {
|
|
194
|
+
isQuotedPrefix,
|
|
195
|
+
signal: options.signal,
|
|
138
196
|
});
|
|
139
197
|
if (suggestions.length === 0)
|
|
140
198
|
return null;
|
|
@@ -143,17 +201,21 @@ export class CombinedAutocompleteProvider {
|
|
|
143
201
|
prefix: atPrefix,
|
|
144
202
|
};
|
|
145
203
|
}
|
|
146
|
-
|
|
147
|
-
if (textBeforeCursor.startsWith("/")) {
|
|
204
|
+
if (!options.force && textBeforeCursor.startsWith("/")) {
|
|
148
205
|
const spaceIndex = textBeforeCursor.indexOf(" ");
|
|
149
206
|
if (spaceIndex === -1) {
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
207
|
+
const prefix = textBeforeCursor.slice(1);
|
|
208
|
+
const commandItems = this.commands.map((cmd) => {
|
|
209
|
+
const name = "name" in cmd ? cmd.name : cmd.value;
|
|
210
|
+
const hint = "argumentHint" in cmd && cmd.argumentHint ? cmd.argumentHint : undefined;
|
|
211
|
+
const desc = cmd.description ?? "";
|
|
212
|
+
const fullDesc = hint ? (desc ? `${hint} — ${desc}` : hint) : desc;
|
|
213
|
+
return {
|
|
214
|
+
name,
|
|
215
|
+
label: name,
|
|
216
|
+
description: fullDesc || undefined,
|
|
217
|
+
};
|
|
218
|
+
});
|
|
157
219
|
const filtered = fuzzyFilter(commandItems, prefix, (item) => item.name).map((item) => ({
|
|
158
220
|
value: item.name,
|
|
159
221
|
label: item.label,
|
|
@@ -166,52 +228,35 @@ export class CombinedAutocompleteProvider {
|
|
|
166
228
|
prefix: textBeforeCursor,
|
|
167
229
|
};
|
|
168
230
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (!command || !("getArgumentCompletions" in command) || !command.getArgumentCompletions) {
|
|
178
|
-
return null; // No argument completion for this command
|
|
179
|
-
}
|
|
180
|
-
const argumentSuggestions = command.getArgumentCompletions(argumentText);
|
|
181
|
-
if (!argumentSuggestions || argumentSuggestions.length === 0) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
return {
|
|
185
|
-
items: argumentSuggestions,
|
|
186
|
-
prefix: argumentText,
|
|
187
|
-
};
|
|
231
|
+
const commandName = textBeforeCursor.slice(1, spaceIndex);
|
|
232
|
+
const argumentText = textBeforeCursor.slice(spaceIndex + 1);
|
|
233
|
+
const command = this.commands.find((cmd) => {
|
|
234
|
+
const name = "name" in cmd ? cmd.name : cmd.value;
|
|
235
|
+
return name === commandName;
|
|
236
|
+
});
|
|
237
|
+
if (!command || !("getArgumentCompletions" in command) || !command.getArgumentCompletions) {
|
|
238
|
+
return null;
|
|
188
239
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const pathMatch = this.extractPathPrefix(textBeforeCursor, false);
|
|
192
|
-
if (pathMatch !== null) {
|
|
193
|
-
const suggestions = this.getFileSuggestions(pathMatch);
|
|
194
|
-
if (suggestions.length === 0)
|
|
240
|
+
const argumentSuggestions = await command.getArgumentCompletions(argumentText);
|
|
241
|
+
if (!Array.isArray(argumentSuggestions) || argumentSuggestions.length === 0) {
|
|
195
242
|
return null;
|
|
196
|
-
// Check if we have an exact match that is a directory
|
|
197
|
-
// In that case, we might want to return suggestions for the directory content instead
|
|
198
|
-
// But only if the prefix ends with /
|
|
199
|
-
if (suggestions.length === 1 &&
|
|
200
|
-
suggestions[0]?.value === pathMatch &&
|
|
201
|
-
!pathMatch.endsWith("/")) {
|
|
202
|
-
// Exact match found (e.g. user typed "src" and "src/" is the only match)
|
|
203
|
-
// We still return it so user can select it and add /
|
|
204
|
-
return {
|
|
205
|
-
items: suggestions,
|
|
206
|
-
prefix: pathMatch,
|
|
207
|
-
};
|
|
208
243
|
}
|
|
209
244
|
return {
|
|
210
|
-
items:
|
|
211
|
-
prefix:
|
|
245
|
+
items: argumentSuggestions,
|
|
246
|
+
prefix: argumentText,
|
|
212
247
|
};
|
|
213
248
|
}
|
|
214
|
-
|
|
249
|
+
const pathMatch = this.extractPathPrefix(textBeforeCursor, options.force ?? false);
|
|
250
|
+
if (pathMatch === null) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const suggestions = this.getFileSuggestions(pathMatch);
|
|
254
|
+
if (suggestions.length === 0)
|
|
255
|
+
return null;
|
|
256
|
+
return {
|
|
257
|
+
items: suggestions,
|
|
258
|
+
prefix: pathMatch,
|
|
259
|
+
};
|
|
215
260
|
}
|
|
216
261
|
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
217
262
|
const currentLine = lines[cursorLine] || "";
|
|
@@ -332,6 +377,41 @@ export class CombinedAutocompleteProvider {
|
|
|
332
377
|
}
|
|
333
378
|
return path;
|
|
334
379
|
}
|
|
380
|
+
resolveScopedFuzzyQuery(rawQuery) {
|
|
381
|
+
const normalizedQuery = toDisplayPath(rawQuery);
|
|
382
|
+
const slashIndex = normalizedQuery.lastIndexOf("/");
|
|
383
|
+
if (slashIndex === -1) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
const displayBase = normalizedQuery.slice(0, slashIndex + 1);
|
|
387
|
+
const query = normalizedQuery.slice(slashIndex + 1);
|
|
388
|
+
let baseDir;
|
|
389
|
+
if (displayBase.startsWith("~/")) {
|
|
390
|
+
baseDir = this.expandHomePath(displayBase);
|
|
391
|
+
}
|
|
392
|
+
else if (displayBase.startsWith("/")) {
|
|
393
|
+
baseDir = displayBase;
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
baseDir = join(this.basePath, displayBase);
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
if (!statSync(baseDir).isDirectory()) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
return { baseDir, query, displayBase };
|
|
407
|
+
}
|
|
408
|
+
scopedPathForDisplay(displayBase, relativePath) {
|
|
409
|
+
const normalizedRelativePath = toDisplayPath(relativePath);
|
|
410
|
+
if (displayBase === "/") {
|
|
411
|
+
return `/${normalizedRelativePath}`;
|
|
412
|
+
}
|
|
413
|
+
return `${toDisplayPath(displayBase)}${normalizedRelativePath}`;
|
|
414
|
+
}
|
|
335
415
|
// Get file/directory suggestions for a given path prefix
|
|
336
416
|
getFileSuggestions(prefix) {
|
|
337
417
|
try {
|
|
@@ -406,7 +486,7 @@ export class CombinedAutocompleteProvider {
|
|
|
406
486
|
// If prefix ends with /, append entry to the prefix
|
|
407
487
|
relativePath = displayPrefix + name;
|
|
408
488
|
}
|
|
409
|
-
else if (displayPrefix.includes("/")) {
|
|
489
|
+
else if (displayPrefix.includes("/") || displayPrefix.includes("\\")) {
|
|
410
490
|
// Preserve ~/ format for home directory paths
|
|
411
491
|
if (displayPrefix.startsWith("~/")) {
|
|
412
492
|
const homeRelativeDir = displayPrefix.slice(2); // Remove ~/
|
|
@@ -425,6 +505,10 @@ export class CombinedAutocompleteProvider {
|
|
|
425
505
|
}
|
|
426
506
|
else {
|
|
427
507
|
relativePath = join(dirname(displayPrefix), name);
|
|
508
|
+
// path.join normalizes away ./ prefix, preserve it
|
|
509
|
+
if (displayPrefix.startsWith("./") && !relativePath.startsWith("./")) {
|
|
510
|
+
relativePath = `./${relativePath}`;
|
|
511
|
+
}
|
|
428
512
|
}
|
|
429
513
|
}
|
|
430
514
|
else {
|
|
@@ -436,6 +520,7 @@ export class CombinedAutocompleteProvider {
|
|
|
436
520
|
relativePath = name;
|
|
437
521
|
}
|
|
438
522
|
}
|
|
523
|
+
relativePath = toDisplayPath(relativePath);
|
|
439
524
|
const pathValue = isDirectory ? `${relativePath}/` : relativePath;
|
|
440
525
|
const value = buildCompletionValue(pathValue, {
|
|
441
526
|
isDirectory,
|
|
@@ -489,30 +574,35 @@ export class CombinedAutocompleteProvider {
|
|
|
489
574
|
return score;
|
|
490
575
|
}
|
|
491
576
|
// Fuzzy file search using fd (fast, respects .gitignore)
|
|
492
|
-
getFuzzyFileSuggestions(query, options) {
|
|
493
|
-
if (!this.fdPath) {
|
|
494
|
-
// fd not available, return empty results
|
|
577
|
+
async getFuzzyFileSuggestions(query, options) {
|
|
578
|
+
if (!this.fdPath || options.signal.aborted) {
|
|
495
579
|
return [];
|
|
496
580
|
}
|
|
497
581
|
try {
|
|
498
|
-
const
|
|
499
|
-
|
|
582
|
+
const scopedQuery = this.resolveScopedFuzzyQuery(query);
|
|
583
|
+
const fdBaseDir = scopedQuery?.baseDir ?? this.basePath;
|
|
584
|
+
const fdQuery = scopedQuery?.query ?? query;
|
|
585
|
+
const entries = await walkDirectoryWithFd(fdBaseDir, this.fdPath, fdQuery, 100, options.signal);
|
|
586
|
+
if (options.signal.aborted) {
|
|
587
|
+
return [];
|
|
588
|
+
}
|
|
500
589
|
const scoredEntries = entries
|
|
501
590
|
.map((entry) => ({
|
|
502
591
|
...entry,
|
|
503
|
-
score:
|
|
592
|
+
score: fdQuery ? this.scoreEntry(entry.path, fdQuery, entry.isDirectory) : 1,
|
|
504
593
|
}))
|
|
505
594
|
.filter((entry) => entry.score > 0);
|
|
506
|
-
// Sort by score (descending) and take top 20
|
|
507
595
|
scoredEntries.sort((a, b) => b.score - a.score);
|
|
508
596
|
const topEntries = scoredEntries.slice(0, 20);
|
|
509
|
-
// Build suggestions
|
|
510
597
|
const suggestions = [];
|
|
511
598
|
for (const { path: entryPath, isDirectory } of topEntries) {
|
|
512
|
-
// fd already includes trailing / for directories
|
|
513
599
|
const pathWithoutSlash = isDirectory ? entryPath.slice(0, -1) : entryPath;
|
|
600
|
+
const displayPath = scopedQuery
|
|
601
|
+
? this.scopedPathForDisplay(scopedQuery.displayBase, pathWithoutSlash)
|
|
602
|
+
: pathWithoutSlash;
|
|
514
603
|
const entryName = basename(pathWithoutSlash);
|
|
515
|
-
const
|
|
604
|
+
const completionPath = isDirectory ? `${displayPath}/` : displayPath;
|
|
605
|
+
const value = buildCompletionValue(completionPath, {
|
|
516
606
|
isDirectory,
|
|
517
607
|
isAtPrefix: true,
|
|
518
608
|
isQuotedPrefix: options.isQuotedPrefix,
|
|
@@ -520,7 +610,7 @@ export class CombinedAutocompleteProvider {
|
|
|
520
610
|
suggestions.push({
|
|
521
611
|
value,
|
|
522
612
|
label: entryName + (isDirectory ? "/" : ""),
|
|
523
|
-
description:
|
|
613
|
+
description: displayPath,
|
|
524
614
|
});
|
|
525
615
|
}
|
|
526
616
|
return suggestions;
|
|
@@ -529,27 +619,6 @@ export class CombinedAutocompleteProvider {
|
|
|
529
619
|
return [];
|
|
530
620
|
}
|
|
531
621
|
}
|
|
532
|
-
// Force file completion (called on Tab key) - always returns suggestions
|
|
533
|
-
getForceFileSuggestions(lines, cursorLine, cursorCol) {
|
|
534
|
-
const currentLine = lines[cursorLine] || "";
|
|
535
|
-
const textBeforeCursor = currentLine.slice(0, cursorCol);
|
|
536
|
-
// Don't trigger if we're typing a slash command at the start of the line
|
|
537
|
-
if (textBeforeCursor.trim().startsWith("/") && !textBeforeCursor.trim().includes(" ")) {
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
// Force extract path prefix - this will always return something
|
|
541
|
-
const pathMatch = this.extractPathPrefix(textBeforeCursor, true);
|
|
542
|
-
if (pathMatch !== null) {
|
|
543
|
-
const suggestions = this.getFileSuggestions(pathMatch);
|
|
544
|
-
if (suggestions.length === 0)
|
|
545
|
-
return null;
|
|
546
|
-
return {
|
|
547
|
-
items: suggestions,
|
|
548
|
-
prefix: pathMatch,
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
622
|
// Check if we should trigger file completion (called on Tab key)
|
|
554
623
|
shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
|
|
555
624
|
const currentLine = lines[cursorLine] || "";
|