Skip to content

Commit 6bbb17d

Browse files
committed
terminal: unregister csi handler when not in use for performance
Fixes microsoft#109864
1 parent 81a25a8 commit 6bbb17d

File tree

2 files changed

+72
-33
lines changed

2 files changed

+72
-33
lines changed

src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts

+71-31
Original file line numberDiff line numberDiff line change
@@ -946,29 +946,37 @@ export class PredictionTimeline {
946946
return buffer.type === 'normal' ? buffer : undefined;
947947
}
948948
}
949+
949950
/**
950-
* Gets the escape sequence to restore state/appearence in the cell.
951+
* Gets the escape sequence args to restore state/appearence in the cell.
951952
*/
952-
const attributesToSeq = (cell: XTermAttributes) => cell.isAttributeDefault()
953-
? `${CSI}0m`
954-
: [
955-
cell.isBold() && `${CSI}1m`,
956-
cell.isDim() && `${CSI}2m`,
957-
cell.isItalic() && `${CSI}3m`,
958-
cell.isUnderline() && `${CSI}4m`,
959-
cell.isBlink() && `${CSI}5m`,
960-
cell.isInverse() && `${CSI}7m`,
961-
cell.isInvisible() && `${CSI}8m`,
962-
963-
cell.isFgRGB() && `${CSI}38;2;${cell.getFgColor() >>> 24};${(cell.getFgColor() >>> 16) & 0xFF};${cell.getFgColor() & 0xFF}m`,
964-
cell.isFgPalette() && `${CSI}38;5;${cell.getFgColor()}m`,
965-
cell.isFgDefault() && `${CSI}39m`,
966-
967-
cell.isBgRGB() && `${CSI}48;2;${cell.getBgColor() >>> 24};${(cell.getBgColor() >>> 16) & 0xFF};${cell.getBgColor() & 0xFF}m`,
968-
cell.isBgPalette() && `${CSI}48;5;${cell.getBgColor()}m`,
969-
cell.isBgDefault() && `${CSI}49m`,
970-
].filter(seq => !!seq).join('');
953+
const attributesToArgs = (cell: XTermAttributes) => {
954+
if (cell.isAttributeDefault()) { return [0]; }
955+
956+
const args = [];
957+
if (cell.isBold()) { args.push(1); }
958+
if (cell.isDim()) { args.push(2); }
959+
if (cell.isItalic()) { args.push(3); }
960+
if (cell.isUnderline()) { args.push(4); }
961+
if (cell.isBlink()) { args.push(5); }
962+
if (cell.isInverse()) { args.push(7); }
963+
if (cell.isInvisible()) { args.push(8); }
964+
965+
if (cell.isFgRGB()) { args.push(38, 2, cell.getFgColor() >>> 24, (cell.getFgColor() >>> 16) & 0xFF, cell.getFgColor() & 0xFF); }
966+
if (cell.isFgPalette()) { args.push(38, 5, cell.getFgColor()); }
967+
if (cell.isFgDefault()) { args.push(39); }
968+
969+
if (cell.isBgRGB()) { args.push(48, 2, cell.getBgColor() >>> 24, (cell.getBgColor() >>> 16) & 0xFF, cell.getBgColor() & 0xFF); }
970+
if (cell.isBgPalette()) { args.push(48, 5, cell.getBgColor()); }
971+
if (cell.isBgDefault()) { args.push(49); }
972+
973+
return args;
974+
};
971975

976+
/**
977+
* Gets the escape sequence to restore state/appearence in the cell.
978+
*/
979+
const attributesToSeq = (cell: XTermAttributes) => `${CSI}${attributesToArgs(cell).join(';')}m`;
972980

973981
const arrayHasPrefixAt = <T>(a: ReadonlyArray<T>, ai: number, b: ReadonlyArray<T>) => {
974982
if (a.length - ai > b.length) {
@@ -1019,7 +1027,7 @@ const getColorWidth = (params: (number | number[])[], pos: number) => {
10191027
return advance;
10201028
};
10211029

1022-
class TypeAheadStyle {
1030+
class TypeAheadStyle implements IDisposable {
10231031
private static compileArgs(args: ReadonlyArray<number>) {
10241032
return `${CSI}${args.join(';')}m`;
10251033
}
@@ -1035,8 +1043,9 @@ class TypeAheadStyle {
10351043

10361044
public apply!: string;
10371045
public undo!: string;
1046+
private csiHandler?: IDisposable;
10381047

1039-
constructor(value: ITerminalConfiguration['localEchoStyle']) {
1048+
constructor(value: ITerminalConfiguration['localEchoStyle'], private readonly terminal: Terminal) {
10401049
this.onUpdate(value);
10411050
}
10421051

@@ -1049,9 +1058,38 @@ class TypeAheadStyle {
10491058
}
10501059

10511060
/**
1052-
* Should be called when an attribut eupdate happens in the terminal.
1061+
* Starts tracking for CSI changes in the terminal.
1062+
*/
1063+
public startTracking() {
1064+
this.expectedIncomingStyles = 0;
1065+
this.onDidWriteSGR(attributesToArgs(core(this.terminal)._inputHandler._curAttrData));
1066+
this.csiHandler = this.terminal.parser.registerCsiHandler({ final: 'm' }, args => {
1067+
this.onDidWriteSGR(args);
1068+
return false;
1069+
});
1070+
}
1071+
1072+
/**
1073+
* Stops tracking terminal CSI changes.
10531074
*/
1054-
public onDidWriteSGR(args: (number | number[])[]) {
1075+
@debounce(2000)
1076+
public debounceStopTracking() {
1077+
this.stopTracking();
1078+
}
1079+
1080+
/**
1081+
* @inheritdoc
1082+
*/
1083+
public dispose() {
1084+
this.stopTracking();
1085+
}
1086+
1087+
private stopTracking() {
1088+
this.csiHandler?.dispose();
1089+
this.csiHandler = undefined;
1090+
}
1091+
1092+
private onDidWriteSGR(args: (number | number[])[]) {
10551093
const originalUndo = this.undoArgs;
10561094
for (let i = 0; i < args.length;) {
10571095
const px = args[i];
@@ -1141,7 +1179,7 @@ class TypeAheadStyle {
11411179
}
11421180

11431181
export class TypeAheadAddon extends Disposable implements ITerminalAddon {
1144-
private typeaheadStyle = new TypeAheadStyle(this.config.config.localEchoStyle);
1182+
private typeaheadStyle?: TypeAheadStyle;
11451183
private typeaheadThreshold = this.config.config.localEchoLatencyThreshold;
11461184
protected lastRow?: { y: number; startingX: number };
11471185
private timeline?: PredictionTimeline;
@@ -1162,22 +1200,19 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon {
11621200
}
11631201

11641202
public activate(terminal: Terminal): void {
1203+
const style = this.typeaheadStyle = this._register(new TypeAheadStyle(this.config.config.localEchoStyle, terminal));
11651204
const timeline = this.timeline = new PredictionTimeline(terminal, this.typeaheadStyle);
11661205
const stats = this.stats = this._register(new PredictionStats(this.timeline));
11671206

11681207
timeline.setShowPredictions(this.typeaheadThreshold === 0);
1169-
this._register(terminal.parser.registerCsiHandler({ final: 'm' }, args => {
1170-
this.typeaheadStyle.onDidWriteSGR(args);
1171-
return false;
1172-
}));
11731208
this._register(terminal.onData(e => this.onUserData(e)));
11741209
this._register(terminal.onResize(() => {
11751210
timeline.setShowPredictions(false);
11761211
timeline.clearCursor();
11771212
this.reevaluatePredictorState(stats, timeline);
11781213
}));
11791214
this._register(this.config.onConfigChanged(() => {
1180-
this.typeaheadStyle.onUpdate(this.config.config.localEchoStyle);
1215+
style.onUpdate(this.config.config.localEchoStyle);
11811216
this.typeaheadThreshold = this.config.config.localEchoLatencyThreshold;
11821217
this.reevaluatePredictorState(stats, timeline);
11831218
}));
@@ -1192,6 +1227,10 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon {
11921227
}, statsSendTelemetryEvery);
11931228
}
11941229

1230+
if (timeline.length === 0) {
1231+
style.debounceStopTracking();
1232+
}
1233+
11951234
this.reevaluatePredictorState(stats, timeline);
11961235
}));
11971236
}
@@ -1306,7 +1345,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon {
13061345

13071346
if (reader.eatCharCode(32, 126)) { // alphanum
13081347
const char = data[reader.index - 1];
1309-
if (this.timeline.addPrediction(buffer, new CharacterPrediction(this.typeaheadStyle, char)) && this.timeline.getCursor(buffer).x === terminal.cols) {
1348+
if (this.timeline.addPrediction(buffer, new CharacterPrediction(this.typeaheadStyle!, char)) && this.timeline.getCursor(buffer).x === terminal.cols) {
13101349
this.timeline.addBoundary(buffer, new TentativeBoundary(new LinewrapPrediction()));
13111350
}
13121351
continue;
@@ -1346,6 +1385,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon {
13461385

13471386
if (this.timeline.length === 1) {
13481387
this.deferClearingPredictions();
1388+
this.typeaheadStyle!.startTracking();
13491389
}
13501390
}
13511391

src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,7 @@ suite('Workbench - Terminal Typeahead', () => {
149149
`${CSI}?25l`, // hide cursor
150150
`${CSI}2;7H`, // move cursor cursor
151151
`${CSI}X`, // delete character
152-
`${CSI}1m`, // reset style
153-
`${CSI}38;5;1m`, // reset style
152+
`${CSI}1;38;5;1m`, // reset style
154153
'q', // new character
155154
`${CSI}?25h`, // show cursor
156155
].join(''));

0 commit comments

Comments
 (0)