Skip to content

Commit 998ab6f

Browse files
authored
fix: prevent duplicate empty args in generated completions (#132)
1 parent 9db9d99 commit 998ab6f

8 files changed

Lines changed: 579 additions & 12 deletions

File tree

.changeset/fast-lamps-march.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@bomb.sh/tab': patch
3+
---
4+
5+
prevent duplicate empty args in generated completions

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
run: pnpm install
3535

3636
- name: Run tests
37-
run: pnpm test
37+
run: pnpm test:unit
3838

3939
typecheck:
4040
name: Lint and Type Check
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Shell Completions
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
shell-argv-protocol:
13+
name: Shell argv protocol
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Install shell dependencies
21+
shell: bash
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get install -y zsh fish wget apt-transport-https software-properties-common
25+
26+
- name: Install PowerShell
27+
shell: bash
28+
run: |
29+
source /etc/os-release
30+
wget -q https://packages.microsoft.com/config/ubuntu/$VERSION_ID/packages-microsoft-prod.deb
31+
sudo dpkg -i packages-microsoft-prod.deb
32+
rm packages-microsoft-prod.deb
33+
sudo apt-get update
34+
sudo apt-get install -y powershell
35+
36+
- name: Install pnpm
37+
uses: pnpm/action-setup@v4.0.0
38+
39+
- name: Set node version to 20
40+
uses: actions/setup-node@v4
41+
with:
42+
node-version: 20
43+
registry-url: https://registry.npmjs.org/
44+
cache: pnpm
45+
46+
- name: Install deps
47+
run: pnpm install
48+
49+
- name: Print shell versions
50+
shell: bash
51+
run: |
52+
bash --version
53+
zsh --version
54+
fish --version
55+
pwsh --version
56+
57+
- name: Run shell argv protocol tests
58+
run: pnpm test tests/shell-empty-argv.test.ts

examples/demo.t.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,26 @@ t.command('lint', 'Lint project').argument(
120120
true
121121
); // Variadic argument for multiple files
122122

123+
const supportedShells = ['zsh', 'bash', 'fish', 'powershell'];
124+
const completeUsage = 'Usage: vite complete <shell> | vite complete -- <argv>';
125+
126+
function printCompleteUsageAndExit() {
127+
console.error(completeUsage);
128+
process.exit(1);
129+
}
130+
123131
// Handle completion command
124132
if (process.argv[2] === 'complete') {
125-
const shell = process.argv[3];
126-
if (shell && ['zsh', 'bash', 'fish', 'powershell'].includes(shell)) {
127-
t.setup('vite', 'pnpm tsx examples/demo.t.ts', shell);
133+
const mode = process.argv[3];
134+
135+
if (mode === '--') {
136+
// Runtime completion request from the generated shell script.
137+
t.parse(process.argv.slice(4));
138+
} else if (mode && supportedShells.includes(mode)) {
139+
// Shell script generation.
140+
t.setup('vite', 'pnpm tsx examples/demo.t.ts', mode);
128141
} else {
129-
// Parse completion arguments (everything after --)
130-
const separatorIndex = process.argv.indexOf('--');
131-
const completionArgs =
132-
separatorIndex !== -1 ? process.argv.slice(separatorIndex + 1) : [];
133-
t.parse(completionArgs);
142+
printCompleteUsageAndExit();
134143
}
135144
} else {
136145
// Regular CLI usage (just show help for demo)

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
},
88
"scripts": {
99
"test": "vitest run",
10+
"test:unit": "vitest run --exclude tests/shell-empty-argv.test.ts",
11+
"test:shell": "vitest run tests/shell-empty-argv.test.ts",
1012
"type-check": "tsc --noEmit",
1113
"format": "prettier --write .",
1214
"format:check": "prettier --check .",

src/powershell.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,10 @@ export function generate(name: string, exec: string): string {
9393
# Remove the flag part
9494
$Flag, $WordToComplete = $WordToComplete.Split("=", 2)
9595
}
96-
97-
if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {
96+
$HasTrailingEmptyArg = $QuotedArgs -match "(^| )''$"
97+
__${name}_debug "HasTrailingEmptyArg: $HasTrailingEmptyArg"
98+
99+
if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag ) -And ( -Not $HasTrailingEmptyArg )) {
98100
# If the last parameter is complete (there is a space following it)
99101
# We add an extra empty parameter so we can indicate this to the go method.
100102
__${name}_debug "Adding extra empty parameter"

src/zsh.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ _${name}() {
4848
4949
# Prepare the command to obtain completions, ensuring arguments are quoted for eval
5050
local -a args_to_quote=("\${(@)words[2,-1]}")
51-
if [ "\${lastChar}" = "" ]; then
51+
if [ "\${lastChar}" = "" ] && [ "\${args_to_quote[-1]}" != "" ]; then
5252
# If the last parameter is complete (there is a space following it)
5353
# We add an extra empty parameter so we can indicate this to the go completion code.
5454
__${name}_debug "Adding extra empty parameter"

0 commit comments

Comments
 (0)