mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Support multiple links in a line, fix Linkifier tests
This commit is contained in:
+1
-1
@@ -114,7 +114,7 @@ export class Buffer implements IBuffer {
|
||||
if (this._lines.length > 0) {
|
||||
// Deal with columns increasing (we don't do anything when columns reduce)
|
||||
if (this._terminal.cols < newCols) {
|
||||
const ch: CharData = [this._terminal.defAttr, ' ', 1]; // does xterm use the default attr?
|
||||
const ch: CharData = [this._terminal.defAttr, ' ', 1, 32]; // does xterm use the default attr?
|
||||
for (let i = 0; i < this._lines.length; i++) {
|
||||
// TODO: This should be removed, with tests setup for the case that was
|
||||
// causing the underlying bug, see https://github.com/sourcelair/xterm.js/issues/824
|
||||
|
||||
+4
-4
@@ -189,7 +189,7 @@ export class InputHandler implements IInputHandler {
|
||||
|
||||
const row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
let j = this._terminal.buffer.x;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm
|
||||
|
||||
while (param-- && j < this._terminal.cols) {
|
||||
this._terminal.buffer.lines.get(row).splice(j++, 0, ch);
|
||||
@@ -488,7 +488,7 @@ export class InputHandler implements IInputHandler {
|
||||
}
|
||||
|
||||
const row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm
|
||||
|
||||
while (param--) {
|
||||
this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 1);
|
||||
@@ -536,7 +536,7 @@ export class InputHandler implements IInputHandler {
|
||||
|
||||
const row = this._terminal.buffer.y + this._terminal.buffer.ybase;
|
||||
let j = this._terminal.buffer.x;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1]; // xterm
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm
|
||||
|
||||
while (param-- && j < this._terminal.cols) {
|
||||
this._terminal.buffer.lines.get(row)[j++] = ch;
|
||||
@@ -590,7 +590,7 @@ export class InputHandler implements IInputHandler {
|
||||
public repeatPrecedingCharacter(params: number[]): void {
|
||||
let param = params[0] || 1;
|
||||
const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y);
|
||||
const ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1];
|
||||
const ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1, 32];
|
||||
|
||||
while (param--) {
|
||||
line[this._terminal.buffer.x++] = ch;
|
||||
|
||||
+5
-2
@@ -18,7 +18,11 @@ export interface IBrowser {
|
||||
isMSWindows: boolean;
|
||||
}
|
||||
|
||||
export interface ITerminal extends IEventEmitter {
|
||||
export interface IBufferAccessor {
|
||||
buffer: IBuffer;
|
||||
}
|
||||
|
||||
export interface ITerminal extends IBufferAccessor, IEventEmitter {
|
||||
element: HTMLElement;
|
||||
selectionManager: ISelectionManager;
|
||||
charMeasure: ICharMeasure;
|
||||
@@ -32,7 +36,6 @@ export interface ITerminal extends IEventEmitter {
|
||||
defAttr: number;
|
||||
options: ITerminalOptions;
|
||||
buffers: IBufferSet;
|
||||
buffer: IBuffer;
|
||||
isFocused: boolean;
|
||||
|
||||
/**
|
||||
|
||||
+87
-76
@@ -2,42 +2,84 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import jsdom = require('jsdom');
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal, ILinkifier } from './Interfaces';
|
||||
import { ITerminal, ILinkifier, IBuffer, IBufferAccessor } from './Interfaces';
|
||||
import { Linkifier } from './Linkifier';
|
||||
import { LinkMatcher } from './Types';
|
||||
import { LinkMatcher, LineData } from './Types';
|
||||
import { IMouseZoneManager, IMouseZone } from './input/Interfaces';
|
||||
import { MockBuffer } from './utils/TestUtils.test';
|
||||
import { CircularList } from './utils/CircularList';
|
||||
|
||||
class TestLinkifier extends Linkifier {
|
||||
constructor() {
|
||||
constructor(bufferAccessor: IBufferAccessor) {
|
||||
Linkifier.TIME_BEFORE_LINKIFY = 0;
|
||||
super();
|
||||
super(bufferAccessor);
|
||||
}
|
||||
|
||||
public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
|
||||
}
|
||||
|
||||
describe('Linkifier', () => {
|
||||
let dom: jsdom.JSDOM;
|
||||
let window: Window;
|
||||
let document: Document;
|
||||
class TestMouseZoneManager implements IMouseZoneManager {
|
||||
public clears: number = 0;
|
||||
public zones: IMouseZone[] = [];
|
||||
add(zone: IMouseZone): void {
|
||||
this.zones.push(zone);
|
||||
}
|
||||
clearAll(): void {
|
||||
this.clears++;
|
||||
}
|
||||
}
|
||||
|
||||
let container: HTMLElement;
|
||||
let rows: HTMLElement[];
|
||||
describe('Linkifier', () => {
|
||||
let bufferAccessor: IBufferAccessor;
|
||||
let linkifier: TestLinkifier;
|
||||
let mouseZoneManager: TestMouseZoneManager;
|
||||
|
||||
beforeEach(() => {
|
||||
dom = new jsdom.JSDOM('');
|
||||
window = dom.window;
|
||||
document = window.document;
|
||||
linkifier = new TestLinkifier();
|
||||
bufferAccessor = { buffer: new MockBuffer() };
|
||||
bufferAccessor.buffer.lines = new CircularList<LineData>(20);
|
||||
bufferAccessor.buffer.ydisp = 0;
|
||||
linkifier = new TestLinkifier(bufferAccessor);
|
||||
mouseZoneManager = new TestMouseZoneManager();
|
||||
});
|
||||
|
||||
function addRow(html: string): void {
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = html;
|
||||
container.appendChild(element);
|
||||
rows.push(element);
|
||||
function stringToRow(text: string): LineData {
|
||||
let result: LineData = [];
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
result.push([0, text.charAt(i), 1, text.charCodeAt(i)]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function addRow(text: string): void {
|
||||
bufferAccessor.buffer.lines.push(stringToRow(text));
|
||||
}
|
||||
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone): void {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
assert.equal(mouseZoneManager.zones[0].x1, 1);
|
||||
assert.equal(mouseZoneManager.zones[0].x2, uri.length + 1);
|
||||
assert.equal(mouseZoneManager.zones[0].y, bufferAccessor.buffer.lines.length);
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, links: {x: number, length: number}[], done: MochaDone): void {
|
||||
addRow(rowText);
|
||||
linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
|
||||
linkifier.linkifyRow(0);
|
||||
// Allow linkify to happen
|
||||
setTimeout(() => {
|
||||
assert.equal(mouseZoneManager.zones.length, links.length);
|
||||
links.forEach((l, i) => {
|
||||
assert.equal(mouseZoneManager.zones[i].x1, l.x + 1);
|
||||
assert.equal(mouseZoneManager.zones[i].x2, l.x + l.length + 1);
|
||||
assert.equal(mouseZoneManager.zones[i].y, bufferAccessor.buffer.lines.length);
|
||||
});
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
describe('before attachToDom', () => {
|
||||
@@ -52,78 +94,42 @@ describe('Linkifier', () => {
|
||||
|
||||
describe('after attachToDom', () => {
|
||||
beforeEach(() => {
|
||||
rows = [];
|
||||
linkifier.attachToDom(document, rows);
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
linkifier.attachToDom(mouseZoneManager);
|
||||
});
|
||||
|
||||
function clickElement(element: Node): void {
|
||||
const event = document.createEvent('MouseEvent');
|
||||
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone): void {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
describe('http links', () => {
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone): void {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
it('should allow ~ character in URI path', done => assertLinkifiesEntireRow('http://foo.com/a~b#c~d?e~f', done));
|
||||
it('should allow ~ character in URI path', (done) => {
|
||||
assertLinkifiesEntireRow('http://foo.com/a~b#c~d?e~f', done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('link matcher', () => {
|
||||
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, expectedHtml: string, done: MochaDone): void {
|
||||
addRow(rowText);
|
||||
linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
|
||||
linkifier.linkifyRow(0);
|
||||
// Allow linkify to happen
|
||||
setTimeout(() => {
|
||||
assert.equal(rows[0].innerHTML, expectedHtml);
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
it('should match a single link', done => {
|
||||
assertLinkifiesRow('foo', /foo/, '<a>foo</a>', done);
|
||||
assertLinkifiesRow('foo', /foo/, [{x: 0, length: 3}], done);
|
||||
});
|
||||
it('should match a single link at the start of a text node', done => {
|
||||
assertLinkifiesRow('foo bar', /foo/, '<a>foo</a> bar', done);
|
||||
assertLinkifiesRow('foo bar', /foo/, [{x: 0, length: 3}], done);
|
||||
});
|
||||
it('should match a single link in the middle of a text node', done => {
|
||||
assertLinkifiesRow('foo bar baz', /bar/, 'foo <a>bar</a> baz', done);
|
||||
assertLinkifiesRow('foo bar baz', /bar/, [{x: 4, length: 3}], done);
|
||||
});
|
||||
it('should match a single link at the end of a text node', done => {
|
||||
assertLinkifiesRow('foo bar', /bar/, 'foo <a>bar</a>', done);
|
||||
assertLinkifiesRow('foo bar', /bar/, [{x: 4, length: 3}], done);
|
||||
});
|
||||
it('should match a link after a link at the start of a text node', done => {
|
||||
assertLinkifiesRow('foo bar', /foo|bar/, '<a>foo</a> <a>bar</a>', done);
|
||||
assertLinkifiesRow('foo bar', /foo|bar/, [{x: 0, length: 3}, {x: 4, length: 3}], done);
|
||||
});
|
||||
it('should match a link after a link in the middle of a text node', done => {
|
||||
assertLinkifiesRow('foo bar baz', /bar|baz/, 'foo <a>bar</a> <a>baz</a>', done);
|
||||
assertLinkifiesRow('foo bar baz', /bar|baz/, [{x: 4, length: 3}, {x: 8, length: 3}], done);
|
||||
});
|
||||
it('should match a link immediately after a link at the end of a text node', done => {
|
||||
assertLinkifiesRow('<span>foo bar</span>baz', /bar|baz/, '<span>foo <a>bar</a></span><a>baz</a>', done);
|
||||
assertLinkifiesRow('foo barbaz', /bar|baz/, [{x: 4, length: 3}, {x: 7, length: 3}], done);
|
||||
});
|
||||
it('should not duplicate text after a unicode character (wrapped in a span)', done => {
|
||||
// This is a regression test for an issue that came about when using
|
||||
// an oh-my-zsh theme that added the large blue diamond unicode
|
||||
// character (U+1F537) which caused the path to be duplicated. See #642.
|
||||
assertLinkifiesRow('echo \'<span class="xterm-normal-char">🔷</span>foo\'', /foo/, 'echo \'<span class="xterm-normal-char">🔷</span><a>foo</a>\'', done);
|
||||
assertLinkifiesRow('echo \'🔷foo\'', /foo/, [{x: 8, length: 3}], done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,10 +137,15 @@ describe('Linkifier', () => {
|
||||
it('should enable link if true', done => {
|
||||
addRow('test');
|
||||
linkifier.registerLinkMatcher(/test/, () => done(), {
|
||||
validationCallback: (url, element, cb) => {
|
||||
validationCallback: (url, cb) => {
|
||||
assert.equal(mouseZoneManager.zones.length, 0);
|
||||
cb(true);
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
setTimeout(() => clickElement(rows[0].firstChild), 0);
|
||||
assert.equal(mouseZoneManager.zones.length, 1);
|
||||
assert.equal(mouseZoneManager.zones[0].x1, 1);
|
||||
assert.equal(mouseZoneManager.zones[0].x2, 5);
|
||||
assert.equal(mouseZoneManager.zones[0].y, 1);
|
||||
// Fires done()
|
||||
mouseZoneManager.zones[0].clickCallback(<any>{});
|
||||
}
|
||||
});
|
||||
linkifier.linkifyRow(0);
|
||||
@@ -143,14 +154,14 @@ describe('Linkifier', () => {
|
||||
it('should disable link if false', done => {
|
||||
addRow('test');
|
||||
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
|
||||
validationCallback: (url, element, cb) => {
|
||||
validationCallback: (url, cb) => {
|
||||
assert.equal(mouseZoneManager.zones.length, 0);
|
||||
cb(false);
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
setTimeout(() => clickElement(rows[0].firstChild), 0);
|
||||
assert.equal(mouseZoneManager.zones.length, 0);
|
||||
}
|
||||
});
|
||||
linkifier.linkifyRow(0);
|
||||
// Allow time for the click to be performed
|
||||
// Allow time for the validation callback to be performed
|
||||
setTimeout(() => done(), 10);
|
||||
});
|
||||
|
||||
@@ -158,7 +169,7 @@ describe('Linkifier', () => {
|
||||
addRow('test test');
|
||||
let count = 0;
|
||||
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
|
||||
validationCallback: (url, element, cb) => {
|
||||
validationCallback: (url, cb) => {
|
||||
count += 1;
|
||||
if (count === 2) {
|
||||
done();
|
||||
|
||||
+12
-9
@@ -2,7 +2,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ILinkMatcherOptions, ITerminal } from './Interfaces';
|
||||
import { ILinkMatcherOptions, ITerminal, IBufferAccessor } from './Interfaces';
|
||||
import { LinkMatcher, LinkMatcherHandler, LinkMatcherValidationCallback, LineData } from './Types';
|
||||
import { IMouseZoneManager } from './input/Interfaces';
|
||||
import { MouseZone } from './input/MouseZoneManager';
|
||||
@@ -52,7 +52,7 @@ export class Linkifier {
|
||||
private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;
|
||||
|
||||
constructor(
|
||||
private _terminal: ITerminal
|
||||
private _terminal: IBufferAccessor
|
||||
) {
|
||||
this._rowTimeoutIds = [];
|
||||
this._linkMatchers = [];
|
||||
@@ -213,7 +213,7 @@ export class Linkifier {
|
||||
* @param {LinkMatcher} matcher The link matcher for this line.
|
||||
* @return The link element(s) that were added.
|
||||
*/
|
||||
private _doLinkifyRow(rowIndex: number, text: string, matcher: LinkMatcher): void {
|
||||
private _doLinkifyRow(rowIndex: number, text: string, matcher: LinkMatcher, offset: number = 0): void {
|
||||
// Iterate over nodes as we want to consider text nodes
|
||||
let result = [];
|
||||
const isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID;
|
||||
@@ -225,10 +225,6 @@ export class Linkifier {
|
||||
}
|
||||
let uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];
|
||||
|
||||
// TODO: Match more than one link per row
|
||||
// Set the next searches start index
|
||||
// let rowStartIndex = match.index + uri.length;
|
||||
|
||||
// Get index, match.index is for the outer match which includes negated chars
|
||||
const index = text.indexOf(uri);
|
||||
|
||||
@@ -237,11 +233,18 @@ export class Linkifier {
|
||||
matcher.validationCallback(text, isValid => {
|
||||
if (isValid) {
|
||||
// TODO: Discard link if the line has already changed?
|
||||
this._addLink(index, rowIndex, uri, matcher);
|
||||
this._addLink(offset + index, rowIndex, uri, matcher);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._addLink(index, rowIndex, uri, matcher);
|
||||
this._addLink(offset + index, rowIndex, uri, matcher);
|
||||
}
|
||||
|
||||
// Recursively check for links in the rest of the text
|
||||
const remainingStartIndex = index + uri.length;
|
||||
const remainingText = text.substr(remainingStartIndex);
|
||||
if (remainingText.length > 0) {
|
||||
this._doLinkifyRow(rowIndex, remainingText, matcher, offset + remainingStartIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('SelectionManager', () => {
|
||||
function stringToRow(text: string): LineData {
|
||||
let result: LineData = [];
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
result.push([0, text.charAt(i), 1]);
|
||||
result.push([0, text.charAt(i), 1, text.charCodeAt(i)]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -99,21 +99,21 @@ describe('SelectionManager', () => {
|
||||
it('should expand selection for wide characters', () => {
|
||||
// Wide characters use a special format
|
||||
buffer.lines.set(0, [
|
||||
[null, '中', 2],
|
||||
[null, '', 0],
|
||||
[null, '文', 2],
|
||||
[null, '', 0],
|
||||
[null, ' ', 1],
|
||||
[null, 'a', 1],
|
||||
[null, '中', 2],
|
||||
[null, '', 0],
|
||||
[null, '文', 2],
|
||||
[null, '', 0],
|
||||
[null, 'b', 1],
|
||||
[null, ' ', 1],
|
||||
[null, 'f', 1],
|
||||
[null, 'o', 1],
|
||||
[null, 'o', 1]
|
||||
[null, '中', 2, '中'.charCodeAt(0)],
|
||||
[null, '', 0, null],
|
||||
[null, '文', 2, '文'.charCodeAt(0)],
|
||||
[null, '', 0, null],
|
||||
[null, ' ', 1, ' '.charCodeAt(0)],
|
||||
[null, 'a', 1, 'a'.charCodeAt(0)],
|
||||
[null, '中', 2, '中'.charCodeAt(0)],
|
||||
[null, '', 0, null],
|
||||
[null, '文', 2, '文'.charCodeAt(0)],
|
||||
[null, '', 0, ''.charCodeAt(0)],
|
||||
[null, 'b', 1, 'b'.charCodeAt(0)],
|
||||
[null, ' ', 1, ' '.charCodeAt(0)],
|
||||
[null, 'f', 1, 'f'.charCodeAt(0)],
|
||||
[null, 'o', 1, 'o'.charCodeAt(0)],
|
||||
[null, 'o', 1, 'o'.charCodeAt(0)]
|
||||
]);
|
||||
// Ensure wide characters take up 2 columns
|
||||
selectionManager.selectWordAt([0, 0]);
|
||||
|
||||
+1
-2
@@ -17,6 +17,5 @@ export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: bo
|
||||
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
|
||||
export type Charset = {[key: string]: string};
|
||||
|
||||
// TODO: Add code here?
|
||||
export type CharData = [number, string, number];
|
||||
export type CharData = [number, string, number, number];
|
||||
export type LineData = CharData[];
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { ITerminal, IBuffer, IBufferSet, IBrowser, ICharMeasure, ISelectionManager, ITerminalOptions, IListenerType, IInputHandlingTerminal, IViewport, ICircularList, ICompositionHelper } from '../Interfaces';
|
||||
import { LineData } from '../Types';
|
||||
import { Buffer } from '../Buffer';
|
||||
import * as Browser from './Browser';
|
||||
import { IColorSet } from '../renderer/Interfaces';
|
||||
|
||||
@@ -61,7 +62,7 @@ export class MockTerminal implements ITerminal {
|
||||
const line: LineData = [];
|
||||
cols = cols || this.cols;
|
||||
for (let i = 0; i < cols; i++) {
|
||||
line.push([0, ' ', 1]);
|
||||
line.push([0, ' ', 1, 32]);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
@@ -130,7 +131,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
||||
eraseLeft(x: number, y: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
blankLine(cur?: boolean, isWrapped?: boolean): [number, string, number][] {
|
||||
blankLine(cur?: boolean, isWrapped?: boolean): [number, string, number, number][] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
prevStop(x?: number): number {
|
||||
@@ -182,7 +183,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
||||
|
||||
export class MockBuffer implements IBuffer {
|
||||
isCursorInViewport: boolean;
|
||||
lines: ICircularList<[number, string, number][]>;
|
||||
lines: ICircularList<[number, string, number, number][]>;
|
||||
ydisp: number;
|
||||
ybase: number;
|
||||
y: number;
|
||||
@@ -193,7 +194,7 @@ export class MockBuffer implements IBuffer {
|
||||
savedY: number;
|
||||
savedX: number;
|
||||
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
|
||||
throw new Error('Method not implemented.');
|
||||
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
|
||||
}
|
||||
nextStop(x?: number): number {
|
||||
throw new Error('Method not implemented.');
|
||||
|
||||
Vendored
+5
@@ -133,6 +133,11 @@ interface ILinkMatcherOptions {
|
||||
*/
|
||||
validationCallback?: (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void;
|
||||
|
||||
/**
|
||||
* A callback that fired when the mouse hovers over a link.
|
||||
*/
|
||||
hoverCallback?: LinkMatcherHandler;
|
||||
|
||||
/**
|
||||
* The priority of the link matcher, this defines the order in which the link
|
||||
* matcher is evaluated relative to others, from highest to lowest. The
|
||||
|
||||
Reference in New Issue
Block a user