Support multiline links in the WebLinksAddon

This commit is contained in:
Jon Bockhorst
2019-11-05 23:35:20 -06:00
parent b3d95d20dc
commit ac6c8a5bd3
3 changed files with 70 additions and 14 deletions
@@ -1,3 +1,8 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ILinkProvider, IBufferCellPosition, ILink, Terminal, IBuffer } from 'xterm'; import { ILinkProvider, IBufferCellPosition, ILink, Terminal, IBuffer } from 'xterm';
export class WebLinkProvider implements ILinkProvider { export class WebLinkProvider implements ILinkProvider {
@@ -11,20 +16,15 @@ export class WebLinkProvider implements ILinkProvider {
} }
provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void { provideLink(position: IBufferCellPosition, callback: (link: ILink | undefined) => void): void {
callback(LinkComputer.computeLink(position, this._regex, this._terminal.buffer, this._handler)); callback(LinkComputer.computeLink(position, this._regex, this._terminal, this._handler));
} }
} }
export class LinkComputer { export class LinkComputer {
public static computeLink(position: IBufferCellPosition, regex: RegExp, buffer: IBuffer, handle: (event: MouseEvent, uri: string) => void): ILink | undefined { public static computeLink(position: IBufferCellPosition, regex: RegExp, terminal: Terminal, handle: (event: MouseEvent, uri: string) => void): ILink | undefined {
const rex = new RegExp(regex.source, (regex.flags || '') + 'g'); const rex = new RegExp(regex.source, (regex.flags || '') + 'g');
const bufferLine = buffer.getLine(position.y - 1);
if (!bufferLine) { const [line, startLineIndex] = LinkComputer._translateBufferLineToStringWithWrap(position.y - 1, false, terminal);
return;
}
const line = bufferLine.translateToString();
let match; let match;
let stringIndex = -1; let stringIndex = -1;
@@ -49,18 +49,66 @@ export class LinkComputer {
break; break;
} }
let endX = stringIndex + url.length + 1;
let endY = startLineIndex + 1;
while (endX > terminal.cols) {
endX -= terminal.cols;
endY++;
}
const range = { const range = {
start: { start: {
x: stringIndex + 1, x: stringIndex + 1,
y: position.y y: startLineIndex + 1
}, },
end: { end: {
x: stringIndex + url.length + 1, x: endX,
y: position.y y: endY
} }
}; };
return { range, url, handle }; return { range, url, handle };
} }
} }
/**
* Gets the entire line for the buffer line
* @param line The line being translated.
* @param trimRight Whether to trim whitespace to the right.
* @param terminal The terminal
*/
private static _translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean, terminal: Terminal): [string, number] {
let lineString = '';
let lineWrapsToNext: boolean;
let prevLinesToWrap: boolean;
do {
const line = terminal.buffer.getLine(lineIndex);
if (!line) {
break;
}
if (line.isWrapped) {
lineIndex--;
}
prevLinesToWrap = line.isWrapped;
} while (prevLinesToWrap);
const startLineIndex = lineIndex;
do {
const nextLine = terminal.buffer.getLine(lineIndex + 1);
lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
const line = terminal.buffer.getLine(lineIndex);
if (!line) {
break;
}
lineString += line.translateToString(!lineWrapsToNext && trimRight).substring(0, terminal.cols);
lineIndex++;
} while (lineWrapsToNext);
return [lineString, startLineIndex];
}
} }
@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2017 The xterm.js authors. All rights reserved. * Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT * @license MIT
*/ */
+10 -2
View File
@@ -8,6 +8,7 @@ import { IDisposable } from 'common/Types';
import { IMouseService } from './services/Services'; import { IMouseService } from './services/Services';
import { IBufferService, ICoreService } from 'common/services/Services'; import { IBufferService, ICoreService } from 'common/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter'; import { EventEmitter, IEvent } from 'common/EventEmitter';
import { LinkRenderLayer } from './renderer/LinkRenderLayer';
export class Linkifier2 implements ILinkifier2 { export class Linkifier2 implements ILinkifier2 {
private _element: HTMLElement | undefined; private _element: HTMLElement | undefined;
@@ -206,9 +207,16 @@ export class Linkifier2 implements ILinkifier2 {
* @param position * @param position
*/ */
private _linkAtPosition(link: ILink, position: IBufferCellPosition): boolean { private _linkAtPosition(link: ILink, position: IBufferCellPosition): boolean {
return link.range.start.x <= position.x && const sameLine = link.range.start.y === link.range.end.y;
const wrappedFromLeft = link.range.start.y < position.y;
const wrappedToRight = link.range.end.y > position.y;
// If the start and end have the same y, then the position must be between start and end x
// If not, then handle each case seperately, depending on which way it wraps
return ((sameLine && link.range.start.x <= position.x && link.range.end.x >= position.x) ||
(wrappedFromLeft && link.range.end.x >= position.x) ||
(wrappedToRight && link.range.start.x <= position.x)) &&
link.range.start.y <= position.y && link.range.start.y <= position.y &&
link.range.end.x >= position.x &&
link.range.end.y >= position.y; link.range.end.y >= position.y;
} }