Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add terminal completions for other shell types #233776

Draft
wants to merge 117 commits into
base: main
Choose a base branch
from

Conversation

meganrogge
Copy link
Contributor

@meganrogge meganrogge commented Nov 13, 2024

fixes #233805
fixes #226562

Done:

@meganrogge
Copy link
Contributor Author

meganrogge commented Nov 14, 2024

Think I finally figured out the bundling issue

It did not like this

const module = await import(file);

But changing the tsconfig fixed it however, now running OSS, I see Activating extension 'vscode.terminal-suggest' failed: Cannot use import statement outside a module. 😢

@meganrogge
Copy link
Contributor Author

looks like some tests are failing / leaking disposables now... looking into it

@meganrogge
Copy link
Contributor Author

going to try to reproduce

Screenshot 2024-11-14 at 11 23 33 AM

@Tyriar
Copy link
Member

Tyriar commented Nov 14, 2024

But changing the tsconfig fixed it however, now running OSS, I see Activating extension 'vscode.terminal-suggest' failed: Cannot use import statement outside a module. 😢

You might need to hard code all the imports for now? I'm not sure how dynamic imports work with extensions.

Comment on lines +1 to +9
# Node npm

**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.

## Features

### Terminal suggest

Provides terminal suggestions for zsh and bash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to publish this up some before merging:

image

Comment on lines +24 to +32
"@fig/autocomplete-generators": "^2.4.0",
"find-up": "^5.0.0",
"find-yarn-workspace-root": "^2.0.0",
"jsonc-parser": "^3.2.0",
"minimatch": "^5.1.6",
"request-light": "^0.7.0",
"vscode-uri": "^3.0.8",
"which": "^4.0.0",
"which-pm": "^2.1.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all these?

Comment on lines +139 to +143
const enableExtensionCompletions = this._configurationService.getValue<ITerminalSuggestConfiguration>(terminalSuggestConfigSection).enableExtensionCompletions;
if (enableExtensionCompletions && !this._hasActivatedExtensions) {
await this._extensionService.activateByEvent('onTerminalCompletionsRequested');
this._hasActivatedExtensions = true;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to always activate this event? If the user requests completions, then installs an extension, it will remain deactivated currently I think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a bunch of listeners in _registerExtensionListeners now, but can we just fire the activation event and do away with that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no because if you restart the extension host for example, it won't get reset so it won't get activated without the listeners?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if you just call activate every time? I thought that's how it's done everywhere else, as opposed to trying to cover all the cases like you're doing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah other places just activate every time

}

export const terminalSuggestConfiguration: IStringDictionary<IConfigurationPropertySchema> = {
[TerminalSuggestSettingId.Enabled]: {
restricted: true,
markdownDescription: localize('suggest.enabled', "Enables experimental terminal Intellisense suggestions for supported shells ({0}) when {1} is set to {2}.\n\nIf shell integration is installed manually, {3} needs to be set to {4} before calling the shell integration script.", 'PowerShell v7+', `\`#${TerminalSettingId.ShellIntegrationEnabled}#\``, '`true`', '`VSCODE_SUGGEST`', '`1`'),
markdownDescription: localize('suggest.enabled', "Enables experimental terminal Intellisense suggestions for supported shells ({0}) when {1} is set to {2}.\n\nIf shell integration is installed manually, {3} needs to be set to {4} before calling the shell integration script. \n\nFor zsh and bash completions, {5} will also need to be set.", 'PowerShell v7+, zsh, bash', `\`#${TerminalSettingId.ShellIntegrationEnabled}#\``, '`true`', '`VSCODE_SUGGEST`', '`1`', `\`#${TerminalSuggestSettingId.EnableExtensionCompletions}#\``),
type: 'boolean',
default: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add experimental to this too

Comment on lines +117 to +118
// scored. If word is undefined, then match against the empty string.
item.word = word || '';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If word could be undefined wouldn't it throw above when word.length is called?

// TODO: Use Promise.all so all providers are called in parallel
for (const providerMap of this._providers.values()) {
for (const [extensionId, provider] of providerMap) {
const collectCompletions = async (providers: ITerminalCompletionProvider[]) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull into private member function?

@@ -57,31 +63,46 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo
});
}

async provideCompletions(promptValue: string, cursorPosition: number, shellType: TerminalShellType): Promise<ISimpleCompletion[] | undefined> {
async provideCompletions(promptValue: string, cursorPosition: number, shellType: TerminalShellType, token: CancellationToken, triggeredProviders?: ITerminalCompletionProvider[]): Promise<ISimpleCompletion[] | undefined> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing in triggeredProviders here is a little confusing. Could we not allow the terminal completion service complete control over what providers are used?

}

export interface ITerminalCompletionService {
_serviceBrand: undefined;
providers: ITerminalCompletionProvider[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
providers: ITerminalCompletionProvider[];
readonly providers: ITerminalCompletionProvider[];

export class TerminalCompletionService extends Disposable implements ITerminalCompletionService {
declare _serviceBrand: undefined;
private readonly _providers: Map</*ext id*/string, Map</*provider id*/string, ITerminalCompletionProvider>> = new Map();
get providers() { return [...this._providers.values()].flatMap(providerMap => [...providerMap.values()]); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A generator would be preferable here

@@ -165,13 +164,13 @@ export class SimpleCompletionModel {
} else {
// by default match `word` against the `label`
const match = scoreFn(word, wordLow, wordPos, item.completion.label, item.labelLow, 0, this._fuzzyScoreOptions);
if (!match) {
if (!match && word !== '') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this was needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match is undefined if the prompt is empty. this is part of the fix for #233805

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This worked before in pwsh fine though 🤔

@meganrogge
Copy link
Contributor Author

I reverted the tsconfig change for now so you can test in oss

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants