mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into fixing-#5270
This commit is contained in:
@@ -167,9 +167,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [18] # just one as integration tests are about testing in browser
|
node-version: [18] # just one as integration tests are about testing in browser
|
||||||
runs-on: [ubuntu] # macos is flaky
|
runs-on: [ubuntu-22.04] # macos is flaky
|
||||||
browser: [chromium, firefox, webkit]
|
browser: [chromium, firefox, webkit]
|
||||||
runs-on: ${{ matrix.runs-on }}-latest
|
runs-on: ${{ matrix.runs-on }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Use Node.js ${{ matrix.node-version }}.x
|
- name: Use Node.js ${{ matrix.node-version }}.x
|
||||||
|
|||||||
@@ -377,8 +377,11 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
let line: IBufferLine;
|
let line: IBufferLine;
|
||||||
let joinedRanges: [number, number][];
|
let joinedRanges: [number, number][];
|
||||||
let isJoined: boolean;
|
let isJoined: boolean;
|
||||||
|
let skipJoinedCheckUntilX: number = 0;
|
||||||
|
let isValidJoinRange: boolean = true;
|
||||||
let lastCharX: number;
|
let lastCharX: number;
|
||||||
let range: [number, number];
|
let range: [number, number];
|
||||||
|
let isCursorRow: boolean;
|
||||||
let chars: string;
|
let chars: string;
|
||||||
let code: number;
|
let code: number;
|
||||||
let width: number;
|
let width: number;
|
||||||
@@ -405,6 +408,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
row = y + terminal.buffer.ydisp;
|
row = y + terminal.buffer.ydisp;
|
||||||
line = terminal.buffer.lines.get(row)!;
|
line = terminal.buffer.lines.get(row)!;
|
||||||
this._model.lineLengths[y] = 0;
|
this._model.lineLengths[y] = 0;
|
||||||
|
isCursorRow = cursorY === row;
|
||||||
|
skipJoinedCheckUntilX = 0;
|
||||||
joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
|
joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
|
||||||
for (x = 0; x < terminal.cols; x++) {
|
for (x = 0; x < terminal.cols; x++) {
|
||||||
lastBg = this._cellColorResolver.result.bg;
|
lastBg = this._cellColorResolver.result.bg;
|
||||||
@@ -416,25 +421,43 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// If true, indicates that the current character(s) to draw were joined.
|
// If true, indicates that the current character(s) to draw were joined.
|
||||||
isJoined = false;
|
isJoined = false;
|
||||||
|
|
||||||
|
// Indicates whether this cell is part of a joined range that should be ignored as it cannot
|
||||||
|
// be rendered entirely, like the selection state differs across the range.
|
||||||
|
isValidJoinRange = (x >= skipJoinedCheckUntilX);
|
||||||
|
|
||||||
lastCharX = x;
|
lastCharX = x;
|
||||||
|
|
||||||
// Process any joined character ranges as needed. Because of how the
|
// Process any joined character ranges as needed. Because of how the
|
||||||
// ranges are produced, we know that they are valid for the characters
|
// ranges are produced, we know that they are valid for the characters
|
||||||
// and attributes of our input.
|
// and attributes of our input.
|
||||||
if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
|
if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) {
|
||||||
isJoined = true;
|
|
||||||
range = joinedRanges.shift()!;
|
range = joinedRanges.shift()!;
|
||||||
|
|
||||||
// We already know the exact start and end column of the joined range,
|
// If the ligature's selection state is not consistent, don't join it. This helps the
|
||||||
// so we get the string and width representing it directly.
|
// selection render correctly regardless whether they should be joined.
|
||||||
cell = new JoinedCellData(
|
const firstSelectionState = this._model.selection.isCellSelected(this._terminal, range[0], row);
|
||||||
cell,
|
for (i = range[0] + 1; i < range[1]; i++) {
|
||||||
line!.translateToString(true, range[0], range[1]),
|
isValidJoinRange &&= (firstSelectionState === this._model.selection.isCellSelected(this._terminal, i, row));
|
||||||
range[1] - range[0]
|
}
|
||||||
);
|
// Similarly, if the cursor is in the ligature, don't join it.
|
||||||
|
isValidJoinRange &&= !isCursorRow || cursorX < range[0] || cursorX >= range[1];
|
||||||
|
if (!isValidJoinRange) {
|
||||||
|
skipJoinedCheckUntilX = range[1];
|
||||||
|
} else {
|
||||||
|
isJoined = true;
|
||||||
|
|
||||||
// Skip over the cells occupied by this range in the loop
|
// We already know the exact start and end column of the joined range,
|
||||||
lastCharX = range[1] - 1;
|
// so we get the string and width representing it directly.
|
||||||
|
cell = new JoinedCellData(
|
||||||
|
cell,
|
||||||
|
line!.translateToString(true, range[0], range[1]),
|
||||||
|
range[1] - range[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Skip over the cells occupied by this range in the loop
|
||||||
|
lastCharX = range[1] - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chars = cell.getChars();
|
chars = cell.getChars();
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ if (document.location.pathname === '/test') {
|
|||||||
document.getElementById('add-decoration').addEventListener('click', addDecoration);
|
document.getElementById('add-decoration').addEventListener('click', addDecoration);
|
||||||
document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler);
|
document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler);
|
||||||
document.getElementById('decoration-stress-test').addEventListener('click', decorationStressTest);
|
document.getElementById('decoration-stress-test').addEventListener('click', decorationStressTest);
|
||||||
|
document.getElementById('ligatures-test').addEventListener('click', ligaturesTest);
|
||||||
document.getElementById('weblinks-test').addEventListener('click', testWeblinks);
|
document.getElementById('weblinks-test').addEventListener('click', testWeblinks);
|
||||||
document.getElementById('bce').addEventListener('click', coloredErase);
|
document.getElementById('bce').addEventListener('click', coloredErase);
|
||||||
addVtButtons();
|
addVtButtons();
|
||||||
@@ -1307,6 +1308,20 @@ function addVtButtons(): void {
|
|||||||
document.querySelector('#vt-container').appendChild(vtFragment);
|
document.querySelector('#vt-container').appendChild(vtFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ligaturesTest(): void {
|
||||||
|
term.write([
|
||||||
|
'',
|
||||||
|
'-<< -< -<- <-- <--- <<- <- -> ->> --> ---> ->- >- >>-',
|
||||||
|
'=<< =< =<= <== <=== <<= <= => =>> ==> ===> =>= >= >>=',
|
||||||
|
'<-> <--> <---> <----> <=> <==> <===> <====> :: ::: __',
|
||||||
|
'<~~ </ </> /> ~~> == != /= ~= <> === !== !=== =/= =!=',
|
||||||
|
'<: := *= *+ <* <*> *> <| <|> |> <. <.> .> +* =* =: :>',
|
||||||
|
'(* *) /* */ [| |] {| |} ++ +++ \/ /\ |- -| <!-- <!---',
|
||||||
|
'==== ===== ====== ======= ======== =========',
|
||||||
|
'---- ----- ------ ------- -------- ---------'
|
||||||
|
].join('\r\n'));
|
||||||
|
}
|
||||||
|
|
||||||
function testWeblinks(): void {
|
function testWeblinks(): void {
|
||||||
const linkExamples = `
|
const linkExamples = `
|
||||||
aaa http://example.com aaa http://example.com aaa
|
aaa http://example.com aaa http://example.com aaa
|
||||||
|
|||||||
@@ -106,6 +106,9 @@
|
|||||||
<dd><button id="add-overview-ruler" title="Add an overview ruler to the terminal">Add Overview Ruler</button></dd>
|
<dd><button id="add-overview-ruler" title="Add an overview ruler to the terminal">Add Overview Ruler</button></dd>
|
||||||
<dd><button id="decoration-stress-test" title="Toggle between adding and removing a decoration to each line">Stress Test</button></dd>
|
<dd><button id="decoration-stress-test" title="Toggle between adding and removing a decoration to each line">Stress Test</button></dd>
|
||||||
|
|
||||||
|
<dt>Ligatures Addon</dt>
|
||||||
|
<dd><button id="ligatures-test" title="Write common ligatures sequences">Common ligatures</button></dd>
|
||||||
|
|
||||||
<dt>Weblinks Addon</dt>
|
<dt>Weblinks Addon</dt>
|
||||||
<dd><button id="weblinks-test" title="Various url conditions from demo data, hover&click to test">Test URLs</button></dd>
|
<dd><button id="weblinks-test" title="Various url conditions from demo data, hover&click to test">Test URLs</button></dd>
|
||||||
|
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the terminal is already opened
|
// If the terminal is already opened
|
||||||
if (this.element?.ownerDocument.defaultView && this._coreBrowserService) {
|
if (this.element?.ownerDocument.defaultView && this._coreBrowserService && this.element?.isConnected) {
|
||||||
// Adjust the window if needed
|
// Adjust the window if needed
|
||||||
if (this.element.ownerDocument.defaultView !== this._coreBrowserService.window) {
|
if (this.element.ownerDocument.defaultView !== this._coreBrowserService.window) {
|
||||||
this._coreBrowserService.window = this.element.ownerDocument.defaultView;
|
this._coreBrowserService.window = this.element.ownerDocument.defaultView;
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export class DomRendererRowFactory {
|
|||||||
let charElement: HTMLSpanElement | undefined;
|
let charElement: HTMLSpanElement | undefined;
|
||||||
let cellAmount = 0;
|
let cellAmount = 0;
|
||||||
let text = '';
|
let text = '';
|
||||||
|
let i = 0;
|
||||||
let oldBg = 0;
|
let oldBg = 0;
|
||||||
let oldFg = 0;
|
let oldFg = 0;
|
||||||
let oldExt = 0;
|
let oldExt = 0;
|
||||||
@@ -91,6 +92,7 @@ export class DomRendererRowFactory {
|
|||||||
let oldSpacing = 0;
|
let oldSpacing = 0;
|
||||||
let oldIsInSelection: boolean = false;
|
let oldIsInSelection: boolean = false;
|
||||||
let spacing = 0;
|
let spacing = 0;
|
||||||
|
let skipJoinedCheckUntilX = 0;
|
||||||
const classes: string[] = [];
|
const classes: string[] = [];
|
||||||
|
|
||||||
const hasHover = linkStart !== -1 && linkEnd !== -1;
|
const hasHover = linkStart !== -1 && linkEnd !== -1;
|
||||||
@@ -106,29 +108,46 @@ export class DomRendererRowFactory {
|
|||||||
|
|
||||||
// If true, indicates that the current character(s) to draw were joined.
|
// If true, indicates that the current character(s) to draw were joined.
|
||||||
let isJoined = false;
|
let isJoined = false;
|
||||||
|
|
||||||
|
// Indicates whether this cell is part of a joined range that should be ignored as it cannot
|
||||||
|
// be rendered entirely, like the selection state differs across the range.
|
||||||
|
let isValidJoinRange = (x >= skipJoinedCheckUntilX);
|
||||||
|
|
||||||
let lastCharX = x;
|
let lastCharX = x;
|
||||||
|
|
||||||
// Process any joined character ranges as needed. Because of how the
|
// Process any joined character ranges as needed. Because of how the
|
||||||
// ranges are produced, we know that they are valid for the characters
|
// ranges are produced, we know that they are valid for the characters
|
||||||
// and attributes of our input.
|
// and attributes of our input.
|
||||||
let cell = this._workCell;
|
let cell = this._workCell;
|
||||||
if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
|
if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) {
|
||||||
isJoined = true;
|
|
||||||
const range = joinedRanges.shift()!;
|
const range = joinedRanges.shift()!;
|
||||||
|
// If the ligature's selection state is not consistent, don't join it. This helps the
|
||||||
|
// selection render correctly regardless whether they should be joined.
|
||||||
|
const firstSelectionState = this._isCellInSelection(range[0], row);
|
||||||
|
for (i = range[0] + 1; i < range[1]; i++) {
|
||||||
|
isValidJoinRange &&= (firstSelectionState === this._isCellInSelection(i, row));
|
||||||
|
}
|
||||||
|
// Similarly, if the cursor is in the ligature, don't join it.
|
||||||
|
isValidJoinRange &&= !isCursorRow || cursorX < range[0] || cursorX >= range[1];
|
||||||
|
if (!isValidJoinRange) {
|
||||||
|
skipJoinedCheckUntilX = range[1];
|
||||||
|
} else {
|
||||||
|
isJoined = true;
|
||||||
|
|
||||||
// We already know the exact start and end column of the joined range,
|
// We already know the exact start and end column of the joined range,
|
||||||
// so we get the string and width representing it directly
|
// so we get the string and width representing it directly
|
||||||
cell = new JoinedCellData(
|
cell = new JoinedCellData(
|
||||||
this._workCell,
|
this._workCell,
|
||||||
lineData.translateToString(true, range[0], range[1]),
|
lineData.translateToString(true, range[0], range[1]),
|
||||||
range[1] - range[0]
|
range[1] - range[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Skip over the cells occupied by this range in the loop
|
// Skip over the cells occupied by this range in the loop
|
||||||
lastCharX = range[1] - 1;
|
lastCharX = range[1] - 1;
|
||||||
|
|
||||||
// Recalculate width
|
// Recalculate width
|
||||||
width = cell.getWidth();
|
width = cell.getWidth();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInSelection = this._isCellInSelection(x, row);
|
const isInSelection = this._isCellInSelection(x, row);
|
||||||
@@ -178,6 +197,7 @@ export class DomRendererRowFactory {
|
|||||||
&& !isCursorCell
|
&& !isCursorCell
|
||||||
&& !isJoined
|
&& !isJoined
|
||||||
&& !isDecorated
|
&& !isDecorated
|
||||||
|
&& isValidJoinRange
|
||||||
) {
|
) {
|
||||||
// no span alterations, thus only account chars skipping all code below
|
// no span alterations, thus only account chars skipping all code below
|
||||||
if (cell.isInvisible()) {
|
if (cell.isInvisible()) {
|
||||||
@@ -435,7 +455,7 @@ export class DomRendererRowFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// exclude conditions for cell merging - never merge these
|
// exclude conditions for cell merging - never merge these
|
||||||
if (!isCursorCell && !isJoined && !isDecorated) {
|
if (!isCursorCell && !isJoined && !isDecorated && isValidJoinRange) {
|
||||||
cellAmount++;
|
cellAmount++;
|
||||||
} else {
|
} else {
|
||||||
charElement.textContent = text;
|
charElement.textContent = text;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export class InstantiationService implements IInstantiationService {
|
|||||||
for (const dependency of serviceDependencies) {
|
for (const dependency of serviceDependencies) {
|
||||||
const service = this._services.get(dependency.id);
|
const service = this._services.get(dependency.id);
|
||||||
if (!service) {
|
if (!service) {
|
||||||
throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`);
|
throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id._id}.`);
|
||||||
}
|
}
|
||||||
serviceArgs.push(service);
|
serviceArgs.push(service);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export function createDecorator<T>(id: string): IServiceIdentifier<T> {
|
|||||||
storeServiceDependency(decorator, target, index);
|
storeServiceDependency(decorator, target, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
decorator.toString = () => id;
|
decorator._id = id;
|
||||||
|
|
||||||
serviceRegistry.set(id, decorator);
|
serviceRegistry.set(id, decorator);
|
||||||
return decorator;
|
return decorator;
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export interface ICharsetService {
|
|||||||
export interface IServiceIdentifier<T> {
|
export interface IServiceIdentifier<T> {
|
||||||
(...args: any[]): void;
|
(...args: any[]): void;
|
||||||
type: T;
|
type: T;
|
||||||
|
_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBrandedService {
|
export interface IBrandedService {
|
||||||
|
|||||||
Reference in New Issue
Block a user