Merge branch 'master' into 2586_dom_renderer
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-search",
|
"name": "xterm-addon-search",
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-serialize",
|
"name": "xterm-addon-serialize",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
@@ -18,6 +18,6 @@
|
|||||||
"benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/addons/xterm-addon-serialize/benchmark/*benchmark.js"
|
"benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/addons/xterm-addon-serialize/benchmark/*benchmark.js"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"xterm": "^3.14.0"
|
"xterm": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ILinkProvider, IBufferCellPosition, ILink, Terminal, IBuffer } from 'xterm';
|
||||||
|
|
||||||
|
export class WebLinkProvider implements ILinkProvider {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _terminal: Terminal,
|
||||||
|
private readonly _regex: RegExp,
|
||||||
|
private readonly _handler: (event: MouseEvent, uri: string) => void
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void {
|
||||||
|
callback(LinkComputer.computeLink(position, this._regex, this._terminal, this._handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkComputer {
|
||||||
|
public static computeLink(position: IBufferCellPosition, regex: RegExp, terminal: Terminal, handler: (event: MouseEvent, uri: string) => void): ILink | undefined {
|
||||||
|
const rex = new RegExp(regex.source, (regex.flags || '') + 'g');
|
||||||
|
|
||||||
|
const [line, startLineIndex] = LinkComputer._translateBufferLineToStringWithWrap(position.y - 1, false, terminal);
|
||||||
|
|
||||||
|
let match;
|
||||||
|
let stringIndex = -1;
|
||||||
|
|
||||||
|
while ((match = rex.exec(line)) !== null) {
|
||||||
|
const text = match[1];
|
||||||
|
if (!text) {
|
||||||
|
// something matched but does not comply with the given matchIndex
|
||||||
|
// since this is most likely a bug the regex itself we simply do nothing here
|
||||||
|
console.log('match found without corresponding matchIndex');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get index, match.index is for the outer match which includes negated chars
|
||||||
|
// therefore we cannot use match.index directly, instead we search the position
|
||||||
|
// of the match group in text again
|
||||||
|
// also correct regex and string search offsets for the next loop run
|
||||||
|
stringIndex = line.indexOf(text, stringIndex + 1);
|
||||||
|
rex.lastIndex = stringIndex + text.length;
|
||||||
|
if (stringIndex < 0) {
|
||||||
|
// invalid stringIndex (should not have happened)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let endX = stringIndex + text.length + 1;
|
||||||
|
let endY = startLineIndex + 1;
|
||||||
|
|
||||||
|
while (endX > terminal.cols) {
|
||||||
|
endX -= terminal.cols;
|
||||||
|
endY++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const range = {
|
||||||
|
start: {
|
||||||
|
x: stringIndex + 1,
|
||||||
|
y: startLineIndex + 1
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
x: endX,
|
||||||
|
y: endY
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { range, text, activate: handler };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the entire line for the buffer line
|
||||||
|
* @param line The line being translated.
|
||||||
|
* @param trimRight Whether to trim whitespace to the right.
|
||||||
|
* @param terminal The terminal
|
||||||
|
*/
|
||||||
|
private static _translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean, terminal: Terminal): [string, number] {
|
||||||
|
let lineString = '';
|
||||||
|
let lineWrapsToNext: boolean;
|
||||||
|
let prevLinesToWrap: boolean;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const line = terminal.buffer.getLine(lineIndex);
|
||||||
|
if (!line) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.isWrapped) {
|
||||||
|
lineIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevLinesToWrap = line.isWrapped;
|
||||||
|
} while (prevLinesToWrap);
|
||||||
|
|
||||||
|
const startLineIndex = lineIndex;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const nextLine = terminal.buffer.getLine(lineIndex + 1);
|
||||||
|
lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
|
||||||
|
const line = terminal.buffer.getLine(lineIndex);
|
||||||
|
if (!line) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lineString += line.translateToString(!lineWrapsToNext && trimRight).substring(0, terminal.cols);
|
||||||
|
lineIndex++;
|
||||||
|
} while (lineWrapsToNext);
|
||||||
|
|
||||||
|
return [lineString, startLineIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ const width = 800;
|
|||||||
const height = 600;
|
const height = 600;
|
||||||
|
|
||||||
describe('WebLinksAddon', () => {
|
describe('WebLinksAddon', () => {
|
||||||
before(async function(): Promise<any> {
|
before(async function (): Promise<any> {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
@@ -29,29 +29,29 @@ describe('WebLinksAddon', () => {
|
|||||||
await browser.close();
|
await browser.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async function(): Promise<any> {
|
beforeEach(async function (): Promise<any> {
|
||||||
this.timeout(5000);
|
this.timeout(5000);
|
||||||
await page.goto(APP);
|
await page.goto(APP);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('.com', async function(): Promise<any> {
|
it('.com', async function (): Promise<any> {
|
||||||
this.timeout(20000);
|
this.timeout(20000);
|
||||||
await testHostName('foo.com');
|
await testHostName('foo.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('.com.au', async function(): Promise<any> {
|
it('.com.au', async function (): Promise<any> {
|
||||||
this.timeout(20000);
|
this.timeout(20000);
|
||||||
await testHostName('foo.com.au');
|
await testHostName('foo.com.au');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('.io', async function(): Promise<any> {
|
it('.io', async function (): Promise<any> {
|
||||||
this.timeout(20000);
|
this.timeout(20000);
|
||||||
await testHostName('foo.io');
|
await testHostName('foo.io');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function testHostName(hostname: string): Promise<void> {
|
async function testHostName(hostname: string): Promise<void> {
|
||||||
await openTerminal({ rendererType: 'dom' });
|
await openTerminal({ rendererType: 'dom', cols: 40 });
|
||||||
await page.evaluate(`window.term.loadAddon(new window.WebLinksAddon())`);
|
await page.evaluate(`window.term.loadAddon(new window.WebLinksAddon())`);
|
||||||
const data = ` http://${hostname} \\r\\n` +
|
const data = ` http://${hostname} \\r\\n` +
|
||||||
` http://${hostname}/a~b#c~d?e~f \\r\\n` +
|
` http://${hostname}/a~b#c~d?e~f \\r\\n` +
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Terminal, ILinkMatcherOptions, ITerminalAddon } from 'xterm';
|
import { Terminal, ILinkMatcherOptions, ITerminalAddon, ILinkProvider, IDisposable } from 'xterm';
|
||||||
|
import { WebLinkProvider } from './WebLinkProvider';
|
||||||
|
|
||||||
const protocolClause = '(https?:\\/\\/)';
|
const protocolClause = '(https?:\\/\\/)';
|
||||||
const domainCharacterSet = '[\\da-z\\.-]+';
|
const domainCharacterSet = '[\\da-z\\.-]+';
|
||||||
@@ -38,22 +39,32 @@ function handleLink(event: MouseEvent, uri: string): void {
|
|||||||
export class WebLinksAddon implements ITerminalAddon {
|
export class WebLinksAddon implements ITerminalAddon {
|
||||||
private _linkMatcherId: number | undefined;
|
private _linkMatcherId: number | undefined;
|
||||||
private _terminal: Terminal | undefined;
|
private _terminal: Terminal | undefined;
|
||||||
|
private _linkProvider: IDisposable | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _handler: (event: MouseEvent, uri: string) => void = handleLink,
|
private _handler: (event: MouseEvent, uri: string) => void = handleLink,
|
||||||
private _options: ILinkMatcherOptions = {}
|
private _options: ILinkMatcherOptions = {},
|
||||||
|
private _useLinkProvider: boolean = false
|
||||||
) {
|
) {
|
||||||
this._options.matchIndex = 1;
|
this._options.matchIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public activate(terminal: Terminal): void {
|
public activate(terminal: Terminal): void {
|
||||||
this._terminal = terminal;
|
this._terminal = terminal;
|
||||||
this._linkMatcherId = this._terminal.registerLinkMatcher(strictUrlRegex, this._handler, this._options);
|
|
||||||
|
if (this._useLinkProvider && 'registerLinkProvider' in this._terminal) {
|
||||||
|
this._linkProvider = this._terminal.registerLinkProvider(new WebLinkProvider(this._terminal, strictUrlRegex, this._handler));
|
||||||
|
} else {
|
||||||
|
// TODO: This should be removed eventually
|
||||||
|
this._linkMatcherId = (<Terminal>this._terminal).registerLinkMatcher(strictUrlRegex, this._handler, this._options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
if (this._linkMatcherId !== undefined && this._terminal !== undefined) {
|
if (this._linkMatcherId !== undefined && this._terminal !== undefined) {
|
||||||
this._terminal.deregisterLinkMatcher(this._linkMatcherId);
|
this._terminal.deregisterLinkMatcher(this._linkMatcherId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._linkProvider?.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,12 @@ declare module 'xterm-addon-web-links' {
|
|||||||
* Creates a new web links addon.
|
* Creates a new web links addon.
|
||||||
* @param handler The callback when the link is called.
|
* @param handler The callback when the link is called.
|
||||||
* @param options Options for the link matcher.
|
* @param options Options for the link matcher.
|
||||||
|
* @param useLinkProvider Whether to use the new link provider API to create
|
||||||
|
* the links. This is an option because use of both link matcher (old) and
|
||||||
|
* link provider (new) may cause issues. Link provider will eventually be
|
||||||
|
* the default and only option.
|
||||||
*/
|
*/
|
||||||
constructor(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions);
|
constructor(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions, useLinkProvider?: boolean);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activates the addon
|
* Activates the addon
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ export class LinkRenderLayer extends BaseRenderLayer {
|
|||||||
super(container, 'link', zIndex, true, colors);
|
super(container, 'link', zIndex, true, colors);
|
||||||
terminal.linkifier.onLinkHover(e => this._onLinkHover(e));
|
terminal.linkifier.onLinkHover(e => this._onLinkHover(e));
|
||||||
terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e));
|
terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e));
|
||||||
|
|
||||||
|
terminal.linkifier2.onLinkHover(e => this._onLinkHover(e));
|
||||||
|
terminal.linkifier2.onLinkLeave(e => this._onLinkLeave(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(terminal: Terminal, dim: IRenderDimensions): void {
|
public resize(terminal: Terminal, dim: IRenderDimensions): void {
|
||||||
|
|||||||
+2
-1
@@ -152,7 +152,8 @@ function createTerminal(): void {
|
|||||||
addons.serialize.instance = new SerializeAddon();
|
addons.serialize.instance = new SerializeAddon();
|
||||||
addons.fit.instance = new FitAddon();
|
addons.fit.instance = new FitAddon();
|
||||||
addons.unicode11.instance = new Unicode11Addon();
|
addons.unicode11.instance = new Unicode11Addon();
|
||||||
addons['web-links'].instance = new WebLinksAddon();
|
// TODO: Remove arguments when link provider API is the default
|
||||||
|
addons['web-links'].instance = new WebLinksAddon(undefined, undefined, true);
|
||||||
typedTerm.loadAddon(addons.fit.instance);
|
typedTerm.loadAddon(addons.fit.instance);
|
||||||
typedTerm.loadAddon(addons.search.instance);
|
typedTerm.loadAddon(addons.search.instance);
|
||||||
typedTerm.loadAddon(addons.serialize.instance);
|
typedTerm.loadAddon(addons.serialize.instance);
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@
|
|||||||
<div id="terminal-container"></div>
|
<div id="terminal-container"></div>
|
||||||
<div>
|
<div>
|
||||||
<h3>Options</h3>
|
<h3>Options</h3>
|
||||||
<p>These options can be set in the <code>Terminal</code> constructor or using the <code>Terminal.setOption</code> function.</p>
|
<p>These options can be set in the <code>Terminal</code> constructor or by using the <code>Terminal.setOption</code> function.</p>
|
||||||
<div id="options-container"></div>
|
<div id="options-container"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
+20
-10
@@ -37,7 +37,7 @@ import * as Strings from 'browser/LocalizableStrings';
|
|||||||
import { SoundService } from 'browser/services/SoundService';
|
import { SoundService } from 'browser/services/SoundService';
|
||||||
import { MouseZoneManager } from 'browser/MouseZoneManager';
|
import { MouseZoneManager } from 'browser/MouseZoneManager';
|
||||||
import { AccessibilityManager } from './AccessibilityManager';
|
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 { DomRenderer } from 'browser/renderer/dom/DomRenderer';
|
||||||
import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types';
|
import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types';
|
||||||
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||||
@@ -57,11 +57,12 @@ import { MouseService } from 'browser/services/MouseService';
|
|||||||
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
|
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
|
||||||
import { CoreService } from 'common/services/CoreService';
|
import { CoreService } from 'common/services/CoreService';
|
||||||
import { LogService } from 'common/services/LogService';
|
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 { DirtyRowService } from 'common/services/DirtyRowService';
|
||||||
import { InstantiationService } from 'common/services/InstantiationService';
|
import { InstantiationService } from 'common/services/InstantiationService';
|
||||||
import { CoreMouseService } from 'common/services/CoreMouseService';
|
import { CoreMouseService } from 'common/services/CoreMouseService';
|
||||||
import { WriteBuffer } from 'common/input/WriteBuffer';
|
import { WriteBuffer } from 'common/input/WriteBuffer';
|
||||||
|
import { Linkifier2 } from 'browser/Linkifier2';
|
||||||
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
||||||
import { UnicodeService } from 'common/services/UnicodeService';
|
import { UnicodeService } from 'common/services/UnicodeService';
|
||||||
import { CharsetService } from 'common/services/CharsetService';
|
import { CharsetService } from 'common/services/CharsetService';
|
||||||
@@ -131,6 +132,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
|
|
||||||
private _inputHandler: InputHandler;
|
private _inputHandler: InputHandler;
|
||||||
public linkifier: ILinkifier;
|
public linkifier: ILinkifier;
|
||||||
|
public linkifier2: ILinkifier2;
|
||||||
public viewport: IViewport;
|
public viewport: IViewport;
|
||||||
private _compositionHelper: ICompositionHelper;
|
private _compositionHelper: ICompositionHelper;
|
||||||
private _mouseZoneManager: IMouseZoneManager;
|
private _mouseZoneManager: IMouseZoneManager;
|
||||||
@@ -228,7 +230,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
this._windowsMode = undefined;
|
this._windowsMode = undefined;
|
||||||
this._renderService?.dispose();
|
this._renderService?.dispose();
|
||||||
this._customKeyEventHandler = null;
|
this._customKeyEventHandler = null;
|
||||||
this.write = () => {};
|
this.write = () => { };
|
||||||
this.element?.parentNode?.removeChild(this.element);
|
this.element?.parentNode?.removeChild(this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,6 +259,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
if (!this.linkifier) {
|
if (!this.linkifier) {
|
||||||
this.linkifier = new Linkifier(this._bufferService, this._logService, this.optionsService, this.unicodeService);
|
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) {
|
if (this.options.windowsMode) {
|
||||||
this._enableWindowsMode();
|
this._enableWindowsMode();
|
||||||
@@ -585,6 +590,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
this.register(this._mouseZoneManager);
|
this.register(this._mouseZoneManager);
|
||||||
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
|
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
|
||||||
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
|
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 event listener must be registered aftre MouseZoneManager is created
|
||||||
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
|
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 {
|
private _createRenderer(): IRenderer {
|
||||||
switch (this.options.rendererType) {
|
switch (this.options.rendererType) {
|
||||||
case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager.colors, this.screenElement, 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);
|
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}"`);
|
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -729,13 +735,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
* Note: 'mousedown' currently is "always on" and not managed
|
* Note: 'mousedown' currently is "always on" and not managed
|
||||||
* by onProtocolChange.
|
* by onProtocolChange.
|
||||||
*/
|
*/
|
||||||
const requestedEvents: {[key: string]: ((ev: Event) => void) | null} = {
|
const requestedEvents: { [key: string]: ((ev: Event) => void) | null } = {
|
||||||
mouseup: null,
|
mouseup: null,
|
||||||
wheel: null,
|
wheel: null,
|
||||||
mousedrag: null,
|
mousedrag: null,
|
||||||
mousemove: null
|
mousemove: null
|
||||||
};
|
};
|
||||||
const eventListeners: {[key: string]: (ev: Event) => void} = {
|
const eventListeners: { [key: string]: (ev: Event) => void } = {
|
||||||
mouseup: (ev: MouseEvent) => {
|
mouseup: (ev: MouseEvent) => {
|
||||||
sendEvent(ev);
|
sendEvent(ev);
|
||||||
if (!ev.buttons) {
|
if (!ev.buttons) {
|
||||||
@@ -858,7 +864,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct and send sequences
|
// 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 = '';
|
let data = '';
|
||||||
for (let i = 0; i < Math.abs(amount); i++) {
|
for (let i = 0; i < Math.abs(amount); i++) {
|
||||||
data += sequence;
|
data += sequence;
|
||||||
@@ -1121,6 +1127,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 {
|
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
|
||||||
const joinerId = this._renderService.registerCharacterJoiner(handler);
|
const joinerId = this._renderService.registerCharacterJoiner(handler);
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
@@ -1273,8 +1283,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
|
|
||||||
private _isThirdLevelShift(browser: IBrowser, ev: IKeyboardEvent): boolean {
|
private _isThirdLevelShift(browser: IBrowser, ev: IKeyboardEvent): boolean {
|
||||||
const thirdLevelKey =
|
const thirdLevelKey =
|
||||||
(browser.isMac && !this.options.macOptionIsMeta && 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);
|
(browser.isWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);
|
||||||
|
|
||||||
if (ev.type === 'keypress') {
|
if (ev.type === 'keypress') {
|
||||||
return thirdLevelKey;
|
return thirdLevelKey;
|
||||||
|
|||||||
+16
-12
@@ -9,10 +9,10 @@ import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'
|
|||||||
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset } from 'common/Types';
|
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset } from 'common/Types';
|
||||||
import { Buffer } from 'common/buffer/Buffer';
|
import { Buffer } from 'common/buffer/Buffer';
|
||||||
import * as Browser from 'common/Platform';
|
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 { Terminal } from './Terminal';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
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 { IOptionsService, IUnicodeService } from 'common/services/Services';
|
||||||
import { EventEmitter } from 'common/EventEmitter';
|
import { EventEmitter } from 'common/EventEmitter';
|
||||||
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
|
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
|
||||||
@@ -94,6 +94,9 @@ export class MockTerminal implements ITerminal {
|
|||||||
deregisterLinkMatcher(matcherId: number): void {
|
deregisterLinkMatcher(matcherId: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
hasSelection(): boolean {
|
hasSelection(): boolean {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
@@ -136,6 +139,7 @@ export class MockTerminal implements ITerminal {
|
|||||||
bracketedPasteMode: boolean;
|
bracketedPasteMode: boolean;
|
||||||
renderer: IRenderer;
|
renderer: IRenderer;
|
||||||
linkifier: ILinkifier;
|
linkifier: ILinkifier;
|
||||||
|
linkifier2: ILinkifier2;
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
options: ITerminalOptions = {};
|
options: ITerminalOptions = {};
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
@@ -300,16 +304,16 @@ export class MockRenderer implements IRenderer {
|
|||||||
setColors(colors: IColorSet): void {
|
setColors(colors: IColorSet): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
onResize(cols: number, rows: number): void {}
|
onResize(cols: number, rows: number): void { }
|
||||||
onCharSizeChanged(): void {}
|
onCharSizeChanged(): void { }
|
||||||
onBlur(): void {}
|
onBlur(): void { }
|
||||||
onFocus(): void {}
|
onFocus(): void { }
|
||||||
onSelectionChanged(start: [number, number], end: [number, number]): void {}
|
onSelectionChanged(start: [number, number], end: [number, number]): void { }
|
||||||
onCursorMove(): void {}
|
onCursorMove(): void { }
|
||||||
onOptionsChanged(): void {}
|
onOptionsChanged(): void { }
|
||||||
onDevicePixelRatioChange(): void {}
|
onDevicePixelRatioChange(): void { }
|
||||||
clear(): void {}
|
clear(): void { }
|
||||||
renderRows(start: number, end: number): void {}
|
renderRows(start: number, end: number): void { }
|
||||||
registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
|
registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
|
||||||
deregisterCharacterJoiner(): boolean { return true; }
|
deregisterCharacterJoiner(): boolean { return true; }
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+4
-2
@@ -3,10 +3,10 @@
|
|||||||
* @license MIT
|
* @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 { ICharset, IAttributeData, CharData, CoreMouseEventType } from 'common/Types';
|
||||||
import { IEvent, IEventEmitter } from 'common/EventEmitter';
|
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 { IOptionsService, IUnicodeService } from 'common/services/Services';
|
||||||
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
||||||
import { IParams, IFunctionIdentifier } from 'common/parser/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;
|
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
||||||
registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number;
|
registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number;
|
||||||
deregisterLinkMatcher(matcherId: number): void;
|
deregisterLinkMatcher(matcherId: number): void;
|
||||||
|
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
|
||||||
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
|
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
|
||||||
deregisterCharacterJoiner(joinerId: number): void;
|
deregisterCharacterJoiner(joinerId: number): void;
|
||||||
addMarker(cursorYOffset: number): IMarker;
|
addMarker(cursorYOffset: number): IMarker;
|
||||||
@@ -212,6 +213,7 @@ export interface IElementAccessor {
|
|||||||
|
|
||||||
export interface ILinkifierAccessor {
|
export interface ILinkifierAccessor {
|
||||||
linkifier: ILinkifier;
|
linkifier: ILinkifier;
|
||||||
|
linkifier2: ILinkifier2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: The options that are not in the public API should be reviewed
|
// TODO: The options that are not in the public API should be reviewed
|
||||||
|
|||||||
@@ -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, ICoreService } 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);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _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 - 1, range.end.y - scrollOffset - 1, undefined));
|
||||||
|
element.classList.add('xterm-cursor-pointer');
|
||||||
|
|
||||||
|
if (link.hover) {
|
||||||
|
link.hover(event, link.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _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 - 1, 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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+31
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import { IEvent } from 'common/EventEmitter';
|
import { IEvent } from 'common/EventEmitter';
|
||||||
import { IDisposable } from 'common/Types';
|
import { IDisposable } from 'common/Types';
|
||||||
|
import { IMouseService, IRenderService } from './services/Services';
|
||||||
|
|
||||||
export interface IColorManager {
|
export interface IColorManager {
|
||||||
colors: IColorSet;
|
colors: IColorSet;
|
||||||
@@ -105,6 +106,14 @@ export interface ILinkifier {
|
|||||||
deregisterLinkMatcher(matcherId: number): boolean;
|
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 {
|
export interface ILinkMatcherOptions {
|
||||||
/**
|
/**
|
||||||
* The index of the link from the regex.match(text) call. This defaults to 0
|
* 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;
|
leaveCallback: () => any | undefined;
|
||||||
willLinkActivate: (e: MouseEvent) => boolean;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
private _bubbleScroll(ev: Event, amount: number): boolean {
|
private _bubbleScroll(ev: Event, amount: number): boolean {
|
||||||
const scrollPosFromTop = this._viewportElement.scrollTop + this._lastRecordedViewportHeight;
|
const scrollPosFromTop = this._viewportElement.scrollTop + this._lastRecordedViewportHeight;
|
||||||
if ((amount < 0 && this._viewportElement.scrollTop !== 0) ||
|
if ((amount < 0 && this._viewportElement.scrollTop !== 0) ||
|
||||||
(amount > 0 && scrollPosFromTop < this._lastRecordedBufferHeight)) {
|
(amount > 0 && scrollPosFromTop < this._lastRecordedBufferHeight)) {
|
||||||
if (ev.cancelable) {
|
if (ev.cancelable) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
@@ -235,8 +235,8 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
const modifier = this._optionsService.options.fastScrollModifier;
|
const modifier = this._optionsService.options.fastScrollModifier;
|
||||||
// Multiply the scroll speed when the modifier is down
|
// Multiply the scroll speed when the modifier is down
|
||||||
if ((modifier === 'alt' && ev.altKey) ||
|
if ((modifier === 'alt' && ev.altKey) ||
|
||||||
(modifier === 'ctrl' && ev.ctrlKey) ||
|
(modifier === 'ctrl' && ev.ctrlKey) ||
|
||||||
(modifier === 'shift' && ev.shiftKey)) {
|
(modifier === 'shift' && ev.shiftKey)) {
|
||||||
return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity;
|
return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { IRenderDimensions } from 'browser/renderer/Types';
|
|||||||
import { BaseRenderLayer } from './BaseRenderLayer';
|
import { BaseRenderLayer } from './BaseRenderLayer';
|
||||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||||
import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils';
|
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';
|
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||||
|
|
||||||
export class LinkRenderLayer extends BaseRenderLayer {
|
export class LinkRenderLayer extends BaseRenderLayer {
|
||||||
@@ -19,12 +19,16 @@ export class LinkRenderLayer extends BaseRenderLayer {
|
|||||||
colors: IColorSet,
|
colors: IColorSet,
|
||||||
rendererId: number,
|
rendererId: number,
|
||||||
linkifier: ILinkifier,
|
linkifier: ILinkifier,
|
||||||
|
linkifier2: ILinkifier2,
|
||||||
readonly bufferService: IBufferService,
|
readonly bufferService: IBufferService,
|
||||||
readonly optionsService: IOptionsService
|
readonly optionsService: IOptionsService
|
||||||
) {
|
) {
|
||||||
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService);
|
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService);
|
||||||
linkifier.onLinkHover(e => this._onLinkHover(e));
|
linkifier.onLinkHover(e => this._onLinkHover(e));
|
||||||
linkifier.onLinkLeave(e => this._onLinkLeave(e));
|
linkifier.onLinkLeave(e => this._onLinkLeave(e));
|
||||||
|
|
||||||
|
linkifier2.onLinkHover(e => this._onLinkHover(e));
|
||||||
|
linkifier2.onLinkLeave(e => this._onLinkLeave(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(dim: IRenderDimensions): void {
|
public resize(dim: IRenderDimensions): void {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICh
|
|||||||
import { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer';
|
import { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer';
|
||||||
import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry';
|
import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
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 { ICharSizeService, ICoreBrowserService } from 'browser/services/Services';
|
||||||
import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';
|
import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';
|
||||||
import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache';
|
import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache';
|
||||||
@@ -33,7 +33,8 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
constructor(
|
constructor(
|
||||||
private _colors: IColorSet,
|
private _colors: IColorSet,
|
||||||
private readonly _screenElement: HTMLElement,
|
private readonly _screenElement: HTMLElement,
|
||||||
private readonly _linkifier: ILinkifier,
|
readonly linkifier: ILinkifier,
|
||||||
|
readonly linkifier2: ILinkifier2,
|
||||||
@IBufferService private readonly _bufferService: IBufferService,
|
@IBufferService private readonly _bufferService: IBufferService,
|
||||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||||
@@ -47,7 +48,7 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
this._renderLayers = [
|
this._renderLayers = [
|
||||||
new TextRenderLayer(this._screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService),
|
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 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)
|
new CursorRenderLayer(this._screenElement, 3, this._colors, this._id, this._onRequestRefreshRows, this._bufferService, _optionsService, coreService, coreBrowserService)
|
||||||
];
|
];
|
||||||
this.dimensions = {
|
this.dimensions = {
|
||||||
|
|||||||
@@ -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 { 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 { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
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 { ICharSizeService } from 'browser/services/Services';
|
||||||
import { IOptionsService, IBufferService } from 'common/services/Services';
|
import { IOptionsService, IBufferService } from 'common/services/Services';
|
||||||
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||||
@@ -48,6 +48,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
private readonly _screenElement: HTMLElement,
|
private readonly _screenElement: HTMLElement,
|
||||||
private readonly _viewportElement: HTMLElement,
|
private readonly _viewportElement: HTMLElement,
|
||||||
private readonly _linkifier: ILinkifier,
|
private readonly _linkifier: ILinkifier,
|
||||||
|
private readonly _linkifier2: ILinkifier2,
|
||||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||||
@IBufferService private readonly _bufferService: IBufferService
|
@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.onLinkHover(e => this._onLinkHover(e));
|
||||||
this._linkifier.onLinkLeave(e => this._onLinkLeave(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 {
|
public dispose(): void {
|
||||||
@@ -127,12 +131,12 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles =
|
const styles =
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
|
||||||
` display: inline-block;` +
|
` display: inline-block;` +
|
||||||
` height: 100%;` +
|
` height: 100%;` +
|
||||||
` vertical-align: top;` +
|
` vertical-align: top;` +
|
||||||
` width: ${this.dimensions.actualCellWidth}px` +
|
` width: ${this.dimensions.actualCellWidth}px` +
|
||||||
`}`;
|
`}`;
|
||||||
|
|
||||||
this._dimensionsStyleElement.innerHTML = styles;
|
this._dimensionsStyleElement.innerHTML = styles;
|
||||||
|
|
||||||
@@ -154,84 +158,84 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Base CSS
|
// Base CSS
|
||||||
let styles =
|
let styles =
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
|
||||||
` color: ${this._colors.foreground.css};` +
|
` color: ${this._colors.foreground.css};` +
|
||||||
` font-family: ${this._optionsService.options.fontFamily};` +
|
` font-family: ${this._optionsService.options.fontFamily};` +
|
||||||
` font-size: ${this._optionsService.options.fontSize}px;` +
|
` font-size: ${this._optionsService.options.fontSize}px;` +
|
||||||
`}`;
|
`}`;
|
||||||
// Text styles
|
// Text styles
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +
|
`${this._terminalSelector} span:not(.${BOLD_CLASS}) {` +
|
||||||
` font-weight: ${this._optionsService.options.fontWeight};` +
|
` font-weight: ${this._optionsService.options.fontWeight};` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} span.${BOLD_CLASS} {` +
|
`${this._terminalSelector} span.${BOLD_CLASS} {` +
|
||||||
` font-weight: ${this._optionsService.options.fontWeightBold};` +
|
` font-weight: ${this._optionsService.options.fontWeightBold};` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} span.${ITALIC_CLASS} {` +
|
`${this._terminalSelector} span.${ITALIC_CLASS} {` +
|
||||||
` font-style: italic;` +
|
` font-style: italic;` +
|
||||||
`}`;
|
`}`;
|
||||||
// Blink animation
|
// Blink animation
|
||||||
styles +=
|
styles +=
|
||||||
`@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` +
|
`@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` +
|
||||||
` 50% {` +
|
` 50% {` +
|
||||||
` box-shadow: none;` +
|
` box-shadow: none;` +
|
||||||
` }` +
|
` }` +
|
||||||
`}`;
|
`}`;
|
||||||
styles +=
|
styles +=
|
||||||
`@keyframes blink_block` + `_` + this._terminalClass + ` {` +
|
`@keyframes blink_block` + `_` + this._terminalClass + ` {` +
|
||||||
` 0% {` +
|
` 0% {` +
|
||||||
` background-color: ${this._colors.cursor.css};` +
|
` background-color: ${this._colors.cursor.css};` +
|
||||||
` color: ${this._colors.cursorAccent.css};` +
|
` color: ${this._colors.cursorAccent.css};` +
|
||||||
` }` +
|
` }` +
|
||||||
` 50% {` +
|
` 50% {` +
|
||||||
` background-color: ${this._colors.cursorAccent.css};` +
|
` background-color: ${this._colors.cursorAccent.css};` +
|
||||||
` color: ${this._colors.cursor.css};` +
|
` color: ${this._colors.cursor.css};` +
|
||||||
` }` +
|
` }` +
|
||||||
`}`;
|
`}`;
|
||||||
// Cursor
|
// Cursor
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||||
` outline: 1px solid ${this._colors.cursor.css};` +
|
` outline: 1px solid ${this._colors.cursor.css};` +
|
||||||
` outline-offset: -1px;` +
|
` outline-offset: -1px;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` +
|
`${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;` +
|
` 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} {` +
|
`${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;` +
|
` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||||
` background-color: ${this._colors.cursor.css};` +
|
` background-color: ${this._colors.cursor.css};` +
|
||||||
` color: ${this._colors.cursorAccent.css};` +
|
` color: ${this._colors.cursorAccent.css};` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
|
`${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;` +
|
` 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} {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
||||||
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
|
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
|
||||||
`}`;
|
`}`;
|
||||||
// Selection
|
// Selection
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${SELECTION_CLASS} {` +
|
`${this._terminalSelector} .${SELECTION_CLASS} {` +
|
||||||
` position: absolute;` +
|
` position: absolute;` +
|
||||||
` top: 0;` +
|
` top: 0;` +
|
||||||
` left: 0;` +
|
` left: 0;` +
|
||||||
` z-index: 1;` +
|
` z-index: 1;` +
|
||||||
` pointer-events: none;` +
|
` pointer-events: none;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${SELECTION_CLASS} div {` +
|
`${this._terminalSelector} .${SELECTION_CLASS} div {` +
|
||||||
` position: absolute;` +
|
` position: absolute;` +
|
||||||
` background-color: ${this._colors.selection.css};` +
|
` background-color: ${this._colors.selection.css};` +
|
||||||
`}`;
|
`}`;
|
||||||
// Colors
|
// Colors
|
||||||
this._colors.ansi.forEach((c, i) => {
|
this._colors.ansi.forEach((c, i) => {
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
|
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
|
||||||
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
|
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
|
||||||
});
|
});
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).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._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`;
|
||||||
|
|
||||||
this._themeStyleElement.innerHTML = styles;
|
this._themeStyleElement.innerHTML = styles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* @license MIT
|
* @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, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier, ILinkProvider, IUnicodeHandling, IUnicodeVersionProvider } from 'xterm';
|
||||||
import { ITerminal } from '../Types';
|
import { ITerminal } from '../Types';
|
||||||
import { IBufferLine, ICellData } from 'common/Types';
|
import { IBufferLine, ICellData } from 'common/Types';
|
||||||
import { IBuffer } from 'common/buffer/Types';
|
import { IBuffer } from 'common/buffer/Types';
|
||||||
@@ -72,6 +72,9 @@ export class Terminal implements ITerminalApi {
|
|||||||
public deregisterLinkMatcher(matcherId: number): void {
|
public deregisterLinkMatcher(matcherId: number): void {
|
||||||
this._core.deregisterLinkMatcher(matcherId);
|
this._core.deregisterLinkMatcher(matcherId);
|
||||||
}
|
}
|
||||||
|
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
|
||||||
|
return this._core.registerLinkProvider(linkProvider);
|
||||||
|
}
|
||||||
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
|
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
|
||||||
return this._core.registerCharacterJoiner(handler);
|
return this._core.registerCharacterJoiner(handler);
|
||||||
}
|
}
|
||||||
@@ -229,7 +232,7 @@ class BufferLineApiView implements IBufferLineApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ParserApi implements IParser {
|
class ParserApi implements IParser {
|
||||||
constructor(private _core: ITerminal) {}
|
constructor(private _core: ITerminal) { }
|
||||||
|
|
||||||
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||||
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
|
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
|
||||||
@@ -258,7 +261,7 @@ class ParserApi implements IParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UnicodeApi implements IUnicodeHandling {
|
class UnicodeApi implements IUnicodeHandling {
|
||||||
constructor(private _core: ITerminal) {}
|
constructor(private _core: ITerminal) { }
|
||||||
|
|
||||||
public register(provider: IUnicodeVersionProvider): void {
|
public register(provider: IUnicodeVersionProvider): void {
|
||||||
this._core.unicodeService.register(provider);
|
this._core.unicodeService.register(provider);
|
||||||
|
|||||||
@@ -524,6 +524,142 @@ describe('API Integration Tests', function(): void {
|
|||||||
await page.evaluate(`window.term.dispose()`);
|
await page.evaluate(`window.term.dispose()`);
|
||||||
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
|
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('registerLinkProvider', () => {
|
||||||
|
it('should fire provideLink when hovering cells', async () => {
|
||||||
|
await openTerminal({ rendererType: 'dom' });
|
||||||
|
await page.evaluate(`
|
||||||
|
window.calls = [];
|
||||||
|
window.disposable = window.term.registerLinkProvider({
|
||||||
|
provideLink: (position, cb) => {
|
||||||
|
calls.push(position);
|
||||||
|
cb(undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`);
|
||||||
|
const dims = await getDimensions();
|
||||||
|
await moveMouseCell(page, dims, 1, 1);
|
||||||
|
await moveMouseCell(page, dims, 2, 2);
|
||||||
|
await moveMouseCell(page, dims, 10, 4);
|
||||||
|
await pollFor(page, `window.calls`, [{ x: 1, y: 1 }, { x: 2, y: 2 }, { x: 10, y: 4 }]);
|
||||||
|
await page.evaluate(`window.disposable.dispose()`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire hover and leave events on the link', async () => {
|
||||||
|
await openTerminal({ rendererType: 'dom' });
|
||||||
|
await writeSync(page, 'foo bar baz');
|
||||||
|
// Wait for renderer to catch up as links are cleared on render
|
||||||
|
await pollFor(page, `document.querySelector('.xterm-rows').textContent`, 'foo bar baz ');
|
||||||
|
await page.evaluate(`
|
||||||
|
window.calls = [];
|
||||||
|
window.disposable = window.term.registerLinkProvider({
|
||||||
|
provideLink: (position, cb) => {
|
||||||
|
window.calls.push('provide ' + position.x + ',' + position.y);
|
||||||
|
if (position.x >= 5 && position.x <= 7 && position.y === 1) {
|
||||||
|
window.calls.push('match');
|
||||||
|
cb({
|
||||||
|
range: { start: { x: 5, y: 1 }, end: { x: 7, y: 1 } },
|
||||||
|
text: 'bar',
|
||||||
|
activate: () => window.calls.push('activate'),
|
||||||
|
hover: () => window.calls.push('hover'),
|
||||||
|
leave: () => window.calls.push('leave')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`);
|
||||||
|
const dims = await getDimensions();
|
||||||
|
await moveMouseCell(page, dims, 5, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match', 'hover']);
|
||||||
|
await moveMouseCell(page, dims, 4, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match', 'hover', 'leave', 'provide 4,1']);
|
||||||
|
await moveMouseCell(page, dims, 7, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match', 'hover', 'leave', 'provide 4,1', 'provide 7,1', 'match', 'hover']);
|
||||||
|
await moveMouseCell(page, dims, 8, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match', 'hover', 'leave', 'provide 4,1', 'provide 7,1', 'match', 'hover', 'leave', 'provide 8,1']);
|
||||||
|
await page.evaluate(`window.disposable.dispose()`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work fine when hover and leave callbacks are not provided', async () => {
|
||||||
|
await openTerminal({ rendererType: 'dom' });
|
||||||
|
await writeSync(page, 'foo bar baz');
|
||||||
|
// Wait for renderer to catch up as links are cleared on render
|
||||||
|
await pollFor(page, `document.querySelector('.xterm-rows').textContent`, 'foo bar baz ');
|
||||||
|
await page.evaluate(`
|
||||||
|
window.calls = [];
|
||||||
|
window.disposable = window.term.registerLinkProvider({
|
||||||
|
provideLink: (position, cb) => {
|
||||||
|
window.calls.push('provide ' + position.x + ',' + position.y);
|
||||||
|
if (position.x >= 5 && position.x <= 7 && position.y === 1) {
|
||||||
|
window.calls.push('match');
|
||||||
|
cb({
|
||||||
|
range: { start: { x: 5, y: 1 }, end: { x: 7, y: 1 } },
|
||||||
|
text: 'bar',
|
||||||
|
activate: () => window.calls.push('activate')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`);
|
||||||
|
const dims = await getDimensions();
|
||||||
|
await moveMouseCell(page, dims, 5, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match']);
|
||||||
|
await moveMouseCell(page, dims, 4, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match', 'provide 4,1']);
|
||||||
|
await moveMouseCell(page, dims, 7, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match', 'provide 4,1', 'provide 7,1', 'match']);
|
||||||
|
await moveMouseCell(page, dims, 8, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 5,1', 'match', 'provide 4,1', 'provide 7,1', 'match', 'provide 8,1']);
|
||||||
|
await page.evaluate(`window.disposable.dispose()`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire activate events when clicking the link', async () => {
|
||||||
|
await openTerminal({ rendererType: 'dom' });
|
||||||
|
await writeSync(page, 'a b c');
|
||||||
|
|
||||||
|
// Wait for renderer to catch up as links are cleared on render
|
||||||
|
await pollFor(page, `document.querySelector('.xterm-rows').textContent`, 'a b c ');
|
||||||
|
|
||||||
|
// Focus terminal to avoid a render event clearing the active link
|
||||||
|
const dims = await getDimensions();
|
||||||
|
await moveMouseCell(page, dims, 5, 5);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.up();
|
||||||
|
await timeout(50); // Not sure how to avoid this timeout, checking for xterm-focus doesn't help
|
||||||
|
|
||||||
|
await page.evaluate(`
|
||||||
|
window.calls = [];
|
||||||
|
window.disposable = window.term.registerLinkProvider({
|
||||||
|
provideLink: (position, cb) => {
|
||||||
|
window.calls.push('provide ' + position.x + ',' + position.y);
|
||||||
|
cb({
|
||||||
|
range: { start: position, end: position },
|
||||||
|
text: window.term.buffer.getLine(position.y - 1).getCell(position.x - 1).getChars(),
|
||||||
|
activate: (_, text) => window.calls.push('activate ' + text),
|
||||||
|
hover: () => window.calls.push('hover'),
|
||||||
|
leave: () => window.calls.push('leave')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`);
|
||||||
|
await moveMouseCell(page, dims, 3, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 3,1', 'hover']);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.up();
|
||||||
|
await pollFor(page, `window.calls`, ['provide 3,1', 'hover', 'activate b']);
|
||||||
|
await moveMouseCell(page, dims, 1, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 3,1', 'hover', 'activate b', 'leave', 'provide 1,1', 'hover']);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.up();
|
||||||
|
await pollFor(page, `window.calls`, ['provide 3,1', 'hover', 'activate b', 'leave', 'provide 1,1', 'hover', 'activate a']);
|
||||||
|
await moveMouseCell(page, dims, 5, 1);
|
||||||
|
await pollFor(page, `window.calls`, ['provide 3,1', 'hover', 'activate b', 'leave', 'provide 1,1', 'hover', 'activate a', 'leave', 'provide 5,1', 'hover']);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.up();
|
||||||
|
await pollFor(page, `window.calls`, ['provide 3,1', 'hover', 'activate b', 'leave', 'provide 1,1', 'hover', 'activate a', 'leave', 'provide 5,1', 'hover', 'activate c']);
|
||||||
|
await page.evaluate(`window.disposable.dispose()`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
||||||
@@ -535,3 +671,49 @@ async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
|||||||
await page.waitForSelector('.xterm-text-layer');
|
await page.waitForSelector('.xterm-text-layer');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IDimensions {
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
renderDimensions: IRenderDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRenderDimensions {
|
||||||
|
scaledCharWidth: number;
|
||||||
|
scaledCharHeight: number;
|
||||||
|
scaledCellWidth: number;
|
||||||
|
scaledCellHeight: number;
|
||||||
|
scaledCharLeft: number;
|
||||||
|
scaledCharTop: number;
|
||||||
|
scaledCanvasWidth: number;
|
||||||
|
scaledCanvasHeight: number;
|
||||||
|
canvasWidth: number;
|
||||||
|
canvasHeight: number;
|
||||||
|
actualCellWidth: number;
|
||||||
|
actualCellHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDimensions(): Promise<IDimensions> {
|
||||||
|
return await page.evaluate(`
|
||||||
|
(function() {
|
||||||
|
const rect = document.querySelector('.xterm-rows').getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
top: rect.top,
|
||||||
|
left: rect.left,
|
||||||
|
renderDimensions: window.term._core._renderService.dimensions
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCellCoordinates(dimensions: IDimensions, col: number, row: number): Promise<{ x: number, y: number }> {
|
||||||
|
return {
|
||||||
|
x: dimensions.left + dimensions.renderDimensions.scaledCellWidth * (col - 0.5),
|
||||||
|
y: dimensions.top + dimensions.renderDimensions.scaledCellHeight * (row - 0.5)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function moveMouseCell(page: puppeteer.Page, dimensions: IDimensions, col: number, row: number) {
|
||||||
|
const coords = await getCellCoordinates(dimensions, col, row);
|
||||||
|
await page.mouse.move(coords.x, coords.y);
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+116
-25
@@ -387,10 +387,10 @@ declare module 'xterm' {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable various window manipulation and report features (CSI Ps ; Ps ; Ps t).
|
* Enable various window manipulation and report features (CSI Ps ; Ps ; Ps t).
|
||||||
*
|
*
|
||||||
* Most settings have no default implementation, as they heavily rely on
|
* Most settings have no default implementation, as they heavily rely on
|
||||||
* the embedding environment.
|
* the embedding environment.
|
||||||
*
|
*
|
||||||
* To implement a feature, create a custom CSI hook like this:
|
* To implement a feature, create a custom CSI hook like this:
|
||||||
* ```ts
|
* ```ts
|
||||||
* term.parser.addCsiHandler({final: 't'}, params => {
|
* term.parser.addCsiHandler({final: 't'}, params => {
|
||||||
@@ -403,8 +403,8 @@ declare module 'xterm' {
|
|||||||
* return false; // any Ps that was not handled
|
* return false; // any Ps that was not handled
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Note on security:
|
* Note on security:
|
||||||
* Most features are meant to deal with some information of the host machine
|
* Most features are meant to deal with some information of the host machine
|
||||||
* where the terminal runs on. This is seen as a security risk possibly leaking
|
* where the terminal runs on. This is seen as a security risk possibly leaking
|
||||||
* sensitive data of the host to the program in the terminal. Therefore all options
|
* sensitive data of the host to the program in the terminal. Therefore all options
|
||||||
@@ -413,18 +413,18 @@ declare module 'xterm' {
|
|||||||
*/
|
*/
|
||||||
export interface IWindowOptions {
|
export interface IWindowOptions {
|
||||||
/**
|
/**
|
||||||
* Ps=1 De-iconify window.
|
* Ps=1 De-iconify window.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
restoreWin?: boolean;
|
restoreWin?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=2 Iconify window.
|
* Ps=2 Iconify window.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
minimizeWin?: boolean;
|
minimizeWin?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=3 ; x ; y
|
* Ps=3 ; x ; y
|
||||||
* Move window to [x, y].
|
* Move window to [x, y].
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
setWinPosition?: boolean;
|
setWinPosition?: boolean;
|
||||||
@@ -432,17 +432,17 @@ declare module 'xterm' {
|
|||||||
* Ps = 4 ; height ; width
|
* Ps = 4 ; height ; width
|
||||||
* Resize the window to given `height` and `width` in pixels.
|
* Resize the window to given `height` and `width` in pixels.
|
||||||
* Omitted parameters should reuse the current height or width.
|
* Omitted parameters should reuse the current height or width.
|
||||||
* Zero parameters should use the display's height or width.
|
* Zero parameters should use the display's height or width.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
setWinSizePixels?: boolean;
|
setWinSizePixels?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=5 Raise the window to the front of the stacking order.
|
* Ps=5 Raise the window to the front of the stacking order.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
raiseWin?: boolean;
|
raiseWin?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=6 Lower the xterm window to the bottom of the stacking order.
|
* Ps=6 Lower the xterm window to the bottom of the stacking order.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
lowerWin?: boolean;
|
lowerWin?: boolean;
|
||||||
@@ -452,7 +452,7 @@ declare module 'xterm' {
|
|||||||
* Ps = 8 ; height ; width
|
* Ps = 8 ; height ; width
|
||||||
* Resize the text area to given height and width in characters.
|
* Resize the text area to given height and width in characters.
|
||||||
* Omitted parameters should reuse the current height or width.
|
* Omitted parameters should reuse the current height or width.
|
||||||
* Zero parameters use the display's height or width.
|
* Zero parameters use the display's height or width.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
setWinSizeChars?: boolean;
|
setWinSizeChars?: boolean;
|
||||||
@@ -460,81 +460,81 @@ declare module 'xterm' {
|
|||||||
* Ps=9 ; 0 Restore maximized window.
|
* Ps=9 ; 0 Restore maximized window.
|
||||||
* Ps=9 ; 1 Maximize window (i.e., resize to screen size).
|
* Ps=9 ; 1 Maximize window (i.e., resize to screen size).
|
||||||
* Ps=9 ; 2 Maximize window vertically.
|
* Ps=9 ; 2 Maximize window vertically.
|
||||||
* Ps=9 ; 3 Maximize window horizontally.
|
* Ps=9 ; 3 Maximize window horizontally.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
maximizeWin?: boolean;
|
maximizeWin?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=10 ; 0 Undo full-screen mode.
|
* Ps=10 ; 0 Undo full-screen mode.
|
||||||
* Ps=10 ; 1 Change to full-screen.
|
* Ps=10 ; 1 Change to full-screen.
|
||||||
* Ps=10 ; 2 Toggle full-screen.
|
* Ps=10 ; 2 Toggle full-screen.
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
fullscreenWin?: boolean;
|
fullscreenWin?: boolean;
|
||||||
/** Ps=11 Report xterm window state.
|
/** Ps=11 Report xterm window state.
|
||||||
* If the xterm window is non-iconified, it returns "CSI 1 t".
|
* If the xterm window is non-iconified, it returns "CSI 1 t".
|
||||||
* If the xterm window is iconified, it returns "CSI 2 t".
|
* If the xterm window is iconified, it returns "CSI 2 t".
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
getWinState?: boolean;
|
getWinState?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=13 Report xterm window position. Result is "CSI 3 ; x ; y t".
|
* Ps=13 Report xterm window position. Result is "CSI 3 ; x ; y t".
|
||||||
* Ps=13 ; 2 Report xterm text-area position. Result is "CSI 3 ; x ; y t".
|
* Ps=13 ; 2 Report xterm text-area position. Result is "CSI 3 ; x ; y t".
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
getWinPosition?: boolean;
|
getWinPosition?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=14 Report xterm text area size in pixels. Result is "CSI 4 ; height ; width t".
|
* Ps=14 Report xterm text area size in pixels. Result is "CSI 4 ; height ; width t".
|
||||||
* Ps=14 ; 2 Report xterm window size in pixels. Result is "CSI 4 ; height ; width t".
|
* Ps=14 ; 2 Report xterm window size in pixels. Result is "CSI 4 ; height ; width t".
|
||||||
* Has a default implementation.
|
* Has a default implementation.
|
||||||
*/
|
*/
|
||||||
getWinSizePixels?: boolean;
|
getWinSizePixels?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=15 Report size of the screen in pixels. Result is "CSI 5 ; height ; width t".
|
* Ps=15 Report size of the screen in pixels. Result is "CSI 5 ; height ; width t".
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
getScreenSizePixels?: boolean;
|
getScreenSizePixels?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=16 Report xterm character cell size in pixels. Result is "CSI 6 ; height ; width t".
|
* Ps=16 Report xterm character cell size in pixels. Result is "CSI 6 ; height ; width t".
|
||||||
* Has a default implementation.
|
* Has a default implementation.
|
||||||
*/
|
*/
|
||||||
getCellSizePixels?: boolean;
|
getCellSizePixels?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=18 Report the size of the text area in characters. Result is "CSI 8 ; height ; width t".
|
* Ps=18 Report the size of the text area in characters. Result is "CSI 8 ; height ; width t".
|
||||||
* Has a default implementation.
|
* Has a default implementation.
|
||||||
*/
|
*/
|
||||||
getWinSizeChars?: boolean;
|
getWinSizeChars?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=19 Report the size of the screen in characters. Result is "CSI 9 ; height ; width t".
|
* Ps=19 Report the size of the screen in characters. Result is "CSI 9 ; height ; width t".
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
getScreenSizeChars?: boolean;
|
getScreenSizeChars?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=20 Report xterm window's icon label. Result is "OSC L label ST".
|
* Ps=20 Report xterm window's icon label. Result is "OSC L label ST".
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
getIconTitle?: boolean;
|
getIconTitle?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=21 Report xterm window's title. Result is "OSC l label ST".
|
* Ps=21 Report xterm window's title. Result is "OSC l label ST".
|
||||||
* No default implementation.
|
* No default implementation.
|
||||||
*/
|
*/
|
||||||
getWinTitle?: boolean;
|
getWinTitle?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=22 ; 0 Save xterm icon and window title on stack.
|
* Ps=22 ; 0 Save xterm icon and window title on stack.
|
||||||
* Ps=22 ; 1 Save xterm icon title on stack.
|
* Ps=22 ; 1 Save xterm icon title on stack.
|
||||||
* Ps=22 ; 2 Save xterm window title on stack.
|
* Ps=22 ; 2 Save xterm window title on stack.
|
||||||
* All variants have a default implementation.
|
* All variants have a default implementation.
|
||||||
*/
|
*/
|
||||||
pushTitle?: boolean;
|
pushTitle?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps=23 ; 0 Restore xterm icon and window title from stack.
|
* Ps=23 ; 0 Restore xterm icon and window title from stack.
|
||||||
* Ps=23 ; 1 Restore xterm icon title from stack.
|
* Ps=23 ; 1 Restore xterm icon title from stack.
|
||||||
* Ps=23 ; 2 Restore xterm window title from stack.
|
* Ps=23 ; 2 Restore xterm window title from stack.
|
||||||
* All variants have a default implementation.
|
* All variants have a default implementation.
|
||||||
*/
|
*/
|
||||||
popTitle?: boolean;
|
popTitle?: boolean;
|
||||||
/**
|
/**
|
||||||
* Ps>=24 Resize to Ps lines (DECSLPP).
|
* Ps>=24 Resize to Ps lines (DECSLPP).
|
||||||
* DECSLPP is not implemented. This settings is also used to
|
* DECSLPP is not implemented. This settings is also used to
|
||||||
* enable / disable DECCOLM (earlier variant of DECSLPP).
|
* enable / disable DECCOLM (earlier variant of DECSLPP).
|
||||||
*/
|
*/
|
||||||
@@ -722,6 +722,8 @@ declare module 'xterm' {
|
|||||||
/**
|
/**
|
||||||
* (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to
|
* (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to
|
||||||
* be matched and handled.
|
* be matched and handled.
|
||||||
|
* @deprecated The link matcher API is now deprecated in favor of the link
|
||||||
|
* provider API, see `registerLinkProvider`.
|
||||||
* @param regex The regular expression to search for, specifically this
|
* @param regex The regular expression to search for, specifically this
|
||||||
* searches the textContent of the rows. You will want to use \s to match a
|
* searches the textContent of the rows. You will want to use \s to match a
|
||||||
* space ' ' character for example.
|
* space ' ' character for example.
|
||||||
@@ -733,10 +735,20 @@ declare module 'xterm' {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* (EXPERIMENTAL) Deregisters a link matcher if it has been registered.
|
* (EXPERIMENTAL) Deregisters a link matcher if it has been registered.
|
||||||
|
* @deprecated The link matcher API is now deprecated in favor of the link
|
||||||
|
* provider API, see `registerLinkProvider`.
|
||||||
* @param matcherId The link matcher's ID (returned after register)
|
* @param matcherId The link matcher's ID (returned after register)
|
||||||
*/
|
*/
|
||||||
deregisterLinkMatcher(matcherId: number): void;
|
deregisterLinkMatcher(matcherId: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (EXPERIMENTAL) Registers a link provider, allowing a custom parser to
|
||||||
|
* be used to match and handle links. Multiple link providers can be used,
|
||||||
|
* they will be asked in the order in which they are registered.
|
||||||
|
* @param linkProvider The link provider to use to detect links.
|
||||||
|
*/
|
||||||
|
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (EXPERIMENTAL) Registers a character joiner, allowing custom sequences of
|
* (EXPERIMENTAL) Registers a character joiner, allowing custom sequences of
|
||||||
* characters to be rendered as a single unit. This is useful in particular
|
* characters to be rendered as a single unit. This is useful in particular
|
||||||
@@ -1073,6 +1085,85 @@ declare module 'xterm' {
|
|||||||
y: number;
|
y: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom link provider.
|
||||||
|
*/
|
||||||
|
interface ILinkProvider {
|
||||||
|
/**
|
||||||
|
* Provides a link a buffer position
|
||||||
|
* @param position The position of the buffer that is currently active.
|
||||||
|
* @param callback The callback to be fired with the resulting link or
|
||||||
|
* `undefined` when ready.
|
||||||
|
*/
|
||||||
|
provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A link within the terminal.
|
||||||
|
*/
|
||||||
|
interface ILink {
|
||||||
|
/**
|
||||||
|
* The buffer range of the link.
|
||||||
|
*/
|
||||||
|
range: IBufferRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text of the link.
|
||||||
|
*/
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls when the link is activated.
|
||||||
|
* @param event The mouse event triggering the callback.
|
||||||
|
* @param text The text of the link.
|
||||||
|
*/
|
||||||
|
activate(event: MouseEvent, text: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the mouse hovers the link.
|
||||||
|
* @param event The mouse event triggering the callback.
|
||||||
|
* @param text The text of the link.
|
||||||
|
*/
|
||||||
|
hover?(event: MouseEvent, text: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the mouse leaves the link.
|
||||||
|
* @param event The mouse event triggering the callback.
|
||||||
|
* @param text The text of the link.
|
||||||
|
*/
|
||||||
|
leave?(event: MouseEvent, text: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A range within a buffer.
|
||||||
|
*/
|
||||||
|
interface IBufferRange {
|
||||||
|
/**
|
||||||
|
* The start position of the range.
|
||||||
|
*/
|
||||||
|
start: IBufferCellPosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end position of the range.
|
||||||
|
*/
|
||||||
|
end: IBufferCellPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A position within a buffer.
|
||||||
|
*/
|
||||||
|
interface IBufferCellPosition {
|
||||||
|
/**
|
||||||
|
* The x position within the buffer.
|
||||||
|
*/
|
||||||
|
x: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The y position within the buffer.
|
||||||
|
*/
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a terminal buffer.
|
* Represents a terminal buffer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user