mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Merge pull request #225336 from microsoft/tyriar/225330
Pause terminal input while completions are requested
This commit is contained in:
@@ -30,6 +30,7 @@ import { ACTIVE_GROUP_TYPE, AUX_WINDOW_GROUP_TYPE, SIDE_GROUP_TYPE } from 'vs/wo
|
||||
import type { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand';
|
||||
import type { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
import type { IMenu } from 'vs/platform/actions/common/actions';
|
||||
import type { Barrier } from 'vs/base/common/async';
|
||||
|
||||
export const ITerminalService = createDecorator<ITerminalService>('terminalService');
|
||||
export const ITerminalConfigurationService = createDecorator<ITerminalConfigurationService>('terminalConfigurationService');
|
||||
@@ -1050,6 +1051,12 @@ export interface ITerminalInstance extends IBaseTerminalInstance {
|
||||
* @returns Whether the context menu should be suppressed.
|
||||
*/
|
||||
handleMouseEvent(event: MouseEvent, contextMenu: IMenu): Promise<{ cancelContextMenu: boolean } | void>;
|
||||
|
||||
/**
|
||||
* Pause input events until the provided barrier is resolved.
|
||||
* @param barrier The barrier to wait for until input events can continue.
|
||||
*/
|
||||
pauseInputEvents(barrier: Barrier): void;
|
||||
}
|
||||
|
||||
export const enum XtermTerminalConstants {
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as dom from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Orientation } from 'vs/base/browser/ui/sash/sash';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { AutoOpenBarrier, Promises, disposableTimeout, timeout } from 'vs/base/common/async';
|
||||
import { AutoOpenBarrier, Barrier, Promises, disposableTimeout, timeout } from 'vs/base/common/async';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { debounce } from 'vs/base/common/decorators';
|
||||
import { ErrorNoTelemetry, onUnexpectedError } from 'vs/base/common/errors';
|
||||
@@ -198,6 +198,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
private _lineDataEventAddon: LineDataEventAddon | undefined;
|
||||
private readonly _scopedContextKeyService: IContextKeyService;
|
||||
private _resizeDebouncer?: TerminalResizeDebouncer;
|
||||
private _pauseInputEventBarrier: Barrier | undefined;
|
||||
pauseInputEvents(barrier: Barrier): void {
|
||||
this._pauseInputEventBarrier = barrier;
|
||||
}
|
||||
|
||||
readonly capabilities = this._register(new TerminalCapabilityStoreMultiplexer());
|
||||
readonly statusList: ITerminalStatusList;
|
||||
@@ -806,6 +810,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
|
||||
this._register(this._processManager.onProcessData(e => this._onProcessData(e)));
|
||||
this._register(xterm.raw.onData(async data => {
|
||||
await this._pauseInputEventBarrier?.wait();
|
||||
await this._processManager.write(data);
|
||||
this._onDidInputData.fire(data);
|
||||
}));
|
||||
|
||||
+20
-6
@@ -5,6 +5,7 @@
|
||||
|
||||
import type { Terminal as RawXtermTerminal } from '@xterm/xterm';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { AutoOpenBarrier } from 'vs/base/common/async';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -157,15 +158,28 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo
|
||||
return;
|
||||
}
|
||||
if (this._terminalSuggestWidgetVisibleContextKey) {
|
||||
this._addon.value = this._instantiationService.createInstance(SuggestAddon, TerminalSuggestContribution._cachedPwshCommands, this._instance.capabilities, this._terminalSuggestWidgetVisibleContextKey);
|
||||
xterm.loadAddon(this._addon.value);
|
||||
this._addon.value.setPanel(dom.findParentWithClass(xterm.element!, 'panel')!);
|
||||
this._addon.value.setScreen(xterm.element!.querySelector('.xterm-screen')!);
|
||||
this.add(this._instance.onDidBlur(() => this._addon.value?.hideSuggestWidget()));
|
||||
this.add(this._addon.value.onAcceptedCompletion(async text => {
|
||||
const addon = this._addon.value = this._instantiationService.createInstance(SuggestAddon, TerminalSuggestContribution._cachedPwshCommands, this._instance.capabilities, this._terminalSuggestWidgetVisibleContextKey);
|
||||
xterm.loadAddon(addon);
|
||||
addon.setPanel(dom.findParentWithClass(xterm.element!, 'panel')!);
|
||||
addon.setScreen(xterm.element!.querySelector('.xterm-screen')!);
|
||||
this.add(this._instance.onDidBlur(() => addon.hideSuggestWidget()));
|
||||
this.add(addon.onAcceptedCompletion(async text => {
|
||||
this._instance.focus();
|
||||
this._instance.sendText(text, false);
|
||||
}));
|
||||
|
||||
// If completions are requested, pause and queue input events until completions are
|
||||
// received. This fixing some problems in PowerShell, particularly enter not executing
|
||||
// when typing quickly and some characters being printed twice.
|
||||
let barrier: AutoOpenBarrier | undefined;
|
||||
this.add(addon.onDidRequestCompletions(() => {
|
||||
barrier = new AutoOpenBarrier(2000);
|
||||
this._instance.pauseInputEvents(barrier);
|
||||
}));
|
||||
this.add(addon.onDidReceiveCompletions(() => {
|
||||
barrier?.open();
|
||||
barrier = undefined;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
|
||||
readonly onBell = this._onBell.event;
|
||||
private readonly _onAcceptedCompletion = this._register(new Emitter<string>());
|
||||
readonly onAcceptedCompletion = this._onAcceptedCompletion.event;
|
||||
private readonly _onDidRequestCompletions = this._register(new Emitter<void>());
|
||||
readonly onDidRequestCompletions = this._onDidRequestCompletions.event;
|
||||
private readonly _onDidReceiveCompletions = this._register(new Emitter<void>());
|
||||
readonly onDidReceiveCompletions = this._onDidReceiveCompletions.event;
|
||||
|
||||
constructor(
|
||||
private readonly _cachedPwshCommands: Set<SimpleCompletionItem>,
|
||||
@@ -211,6 +215,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
|
||||
// completions being requested again right after accepting a completion
|
||||
if (this._lastUserDataTimestamp > this._lastAcceptedCompletionTimestamp) {
|
||||
this._onAcceptedCompletion.fire(SuggestAddon.requestCompletionsSequence);
|
||||
this._onDidRequestCompletions.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +334,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
|
||||
private _replacementLength: number = 0;
|
||||
|
||||
private _handleCompletionsSequence(terminal: Terminal, data: string, command: string, args: string[]): void {
|
||||
this._onDidReceiveCompletions.fire();
|
||||
|
||||
// Nothing to handle if the terminal is not attached
|
||||
if (!terminal.element || !this._enableWidget || !this._promptInputModel) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user