Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ export interface LinkMatcherOptions {
* false if invalid.
*/
validationCallback?: LinkMatcherValidationCallback;
/**
* 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
* default value is 0.
*/
priority?: number;
}

/**
Expand Down
27 changes: 25 additions & 2 deletions src/Linkifier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import jsdom = require('jsdom');
import { assert } from 'chai';
import { ITerminal, ILinkifier } from './Interfaces';
import { Linkifier } from './Linkifier';
import { LinkMatcher } from './Types';

class TestLinkifier extends Linkifier {
constructor(document: Document, rows: HTMLElement[]) {
Linkifier.TIME_BEFORE_LINKIFY = 0;
super(document, rows);
}

public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
}

describe('Linkifier', () => {
Expand All @@ -19,14 +22,14 @@ describe('Linkifier', () => {

let container: HTMLElement;
let rows: HTMLElement[];
let linkifier: ILinkifier;
let linkifier: TestLinkifier;

beforeEach(done => {
rows = [];
jsdom.env('', (err, w) => {
window = w;
document = window.document;
linkifier = new Linkifier(document, rows);
linkifier = new TestLinkifier(document, rows);
container = document.createElement('div');
document.body.appendChild(container);
done();
Expand Down Expand Up @@ -73,4 +76,24 @@ describe('Linkifier', () => {
setTimeout(() => done(), 10);
});
});

describe('priority', () => {
it('should order the list from highest priority to lowest #1', () => {
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 });
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 });
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, 0, bId]);
});

it('should order the list from highest priority to lowest #2', () => {
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 });
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 });
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, 0, aId]);
});

it('should order items of equal priority in the order they are added', () => {
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 });
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 });
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [0, aId, bId]);
});
});
});
41 changes: 28 additions & 13 deletions src/Linkifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
*/

import { LinkMatcherOptions } from './Interfaces';
import { LinkMatcherHandler, LinkMatcherValidationCallback } from './Types';

type LinkMatcher = {
id: number,
regex: RegExp,
handler: LinkMatcherHandler,
matchIndex?: number,
validationCallback?: LinkMatcherValidationCallback;
};
import { LinkMatcher, LinkMatcherHandler, LinkMatcherValidationCallback } from './Types';

const INVALID_LINK_CLASS = 'xterm-invalid-link';

Expand Down Expand Up @@ -50,10 +42,11 @@ export class Linkifier {
*/
protected static TIME_BEFORE_LINKIFY = 200;

protected _linkMatchers: LinkMatcher[];

private _document: Document;
private _rows: HTMLElement[];
private _rowTimeoutIds: number[];
private _linkMatchers: LinkMatcher[];
private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;

constructor(document: Document, rows: HTMLElement[]) {
Expand Down Expand Up @@ -105,12 +98,35 @@ export class Linkifier {
regex,
handler,
matchIndex: options.matchIndex,
validationCallback: options.validationCallback
validationCallback: options.validationCallback,
priority: options.priority || 0
};
this._linkMatchers.push(matcher);
this._addLinkMatcherToList(matcher);
return matcher.id;
}

/**
* Inserts a link matcher to the list in the correct position based on the
* priority of each link matcher. New link matchers of equal priority are
* considered after older link matchers.
* @param matcher The link matcher to be added.
*/
private _addLinkMatcherToList(matcher: LinkMatcher): void {
if (this._linkMatchers.length === 0) {
this._linkMatchers.push(matcher);
return;
}

for (let i = this._linkMatchers.length - 1; i >= 0; i--) {
if (matcher.priority <= this._linkMatchers[i].priority) {
this._linkMatchers.splice(i + 1, 0, matcher);
return;
}
}

this._linkMatchers.splice(0, 0, matcher);
}

/**
* Deregisters a link matcher if it has been registered.
* @param {number} matcherId The link matcher's ID (returned after register)
Expand All @@ -137,7 +153,6 @@ export class Linkifier {
return;
}
const text = row.textContent;
// TODO: Onl execute handler if isValid
for (let i = 0; i < this._linkMatchers.length; i++) {
const matcher = this._linkMatchers[i];
const uri = this._findLinkMatch(text, matcher.regex, matcher.matchIndex);
Expand Down
8 changes: 8 additions & 0 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,13 @@
* @license MIT
*/

export type LinkMatcher = {
id: number,
regex: RegExp,
handler: LinkMatcherHandler,
matchIndex?: number,
validationCallback?: LinkMatcherValidationCallback,
priority?: number
};
export type LinkMatcherHandler = (uri: string) => void;
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;