Skip to content

Commit

Permalink
terminal: allow excluding programs by name from typeahead
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed Nov 9, 2020
1 parent b5f6a52 commit e300dfc
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;

/**
Expand All @@ -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();
Expand All @@ -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)));
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/contrib/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[`;

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -260,13 +262,39 @@ 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');
});
});
});

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 {
Expand All @@ -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[]>();

Expand All @@ -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);
Expand Down

0 comments on commit e300dfc

Please sign in to comment.