diff --git a/src/Linkifier.test.ts b/src/Linkifier.test.ts index 6b28acb9..f165590f 100644 --- a/src/Linkifier.test.ts +++ b/src/Linkifier.test.ts @@ -11,12 +11,13 @@ import { MockBuffer } from './utils/TestUtils.test'; import { CircularList } from './utils/CircularList'; class TestLinkifier extends Linkifier { - constructor(bufferAccessor: IBufferAccessor) { + constructor(private _bufferAccessor: IBufferAccessor) { + super(_bufferAccessor); Linkifier.TIME_BEFORE_LINKIFY = 0; - super(bufferAccessor); } public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; } + public linkifyRows(): void { super.linkifyRows(0, this._bufferAccessor.buffer.lines.length - 1); } } class TestMouseZoneManager implements IMouseZoneManager { @@ -57,7 +58,7 @@ describe('Linkifier', () => { function assertLinkifiesEntireRow(uri: string, done: MochaDone): void { addRow(uri); - linkifier.linkifyRow(0); + linkifier.linkifyRows(); setTimeout(() => { assert.equal(mouseZoneManager.zones[0].x1, 1); assert.equal(mouseZoneManager.zones[0].x2, uri.length + 1); @@ -69,7 +70,7 @@ describe('Linkifier', () => { function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, links: {x: number, length: number}[], done: MochaDone): void { addRow(rowText); linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); - linkifier.linkifyRow(0); + linkifier.linkifyRows(); // Allow linkify to happen setTimeout(() => { assert.equal(mouseZoneManager.zones.length, links.length); @@ -148,7 +149,7 @@ describe('Linkifier', () => { mouseZoneManager.zones[0].clickCallback({}); } }); - linkifier.linkifyRow(0); + linkifier.linkifyRows(); }); it('should disable link if false', done => { @@ -160,7 +161,7 @@ describe('Linkifier', () => { assert.equal(mouseZoneManager.zones.length, 0); } }); - linkifier.linkifyRow(0); + linkifier.linkifyRows(); // Allow time for the validation callback to be performed setTimeout(() => done(), 10); }); @@ -177,7 +178,7 @@ describe('Linkifier', () => { cb(false); } }); - linkifier.linkifyRow(0); + linkifier.linkifyRows(); }); }); diff --git a/src/Linkifier.ts b/src/Linkifier.ts index cae0d662..d3150b4f 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -44,30 +44,31 @@ export class Linkifier { */ protected static TIME_BEFORE_LINKIFY = 200; - protected _linkMatchers: LinkMatcher[]; + protected _linkMatchers: LinkMatcher[] = []; private _mouseZoneManager: IMouseZoneManager; - private _rowTimeoutIds: number[]; private _rowsTimeoutId: number; private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID; constructor( private _terminal: IBufferAccessor ) { - this._rowTimeoutIds = []; - this._linkMatchers = []; this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 }); } /** * Attaches the linkifier to the DOM, enabling linkification. - * @param document The document object. - * @param rows The array of rows to apply links to. + * @param mouseZoneManager The mouse zone manager to register link zones with. */ public attachToDom(mouseZoneManager: IMouseZoneManager): void { this._mouseZoneManager = mouseZoneManager; } + /** + * Queue linkification on a set of rows. + * @param start The row to linkify from (inclusive). + * @param end The row to linkify to (inclusive). + */ public linkifyRows(start: number, end: number): void { // Don't attempt linkify if not yet attached to DOM if (!this._mouseZoneManager) { @@ -84,6 +85,11 @@ export class Linkifier { this._rowsTimeoutId = setTimeout(this._linkifyRows.bind(this, start, end), Linkifier.TIME_BEFORE_LINKIFY); } + /** + * Linkifies + * @param start The row to start at. + * @param end The row to end at. + */ private _linkifyRows(start: number, end: number): void { for (let i = start; i <= end; i++) { this._linkifyRow(i); @@ -91,27 +97,9 @@ export class Linkifier { } /** - * Queues a row for linkification. - * @param {number} rowIndex The index of the row to linkify. - */ - public linkifyRow(rowIndex: number): void { - // Don't attempt linkify if not yet attached to DOM - if (!this._mouseZoneManager) { - return; - } - - const timeoutId = this._rowTimeoutIds[rowIndex]; - if (timeoutId) { - clearTimeout(timeoutId); - } - this._rowTimeoutIds[rowIndex] = setTimeout(this._linkifyRow.bind(this, rowIndex), Linkifier.TIME_BEFORE_LINKIFY); - } - - /** - * Attaches a handler for hypertext links, overriding default behavior - * for standard http(s) links. - * @param {LinkHandler} handler The handler to use, this can be cleared with - * null. + * Attaches a handler for hypertext links, overriding default behavior for + * tandard http(s) links. + * @param handler The handler to use, this can be cleared with null. */ public setHypertextLinkHandler(handler: LinkMatcherHandler): void { this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler; @@ -119,8 +107,7 @@ export class Linkifier { /** * Attaches a validation callback for hypertext links. - * @param {LinkMatcherValidationCallback} callback The callback to use, this - * can be cleared with null. + * @param callback The callback to use, this can be cleared with null. */ public setHypertextValidationCallback(callback: LinkMatcherValidationCallback): void { this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].validationCallback = callback; @@ -129,12 +116,12 @@ export class Linkifier { /** * Registers a link matcher, allowing custom link patterns to be matched and * handled. - * @param {RegExp} regex The regular expression to search for, specifically - * this searches the textContent of the rows. You will want to use \s to match - * a space ' ' character for example. - * @param {LinkHandler} handler The callback when the link is called. - * @param {ILinkMatcherOptions} [options] Options for the link matcher. - * @return {number} The ID of the new matcher, this can be used to deregister. + * @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 + * space ' ' character for example. + * @param handler The callback when the link is called. + * @param options Options for the link matcher. + * @return The ID of the new matcher, this can be used to deregister. */ public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: ILinkMatcherOptions = {}): number { if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) { @@ -177,8 +164,8 @@ export class Linkifier { /** * Deregisters a link matcher if it has been registered. - * @param {number} matcherId The link matcher's ID (returned after register) - * @return {boolean} Whether a link matcher was found and deregistered. + * @param matcherId The link matcher's ID (returned after register) + * @return Whether a link matcher was found and deregistered. */ public deregisterLinkMatcher(matcherId: number): boolean { // ID 0 is the hypertext link matcher which cannot be deregistered @@ -193,7 +180,7 @@ export class Linkifier { /** * Linkifies a row. - * @param {number} rowIndex The index of the row to linkify. + * @param rowIndex The index of the row to linkify. */ private _linkifyRow(rowIndex: number): void { const absoluteRowIndex = this._terminal.buffer.ydisp + rowIndex; @@ -209,8 +196,10 @@ export class Linkifier { /** * Linkifies a row given a specific handler. * @param rowIndex The row index to linkify. - * @param text The text of the row. - * @param {LinkMatcher} matcher The link matcher for this line. + * @param text The text of the row (excludes text in the row that's already + * linkified). + * @param matcher The link matcher for this line. + * @param offset The how much of the row has already been linkified. * @return The link element(s) that were added. */ private _doLinkifyRow(rowIndex: number, text: string, matcher: LinkMatcher, offset: number = 0): void { @@ -248,6 +237,13 @@ export class Linkifier { } } + /** + * Registers a link to the mouse zone manager. + * @param x The column the link starts. + * @param y The row the link is on. + * @param uri The URI of the link. + * @param matcher The link matcher for the link. + */ private _addLink(x: number, y: number, uri: string, matcher: LinkMatcher): void { this._mouseZoneManager.add(new MouseZone( x + 1, @@ -257,7 +253,7 @@ export class Linkifier { if (matcher.handler) { return matcher.handler(e, uri); } - window.open(uri, '_blink'); + window.open(uri, '_blank'); }, e => { if (matcher.hoverCallback) {