Merge branch 'master' into 2498_alt_enter

This commit is contained in:
Daniel Imms
2020-04-10 12:53:16 -07:00
committed by GitHub
92 changed files with 4760 additions and 3397 deletions
+1
View File
@@ -56,6 +56,7 @@ export class AccessibilityManager extends Disposable {
this._rowContainer = document.createElement('div');
this._rowContainer.classList.add('xterm-accessibility-tree');
this._rowContainer.setAttribute('role', 'list');
this._rowElements = [];
for (let i = 0; i < this._terminal.rows; i++) {
this._rowElements[i] = this._createAccessibilityTreeNode();
+49
View File
@@ -1445,4 +1445,53 @@ describe('InputHandler', () => {
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
});
});
describe('DECSTR', () => {
let term: TestTerminal;
beforeEach(() => {
term = new TestTerminal({cols: 10, rows: 5, scrollback: 1});
term.writeSync('01234567890123');
});
it('should reset IRM', () => {
term.writeSync('\x1b[4h');
assert.equal(term.insertMode, true);
term.writeSync('\x1b[!p');
assert.equal(term.insertMode, false);
});
it('should reset cursor visibility', () => {
term.writeSync('\x1b[?25l');
assert.equal((term as any)._coreService.isCursorHidden, true);
term.writeSync('\x1b[!p');
assert.equal((term as any)._coreService.isCursorHidden, false);
});
it('should reset scroll margins', () => {
term.writeSync('\x1b[2;4r');
assert.equal((term as any)._bufferService.buffer.scrollTop, 1);
assert.equal((term as any)._bufferService.buffer.scrollBottom, 3);
term.writeSync('\x1b[!p');
assert.equal((term as any)._bufferService.buffer.scrollTop, 0);
assert.equal((term as any)._bufferService.buffer.scrollBottom, term.rows - 1);
});
it('should reset text attributes', () => {
term.writeSync('\x1b[1;2;32;43m');
assert.equal(!!term.curAttrData.isBold(), true);
term.writeSync('\x1b[!p');
assert.equal(!!term.curAttrData.isBold(), false);
assert.equal(term.curAttrData.fg, 0);
assert.equal(term.curAttrData.bg, 0);
});
it('should reset DECSC data', () => {
term.writeSync('\x1b7');
assert.equal((term as any)._bufferService.buffer.savedX, 4);
assert.equal((term as any)._bufferService.buffer.savedY, 1);
term.writeSync('\x1b[!p');
assert.equal((term as any)._bufferService.buffer.savedX, 0);
assert.equal((term as any)._bufferService.buffer.savedY, 0);
});
it('should reset DECOM', () => {
term.writeSync('\x1b[?6h');
assert.equal((term as any)._coreService.decPrivateModes.origin, true);
term.writeSync('\x1b[!p');
assert.equal((term as any)._coreService.decPrivateModes.origin, false);
});
});
});
+14 -4
View File
@@ -1998,7 +1998,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._coreMouseService.activeEncoding = 'DEFAULT';
break;
case 1015: // urxvt ext mode mouse - removed in #2507
this._logService.debug('DECRST 1015 not supported (see #2507)');
this._logService.debug('DECRST 1015 not supported (see #2507)');
break;
case 25: // hide cursor
this._coreService.isCursorHidden = true;
@@ -2007,7 +2007,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this.restoreCursor();
break;
case 1049: // alt screen buffer cursor
// FALL-THROUGH
// FALL-THROUGH
case 47: // normal screen buffer
case 1047: // normal screen buffer - clearing it first
// Ensure the selection manager has the correct buffer
@@ -2344,11 +2344,12 @@ export class InputHandler extends Disposable implements IInputHandler {
* DECSTR only resets certain attributes. For most needs DECSTR should be sufficient.
*
* The following terminal attributes are reset to default values:
* - cursor is reset (default = visible, home position)
* - IRM is reset (dafault = false)
* - scroll margins are reset (default = viewport size)
* - erase attributes are reset to default
* - charsets are reset
* - DECSC data is reset to initial values
* - DECOM is reset to absolute mode
*
*
* FIXME: there are several more attributes missing (see VT520 manual)
@@ -2360,9 +2361,18 @@ export class InputHandler extends Disposable implements IInputHandler {
this._bufferService.buffer.scrollTop = 0;
this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1;
this._curAttrData = DEFAULT_ATTR_DATA.clone();
this._bufferService.buffer.x = this._bufferService.buffer.y = 0; // ?
this._coreService.reset();
this._charsetService.reset();
// reset DECSC data
this._bufferService.buffer.savedX = 0;
this._bufferService.buffer.savedY = this._bufferService.buffer.ybase;
this._bufferService.buffer.savedCurAttrData.fg = this._curAttrData.fg;
this._bufferService.buffer.savedCurAttrData.bg = this._curAttrData.bg;
this._bufferService.buffer.savedCharset = this._charsetService.charset;
// reset DECOM
this._coreService.decPrivateModes.origin = false;
}
/**
+26 -18
View File
@@ -37,7 +37,7 @@ import * as Strings from 'browser/LocalizableStrings';
import { SoundService } from 'browser/services/SoundService';
import { MouseZoneManager } from 'browser/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm';
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types';
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
@@ -57,11 +57,12 @@ import { MouseService } from 'browser/services/MouseService';
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
import { CoreService } from 'common/services/CoreService';
import { LogService } from 'common/services/LogService';
import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types';
import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
import { DirtyRowService } from 'common/services/DirtyRowService';
import { InstantiationService } from 'common/services/InstantiationService';
import { CoreMouseService } from 'common/services/CoreMouseService';
import { WriteBuffer } from 'common/input/WriteBuffer';
import { Linkifier2 } from 'browser/Linkifier2';
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
import { UnicodeService } from 'common/services/UnicodeService';
import { CharsetService } from 'common/services/CharsetService';
@@ -131,6 +132,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _inputHandler: InputHandler;
public linkifier: ILinkifier;
public linkifier2: ILinkifier2;
public viewport: IViewport;
private _compositionHelper: ICompositionHelper;
private _mouseZoneManager: IMouseZoneManager;
@@ -228,7 +230,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._windowsMode = undefined;
this._renderService?.dispose();
this._customKeyEventHandler = null;
this.write = () => {};
this.write = () => { };
this.element?.parentNode?.removeChild(this.element);
}
@@ -257,6 +259,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
if (!this.linkifier) {
this.linkifier = new Linkifier(this._bufferService, this._logService, this.optionsService, this.unicodeService);
}
if (!this.linkifier2) {
this.linkifier2 = new Linkifier2(this._bufferService);
}
if (this.options.windowsMode) {
this._enableWindowsMode();
@@ -414,7 +419,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
copyHandler(event, this._selectionService);
}));
const pasteHandlerWrapper = (event: ClipboardEvent) => handlePasteEvent(event, this.textarea, this.bracketedPasteMode, this._coreService);
const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea, this.bracketedPasteMode, this._coreService);
this.register(addDisposableDomListener(this.textarea, 'paste', pasteHandlerWrapper));
this.register(addDisposableDomListener(this.element, 'paste', pasteHandlerWrapper));
@@ -471,7 +476,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
if (!document.body.contains(parent)) {
this._logService.warn('Terminal.open was called on an element that was not attached to the DOM');
this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
}
this._document = parent.ownerDocument;
@@ -585,6 +590,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.register(this._mouseZoneManager);
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.element, this._mouseService, this._renderService);
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
@@ -619,8 +625,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _createRenderer(): IRenderer {
switch (this.options.rendererType) {
case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager.colors, this.screenElement, this.linkifier);
case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier);
case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager.colors, this.screenElement, this.linkifier, this.linkifier2);
case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier, this.linkifier2);
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
}
}
@@ -657,10 +663,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// send event to CoreMouseService
function sendEvent(ev: MouseEvent | WheelEvent): boolean {
let pos;
// get mouse coordinates
pos = self._mouseService.getRawByteCoords(ev, self.screenElement, self.cols, self.rows);
const pos = self._mouseService.getRawByteCoords(ev, self.screenElement, self.cols, self.rows);
if (!pos) {
return false;
}
@@ -679,8 +683,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
} else {
// according to MDN buttons only reports up to button 5 (AUX2)
but = ev.buttons & 1 ? CoreMouseButton.LEFT :
ev.buttons & 4 ? CoreMouseButton.MIDDLE :
ev.buttons & 2 ? CoreMouseButton.RIGHT :
ev.buttons & 4 ? CoreMouseButton.MIDDLE :
ev.buttons & 2 ? CoreMouseButton.RIGHT :
CoreMouseButton.NONE; // fallback to NONE
}
break;
@@ -729,13 +733,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* Note: 'mousedown' currently is "always on" and not managed
* by onProtocolChange.
*/
const requestedEvents: {[key: string]: ((ev: Event) => void) | null} = {
const requestedEvents: { [key: string]: ((ev: Event) => void) | null } = {
mouseup: null,
wheel: null,
mousedrag: null,
mousemove: null
};
const eventListeners: {[key: string]: (ev: Event) => void} = {
const eventListeners: { [key: string]: (ev: Event) => void } = {
mouseup: (ev: MouseEvent) => {
sendEvent(ev);
if (!ev.buttons) {
@@ -858,7 +862,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
// Construct and send sequences
const sequence = C0.ESC + (this._coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + ( ev.deltaY < 0 ? 'A' : 'B');
const sequence = C0.ESC + (this._coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
let data = '';
for (let i = 0; i < Math.abs(amount); i++) {
data += sequence;
@@ -981,7 +985,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
} else {
// scrollTop is non-zero which means no line will be going to the
// scrollback, instead we can just shift them in-place.
const scrollRegionHeight = bottomRow - topRow + 1/*as it's zero-based*/;
const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */;
this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);
this.buffer.lines.set(bottomRow, newLine.clone());
}
@@ -1121,6 +1125,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
}
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
return this.linkifier2.registerLinkProvider(linkProvider);
}
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
const joinerId = this._renderService.registerCharacterJoiner(handler);
this.refresh(0, this.rows - 1);
@@ -1273,8 +1281,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _isThirdLevelShift(browser: IBrowser, ev: IKeyboardEvent): boolean {
const thirdLevelKey =
(browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||
(browser.isWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);
(browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||
(browser.isWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);
if (ev.type === 'keypress') {
return thirdLevelKey;
+22 -18
View File
@@ -9,10 +9,10 @@ import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset } from 'common/Types';
import { Buffer } from 'common/buffer/Buffer';
import * as Browser from 'common/Platform';
import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm';
import { IDisposable, IMarker, IEvent, ISelectionPosition, ILinkProvider } from 'xterm';
import { Terminal } from './Terminal';
import { AttributeData } from 'common/buffer/AttributeData';
import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport } from 'browser/Types';
import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport, ILinkifier2 } from 'browser/Types';
import { IOptionsService, IUnicodeService } from 'common/services/Services';
import { EventEmitter } from 'common/EventEmitter';
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
@@ -36,9 +36,9 @@ export class MockTerminal implements ITerminal {
onBinary: IEvent<string>;
onTitleChange: IEvent<string>;
onScroll: IEvent<number>;
onKey: IEvent<{ key: string; domEvent: KeyboardEvent; }>;
onRender: IEvent<{ start: number; end: number; }>;
onResize: IEvent<{ cols: number; rows: number; }>;
onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>;
onRender: IEvent<{ start: number, end: number }>;
onResize: IEvent<{ cols: number, rows: number }>;
markers: IMarker[];
optionsService: IOptionsService;
unicodeService: IUnicodeService;
@@ -94,6 +94,9 @@ export class MockTerminal implements ITerminal {
deregisterLinkMatcher(matcherId: number): void {
throw new Error('Method not implemented.');
}
registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
throw new Error('Method not implemented.');
}
hasSelection(): boolean {
throw new Error('Method not implemented.');
}
@@ -136,6 +139,7 @@ export class MockTerminal implements ITerminal {
bracketedPasteMode: boolean;
renderer: IRenderer;
linkifier: ILinkifier;
linkifier2: ILinkifier2;
isFocused: boolean;
options: ITerminalOptions = {};
element: HTMLElement;
@@ -247,7 +251,7 @@ export class MockBuffer implements IBuffer {
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
}
getWrappedRangeForLine(y: number): { first: number; last: number; } {
getWrappedRangeForLine(y: number): { first: number, last: number } {
return Buffer.prototype.getWrappedRangeForLine.apply(this, arguments);
}
nextStop(x?: number): number {
@@ -278,8 +282,8 @@ export class MockBuffer implements IBuffer {
export class MockRenderer implements IRenderer {
onRequestRefreshRows: IEvent<IRequestRefreshRowsEvent>;
onCanvasResize: IEvent<{ width: number; height: number; }>;
onRender: IEvent<{ start: number; end: number; }>;
onCanvasResize: IEvent<{ width: number, height: number }>;
onRender: IEvent<{ start: number, end: number }>;
dispose(): void {
throw new Error('Method not implemented.');
}
@@ -300,16 +304,16 @@ export class MockRenderer implements IRenderer {
setColors(colors: IColorSet): void {
throw new Error('Method not implemented.');
}
onResize(cols: number, rows: number): void {}
onCharSizeChanged(): void {}
onBlur(): void {}
onFocus(): void {}
onSelectionChanged(start: [number, number], end: [number, number]): void {}
onCursorMove(): void {}
onOptionsChanged(): void {}
onDevicePixelRatioChange(): void {}
clear(): void {}
renderRows(start: number, end: number): void {}
onResize(cols: number, rows: number): void { }
onCharSizeChanged(): void { }
onBlur(): void { }
onFocus(): void { }
onSelectionChanged(start: [number, number], end: [number, number]): void { }
onCursorMove(): void { }
onOptionsChanged(): void { }
onDevicePixelRatioChange(): void { }
clear(): void { }
renderRows(start: number, end: number): void { }
registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
deregisterCharacterJoiner(): boolean { return true; }
}
+4 -2
View File
@@ -3,10 +3,10 @@
* @license MIT
*/
import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition } from 'xterm';
import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
import { ICharset, IAttributeData, CharData, CoreMouseEventType } from 'common/Types';
import { IEvent, IEventEmitter } from 'common/EventEmitter';
import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport } from 'browser/Types';
import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
import { IOptionsService, IUnicodeService } from 'common/services/Services';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
@@ -179,6 +179,7 @@ export interface IPublicTerminal extends IDisposable {
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number;
deregisterLinkMatcher(matcherId: number): void;
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
deregisterCharacterJoiner(joinerId: number): void;
addMarker(cursorYOffset: number): IMarker;
@@ -212,6 +213,7 @@ export interface IElementAccessor {
export interface ILinkifierAccessor {
linkifier: ILinkifier;
linkifier2: ILinkifier2;
}
// TODO: The options that are not in the public API should be reviewed
+1 -1
View File
@@ -11,7 +11,7 @@ describe('ColorManager', () => {
let cm: ColorManager;
let dom: jsdom.JSDOM;
let document: Document;
let window: Window;
let window: jsdom.DOMWindow;
beforeEach(() => {
dom = new jsdom.JSDOM('');
+1 -1
View File
@@ -192,7 +192,7 @@ export class ColorManager implements IColorManager {
const rgba: number = channels.toRgba(r, g, b, alpha);
return {
rgba,
css: channels.toCss(r, g, b, alpha)
css
};
}
+40
View File
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { addDisposableDomListener } from './Lifecycle';
import jsdom = require('jsdom');
describe('addDisposableDomListener', () => {
const dom = new jsdom.JSDOM();
const document = dom.window.document;
function createEvent(type: string): Event {
const event = document.createEvent('Event');
event.initEvent(type);
return event;
}
it('dispose', () => {
let calledTimes = 0;
const div = document.createElement('div');
const disposable = addDisposableDomListener(div, 'test', () => { calledTimes++; });
assert.equal(calledTimes, 0);
div.dispatchEvent(createEvent('test'));
assert.equal(calledTimes, 1);
disposable.dispose();
div.dispatchEvent(createEvent('test'));
assert.equal(calledTimes, 1);
disposable.dispose(); // double disposing
div.dispatchEvent(createEvent('test'));
assert.equal(calledTimes, 1);
});
});
+1 -1
View File
@@ -20,7 +20,7 @@ export function addDisposableDomListener(
let disposed = false;
return {
dispose: () => {
if (!disposed) {
if (disposed) {
return;
}
disposed = true;
+76
View File
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { IBufferService } from 'common/services/Services';
import { Linkifier2 } from 'browser/Linkifier2';
import { MockBufferService } from 'common/TestUtils.test';
import { ILink } from 'browser/Types';
class TestLinkifier2 extends Linkifier2 {
constructor(bufferService: IBufferService) {
super(bufferService);
}
public linkHover(element: HTMLElement, link: ILink, event: MouseEvent): void {
this._linkHover(element, link, event);
}
public linkLeave(element: HTMLElement, link: ILink, event: MouseEvent): void {
this._linkLeave(element, link, event);
}
}
describe('Linkifier2', () => {
let bufferService: IBufferService;
let linkifier: TestLinkifier2;
beforeEach(() => {
bufferService = new MockBufferService(100, 10);
linkifier = new TestLinkifier2(bufferService);
});
const link: ILink = {
text: 'foo',
range: {
start: {
x: 5,
y: 1
},
end: {
x: 7,
y: 1
}
},
activate: () => { }
};
it('onLinkHover event range is correct', done => {
linkifier.onLinkHover(e => {
assert.equal(link.range.start.x - 1, e.x1);
assert.equal(link.range.start.y - 1, e.y1);
assert.equal(link.range.end.x, e.x2);
assert.equal(link.range.end.y - 1, e.y2);
done();
});
linkifier.linkHover({ classList: { add: () => { } } } as any, link, {} as any);
});
it('onLinkLeave event range is correct', done => {
linkifier.onLinkLeave(e => {
assert.equal(link.range.start.x - 1, e.x1);
assert.equal(link.range.start.y - 1, e.y1);
assert.equal(link.range.end.x, e.x2);
assert.equal(link.range.end.y - 1, e.y2);
done();
});
linkifier.linkLeave({ classList: { add: () => { } } } as any, link, {} as any);
});
});
+243
View File
@@ -0,0 +1,243 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent } from './Types';
import { IDisposable } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
import { IBufferService } from 'common/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
export class Linkifier2 implements ILinkifier2 {
private _element: HTMLElement | undefined;
private _mouseService: IMouseService | undefined;
private _renderService: IRenderService | undefined;
private _linkProviders: ILinkProvider[] = [];
private _currentLink: ILink | undefined;
private _lastMouseEvent: MouseEvent | undefined;
private _linkCacheDisposables: IDisposable[] = [];
private _lastBufferCell: IBufferCellPosition | undefined;
private _onLinkHover = new EventEmitter<ILinkifierEvent>();
public get onLinkHover(): IEvent<ILinkifierEvent> { return this._onLinkHover.event; }
private _onLinkLeave = new EventEmitter<ILinkifierEvent>();
public get onLinkLeave(): IEvent<ILinkifierEvent> { return this._onLinkLeave.event; }
constructor(
private readonly _bufferService: IBufferService
) {
}
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
this._linkProviders.push(linkProvider);
return {
dispose: () => {
// Remove the link provider from the list
const providerIndex = this._linkProviders.indexOf(linkProvider);
if (providerIndex !== -1) {
this._linkProviders.splice(providerIndex, 1);
}
}
};
}
public attachToDom(element: HTMLElement, mouseService: IMouseService, renderService: IRenderService): void {
this._element = element;
this._mouseService = mouseService;
this._renderService = renderService;
this._element.addEventListener('mousemove', this._onMouseMove.bind(this));
this._element.addEventListener('click', this._onMouseDown.bind(this));
}
private _onMouseMove(event: MouseEvent): void {
this._lastMouseEvent = event;
if (!this._element || !this._mouseService) {
return;
}
const position = this._positionFromMouseEvent(event, this._element, this._mouseService);
if (!position) {
return;
}
if (!this._lastBufferCell || (position.x !== this._lastBufferCell.x || position.y !== this._lastBufferCell.y)) {
this._onHover(position);
this._lastBufferCell = position;
}
}
private _onHover(position: IBufferCellPosition): void {
if (this._currentLink) {
// Check the if the link is in the mouse position
const isInPosition = this._linkAtPosition(this._currentLink, position);
// Check if we need to clear the link
if (!isInPosition) {
this._clearCurrentLink();
this._askForLink(position);
}
} else {
this._askForLink(position);
}
}
private _askForLink(position: IBufferCellPosition): void {
const providerReplies: Map<Number, ILink | undefined> = new Map();
let linkProvided = false;
// There is no link cached, so ask for one
this._linkProviders.forEach((linkProvider, i) => {
linkProvider.provideLink(position, (link: ILink | undefined) => {
providerReplies.set(i, link);
// Check if every provider before this one has come back undefined
let hasLinkBefore = false;
for (let j = 0; j < i; j++) {
if (!providerReplies.has(j) || providerReplies.get(j)) {
hasLinkBefore = true;
}
}
// If all providers with higher priority came back undefined, then this link should be used
if (!hasLinkBefore && link) {
linkProvided = true;
this._handleNewLink(link);
}
// Check if all the providers have responded
if (providerReplies.size === this._linkProviders.length && !linkProvided) {
// Respect the order of the link providers
for (let j = 0; j < providerReplies.size; j++) {
const currentLink = providerReplies.get(j);
if (currentLink) {
this._handleNewLink(currentLink);
break;
}
}
}
});
});
}
private _onMouseDown(event: MouseEvent): void {
if (!this._element || !this._mouseService || !this._currentLink) {
return;
}
const position = this._positionFromMouseEvent(event, this._element, this._mouseService);
if (!position) {
return;
}
if (this._linkAtPosition(this._currentLink, position)) {
this._currentLink.activate(event, this._currentLink.text);
}
}
private _clearCurrentLink(startRow?: number, endRow?: number): void {
if (!this._element || !this._currentLink || !this._lastMouseEvent) {
return;
}
// If we have a start and end row, check that the link is within it
if (!startRow || !endRow || (this._currentLink.range.start.y >= startRow && this._currentLink.range.end.y <= endRow)) {
this._linkLeave(this._element, this._currentLink, this._lastMouseEvent);
this._currentLink = undefined;
this._linkCacheDisposables.forEach(l => l.dispose());
this._linkCacheDisposables = [];
}
}
private _handleNewLink(link: ILink): void {
if (!this._element || !this._lastMouseEvent || !this._mouseService) {
return;
}
const position = this._positionFromMouseEvent(this._lastMouseEvent, this._element, this._mouseService);
if (!position) {
return;
}
// Trigger hover if the we have a link at the position
if (this._linkAtPosition(link, position)) {
this._currentLink = link;
this._linkHover(this._element, link, this._lastMouseEvent);
// Add listener for rerendering
if (this._renderService) {
this._linkCacheDisposables.push(this._renderService.onRender(e => {
this._clearCurrentLink(e.start + 1 + this._bufferService.buffer.ydisp, e.end + 1 + this._bufferService.buffer.ydisp);
}));
}
}
}
protected _linkHover(element: HTMLElement, link: ILink, event: MouseEvent): void {
const range = link.range;
const scrollOffset = this._bufferService.buffer.ydisp;
this._onLinkHover.fire(this._createLinkHoverEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x, range.end.y - scrollOffset - 1, undefined));
element.classList.add('xterm-cursor-pointer');
if (link.hover) {
link.hover(event, link.text);
}
}
protected _linkLeave(element: HTMLElement, link: ILink, event: MouseEvent): void {
const range = link.range;
const scrollOffset = this._bufferService.buffer.ydisp;
this._onLinkLeave.fire(this._createLinkHoverEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x, range.end.y - scrollOffset - 1, undefined));
element.classList.remove('xterm-cursor-pointer');
if (link.leave) {
link.leave(event, link.text);
}
}
/**
* Check if the buffer position is within the link
* @param link
* @param position
*/
private _linkAtPosition(link: ILink, position: IBufferCellPosition): boolean {
const sameLine = link.range.start.y === link.range.end.y;
const wrappedFromLeft = link.range.start.y < position.y;
const wrappedToRight = link.range.end.y > position.y;
// If the start and end have the same y, then the position must be between start and end x
// If not, then handle each case seperately, depending on which way it wraps
return ((sameLine && link.range.start.x <= position.x && link.range.end.x >= position.x) ||
(wrappedFromLeft && link.range.end.x >= position.x) ||
(wrappedToRight && link.range.start.x <= position.x) ||
(wrappedFromLeft && wrappedToRight)) &&
link.range.start.y <= position.y &&
link.range.end.y >= position.y;
}
/**
* Get the buffer position from a mouse event
* @param event
*/
private _positionFromMouseEvent(event: MouseEvent, element: HTMLElement, mouseService: IMouseService): IBufferCellPosition | undefined {
const coords = mouseService.getCoords(event, element, this._bufferService.cols, this._bufferService.rows);
if (!coords) {
return;
}
return { x: coords[0], y: coords[1] + this._bufferService.buffer.ydisp };
}
private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number, fg: number | undefined): ILinkifierEvent {
return { x1, y1, x2, y2, cols: this._bufferService.cols, fg };
}
}
+3
View File
@@ -3,5 +3,8 @@
* @license MIT
*/
// eslint-disable-next-line prefer-const
export let promptLabel = 'Terminal input';
// eslint-disable-next-line prefer-const
export let tooMuchOutput = 'Too much output to announce, navigate to rows manually to read';
+31
View File
@@ -5,6 +5,7 @@
import { IEvent } from 'common/EventEmitter';
import { IDisposable } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
export interface IColorManager {
colors: IColorSet;
@@ -105,6 +106,14 @@ export interface ILinkifier {
deregisterLinkMatcher(matcherId: number): boolean;
}
export interface ILinkifier2 {
onLinkHover: IEvent<ILinkifierEvent>;
onLinkLeave: IEvent<ILinkifierEvent>;
attachToDom(element: HTMLElement, mouseService: IMouseService, renderService: IRenderService): void;
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
}
export interface ILinkMatcherOptions {
/**
* The index of the link from the regex.match(text) call. This defaults to 0
@@ -155,3 +164,25 @@ export interface IMouseZone {
leaveCallback: () => any | undefined;
willLinkActivate: (e: MouseEvent) => boolean;
}
interface ILinkProvider {
provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void;
}
interface ILink {
range: IBufferRange;
text: string;
activate(event: MouseEvent, text: string): void;
hover?(event: MouseEvent, text: string): void;
leave?(event: MouseEvent, text: string): void;
}
interface IBufferRange {
start: IBufferCellPosition;
end: IBufferCellPosition;
}
interface IBufferCellPosition {
x: number;
y: number;
}
+3 -3
View File
@@ -167,7 +167,7 @@ export class Viewport extends Disposable implements IViewport {
private _bubbleScroll(ev: Event, amount: number): boolean {
const scrollPosFromTop = this._viewportElement.scrollTop + this._lastRecordedViewportHeight;
if ((amount < 0 && this._viewportElement.scrollTop !== 0) ||
(amount > 0 && scrollPosFromTop < this._lastRecordedBufferHeight)) {
(amount > 0 && scrollPosFromTop < this._lastRecordedBufferHeight)) {
if (ev.cancelable) {
ev.preventDefault();
}
@@ -235,8 +235,8 @@ export class Viewport extends Disposable implements IViewport {
const modifier = this._optionsService.options.fastScrollModifier;
// Multiply the scroll speed when the modifier is down
if ((modifier === 'alt' && ev.altKey) ||
(modifier === 'ctrl' && ev.ctrlKey) ||
(modifier === 'shift' && ev.shiftKey)) {
(modifier === 'ctrl' && ev.ctrlKey) ||
(modifier === 'shift' && ev.shiftKey)) {
return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity;
}
+2 -1
View File
@@ -87,7 +87,8 @@ export class CompositionHelper {
if (ev.keyCode === 229) {
// Continue composing if the keyCode is the "composition character"
return false;
} else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
}
if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
// Continue composing if the keyCode is a modifier key
return false;
}
+1 -1
View File
@@ -38,7 +38,7 @@ export function moveToCellSequence(targetX: number, targetY: number, bufferServi
direction = startY > targetY ? Direction.LEFT : Direction.RIGHT;
const rowDifference = Math.abs(startY - targetY);
const cellsToMove = colsFromRowEnd(startY > targetY ? targetX : startX, bufferService) +
(rowDifference - 1) * bufferService.cols + 1 /*wrap around 1 row*/ +
(rowDifference - 1) * bufferService.cols + 1 /* wrap around 1 row */ +
colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService);
return repeat(cellsToMove, sequence(direction, applicationCursor));
}
+35 -35
View File
@@ -93,7 +93,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
const oldCanvas = this._canvas;
this._alpha = alpha;
// Cloning preserves properties
this._canvas = <HTMLCanvasElement>this._canvas.cloneNode();
this._canvas = this._canvas.cloneNode() as HTMLCanvasElement;
this._initCanvas();
this._container.replaceChild(this._canvas, oldCanvas);
@@ -145,10 +145,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
*/
protected _fillCells(x: number, y: number, width: number, height: number): void {
this._ctx.fillRect(
x * this._scaledCellWidth,
y * this._scaledCellHeight,
width * this._scaledCellWidth,
height * this._scaledCellHeight);
x * this._scaledCellWidth,
y * this._scaledCellHeight,
width * this._scaledCellWidth,
height * this._scaledCellHeight);
}
/**
@@ -159,10 +159,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
*/
protected _fillBottomLineAtCells(x: number, y: number, width: number = 1): void {
this._ctx.fillRect(
x * this._scaledCellWidth,
(y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
width * this._scaledCellWidth,
window.devicePixelRatio);
x * this._scaledCellWidth,
(y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
width * this._scaledCellWidth,
window.devicePixelRatio);
}
/**
@@ -173,10 +173,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
*/
protected _fillLeftLineAtCell(x: number, y: number, width: number): void {
this._ctx.fillRect(
x * this._scaledCellWidth,
y * this._scaledCellHeight,
window.devicePixelRatio * width,
this._scaledCellHeight);
x * this._scaledCellWidth,
y * this._scaledCellHeight,
window.devicePixelRatio * width,
this._scaledCellHeight);
}
/**
@@ -188,10 +188,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
protected _strokeRectAtCell(x: number, y: number, width: number, height: number): void {
this._ctx.lineWidth = window.devicePixelRatio;
this._ctx.strokeRect(
x * this._scaledCellWidth + window.devicePixelRatio / 2,
y * this._scaledCellHeight + (window.devicePixelRatio / 2),
width * this._scaledCellWidth - window.devicePixelRatio,
(height * this._scaledCellHeight) - window.devicePixelRatio);
x * this._scaledCellWidth + window.devicePixelRatio / 2,
y * this._scaledCellHeight + (window.devicePixelRatio / 2),
width * this._scaledCellWidth - window.devicePixelRatio,
(height * this._scaledCellHeight) - window.devicePixelRatio);
}
/**
@@ -216,17 +216,17 @@ export abstract class BaseRenderLayer implements IRenderLayer {
protected _clearCells(x: number, y: number, width: number, height: number): void {
if (this._alpha) {
this._ctx.clearRect(
x * this._scaledCellWidth,
y * this._scaledCellHeight,
width * this._scaledCellWidth,
height * this._scaledCellHeight);
x * this._scaledCellWidth,
y * this._scaledCellHeight,
width * this._scaledCellWidth,
height * this._scaledCellHeight);
} else {
this._ctx.fillStyle = this._colors.background.css;
this._ctx.fillRect(
x * this._scaledCellWidth,
y * this._scaledCellHeight,
width * this._scaledCellWidth,
height * this._scaledCellHeight);
x * this._scaledCellWidth,
y * this._scaledCellHeight,
width * this._scaledCellWidth,
height * this._scaledCellHeight);
}
}
@@ -244,9 +244,9 @@ export abstract class BaseRenderLayer implements IRenderLayer {
this._ctx.textBaseline = 'middle';
this._clipRow(y);
this._ctx.fillText(
cell.getChars(),
x * this._scaledCellWidth + this._scaledCharLeft,
y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight / 2);
cell.getChars(),
x * this._scaledCellWidth + this._scaledCharLeft,
y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight / 2);
}
/**
@@ -359,9 +359,9 @@ export abstract class BaseRenderLayer implements IRenderLayer {
}
// Draw the character
this._ctx.fillText(
cell.getChars(),
x * this._scaledCellWidth + this._scaledCharLeft,
y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight / 2);
cell.getChars(),
x * this._scaledCellWidth + this._scaledCharLeft,
y * this._scaledCellHeight + this._scaledCharTop + this._scaledCharHeight / 2);
this._ctx.restore();
}
@@ -372,10 +372,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
private _clipRow(y: number): void {
this._ctx.beginPath();
this._ctx.rect(
0,
y * this._scaledCellHeight,
this._bufferService.cols * this._scaledCellWidth,
this._scaledCellHeight);
0,
y * this._scaledCellHeight,
this._bufferService.cols * this._scaledCellWidth,
this._scaledCellHeight);
this._ctx.clip();
}
+14 -9
View File
@@ -140,7 +140,9 @@ export class CursorRenderLayer extends BaseRenderLayer {
return;
}
this._bufferService.buffer.lines.get(cursorY)!.loadCell(this._bufferService.buffer.x, this._cell);
// in case cursor.x == cols adjust visual cursor to cols - 1
const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);
this._bufferService.buffer.lines.get(cursorY)!.loadCell(cursorX, this._cell);
if (this._cell.content === undefined) {
return;
}
@@ -151,12 +153,12 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._ctx.fillStyle = this._colors.cursor.css;
const cursorStyle = this._optionsService.options.cursorStyle;
if (cursorStyle && cursorStyle !== 'block') {
this._cursorRenderers[cursorStyle](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
this._cursorRenderers[cursorStyle](cursorX, viewportRelativeCursorY, this._cell);
} else {
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell);
}
this._ctx.restore();
this._state.x = this._bufferService.buffer.x;
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = cursorStyle;
@@ -172,7 +174,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
if (this._state) {
// The cursor is already in the correct spot, don't redraw
if (this._state.x === this._bufferService.buffer.x &&
if (this._state.x === cursorX &&
this._state.y === viewportRelativeCursorY &&
this._state.isFocused === this._coreBrowserService.isFocused &&
this._state.style === this._optionsService.options.cursorStyle &&
@@ -183,10 +185,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
}
this._ctx.save();
this._cursorRenderers[this._optionsService.options.cursorStyle || 'block'](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
this._cursorRenderers[this._optionsService.options.cursorStyle || 'block'](cursorX, viewportRelativeCursorY, this._cell);
this._ctx.restore();
this._state.x = this._bufferService.buffer.x;
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = this._optionsService.options.cursorStyle;
@@ -304,7 +306,7 @@ class CursorBlinkStateManager {
// the regular interval is setup in order to support restarting the blink
// animation in a lightweight way (without thrashing clearInterval and
// setInterval).
this._blinkStartTimeout = <number><any>setTimeout(() => {
this._blinkStartTimeout = window.setTimeout(() => {
// Check if another animation restart was requested while this was being
// started
if (this._animationTimeRestarted) {
@@ -324,7 +326,7 @@ class CursorBlinkStateManager {
});
// Setup the blink interval
this._blinkInterval = <number><any>setInterval(() => {
this._blinkInterval = window.setInterval(() => {
// Adjust the animation time if it was restarted
if (this._animationTimeRestarted) {
// calc time diff
@@ -362,6 +364,9 @@ class CursorBlinkStateManager {
}
public resume(): void {
// Clear out any existing timers just in case
this.pause();
this._animationTimeRestarted = undefined;
this._restartInterval();
this.restartBlinkAnimation();
+5 -1
View File
@@ -7,7 +7,7 @@ import { IRenderDimensions } from 'browser/renderer/Types';
import { BaseRenderLayer } from './BaseRenderLayer';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils';
import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types';
import { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types';
import { IBufferService, IOptionsService } from 'common/services/Services';
export class LinkRenderLayer extends BaseRenderLayer {
@@ -19,12 +19,16 @@ export class LinkRenderLayer extends BaseRenderLayer {
colors: IColorSet,
rendererId: number,
linkifier: ILinkifier,
linkifier2: ILinkifier2,
readonly bufferService: IBufferService,
readonly optionsService: IOptionsService
) {
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService);
linkifier.onLinkHover(e => this._onLinkHover(e));
linkifier.onLinkLeave(e => this._onLinkLeave(e));
linkifier2.onLinkHover(e => this._onLinkHover(e));
linkifier2.onLinkLeave(e => this._onLinkLeave(e));
}
public resize(dim: IRenderDimensions): void {
+4 -3
View File
@@ -10,7 +10,7 @@ import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICh
import { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer';
import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry';
import { Disposable } from 'common/Lifecycle';
import { IColorSet, ILinkifier } from 'browser/Types';
import { IColorSet, ILinkifier, ILinkifier2 } from 'browser/Types';
import { ICharSizeService, ICoreBrowserService } from 'browser/services/Services';
import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';
import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache';
@@ -33,7 +33,8 @@ export class Renderer extends Disposable implements IRenderer {
constructor(
private _colors: IColorSet,
private readonly _screenElement: HTMLElement,
private readonly _linkifier: ILinkifier,
readonly linkifier: ILinkifier,
readonly linkifier2: ILinkifier2,
@IBufferService private readonly _bufferService: IBufferService,
@ICharSizeService private readonly _charSizeService: ICharSizeService,
@IOptionsService private readonly _optionsService: IOptionsService,
@@ -47,7 +48,7 @@ export class Renderer extends Disposable implements IRenderer {
this._renderLayers = [
new TextRenderLayer(this._screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService),
new SelectionRenderLayer(this._screenElement, 1, this._colors, this._id, this._bufferService, _optionsService),
new LinkRenderLayer(this._screenElement, 2, this._colors, this._id, this._linkifier, this._bufferService, _optionsService),
new LinkRenderLayer(this._screenElement, 2, this._colors, this._id, linkifier, linkifier2, this._bufferService, _optionsService),
new CursorRenderLayer(this._screenElement, 3, this._colors, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService, coreService, coreBrowserService)
];
this.dimensions = {
@@ -140,7 +140,8 @@ export class DynamicCharAtlas extends BaseCharAtlas {
if (cacheValue !== null && cacheValue !== undefined) {
this._drawFromCache(ctx, cacheValue, x, y);
return true;
} else if (this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) {
}
if (this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) {
let index;
if (this._cacheMap.size < this._cacheMap.capacity) {
index = this._cacheMap.size;
@@ -213,9 +214,11 @@ export class DynamicCharAtlas extends BaseCharAtlas {
// transparent in the atlas. Otherwise we'd end up drawing the transparent background twice
// around the anti-aliased edges of the glyph, and it would look too dark.
return TRANSPARENT_COLOR;
} else if (glyph.bg === INVERTED_DEFAULT_COLOR) {
}
if (glyph.bg === INVERTED_DEFAULT_COLOR) {
return this._config.colors.foreground;
} else if (glyph.bg < 256) {
}
if (glyph.bg < 256) {
return this._getColorFromAnsiIndex(glyph.bg);
}
return this._config.colors.background;
@@ -224,7 +227,8 @@ export class DynamicCharAtlas extends BaseCharAtlas {
private _getForegroundColor(glyph: IGlyphIdentifier): IColor {
if (glyph.fg === INVERTED_DEFAULT_COLOR) {
return color.opaque(this._config.colors.background);
} else if (glyph.fg < 256) {
}
if (glyph.fg < 256) {
// 256 color support
return this._getColorFromAnsiIndex(glyph.fg);
}
+76 -73
View File
@@ -7,7 +7,7 @@ import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRefreshRo
import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { Disposable } from 'common/Lifecycle';
import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types';
import { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types';
import { ICharSizeService } from 'browser/services/Services';
import { IOptionsService, IBufferService } from 'common/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
@@ -48,6 +48,7 @@ export class DomRenderer extends Disposable implements IRenderer {
private readonly _screenElement: HTMLElement,
private readonly _viewportElement: HTMLElement,
private readonly _linkifier: ILinkifier,
private readonly _linkifier2: ILinkifier2,
@ICharSizeService private readonly _charSizeService: ICharSizeService,
@IOptionsService private readonly _optionsService: IOptionsService,
@IBufferService private readonly _bufferService: IBufferService
@@ -88,6 +89,9 @@ export class DomRenderer extends Disposable implements IRenderer {
this._linkifier.onLinkHover(e => this._onLinkHover(e));
this._linkifier.onLinkLeave(e => this._onLinkLeave(e));
this._linkifier2.onLinkHover(e => this._onLinkHover(e));
this._linkifier2.onLinkLeave(e => this._onLinkLeave(e));
}
public dispose(): void {
@@ -127,12 +131,12 @@ export class DomRenderer extends Disposable implements IRenderer {
}
const styles =
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
` display: inline-block;` +
` height: 100%;` +
` vertical-align: top;` +
` width: ${this.dimensions.actualCellWidth}px` +
`}`;
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
` display: inline-block;` +
` height: 100%;` +
` vertical-align: top;` +
` width: ${this.dimensions.actualCellWidth}px` +
`}`;
this._dimensionsStyleElement.innerHTML = styles;
@@ -154,85 +158,84 @@ export class DomRenderer extends Disposable implements IRenderer {
// Base CSS
let styles =
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
` color: ${this._colors.foreground.css};` +
` background-color: ${this._colors.background.css};` +
` font-family: ${this._optionsService.options.fontFamily};` +
` font-size: ${this._optionsService.options.fontSize}px;` +
`}`;
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
` color: ${this._colors.foreground.css};` +
` font-family: ${this._optionsService.options.fontFamily};` +
` font-size: ${this._optionsService.options.fontSize}px;` +
`}`;
// Text styles
styles +=
`${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +
` font-weight: ${this._optionsService.options.fontWeight};` +
`}` +
`${this._terminalSelector} span.${BOLD_CLASS} {` +
` font-weight: ${this._optionsService.options.fontWeightBold};` +
`}` +
`${this._terminalSelector} span.${ITALIC_CLASS} {` +
` font-style: italic;` +
`}`;
`${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +
` font-weight: ${this._optionsService.options.fontWeight};` +
`}` +
`${this._terminalSelector} span.${BOLD_CLASS} {` +
` font-weight: ${this._optionsService.options.fontWeightBold};` +
`}` +
`${this._terminalSelector} span.${ITALIC_CLASS} {` +
` font-style: italic;` +
`}`;
// Blink animation
styles +=
`@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` +
` 50% {` +
` box-shadow: none;` +
` }` +
`}`;
`@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` +
` 50% {` +
` box-shadow: none;` +
` }` +
`}`;
styles +=
`@keyframes blink_block` + `_` + this._terminalClass + ` {` +
` 0% {` +
` background-color: ${this._colors.cursor.css};` +
` color: ${this._colors.cursorAccent.css};` +
` }` +
` 50% {` +
` background-color: ${this._colors.cursorAccent.css};` +
` color: ${this._colors.cursor.css};` +
` }` +
`}`;
`@keyframes blink_block` + `_` + this._terminalClass + ` {` +
` 0% {` +
` background-color: ${this._colors.cursor.css};` +
` color: ${this._colors.cursorAccent.css};` +
` }` +
` 50% {` +
` background-color: ${this._colors.cursorAccent.css};` +
` color: ${this._colors.cursor.css};` +
` }` +
`}`;
// Cursor
styles +=
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` outline: 1px solid ${this._colors.cursor.css};` +
` outline-offset: -1px;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` +
` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` background-color: ${this._colors.cursor.css};` +
` color: ${this._colors.cursorAccent.css};` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
` box-shadow: ${this._optionsService.options.cursorWidth}px 0 0 ${this._colors.cursor.css} inset;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
`}`;
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` outline: 1px solid ${this._colors.cursor.css};` +
` outline-offset: -1px;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` +
` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` background-color: ${this._colors.cursor.css};` +
` color: ${this._colors.cursorAccent.css};` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
` box-shadow: ${this._optionsService.options.cursorWidth}px 0 0 ${this._colors.cursor.css} inset;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
`}`;
// Selection
styles +=
`${this._terminalSelector} .${SELECTION_CLASS} {` +
` position: absolute;` +
` top: 0;` +
` left: 0;` +
` z-index: 1;` +
` pointer-events: none;` +
`}` +
`${this._terminalSelector} .${SELECTION_CLASS} div {` +
` position: absolute;` +
` background-color: ${this._colors.selection.css};` +
`}`;
`${this._terminalSelector} .${SELECTION_CLASS} {` +
` position: absolute;` +
` top: 0;` +
` left: 0;` +
` z-index: 1;` +
` pointer-events: none;` +
`}` +
`${this._terminalSelector} .${SELECTION_CLASS} div {` +
` position: absolute;` +
` background-color: ${this._colors.selection.css};` +
`}`;
// Colors
this._colors.ansi.forEach((c, i) => {
styles +=
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
});
styles +=
`${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._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;
}
@@ -349,7 +352,7 @@ export class DomRenderer extends Disposable implements IRenderer {
public renderRows(start: number, end: number): void {
const cursorAbsoluteY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
const cursorX = this._bufferService.buffer.x;
const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);
const cursorBlink = this._optionsService.options.cursorBlink;
for (let y = start; y <= end; y++) {
+10 -10
View File
@@ -835,12 +835,12 @@ export class SelectionService implements ISelectionService {
// Calculate the length in _columns_, converting the the string indexes back
// to column coordinates.
let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols
endIndex // The index of the selection's end char in the line string
- startIndex // The index of the selection's start char in the line string
+ leftWideCharCount // The number of wide chars left of the initial char
+ rightWideCharCount // The number of wide chars right of the initial char (inclusive)
- leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)
- rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis)
endIndex // The index of the selection's end char in the line string
- startIndex // The index of the selection's start char in the line string
+ leftWideCharCount // The number of wide chars left of the initial char
+ rightWideCharCount // The number of wide chars right of the initial char (inclusive)
- leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)
- rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis)
if (!allowWhitespaceOnlySelection && line.slice(startIndex, endIndex).trim() === '') {
return undefined;
@@ -848,9 +848,9 @@ export class SelectionService implements ISelectionService {
// Recurse upwards if the line is wrapped and the word wraps to the above line
if (followWrappedLinesAbove) {
if (start === 0 && bufferLine.getCodePoint(0) !== 32 /*' '*/) {
if (start === 0 && bufferLine.getCodePoint(0) !== 32 /* ' ' */) {
const previousBufferLine = buffer.lines.get(coords[1] - 1);
if (previousBufferLine && bufferLine.isWrapped && previousBufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /*' '*/) {
if (previousBufferLine && bufferLine.isWrapped && previousBufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /* ' ' */) {
const previousLineWordPosition = this._getWordAt([this._bufferService.cols - 1, coords[1] - 1], false, true, false);
if (previousLineWordPosition) {
const offset = this._bufferService.cols - previousLineWordPosition.start;
@@ -863,9 +863,9 @@ export class SelectionService implements ISelectionService {
// Recurse downwards if the line is wrapped and the word wraps to the next line
if (followWrappedLinesBelow) {
if (start + length === this._bufferService.cols && bufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /*' '*/) {
if (start + length === this._bufferService.cols && bufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /* ' ' */) {
const nextBufferLine = buffer.lines.get(coords[1] + 1);
if (nextBufferLine && nextBufferLine.isWrapped && nextBufferLine.getCodePoint(0) !== 32 /*' '*/) {
if (nextBufferLine && nextBufferLine.isWrapped && nextBufferLine.getCodePoint(0) !== 32 /* ' ' */) {
const nextLineWordPosition = this._getWordAt([0, coords[1] + 1], false, false, true);
if (nextLineWordPosition) {
length += nextLineWordPosition.length;
+2 -2
View File
@@ -47,7 +47,7 @@ export class MockCharsetService implements ICharsetService {
serviceBrand: any;
charset: ICharset | undefined;
glevel: number = 0;
charsets: readonly ICharset[] = [];
charsets: ReadonlyArray<ICharset> = [];
reset(): void {}
setgLevel(g: number): void {}
setgCharset(g: number, charset: ICharset): void {}
@@ -116,7 +116,7 @@ export class MockUnicodeService implements IUnicodeService {
versions: string[] = [];
activeVersion: string = '';
onChange: IEvent<string> = new EventEmitter<string>().event;
wcwidth = (codepoint: number) => this._provider.wcwidth(codepoint);
wcwidth = (codepoint: number): number => this._provider.wcwidth(codepoint);
getStringCellWidth(s: string): number {
throw new Error('Method not implemented.');
}
+1 -3
View File
@@ -5,9 +5,7 @@
import { assert } from 'chai';
import { fillFallback, concat } from 'common/TypedArrayUtils';
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
| Int8Array | Int16Array | Int32Array
| Float32Array | Float64Array;
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array;
function deepEquals(a: TypedArray, b: TypedArray): void {
assert.equal(a.length, b.length);
+1 -3
View File
@@ -3,9 +3,7 @@
* @license MIT
*/
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
| Int8Array | Int16Array | Int32Array
| Float32Array | Float64Array;
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array;
/**
+1
View File
@@ -246,6 +246,7 @@ CHARSETS['='] = {
'\\': 'ç',
']': 'ê',
'^': 'î',
// eslint-disable-next-line @typescript-eslint/naming-convention
'_': 'è',
'`': 'ô',
'{': 'ä',
+10 -10
View File
@@ -107,16 +107,16 @@ it('wcwidth should match all values from the old implementation', function(): vo
function isWideBMP(ucs: number): boolean {
return (
ucs >= 0x1100 && (
ucs <= 0x115f || // Hangul Jamo init. consonants
ucs === 0x2329 ||
ucs === 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi
(ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables
(ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs
(ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms
(ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms
(ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms
(ucs >= 0xffe0 && ucs <= 0xffe6)));
ucs <= 0x115f || // Hangul Jamo init. consonants
ucs === 0x2329 ||
ucs === 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi
(ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables
(ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs
(ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms
(ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms
(ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms
(ucs >= 0xffe0 && ucs <= 0xffe6)));
}
function wcwidthHigh(ucs: number): 0 | 1 | 2 {
if (bisearch(ucs, COMBINING_HIGH)) {
+1
View File
@@ -6,6 +6,7 @@
import { assert } from 'chai';
import { WriteBuffer } from './WriteBuffer';
// eslint-disable-next-line @typescript-eslint/naming-convention
declare let Buffer: any;
function toBytes(s: string): Uint8Array {
+10 -7
View File
@@ -58,6 +58,9 @@ const oscPutParser = new MockOscPutParser();
// derived parser with access to internal states
class TestEscapeSequenceParser extends EscapeSequenceParser {
public get transitions(): TransitionTable {
return this._transitions;
}
public get osc(): string {
return (this._oscParser as MockOscPutParser).data;
}
@@ -192,13 +195,13 @@ describe('EscapeSequenceParser', function (): void {
const parser = testParser;
describe('Parser init and methods', function (): void {
it('constructor', function (): void {
let p: EscapeSequenceParser = new EscapeSequenceParser();
chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE);
p = new EscapeSequenceParser(VT500_TRANSITION_TABLE);
chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE);
let p = new TestEscapeSequenceParser();
chai.expect(p.transitions).equal(VT500_TRANSITION_TABLE);
p = new TestEscapeSequenceParser(VT500_TRANSITION_TABLE);
chai.expect(p.transitions).equal(VT500_TRANSITION_TABLE);
const tansitions: TransitionTable = new TransitionTable(10);
p = new EscapeSequenceParser(tansitions);
chai.expect(p.TRANSITIONS).equal(tansitions);
p = new TestEscapeSequenceParser(tansitions);
chai.expect(p.transitions).equal(tansitions);
});
it('inital states', function (): void {
chai.expect(parser.initialState).equal(ParserState.GROUND);
@@ -735,7 +738,7 @@ describe('EscapeSequenceParser', function (): void {
it('state OSC_STRING ignore rules', function (): void {
parser.reset();
const ignored = [
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', /*'\x07',*/ '\x08',
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', /* '\x07', */ '\x08',
'\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11',
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f'];
for (let i = 0; i < ignored.length; ++i) {
+5 -3
View File
@@ -82,7 +82,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
// range macro for byte
const BYTE_VALUES = 256;
const blueprint = Array.apply(null, Array(BYTE_VALUES)).map((unused: any, i: number) => i);
const r = (start: number, end: number) => blueprint.slice(start, end);
const r = (start: number, end: number): number[] => blueprint.slice(start, end);
// Default definitions.
const PRINTABLES = r(0x20, 0x7f); // 0x20 (SP) included, 0x7F (DEL) excluded
@@ -253,7 +253,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
protected _escHandlerFb: EscFallbackHandlerType;
protected _errorHandlerFb: (state: IParsingState) => IParsingState;
constructor(readonly TRANSITIONS: TransitionTable = VT500_TRANSITION_TABLE) {
constructor(
protected readonly _transitions: TransitionTable = VT500_TRANSITION_TABLE
) {
super();
this.initialState = ParserState.GROUND;
@@ -471,7 +473,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
const dcs = this._dcsParser;
let collect = this._collect;
const params = this._params;
const table: Uint8Array = this.TRANSITIONS.table;
const table: Uint8Array = this._transitions.table;
// process input string
for (let i = 0; i < length; ++i) {
+5 -5
View File
@@ -284,11 +284,11 @@ export class CoreMouseService implements ICoreMouseService {
public explainEvents(events: CoreMouseEventType): {[event: string]: boolean} {
return {
DOWN: !!(events & CoreMouseEventType.DOWN),
UP: !!(events & CoreMouseEventType.UP),
DRAG: !!(events & CoreMouseEventType.DRAG),
MOVE: !!(events & CoreMouseEventType.MOVE),
WHEEL: !!(events & CoreMouseEventType.WHEEL)
down: !!(events & CoreMouseEventType.DOWN),
up: !!(events & CoreMouseEventType.UP),
drag: !!(events & CoreMouseEventType.DRAG),
move: !!(events & CoreMouseEventType.MOVE),
wheel: !!(events & CoreMouseEventType.WHEEL)
};
}
+2 -2
View File
@@ -56,7 +56,7 @@ export class InstantiationService implements IInstantiationService {
return this._services.get(id);
}
public createInstance<T>(ctor: any, ...args: any[]): any {
public createInstance<T>(ctor: any, ...args: any[]): T {
const serviceDependencies = getServiceDependencies(ctor).sort((a, b) => a.index - b.index);
const serviceArgs: any[] = [];
@@ -76,6 +76,6 @@ export class InstantiationService implements IInstantiationService {
}
// now create the instance
return <T>new ctor(...[...args, ...serviceArgs]);
return new ctor(...[...args, ...serviceArgs]);
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ export function createDecorator<T>(id: string): IServiceIdentifier<T> {
return serviceRegistry.get(id)!;
}
const decorator = <any>function (target: Function, key: string, index: number): any {
const decorator: any = function (target: Function, key: string, index: number): any {
if (arguments.length !== 3) {
throw new Error('@IServiceName-decorator can only be used to decorate a parameter');
}
+13 -13
View File
@@ -87,7 +87,7 @@ export interface ICoreService {
* Triggers the onBinary event in the public API.
* @param data The data that is being emitted.
*/
triggerBinaryEvent(data: string): void;
triggerBinaryEvent(data: string): void;
}
export const ICharsetService = createDecorator<ICharsetService>('CharsetService');
@@ -104,7 +104,7 @@ export interface ICharsetService {
* Set the G level of the terminal.
* @param g
*/
setgLevel(g: number): void;
setgLevel(g: number): void;
/**
* Set the charset for the given G level of the terminal.
@@ -133,39 +133,39 @@ export interface IServiceIdentifier<T> {
}
export interface IConstructorSignature0<T> {
new(...services: { serviceBrand: any; }[]): T;
new(...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature1<A1, T> {
new(first: A1, ...services: { serviceBrand: any; }[]): T;
new(first: A1, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature2<A1, A2, T> {
new(first: A1, second: A2, ...services: { serviceBrand: any; }[]): T;
new(first: A1, second: A2, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature3<A1, A2, A3, T> {
new(first: A1, second: A2, third: A3, ...services: { serviceBrand: any; }[]): T;
new(first: A1, second: A2, third: A3, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature4<A1, A2, A3, A4, T> {
new(first: A1, second: A2, third: A3, fourth: A4, ...services: { serviceBrand: any; }[]): T;
new(first: A1, second: A2, third: A3, fourth: A4, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature5<A1, A2, A3, A4, A5, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { serviceBrand: any; }[]): T;
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature6<A1, A2, A3, A4, A5, A6, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { serviceBrand: any; }[]): T;
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature7<A1, A2, A3, A4, A5, A6, A7, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { serviceBrand: any; }[]): T;
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature8<A1, A2, A3, A4, A5, A6, A7, A8, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { serviceBrand: any; }[]): T;
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { serviceBrand: any }[]): T;
}
export const IInstantiationService = createDecorator<IInstantiationService>('InstantiationService');
@@ -213,7 +213,7 @@ export type RendererType = 'dom' | 'canvas';
export interface IPartialTerminalOptions {
allowTransparency?: boolean;
bellSound?: string;
bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/;
bellStyle?: 'none' | 'sound' /* | 'visual' | 'both' */;
cols?: number;
cursorBlink?: boolean;
cursorStyle?: 'block' | 'underline' | 'bar';
@@ -247,7 +247,7 @@ export interface IPartialTerminalOptions {
export interface ITerminalOptions {
allowTransparency: boolean;
bellSound: string;
bellStyle: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/;
bellStyle: 'none' | 'sound' /* | 'visual' | 'both' */;
cols: number;
cursorBlink: boolean;
cursorStyle: 'block' | 'underline' | 'bar';
+42 -7
View File
@@ -3,14 +3,14 @@
* @license MIT
*/
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier, IUnicodeHandling, IUnicodeVersionProvider } from 'xterm';
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier, ILinkProvider, IUnicodeHandling, IUnicodeVersionProvider } from 'xterm';
import { ITerminal } from '../Types';
import { IBufferLine, ICellData } from 'common/Types';
import { IBuffer } from 'common/buffer/Types';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { CellData } from 'common/buffer/CellData';
import { Terminal as TerminalCore } from '../Terminal';
import * as Strings from '../browser/LocalizableStrings';
import { IEvent } from 'common/EventEmitter';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { AddonManager } from './AddonManager';
import { IParams } from 'common/parser/Types';
@@ -48,7 +48,7 @@ export class Terminal implements ITerminalApi {
public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; }
public get rows(): number { return this._core.rows; }
public get cols(): number { return this._core.cols; }
public get buffer(): IBufferApi { return new BufferApiView(this._core.buffer); }
public get buffer(): IBufferNamespaceApi { return new BufferNamespaceApi(this._core.buffers); }
public get markers(): ReadonlyArray<IMarker> { return this._core.markers; }
public blur(): void {
this._core.blur();
@@ -72,6 +72,9 @@ export class Terminal implements ITerminalApi {
public deregisterLinkMatcher(matcherId: number): void {
this._core.deregisterLinkMatcher(matcherId);
}
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
return this._core.registerLinkProvider(linkProvider);
}
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
return this._core.registerCharacterJoiner(handler);
}
@@ -190,7 +193,15 @@ export class Terminal implements ITerminalApi {
}
class BufferApiView implements IBufferApi {
constructor(private _buffer: IBuffer) { }
constructor(
private _buffer: IBuffer,
public readonly type: 'normal' | 'alternate'
) { }
public init(buffer: IBuffer): BufferApiView {
this._buffer = buffer;
return this;
}
public get cursorY(): number { return this._buffer.y; }
public get cursorX(): number { return this._buffer.x; }
@@ -207,6 +218,30 @@ class BufferApiView implements IBufferApi {
public getNullCell(): IBufferCellApi { return new CellData(); }
}
class BufferNamespaceApi implements IBufferNamespaceApi {
private _normal: BufferApiView;
private _alternate: BufferApiView;
private _onBufferChange = new EventEmitter<IBufferApi>();
public get onBufferChange(): IEvent<IBufferApi> { 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));
}
public get active(): IBufferApi {
if (this._buffers.active === this._buffers.normal) { return this.normal; }
if (this._buffers.active === this._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);
}
public get alternate(): IBufferApi {
return this._alternate.init(this._buffers.alt);
}
}
class BufferLineApiView implements IBufferLineApi {
constructor(private _line: IBufferLine) { }
@@ -229,7 +264,7 @@ class BufferLineApiView implements IBufferLineApi {
}
class ParserApi implements IParser {
constructor(private _core: ITerminal) {}
constructor(private _core: ITerminal) { }
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
@@ -258,7 +293,7 @@ class ParserApi implements IParser {
}
class UnicodeApi implements IUnicodeHandling {
constructor(private _core: ITerminal) {}
constructor(private _core: ITerminal) { }
public register(provider: IUnicodeVersionProvider): void {
this._core.unicodeService.register(provider);