diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index d9127e7b..110e4f51 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -8,8 +8,8 @@ import { InputHandler } from 'common/InputHandler'; import { IBufferLine, IAttributeData, IColorEvent, ColorIndex, ColorRequestType } from 'common/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { Attributes, UnderlineStyle } from 'common/buffer/Constants'; -import { AttributeData } from 'common/buffer/AttributeData'; +import { Attributes, BgFlags, UnderlineStyle } from 'common/buffer/Constants'; +import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockOptionsService, MockLogService, MockCoreMouseService, MockCharsetService, MockUnicodeService, MockOscLinkService } from 'common/TestUtils.test'; import { IBufferService, ICoreService } from 'common/services/Services'; @@ -1689,6 +1689,37 @@ describe('InputHandler', () => { }); }); + describe('reset text attributes (SGR 0)', () => { + it('resets all attributes if there is no url', async () => { + await inputHandler.parseP('\x1b[30m\x1b[40m\x1b[4m'); + assert.notEqual(inputHandler.curAttrData.fg, 0); + assert.notEqual(inputHandler.curAttrData.bg, 0); + assert.isFalse(inputHandler.curAttrData.extended.isEmpty()); + + await inputHandler.parseP('\x1b[m'); + assert.equal(inputHandler.curAttrData.fg, 0); + assert.equal(inputHandler.curAttrData.bg, 0); + assert.isTrue(inputHandler.curAttrData.extended.isEmpty()); + }); + + it('resets all attributes except for the url', async () => { + await inputHandler.parseP('\x1b[30m\x1b[40m\x1b[4m'); + await inputHandler.parseP('\x1b]8;;http://example.com\x1b\\'); + assert.notEqual(inputHandler.curAttrData.fg, 0); + assert.notEqual(inputHandler.curAttrData.bg, 0); + assert.notEqual(inputHandler.curAttrData.extended.ext, 0); + const urlId = inputHandler.curAttrData.extended.urlId; + assert.notEqual(urlId, 0); + + await inputHandler.parseP('\x1b[m'); + assert.equal(inputHandler.curAttrData.fg, 0); + assert.equal(inputHandler.curAttrData.bg, BgFlags.HAS_EXTENDED); + const expectedExtended = new ExtendedAttrs(); + expectedExtended.urlId = urlId; + assert.deepEqual(inputHandler.curAttrData.extended, expectedExtended); + }); + }); + describe('extended underline style support (SGR 4)', () => { beforeEach(() => { bufferService.resize(10, 5); diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 95ec00c1..8b6a480e 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -121,7 +121,6 @@ export class InputHandler extends Disposable implements IInputHandler { private _workCell: CellData = new CellData(); private _windowTitle = ''; private _iconName = ''; - private _currentLinkId?: number; private _dirtyRowTracker: IDirtyRowTracker; protected _windowTitleStack: string[] = []; protected _iconNameStack: string[] = []; @@ -406,6 +405,10 @@ export class InputHandler extends Disposable implements IInputHandler { } } + private _getCurrentLinkId(): number { + return this._curAttrData.extended.urlId; + } + /** * Parse call with async handler support. * @@ -533,8 +536,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (screenReaderMode) { this._onA11yChar.fire(stringFromCodePoint(code)); } - if (this._currentLinkId !== undefined) { - this._oscLinkService.addLineToLink(this._currentLinkId, this._activeBuffer.ybase + this._activeBuffer.y); + if (this._getCurrentLinkId()) { + this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y); } // insert combining char at last cursor position @@ -2353,6 +2356,17 @@ export class InputHandler extends Disposable implements IInputHandler { attr.updateExtended(); } + private _processSGR0(attr: IAttributeData): void { + attr.fg = DEFAULT_ATTR_DATA.fg; + attr.bg = DEFAULT_ATTR_DATA.bg; + attr.extended = attr.extended.clone(); + // Reset underline style and color. Note that we don't want to reset other + // fields such as the url id. + attr.extended.underlineStyle = UnderlineStyle.NONE; + attr.extended.underlineColor &= ~(Attributes.CM_MASK | Attributes.RGB_MASK); + attr.updateExtended(); + } + /** * CSI Pm m Character Attributes (SGR). * @@ -2438,8 +2452,7 @@ export class InputHandler extends Disposable implements IInputHandler { public charAttributes(params: IParams): boolean { // Optimize a single SGR0. if (params.length === 1 && params.params[0] === 0) { - this._curAttrData.fg = DEFAULT_ATTR_DATA.fg; - this._curAttrData.bg = DEFAULT_ATTR_DATA.bg; + this._processSGR0(this._curAttrData); return true; } @@ -2467,8 +2480,7 @@ export class InputHandler extends Disposable implements IInputHandler { attr.bg |= Attributes.CM_P16 | (p - 100) | 8; } else if (p === 0) { // default - attr.fg = DEFAULT_ATTR_DATA.fg; - attr.bg = DEFAULT_ATTR_DATA.bg; + this._processSGR0(attr); } else if (p === 1) { // bold text attr.fg |= FgFlags.BOLD; @@ -2935,7 +2947,7 @@ export class InputHandler extends Disposable implements IInputHandler { private _createHyperlink(params: string, uri: string): boolean { // It's legal to open a new hyperlink without explicitly finishing the previous one - if (this._currentLinkId !== undefined) { + if (this._getCurrentLinkId()) { this._finishHyperlink(); } const parsedParams = params.split(':'); @@ -2945,8 +2957,7 @@ export class InputHandler extends Disposable implements IInputHandler { id = parsedParams[idParamIndex].slice(3) || undefined; } this._curAttrData.extended = this._curAttrData.extended.clone(); - this._currentLinkId = this._oscLinkService.registerLink({ id, uri }); - this._curAttrData.extended.urlId = this._currentLinkId; + this._curAttrData.extended.urlId = this._oscLinkService.registerLink({ id, uri }); this._curAttrData.updateExtended(); return true; } @@ -2955,7 +2966,6 @@ export class InputHandler extends Disposable implements IInputHandler { this._curAttrData.extended = this._curAttrData.extended.clone(); this._curAttrData.extended.urlId = 0; this._curAttrData.updateExtended(); - this._currentLinkId = undefined; return true; }