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. 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": { 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" diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index f5505689..efc12662 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -158,8 +158,15 @@ export class SearchAddon implements ITerminalAddon { }; if (incremental) { + // Try to expand selection to right first. result = this._findInLine(term, searchPosition, searchOptions, false); - if (!(result && result.row === startRow && result.col === startCol)) { + 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; + } result = this._findInLine(term, searchPosition, searchOptions, true); } } else { @@ -245,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 { 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; diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index c55aaad9..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.innerHTML = ' '; + element.innerText = '\u00a0'; } else { element.textContent = lineData; } 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. diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index f8b8b3e4..06ac57f3 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, 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'; @@ -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 { 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; @@ -148,6 +149,7 @@ 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(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter)); @@ -157,6 +159,19 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows))); } + private _changeAnsiColor(event: IAnsiColorChangeEvent): void { + if (!this._colorManager) { return; } + + event.colors.forEach(ansiColor => { + const color = rgba.toColor(ansiColor.red, ansiColor.green, ansiColor.blue); + + this._colorManager!.colors.ansi[ansiColor.colorIndex] = color; + }); + + this._renderService?.setColors(this._colorManager!.colors); + this.viewport?.onThemeChange(this._colorManager!.colors); + } + public dispose(): void { if (this._isDisposed) { return; diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 64c65fe9..70247f88 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -13,11 +13,13 @@ 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; private _addonManager: AddonManager; private _parser: IParser | undefined; + private _buffer: BufferNamespaceApi | undefined; constructor(options?: ITerminalOptions) { this._core = new TerminalCore(options); @@ -58,7 +60,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) { + this._buffer = new BufferNamespaceApi(this._core); + } + return this._buffer; } public get markers(): ReadonlyArray { this._checkProposedApi(); @@ -245,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/browser/renderer/atlas/CharAtlasUtils.ts b/src/browser/renderer/atlas/CharAtlasUtils.ts index 346b35f8..20695d3c 100644 --- a/src/browser/renderer/atlas/CharAtlasUtils.ts +++ b/src/browser/renderer/atlas/CharAtlasUtils.ts @@ -16,9 +16,7 @@ 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) + ansi: colors.ansi }; return { devicePixelRatio: window.devicePixelRatio, 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); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 46f8eacc..4c0d8559 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'; @@ -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', () => { @@ -1692,4 +1693,55 @@ describe('InputHandler', () => { assert.equal(coreService.decPrivateModes.origin, false); }); }); + describe('OSC', () => { + it('4: should parse correct Ansi color change data', () => { + // this is testing a private method + 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 + 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 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); + 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 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 }); + 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 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 }); + }); + it('4: should fire event on Ansi color change', (done) => { + inputHandler.onAnsiColorChange(e => { + 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;12;rgb:11/22/33\x1b\\'); + }); + }); }); diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index dbf695e6..626e1fe2 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, 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,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,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) => 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) @@ -2711,6 +2714,45 @@ export class InputHandler extends Disposable implements IInputHandler { this._iconName = data; } + 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; + let match; + + 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) + }); + } + + if (result.colors.length === 0) { + return null; + } + + return result; + } + + /** + * 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: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); + if (event) { + this._onAnsiColorChange.fire(event); + } + else { + this._logService.warn(`Expected format ;rgb:// but got data: ${data}`); + } + } + /** * ESC E * C1.NEL diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index bd0d11c6..1ea26c2a 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -328,6 +328,20 @@ export interface IWindowOptions { setWinLines?: boolean; } +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. */ @@ -389,6 +403,7 @@ export interface IInputHandler { /** CSI ' ~ */ deleteColumns(params: IParams): void; /** OSC 0 OSC 2 */ setTitle(data: string): void; + /** OSC 4 */ setAnsiColor(data: string): void; /** ESC E */ nextLine(): void; /** ESC = */ keypadApplicationMode(): void; /** ESC > */ keypadNumericMode(): void; 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/input/Keyboard.ts b/src/common/input/Keyboard.ts index fdb777b4..20a28f61 100644 --- a/src/common/input/Keyboard.ts +++ b/src/common/input/Keyboard.ts @@ -356,7 +356,7 @@ export function evaluateKeyboardEvent( const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32; result.key = C0.ESC + String.fromCharCode(keyCode); } - } else if (isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { + } else if (isMac && !ev.altKey && !ev.ctrlKey && !ev.shiftKey && ev.metaKey) { if (ev.keyCode === 65) { // cmd + a result.type = KeyboardResultType.SELECT_ALL; } 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; } }