Files
pnpm/completion/plugin-commands-completion/src/completionServer.ts
sadan4 a2591e8cda fix(shell-completion): give correct suggestions when command line ends with a space (#10607)
* fix(shell-completion): give correct suggestions when command line ends with a space

fixes an issue where if you tried to complete any part of any subcommand
with a space before <TAB> (eg: `pnpm run <TAB>`, `pnpm rm react <TAB>`),
pnpm would give you suggestions for the root command, as if you had
typed `pnpm <TAB>`

close #7964
close #5426

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-02-25 11:37:48 +01:00

58 lines
1.9 KiB
TypeScript

import { type CompletionItem, getShellFromEnv } from '@pnpm/tabtab'
import { type CompletionFunc } from '@pnpm/command'
import { split as splitCmd } from 'split-cmd/index.modern.mjs'
import tabtab from '@pnpm/tabtab'
import {
currentTypedWordType,
getLastOption,
} from './getOptionType.js'
import { type ParsedCliArgs } from '@pnpm/parse-cli-args'
import { complete } from './complete.js'
export function createCompletionServer (
opts: {
cliOptionsTypesByCommandName: Record<string, () => Record<string, unknown>>
completionByCommandName: Record<string, CompletionFunc>
initialCompletion: () => CompletionItem[]
shorthandsByCommandName: Record<string, Record<string, string | string[]>>
parseCliArgs: (args: string[]) => Promise<ParsedCliArgs>
universalOptionsTypes: Record<string, unknown>
universalShorthands: Record<string, string>
}
): () => Promise<void> {
return async () => {
const shell = getShellFromEnv(process.env)
const env = tabtab.parseEnv(process.env)
if (!env.complete) return
const inputArgv = splitCmd(stripPartialWord(env)).slice(1)
// We cannot autocomplete what a user types after "pnpm test --"
if (inputArgv.includes('--')) return
const { params, options, cmd } = await opts.parseCliArgs(inputArgv)
tabtab.log(
await complete(
opts,
{
cmd,
currentTypedWordType: currentTypedWordType(env),
lastOption: getLastOption(env),
options,
params,
}
),
shell
)
}
}
/**
* Returns the portion of the command line that consists of fully typed words,
*/
function stripPartialWord (env: { partial: string, lastPartial: string }): string {
if (env.lastPartial.length > 0) {
// stripping any word the user is currently typing.
return env.partial.slice(0, -env.lastPartial.length)
}
return env.partial
}