Skip to content

Commit

Permalink
Merge pull request #171648 from microsoft/tyriar/154662
Browse files Browse the repository at this point in the history
Pwsh autocomplete
  • Loading branch information
Tyriar committed Jan 19, 2023
2 parents 0815aad + 70ed6fa commit df931c9
Show file tree
Hide file tree
Showing 25 changed files with 2,217 additions and 20 deletions.
4 changes: 4 additions & 0 deletions build/lib/i18n.resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,10 @@
"name": "vs/workbench/services/search",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/services/suggest",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/services/textfile",
"project": "vscode-workbench"
Expand Down
6 changes: 3 additions & 3 deletions src/vs/editor/contrib/suggest/browser/suggestWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { isHighContrast } from 'vs/platform/theme/common/theme';
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { CompletionModel } from './completionModel';
import { ResizableHTMLElement } from 'vs/base/browser/ui/resizable/resizable';
import { CompletionItem, Context as SuggestContext } from './suggest';
import { CompletionItem, Context as SuggestContext, suggestWidgetStatusbarMenu } from './suggest';
import { canExpandCompletionItem, SuggestDetailsOverlay, SuggestDetailsWidget } from './suggestWidgetDetails';
import { getAriaId, ItemRenderer } from './suggestWidgetRenderer';
import { getListStyles } from 'vs/platform/theme/browser/defaultStyles';
Expand All @@ -43,7 +43,7 @@ registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: e
const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hcDark: editorForeground, hcLight: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.'));
registerColor('editorSuggestWidget.selectedForeground', { dark: quickInputListFocusForeground, light: quickInputListFocusForeground, hcDark: quickInputListFocusForeground, hcLight: quickInputListFocusForeground }, nls.localize('editorSuggestWidgetSelectedForeground', 'Foreground color of the selected entry in the suggest widget.'));
registerColor('editorSuggestWidget.selectedIconForeground', { dark: quickInputListFocusIconForeground, light: quickInputListFocusIconForeground, hcDark: quickInputListFocusIconForeground, hcLight: quickInputListFocusIconForeground }, nls.localize('editorSuggestWidgetSelectedIconForeground', 'Icon foreground color of the selected entry in the suggest widget.'));
const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hcDark: quickInputListFocusBackground, hcLight: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hcDark: quickInputListFocusBackground, hcLight: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.'));
registerColor('editorSuggestWidget.focusHighlightForeground', { dark: listFocusHighlightForeground, light: listFocusHighlightForeground, hcDark: listFocusHighlightForeground, hcLight: listFocusHighlightForeground }, nls.localize('editorSuggestWidgetFocusHighlightForeground', 'Color of the match highlights in the suggest widget when an item is focused.'));
registerColor('editorSuggestWidgetStatus.foreground', { dark: transparent(editorSuggestWidgetForeground, .5), light: transparent(editorSuggestWidgetForeground, .5), hcDark: transparent(editorSuggestWidgetForeground, .5), hcLight: transparent(editorSuggestWidgetForeground, .5) }, nls.localize('editorSuggestWidgetStatusForeground', 'Foreground color of the suggest widget status.'));
Expand Down Expand Up @@ -264,7 +264,7 @@ export class SuggestWidget implements IDisposable {
listInactiveFocusOutline: activeContrastBorder
}));

this._status = instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode);
this._status = instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode, suggestWidgetStatusbarMenu);
const applyStatusBarStyle = () => this.element.domNode.classList.toggle('with-status-bar', this.editor.getOption(EditorOption.suggest).showStatusBar);
applyStatusBarStyle();

Expand Down
8 changes: 4 additions & 4 deletions src/vs/editor/contrib/suggest/browser/suggestWidgetStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar
import { IAction } from 'vs/base/common/actions';
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { suggestWidgetStatusbarMenu } from 'vs/editor/contrib/suggest/browser/suggest';
import { localize } from 'vs/nls';
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions';
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

Expand All @@ -23,7 +22,7 @@ class StatusBarViewItem extends MenuEntryActionViewItem {
return super.updateLabel();
}
if (this.label) {
this.label.textContent = localize('ddd', '{0} ({1})', this._action.label, StatusBarViewItem.symbolPrintEnter(kb));
this.label.textContent = localize({ key: 'content', comment: ['A label', 'A keybinding'] }, '{0} ({1})', this._action.label, StatusBarViewItem.symbolPrintEnter(kb));
}
}

Expand All @@ -42,6 +41,7 @@ export class SuggestWidgetStatus {

constructor(
container: HTMLElement,
private readonly _menuId: MenuId,
@IInstantiationService instantiationService: IInstantiationService,
@IMenuService private _menuService: IMenuService,
@IContextKeyService private _contextKeyService: IContextKeyService,
Expand All @@ -64,7 +64,7 @@ export class SuggestWidgetStatus {
}

show(): void {
const menu = this._menuService.createMenu(suggestWidgetStatusbarMenu, this._contextKeyService);
const menu = this._menuService.createMenu(this._menuId, this._contextKeyService);
const renderMenu = () => {
const left: IAction[] = [];
const right: IAction[] = [];
Expand Down
2 changes: 2 additions & 0 deletions src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export const enum TerminalSettingId {
ShellIntegrationShowWelcome = 'terminal.integrated.shellIntegration.showWelcome',
ShellIntegrationDecorationsEnabled = 'terminal.integrated.shellIntegration.decorationsEnabled',
ShellIntegrationCommandHistory = 'terminal.integrated.shellIntegration.history',
ShellIntegrationSuggestEnabled = 'terminal.integrated.shellIntegration.suggestEnabled',
SmoothScrolling = 'terminal.integrated.smoothScrolling'
}

Expand Down Expand Up @@ -597,6 +598,7 @@ export interface IShellLaunchConfigDto {
export interface ITerminalProcessOptions {
shellIntegration: {
enabled: boolean;
suggestEnabled: boolean;
};
windowsEnableConpty: boolean;
environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined;
Expand Down
6 changes: 6 additions & 0 deletions src/vs/platform/terminal/node/terminalEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ export function getShellIntegrationInjection(
}
newArgs = [...newArgs]; // Shallow clone the array to avoid setting the default array
newArgs[newArgs.length - 1] = format(newArgs[newArgs.length - 1], appRoot, '');
if (options.shellIntegration.suggestEnabled) {
envMixin['VSCODE_SUGGEST'] = '1';
}
return { newArgs, envMixin };
}
logService.warn(`Shell integration cannot be enabled for executable "${shellLaunchConfig.executable}" and args`, shellLaunchConfig.args);
Expand Down Expand Up @@ -182,6 +185,9 @@ export function getShellIntegrationInjection(
if (!newArgs) {
return undefined;
}
if (options.shellIntegration.suggestEnabled) {
envMixin['VSCODE_SUGGEST'] = '1';
}
newArgs = [...newArgs]; // Shallow clone the array to avoid setting the default array
newArgs[newArgs.length - 1] = format(newArgs[newArgs.length - 1], appRoot, '');
return { newArgs, envMixin };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal';
import { getShellIntegrationInjection, IShellIntegrationConfigInjection } from 'vs/platform/terminal/node/terminalEnvironment';

const enabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true }, windowsEnableConpty: true, environmentVariableCollections: undefined };
const disabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: false }, windowsEnableConpty: true, environmentVariableCollections: undefined };
const winptyProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true }, windowsEnableConpty: false, environmentVariableCollections: undefined };
const enabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true, suggestEnabled: false }, windowsEnableConpty: true, environmentVariableCollections: undefined };
const disabledProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: false, suggestEnabled: false }, windowsEnableConpty: true, environmentVariableCollections: undefined };
const winptyProcessOptions: ITerminalProcessOptions = { shellIntegration: { enabled: true, suggestEnabled: false }, windowsEnableConpty: false, environmentVariableCollections: undefined };
const pwshExe = process.platform === 'win32' ? 'pwsh.exe' : 'pwsh';
const repoRoot = process.platform === 'win32' ? process.cwd()[0].toLowerCase() + process.cwd().substring(1) : process.cwd();
const logService = new NullLogService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,46 @@ function Set-MappedKeyHandlers {
Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c'
Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d'

# Conditionally enable suggestions
if ($env:VSCODE_SUGGEST -eq '1') {
Remove-Item Env:VSCODE_SUGGEST

# VS Code send completions request (may override Ctrl+Spacebar)
Set-PSReadLineKeyHandler -Chord 'F12,e' -ScriptBlock {
Send-Completions
}

# Suggest trigger characters
Set-PSReadLineKeyHandler -Chord "-" -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("-")
Send-Completions
}
}
}

function Send-Completions {
$commandLine = ""
$cursorIndex = 0
# TODO: Since fuzzy matching exists, should completions be provided only for character after the
# last space and then filter on the client side? That would let you trigger ctrl+space
# anywhere on a word and have full completions available
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex)
$completionPrefix = $commandLine

# Get completions
$result = "`e]633;Completions"
if ($completionPrefix.Length -gt 0) {
# Get and send completions
$completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex
if ($null -ne $completions.CompletionMatches) {
$result += ";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);"
$result += $completions.CompletionMatches | ConvertTo-Json -Compress
}
}
$result += "`a"

Write-Host -NoNewLine $result
}

Set-MappedKeyHandlers
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/brows
import { registerTerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility';
import { terminalViewIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons';
import { WindowsShellType } from 'vs/platform/terminal/common/terminal';
import { TerminalSettingId, WindowsShellType } from 'vs/platform/terminal/common/terminal';
import { isIOS, isWindows } from 'vs/base/common/platform';
import { setupTerminalMenus } from 'vs/workbench/contrib/terminal/browser/terminalMenus';
import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService';
Expand Down Expand Up @@ -204,6 +204,11 @@ registerSendSequenceKeybinding('\x1b[24~d', { // F12,d -> shift+end (SelectLine)
when: ContextKeyExpr.and(TerminalContextKeys.focus, ContextKeyExpr.equals(TerminalContextKeyStrings.ShellType, WindowsShellType.PowerShell), TerminalContextKeys.terminalShellIntegrationEnabled, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()),
mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.RightArrow }
});
registerSendSequenceKeybinding('\x1b[24~e', { // F12,e -> ctrl+space (Native suggest)
when: ContextKeyExpr.and(TerminalContextKeys.focus, ContextKeyExpr.equals(TerminalContextKeyStrings.ShellType, WindowsShellType.PowerShell), TerminalContextKeys.terminalShellIntegrationEnabled, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), ContextKeyExpr.equals(`config.${TerminalSettingId.ShellIntegrationSuggestEnabled}`, true)),
primary: KeyMod.CtrlCmd | KeyCode.Space,
mac: { primary: KeyMod.WinCtrl | KeyCode.Space }
});

// Always on pwsh keybindings
registerSendSequenceKeybinding('\x1b[1;2H', { // Shift+home
Expand Down
45 changes: 45 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/mark
import { ITerminalQuickFixAddon } from 'vs/workbench/contrib/terminal/browser/xterm/quickFixAddon';
import { INavigationMode, IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalBackend, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget';
import { IMarker } from 'xterm';

export const ITerminalService = createDecorator<ITerminalService>('terminalService');
Expand Down Expand Up @@ -940,6 +941,36 @@ export interface ITerminalInstance {
* If successful, places commandToRun on the command line
*/
freePortKillProcess(port: string, commandToRun: string): Promise<void>;

/**
* Selects the previous suggestion if the suggest widget is visible.
*/
selectPreviousSuggestion(): void;

/**
* Selects the previous page suggestion if the suggest widget is visible.
*/
selectPreviousPageSuggestion(): void;

/**
* Selects the next suggestion if the suggest widget is visible.
*/
selectNextSuggestion(): void;

/**
* Selects the next page suggestion if the suggest widget is visible.
*/
selectNextPageSuggestion(): void;

/**
* Accepts the current suggestion if the suggest widget is visible.
*/
acceptSelectedSuggestion(): Promise<void>;

/**
* Hides the suggest widget.
*/
hideSuggestWidget(): void;
}


Expand Down Expand Up @@ -1047,3 +1078,17 @@ export const enum LinuxDistro {
export const enum TerminalDataTransfers {
Terminals = 'Terminals'
}

export interface ISuggestController {
selectPreviousSuggestion(): void;
selectPreviousPageSuggestion(): void;
selectNextSuggestion(): void;
selectNextPageSuggestion(): void;
acceptSelectedSuggestion(suggestion?: Pick<ISimpleSelectedSuggestion, 'item' | 'model'>): void;
hideSuggestWidget(): void;
/**
* Handle data written to the terminal outside of xterm.js which has no corresponding
* `Terminal.onData` event.
*/
handleNonXtermData(data: string): void;
}

0 comments on commit df931c9

Please sign in to comment.