add support for file/folder terminal completions (#234289)

This commit is contained in:
Megan Rogge 2024-11-20 20:35:55 -05:00 коммит произвёл GitHub
Родитель ee21e638be
Коммит d79858c114
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
8 изменённых файлов: 338 добавлений и 111 удалений

Просмотреть файл

@ -20,11 +20,13 @@ function getBuiltinCommands(shell: string): string[] | undefined {
if (cachedCommands) {
return cachedCommands;
}
// fixes a bug with file/folder completions brought about by the '.' command
const filter = (cmd: string) => cmd && cmd !== '.';
const options: ExecOptionsWithStringEncoding = { encoding: 'utf-8', shell };
switch (shellType) {
case 'bash': {
const bashOutput = execSync('compgen -b', options);
const bashResult = bashOutput.split('\n').filter(cmd => cmd);
const bashResult = bashOutput.split('\n').filter(filter);
if (bashResult.length) {
cachedBuiltinCommands?.set(shellType, bashResult);
return bashResult;
@ -33,7 +35,7 @@ function getBuiltinCommands(shell: string): string[] | undefined {
}
case 'zsh': {
const zshOutput = execSync('printf "%s\\n" ${(k)builtins}', options);
const zshResult = zshOutput.split('\n').filter(cmd => cmd);
const zshResult = zshOutput.split('\n').filter(filter);
if (zshResult.length) {
cachedBuiltinCommands?.set(shellType, zshResult);
return zshResult;
@ -43,7 +45,7 @@ function getBuiltinCommands(shell: string): string[] | undefined {
// TODO: ghost text in the command line prevents
// completions from working ATM for fish
const fishOutput = execSync('functions -n', options);
const fishResult = fishOutput.split(', ').filter(cmd => cmd);
const fishResult = fishOutput.split(', ').filter(filter);
if (fishResult.length) {
cachedBuiltinCommands?.set(shellType, fishResult);
return fishResult;
@ -64,122 +66,81 @@ function getBuiltinCommands(shell: string): string[] | undefined {
export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.window.registerTerminalCompletionProvider({
id: 'terminal-suggest',
async provideTerminalCompletions(terminal: vscode.Terminal, terminalContext: { commandLine: string; cursorPosition: number }, token: vscode.CancellationToken): Promise<vscode.TerminalCompletionItem[] | undefined> {
async provideTerminalCompletions(terminal: vscode.Terminal, terminalContext: { commandLine: string; cursorPosition: number }, token: vscode.CancellationToken): Promise<vscode.TerminalCompletionItem[] | vscode.TerminalCompletionList | undefined> {
if (token.isCancellationRequested) {
return;
}
const availableCommands = await getCommandsInPath();
if (!availableCommands) {
return;
}
// TODO: Leverage shellType when available https://github.com/microsoft/vscode/issues/230165
const shellPath = 'shellPath' in terminal.creationOptions ? terminal.creationOptions.shellPath : vscode.env.shell;
if (!shellPath) {
return;
}
const commandsInPath = await getCommandsInPath();
const builtinCommands = getBuiltinCommands(shellPath);
builtinCommands?.forEach(command => availableCommands.add(command));
const prefix = getPrefix(terminalContext.commandLine, terminalContext.cursorPosition);
let result: vscode.TerminalCompletionItem[] = [];
const specs = [codeCompletionSpec, codeInsidersCompletionSpec];
for (const spec of specs) {
const specName = getLabel(spec);
if (!specName || !availableCommands.has(specName)) {
continue;
}
if (terminalContext.commandLine.startsWith(specName)) {
if ('options' in codeInsidersCompletionSpec && codeInsidersCompletionSpec.options) {
for (const option of codeInsidersCompletionSpec.options) {
const optionLabel = getLabel(option);
if (!optionLabel) {
continue;
}
if (optionLabel.startsWith(prefix) || (prefix.length > specName.length && prefix.trim() === specName)) {
result.push(createCompletionItem(terminalContext.cursorPosition, prefix, optionLabel, option.description, false, vscode.TerminalCompletionItemKind.Flag));
}
if (option.args !== undefined) {
const args = Array.isArray(option.args) ? option.args : [option.args];
for (const arg of args) {
if (!arg) {
continue;
}
if (arg.template) {
// TODO: return file/folder completion items
if (arg.template === 'filepaths') {
// if (label.startsWith(prefix+\s*)) {
// result.push(FilePathCompletionItem)
// }
} else if (arg.template === 'folders') {
// if (label.startsWith(prefix+\s*)) {
// result.push(FolderPathCompletionItem)
// }
}
continue;
}
const precedingText = terminalContext.commandLine.slice(0, terminalContext.cursorPosition);
const expectedText = `${optionLabel} `;
if (arg.suggestions?.length && precedingText.includes(expectedText)) {
// there are specific suggestions to show
result = [];
const indexOfPrecedingText = terminalContext.commandLine.lastIndexOf(expectedText);
const currentPrefix = precedingText.slice(indexOfPrecedingText + expectedText.length);
for (const suggestion of arg.suggestions) {
const suggestionLabel = getLabel(suggestion);
if (suggestionLabel && suggestionLabel.startsWith(currentPrefix)) {
const hasSpaceBeforeCursor = terminalContext.commandLine[terminalContext.cursorPosition - 1] === ' ';
// prefix will be '' if there is a space before the cursor
result.push(createCompletionItem(terminalContext.cursorPosition, precedingText, suggestionLabel, arg.name, hasSpaceBeforeCursor, vscode.TerminalCompletionItemKind.Argument));
}
}
if (result.length) {
return result;
}
}
}
}
}
}
}
if (!commandsInPath || !builtinCommands) {
return;
}
const commands = [...commandsInPath, ...builtinCommands];
for (const command of availableCommands) {
if (command.startsWith(prefix)) {
result.push(createCompletionItem(terminalContext.cursorPosition, prefix, command));
const items: vscode.TerminalCompletionItem[] = [];
const prefix = getPrefix(terminalContext.commandLine, terminalContext.cursorPosition);
const specs = [codeCompletionSpec, codeInsidersCompletionSpec];
const specCompletions = await getCompletionItemsFromSpecs(specs, terminalContext, new Set(commands), prefix, token);
let filesRequested = specCompletions.filesRequested;
let foldersRequested = specCompletions.foldersRequested;
items.push(...specCompletions.items);
if (!specCompletions.specificSuggestionsProvided) {
for (const command of commands) {
if (command.startsWith(prefix)) {
items.push(createCompletionItem(terminalContext.cursorPosition, prefix, command));
}
}
}
if (token.isCancellationRequested) {
return undefined;
}
const uniqueResults = new Map<string, vscode.TerminalCompletionItem>();
for (const item of result) {
for (const item of items) {
if (!uniqueResults.has(item.label)) {
uniqueResults.set(item.label, item);
}
}
return uniqueResults.size ? Array.from(uniqueResults.values()) : undefined;
const resultItems = uniqueResults.size ? Array.from(uniqueResults.values()) : undefined;
// If no completions are found, the prefix is a path, and neither files nor folders
// are going to be requested (for a specific spec's argument), show file/folder completions
const shouldShowResourceCompletions = !resultItems?.length && prefix.match(/^[./\\ ]/) && !filesRequested && !foldersRequested;
if (shouldShowResourceCompletions) {
filesRequested = true;
foldersRequested = true;
}
if (filesRequested || foldersRequested) {
return new vscode.TerminalCompletionList(resultItems, { filesRequested, foldersRequested, cwd: terminal.shellIntegration?.cwd, pathSeparator: shellPath.includes('/') ? '/' : '\\' });
}
return resultItems;
}
}));
}
function getLabel(spec: Fig.Spec | Fig.Arg | Fig.Suggestion | string): string | undefined {
function getLabel(spec: Fig.Spec | Fig.Arg | Fig.Suggestion | string): string[] | undefined {
if (typeof spec === 'string') {
return spec;
return [spec];
}
if (typeof spec.name === 'string') {
return spec.name;
return [spec.name];
}
if (!Array.isArray(spec.name) || spec.name.length === 0) {
return;
}
return spec.name[0];
return spec.name;
}
function createCompletionItem(cursorPosition: number, prefix: string, label: string, description?: string, hasSpaceBeforeCursor?: boolean, kind?: vscode.TerminalCompletionItemKind): vscode.TerminalCompletionItem {
@ -245,3 +206,89 @@ function getPrefix(commandLine: string, cursorPosition: number): string {
return match ? match[0] : '';
}
export function asArray<T>(x: T | T[]): T[];
export function asArray<T>(x: T | readonly T[]): readonly T[];
export function asArray<T>(x: T | T[]): T[] {
return Array.isArray(x) ? x : [x];
}
function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalContext: { commandLine: string; cursorPosition: number }, availableCommands: Set<string>, prefix: string, token: vscode.CancellationToken): { items: vscode.TerminalCompletionItem[]; filesRequested: boolean; foldersRequested: boolean; specificSuggestionsProvided: boolean } {
let items: vscode.TerminalCompletionItem[] = [];
let filesRequested = false;
let foldersRequested = false;
for (const spec of specs) {
const specLabels = getLabel(spec);
if (!specLabels) {
continue;
}
for (const specLabel of specLabels) {
if (!availableCommands.has(specLabel) || token.isCancellationRequested) {
continue;
}
if (terminalContext.commandLine.startsWith(specLabel)) {
if ('options' in spec && spec.options) {
for (const option of spec.options) {
const optionLabels = getLabel(option);
if (!optionLabels) {
continue;
}
for (const optionLabel of optionLabels) {
if (optionLabel.startsWith(prefix) || (prefix.length > specLabel.length && prefix.trim() === specLabel)) {
items.push(createCompletionItem(terminalContext.cursorPosition, prefix, optionLabel, option.description, false, vscode.TerminalCompletionItemKind.Flag));
}
if (!option.args) {
continue;
}
const args = asArray(option.args);
for (const arg of args) {
if (!arg) {
continue;
}
const precedingText = terminalContext.commandLine.slice(0, terminalContext.cursorPosition + 1);
const expectedText = `${specLabel} ${optionLabel} `;
if (!precedingText.includes(expectedText)) {
continue;
}
if (arg.template) {
if (arg.template === 'filepaths') {
if (precedingText.includes(expectedText)) {
filesRequested = true;
}
} else if (arg.template === 'folders') {
if (precedingText.includes(expectedText)) {
foldersRequested = true;
}
}
}
if (arg.suggestions?.length) {
// there are specific suggestions to show
items = [];
const indexOfPrecedingText = terminalContext.commandLine.lastIndexOf(expectedText);
const currentPrefix = precedingText.slice(indexOfPrecedingText + expectedText.length);
for (const suggestion of arg.suggestions) {
const suggestionLabels = getLabel(suggestion);
if (!suggestionLabels) {
continue;
}
for (const suggestionLabel of suggestionLabels) {
if (suggestionLabel && suggestionLabel.startsWith(currentPrefix.trim())) {
const hasSpaceBeforeCursor = terminalContext.commandLine[terminalContext.cursorPosition - 1] === ' ';
// prefix will be '' if there is a space before the cursor
items.push(createCompletionItem(terminalContext.cursorPosition, precedingText, suggestionLabel, arg.name, hasSpaceBeforeCursor, vscode.TerminalCompletionItemKind.Argument));
}
}
}
if (items.length) {
return { items, filesRequested, foldersRequested, specificSuggestionsProvided: true };
}
}
}
}
}
}
}
}
}
return { items, filesRequested, foldersRequested, specificSuggestionsProvided: false };
}

Просмотреть файл

@ -1667,6 +1667,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
TerminalShellExecutionCommandLineConfidence: extHostTypes.TerminalShellExecutionCommandLineConfidence,
TerminalCompletionItem: extHostTypes.TerminalCompletionItem,
TerminalCompletionItemKind: extHostTypes.TerminalCompletionItemKind,
TerminalCompletionList: extHostTypes.TerminalCompletionList,
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
TextEdit: extHostTypes.TextEdit,
SnippetTextEdit: extHostTypes.SnippetTextEdit,

Просмотреть файл

@ -84,7 +84,7 @@ import { IFileQueryBuilderOptions, ITextQueryBuilderOptions } from '../../servic
import * as search from '../../services/search/common/search.js';
import { TextSearchCompleteMessage } from '../../services/search/common/searchExtTypes.js';
import { ISaveProfileResult } from '../../services/userDataProfile/common/userDataProfile.js';
import { TerminalCompletionItem, TerminalShellExecutionCommandLineConfidence } from './extHostTypes.js';
import { TerminalCompletionItem, TerminalCompletionList, TerminalShellExecutionCommandLineConfidence } from './extHostTypes.js';
import * as tasks from './shared/tasks.js';
export interface IWorkspaceData extends IStaticWorkspaceData {
@ -2430,7 +2430,7 @@ export interface ExtHostTerminalServiceShape {
$acceptDefaultProfile(profile: ITerminalProfile, automationProfile: ITerminalProfile): void;
$createContributedProfileTerminal(id: string, options: ICreateContributedTerminalProfileOptions): Promise<void>;
$provideTerminalQuickFixes(id: string, matchResult: TerminalCommandMatchResultDto, token: CancellationToken): Promise<SingleOrMany<TerminalQuickFix> | undefined>;
$provideTerminalCompletions(id: string, options: ITerminalCompletionContextDto, token: CancellationToken): Promise<TerminalCompletionItem[] | undefined>;
$provideTerminalCompletions(id: string, options: ITerminalCompletionContextDto, token: CancellationToken): Promise<TerminalCompletionItem[] | TerminalCompletionList | undefined>;
}
export interface ExtHostTerminalShellIntegrationShape {

Просмотреть файл

@ -56,7 +56,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID
getEnvironmentVariableCollection(extension: IExtensionDescription): IEnvironmentVariableCollection;
getTerminalById(id: number): ExtHostTerminal | null;
getTerminalIdByApiObject(apiTerminal: vscode.Terminal): number | null;
registerTerminalCompletionProvider<T extends vscode.TerminalCompletionItem[]>(extension: IExtensionDescription, provider: vscode.TerminalCompletionProvider<vscode.TerminalCompletionItem>, ...triggerCharacters: string[]): vscode.Disposable;
registerTerminalCompletionProvider(extension: IExtensionDescription, provider: vscode.TerminalCompletionProvider<vscode.TerminalCompletionItem>, ...triggerCharacters: string[]): vscode.Disposable;
}
interface IEnvironmentVariableCollection extends vscode.EnvironmentVariableCollection {
@ -746,7 +746,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
});
}
public async $provideTerminalCompletions(id: string, options: ITerminalCompletionContextDto): Promise<vscode.TerminalCompletionItem[] | undefined> {
public async $provideTerminalCompletions(id: string, options: ITerminalCompletionContextDto): Promise<vscode.TerminalCompletionItem[] | vscode.TerminalCompletionList | undefined> {
const token = new CancellationTokenSource().token;
if (token.isCancellationRequested || !this.activeTerminal) {
return undefined;

Просмотреть файл

@ -2145,6 +2145,41 @@ export class TerminalCompletionItem implements vscode.TerminalCompletionItem {
}
/**
* Represents a collection of {@link CompletionItem completion items} to be presented
* in the editor.
*/
export class TerminalCompletionList<T extends TerminalCompletionItem = TerminalCompletionItem> {
/**
* Resources should be shown in the completions list
*/
resourceRequestConfig?: TerminalResourceRequestConfig;
/**
* The completion items.
*/
items: T[];
/**
* Creates a new completion list.
*
* @param items The completion items.
* @param isIncomplete The list is not complete.
*/
constructor(items?: T[], resourceRequestConfig?: TerminalResourceRequestConfig) {
this.items = items ?? [];
this.resourceRequestConfig = resourceRequestConfig;
}
}
export interface TerminalResourceRequestConfig {
filesRequested?: boolean;
foldersRequested?: boolean;
cwd?: vscode.Uri;
pathSeparator: string;
}
export enum TaskRevealKind {
Always = 1,

Просмотреть файл

@ -4,7 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from '../../../../../base/common/cancellation.js';
import { Disposable, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js';
import { URI } from '../../../../../base/common/uri.js';
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { IFileService } from '../../../../../platform/files/common/files.js';
import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
import { TerminalSettingId, TerminalShellType } from '../../../../../platform/terminal/common/terminal.js';
import { ISimpleCompletion } from '../../../../services/suggest/browser/simpleCompletionItem.js';
@ -24,10 +26,47 @@ export interface ITerminalCompletion extends ISimpleCompletion {
kind?: TerminalCompletionItemKind;
}
/**
* Represents a collection of {@link CompletionItem completion items} to be presented
* in the editor.
*/
export class TerminalCompletionList<ITerminalCompletion> {
/**
* Resources should be shown in the completions list
*/
resourceRequestConfig?: TerminalResourceRequestConfig;
/**
* The completion items.
*/
items?: ITerminalCompletion[];
/**
* Creates a new completion list.
*
* @param items The completion items.
* @param isIncomplete The list is not complete.
*/
constructor(items?: ITerminalCompletion[], resourceRequestConfig?: TerminalResourceRequestConfig) {
this.items = items;
this.resourceRequestConfig = resourceRequestConfig;
}
}
export interface TerminalResourceRequestConfig {
filesRequested?: boolean;
foldersRequested?: boolean;
cwd?: URI;
pathSeparator: string;
}
export interface ITerminalCompletionProvider {
id: string;
shellTypes?: TerminalShellType[];
provideCompletions(value: string, cursorPosition: number, token: CancellationToken): Promise<ISimpleCompletion[] | undefined>;
provideCompletions(value: string, cursorPosition: number, token: CancellationToken): Promise<ITerminalCompletion[] | TerminalCompletionList<ITerminalCompletion> | undefined>;
triggerCharacters?: string[];
isBuiltin?: boolean;
}
@ -55,7 +94,9 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo
}
}
constructor(@IConfigurationService private readonly _configurationService: IConfigurationService) {
constructor(@IConfigurationService private readonly _configurationService: IConfigurationService,
@IFileService private readonly _fileService: IFileService
) {
super();
}
@ -79,9 +120,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo
});
}
async provideCompletions(promptValue: string, cursorPosition: number, shellType: TerminalShellType, token: CancellationToken, triggerCharacter?: boolean): Promise<ISimpleCompletion[] | undefined> {
const completionItems: ISimpleCompletion[] = [];
async provideCompletions(promptValue: string, cursorPosition: number, shellType: TerminalShellType, token: CancellationToken, triggerCharacter?: boolean): Promise<ITerminalCompletion[] | undefined> {
if (!this._providers || !this._providers.values) {
return undefined;
}
@ -110,31 +149,93 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo
providers = providers.filter(p => p.isBuiltin);
}
await this._collectCompletions(providers, shellType, promptValue, cursorPosition, completionItems, token);
return completionItems.length > 0 ? completionItems : undefined;
return this._collectCompletions(providers, shellType, promptValue, cursorPosition, token);
}
private async _collectCompletions(providers: ITerminalCompletionProvider[], shellType: TerminalShellType, promptValue: string, cursorPosition: number, completionItems: ISimpleCompletion[], token: CancellationToken) {
private async _collectCompletions(providers: ITerminalCompletionProvider[], shellType: TerminalShellType, promptValue: string, cursorPosition: number, token: CancellationToken): Promise<ITerminalCompletion[] | undefined> {
const completionPromises = providers.map(async provider => {
if (provider.shellTypes && !provider.shellTypes.includes(shellType)) {
return [];
return undefined;
}
const completions: ITerminalCompletion[] | TerminalCompletionList<ITerminalCompletion> | undefined = await provider.provideCompletions(promptValue, cursorPosition, token);
if (!completions) {
return undefined;
}
const completions = await provider.provideCompletions(promptValue, cursorPosition, token);
const devModeEnabled = this._configurationService.getValue(TerminalSettingId.DevMode);
if (completions) {
return completions.map(completion => {
if (devModeEnabled && !completion.detail?.includes(provider.id)) {
completion.detail = `(${provider.id}) ${completion.detail ?? ''}`;
}
return completion;
});
const completionItems = Array.isArray(completions) ? completions : completions.items ?? [];
const itemsWithModifiedLabels = completionItems.map(completion => {
if (devModeEnabled && !completion.detail?.includes(provider.id)) {
completion.detail = `(${provider.id}) ${completion.detail ?? ''}`;
}
return completion;
});
if (Array.isArray(completions)) {
return itemsWithModifiedLabels;
}
return [];
if (completions.resourceRequestConfig) {
const resourceCompletions = await this._resolveResources(completions.resourceRequestConfig, promptValue, cursorPosition);
if (resourceCompletions) {
itemsWithModifiedLabels.push(...resourceCompletions);
}
return itemsWithModifiedLabels;
}
return;
});
const results = await Promise.all(completionPromises);
results.forEach(completions => completionItems.push(...completions));
return results.filter(result => !!result).flat();
}
private async _resolveResources(resourceRequestConfig: TerminalResourceRequestConfig, promptValue: string, cursorPosition: number): Promise<ITerminalCompletion[] | undefined> {
const cwd = URI.revive(resourceRequestConfig.cwd);
const foldersRequested = resourceRequestConfig.foldersRequested ?? false;
const filesRequested = resourceRequestConfig.filesRequested ?? false;
if (!cwd || (!foldersRequested && !filesRequested)) {
return;
}
const resourceCompletions: ITerminalCompletion[] = [];
const fileStat = await this._fileService.resolve(cwd, { resolveSingleChildDescendants: true });
if (!fileStat || !fileStat?.children) {
return;
}
for (const stat of fileStat.children) {
let kind: TerminalCompletionItemKind | undefined;
if (foldersRequested && stat.isDirectory) {
kind = TerminalCompletionItemKind.Folder;
}
if (filesRequested && !stat.isDirectory && (stat.isFile || stat.resource.scheme === 'file')) {
kind = TerminalCompletionItemKind.File;
}
if (kind === undefined) {
continue;
}
const lastWord = promptValue.substring(0, cursorPosition).split(' ').pop();
const lastIndexOfDot = lastWord?.lastIndexOf('.') ?? -1;
const lastIndexOfSlash = lastWord?.lastIndexOf(resourceRequestConfig.pathSeparator) ?? -1;
let label;
if (lastIndexOfSlash > -1) {
label = stat.resource.fsPath.replace(cwd.fsPath, '').substring(1);
} else if (lastIndexOfDot === -1) {
label = '.' + stat.resource.fsPath.replace(cwd.fsPath, '');
} else {
label = stat.resource.fsPath.replace(cwd.fsPath, '');
}
resourceCompletions.push({
label,
kind,
isDirectory: kind === TerminalCompletionItemKind.Folder,
isFile: kind === TerminalCompletionItemKind.File,
replacementIndex: cursorPosition,
replacementLength: label.length
});
}
return resourceCompletions.length ? resourceCompletions : undefined;
}
}

Просмотреть файл

@ -202,7 +202,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
normalizedLeadingLineContent = normalizePathSeparator(normalizedLeadingLineContent, this._pathSeparator);
}
for (const completion of completions) {
if (!completion.icon && completion.kind) {
if (!completion.icon && completion.kind !== undefined) {
completion.icon = this._kindToIconMap.get(completion.kind);
}
}

Просмотреть файл

@ -16,10 +16,9 @@ declare module 'vscode' {
* @param token A cancellation token.
* @return A list of completions.
*/
provideTerminalCompletions(terminal: Terminal, context: TerminalCompletionContext, token: CancellationToken): ProviderResult<T[]>;
provideTerminalCompletions(terminal: Terminal, context: TerminalCompletionContext, token: CancellationToken): ProviderResult<T[] | TerminalCompletionList<T>>;
}
export interface TerminalCompletionItem {
/**
* The label of the completion.
@ -80,4 +79,48 @@ declare module 'vscode' {
*/
export function registerTerminalCompletionProvider<T extends TerminalCompletionItem>(provider: TerminalCompletionProvider<T>, ...triggerCharacters: string[]): Disposable;
}
/**
* Represents a collection of {@link TerminalCompletionItem completion items} to be presented
* in the terminal.
*/
export class TerminalCompletionList<T extends TerminalCompletionItem = TerminalCompletionItem> {
/**
* Resources that should be shown in the completions list for the cwd of the terminal.
*/
resourceRequestConfig?: TerminalResourceRequestConfig;
/**
* The completion items.
*/
items: T[];
/**
* Creates a new completion list.
*
* @param items The completion items.
* @param resourceRequestConfig Indicates which resources should be shown as completions for the cwd of the terminal.
*/
constructor(items?: T[], resourceRequestConfig?: TerminalResourceRequestConfig);
}
export interface TerminalResourceRequestConfig {
/**
* Show files as completion items.
*/
filesRequested?: boolean;
/**
* Show folders as completion items.
*/
foldersRequested?: boolean;
/**
* If no cwd is provided, no resources will be shown as completions.
*/
cwd?: Uri;
/**
* The path separator to use when constructing paths.
*/
pathSeparator: string;
}
}