From a9d2b8a55c40b4d0b477edba5ef3c7d2f1d9fd4e Mon Sep 17 00:00:00 2001 From: Ken Aoki Date: Sat, 10 Oct 2020 04:04:52 +0000 Subject: [PATCH 01/31] Fix search not expanding selection to left when appropriate --- addons/xterm-addon-search/src/SearchAddon.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index f5505689..561794e1 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -141,14 +141,14 @@ export class SearchAddon implements ITerminalAddon { const isReverseSearch = true; let startRow = this._terminal.buffer.active.baseY + this._terminal.rows; let startCol = this._terminal.cols; - let result: ISearchResult | undefined; - const incremental = searchOptions ? searchOptions.incremental : false; let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { - currentSelection = this._terminal.getSelectionPosition()!; + const incremental = searchOptions ? searchOptions.incremental : false; // Start from selection start if there is a selection - startRow = currentSelection.startRow; - startCol = currentSelection.startColumn; + // For incremental search, use selection end + currentSelection = this._terminal.getSelectionPosition()!; + startRow = incremental ? currentSelection.endRow : currentSelection.startRow; + startCol = incremental ? currentSelection.endColumn : currentSelection.startColumn; } this._initLinesCache(); @@ -157,14 +157,8 @@ export class SearchAddon implements ITerminalAddon { startCol }; - if (incremental) { - result = this._findInLine(term, searchPosition, searchOptions, false); - if (!(result && result.row === startRow && result.col === startCol)) { - result = this._findInLine(term, searchPosition, searchOptions, true); - } - } else { - result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); - } + // Search startRow + let result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); // Search from startRow - 1 to top if (!result) { From b6074df4bf76e996fea449dc718a2122ceeab30a Mon Sep 17 00:00:00 2001 From: Ken Aoki Date: Tue, 13 Oct 2020 06:44:01 +0000 Subject: [PATCH 02/31] Revert "Fix search not expanding selection to left when appropriate" This reverts commit a9d2b8a55c40b4d0b477edba5ef3c7d2f1d9fd4e. --- addons/xterm-addon-search/src/SearchAddon.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 561794e1..f5505689 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -141,14 +141,14 @@ export class SearchAddon implements ITerminalAddon { const isReverseSearch = true; let startRow = this._terminal.buffer.active.baseY + this._terminal.rows; let startCol = this._terminal.cols; + let result: ISearchResult | undefined; + const incremental = searchOptions ? searchOptions.incremental : false; let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { - const incremental = searchOptions ? searchOptions.incremental : false; - // Start from selection start if there is a selection - // For incremental search, use selection end currentSelection = this._terminal.getSelectionPosition()!; - startRow = incremental ? currentSelection.endRow : currentSelection.startRow; - startCol = incremental ? currentSelection.endColumn : currentSelection.startColumn; + // Start from selection start if there is a selection + startRow = currentSelection.startRow; + startCol = currentSelection.startColumn; } this._initLinesCache(); @@ -157,8 +157,14 @@ export class SearchAddon implements ITerminalAddon { startCol }; - // Search startRow - let result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); + if (incremental) { + result = this._findInLine(term, searchPosition, searchOptions, false); + if (!(result && result.row === startRow && result.col === startCol)) { + result = this._findInLine(term, searchPosition, searchOptions, true); + } + } else { + result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); + } // Search from startRow - 1 to top if (!result) { From d3d8ec3b2018c51f6cbd3ee8b5225d8449d044db Mon Sep 17 00:00:00 2001 From: Ken Aoki Date: Tue, 13 Oct 2020 09:55:10 +0000 Subject: [PATCH 03/31] Fix search not expanding selection to left when appropriate (revenge) --- addons/xterm-addon-search/src/SearchAddon.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index f5505689..54a4a198 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -158,8 +158,13 @@ export class SearchAddon implements ITerminalAddon { }; if (incremental) { - result = this._findInLine(term, searchPosition, searchOptions, false); + result = this._findInLine(term, searchPosition, searchOptions, false); // Try to expand selection to right first. if (!(result && result.row === startRow && result.col === startCol)) { + // If selection was not able to be expanded to right, then reverse search begin. + if (currentSelection) { + searchPosition.startRow = currentSelection.endRow; + searchPosition.startCol = currentSelection.endColumn; + } result = this._findInLine(term, searchPosition, searchOptions, true); } } else { From 5c7644adc0bb6fc2691fe29cd5d0a69dfee5cd42 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Tue, 3 Nov 2020 18:36:28 +0100 Subject: [PATCH 04/31] Add rudimenatary and not fully working support of OSC 4 --- src/browser/Terminal.ts | 17 +++++++++++++++++ src/common/InputHandler.ts | 17 +++++++++++++++++ src/common/Types.d.ts | 1 + 3 files changed, 35 insertions(+) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index f8b8b3e4..115ccce7 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -53,6 +53,7 @@ import { Linkifier2 } from 'browser/Linkifier2'; import { CoreBrowserService } from 'browser/services/CoreBrowserService'; import { CoreTerminal } from 'common/CoreTerminal'; import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services'; +import { css } from 'browser/Color'; // Let it work inside Node.js for automated testing purposes. const document: Document = (typeof window !== 'undefined') ? window.document : null as any; @@ -150,6 +151,7 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type))); this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove)); this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange)); + this.register(this._inputHandler.onAnsiColorChange((index, color) => this.changeAnsiColor(index, color))); this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter)); this.register(forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter)); @@ -157,6 +159,21 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows))); } + private changeAnsiColor(colorIndex: number, colorValue: string): void { + // colorValue = rgb:xx/yy/zz + const r = colorValue.substring(4, 6); + const g = colorValue.substring(7, 9); + const b = colorValue.substring(10, 12); + const color = `#${r}${g}${b}`; + + //TODO: remove debug + console.log(`Change ANSI color[${colorIndex}]=${colorValue} (${color})`); + + this._colorManager!.colors.ansi[colorIndex] = css.toColor(color); + this._renderService?.setColors(this._colorManager!.colors); + this.viewport?.onThemeChange(this._colorManager!.colors); + } + public dispose(): void { if (this._isDisposed) { return; diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index dbf695e6..3dd0c11b 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -250,6 +250,8 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } private _onTitleChange = new EventEmitter(); public get onTitleChange(): IEvent { return this._onTitleChange.event; } + private _onAnsiColorChange = new EventEmitter(); + public get onAnsiColorChange(): IEvent { return this._onAnsiColorChange.event; } constructor( private readonly _bufferService: IBufferService, @@ -372,6 +374,12 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.setOscHandler(2, new OscHandler((data: string) => this.setTitle(data))); // 3 - set property X in the form "prop=value" // 4 - Change Color Number + this._parser.setOscHandler(4, new OscHandler((data: string) => { + const ansiColor = data.split(';'); + const colorIndex = parseInt(ansiColor[0]); + const colorValue = ansiColor[1]; + this.setAnsiColor(colorIndex, colorValue); + })); // 5 - Change Special Color Number // 6 - Enable/disable Special Color Number c // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) @@ -2711,6 +2719,15 @@ export class InputHandler extends Disposable implements IInputHandler { this._iconName = data; } + /** + * OSC 4; ; ST (set ANSI color to ) + */ + public setAnsiColor(colorIndex: number, colorData: string): void { + //TODO: remove debug + console.log(`Setting ANSI color ${colorIndex} to value ${colorData}`); + this._onAnsiColorChange.fire(colorIndex, colorData); + } + /** * ESC E * C1.NEL diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index bd0d11c6..00497932 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -389,6 +389,7 @@ export interface IInputHandler { /** CSI ' ~ */ deleteColumns(params: IParams): void; /** OSC 0 OSC 2 */ setTitle(data: string): void; + /** OSC 4 */ setAnsiColor(colorIndex: number, colorData: string): void; /** ESC E */ nextLine(): void; /** ESC = */ keypadApplicationMode(): void; /** ESC > */ keypadNumericMode(): void; From b22d7f1140676e1e39b08fcbe7b62d0ce4660243 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Wed, 4 Nov 2020 22:19:55 +0100 Subject: [PATCH 05/31] Move color parsing from Terminal to InputHandler --- src/browser/Terminal.ts | 19 ++++++------------- src/common/InputHandler.ts | 39 +++++++++++++++++++++++++------------- src/common/Types.d.ts | 2 +- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 115ccce7..216b541a 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -39,7 +39,7 @@ import { MouseZoneManager } from 'browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm'; import { DomRenderer } from 'browser/renderer/dom/DomRenderer'; -import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions } from 'common/Types'; +import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, IColorRGB } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; @@ -53,7 +53,7 @@ import { Linkifier2 } from 'browser/Linkifier2'; import { CoreBrowserService } from 'browser/services/CoreBrowserService'; import { CoreTerminal } from 'common/CoreTerminal'; import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services'; -import { css } from 'browser/Color'; +import { rgba } from 'browser/Color'; // Let it work inside Node.js for automated testing purposes. const document: Document = (typeof window !== 'undefined') ? window.document : null as any; @@ -151,7 +151,7 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type))); this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove)); this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange)); - this.register(this._inputHandler.onAnsiColorChange((index, color) => this.changeAnsiColor(index, color))); + this.register(this._inputHandler.onAnsiColorChange((index, color) => this._changeAnsiColor(index, color))); this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter)); this.register(forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter)); @@ -159,17 +159,10 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows))); } - private changeAnsiColor(colorIndex: number, colorValue: string): void { - // colorValue = rgb:xx/yy/zz - const r = colorValue.substring(4, 6); - const g = colorValue.substring(7, 9); - const b = colorValue.substring(10, 12); - const color = `#${r}${g}${b}`; + private _changeAnsiColor(colorIndex: number, colorRGB: IColorRGB): void { + const color = rgba.toColor(colorRGB[0], colorRGB[1], colorRGB[2]); - //TODO: remove debug - console.log(`Change ANSI color[${colorIndex}]=${colorValue} (${color})`); - - this._colorManager!.colors.ansi[colorIndex] = css.toColor(color); + this._colorManager!.colors.ansi[colorIndex] = color; this._renderService?.setColors(this._colorManager!.colors); this.viewport?.onThemeChange(this._colorManager!.colors); } diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 3dd0c11b..46350a14 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { IInputHandler, IAttributeData, IDisposable, IWindowOptions } from 'common/Types'; +import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorRGB } from 'common/Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; @@ -250,8 +250,8 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } private _onTitleChange = new EventEmitter(); public get onTitleChange(): IEvent { return this._onTitleChange.event; } - private _onAnsiColorChange = new EventEmitter(); - public get onAnsiColorChange(): IEvent { return this._onAnsiColorChange.event; } + private _onAnsiColorChange = new EventEmitter(); + public get onAnsiColorChange(): IEvent { return this._onAnsiColorChange.event; } constructor( private readonly _bufferService: IBufferService, @@ -374,12 +374,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.setOscHandler(2, new OscHandler((data: string) => this.setTitle(data))); // 3 - set property X in the form "prop=value" // 4 - Change Color Number - this._parser.setOscHandler(4, new OscHandler((data: string) => { - const ansiColor = data.split(';'); - const colorIndex = parseInt(ansiColor[0]); - const colorValue = ansiColor[1]; - this.setAnsiColor(colorIndex, colorValue); - })); + this._parser.setOscHandler(4, new OscHandler((data: string) => this.setAnsiColor(data))); // 5 - Change Special Color Number // 6 - Enable/disable Special Color Number c // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) @@ -2721,11 +2716,29 @@ export class InputHandler extends Disposable implements IInputHandler { /** * OSC 4; ; ST (set ANSI color to ) + * + * The expected content of data is: ;rgb:// where rr, gg, bb are hex numbers. */ - public setAnsiColor(colorIndex: number, colorData: string): void { - //TODO: remove debug - console.log(`Setting ANSI color ${colorIndex} to value ${colorData}`); - this._onAnsiColorChange.fire(colorIndex, colorData); + public setAnsiColor(data: string): void { + // example data: 5;rgb:aa/bb/cc + const regex = /(\d+);rgb:([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})/; + const match = data.match(regex); + + if (match) { + const colorIndex = parseInt(match[1]); + const color: IColorRGB = [ + parseInt(match[2], 16), + parseInt(match[3], 16), + parseInt(match[4], 16) + ]; + + //TODO: remove debug + console.log(`Setting ANSI color ${colorIndex} to RGB value ${color}`); + this._onAnsiColorChange.fire(colorIndex, color); + } + else { + this._logService.warn(`Expected format ;rgb:// but got data: ${data}`); + } } /** diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 00497932..4967da12 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -389,7 +389,7 @@ export interface IInputHandler { /** CSI ' ~ */ deleteColumns(params: IParams): void; /** OSC 0 OSC 2 */ setTitle(data: string): void; - /** OSC 4 */ setAnsiColor(colorIndex: number, colorData: string): void; + /** OSC 4 */ setAnsiColor(data: string): void; /** ESC E */ nextLine(): void; /** ESC = */ keypadApplicationMode(): void; /** ESC > */ keypadNumericMode(): void; From b127436cc4f004964b252404605c66843ebe131c Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Thu, 5 Nov 2020 08:51:01 +0100 Subject: [PATCH 06/31] Fix TODO comment --- src/common/InputHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 46350a14..1b235abf 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2732,7 +2732,7 @@ export class InputHandler extends Disposable implements IInputHandler { parseInt(match[4], 16) ]; - //TODO: remove debug + // TODO: remove debug console.log(`Setting ANSI color ${colorIndex} to RGB value ${color}`); this._onAnsiColorChange.fire(colorIndex, color); } From b898e016f3e252f4e8ca65a520ef6a65803cc9d1 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sat, 7 Nov 2020 21:33:42 +0100 Subject: [PATCH 07/31] Add IAnsiColorChangeEvent type --- src/browser/Terminal.ts | 10 ++++---- src/common/InputHandler.test.ts | 2 +- src/common/InputHandler.ts | 42 +++++++++++++++++++-------------- src/common/Types.d.ts | 10 ++++++++ 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 216b541a..9452833e 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -39,7 +39,7 @@ import { MouseZoneManager } from 'browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm'; import { DomRenderer } from 'browser/renderer/dom/DomRenderer'; -import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, IColorRGB } from 'common/Types'; +import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, IAnsiColorChangeEvent } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; @@ -149,9 +149,9 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._inputHandler.onRequestReset(() => this.reset())); this.register(this._inputHandler.onRequestScroll((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined))); this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type))); + this.register(this._inputHandler.onAnsiColorChange((event) => this._changeAnsiColor(event))); this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove)); this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange)); - this.register(this._inputHandler.onAnsiColorChange((index, color) => this._changeAnsiColor(index, color))); this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter)); this.register(forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter)); @@ -159,10 +159,10 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows))); } - private _changeAnsiColor(colorIndex: number, colorRGB: IColorRGB): void { - const color = rgba.toColor(colorRGB[0], colorRGB[1], colorRGB[2]); + private _changeAnsiColor(event: IAnsiColorChangeEvent): void { + const color = rgba.toColor(event.red, event.green, event.blue); - this._colorManager!.colors.ansi[colorIndex] = color; + this._colorManager!.colors.ansi[event.colorIndex] = color; this._renderService?.setColors(this._colorManager!.colors); this.viewport?.onThemeChange(this._colorManager!.colors); } diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 46f8eacc..6ff0fd57 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -5,7 +5,7 @@ import { assert, expect } from 'chai'; import { InputHandler } from 'common/InputHandler'; -import { IBufferLine, IAttributeData } from 'common/Types'; +import { IBufferLine, IAttributeData, IAnsiColorChangeEvent } from 'common/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; import { Attributes, UnderlineStyle } from 'common/buffer/Constants'; diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 1b235abf..0bd1b3a6 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorRGB } from 'common/Types'; +import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IAnsiColorChangeEvent } from 'common/Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; @@ -250,8 +250,8 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } private _onTitleChange = new EventEmitter(); public get onTitleChange(): IEvent { return this._onTitleChange.event; } - private _onAnsiColorChange = new EventEmitter(); - public get onAnsiColorChange(): IEvent { return this._onAnsiColorChange.event; } + private _onAnsiColorChange = new EventEmitter(); + public get onAnsiColorChange(): IEvent { return this._onAnsiColorChange.event; } constructor( private readonly _bufferService: IBufferService, @@ -2714,27 +2714,33 @@ export class InputHandler extends Disposable implements IInputHandler { this._iconName = data; } + // This is really an internal method and not part of IInputHandler implementation. + // Making it public so that I can test it. + public parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { + // example data: 5;rgb:aa/bb/cc + const regex = /(\d+);rgb:([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})/; + const match = data.match(regex); + + if (match) { + return { + colorIndex: parseInt(match[1]), + red: parseInt(match[2], 16), + green: parseInt(match[3], 16), + blue: parseInt(match[4], 16) + }; + } + return null; + } + /** * OSC 4; ; ST (set ANSI color to ) * * The expected content of data is: ;rgb:// where rr, gg, bb are hex numbers. */ public setAnsiColor(data: string): void { - // example data: 5;rgb:aa/bb/cc - const regex = /(\d+);rgb:([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})/; - const match = data.match(regex); - - if (match) { - const colorIndex = parseInt(match[1]); - const color: IColorRGB = [ - parseInt(match[2], 16), - parseInt(match[3], 16), - parseInt(match[4], 16) - ]; - - // TODO: remove debug - console.log(`Setting ANSI color ${colorIndex} to RGB value ${color}`); - this._onAnsiColorChange.fire(colorIndex, color); + const event = this.parseAnsiColorChange(data); + if (event) { + this._onAnsiColorChange.fire(event); } else { this._logService.warn(`Expected format ;rgb:// but got data: ${data}`); diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 4967da12..cbb11b3f 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -328,6 +328,16 @@ export interface IWindowOptions { setWinLines?: boolean; } +/** + * Event fired for OSC 4 command - to change ANSI color based on its index. + */ +export interface IAnsiColorChangeEvent { + colorIndex: number; + red: number; + green: number; + blue: number; +} + /** * Calls the parser and handles actions generated by the parser. */ From d5f4ae53c902ed972cc0bf7d57b083ad61160b03 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sat, 7 Nov 2020 21:34:21 +0100 Subject: [PATCH 08/31] Add IAnsiColorChangeEvent related tests --- src/common/InputHandler.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 6ff0fd57..3b8d1387 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -1692,4 +1692,25 @@ describe('InputHandler', () => { assert.equal(coreService.decPrivateModes.origin, false); }); }); + describe('OSC', () => { + it('should ignore incorrect Ansi color change data', () => { + assert.deepEqual(inputHandler.parseAnsiColorChange('17;rgb:1a/2b/3c'), { + colorIndex: 17, + red: 0x1a, + green: 0x2b, + blue: 0x3c + }); + assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:a/b/c')); + assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:#aabbcc')); + assert.isNull(inputHandler.parseAnsiColorChange('17;rgba:aa/bb/cc')); + assert.isNull(inputHandler.parseAnsiColorChange('rgb:aa/bb/cc')); + }); + it('should fire event on Ansi color change', (done) => { + inputHandler.onAnsiColorChange(e => { + assert.deepEqual(e, { colorIndex: 17, red: 0x1a, green: 0x2b, blue: 0x3c }); + done(); + }); + inputHandler.parse('\x1b]4;17;rgb:1a/2b/3c\x1b\\'); + }); + }); }); From bc86b7804c40f5036b8fc97c93b6dfccb5e5ea07 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sat, 7 Nov 2020 21:35:36 +0100 Subject: [PATCH 09/31] Use all and not only 16 Ansi colors in CharAtlas config --- src/browser/renderer/atlas/CharAtlasUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/browser/renderer/atlas/CharAtlasUtils.ts b/src/browser/renderer/atlas/CharAtlasUtils.ts index 346b35f8..2b876f2b 100644 --- a/src/browser/renderer/atlas/CharAtlasUtils.ts +++ b/src/browser/renderer/atlas/CharAtlasUtils.ts @@ -18,7 +18,9 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number selection: undefined, // For the static char atlas, we only use the first 16 colors, but we need all 256 for the // dynamic character atlas. - ansi: colors.ansi.slice(0, 16) + // ansi: colors.ansi.slice(0, 16) + // TODO: Using entire array to support OSC 4; can this break anything? + ansi: colors.ansi }; return { devicePixelRatio: window.devicePixelRatio, From 77ed5ba7edf6445655c5a4efe61d37315f34500b Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sun, 8 Nov 2020 10:19:12 +0100 Subject: [PATCH 10/31] Add terminal sequence doc --- src/common/InputHandler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 0bd1b3a6..aec5c7a9 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2735,7 +2735,8 @@ export class InputHandler extends Disposable implements IInputHandler { /** * OSC 4; ; ST (set ANSI color to ) * - * The expected content of data is: ;rgb:// where rr, gg, bb are hex numbers. + * @vt: #Y OSC 4 "Set ANSI color" "OSC 4 ; Ps ; Pt BEL" "Set ANSI color `Ps` to `Pt`." + * `Ps` is the color index between 0 and 255. `Pt` color format is 'rgb:rr/gg/bb' where r, g, b are hexadecimal digits. */ public setAnsiColor(data: string): void { const event = this.parseAnsiColorChange(data); From 653599575359c382eac3670525a1ec4cd911b742 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sun, 8 Nov 2020 22:05:25 +0100 Subject: [PATCH 11/31] Update OSC 4 description to match #3036 --- src/common/InputHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index aec5c7a9..235515b1 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2735,8 +2735,8 @@ export class InputHandler extends Disposable implements IInputHandler { /** * OSC 4; ; ST (set ANSI color to ) * - * @vt: #Y OSC 4 "Set ANSI color" "OSC 4 ; Ps ; Pt BEL" "Set ANSI color `Ps` to `Pt`." - * `Ps` is the color index between 0 and 255. `Pt` color format is 'rgb:rr/gg/bb' where r, g, b are hexadecimal digits. + * @vt: #Y OSC 4 "Set ANSI color" "OSC 4 ; c ; spec BEL" "Change color number `c` to the color specified by `spec`." + * `c` is the color index between 0 and 255. `spec` color format is 'rgb:rr/gg/bb' where r, g, b are hexadecimal digits. */ public setAnsiColor(data: string): void { const event = this.parseAnsiColorChange(data); From e6917d19330364e4eb3d274644ad08b442cc8cf0 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sun, 8 Nov 2020 22:07:24 +0100 Subject: [PATCH 12/31] Clarify color spec description --- src/common/InputHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 235515b1..7867a3fe 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2736,7 +2736,7 @@ export class InputHandler extends Disposable implements IInputHandler { * OSC 4; ; ST (set ANSI color to ) * * @vt: #Y OSC 4 "Set ANSI color" "OSC 4 ; c ; spec BEL" "Change color number `c` to the color specified by `spec`." - * `c` is the color index between 0 and 255. `spec` color format is 'rgb:rr/gg/bb' where r, g, b are hexadecimal digits. + * `c` is the color index between 0 and 255. `spec` color format is 'rgb:hh/hh/hh' where `h` are hexadecimal digits. */ public setAnsiColor(data: string): void { const event = this.parseAnsiColorChange(data); From ec1234e8d8ed2b2dd635badf16000cfb697484e2 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Fri, 27 Nov 2020 14:17:57 -0600 Subject: [PATCH 13/31] fix: update font-finder to v1.1.0 --- addons/xterm-addon-ligatures/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-ligatures/package.json b/addons/xterm-addon-ligatures/package.json index df85c020..a35cfc5c 100644 --- a/addons/xterm-addon-ligatures/package.json +++ b/addons/xterm-addon-ligatures/package.json @@ -31,7 +31,7 @@ ], "license": "MIT", "dependencies": { - "font-finder": "^1.0.4", + "font-finder": "^1.1.0", "font-ligatures": "^1.3.3" }, "devDependencies": { From 52b64beefce87e48f0b3d81ee795b643f6faa7cd Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sun, 29 Nov 2020 16:30:05 +0100 Subject: [PATCH 14/31] Make InputHandler parseAnsiColorChange private --- src/common/InputHandler.test.ts | 17 +++++++---------- src/common/InputHandler.ts | 6 ++---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 3b8d1387..8cc1bc97 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -1694,16 +1694,13 @@ describe('InputHandler', () => { }); describe('OSC', () => { it('should ignore incorrect Ansi color change data', () => { - assert.deepEqual(inputHandler.parseAnsiColorChange('17;rgb:1a/2b/3c'), { - colorIndex: 17, - red: 0x1a, - green: 0x2b, - blue: 0x3c - }); - assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:a/b/c')); - assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:#aabbcc')); - assert.isNull(inputHandler.parseAnsiColorChange('17;rgba:aa/bb/cc')); - assert.isNull(inputHandler.parseAnsiColorChange('rgb:aa/bb/cc')); + // this is testing a private method + const parseAnsiColorChange = inputHandler["_parseAnsiColorChange"]; + + assert.isNull(parseAnsiColorChange('17;rgb:a/b/c')); + assert.isNull(parseAnsiColorChange('17;rgb:#aabbcc')); + assert.isNull(parseAnsiColorChange('17;rgba:aa/bb/cc')); + assert.isNull(parseAnsiColorChange('rgb:aa/bb/cc')); }); it('should fire event on Ansi color change', (done) => { inputHandler.onAnsiColorChange(e => { diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 7867a3fe..3560ee6a 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2714,9 +2714,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._iconName = data; } - // This is really an internal method and not part of IInputHandler implementation. - // Making it public so that I can test it. - public parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { + private _parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { // example data: 5;rgb:aa/bb/cc const regex = /(\d+);rgb:([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})/; const match = data.match(regex); @@ -2739,7 +2737,7 @@ export class InputHandler extends Disposable implements IInputHandler { * `c` is the color index between 0 and 255. `spec` color format is 'rgb:hh/hh/hh' where `h` are hexadecimal digits. */ public setAnsiColor(data: string): void { - const event = this.parseAnsiColorChange(data); + const event = this._parseAnsiColorChange(data); if (event) { this._onAnsiColorChange.fire(event); } From f4a56389cdabeea2919125eb63fa07d5410b6954 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sun, 29 Nov 2020 23:22:31 +0100 Subject: [PATCH 15/31] Fix quotes --- src/common/InputHandler.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 8cc1bc97..eff8c76d 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -1695,7 +1695,7 @@ describe('InputHandler', () => { describe('OSC', () => { it('should ignore incorrect Ansi color change data', () => { // this is testing a private method - const parseAnsiColorChange = inputHandler["_parseAnsiColorChange"]; + const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; assert.isNull(parseAnsiColorChange('17;rgb:a/b/c')); assert.isNull(parseAnsiColorChange('17;rgb:#aabbcc')); From 5a74ce2e596e085e390c31d79286069ef8a0ee0d Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Mon, 30 Nov 2020 22:00:04 +0100 Subject: [PATCH 16/31] Add Ansi color change positive test --- src/common/InputHandler.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index eff8c76d..57680052 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -1693,6 +1693,15 @@ describe('InputHandler', () => { }); }); describe('OSC', () => { + it('should parse correct Ansi color change data', () => { + // this is testing a private method + const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; + + assert.deepEqual( + parseAnsiColorChange('19;rgb:a1/b2/c3'), + { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 } + ); + }), it('should ignore incorrect Ansi color change data', () => { // this is testing a private method const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; From 64ac67026a1be56ff7a90b96995f8510aeeb9566 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 2 Dec 2020 16:25:19 +0000 Subject: [PATCH 17/31] avoid innerHTML usages --- src/browser/AccessibilityManager.ts | 2 +- src/browser/renderer/dom/DomRenderer.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index c55aaad9..f1a9b5d0 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -263,7 +263,7 @@ export class AccessibilityManager extends Disposable { const element = this._rowElements[i]; if (element) { if (lineData.length === 0) { - element.innerHTML = ' '; + element.innerText = '\u00a0;'; } else { element.textContent = lineData; } diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index ed2c340b..f0a92259 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -138,7 +138,7 @@ export class DomRenderer extends Disposable implements IRenderer { ` width: ${this.dimensions.actualCellWidth}px` + `}`; - this._dimensionsStyleElement.innerHTML = styles; + this._dimensionsStyleElement.textContent = styles; this._selectionContainer.style.height = this._viewportElement.style.height; this._screenElement.style.width = `${this.dimensions.canvasWidth}px`; @@ -237,7 +237,7 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`; - this._themeStyleElement.innerHTML = styles; + this._themeStyleElement.textContent = styles; } public onDevicePixelRatioChange(): void { @@ -348,7 +348,7 @@ export class DomRenderer extends Disposable implements IRenderer { public clear(): void { for (const e of this._rowElements) { - e.innerHTML = ''; + e.innerText = ''; } } @@ -359,7 +359,7 @@ export class DomRenderer extends Disposable implements IRenderer { for (let y = start; y <= end; y++) { const rowElement = this._rowElements[y]; - rowElement.innerHTML = ''; + rowElement.innerText = ''; const row = y + this._bufferService.buffer.ydisp; const lineData = this._bufferService.buffer.lines.get(row); From 2804bfa8b7403aa515ce14ae78fdd8a2110d96d1 Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Mon, 7 Dec 2020 23:20:10 +0100 Subject: [PATCH 18/31] Make safe references to colorManager in _changeAnsiColor --- src/browser/Terminal.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 9452833e..1c65559a 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -160,11 +160,13 @@ export class Terminal extends CoreTerminal implements ITerminal { } private _changeAnsiColor(event: IAnsiColorChangeEvent): void { + if (!this._colorManager) { return; } + const color = rgba.toColor(event.red, event.green, event.blue); - this._colorManager!.colors.ansi[event.colorIndex] = color; - this._renderService?.setColors(this._colorManager!.colors); - this.viewport?.onThemeChange(this._colorManager!.colors); + this._colorManager.colors.ansi[event.colorIndex] = color; + this._renderService?.setColors(this._colorManager.colors); + this.viewport?.onThemeChange(this._colorManager.colors); } public dispose(): void { From b2c4ed638bb61278bce8855d8a3dab5106ebf142 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Dec 2020 09:08:14 +0000 Subject: [PATCH 19/31] fix bad none-breaking whitespace --- src/browser/AccessibilityManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index f1a9b5d0..e5cbb372 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -263,7 +263,7 @@ export class AccessibilityManager extends Disposable { const element = this._rowElements[i]; if (element) { if (lineData.length === 0) { - element.innerText = '\u00a0;'; + element.innerText = '\u00a0'; } else { element.textContent = lineData; } From 7d3f5375a81d8efda13fa0e93e738a0f911a1afd Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Mon, 7 Dec 2020 23:20:52 +0100 Subject: [PATCH 20/31] Implement OSC 4 for list of colors --- src/browser/Terminal.ts | 11 +++++--- src/common/InputHandler.test.ts | 48 ++++++++++++++++++++++++++------- src/common/InputHandler.ts | 19 ++++++++----- src/common/Types.d.ts | 12 ++++++--- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 1c65559a..06ac57f3 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -162,11 +162,14 @@ export class Terminal extends CoreTerminal implements ITerminal { private _changeAnsiColor(event: IAnsiColorChangeEvent): void { if (!this._colorManager) { return; } - const color = rgba.toColor(event.red, event.green, event.blue); + event.colors.forEach(ansiColor => { + const color = rgba.toColor(ansiColor.red, ansiColor.green, ansiColor.blue); - this._colorManager.colors.ansi[event.colorIndex] = color; - this._renderService?.setColors(this._colorManager.colors); - this.viewport?.onThemeChange(this._colorManager.colors); + this._colorManager!.colors.ansi[ansiColor.colorIndex] = color; + }); + + this._renderService?.setColors(this._colorManager!.colors); + this.viewport?.onThemeChange(this._colorManager!.colors); } public dispose(): void { diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 57680052..05b6ea2f 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -1693,16 +1693,15 @@ describe('InputHandler', () => { }); }); describe('OSC', () => { - it('should parse correct Ansi color change data', () => { + it('4: should parse correct Ansi color change data', () => { // this is testing a private method const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; + const event = parseAnsiColorChange('19;rgb:a1/b2/c3'); - assert.deepEqual( - parseAnsiColorChange('19;rgb:a1/b2/c3'), - { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 } - ); + assert.isNotNull(event); + assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 }); }), - it('should ignore incorrect Ansi color change data', () => { + it('4: should ignore incorrect Ansi color change data', () => { // this is testing a private method const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; @@ -1711,12 +1710,43 @@ describe('InputHandler', () => { assert.isNull(parseAnsiColorChange('17;rgba:aa/bb/cc')); assert.isNull(parseAnsiColorChange('rgb:aa/bb/cc')); }); - it('should fire event on Ansi color change', (done) => { + it('4: should parse a list of Ansi color changes', () => { + // this is testing a private method + const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; + const event = parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:00/11/22;255;rgb:01/ef/2d'); + + assert.isNotNull(event); + assert.equal(event!.colors.length, 3); + assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 }); + assert.deepEqual(event!.colors[1], { colorIndex: 17, red: 0x00, green: 0x11, blue: 0x22 }); + assert.deepEqual(event!.colors[2], { colorIndex: 255, red: 0x01, green: 0xef, blue: 0x2d }); + }); + it('4: should ignore incorrect colors in a list of Ansi color changes', () => { + // this is testing a private method + const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; + const event = parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:WR/ON/G;255;rgb:01/ef/2d'); + + assert.equal(event!.colors.length, 2); + assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 }); + assert.deepEqual(event!.colors[1], { colorIndex: 255, red: 0x01, green: 0xef, blue: 0x2d }); + }); + it('4: should be case insensitive when parsing Ansi color changes', () => { + // this is testing a private method + const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; + const event = parseAnsiColorChange('19;rGb:A1/b2/C3'); + + assert.equal(event!.colors.length, 1); + assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 }); + }); + it('4: should fire event on Ansi color change', (done) => { inputHandler.onAnsiColorChange(e => { - assert.deepEqual(e, { colorIndex: 17, red: 0x1a, green: 0x2b, blue: 0x3c }); + assert.isNotNull(e); + assert.isNotNull(e!.colors); + assert.deepEqual(e!.colors[0], { colorIndex: 17, red: 0x1a, green: 0x2b, blue: 0x3c }); + assert.deepEqual(e!.colors[1], { colorIndex: 12, red: 0x11, green: 0x22, blue: 0x33 }); done(); }); - inputHandler.parse('\x1b]4;17;rgb:1a/2b/3c\x1b\\'); + inputHandler.parse('\x1b]4;17;rgb:1a/2b/3c;12;rgb:11/22/33\x1b\\'); }); }); }); diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 3560ee6a..44520cb9 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2715,19 +2715,25 @@ export class InputHandler extends Disposable implements IInputHandler { } private _parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { + const result: IAnsiColorChangeEvent = { colors: [] }; // example data: 5;rgb:aa/bb/cc - const regex = /(\d+);rgb:([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})\/([0-9a-fA-F]{2})/; - const match = data.match(regex); + const regex = /(\d+);rgb:([0-9a-f]{2})\/([0-9a-f]{2})\/([0-9a-f]{2})/gi; + let match; - if (match) { - return { + while ((match = regex.exec(data)) !== null) { + result.colors.push({ colorIndex: parseInt(match[1]), red: parseInt(match[2], 16), green: parseInt(match[3], 16), blue: parseInt(match[4], 16) - }; + }); } - return null; + + if (result.colors.length === 0) { + return null; + } + + return result; } /** @@ -2735,6 +2741,7 @@ export class InputHandler extends Disposable implements IInputHandler { * * @vt: #Y OSC 4 "Set ANSI color" "OSC 4 ; c ; spec BEL" "Change color number `c` to the color specified by `spec`." * `c` is the color index between 0 and 255. `spec` color format is 'rgb:hh/hh/hh' where `h` are hexadecimal digits. + * There may be multipe c ; spec elements present in the same instruction, e.g. 1;rgb:10/20/30;2;rgb:a0/b0/c0. */ public setAnsiColor(data: string): void { const event = this._parseAnsiColorChange(data); diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index cbb11b3f..1ea26c2a 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -328,16 +328,20 @@ export interface IWindowOptions { setWinLines?: boolean; } -/** - * Event fired for OSC 4 command - to change ANSI color based on its index. - */ -export interface IAnsiColorChangeEvent { +export interface IAnsiColorChangeEventColor { colorIndex: number; red: number; green: number; blue: number; } +/** + * Event fired for OSC 4 command - to change ANSI color based on its index. + */ +export interface IAnsiColorChangeEvent { + colors: IAnsiColorChangeEventColor[]; +} + /** * Calls the parser and handles actions generated by the parser. */ From 8baff9bade19dbde2f381311d31ece8077f0220c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 4 Jan 2021 08:31:22 -0800 Subject: [PATCH 21/31] cache buffer --- src/browser/public/Terminal.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 64c65fe9..4db82240 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -18,6 +18,7 @@ export class Terminal implements ITerminalApi { private _core: ITerminal; private _addonManager: AddonManager; private _parser: IParser | undefined; + private _buffer: BufferNamespaceApi | undefined; constructor(options?: ITerminalOptions) { this._core = new TerminalCore(options); @@ -58,7 +59,10 @@ export class Terminal implements ITerminalApi { public get cols(): number { return this._core.cols; } public get buffer(): IBufferNamespaceApi { this._checkProposedApi(); - return new BufferNamespaceApi(this._core.buffers); + if (!this._buffer) { + return new BufferNamespaceApi(this._core.buffers); + } + return this._buffer; } public get markers(): ReadonlyArray { this._checkProposedApi(); From fe2b1c0d43d661b3a63023cfedb118c6e0dbf08e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 4 Jan 2021 11:32:11 -0800 Subject: [PATCH 22/31] set this._buffer to new buffer --- src/browser/public/Terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 4db82240..6c485a5c 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -60,7 +60,7 @@ export class Terminal implements ITerminalApi { public get buffer(): IBufferNamespaceApi { this._checkProposedApi(); if (!this._buffer) { - return new BufferNamespaceApi(this._core.buffers); + this._buffer = new BufferNamespaceApi(this._core.buffers); } return this._buffer; } From b34289cc441ab09a4bb54c97edc4014a7e241e13 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 5 Jan 2021 07:10:27 -0800 Subject: [PATCH 23/31] use reset --- src/browser/public/Terminal.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 6c485a5c..517f7c4f 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -13,6 +13,7 @@ import * as Strings from '../LocalizableStrings'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { AddonManager } from './AddonManager'; import { IParams } from 'common/parser/Types'; +import { BufferSet } from 'common/buffer/BufferSet'; export class Terminal implements ITerminalApi { private _core: ITerminal; @@ -59,6 +60,7 @@ export class Terminal implements ITerminalApi { public get cols(): number { return this._core.cols; } public get buffer(): IBufferNamespaceApi { this._checkProposedApi(); + this._core.reset(); if (!this._buffer) { this._buffer = new BufferNamespaceApi(this._core.buffers); } From 714513083560612b30f127a36f6a366f2d84977e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Jan 2021 09:32:09 -0800 Subject: [PATCH 24/31] Fix onBufferChange event not working after reset Co-authored-by: Megan Rogge (megan.rogge@microsoft.com) --- src/browser/public/Terminal.ts | 19 +++++++++---------- src/common/buffer/BufferSet.ts | 18 ++++++++++-------- src/common/buffer/Types.d.ts | 1 + src/common/services/BufferService.ts | 3 +-- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 517f7c4f..70247f88 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -60,9 +60,8 @@ export class Terminal implements ITerminalApi { public get cols(): number { return this._core.cols; } public get buffer(): IBufferNamespaceApi { this._checkProposedApi(); - this._core.reset(); if (!this._buffer) { - this._buffer = new BufferNamespaceApi(this._core.buffers); + this._buffer = new BufferNamespaceApi(this._core); } return this._buffer; } @@ -251,21 +250,21 @@ class BufferNamespaceApi implements IBufferNamespaceApi { private _onBufferChange = new EventEmitter(); public get onBufferChange(): IEvent { return this._onBufferChange.event; } - constructor(private _buffers: IBufferSet) { - this._normal = new BufferApiView(this._buffers.normal, 'normal'); - this._alternate = new BufferApiView(this._buffers.alt, 'alternate'); - this._buffers.onBufferActivate(() => this._onBufferChange.fire(this.active)); + constructor(private _core: ITerminal) { + this._normal = new BufferApiView(this._core.buffers.normal, 'normal'); + this._alternate = new BufferApiView(this._core.buffers.alt, 'alternate'); + this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active)); } public get active(): IBufferApi { - if (this._buffers.active === this._buffers.normal) { return this.normal; } - if (this._buffers.active === this._buffers.alt) { return this.alternate; } + if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; } + if (this._core.buffers.active === this._core.buffers.alt) { return this.alternate; } throw new Error('Active buffer is neither normal nor alternate'); } public get normal(): IBufferApi { - return this._normal.init(this._buffers.normal); + return this._normal.init(this._core.buffers.normal); } public get alternate(): IBufferApi { - return this._alternate.init(this._buffers.alt); + return this._alternate.init(this._core.buffers.alt); } } diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index b9dc7995..b74c4eac 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -15,10 +15,9 @@ import { Disposable } from 'common/Lifecycle'; * provides also utilities for working with them. */ export class BufferSet extends Disposable implements IBufferSet { - private _normal: Buffer; - private _alt: Buffer; - private _activeBuffer: Buffer; - + private _normal!: Buffer; + private _alt!: Buffer; + private _activeBuffer!: Buffer; private _onBufferActivate = this.register(new EventEmitter<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}>()); public get onBufferActivate(): IEvent<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}> { return this._onBufferActivate.event; } @@ -28,17 +27,20 @@ export class BufferSet extends Disposable implements IBufferSet { * @param _terminal - The terminal the BufferSet will belong to */ constructor( - optionsService: IOptionsService, - bufferService: IBufferService + private readonly _optionsService: IOptionsService, + private readonly _bufferService: IBufferService ) { super(); + this.reset(); + } - this._normal = new Buffer(true, optionsService, bufferService); + public reset(): void { + this._normal = new Buffer(true, this._optionsService, this._bufferService); this._normal.fillViewportRows(); // The alt buffer should never have scrollback. // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer - this._alt = new Buffer(false, optionsService, bufferService); + this._alt = new Buffer(false, this._optionsService, this._bufferService); this._activeBuffer = this._normal; this.setupTabStops(); diff --git a/src/common/buffer/Types.d.ts b/src/common/buffer/Types.d.ts index 752b1a26..cbf40a03 100644 --- a/src/common/buffer/Types.d.ts +++ b/src/common/buffer/Types.d.ts @@ -56,6 +56,7 @@ export interface IBufferSet extends IDisposable { activateNormalBuffer(): void; activateAltBuffer(fillAttr?: IAttributeData): void; + reset(): void; resize(newCols: number, newRows: number): void; setupTabStops(i?: number): void; } diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 301146e1..47e54729 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -49,8 +49,7 @@ export class BufferService extends Disposable implements IBufferService { } public reset(): void { - this.buffers.dispose(); - this.buffers = new BufferSet(this._optionsService, this); + this.buffers.reset(); this.isUserScrolling = false; } } From cbe5abcf278524f73a771dc8b0f049edc6d59e83 Mon Sep 17 00:00:00 2001 From: Sun Xiaoran Date: Wed, 6 Jan 2021 14:10:10 +0800 Subject: [PATCH 25/31] Add Commas to the Real-world uses List --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4ef592c2..0e7df5ac 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**TeleType**](https://github.com/akshaykmr/TeleType): cli tool that allows you to share your terminal online conveniently. Show off mad cli-fu, help a colleague, teach, or troubleshoot. - [**Intervue**](https://www.intervue.io): Pair programming for interviews. Multiple programming languages supported, with results displayed by xterm.js. - [**TRASA**](https://trasa.io): Zero trust access to Web, SSH, RDP and Database services. +- [**Commas**](https://github.com/CyanSalt/commas): Commas is a hackable terminal and command runner. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only. From 5bab94730f35f0955d9a6546480083acef19c26b Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 8 Jan 2021 09:46:12 -0500 Subject: [PATCH 26/31] freeze the default ANSI colours const --- src/browser/ColorManager.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts index 9a0fd7ee..b6950d28 100644 --- a/src/browser/ColorManager.ts +++ b/src/browser/ColorManager.ts @@ -17,9 +17,8 @@ const DEFAULT_SELECTION = { rgba: 0xFFFFFF4D }; -// An IIFE to generate DEFAULT_ANSI_COLORS. Do not mutate DEFAULT_ANSI_COLORS, instead make a copy -// and mutate that. -export const DEFAULT_ANSI_COLORS = (() => { +// An IIFE to generate DEFAULT_ANSI_COLORS. +export const DEFAULT_ANSI_COLORS = Object.freeze((() => { const colors = [ // dark: css.toColor('#2e3436'), @@ -64,7 +63,7 @@ export const DEFAULT_ANSI_COLORS = (() => { } return colors; -})(); +})()); /** * Manages the source of truth for a terminal's colors. From 66be7797cf620d23e458b945eff1523ed5dfc9d8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 8 Jan 2021 11:02:34 -0800 Subject: [PATCH 27/31] Get demo working for git bash on windows Co-authored-by: Megan Rogge merogge@microsoft.com Co-authored-by: Daniel Imms daimms@microsoft.com --- demo/server.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/demo/server.js b/demo/server.js index c04c48ea..c0d5e1f6 100644 --- a/demo/server.js +++ b/demo/server.js @@ -43,15 +43,15 @@ function startServer() { const env = Object.assign({}, process.env); env['COLORTERM'] = 'truecolor'; var cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { - name: 'xterm-256color', - cols: cols || 80, - rows: rows || 24, - cwd: env.PWD, - env: env, - encoding: USE_BINARY ? null : 'utf8' - }); + rows = parseInt(req.query.rows), + term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { + name: 'xterm-256color', + cols: cols || 80, + rows: rows || 24, + cwd: process.platform === 'win32' ? undefined : env.PWD, + env: env, + encoding: USE_BINARY ? null : 'utf8' + }); console.log('Created terminal with PID: ' + term.pid); terminals[term.pid] = term; From 5c33e4008db483d54beec303c08bd01d821c190b Mon Sep 17 00:00:00 2001 From: Slawek Zachcial Date: Sat, 9 Jan 2021 14:30:24 +0100 Subject: [PATCH 28/31] Make _parseAnsiColorChange available for tests in TestInputHandler --- src/common/InputHandler.test.ts | 23 +++++++++-------------- src/common/InputHandler.ts | 2 +- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 05b6ea2f..4c0d8559 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -40,6 +40,7 @@ class TestInputHandler extends InputHandler { public get curAttrData(): IAttributeData { return (this as any)._curAttrData; } public get windowTitleStack(): string[] { return this._windowTitleStack; } public get iconNameStack(): string[] { return this._iconNameStack; } + public parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { return this._parseAnsiColorChange(data); } } describe('InputHandler', () => { @@ -1695,25 +1696,21 @@ describe('InputHandler', () => { describe('OSC', () => { it('4: should parse correct Ansi color change data', () => { // this is testing a private method - const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; - const event = parseAnsiColorChange('19;rgb:a1/b2/c3'); + const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3'); assert.isNotNull(event); assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 }); }), it('4: should ignore incorrect Ansi color change data', () => { // this is testing a private method - const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; - - assert.isNull(parseAnsiColorChange('17;rgb:a/b/c')); - assert.isNull(parseAnsiColorChange('17;rgb:#aabbcc')); - assert.isNull(parseAnsiColorChange('17;rgba:aa/bb/cc')); - assert.isNull(parseAnsiColorChange('rgb:aa/bb/cc')); + assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:a/b/c')); + assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:#aabbcc')); + assert.isNull(inputHandler.parseAnsiColorChange('17;rgba:aa/bb/cc')); + assert.isNull(inputHandler.parseAnsiColorChange('rgb:aa/bb/cc')); }); it('4: should parse a list of Ansi color changes', () => { // this is testing a private method - const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; - const event = parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:00/11/22;255;rgb:01/ef/2d'); + const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:00/11/22;255;rgb:01/ef/2d'); assert.isNotNull(event); assert.equal(event!.colors.length, 3); @@ -1723,8 +1720,7 @@ describe('InputHandler', () => { }); it('4: should ignore incorrect colors in a list of Ansi color changes', () => { // this is testing a private method - const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; - const event = parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:WR/ON/G;255;rgb:01/ef/2d'); + const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:WR/ON/G;255;rgb:01/ef/2d'); assert.equal(event!.colors.length, 2); assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 }); @@ -1732,8 +1728,7 @@ describe('InputHandler', () => { }); it('4: should be case insensitive when parsing Ansi color changes', () => { // this is testing a private method - const parseAnsiColorChange = inputHandler['_parseAnsiColorChange']; - const event = parseAnsiColorChange('19;rGb:A1/b2/C3'); + const event = inputHandler.parseAnsiColorChange('19;rGb:A1/b2/C3'); assert.equal(event!.colors.length, 1); assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 }); diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 44520cb9..626e1fe2 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2714,7 +2714,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._iconName = data; } - private _parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { + protected _parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { const result: IAnsiColorChangeEvent = { colors: [] }; // example data: 5;rgb:aa/bb/cc const regex = /(\d+);rgb:([0-9a-f]{2})\/([0-9a-f]{2})\/([0-9a-f]{2})/gi; From 19c14b076b018ac3faa86dff2f147757528a4b84 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Jan 2021 07:40:25 -0800 Subject: [PATCH 29/31] Remove resolved TODO --- src/browser/renderer/atlas/CharAtlasUtils.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/browser/renderer/atlas/CharAtlasUtils.ts b/src/browser/renderer/atlas/CharAtlasUtils.ts index 2b876f2b..20695d3c 100644 --- a/src/browser/renderer/atlas/CharAtlasUtils.ts +++ b/src/browser/renderer/atlas/CharAtlasUtils.ts @@ -16,10 +16,6 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number cursor: undefined, cursorAccent: undefined, selection: undefined, - // For the static char atlas, we only use the first 16 colors, but we need all 256 for the - // dynamic character atlas. - // ansi: colors.ansi.slice(0, 16) - // TODO: Using entire array to support OSC 4; can this break anything? ansi: colors.ansi }; return { From 5ada5f0c0c1e9cf221706607414e34ffa554b0a1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Jan 2021 08:31:56 -0800 Subject: [PATCH 30/31] Update yarn.lock for font-finder Previous update was likely done using npm, not yarn --- addons/xterm-addon-ligatures/yarn.lock | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-ligatures/yarn.lock b/addons/xterm-addon-ligatures/yarn.lock index d8e64e49..049b798a 100644 --- a/addons/xterm-addon-ligatures/yarn.lock +++ b/addons/xterm-addon-ligatures/yarn.lock @@ -78,13 +78,21 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -font-finder@^1.0.3, font-finder@^1.0.4: +font-finder@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.0.4.tgz#2ca944954dd8d0e1b5bdc4c596cc08607761d89b" dependencies: get-system-fonts "^2.0.0" promise-stream-reader "^1.0.1" +font-finder@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.1.0.tgz#2bff2b2762acba720239c8bec898a96daae90858" + integrity sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw== + dependencies: + get-system-fonts "^2.0.0" + promise-stream-reader "^1.0.1" + font-ligatures@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/font-ligatures/-/font-ligatures-1.3.3.tgz#63fff18dc8adb3a11fe5eec1f4e8d7edfa8075b9" From 519e44d30e45c86b8c9f27a2e2e49a1bc2ae31b0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 11 Jan 2021 09:19:53 -0800 Subject: [PATCH 31/31] Clean up, improve comments Co-authored-by: Megan Rogge --- addons/xterm-addon-search/src/SearchAddon.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 54a4a198..efc12662 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -158,9 +158,11 @@ export class SearchAddon implements ITerminalAddon { }; if (incremental) { - result = this._findInLine(term, searchPosition, searchOptions, false); // Try to expand selection to right first. - if (!(result && result.row === startRow && result.col === startCol)) { - // If selection was not able to be expanded to right, then reverse search begin. + // Try to expand selection to right first. + result = this._findInLine(term, searchPosition, searchOptions, false); + const isOldResultHighlighted = result && result.row === startRow && result.col === startCol; + if (!isOldResultHighlighted) { + // If selection was not able to be expanded to the right, then try reverse search if (currentSelection) { searchPosition.startRow = currentSelection.endRow; searchPosition.startCol = currentSelection.endColumn; @@ -250,6 +252,7 @@ export class SearchAddon implements ITerminalAddon { * @param term The search term. * @param position The position to start the search. * @param searchOptions Search options. + * @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left. * @return The search result if it was found. */ protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {