Merge pull request #577 from Tyriar/549_link_priority

Implement link matcher priorities
This commit is contained in:
Daniel Imms
2017-03-02 15:24:09 -08:00
committed by GitHub
4 changed files with 67 additions and 15 deletions
+6
View File
@@ -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;
}
/**
+25 -2
View File
@@ -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', () => {
@@ -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();
@@ -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]);
});
});
});
+28 -13
View File
@@ -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';
@@ -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[]) {
@@ -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)
@@ -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);
+8
View File
@@ -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;