mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
initial rewrite of addon mechs
This commit is contained in:
@@ -45,7 +45,10 @@ export class LinkComputer {
|
||||
public static computeLink(y: number, regex: RegExp, terminal: Terminal, activate: (event: MouseEvent, uri: string) => void): ILink[] {
|
||||
const rex = new RegExp(regex.source, (regex.flags || '') + 'g');
|
||||
|
||||
const [line, startLineIndex] = LinkComputer._translateBufferLineToStringWithWrap(y - 1, false, terminal);
|
||||
const [lines, startLineIndex] = LinkComputer._getFullLineString(y - 1, terminal);
|
||||
|
||||
// TODO: do locally limited search if string is too long
|
||||
const line = lines.join('');
|
||||
|
||||
// Don't try if the wrapped line if excessively large as the regex matching will block the main
|
||||
// thread.
|
||||
@@ -58,7 +61,7 @@ export class LinkComputer {
|
||||
const result: ILink[] = [];
|
||||
|
||||
while ((match = rex.exec(line)) !== null) {
|
||||
const text = match[1];
|
||||
const text = match[0];
|
||||
if (!text) {
|
||||
// something matched but does not comply with the given matchIndex
|
||||
// since this is most likely a bug the regex itself we simply do nothing here
|
||||
@@ -77,28 +80,39 @@ export class LinkComputer {
|
||||
break;
|
||||
}
|
||||
|
||||
let endX = stringIndex + text.length;
|
||||
let endY = startLineIndex + 1;
|
||||
|
||||
while (endX > terminal.cols) {
|
||||
endX -= terminal.cols;
|
||||
endY++;
|
||||
// check via URL if the matched text would form a proper url
|
||||
// NOTE: This outsources the ugly url parsing to the browser.
|
||||
// To avoid surprising auto expansion from URL we additionally
|
||||
// check afterwards if the provided string resembles the parsed
|
||||
// one close enough:
|
||||
// - decodeURI decode path segement back to byte repr
|
||||
// to detect unicode auto conversion correctly
|
||||
// - append / also match domain urls w'o any path notion
|
||||
try {
|
||||
const url = new URL(text);
|
||||
const urlText = decodeURI(url.toString());
|
||||
if (text !== urlText && text + '/' !== urlText) {
|
||||
continue;
|
||||
}
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let startX = stringIndex + 1;
|
||||
let startY = startLineIndex + 1;
|
||||
while (startX > terminal.cols) {
|
||||
startX -= terminal.cols;
|
||||
startY++;
|
||||
|
||||
const [startY, startX] = LinkComputer._mapStringIndexToBuffer(startLineIndex, stringIndex, terminal);
|
||||
const [endY, endX] = LinkComputer._mapStringIndexToBuffer(startLineIndex, stringIndex + text.length, terminal);
|
||||
|
||||
if (startY === -1 || startX === -1 || endY === -1 || endX === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const range = {
|
||||
start: {
|
||||
x: startX,
|
||||
y: startY
|
||||
x: startX + 1,
|
||||
y: startY + 1
|
||||
},
|
||||
end: {
|
||||
x: endX,
|
||||
x: endX + 1,
|
||||
y: endY
|
||||
}
|
||||
};
|
||||
@@ -112,39 +126,66 @@ export class LinkComputer {
|
||||
/**
|
||||
* Gets the entire line for the buffer line
|
||||
* @param lineIndex The index of the line being translated.
|
||||
* @param trimRight Whether to trim whitespace to the right.
|
||||
*/
|
||||
private static _translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean, terminal: Terminal): [string, number] {
|
||||
let lineString = '';
|
||||
let lineWrapsToNext: boolean;
|
||||
let prevLinesToWrap: boolean;
|
||||
private static _getFullLineString(lineIndex: number, terminal: Terminal): [string[], number] {
|
||||
let line: any;
|
||||
|
||||
do {
|
||||
const line = terminal.buffer.active.getLine(lineIndex);
|
||||
// expand top
|
||||
let topIdx = lineIndex;
|
||||
while ((line = terminal.buffer.active.getLine(topIdx)) && line.isWrapped) {
|
||||
topIdx--;
|
||||
}
|
||||
|
||||
// expand bottom
|
||||
let bottomIdx = lineIndex + 1;
|
||||
while ((line = terminal.buffer.active.getLine(bottomIdx)) && line.isWrapped) {
|
||||
bottomIdx++;
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
for (let idx = topIdx; idx < bottomIdx; ++idx) {
|
||||
lines.push(terminal.buffer.active.getLine(idx)?.translateToString(true)!);
|
||||
}
|
||||
|
||||
return [lines, topIdx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a string index back to buffer positions.
|
||||
* Returns buffer position as [lineIndex, columnIndex] 0-based,
|
||||
* or [-1, -1] in case the lookup ran into a non-existing line.
|
||||
*/
|
||||
private static _mapStringIndexToBuffer(lineIndex: number, stringIndex: number, terminal: Terminal): [number, number] {
|
||||
const buf = terminal.buffer.active;
|
||||
const cell = buf.getNullCell();
|
||||
while (stringIndex) {
|
||||
const line = buf.getLine(lineIndex);
|
||||
if (!line) {
|
||||
break;
|
||||
return [-1, -1];
|
||||
}
|
||||
|
||||
if (line.isWrapped) {
|
||||
lineIndex--;
|
||||
for (let i = 0; i < line.length; ++i) {
|
||||
line.getCell(i, cell);
|
||||
const chars = cell.getChars();
|
||||
const width = cell.getWidth();
|
||||
if (width) {
|
||||
stringIndex -= chars.length || 1;
|
||||
}
|
||||
// look ahead for early wrap around of wide chars
|
||||
if (i === line.length - 1 && chars === '' && width) {
|
||||
const line = buf.getLine(lineIndex + 1);
|
||||
if (line && line.isWrapped) {
|
||||
line.getCell(0, cell);
|
||||
if (cell.getWidth() === 2) {
|
||||
stringIndex += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stringIndex < 0) {
|
||||
return [lineIndex, i];
|
||||
}
|
||||
}
|
||||
|
||||
prevLinesToWrap = line.isWrapped;
|
||||
} while (prevLinesToWrap);
|
||||
|
||||
const startLineIndex = lineIndex;
|
||||
|
||||
do {
|
||||
const nextLine = terminal.buffer.active.getLine(lineIndex + 1);
|
||||
lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
|
||||
const line = terminal.buffer.active.getLine(lineIndex);
|
||||
if (!line) {
|
||||
break;
|
||||
}
|
||||
lineString += line.translateToString(!lineWrapsToNext && trimRight).substring(0, terminal.cols);
|
||||
lineIndex++;
|
||||
} while (lineWrapsToNext);
|
||||
|
||||
return [lineString, startLineIndex];
|
||||
}
|
||||
return [lineIndex, 0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,25 +6,10 @@
|
||||
import { Terminal, ITerminalAddon, IDisposable } from 'xterm';
|
||||
import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider';
|
||||
|
||||
const protocolClause = '(https?:\\/\\/)';
|
||||
const domainCharacterSet = '[\\da-z\\.-]+';
|
||||
const negatedDomainCharacterSet = '[^\\da-z\\.-]+';
|
||||
const domainBodyClause = '(' + domainCharacterSet + ')';
|
||||
const tldClause = '([a-z\\.]{2,18})';
|
||||
const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
|
||||
const localHostClause = '(localhost)';
|
||||
const portClause = '(:\\d{1,5})';
|
||||
const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
|
||||
const pathCharacterSet = '(\\/[\\/\\w\\.\\-%~:+@]*)*([^:"\'\\s])';
|
||||
const pathClause = '(' + pathCharacterSet + ')?';
|
||||
const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
|
||||
const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
|
||||
const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
|
||||
const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
|
||||
const bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;
|
||||
const start = '(?:^|' + negatedDomainCharacterSet + ')(';
|
||||
const end = ')($|' + negatedPathCharacterSet + ')';
|
||||
const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);
|
||||
// consider everthing starting with http:// or https://
|
||||
// up to first whitespace as url
|
||||
// gets further narrowed down with URL later on
|
||||
const strictUrlRegex = /https?:[/]{2}\S*/;
|
||||
|
||||
function handleLink(event: MouseEvent, uri: string): void {
|
||||
const newWindow = window.open();
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# all half width - only good case
|
||||
echo "aaa http://example.com aaa http://example.com aaa"
|
||||
|
||||
# full width before - wrong offset
|
||||
echo "¥¥¥ http://example.com aaa http://example.com aaa"
|
||||
|
||||
# full width between - wrong offset
|
||||
echo "aaa http://example.com ¥¥¥ http://example.com aaa"
|
||||
|
||||
# full width before and between - error in offsets adding up
|
||||
echo "¥¥¥ http://example.com ¥¥¥ http://example.com aaa"
|
||||
|
||||
# full width within url - partial wrong match
|
||||
echo "aaa https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문"
|
||||
|
||||
# full width within and before - partial wrong match + wrong offsets
|
||||
echo "¥¥¥ https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문"
|
||||
|
||||
# not matching at all
|
||||
echo "http://test:password@example.com/some_path"
|
||||
Reference in New Issue
Block a user