terminal: allow excluding programs by name from typeahead
Fixes https://github.com/microsoft/vscode/issues/110109
This commit is contained in:
parent
b5f6a521e0
commit
e300dfcdd2
|
@ -8,10 +8,11 @@ import { Color } from 'vs/base/common/color';
|
|||
import { debounce } from 'vs/base/common/decorators';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
import { XTermAttributes, XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm';
|
||||
|
||||
const ESC = '\x1b';
|
||||
|
@ -1178,11 +1179,16 @@ class TypeAheadStyle implements IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
const compileExcludeRegexp = (programs = DEFAULT_LOCAL_ECHO_EXCLUDE) =>
|
||||
new RegExp(`\\b(${programs.map(escapeRegExpCharacters).join('|')})\\b`, 'i');
|
||||
|
||||
export class TypeAheadAddon extends Disposable implements ITerminalAddon {
|
||||
private typeaheadStyle?: TypeAheadStyle;
|
||||
private typeaheadThreshold = this.config.config.localEchoLatencyThreshold;
|
||||
private excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms);
|
||||
protected lastRow?: { y: number; startingX: number };
|
||||
private timeline?: PredictionTimeline;
|
||||
protected timeline?: PredictionTimeline;
|
||||
private terminalTitle = '';
|
||||
public stats?: PredictionStats;
|
||||
|
||||
/**
|
||||
|
@ -1206,6 +1212,10 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon {
|
|||
|
||||
timeline.setShowPredictions(this.typeaheadThreshold === 0);
|
||||
this._register(terminal.onData(e => this.onUserData(e)));
|
||||
this._register(terminal.onTitleChange(title => {
|
||||
this.terminalTitle = title;
|
||||
this.reevaluatePredictorState(stats, timeline);
|
||||
}));
|
||||
this._register(terminal.onResize(() => {
|
||||
timeline.setShowPredictions(false);
|
||||
timeline.clearCursor();
|
||||
|
@ -1214,6 +1224,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon {
|
|||
this._register(this.config.onConfigChanged(() => {
|
||||
style.onUpdate(this.config.config.localEchoStyle);
|
||||
this.typeaheadThreshold = this.config.config.localEchoLatencyThreshold;
|
||||
this.excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms);
|
||||
this.reevaluatePredictorState(stats, timeline);
|
||||
}));
|
||||
this._register(this.processManager.onBeforeProcessData(e => this.onBeforeProcessData(e)));
|
||||
|
@ -1260,8 +1271,14 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon {
|
|||
* terminal cursor is not updated, causes issues.
|
||||
*/
|
||||
@debounce(100)
|
||||
private reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) {
|
||||
if (this.typeaheadThreshold < 0) {
|
||||
protected reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) {
|
||||
this.reevaluatePredictorStateNow(stats, timeline);
|
||||
}
|
||||
|
||||
protected reevaluatePredictorStateNow(stats: PredictionStats, timeline: PredictionTimeline) {
|
||||
if (this.excludeProgramRe.test(this.terminalTitle)) {
|
||||
timeline.setShowPredictions(false);
|
||||
} else if (this.typeaheadThreshold < 0) {
|
||||
timeline.setShowPredictions(false);
|
||||
} else if (this.typeaheadThreshold === 0) {
|
||||
timeline.setShowPredictions(true);
|
||||
|
|
|
@ -136,11 +136,14 @@ export interface ITerminalConfiguration {
|
|||
unicodeVersion: '6' | '11';
|
||||
experimentalLinkProvider: boolean;
|
||||
localEchoLatencyThreshold: number;
|
||||
localEchoExcludePrograms: ReadonlyArray<string>;
|
||||
localEchoStyle: 'bold' | 'dim' | 'italic' | 'underlined' | 'inverted' | string;
|
||||
serverSpawn: boolean;
|
||||
enablePersistentSessions: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_LOCAL_ECHO_EXCLUDE: ReadonlyArray<string> = ['vim', 'vi', 'nano', 'tmux'];
|
||||
|
||||
export interface ITerminalConfigHelper {
|
||||
config: ITerminalConfiguration;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_LOCAL_ECHO_EXCLUDE } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { isMacintosh, isWindows, Platform } from 'vs/base/common/platform';
|
||||
|
||||
export const terminalConfiguration: IConfigurationNode = {
|
||||
|
@ -358,6 +358,15 @@ export const terminalConfiguration: IConfigurationNode = {
|
|||
minimum: -1,
|
||||
default: 30,
|
||||
},
|
||||
'terminal.integrated.localEchoExcludePrograms': {
|
||||
description: localize('terminal.integrated.localEchoExcludePrograms', "Experimental: local echo will be disabled when any of these program names are found in the terminal title."),
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
uniqueItems: true
|
||||
},
|
||||
default: DEFAULT_LOCAL_ECHO_EXCLUDE,
|
||||
},
|
||||
'terminal.integrated.localEchoStyle': {
|
||||
description: localize('terminal.integrated.localEchoStyle', "Experimental: terminal style of locally echoed text; either a font style or an RGB color."),
|
||||
default: 'dim',
|
||||
|
|
|
@ -8,9 +8,10 @@ import { Terminal } from 'xterm';
|
|||
import { SinonStub, stub, useFakeTimers } from 'sinon';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon';
|
||||
import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
const CSI = `\x1b[`;
|
||||
|
||||
|
@ -92,7 +93,8 @@ suite('Workbench - Terminal Typeahead', () => {
|
|||
setup(() => {
|
||||
config = upcastPartial<ITerminalConfiguration>({
|
||||
localEchoStyle: 'italic',
|
||||
localEchoLatencyThreshold: 0
|
||||
localEchoLatencyThreshold: 0,
|
||||
localEchoExcludePrograms: DEFAULT_LOCAL_ECHO_EXCLUDE,
|
||||
});
|
||||
publicLog = stub();
|
||||
addon = new TestTypeAheadAddon(
|
||||
|
@ -260,6 +262,24 @@ suite('Workbench - Terminal Typeahead', () => {
|
|||
onBeforeProcessData.fire({ data: 'o' });
|
||||
}
|
||||
});
|
||||
|
||||
test('disables on title change', async () => {
|
||||
const t = createMockTerminal({ lines: ['hello|'] });
|
||||
addon.activate(t.terminal);
|
||||
|
||||
await timeout(1000);
|
||||
|
||||
addon.reevaluateNow();
|
||||
assert.strictEqual(addon.isShowing, true, 'expected to show initially');
|
||||
|
||||
t.onTitleChange.fire('foo - VIM.exe');
|
||||
addon.reevaluateNow();
|
||||
assert.strictEqual(addon.isShowing, false, 'expected to hide when vim is open');
|
||||
|
||||
t.onTitleChange.fire('foo - git.exe');
|
||||
addon.reevaluateNow();
|
||||
assert.strictEqual(addon.isShowing, true, 'expected to show again after vim closed');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -267,6 +287,14 @@ class TestTypeAheadAddon extends TypeAheadAddon {
|
|||
public unlockLeftNavigating() {
|
||||
this.lastRow = { y: 1, startingX: 1 };
|
||||
}
|
||||
|
||||
public reevaluateNow() {
|
||||
this.reevaluatePredictorStateNow(this.stats!, this.timeline!);
|
||||
}
|
||||
|
||||
public get isShowing() {
|
||||
return !!this.timeline?.isShowingPredictions;
|
||||
}
|
||||
}
|
||||
|
||||
function upcastPartial<T>(v: Partial<T>): T {
|
||||
|
@ -292,6 +320,7 @@ function createMockTerminal({ lines, cursorAttrs }: {
|
|||
}) {
|
||||
const written: string[] = [];
|
||||
const cursor = { y: 1, x: 1 };
|
||||
const onTitleChange = new Emitter<string>();
|
||||
const onData = new Emitter<string>();
|
||||
const csiEmitter = new Emitter<number[]>();
|
||||
|
||||
|
@ -315,11 +344,13 @@ function createMockTerminal({ lines, cursorAttrs }: {
|
|||
clearWritten: () => written.splice(0, written.length),
|
||||
onData: (s: string) => onData.fire(s),
|
||||
csiEmitter,
|
||||
onTitleChange,
|
||||
terminal: {
|
||||
cols: 80,
|
||||
rows: 5,
|
||||
onResize: new Emitter<void>().event,
|
||||
onData: onData.event,
|
||||
onTitleChange: onTitleChange.event,
|
||||
parser: {
|
||||
registerCsiHandler(_: unknown, callback: () => void) {
|
||||
csiEmitter.event(callback);
|
||||
|
|
Loading…
Reference in a new issue