Skip to content

Commit 6c5e8aa

Browse files
committed
positionals nearly there
1 parent 82e0ec7 commit 6c5e8aa

File tree

5 files changed

+44
-21
lines changed

5 files changed

+44
-21
lines changed

demo.citty.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ for (const command of completion.commands.values()) {
6868

6969
if (command.name === 'lint') {
7070
command.handler = () => {
71+
console.log('lint handler')
7172
return [
7273
{ value: 'main.ts', description: 'Main file' },
7374
{ value: 'index.ts', description: 'Index file' },

src/citty.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,11 @@ export default async function tab<T extends ArgsDef = ArgsDef>(
163163
const args = (await resolve(instance.args))!;
164164
const parsed = parseArgs(extra, args);
165165
// TODO: this is not ideal at all
166-
const matchedCommand = parsed._.join(' ');
166+
const matchedCommand = parsed._.join(' ').trim();
167+
// TODO: `command lint i` does not work because `lint` and `i` are potential commands
168+
// instead the potential command should only be `lint`
169+
// and `i` is the to be completed part
170+
console.log('extra', parsed, ctx)
167171
return completion.parse(extra, matchedCommand);
168172
}
169173
}

src/index.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ type Option = {
8080
type Command = {
8181
name: string;
8282
description: string;
83+
args: boolean[]
8384
handler: Handler;
8485
options: Map<string, Option>;
8586
parent?: Command;
@@ -101,6 +102,7 @@ export class Completion {
101102
this.commands.set(key, {
102103
name: key,
103104
description,
105+
args,
104106
handler,
105107
options: new Map(),
106108
parent: parent ? this.commands.get(parent)! : undefined,
@@ -137,11 +139,10 @@ export class Completion {
137139
let toComplete = args[args.length - 1] || '';
138140
const previousArgs = args.slice(0, -1);
139141

140-
if (previousArgs.length > 0) {
141-
const lastPrevArg = previousArgs[previousArgs.length - 1];
142-
if (lastPrevArg.startsWith('--') && !endsWithSpace) {
143-
const { handler } = matchedCommand.options.get(lastPrevArg)!;
144-
if (handler) {
142+
const lastPrevArg = previousArgs[previousArgs.length - 1];
143+
if (lastPrevArg?.startsWith('--') && !endsWithSpace) {
144+
const { handler } = matchedCommand.options.get(lastPrevArg)!;
145+
if (handler) {
145146
const flagSuggestions = await handler(
146147
previousArgs,
147148
toComplete,
@@ -153,11 +154,7 @@ export class Completion {
153154
)
154155
);
155156
directive = ShellCompDirective.ShellCompDirectiveNoFileComp;
156-
// completions.forEach((comp) => );
157-
// console.log(`:${directive}`);
158-
// return;
159157
}
160-
}
161158
} else if (toComplete.startsWith('--')) {
162159
directive = ShellCompDirective.ShellCompDirectiveNoFileComp;
163160
const equalsIndex = toComplete.indexOf('=');
@@ -215,13 +212,15 @@ export class Completion {
215212
}
216213
} else {
217214
const potentialCommandParts = potentialCommand.split(' ');
215+
console.log(potentialCommandParts)
218216
for (const [k, v] of this.commands) {
219217
// if the command is root, skip it
220218
if (k === '') {
221219
continue;
222220
}
223221

224-
const parts = k.split(' ');
222+
const parts = [...k.split(' '), ...v.args];
223+
console.log(parts)
225224
for (let i = 0; i < parts.length; i++) {
226225
const part = parts[i];
227226
const potentialPart = potentialCommandParts[i] || '';
@@ -233,13 +232,35 @@ export class Completion {
233232
break;
234233
}
235234

235+
async function callHandler() {
236+
console.log(matchedCommand)
237+
console.log('callHandler', previousArgs, toComplete, endsWithSpace)
238+
completions.push(...await matchedCommand.handler?.(
239+
previousArgs,
240+
toComplete,
241+
endsWithSpace
242+
))
243+
}
244+
236245
// If we're at the current word being completed
237246
if (i === potentialCommandParts.length - 1) {
238-
// Only add if it matches the current partial input
239-
if (part.startsWith(potentialPart)) {
240-
completions.push({ value: part, description: v.description });
247+
if (endsWithSpace) {
248+
const nextPart = parts[i + 1]
249+
if (typeof nextPart === 'boolean') {
250+
await callHandler()
251+
}
252+
} else {
253+
// Only add if it matches the current partial input
254+
console.log('part', part, potentialPart)
255+
if (typeof part === 'boolean') {
256+
await callHandler()
257+
} else if (part.startsWith(potentialPart)) {
258+
completions.push({ value: part, description: v.description });
259+
}
241260
}
242261
break;
262+
} else if (i === parts.length - 1 && part === true) { // variadic
263+
await callHandler()
243264
}
244265

245266
// For previous parts, they must match exactly

tests/__snapshots__/cli.test.ts.snap

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ exports[`cli completion tests for citty > cli option completion tests > should c
77
`;
88

99
exports[`cli completion tests for citty > cli option exclusion tests > should not suggest already specified option '{ specified: '--config', shouldNotContain: '--config' }' 1`] = `
10-
"--mode Set env mode
11-
--logLevel info | warn | error | silent
12-
:4
10+
":4
1311
"
1412
`;
1513

@@ -63,8 +61,7 @@ exports[`cli completion tests for citty > positional argument completions > shou
6361
`;
6462

6563
exports[`cli completion tests for citty > should complete cli options 1`] = `
66-
"here
67-
dev Start dev server
64+
"dev Start dev server
6865
lint Lint project
6966
:4
7067
"

tests/cli.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,13 @@ describe.each(cliTools)('cli completion tests for %s', (cliTool) => {
123123

124124
describe('positional argument completions', () => {
125125
it('should complete single positional argument when ending with space (vite src/)', async () => {
126-
const command = `${commandPrefix} vite src/ ""`;
126+
const command = `${commandPrefix} lint src/ ""`;
127127
const output = await runCommand(command);
128128
expect(output).toMatchSnapshot();
129129
});
130130

131131
it('should complete multiple positional arguments when ending with space (vite src/ ./)', async () => {
132-
const command = `${commandPrefix} vite ""`;
132+
const command = `${commandPrefix} lint ""`;
133133
const output = await runCommand(command);
134134
expect(output).toMatchSnapshot();
135135
});

0 commit comments

Comments
 (0)