]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Implement link matcher priorities
authorDaniel Imms <daimms@microsoft.com>
Thu, 2 Mar 2017 19:25:27 +0000 (11:25 -0800)
committerDaniel Imms <daimms@microsoft.com>
Thu, 2 Mar 2017 19:25:27 +0000 (11:25 -0800)
Fixes #549

src/Interfaces.ts
src/Linkifier.test.ts
src/Linkifier.ts
src/Types.ts

index d2f8e8726d00313e236df7bf67e6d419f4f34909..ca228ce01bdfb99e8218a887c71a1de3b61be016 100644 (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;
 }
 
 /**
index b1c1541df1ad7d7a5e08ce31ca2ad00a411c69da..012b1b6869b2cc4f9b7729f5f07779c1a681fd9a 100644 (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]);
+    });
+  });
 });
index 0b144cfda0b0cbacfeb2906b5c74214bc85e4633..e1b52cac317d368f1caaa9a1f6226e3f869400f2 100644 (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,39 @@ 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;
+      }
+    }
+
+    if (matcher.priority > this._linkMatchers[0].priority) {
+      this._linkMatchers.splice(0, 0, matcher);
+    } else {
+      this._linkMatchers.push(matcher);
+    }
+  }
+
   /**
    * Deregisters a link matcher if it has been registered.
    * @param {number} matcherId The link matcher's ID (returned after register)
@@ -137,7 +157,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);
index 038a1f7a2cf444c72a9d7f2cf99c6ba3804788a0..2dbb47effb9080298adfc706ba3c334a5d696530 100644 (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;