From 1e8901630cd38ed4e4c97d32b34d177b948f1b10 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=84=BF=E6=97=B6?=
<58261676+childrentime@users.noreply.github.com>
Date: Wed, 2 Mar 2022 11:25:20 +0800
Subject: [PATCH 001/245] Replace all getOption/setOption usage with options
---
.../xterm-addon-ligatures/src/index.test.ts | 36 +++++++++----------
addons/xterm-addon-ligatures/src/index.ts | 8 ++---
addons/xterm-addon-webgl/src/WebglRenderer.ts | 8 ++---
.../src/atlas/CharAtlasUtils.ts | 20 +++++------
.../src/renderLayer/BaseRenderLayer.ts | 4 +--
.../src/renderLayer/CursorRenderLayer.ts | 14 ++++----
.../test/WebglRenderer.api.ts | 36 +++++++++----------
demo/index.html | 2 +-
test/api/InputHandler.api.ts | 4 +--
test/api/MouseTracking.api.ts | 2 +-
test/api/Terminal.api.ts | 7 ----
11 files changed, 67 insertions(+), 74 deletions(-)
diff --git a/addons/xterm-addon-ligatures/src/index.test.ts b/addons/xterm-addon-ligatures/src/index.test.ts
index 838955c0..f9f10d0b 100644
--- a/addons/xterm-addon-ligatures/src/index.test.ts
+++ b/addons/xterm-addon-ligatures/src/index.test.ts
@@ -78,7 +78,7 @@ describe('xterm-addon-ligatures', () => {
});
it('handles quoted font names', done => {
- term.setOption('fontFamily', '"Fira Code", monospace');
+ term.options.fontFamily = '"Fira Code", monospace';
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
@@ -87,7 +87,7 @@ describe('xterm-addon-ligatures', () => {
});
it('falls back to later fonts if earlier ones are not present', done => {
- term.setOption('fontFamily', 'notinstalled, Fira Code, monospace');
+ term.options.fontFamily = 'notinstalled, Fira Code, monospace';
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
@@ -98,17 +98,17 @@ describe('xterm-addon-ligatures', () => {
it('uses the current font value', done => {
// The first three calls are all synchronous so that we don't allow time for
// any fonts to load while we're switching things around
- term.setOption('fontFamily', 'Fira Code');
+ term.options.fontFamily = 'Fira Code';
assert.deepEqual(term.joiner!(input), []);
- term.setOption('fontFamily', 'notinstalled');
+ term.options.fontFamily = 'notinstalled';
assert.deepEqual(term.joiner!(input), []);
- term.setOption('fontFamily', 'Iosevka');
+ term.options.fontFamily = 'Iosevka';
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4]]);
// And switch it back to Fira Code for good measure
- term.setOption('fontFamily', 'Fira Code');
+ term.options.fontFamily = 'Fira Code';
// At this point, we haven't loaded the new font, so the result reverts
// back to empty until that happens
@@ -124,7 +124,7 @@ describe('xterm-addon-ligatures', () => {
it('allows multiple terminal instances that use different fonts', done => {
const onRefresh2 = sinon.stub();
const term2 = new MockTerminal(onRefresh2);
- term2.setOption('fontFamily', 'Iosevka');
+ term2.options.fontFamily = 'Iosevka';
ligatureSupport.enableLigatures(term2 as any);
assert.deepEqual(term.joiner!(input), []);
@@ -140,7 +140,7 @@ describe('xterm-addon-ligatures', () => {
});
it('fails if it finds but cannot load the font', async () => {
- term.setOption('fontFamily', 'Nonexistant Font, monospace');
+ term.options.fontFamily = 'Nonexistant Font, monospace';
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
@@ -148,7 +148,7 @@ describe('xterm-addon-ligatures', () => {
});
it('returns nothing if the font is not present on the system', async () => {
- term.setOption('fontFamily', 'notinstalled');
+ term.options.fontFamily = 'notinstalled';
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
@@ -156,7 +156,7 @@ describe('xterm-addon-ligatures', () => {
});
it('returns nothing if no specific font is specified', async () => {
- term.setOption('fontFamily', 'monospace');
+ term.options.fontFamily = 'monospace';
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
@@ -164,7 +164,7 @@ describe('xterm-addon-ligatures', () => {
});
it('returns nothing if no fonts are provided', async () => {
- term.setOption('fontFamily', '');
+ term.options.fontFamily = '';
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
@@ -172,7 +172,7 @@ describe('xterm-addon-ligatures', () => {
});
it('fails when given malformed inputs', async () => {
- term.setOption('fontFamily', {} as any);
+ term.options.fontFamily = {} as any;
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
@@ -181,7 +181,7 @@ describe('xterm-addon-ligatures', () => {
it('ensures no empty errors are thrown', async () => {
sinon.stub(fontLigatures, 'loadFile').callsFake(async () => { throw undefined; });
- term.setOption('fontFamily', 'Iosevka');
+ term.options.fontFamily = 'Iosevka';
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
@@ -209,11 +209,11 @@ class MockTerminal {
public deregisterCharacterJoiner(id: number): void {
this.joiner = undefined;
}
- public setOption(name: string, value: string | number): void {
- this._options[name] = value;
- }
- public getOption(name: string): string | number {
- return this._options[name];
+ public get options(): { [name: string]: string | number } { return this._options; }
+ public set options(options: { [name: string]: string | number }) {
+ for (const key in this._options) {
+ this._options[key] = options[key];
+ }
}
}
diff --git a/addons/xterm-addon-ligatures/src/index.ts b/addons/xterm-addon-ligatures/src/index.ts
index c867369d..c54f3b50 100644
--- a/addons/xterm-addon-ligatures/src/index.ts
+++ b/addons/xterm-addon-ligatures/src/index.ts
@@ -34,7 +34,7 @@ export function enableLigatures(term: Terminal): void {
term.registerCharacterJoiner((text: string): [number, number][] => {
// If the font hasn't been loaded yet, load it and return an empty result
- const termFont = term.getOption('fontFamily');
+ const termFont = term.options.fontFamily;
if (
termFont &&
(loadingState === LoadingState.UNLOADED || currentFontName !== termFont)
@@ -48,20 +48,20 @@ export function enableLigatures(term: Terminal): void {
.then(f => {
// Another request may have come in while we were waiting, so make
// sure our font is still vaild.
- if (currentCallFontName === term.getOption('fontFamily')) {
+ if (currentCallFontName === term.options.fontFamily) {
loadingState = LoadingState.LOADED;
font = f;
// Only refresh things if we actually found a font
if (f) {
- term.refresh(0, term.getOption('rows') - 1);
+ term.refresh(0, term.options.rows! - 1);
}
}
})
.catch(e => {
// Another request may have come in while we were waiting, so make
// sure our font is still vaild.
- if (currentCallFontName === term.getOption('fontFamily')) {
+ if (currentCallFontName === term.options.fontFamily) {
loadingState = LoadingState.FAILED;
font = undefined;
loadError = e;
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts
index 24e55fed..a256b9da 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts
@@ -440,18 +440,18 @@ export class WebglRenderer extends Disposable implements IRenderer {
// will be floored because since lineHeight can never be lower then 1, there
// is a guarentee that the scaled line height will always be larger than
// scaled char height.
- this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.getOption('lineHeight'));
+ this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight!);
// Calculate the y coordinate within a cell that text should draw from in
// order to draw in the center of a cell.
- this.dimensions.scaledCharTop = this._terminal.getOption('lineHeight') === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
+ this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
// Calculate the scaled cell width, taking the letterSpacing into account.
- this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.getOption('letterSpacing'));
+ this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing!);
// Calculate the x coordinate with a cell that text should draw from in
// order to draw in the center of a cell.
- this.dimensions.scaledCharLeft = Math.floor(this._terminal.getOption('letterSpacing') / 2);
+ this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing! / 2);
// Recalculate the canvas dimensions; scaled* define the actual number of
// pixel in the canvas
diff --git a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts
index 962eb7b3..4705796a 100644
--- a/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts
+++ b/addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts
@@ -28,21 +28,21 @@ export function generateConfig(scaledCellWidth: number, scaledCellHeight: number
contrastCache: colors.contrastCache
};
return {
- customGlyphs: terminal.getOption('customGlyphs'),
+ customGlyphs: terminal.options.customGlyphs!,
devicePixelRatio: window.devicePixelRatio,
- letterSpacing: terminal.getOption('letterSpacing'),
- lineHeight: terminal.getOption('lineHeight'),
+ letterSpacing: terminal.options.letterSpacing!,
+ lineHeight: terminal.options.lineHeight!,
scaledCellWidth,
scaledCellHeight,
scaledCharWidth,
scaledCharHeight,
- fontFamily: terminal.getOption('fontFamily'),
- fontSize: terminal.getOption('fontSize'),
- fontWeight: terminal.getOption('fontWeight') as FontWeight,
- fontWeightBold: terminal.getOption('fontWeightBold') as FontWeight,
- allowTransparency: terminal.getOption('allowTransparency'),
- drawBoldTextInBrightColors: terminal.getOption('drawBoldTextInBrightColors'),
- minimumContrastRatio: terminal.getOption('minimumContrastRatio'),
+ fontFamily: terminal.options.fontFamily!,
+ fontSize: terminal.options.fontSize!,
+ fontWeight: terminal.options.fontWeight as FontWeight,
+ fontWeightBold: terminal.options.fontWeightBold as FontWeight,
+ allowTransparency: terminal.options.allowTransparency!,
+ drawBoldTextInBrightColors: terminal.options.drawBoldTextInBrightColors!,
+ minimumContrastRatio: terminal.options.minimumContrastRatio!,
colors: clonedColors
};
}
diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts
index 4c17aad4..619ca1a8 100644
--- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts
+++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts
@@ -254,10 +254,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
* @param isBold If we should use the bold fontWeight.
*/
protected _getFont(terminal: Terminal, isBold: boolean, isItalic: boolean): string {
- const fontWeight = isBold ? terminal.getOption('fontWeightBold') : terminal.getOption('fontWeight');
+ const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight;
const fontStyle = isItalic ? 'italic' : '';
- return `${fontStyle} ${fontWeight} ${terminal.getOption('fontSize') * window.devicePixelRatio}px ${terminal.getOption('fontFamily')}`;
+ return `${fontStyle} ${fontWeight} ${terminal.options.fontSize! * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
}
}
diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts
index 4199b7e1..c80b4c56 100644
--- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts
+++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts
@@ -83,7 +83,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
}
public onOptionsChanged(terminal: Terminal): void {
- if (terminal.getOption('cursorBlink')) {
+ if (terminal.options.cursorBlink) {
if (!this._cursorBlinkStateManager) {
this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, () => {
this._render(terminal, true);
@@ -140,7 +140,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._clearCursor();
this._ctx.save();
this._ctx.fillStyle = this._colors.cursor.css;
- const cursorStyle = terminal.getOption('cursorStyle');
+ const cursorStyle = terminal.options.cursorStyle;
if (cursorStyle && cursorStyle !== 'block') {
this._cursorRenderers[cursorStyle](terminal, cursorX, viewportRelativeCursorY, this._cell);
} else {
@@ -150,7 +150,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
- this._state.style = cursorStyle;
+ this._state.style = cursorStyle!;
this._state.width = this._cell.getWidth();
return;
}
@@ -166,7 +166,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
if (this._state.x === cursorX &&
this._state.y === viewportRelativeCursorY &&
this._state.isFocused === isTerminalFocused(terminal) &&
- this._state.style === terminal.getOption('cursorStyle') &&
+ this._state.style === terminal.options.cursorStyle &&
this._state.width === this._cell.getWidth()) {
return;
}
@@ -174,13 +174,13 @@ export class CursorRenderLayer extends BaseRenderLayer {
}
this._ctx.save();
- this._cursorRenderers[terminal.getOption('cursorStyle') || 'block'](terminal, cursorX, viewportRelativeCursorY, this._cell);
+ this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, cursorX, viewportRelativeCursorY, this._cell);
this._ctx.restore();
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
- this._state.style = terminal.getOption('cursorStyle');
+ this._state.style = terminal.options.cursorStyle!;
this._state.width = this._cell.getWidth();
}
@@ -205,7 +205,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
private _renderBarCursor(terminal: Terminal, x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.fillStyle = this._colors.cursor.css;
- this._fillLeftLineAtCell(x, y, terminal.getOption('cursorWidth'));
+ this._fillLeftLineAtCell(x, y, terminal.options.cursorWidth!);
this._ctx.restore();
}
diff --git a/addons/xterm-addon-webgl/test/WebglRenderer.api.ts b/addons/xterm-addon-webgl/test/WebglRenderer.api.ts
index e6942d1c..080d7ce8 100644
--- a/addons/xterm-addon-webgl/test/WebglRenderer.api.ts
+++ b/addons/xterm-addon-webgl/test/WebglRenderer.api.ts
@@ -49,7 +49,7 @@ describe('WebGL Renderer Integration Tests', async () => {
cyan: '#131415',
white: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[30m█\\x1b[31m█\\x1b[32m█\\x1b[33m█\\x1b[34m█\\x1b[35m█\\x1b[36m█\\x1b[37m█`);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]);
@@ -73,8 +73,8 @@ describe('WebGL Renderer Integration Tests', async () => {
brightWhite: '#161718'
};
await page.evaluate(`
- window.term.setOption('theme', ${JSON.stringify(theme)});
- window.term.setOption('drawBoldTextInBrightColors', true);
+ window.term.options.theme = ${JSON.stringify(theme)};
+ window.term.options.drawBoldTextInBrightColors = true;
`);
await writeSync(page, `\\x1b[1;30m█\\x1b[1;31m█\\x1b[1;32m█\\x1b[1;33m█\\x1b[1;34m█\\x1b[1;35m█\\x1b[1;36m█\\x1b[1;37m█`);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
@@ -98,7 +98,7 @@ describe('WebGL Renderer Integration Tests', async () => {
cyan: '#131415',
white: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[40m \\x1b[41m \\x1b[42m \\x1b[43m \\x1b[44m \\x1b[45m \\x1b[46m \\x1b[47m `);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]);
@@ -121,7 +121,7 @@ describe('WebGL Renderer Integration Tests', async () => {
cyan: '#131415',
white: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[7;30m \\x1b[7;31m \\x1b[7;32m \\x1b[7;33m \\x1b[7;34m \\x1b[7;35m \\x1b[7;36m \\x1b[7;37m `);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]);
@@ -144,7 +144,7 @@ describe('WebGL Renderer Integration Tests', async () => {
cyan: '#131415',
white: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[7;40m█\\x1b[7;41m█\\x1b[7;42m█\\x1b[7;43m█\\x1b[7;44m█\\x1b[7;45m█\\x1b[7;46m█\\x1b[7;47m█`);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]);
@@ -167,7 +167,7 @@ describe('WebGL Renderer Integration Tests', async () => {
cyan: '#131415',
white: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[8;30m \\x1b[8;31m \\x1b[8;32m \\x1b[8;33m \\x1b[8;34m \\x1b[8;35m \\x1b[8;36m \\x1b[8;37m `);
await pollFor(page, () => getCellColor(1, 1), [0, 0, 0, 255]);
await pollFor(page, () => getCellColor(2, 1), [0, 0, 0, 255]);
@@ -190,7 +190,7 @@ describe('WebGL Renderer Integration Tests', async () => {
cyan: '#131415',
white: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[8;40m█\\x1b[8;41m█\\x1b[8;42m█\\x1b[8;43m█\\x1b[8;44m█\\x1b[8;45m█\\x1b[8;46m█\\x1b[8;47m█`);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]);
@@ -213,7 +213,7 @@ describe('WebGL Renderer Integration Tests', async () => {
brightCyan: '#131415',
brightWhite: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[90m█\\x1b[91m█\\x1b[92m█\\x1b[93m█\\x1b[94m█\\x1b[95m█\\x1b[96m█\\x1b[97m█`);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]);
@@ -236,7 +236,7 @@ describe('WebGL Renderer Integration Tests', async () => {
brightCyan: '#131415',
brightWhite: '#161718'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, `\\x1b[100m \\x1b[101m \\x1b[102m \\x1b[103m \\x1b[104m \\x1b[105m \\x1b[106m \\x1b[107m `);
await pollFor(page, () => getCellColor(1, 1), [1, 2, 3, 255]);
await pollFor(page, () => getCellColor(2, 1), [4, 5, 6, 255]);
@@ -715,8 +715,8 @@ describe('WebGL Renderer Integration Tests', async () => {
brightWhite: '#eeeeec'
};
await page.evaluate(`
- window.term.setOption('theme', ${JSON.stringify(theme)});
- window.term.setOption('minimumContrastRatio', 1);
+ window.term.options.theme = ${JSON.stringify(theme)};
+ window.term.options.minimumContrastRatio = 1;
`);
await writeSync(page,
`\\x1b[30m█\\x1b[31m█\\x1b[32m█\\x1b[33m█\\x1b[34m█\\x1b[35m█\\x1b[36m█\\x1b[37m█\\r\\n` +
@@ -742,7 +742,7 @@ describe('WebGL Renderer Integration Tests', async () => {
// Setting and check for minimum contrast values, note that these are note
// exact to the contrast ratio, if the increase luminance algorithm
// changes then these will probably fail
- await page.evaluate(`window.term.setOption('minimumContrastRatio', 10);`);
+ await page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
await pollFor(page, () => getCellColor(1, 1), [176, 180, 180, 255]);
await pollFor(page, () => getCellColor(2, 1), [238, 158, 158, 255]);
await pollFor(page, () => getCellColor(3, 1), [197, 223, 171, 255]);
@@ -783,8 +783,8 @@ describe('WebGL Renderer Integration Tests', async () => {
brightWhite: '#eeeeec'
};
await page.evaluate(`
- window.term.setOption('theme', ${JSON.stringify(theme)});
- window.term.setOption('minimumContrastRatio', 1);
+ window.term.options.theme = ${JSON.stringify(theme)};
+ window.term.options.minimumContrastRatio = 1;
`);
await writeSync(page,
`\\x1b[30m█\\x1b[31m█\\x1b[32m█\\x1b[33m█\\x1b[34m█\\x1b[35m█\\x1b[36m█\\x1b[37m█\\r\\n` +
@@ -810,7 +810,7 @@ describe('WebGL Renderer Integration Tests', async () => {
// Setting and check for minimum contrast values, note that these are note
// exact to the contrast ratio, if the increase luminance algorithm
// changes then these will probably fail
- await page.evaluate(`window.term.setOption('minimumContrastRatio', 10);`);
+ await page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
await pollFor(page, () => getCellColor(1, 1), [46, 52, 54, 255]);
await pollFor(page, () => getCellColor(2, 1), [132, 0, 0, 255]);
await pollFor(page, () => getCellColor(3, 1), [78, 154, 6, 255]);
@@ -843,7 +843,7 @@ describe('WebGL Renderer Integration Tests', async () => {
background: '#00FF00',
selection: '#0000FF'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await writeSync(page, ` █\\x1b[7m█\\x1b[0m`);
await pollFor(page, () => getCellColor(1, 1), [0, 255, 0, 255]);
await pollFor(page, () => getCellColor(2, 1), [255, 0, 0, 255]);
@@ -867,7 +867,7 @@ describe('WebGL Renderer Integration Tests', async () => {
const theme: ITheme = {
background: '#ff000080'
};
- await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
+ await page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
const data = `\\x1b[7m█\x1b[0m`;
await writeSync(page, data);
// Inverse background should be opaque
diff --git a/demo/index.html b/demo/index.html
index e024222c..31111631 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -28,7 +28,7 @@
Options
-
These options can be set in the Terminal constructor or by using the Terminal.setOption function.
+
These options can be set in the Terminal constructor or by using the Terminal.options property.
diff --git a/test/api/InputHandler.api.ts b/test/api/InputHandler.api.ts
index 4d3a15dd..6e0bf88b 100644
--- a/test/api/InputHandler.api.ts
+++ b/test/api/InputHandler.api.ts
@@ -362,7 +362,7 @@ describe('InputHandler Integration Tests', function(): void {
await pollFor(page, async () => await page.evaluate(`(() => _stack)()`), []);
});
it('14 - GetWinSizePixels', async function(): Promise
{
- await page.evaluate(`window.term.setOption('windowOptions', { getWinSizePixels: true }); `);
+ await page.evaluate(`window.term.options.windowOptions = { getWinSizePixels: true }; `);
await page.evaluate(`(() => {
window._stack = [];
const _h = window.term.onData(data => window._stack.push(data));
@@ -373,7 +373,7 @@ describe('InputHandler Integration Tests', function(): void {
await pollFor(page, async () => await page.evaluate(`(() => _stack)()`), [`\x1b[4;${d.height};${d.width}t`]);
});
it('16 - GetCellSizePixels', async function(): Promise {
- await page.evaluate(`window.term.setOption('windowOptions', { getCellSizePixels: true }); `);
+ await page.evaluate(`window.term.options.windowOptions = { getCellSizePixels: true }; `);
await page.evaluate(`(() => {
window._stack = [];
const _h = window.term.onData(data => window._stack.push(data));
diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts
index ebeb680f..16a9b32c 100644
--- a/test/api/MouseTracking.api.ts
+++ b/test/api/MouseTracking.api.ts
@@ -229,7 +229,7 @@ describe('Mouse Tracking Tests', async () => {
window.calls = [];
window.term.onData(e => calls.push( Array.from(e).map(el => el.charCodeAt(0)) ));
window.term.onBinary(e => calls.push( Array.from(e).map(el => el.charCodeAt(0)) ));
- window.term.setOption('fontSize', ${fontSize});
+ window.term.options.fontSize = ${fontSize};
window.term.resize(${cols}, ${rows});
`);
});
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index ee6a0cfa..cc3b78be 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -154,13 +154,6 @@ describe('API Integration Tests', function(): void {
}
});
- it('getOption, setOption', async () => {
- await openTerminal(page);
- assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'canvas');
- await page.evaluate(`window.term.setOption('rendererType', 'dom')`);
- assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'dom');
- });
-
describe('options', () => {
it('getter', async () => {
await openTerminal(page);
From 78c2f06107030f6abfe165b385318c46c5784df5 Mon Sep 17 00:00:00 2001
From: Simon Lamon
Date: Wed, 2 Mar 2022 13:19:53 +0000
Subject: [PATCH 002/245] Fix triple select edge case
---
src/browser/services/SelectionService.test.ts | 6 ++++++
src/browser/services/SelectionService.ts | 10 +++++++---
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts
index 514d5803..d829e394 100644
--- a/src/browser/services/SelectionService.test.ts
+++ b/src/browser/services/SelectionService.test.ts
@@ -340,6 +340,9 @@ describe('SelectionService', () => {
buffer.lines.set(0, stringToRow('foo bar'));
selectionService.selectLineAt(0);
assert.equal(selectionService.selectionText, 'foo bar', 'The selected text is correct');
+ assert.deepEqual(selectionService.model.selectionStart, [0, 0]);
+ assert.deepEqual(selectionService.model.selectionEnd, undefined);
+ assert.deepEqual(selectionService.model.selectionStartLength, 20);
assert.deepEqual(selectionService.model.finalSelectionStart, [0, 0]);
assert.deepEqual(selectionService.model.finalSelectionEnd, [bufferService.cols, 0], 'The actual selection spans the entire column');
});
@@ -350,6 +353,9 @@ describe('SelectionService', () => {
buffer.lines.set(1, line2);
selectionService.selectLineAt(0);
assert.equal(selectionService.selectionText, 'foobar', 'The selected text is correct');
+ assert.deepEqual(selectionService.model.selectionStart, [0, 0]);
+ assert.deepEqual(selectionService.model.selectionEnd, undefined);
+ assert.deepEqual(selectionService.model.selectionStartLength, 40);
assert.deepEqual(selectionService.model.finalSelectionStart, [0, 0]);
assert.deepEqual(selectionService.model.finalSelectionEnd, [bufferService.cols, 1], 'The actual selection spans the entire column');
});
diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts
index 1ea2395d..53020b53 100644
--- a/src/browser/services/SelectionService.ts
+++ b/src/browser/services/SelectionService.ts
@@ -11,7 +11,7 @@ import { SelectionModel } from 'browser/selection/SelectionModel';
import { CellData } from 'common/buffer/CellData';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { IMouseService, ISelectionService, IRenderService } from 'browser/services/Services';
-import { ILinkifier2 } from 'browser/Types';
+import { IBufferRange, ILinkifier2 } from 'browser/Types';
import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';
import { getCoordsRelativeToElement } from 'browser/input/Mouse';
import { moveToCellSequence } from 'browser/input/MoveToCell';
@@ -1002,8 +1002,12 @@ export class SelectionService extends Disposable implements ISelectionService {
*/
protected _selectLineAt(line: number): void {
const wrappedRange = this._bufferService.buffer.getWrappedRangeForLine(line);
+ const range: IBufferRange = {
+ start: { x: 0, y: wrappedRange.first },
+ end: { x: this._bufferService.cols - 1, y: wrappedRange.last }
+ };
this._model.selectionStart = [0, wrappedRange.first];
- this._model.selectionEnd = [this._bufferService.cols, wrappedRange.last];
- this._model.selectionStartLength = 0;
+ this._model.selectionEnd = undefined;
+ this._model.selectionStartLength = getRangeLength(range, this._bufferService.cols);
}
}
From c10a451381ca0ae2fe8695a12f2c7de293d9d76f Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Wed, 2 Mar 2022 06:59:57 -0800
Subject: [PATCH 003/245] Bring back getOption/setOption api test
---
test/api/Terminal.api.ts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index cc3b78be..ee6a0cfa 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -154,6 +154,13 @@ describe('API Integration Tests', function(): void {
}
});
+ it('getOption, setOption', async () => {
+ await openTerminal(page);
+ assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'canvas');
+ await page.evaluate(`window.term.setOption('rendererType', 'dom')`);
+ assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'dom');
+ });
+
describe('options', () => {
it('getter', async () => {
await openTerminal(page);
From 4799832a33521e2378ba4a56268679626e5e95fc Mon Sep 17 00:00:00 2001
From: Simon Lamon
Date: Wed, 2 Mar 2022 16:17:31 +0000
Subject: [PATCH 004/245] Keep the initial wrapped line upon dragging
---
src/browser/selection/SelectionModel.test.ts | 6 ++++++
src/browser/selection/SelectionModel.ts | 7 ++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/browser/selection/SelectionModel.test.ts b/src/browser/selection/SelectionModel.test.ts
index 410902d7..5ce3e316 100644
--- a/src/browser/selection/SelectionModel.test.ts
+++ b/src/browser/selection/SelectionModel.test.ts
@@ -116,6 +116,12 @@ describe('SelectionModel', () => {
model.selectionStartLength = 4;
assert.deepEqual(model.finalSelectionEnd, [2, 3]);
});
+ it('should return the end on a different row when start + length overflows onto a following row with selectionEnd inbetween', () => {
+ model.selectionStart = [78, 2];
+ model.selectionEnd = [79, 2];
+ model.selectionStartLength = 4;
+ assert.deepEqual(model.finalSelectionEnd, [2, 3]);
+ });
it('should return selection end if selection end is after selection start + length', () => {
model.selectionStart = [2, 2];
model.selectionStartLength = 2;
diff --git a/src/browser/selection/SelectionModel.ts b/src/browser/selection/SelectionModel.ts
index 1d84446a..6c8abbfd 100644
--- a/src/browser/selection/SelectionModel.ts
+++ b/src/browser/selection/SelectionModel.ts
@@ -92,7 +92,12 @@ export class SelectionModel {
if (this.selectionStartLength) {
// Select the larger of the two when start and end are on the same line
if (this.selectionEnd[1] === this.selectionStart[1]) {
- return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]];
+ // Keep the whole wrapped word/line selected if the content wraps multiple lines
+ const startPlusLength = this.selectionStart[0] + this.selectionStartLength;
+ if (startPlusLength > this._bufferService.cols) {
+ return [startPlusLength % this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols)];
+ }
+ return [Math.max(startPlusLength, this.selectionEnd[0]), this.selectionEnd[1]];
}
}
return this.selectionEnd;
From ff6fa739c43165ad187683f6bdca0481ca4b7c93 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Wed, 2 Mar 2022 17:58:27 +0000
Subject: [PATCH 005/245] update addons
---
addons/xterm-addon-ligatures/package.json | 2 +-
addons/xterm-addon-serialize/package.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/addons/xterm-addon-ligatures/package.json b/addons/xterm-addon-ligatures/package.json
index f3c3aeff..7e5e5e90 100644
--- a/addons/xterm-addon-ligatures/package.json
+++ b/addons/xterm-addon-ligatures/package.json
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-ligatures",
- "version": "0.5.2",
+ "version": "0.5.3",
"description": "Add support for programming ligatures to xterm.js",
"author": {
"name": "The xterm.js authors",
diff --git a/addons/xterm-addon-serialize/package.json b/addons/xterm-addon-serialize/package.json
index 1565a7e4..01a8b68f 100644
--- a/addons/xterm-addon-serialize/package.json
+++ b/addons/xterm-addon-serialize/package.json
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-serialize",
- "version": "0.6.1",
+ "version": "0.6.2",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
From f5b66775db75ed7416b20b4fd37766d41fe25fea Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 2 Mar 2022 12:36:35 -0600
Subject: [PATCH 006/245] during buffer clear, don't dispose of markers on
first line
---
src/common/buffer/Buffer.ts | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts
index 7596fef3..f7dc97d3 100644
--- a/src/common/buffer/Buffer.ts
+++ b/src/common/buffer/Buffer.ts
@@ -595,10 +595,14 @@ export class Buffer implements IBuffer {
}
}
} else {
- for (const marker of this.markers) {
- marker.dispose();
+ // buffer has been cleared
+ // don't dispose of markers on the current line (0)
+ for (let i = 0; i < this.markers.length; i++) {
+ if (this.markers[i].line !== 0) {
+ this.markers[i].dispose();
+ this.markers.splice(i--, 1);
+ }
}
- this.markers = [];
}
this._isClearing = false;
}
From 7bf12f9123a0f8c133b1fdf41b74b72b0eab341c Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Wed, 2 Mar 2022 20:29:31 +0000
Subject: [PATCH 007/245] break into two methods
---
src/common/buffer/Buffer.ts | 21 ++++++++++++++-------
src/common/buffer/Types.d.ts | 3 ++-
2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts
index f7dc97d3..a4cc4603 100644
--- a/src/common/buffer/Buffer.ts
+++ b/src/common/buffer/Buffer.ts
@@ -585,24 +585,31 @@ export class Buffer implements IBuffer {
return x >= this._cols ? this._cols - 1 : x < 0 ? 0 : x;
}
- public clearMarkers(y?: number): void {
+ /**
+ * Clears markers on line @param y
+ */
+ public clearMarkers(y: number): void {
this._isClearing = true;
- if (y !== undefined) {
for (let i = 0; i < this.markers.length; i++) {
if (this.markers[i].line === y) {
this.markers[i].dispose();
this.markers.splice(i--, 1);
}
}
- } else {
- // buffer has been cleared
- // don't dispose of markers on the current line (0)
+ this._isClearing = false;
+ }
+
+ /**
+ * Clears markers on all lines except for
+ * those on @param excludeY
+ */
+ public clearAllMarkers(excludeY: number): void {
+ this._isClearing = true;
for (let i = 0; i < this.markers.length; i++) {
- if (this.markers[i].line !== 0) {
+ if (this.markers[i].line !== excludeY) {
this.markers[i].dispose();
this.markers.splice(i--, 1);
}
- }
}
this._isClearing = false;
}
diff --git a/src/common/buffer/Types.d.ts b/src/common/buffer/Types.d.ts
index 36b70b7f..f26b4b26 100644
--- a/src/common/buffer/Types.d.ts
+++ b/src/common/buffer/Types.d.ts
@@ -45,7 +45,8 @@ export interface IBuffer {
getNullCell(attr?: IAttributeData): ICellData;
getWhitespaceCell(attr?: IAttributeData): ICellData;
addMarker(y: number): IMarker;
- clearMarkers(y?: number): void;
+ clearMarkers(y: number): void;
+ clearAllMarkers(excludeY: number): void;
}
export interface IBufferSet extends IDisposable {
From 85dd6a9e87bf61ace28be0ae7a13a6fba2cb7838 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Wed, 2 Mar 2022 15:18:24 -0600
Subject: [PATCH 008/245] Update src/common/buffer/Buffer.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/common/buffer/Buffer.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts
index a4cc4603..8693736c 100644
--- a/src/common/buffer/Buffer.ts
+++ b/src/common/buffer/Buffer.ts
@@ -586,7 +586,8 @@ export class Buffer implements IBuffer {
}
/**
- * Clears markers on line @param y
+ * Clears markers on single line.
+ * @param y The line to clear.
*/
public clearMarkers(y: number): void {
this._isClearing = true;
From 1af22b24c021d6dba1cd94dd2346ab48f03345f0 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Wed, 2 Mar 2022 15:18:29 -0600
Subject: [PATCH 009/245] Update src/common/buffer/Buffer.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/common/buffer/Buffer.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts
index 8693736c..bf9d24ce 100644
--- a/src/common/buffer/Buffer.ts
+++ b/src/common/buffer/Buffer.ts
@@ -601,8 +601,8 @@ export class Buffer implements IBuffer {
}
/**
- * Clears markers on all lines except for
- * those on @param excludeY
+ * Clears markers on all lines except for those on a particular line.
+ * @param excludeY The line to exclude.
*/
public clearAllMarkers(excludeY: number): void {
this._isClearing = true;
From e75b47ca015addc198ee194aa52306903a42d0b8 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 2 Mar 2022 15:21:17 -0600
Subject: [PATCH 010/245] use clearAllMarkers
---
src/browser/Terminal.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 703c995b..08963933 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -1301,7 +1301,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
// Don't clear if it's already clear
return;
}
- this.buffer.clearMarkers();
+ this.buffer.clearAllMarkers(0);
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
this.buffer.lines.length = 1;
this.buffer.ydisp = 0;
From 82706e77dbba4b69843c785a3ae6bc13723f9d41 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 2 Mar 2022 20:19:49 -0600
Subject: [PATCH 011/245] add to mockbuffer
---
src/browser/TestUtils.test.ts | 5 ++++-
src/common/buffer/Buffer.ts | 22 +++++++++++-----------
2 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts
index 10a1435b..ac048be6 100644
--- a/src/browser/TestUtils.test.ts
+++ b/src/browser/TestUtils.test.ts
@@ -257,7 +257,10 @@ export class MockBuffer implements IBuffer {
public getWhitespaceCell(attr?: IAttributeData): ICellData {
throw new Error('Method not implemented.');
}
- public clearMarkers(): void {
+ public clearMarkers(y: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public clearAllMarkers(excludeY: number): void {
throw new Error('Method not implemented.');
}
}
diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts
index bf9d24ce..ab295784 100644
--- a/src/common/buffer/Buffer.ts
+++ b/src/common/buffer/Buffer.ts
@@ -591,12 +591,12 @@ export class Buffer implements IBuffer {
*/
public clearMarkers(y: number): void {
this._isClearing = true;
- for (let i = 0; i < this.markers.length; i++) {
- if (this.markers[i].line === y) {
- this.markers[i].dispose();
- this.markers.splice(i--, 1);
- }
+ for (let i = 0; i < this.markers.length; i++) {
+ if (this.markers[i].line === y) {
+ this.markers[i].dispose();
+ this.markers.splice(i--, 1);
}
+ }
this._isClearing = false;
}
@@ -604,13 +604,13 @@ export class Buffer implements IBuffer {
* Clears markers on all lines except for those on a particular line.
* @param excludeY The line to exclude.
*/
- public clearAllMarkers(excludeY: number): void {
+ public clearAllMarkers(excludeY: number): void {
this._isClearing = true;
- for (let i = 0; i < this.markers.length; i++) {
- if (this.markers[i].line !== excludeY) {
- this.markers[i].dispose();
- this.markers.splice(i--, 1);
- }
+ for (let i = 0; i < this.markers.length; i++) {
+ if (this.markers[i].line !== excludeY) {
+ this.markers[i].dispose();
+ this.markers.splice(i--, 1);
+ }
}
this._isClearing = false;
}
From 2263a1579edc89fd2d209c28bd6a71b5e61793d6 Mon Sep 17 00:00:00 2001
From: coderaiser
Date: Tue, 8 Mar 2022 20:13:40 +0200
Subject: [PATCH 012/245] =?UTF-8?q?chore:=20lint=20using=20=F0=9F=90=8APut?=
=?UTF-8?q?out?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../test/SearchAddon.api.ts | 2 +-
.../src/SerializeAddon.test.ts | 34 +++++++++----------
.../src/SerializeAddon.ts | 2 +-
.../test/SerializeAddon.api.ts | 2 +-
src/browser/ColorManager.ts | 2 +-
src/browser/renderer/atlas/CharAtlasUtils.ts | 2 +-
src/common/input/XParseColor.ts | 2 +-
7 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/addons/xterm-addon-search/test/SearchAddon.api.ts b/addons/xterm-addon-search/test/SearchAddon.api.ts
index 6d75f18f..7cb15c5d 100644
--- a/addons/xterm-addon-search/test/SearchAddon.api.ts
+++ b/addons/xterm-addon-search/test/SearchAddon.api.ts
@@ -134,7 +134,7 @@ describe('Search Tests', function(): void {
.replace(/\n/g, '\\n\\r');
}
fixture = fixture
- .replace(/'/g, '\\\'');
+ .replace(/'/g, `\\'`);
});
it('should find all occurrences using findNext', async () => {
await writeSync(page, fixture);
diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.test.ts b/addons/xterm-addon-serialize/src/SerializeAddon.test.ts
index caa29053..e35751d1 100644
--- a/addons/xterm-addon-serialize/src/SerializeAddon.test.ts
+++ b/addons/xterm-addon-serialize/src/SerializeAddon.test.ts
@@ -86,7 +86,7 @@ describe('xterm-addon-serialize html', () => {
it('empty terminal with selection turned off', () => {
const output = serializeAddon.serializeAsHTML();
assert.notEqual(output, '');
- assert.equal((output.match(new RegExp(' {10}<\/div>', 'g')) || []).length, 2);
+ assert.equal((output.match(/
{10}<\/span><\/div>/g) || []).length, 2);
});
it('empty terminal with no selection', () => {
@@ -103,84 +103,84 @@ describe('xterm-addon-serialize html', () => {
const output = serializeAddon.serializeAsHTML({
onlySelection: true
});
- assert.equal((output.match(new RegExp('terminal<\/span><\/div>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span><\/div>/g) || []).length, 1, output);
});
it('cells with bold styling', async () => {
await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with italic styling', async () => {
await writeP(terminal, ' ' + sgr('3') + 'terminal' + sgr('23') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with inverse styling', async () => {
await writeP(terminal, ' ' + sgr('7') + 'terminal' + sgr('27') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with underline styling', async () => {
await writeP(terminal, ' ' + sgr('4') + 'terminal' + sgr('24') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with invisible styling', async () => {
await writeP(terminal, ' ' + sgr('8') + 'terminal' + sgr('28') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with dim styling', async () => {
await writeP(terminal, ' ' + sgr('2') + 'terminal' + sgr('22') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with strikethrough styling', async () => {
await writeP(terminal, ' ' + sgr('9') + 'terminal' + sgr('29') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with combined styling', async () => {
await writeP(terminal, sgr('1') + ' ' + sgr('9') + 'termi' + sgr('22') + 'nal' + sgr('29') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp(' <\/span>', 'g')) || []).length, 1, output);
- assert.equal((output.match(new RegExp('termi<\/span>', 'g')) || []).length, 1, output);
- assert.equal((output.match(new RegExp('nal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/ <\/span>/g) || []).length, 1, output);
+ assert.equal((output.match(/termi<\/span>/g) || []).length, 1, output);
+ assert.equal((output.match(/nal<\/span>/g) || []).length, 1, output);
});
it('cells with color styling', async () => {
await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('cells with background styling', async () => {
await writeP(terminal, ' ' + sgr('48;5;46') + 'terminal' + sgr('49') + ' ');
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('terminal<\/span>', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
});
it('empty terminal with default options', async () => {
const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(new RegExp('color: #000000; background-color: #ffffff; font-family: courier-new, courier, monospace; font-size: 15px;', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/color: #000000; background-color: #ffffff; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
});
it('empty terminal with custom options', async () => {
@@ -193,13 +193,13 @@ describe('xterm-addon-serialize html', () => {
const output = serializeAddon.serializeAsHTML({
includeGlobalBackground: true
});
- assert.equal((output.match(new RegExp('color: #ff00ff; background-color: #00ff00; font-family: verdana; font-size: 20px;', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/color: #ff00ff; background-color: #00ff00; font-family: verdana; font-size: 20px;/g) || []).length, 1, output);
});
it('empty terminal with background included', async () => {
const output = serializeAddon.serializeAsHTML({
includeGlobalBackground: true
});
- assert.equal((output.match(new RegExp('color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;', 'g')) || []).length, 1, output);
+ assert.equal((output.match(/color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
});
});
diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.ts b/addons/xterm-addon-serialize/src/SerializeAddon.ts
index 25a42e65..8d6f8b36 100644
--- a/addons/xterm-addon-serialize/src/SerializeAddon.ts
+++ b/addons/xterm-addon-serialize/src/SerializeAddon.ts
@@ -544,7 +544,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
return target;
}
- targetLength = targetLength - target.length;
+ targetLength -= target.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length);
}
diff --git a/addons/xterm-addon-serialize/test/SerializeAddon.api.ts b/addons/xterm-addon-serialize/test/SerializeAddon.api.ts
index 334016e6..5c4ae437 100644
--- a/addons/xterm-addon-serialize/test/SerializeAddon.api.ts
+++ b/addons/xterm-addon-serialize/test/SerializeAddon.api.ts
@@ -14,7 +14,7 @@ let page: Page;
const width = 800;
const height = 600;
-const writeRawSync = (page: any, str: string): Promise => writeSync(page, '\' +' + JSON.stringify(str) + '+ \'');
+const writeRawSync = (page: any, str: string): Promise => writeSync(page, `' +` + JSON.stringify(str) + `+ '`);
const testNormalScreenEqual = async (page: any, str: string): Promise => {
await writeRawSync(page, str);
diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts
index b4b57c67..e7ac10ba 100644
--- a/src/browser/ColorManager.ts
+++ b/src/browser/ColorManager.ts
@@ -185,7 +185,7 @@ export class ColorManager implements IColorManager {
foreground: this.colors.foreground,
background: this.colors.background,
cursor: this.colors.cursor,
- ansi: [...this.colors.ansi]
+ ansi: this.colors.ansi.slice()
};
}
diff --git a/src/browser/renderer/atlas/CharAtlasUtils.ts b/src/browser/renderer/atlas/CharAtlasUtils.ts
index be92727a..696c6c12 100644
--- a/src/browser/renderer/atlas/CharAtlasUtils.ts
+++ b/src/browser/renderer/atlas/CharAtlasUtils.ts
@@ -16,7 +16,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
cursor: undefined,
cursorAccent: undefined,
selection: undefined,
- ansi: [...colors.ansi]
+ ansi: colors.ansi.slice()
};
return {
devicePixelRatio: window.devicePixelRatio,
diff --git a/src/common/input/XParseColor.ts b/src/common/input/XParseColor.ts
index 8c023a38..fd23ec4b 100644
--- a/src/common/input/XParseColor.ts
+++ b/src/common/input/XParseColor.ts
@@ -5,7 +5,7 @@
// 'rgb:' rule - matching: r/g/b | rr/gg/bb | rrr/ggg/bbb | rrrr/gggg/bbbb (hex digits)
-const RGB_REX = /^([\da-f]{1})\/([\da-f]{1})\/([\da-f]{1})$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/;
+const RGB_REX = /^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/;
// '#...' rule - matching any hex digits
const HASH_REX = /^[\da-f]+$/;
From a5a9c3b3684797725da03b402c946262b35bced1 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 8 Mar 2022 14:45:14 -0600
Subject: [PATCH 013/245] add scroll decorations
---
css/xterm.css | 14 ++++++
demo/client.ts | 9 ++++
demo/index.html | 1 +
src/browser/services/DecorationService.ts | 53 ++++++++++++++++++++++-
typings/xterm.d.ts | 6 +++
5 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/css/xterm.css b/css/xterm.css
index ab3965b4..3ae486c4 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -178,3 +178,17 @@
z-index: 6;
position: absolute;
}
+
+.xterm-decoration-scrollbar {
+ z-index: 7;
+ position: fixed;
+ top: 0px;
+ right: 0px;
+ width: 50px;
+}
+
+.xterm-decoration-scrollbar.demo-scrollbar {
+ height: 436px;
+ left: 872px;
+ top: 83px;
+}
diff --git a/demo/client.ts b/demo/client.ts
index aee23401..ec7e95ee 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -151,6 +151,7 @@ if (document.location.pathname === '/test') {
document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler);
document.getElementById('load-test').addEventListener('click', loadTest);
document.getElementById('add-decoration').addEventListener('click', addDecoration);
+ document.getElementById('add-scrollbar-decoration').addEventListener('click', addScrollbarDecoration);
}
function createTerminal(): void {
@@ -550,3 +551,11 @@ function addDecoration() {
decoration.element.style.backgroundColor = 'red';
});
}
+
+function addScrollbarDecoration() {
+ term.registerDecoration({marker: term.addMarker(1), scrollbarDecorationColor: 'red'});
+ term.registerDecoration({marker: term.addMarker(3), scrollbarDecorationColor: 'green'});
+ term.registerDecoration({marker: term.addMarker(5), scrollbarDecorationColor: 'blue'});
+ document.querySelector('.xterm-decoration-scrollbar').classList.add('demo-scrollbar');
+}
+
diff --git a/demo/index.html b/demo/index.html
index e024222c..dab52eec 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -69,6 +69,7 @@
+
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index ed3c7224..dfbcd918 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -3,6 +3,7 @@
* @license MIT
*/
+import { addDisposableDomListener } from 'browser/Lifecycle';
import { IDecorationService, IRenderService } from 'browser/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
@@ -17,7 +18,11 @@ export class DecorationService extends Disposable implements IDecorationService
private _renderService: IRenderService | undefined;
private _animationFrame: number | undefined;
- constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { super(); }
+ private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
+ private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
+ private _scrollbarDecorations: { marker: IMarker, color?: string}[] = [];
+
+ constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
public attachToDom(screenElement: HTMLElement, renderService: IRenderService): void {
this._renderService = renderService;
@@ -27,12 +32,22 @@ export class DecorationService extends Disposable implements IDecorationService
screenElement.appendChild(this._container);
this.register(this._renderService.onRenderedBufferChange(() => this.refresh()));
this.register(this._renderService.onDimensionsChange(() => this.refresh(true)));
+ this.register(addDisposableDomListener(window, 'resize', () => this._refreshScollbarDecorations()));
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
if (decorationOptions.marker.isDisposed || !this._container) {
return undefined;
}
+ if (decorationOptions.scrollbarDecorationColor) {
+ if (!this._scrollbarDecorationCanvas) {
+ this._scrollbarDecorationNode = document.createElement('canvas');
+ this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
+ this._screenElement?.parentElement?.appendChild(this._scrollbarDecorationNode);
+ this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
+ }
+ this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.scrollbarDecorationColor);
+ }
const decoration = this._instantiationService.createInstance(Decoration, decorationOptions, this._container);
this._decorations.push(decoration);
decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
@@ -57,6 +72,7 @@ export class DecorationService extends Disposable implements IDecorationService
for (const decoration of this._decorations) {
decoration.render(this._renderService, shouldRecreate);
}
+ this._refreshScollbarDecorations();
}
public dispose(): void {
@@ -67,6 +83,41 @@ export class DecorationService extends Disposable implements IDecorationService
this._screenElement.removeChild(this._container);
}
}
+
+ private _registerScrollbarDecoration(marker: IMarker, color?: string): HTMLCanvasElement | undefined {
+ this._scrollbarDecorations.push({ marker, color });
+ if (!this._scrollbarDecorationCanvas) {
+ return;
+ }
+ this._addScrollbarDecoration(marker, color);
+ }
+
+ private _refreshScollbarDecorations(): void {
+ if (!this._scrollbarDecorationCanvas) {
+ return;
+ }
+ if (this._scrollbarDecorationNode) {
+ this._scrollbarDecorationNode.style.width = '7px';
+ this._scrollbarDecorationNode.style.height = `${this._screenElement?.parentElement!.clientHeight}px`;
+ this._scrollbarDecorationNode.width = Math.floor(7*window.devicePixelRatio);
+ this._scrollbarDecorationNode.height = Math.floor(436*window.devicePixelRatio);
+ }
+ this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
+ for (const scrollbarDecoration of this._scrollbarDecorations) {
+ this._addScrollbarDecoration(scrollbarDecoration.marker, scrollbarDecoration.color);
+ }
+ }
+
+ private _addScrollbarDecoration(marker: IMarker, color?: string): void {
+ if (!this._scrollbarDecorationCanvas || !this._screenElement?.parentElement?.clientHeight) {
+ return;
+ }
+ this._scrollbarDecorationCanvas.lineWidth = 1;
+ if (color) {
+ this._scrollbarDecorationCanvas.strokeStyle = color;
+ }
+ this._scrollbarDecorationCanvas.strokeRect(0, (marker.line / (this._bufferService.buffers.active.lines.length) * Math.floor(436*window.devicePixelRatio)), Math.floor(7*window.devicePixelRatio), window.devicePixelRatio);
+ }
}
export class Decoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 2cd4daa6..eb31c4a0 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -470,6 +470,12 @@ declare module 'xterm' {
* cell height
*/
height?: number;
+
+ /**
+ * When provided, renders the decoration in the scrollbar
+ * with the given color
+ */
+ scrollbarDecorationColor?: string;
}
/**
From 4350c7ffcd5e52cddae85ee46cd5e9a3677e14e0 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 8 Mar 2022 16:23:51 -0600
Subject: [PATCH 014/245] get it to work
---
src/browser/Terminal.ts | 9 +++-
src/browser/services/DecorationService.ts | 60 +++++++++++++++++------
src/browser/services/Services.ts | 2 +-
3 files changed, 54 insertions(+), 17 deletions(-)
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 08963933..a8263cb5 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -70,6 +70,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _viewportElement: HTMLElement | undefined;
private _helperContainer: HTMLElement | undefined;
private _compositionView: HTMLElement | undefined;
+ private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
// private _visualBellTimer: number;
@@ -471,6 +472,12 @@ export class Terminal extends CoreTerminal implements ITerminal {
this._viewportElement = document.createElement('div');
this._viewportElement.classList.add('xterm-viewport');
fragment.appendChild(this._viewportElement);
+
+ //TODO: make this opt in, must be done before the scroll area in order to show up
+ this._scrollbarDecorationNode = document.createElement('canvas');
+ this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
+ this._viewportElement?.appendChild(this._scrollbarDecorationNode);
+
this._viewportScrollArea = document.createElement('div');
this._viewportScrollArea.classList.add('xterm-scroll-area');
this._viewportElement.appendChild(this._viewportScrollArea);
@@ -577,7 +584,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
- this.decorationService.attachToDom(this.screenElement, this._renderService, this._bufferService);
+ this.decorationService.attachToDom(this._scrollbarDecorationNode, this.screenElement, this._viewportElement, this._renderService);
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index dfbcd918..d5b9bbc8 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -15,6 +15,7 @@ export class DecorationService extends Disposable implements IDecorationService
private readonly _decorations: Decoration[] = [];
private _container: HTMLElement | undefined;
private _screenElement: HTMLElement | undefined;
+ private _viewportElement: HTMLElement | undefined;
private _renderService: IRenderService | undefined;
private _animationFrame: number | undefined;
@@ -24,9 +25,11 @@ export class DecorationService extends Disposable implements IDecorationService
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
- public attachToDom(screenElement: HTMLElement, renderService: IRenderService): void {
+ public attachToDom(scrollbarDecorationNode: HTMLCanvasElement, screenElement: HTMLElement, viewportElement: HTMLElement, renderService: IRenderService): void {
this._renderService = renderService;
this._screenElement = screenElement;
+ this._viewportElement = viewportElement;
+ this._scrollbarDecorationNode = scrollbarDecorationNode;
this._container = document.createElement('div');
this._container.classList.add('xterm-decoration-container');
screenElement.appendChild(this._container);
@@ -36,17 +39,14 @@ export class DecorationService extends Disposable implements IDecorationService
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (decorationOptions.marker.isDisposed || !this._container) {
+ if (decorationOptions.marker.isDisposed || !this._container || !this._scrollbarDecorationNode) {
return undefined;
}
if (decorationOptions.scrollbarDecorationColor) {
if (!this._scrollbarDecorationCanvas) {
- this._scrollbarDecorationNode = document.createElement('canvas');
- this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
- this._screenElement?.parentElement?.appendChild(this._scrollbarDecorationNode);
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
}
- this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.scrollbarDecorationColor);
+ return this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.scrollbarDecorationColor);
}
const decoration = this._instantiationService.createInstance(Decoration, decorationOptions, this._container);
this._decorations.push(decoration);
@@ -82,23 +82,23 @@ export class DecorationService extends Disposable implements IDecorationService
if (this._screenElement && this._container && this._screenElement.contains(this._container)) {
this._screenElement.removeChild(this._container);
}
+ this._scrollbarDecorations = [];
+ this._scrollbarDecorationNode?.remove();
}
- private _registerScrollbarDecoration(marker: IMarker, color?: string): HTMLCanvasElement | undefined {
+ private _registerScrollbarDecoration(marker: IMarker, color?: string): IDecoration | undefined {
this._scrollbarDecorations.push({ marker, color });
- if (!this._scrollbarDecorationCanvas) {
- return;
- }
- this._addScrollbarDecoration(marker, color);
+ //TODO: marker on dispose
+ return this._addScrollbarDecoration(marker, color);
}
private _refreshScollbarDecorations(): void {
- if (!this._scrollbarDecorationCanvas) {
+ if (!this._scrollbarDecorationCanvas || !this._viewportElement) {
return;
}
if (this._scrollbarDecorationNode) {
this._scrollbarDecorationNode.style.width = '7px';
- this._scrollbarDecorationNode.style.height = `${this._screenElement?.parentElement!.clientHeight}px`;
+ this._scrollbarDecorationNode.style.height = `${this._viewportElement.clientHeight}px`;
this._scrollbarDecorationNode.width = Math.floor(7*window.devicePixelRatio);
this._scrollbarDecorationNode.height = Math.floor(436*window.devicePixelRatio);
}
@@ -108,8 +108,8 @@ export class DecorationService extends Disposable implements IDecorationService
}
}
- private _addScrollbarDecoration(marker: IMarker, color?: string): void {
- if (!this._scrollbarDecorationCanvas || !this._screenElement?.parentElement?.clientHeight) {
+ private _addScrollbarDecoration(marker: IMarker, color?: string): IDecoration | undefined {
+ if (!this._scrollbarDecorationCanvas || !this._viewportElement?.clientHeight) {
return;
}
this._scrollbarDecorationCanvas.lineWidth = 1;
@@ -117,8 +117,38 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationCanvas.strokeStyle = color;
}
this._scrollbarDecorationCanvas.strokeRect(0, (marker.line / (this._bufferService.buffers.active.lines.length) * Math.floor(436*window.devicePixelRatio)), Math.floor(7*window.devicePixelRatio), window.devicePixelRatio);
+ if (this._scrollbarDecorationNode) {
+ return new ScrollbarDecoration({marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode);
+ }
+ return undefined;
}
}
+export class ScrollbarDecoration extends Disposable implements IDecoration {
+ private readonly _marker: IMarker;
+ private _element: HTMLElement | undefined;
+
+ public isDisposed: boolean = false;
+
+ public get element(): HTMLElement | undefined { return this._element; }
+ public get marker(): IMarker { return this._marker; }
+
+ private _onDispose = new EventEmitter();
+ public get onDispose(): IEvent { return this._onDispose.event; }
+
+ private _onRender = new EventEmitter();
+ public get onRender(): IEvent { return this._onRender.event; }
+
+ constructor(
+ options: IDecorationOptions,
+ element: HTMLCanvasElement
+ ) {
+ super();
+ this._marker = options.marker;
+ this._element = element;
+ this._marker.onDispose(() => this.dispose());
+ }
+}
+
export class Decoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
private _element: HTMLElement | undefined;
diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts
index 7faf3f0f..caf6ec5a 100644
--- a/src/browser/services/Services.ts
+++ b/src/browser/services/Services.ts
@@ -121,5 +121,5 @@ export const IDecorationService = createDecorator('Decoratio
export interface IDecorationService extends IDisposable {
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
refresh(): void;
- attachToDom(screenElement: HTMLElement, renderService: IRenderService, bufferService: IBufferService): void;
+ attachToDom(scrollbarDecorationNode: HTMLCanvasElement, screenElement: HTMLElement, viewportElement: HTMLElement, renderService: IRenderService): void;
}
From 3a499fc35dae6163d16230e4b806e8be202adb7d Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 8 Mar 2022 20:31:40 -0600
Subject: [PATCH 015/245] clear on dispose
---
demo/client.ts | 2 +-
src/browser/services/DecorationService.ts | 57 +++++++++++++----------
2 files changed, 34 insertions(+), 25 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index ec7e95ee..7eb9247a 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -553,9 +553,9 @@ function addDecoration() {
}
function addScrollbarDecoration() {
+ document.querySelector('.xterm-decoration-scrollbar').classList.add('demo-scrollbar');
term.registerDecoration({marker: term.addMarker(1), scrollbarDecorationColor: 'red'});
term.registerDecoration({marker: term.addMarker(3), scrollbarDecorationColor: 'green'});
term.registerDecoration({marker: term.addMarker(5), scrollbarDecorationColor: 'blue'});
- document.querySelector('.xterm-decoration-scrollbar').classList.add('demo-scrollbar');
}
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index d5b9bbc8..f21be5df 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -12,13 +12,14 @@ import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
export class DecorationService extends Disposable implements IDecorationService {
- private readonly _decorations: Decoration[] = [];
private _container: HTMLElement | undefined;
private _screenElement: HTMLElement | undefined;
private _viewportElement: HTMLElement | undefined;
private _renderService: IRenderService | undefined;
private _animationFrame: number | undefined;
+ private readonly _bufferDecorations: BufferDecoration[] = [];
+
private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
private _scrollbarDecorations: { marker: IMarker, color?: string}[] = [];
@@ -46,13 +47,14 @@ export class DecorationService extends Disposable implements IDecorationService
if (!this._scrollbarDecorationCanvas) {
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
}
+ this._refreshScollbarDecorations();
return this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.scrollbarDecorationColor);
}
- const decoration = this._instantiationService.createInstance(Decoration, decorationOptions, this._container);
- this._decorations.push(decoration);
- decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
+ const bufferDecoration = this._instantiationService.createInstance(BufferDecoration, decorationOptions, this._container);
+ this._bufferDecorations.push(bufferDecoration);
+ bufferDecoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(bufferDecoration), 1));
this._queueRefresh();
- return decoration;
+ return bufferDecoration;
}
private _queueRefresh(): void {
@@ -66,18 +68,13 @@ export class DecorationService extends Disposable implements IDecorationService
}
public refresh(shouldRecreate?: boolean): void {
- if (!this._renderService) {
- return;
- }
- for (const decoration of this._decorations) {
- decoration.render(this._renderService, shouldRecreate);
- }
+ this._refreshBufferDecorations(shouldRecreate);
this._refreshScollbarDecorations();
}
public dispose(): void {
- for (const decoration of this._decorations) {
- decoration.dispose();
+ for (const bufferDecoration of this._bufferDecorations) {
+ bufferDecoration.dispose();
}
if (this._screenElement && this._container && this._screenElement.contains(this._container)) {
this._screenElement.removeChild(this._container);
@@ -88,20 +85,26 @@ export class DecorationService extends Disposable implements IDecorationService
private _registerScrollbarDecoration(marker: IMarker, color?: string): IDecoration | undefined {
this._scrollbarDecorations.push({ marker, color });
- //TODO: marker on dispose
return this._addScrollbarDecoration(marker, color);
}
- private _refreshScollbarDecorations(): void {
- if (!this._scrollbarDecorationCanvas || !this._viewportElement) {
+ private _refreshBufferDecorations(shouldRecreate?: boolean): void {
+ if (!this._renderService) {
return;
}
- if (this._scrollbarDecorationNode) {
- this._scrollbarDecorationNode.style.width = '7px';
- this._scrollbarDecorationNode.style.height = `${this._viewportElement.clientHeight}px`;
- this._scrollbarDecorationNode.width = Math.floor(7*window.devicePixelRatio);
- this._scrollbarDecorationNode.height = Math.floor(436*window.devicePixelRatio);
+ for (const bufferDecoration of this._bufferDecorations) {
+ bufferDecoration.render(this._renderService, shouldRecreate);
}
+ }
+
+ private _refreshScollbarDecorations(): void {
+ if (!this._scrollbarDecorationCanvas || !this._viewportElement || !this._scrollbarDecorationNode) {
+ return;
+ }
+ this._scrollbarDecorationNode.style.width = '7px';
+ this._scrollbarDecorationNode.style.height = `${this._viewportElement.clientHeight}px`;
+ this._scrollbarDecorationNode.width = Math.floor(7*window.devicePixelRatio);
+ this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight*window.devicePixelRatio);
this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
for (const scrollbarDecoration of this._scrollbarDecorations) {
this._addScrollbarDecoration(scrollbarDecoration.marker, scrollbarDecoration.color);
@@ -116,9 +119,15 @@ export class DecorationService extends Disposable implements IDecorationService
if (color) {
this._scrollbarDecorationCanvas.strokeStyle = color;
}
- this._scrollbarDecorationCanvas.strokeRect(0, (marker.line / (this._bufferService.buffers.active.lines.length) * Math.floor(436*window.devicePixelRatio)), Math.floor(7*window.devicePixelRatio), window.devicePixelRatio);
+ this._scrollbarDecorationCanvas.strokeRect(
+ 0,
+ (marker.line / this._bufferService.buffers.active.lines.length) * Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio),
+ Math.floor(7 * window.devicePixelRatio),
+ window.devicePixelRatio
+ );
if (this._scrollbarDecorationNode) {
- return new ScrollbarDecoration({marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode);
+ const scrollbarDecoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode);
+ scrollbarDecoration.onDispose(() => this._scrollbarDecorationCanvas?.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height));
}
return undefined;
}
@@ -149,7 +158,7 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
}
}
-export class Decoration extends Disposable implements IDecoration {
+export class BufferDecoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
private _element: HTMLElement | undefined;
From 785a8190a665a408563487e1027131098be6a671 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 8 Mar 2022 20:41:49 -0600
Subject: [PATCH 016/245] clean up
---
src/browser/services/DecorationService.ts | 62 ++++++++++++++---------
1 file changed, 37 insertions(+), 25 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index f21be5df..bb5bd125 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -10,6 +10,10 @@ import { Disposable } from 'common/Lifecycle';
import { IBufferService, IInstantiationService } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
+const enum ScrollbarConstants {
+ WIDTH = 7
+}
+
export class DecorationService extends Disposable implements IDecorationService {
private _container: HTMLElement | undefined;
@@ -22,7 +26,7 @@ export class DecorationService extends Disposable implements IDecorationService
private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
- private _scrollbarDecorations: { marker: IMarker, color?: string}[] = [];
+ private _scrollbarDecorations: { marker: IMarker, color: string }[] = [];
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
@@ -57,16 +61,6 @@ export class DecorationService extends Disposable implements IDecorationService
return bufferDecoration;
}
- private _queueRefresh(): void {
- if (this._animationFrame !== undefined) {
- return;
- }
- this._animationFrame = window.requestAnimationFrame(() => {
- this.refresh();
- this._animationFrame = undefined;
- });
- }
-
public refresh(shouldRecreate?: boolean): void {
this._refreshBufferDecorations(shouldRecreate);
this._refreshScollbarDecorations();
@@ -83,9 +77,15 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationNode?.remove();
}
- private _registerScrollbarDecoration(marker: IMarker, color?: string): IDecoration | undefined {
- this._scrollbarDecorations.push({ marker, color });
- return this._addScrollbarDecoration(marker, color);
+
+ private _queueRefresh(): void {
+ if (this._animationFrame !== undefined) {
+ return;
+ }
+ this._animationFrame = window.requestAnimationFrame(() => {
+ this.refresh();
+ this._animationFrame = undefined;
+ });
}
private _refreshBufferDecorations(shouldRecreate?: boolean): void {
@@ -97,37 +97,49 @@ export class DecorationService extends Disposable implements IDecorationService
}
}
+ private _registerScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
+ this._scrollbarDecorations.push({ marker, color });
+ return this._addScrollbarDecoration(marker, color);
+ }
+
private _refreshScollbarDecorations(): void {
if (!this._scrollbarDecorationCanvas || !this._viewportElement || !this._scrollbarDecorationNode) {
return;
}
- this._scrollbarDecorationNode.style.width = '7px';
+ this._scrollbarDecorationNode.style.width = `${ScrollbarConstants.WIDTH}px`;
this._scrollbarDecorationNode.style.height = `${this._viewportElement.clientHeight}px`;
- this._scrollbarDecorationNode.width = Math.floor(7*window.devicePixelRatio);
- this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight*window.devicePixelRatio);
+ this._scrollbarDecorationNode.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
+ this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio);
this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
+
for (const scrollbarDecoration of this._scrollbarDecorations) {
this._addScrollbarDecoration(scrollbarDecoration.marker, scrollbarDecoration.color);
}
}
- private _addScrollbarDecoration(marker: IMarker, color?: string): IDecoration | undefined {
- if (!this._scrollbarDecorationCanvas || !this._viewportElement?.clientHeight) {
+ private _addScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
+ if (!this._scrollbarDecorationCanvas || !this._scrollbarDecorationNode) {
return;
}
this._scrollbarDecorationCanvas.lineWidth = 1;
- if (color) {
- this._scrollbarDecorationCanvas.strokeStyle = color;
- }
+ this._scrollbarDecorationCanvas.strokeStyle = color;
this._scrollbarDecorationCanvas.strokeRect(
0,
- (marker.line / this._bufferService.buffers.active.lines.length) * Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio),
- Math.floor(7 * window.devicePixelRatio),
+ this._scrollbarDecorationNode.height * (marker.line / this._bufferService.buffers.active.lines.length),
+ this._scrollbarDecorationNode.width,
window.devicePixelRatio
);
if (this._scrollbarDecorationNode) {
const scrollbarDecoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode);
- scrollbarDecoration.onDispose(() => this._scrollbarDecorationCanvas?.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height));
+ scrollbarDecoration.onDispose(() => {
+ this._scrollbarDecorationCanvas?.clearRect(
+ 0,
+ 0,
+ this._scrollbarDecorationCanvas.canvas.width,
+ this._scrollbarDecorationCanvas.canvas.height
+ );
+ });
+ return scrollbarDecoration;
}
return undefined;
}
From d46a68592575c533f11373dee04fbef6cb1b9d20 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 8 Mar 2022 20:49:32 -0600
Subject: [PATCH 017/245] more cleanup
---
src/browser/services/DecorationService.ts | 83 +++++++++++++----------
src/browser/services/Services.ts | 3 +-
2 files changed, 48 insertions(+), 38 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index bb5bd125..25d413fa 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -38,8 +38,8 @@ export class DecorationService extends Disposable implements IDecorationService
this._container = document.createElement('div');
this._container.classList.add('xterm-decoration-container');
screenElement.appendChild(this._container);
- this.register(this._renderService.onRenderedBufferChange(() => this.refresh()));
- this.register(this._renderService.onDimensionsChange(() => this.refresh(true)));
+ this.register(this._renderService.onRenderedBufferChange(() => this._refresh()));
+ this.register(this._renderService.onDimensionsChange(() => this._refresh(true)));
this.register(addDisposableDomListener(window, 'resize', () => this._refreshScollbarDecorations()));
}
@@ -48,22 +48,9 @@ export class DecorationService extends Disposable implements IDecorationService
return undefined;
}
if (decorationOptions.scrollbarDecorationColor) {
- if (!this._scrollbarDecorationCanvas) {
- this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
- }
- this._refreshScollbarDecorations();
return this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.scrollbarDecorationColor);
}
- const bufferDecoration = this._instantiationService.createInstance(BufferDecoration, decorationOptions, this._container);
- this._bufferDecorations.push(bufferDecoration);
- bufferDecoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(bufferDecoration), 1));
- this._queueRefresh();
- return bufferDecoration;
- }
-
- public refresh(shouldRecreate?: boolean): void {
- this._refreshBufferDecorations(shouldRecreate);
- this._refreshScollbarDecorations();
+ return this._registerBufferDecoration(decorationOptions);
}
public dispose(): void {
@@ -77,45 +64,44 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationNode?.remove();
}
+ private _refresh(shouldRecreate?: boolean): void {
+ this._refreshBufferDecorations(shouldRecreate);
+ this._refreshScollbarDecorations();
+ }
private _queueRefresh(): void {
if (this._animationFrame !== undefined) {
return;
}
this._animationFrame = window.requestAnimationFrame(() => {
- this.refresh();
+ this._refresh();
this._animationFrame = undefined;
});
}
- private _refreshBufferDecorations(shouldRecreate?: boolean): void {
- if (!this._renderService) {
+ private _registerBufferDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
+ if (!this._container) {
return;
}
- for (const bufferDecoration of this._bufferDecorations) {
- bufferDecoration.render(this._renderService, shouldRecreate);
- }
+ const bufferDecoration = this._instantiationService.createInstance(BufferDecoration, decorationOptions, this._container);
+ this._bufferDecorations.push(bufferDecoration);
+ bufferDecoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(bufferDecoration), 1));
+ this._queueRefresh();
+ return bufferDecoration;
}
private _registerScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
+ if (!this._scrollbarDecorationNode) {
+ return;
+ }
+ if (!this._scrollbarDecorationCanvas) {
+ this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
+ this._refreshScollbarDecorations();
+ }
this._scrollbarDecorations.push({ marker, color });
return this._addScrollbarDecoration(marker, color);
}
- private _refreshScollbarDecorations(): void {
- if (!this._scrollbarDecorationCanvas || !this._viewportElement || !this._scrollbarDecorationNode) {
- return;
- }
- this._scrollbarDecorationNode.style.width = `${ScrollbarConstants.WIDTH}px`;
- this._scrollbarDecorationNode.style.height = `${this._viewportElement.clientHeight}px`;
- this._scrollbarDecorationNode.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
- this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio);
- this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
-
- for (const scrollbarDecoration of this._scrollbarDecorations) {
- this._addScrollbarDecoration(scrollbarDecoration.marker, scrollbarDecoration.color);
- }
- }
private _addScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
if (!this._scrollbarDecorationCanvas || !this._scrollbarDecorationNode) {
@@ -143,6 +129,31 @@ export class DecorationService extends Disposable implements IDecorationService
}
return undefined;
}
+
+ private _refreshBufferDecorations(shouldRecreate?: boolean): void {
+ if (!this._renderService) {
+ return;
+ }
+ for (const bufferDecoration of this._bufferDecorations) {
+ bufferDecoration.render(this._renderService, shouldRecreate);
+ }
+ }
+
+ private _refreshScollbarDecorations(): void {
+ if (!this._scrollbarDecorationCanvas || !this._viewportElement || !this._scrollbarDecorationNode) {
+ return;
+ }
+ this._scrollbarDecorationNode.style.width = `${ScrollbarConstants.WIDTH}px`;
+ this._scrollbarDecorationNode.style.height = `${this._viewportElement.clientHeight}px`;
+ this._scrollbarDecorationNode.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
+ this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio);
+ this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
+
+ for (const scrollbarDecoration of this._scrollbarDecorations) {
+ this._addScrollbarDecoration(scrollbarDecoration.marker, scrollbarDecoration.color);
+ }
+ }
+
}
export class ScrollbarDecoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts
index caf6ec5a..35943897 100644
--- a/src/browser/services/Services.ts
+++ b/src/browser/services/Services.ts
@@ -119,7 +119,6 @@ export interface ICharacterJoinerService {
export const IDecorationService = createDecorator('DecorationService');
export interface IDecorationService extends IDisposable {
- registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
- refresh(): void;
attachToDom(scrollbarDecorationNode: HTMLCanvasElement, screenElement: HTMLElement, viewportElement: HTMLElement, renderService: IRenderService): void;
+ registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
}
From 89112fb1aef20c993bd3a723683f8593f64797e3 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 8 Mar 2022 21:31:42 -0600
Subject: [PATCH 018/245] refactor to return / store IDecorations
---
src/browser/services/DecorationService.ts | 72 +++++++++++++----------
1 file changed, 40 insertions(+), 32 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 25d413fa..33aec4c7 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -23,10 +23,10 @@ export class DecorationService extends Disposable implements IDecorationService
private _animationFrame: number | undefined;
private readonly _bufferDecorations: BufferDecoration[] = [];
+ private _scrollDecorations: ScrollbarDecoration[] = [];
private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
- private _scrollbarDecorations: { marker: IMarker, color: string }[] = [];
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
@@ -60,7 +60,10 @@ export class DecorationService extends Disposable implements IDecorationService
if (this._screenElement && this._container && this._screenElement.contains(this._container)) {
this._screenElement.removeChild(this._container);
}
- this._scrollbarDecorations = [];
+ for (const scrollbarDecoration of this._scrollDecorations) {
+ scrollbarDecoration.dispose();
+ }
+ this._scrollDecorations = [];
this._scrollbarDecorationNode?.remove();
}
@@ -91,43 +94,26 @@ export class DecorationService extends Disposable implements IDecorationService
}
private _registerScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
- if (!this._scrollbarDecorationNode) {
+ if (!this._scrollbarDecorationNode || !this._viewportElement) {
return;
}
if (!this._scrollbarDecorationCanvas) {
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
this._refreshScollbarDecorations();
}
- this._scrollbarDecorations.push({ marker, color });
- return this._addScrollbarDecoration(marker, color);
- }
-
-
- private _addScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
- if (!this._scrollbarDecorationCanvas || !this._scrollbarDecorationNode) {
- return;
- }
- this._scrollbarDecorationCanvas.lineWidth = 1;
- this._scrollbarDecorationCanvas.strokeStyle = color;
- this._scrollbarDecorationCanvas.strokeRect(
+ this._scrollbarDecorationCanvas!.lineWidth = 1;
+ this._scrollbarDecorationCanvas!.strokeStyle = color;
+ this._scrollbarDecorationCanvas!.strokeRect(
0,
this._scrollbarDecorationNode.height * (marker.line / this._bufferService.buffers.active.lines.length),
this._scrollbarDecorationNode.width,
window.devicePixelRatio
);
if (this._scrollbarDecorationNode) {
- const scrollbarDecoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode);
- scrollbarDecoration.onDispose(() => {
- this._scrollbarDecorationCanvas?.clearRect(
- 0,
- 0,
- this._scrollbarDecorationCanvas.canvas.width,
- this._scrollbarDecorationCanvas.canvas.height
- );
- });
+ const scrollbarDecoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!, this._viewportElement, this._bufferService);
+ this._scrollDecorations.push(scrollbarDecoration);
return scrollbarDecoration;
}
- return undefined;
}
private _refreshBufferDecorations(shouldRecreate?: boolean): void {
@@ -148,21 +134,22 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationNode.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio);
this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
-
- for (const scrollbarDecoration of this._scrollbarDecorations) {
- this._addScrollbarDecoration(scrollbarDecoration.marker, scrollbarDecoration.color);
+ for (const decoration of this._scrollDecorations) {
+ decoration.render();
}
}
}
export class ScrollbarDecoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
- private _element: HTMLElement | undefined;
+ private _canvas: HTMLCanvasElement | undefined;
+ private _color: string | undefined;
public isDisposed: boolean = false;
- public get element(): HTMLElement | undefined { return this._element; }
+ public get element(): HTMLCanvasElement { return this._canvas!; }
public get marker(): IMarker { return this._marker; }
+ public get color(): string { return this._color!; }
private _onDispose = new EventEmitter();
public get onDispose(): IEvent { return this._onDispose.event; }
@@ -172,12 +159,33 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
constructor(
options: IDecorationOptions,
- element: HTMLCanvasElement
+ canvas: HTMLCanvasElement,
+ private readonly _ctx: CanvasRenderingContext2D,
+ private readonly _viewport: HTMLElement,
+ private readonly _bufferService: IBufferService
) {
super();
this._marker = options.marker;
- this._element = element;
+ this._canvas = canvas;
+ this._color = options.scrollbarDecorationColor;
this._marker.onDispose(() => this.dispose());
+ this.render();
+ }
+ public render(): void {
+ this._ctx.lineWidth = 1;
+ this._ctx.strokeStyle = this.color;
+ this._ctx.strokeRect(
+ 0,
+ this.element.height * (this.marker.line / this._bufferService.buffers.active.lines.length),
+ this.element.width,
+ window.devicePixelRatio
+ );
+ }
+
+ public override dispose(): void {
+ this._ctx.clearRect(0, 0, this._ctx.canvas.width, this._ctx.canvas.height);
+
+ super.dispose();
}
}
From 249e73cd255ec71c3e6e9ae37149e02910a8ec6b Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 8 Mar 2022 21:35:31 -0600
Subject: [PATCH 019/245] delete unused code
---
src/browser/services/DecorationService.ts | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 33aec4c7..db8aaee2 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -101,19 +101,9 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
this._refreshScollbarDecorations();
}
- this._scrollbarDecorationCanvas!.lineWidth = 1;
- this._scrollbarDecorationCanvas!.strokeStyle = color;
- this._scrollbarDecorationCanvas!.strokeRect(
- 0,
- this._scrollbarDecorationNode.height * (marker.line / this._bufferService.buffers.active.lines.length),
- this._scrollbarDecorationNode.width,
- window.devicePixelRatio
- );
- if (this._scrollbarDecorationNode) {
- const scrollbarDecoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!, this._viewportElement, this._bufferService);
- this._scrollDecorations.push(scrollbarDecoration);
- return scrollbarDecoration;
- }
+ const scrollbarDecoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!, this._bufferService);
+ this._scrollDecorations.push(scrollbarDecoration);
+ return scrollbarDecoration;
}
private _refreshBufferDecorations(shouldRecreate?: boolean): void {
@@ -161,7 +151,6 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
options: IDecorationOptions,
canvas: HTMLCanvasElement,
private readonly _ctx: CanvasRenderingContext2D,
- private readonly _viewport: HTMLElement,
private readonly _bufferService: IBufferService
) {
super();
From 1948ec886f7df322b3ec14fbaa87eaba7acf2872 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 9 Mar 2022 08:27:50 -0600
Subject: [PATCH 020/245] only clear relevant part of canvas on marker dispose
---
src/browser/services/DecorationService.ts | 40 ++++++++++++++---------
1 file changed, 25 insertions(+), 15 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index db8aaee2..aa3a6e29 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -23,7 +23,7 @@ export class DecorationService extends Disposable implements IDecorationService
private _animationFrame: number | undefined;
private readonly _bufferDecorations: BufferDecoration[] = [];
- private _scrollDecorations: ScrollbarDecoration[] = [];
+ private _scrollbarDecorations: ScrollbarDecoration[] = [];
private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
@@ -60,10 +60,10 @@ export class DecorationService extends Disposable implements IDecorationService
if (this._screenElement && this._container && this._screenElement.contains(this._container)) {
this._screenElement.removeChild(this._container);
}
- for (const scrollbarDecoration of this._scrollDecorations) {
+ for (const scrollbarDecoration of this._scrollbarDecorations) {
scrollbarDecoration.dispose();
}
- this._scrollDecorations = [];
+ this._scrollbarDecorations = [];
this._scrollbarDecorationNode?.remove();
}
@@ -86,11 +86,11 @@ export class DecorationService extends Disposable implements IDecorationService
if (!this._container) {
return;
}
- const bufferDecoration = this._instantiationService.createInstance(BufferDecoration, decorationOptions, this._container);
- this._bufferDecorations.push(bufferDecoration);
- bufferDecoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(bufferDecoration), 1));
+ const decoration = this._instantiationService.createInstance(BufferDecoration, decorationOptions, this._container);
+ this._bufferDecorations.push(decoration);
+ decoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(decoration), 1));
this._queueRefresh();
- return bufferDecoration;
+ return decoration;
}
private _registerScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
@@ -101,17 +101,18 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
this._refreshScollbarDecorations();
}
- const scrollbarDecoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!, this._bufferService);
- this._scrollDecorations.push(scrollbarDecoration);
- return scrollbarDecoration;
+ const decoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!, this._bufferService);
+ decoration.onDispose(() => this._scrollbarDecorations.splice(this._scrollbarDecorations.indexOf(decoration), 1));
+ this._scrollbarDecorations.push(decoration);
+ return decoration;
}
private _refreshBufferDecorations(shouldRecreate?: boolean): void {
if (!this._renderService) {
return;
}
- for (const bufferDecoration of this._bufferDecorations) {
- bufferDecoration.render(this._renderService, shouldRecreate);
+ for (const decoration of this._bufferDecorations) {
+ decoration.render(this._renderService, shouldRecreate);
}
}
@@ -124,7 +125,7 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationNode.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio);
this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
- for (const decoration of this._scrollDecorations) {
+ for (const decoration of this._scrollbarDecorations) {
decoration.render();
}
}
@@ -172,8 +173,17 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
}
public override dispose(): void {
- this._ctx.clearRect(0, 0, this._ctx.canvas.width, this._ctx.canvas.height);
-
+ if (this._isDisposed) {
+ return;
+ }
+ this._ctx.clearRect(
+ 0,
+ this.element.height * (this.marker.line / this._bufferService.buffers.active.lines.length),
+ this.element.width,
+ window.devicePixelRatio
+ );
+ this.isDisposed = true;
+ this._onDispose.fire();
super.dispose();
}
}
From cfd7912afa1c4c35fdebb5cd1217ec147bb28066 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 9 Mar 2022 08:40:40 -0600
Subject: [PATCH 021/245] enable registering a decoration before attach to dom
has happened
---
src/browser/Terminal.ts | 4 +-
src/browser/services/DecorationService.ts | 51 ++++++++++++-----------
src/browser/services/Services.ts | 2 +-
3 files changed, 29 insertions(+), 28 deletions(-)
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index a8263cb5..7dec6323 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -473,7 +473,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this._viewportElement.classList.add('xterm-viewport');
fragment.appendChild(this._viewportElement);
- //TODO: make this opt in, must be done before the scroll area in order to show up
+ // TODO: make this opt in, must be done before the scroll area in order to show up
this._scrollbarDecorationNode = document.createElement('canvas');
this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
this._viewportElement?.appendChild(this._scrollbarDecorationNode);
@@ -584,7 +584,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
- this.decorationService.attachToDom(this._scrollbarDecorationNode, this.screenElement, this._viewportElement, this._renderService);
+ this.decorationService.attachToDom(this._renderService, this.screenElement, this._viewportElement, this._scrollbarDecorationNode);
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index aa3a6e29..6580a2c9 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -7,6 +7,7 @@ import { addDisposableDomListener } from 'browser/Lifecycle';
import { IDecorationService, IRenderService } from 'browser/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
+import { BufferService } from 'common/services/BufferService';
import { IBufferService, IInstantiationService } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
@@ -16,35 +17,33 @@ const enum ScrollbarConstants {
export class DecorationService extends Disposable implements IDecorationService {
- private _container: HTMLElement | undefined;
- private _screenElement: HTMLElement | undefined;
- private _viewportElement: HTMLElement | undefined;
private _renderService: IRenderService | undefined;
private _animationFrame: number | undefined;
+ private _screenElement: HTMLElement | undefined;
+ private _viewportElement: HTMLElement | undefined;
+ private _bufferDecorationContainer: HTMLElement | undefined;
+ private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
+ private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
+
private readonly _bufferDecorations: BufferDecoration[] = [];
private _scrollbarDecorations: ScrollbarDecoration[] = [];
- private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
- private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
+ constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { super(); }
- constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
-
- public attachToDom(scrollbarDecorationNode: HTMLCanvasElement, screenElement: HTMLElement, viewportElement: HTMLElement, renderService: IRenderService): void {
+ public attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement, scrollbarDecorationNode: HTMLCanvasElement): void {
this._renderService = renderService;
this._screenElement = screenElement;
this._viewportElement = viewportElement;
this._scrollbarDecorationNode = scrollbarDecorationNode;
- this._container = document.createElement('div');
- this._container.classList.add('xterm-decoration-container');
- screenElement.appendChild(this._container);
+
this.register(this._renderService.onRenderedBufferChange(() => this._refresh()));
this.register(this._renderService.onDimensionsChange(() => this._refresh(true)));
this.register(addDisposableDomListener(window, 'resize', () => this._refreshScollbarDecorations()));
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (decorationOptions.marker.isDisposed || !this._container || !this._scrollbarDecorationNode) {
+ if (decorationOptions.marker.isDisposed) {
return undefined;
}
if (decorationOptions.scrollbarDecorationColor) {
@@ -57,12 +56,12 @@ export class DecorationService extends Disposable implements IDecorationService
for (const bufferDecoration of this._bufferDecorations) {
bufferDecoration.dispose();
}
- if (this._screenElement && this._container && this._screenElement.contains(this._container)) {
- this._screenElement.removeChild(this._container);
- }
for (const scrollbarDecoration of this._scrollbarDecorations) {
scrollbarDecoration.dispose();
}
+ if (this._screenElement && this._bufferDecorationContainer && this._screenElement.contains(this._bufferDecorationContainer)) {
+ this._screenElement.removeChild(this._bufferDecorationContainer);
+ }
this._scrollbarDecorations = [];
this._scrollbarDecorationNode?.remove();
}
@@ -83,10 +82,12 @@ export class DecorationService extends Disposable implements IDecorationService
}
private _registerBufferDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (!this._container) {
- return;
+ if (this._screenElement && !this._bufferDecorationContainer) {
+ this._bufferDecorationContainer = document.createElement('div');
+ this._bufferDecorationContainer.classList.add('xterm-decoration-container');
+ this._screenElement.appendChild(this._bufferDecorationContainer);
}
- const decoration = this._instantiationService.createInstance(BufferDecoration, decorationOptions, this._container);
+ const decoration = new BufferDecoration(this._instantiationService.createInstance(BufferService), decorationOptions, this._bufferDecorationContainer);
this._bufferDecorations.push(decoration);
decoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(decoration), 1));
this._queueRefresh();
@@ -101,7 +102,7 @@ export class DecorationService extends Disposable implements IDecorationService
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
this._refreshScollbarDecorations();
}
- const decoration = new ScrollbarDecoration({ marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!, this._bufferService);
+ const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!);
decoration.onDispose(() => this._scrollbarDecorations.splice(this._scrollbarDecorations.indexOf(decoration), 1));
this._scrollbarDecorations.push(decoration);
return decoration;
@@ -129,8 +130,8 @@ export class DecorationService extends Disposable implements IDecorationService
decoration.render();
}
}
-
}
+
export class ScrollbarDecoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
private _canvas: HTMLCanvasElement | undefined;
@@ -152,7 +153,7 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
options: IDecorationOptions,
canvas: HTMLCanvasElement,
private readonly _ctx: CanvasRenderingContext2D,
- private readonly _bufferService: IBufferService
+ @IBufferService private readonly _bufferService: IBufferService
) {
super();
this._marker = options.marker;
@@ -209,9 +210,9 @@ export class BufferDecoration extends Disposable implements IDecoration {
public height: number;
constructor(
+ private readonly _bufferService: IBufferService,
options: IDecorationOptions,
- private readonly _container: HTMLElement,
- @IBufferService private readonly _bufferService: IBufferService
+ private readonly _container?: HTMLElement
) {
super();
this.x = options.x ?? 0;
@@ -236,7 +237,7 @@ export class BufferDecoration extends Disposable implements IDecoration {
}
private _createElement(renderService: IRenderService, shouldRecreate?: boolean): void {
- if (shouldRecreate && this._element && this._container.contains(this._element)) {
+ if (shouldRecreate && this._element && this._container && this._container.contains(this._element)) {
this._container.removeChild(this._element);
}
this._element = document.createElement('div');
@@ -272,7 +273,7 @@ export class BufferDecoration extends Disposable implements IDecoration {
}
public override dispose(): void {
- if (this.isDisposed) {
+ if (this.isDisposed || !this._container) {
return;
}
if (this._element && this._container.contains(this._element)) {
diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts
index 35943897..6c9d29b5 100644
--- a/src/browser/services/Services.ts
+++ b/src/browser/services/Services.ts
@@ -119,6 +119,6 @@ export interface ICharacterJoinerService {
export const IDecorationService = createDecorator('DecorationService');
export interface IDecorationService extends IDisposable {
- attachToDom(scrollbarDecorationNode: HTMLCanvasElement, screenElement: HTMLElement, viewportElement: HTMLElement, renderService: IRenderService): void;
+ attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement, scrollbarDecorationNode: HTMLCanvasElement): void;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
}
From e797070121c6f1e3cf06ec950cbdc89248d48dfc Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 9 Mar 2022 08:42:12 -0600
Subject: [PATCH 022/245] call on render
---
src/browser/services/DecorationService.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 6580a2c9..39d89294 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -171,6 +171,7 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
this.element.width,
window.devicePixelRatio
);
+ this._onRender.fire(this.element);
}
public override dispose(): void {
From 724bbebc14d92ec4019bf0eeb7b8e85be08652b0 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 9 Mar 2022 08:46:09 -0600
Subject: [PATCH 023/245] assign element in render call
---
src/browser/services/DecorationService.ts | 10 ++++++----
typings/xterm.d.ts | 4 ++--
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 39d89294..ee1bc2e7 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -134,12 +134,12 @@ export class DecorationService extends Disposable implements IDecorationService
export class ScrollbarDecoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
- private _canvas: HTMLCanvasElement | undefined;
+ private _element: HTMLCanvasElement | undefined;
private _color: string | undefined;
public isDisposed: boolean = false;
- public get element(): HTMLCanvasElement { return this._canvas!; }
+ public get element(): HTMLCanvasElement { return this._element!; }
public get marker(): IMarker { return this._marker; }
public get color(): string { return this._color!; }
@@ -151,18 +151,20 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
constructor(
options: IDecorationOptions,
- canvas: HTMLCanvasElement,
+ private readonly _canvas: HTMLCanvasElement,
private readonly _ctx: CanvasRenderingContext2D,
@IBufferService private readonly _bufferService: IBufferService
) {
super();
this._marker = options.marker;
- this._canvas = canvas;
this._color = options.scrollbarDecorationColor;
this._marker.onDispose(() => this.dispose());
this.render();
}
public render(): void {
+ if (!this._element) {
+ this._element = this._canvas;
+ }
this._ctx.lineWidth = 1;
this._ctx.strokeStyle = this.color;
this._ctx.strokeRect(
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index eb31c4a0..510c099f 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -427,8 +427,8 @@ declare module 'xterm' {
readonly onRender: IEvent;
/**
- * The HTMLElement that gets created after the
- * first _onRender call, or undefined if accessed before
+ * The HTMLElement that gets created or drawn to (for scrollbar decorations)
+ * after the first _onRender call, or undefined if accessed before
* that.
*/
readonly element: HTMLElement | undefined;
From 89a0cee5a49006bc29096e69e49efeb530a8423f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 9 Mar 2022 11:17:26 -0600
Subject: [PATCH 024/245] when alt buffer is active, hide decorations
---
src/browser/services/DecorationService.ts | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index ee1bc2e7..0a54adc6 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -29,7 +29,7 @@ export class DecorationService extends Disposable implements IDecorationService
private readonly _bufferDecorations: BufferDecoration[] = [];
private _scrollbarDecorations: ScrollbarDecoration[] = [];
- constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { super(); }
+ constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
public attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement, scrollbarDecorationNode: HTMLCanvasElement): void {
this._renderService = renderService;
@@ -40,6 +40,9 @@ export class DecorationService extends Disposable implements IDecorationService
this.register(this._renderService.onRenderedBufferChange(() => this._refresh()));
this.register(this._renderService.onDimensionsChange(() => this._refresh(true)));
this.register(addDisposableDomListener(window, 'resize', () => this._refreshScollbarDecorations()));
+ this.register(this._bufferService.buffers.onBufferActivate(() => {
+ this._scrollbarDecorationNode!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
+ }));
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
@@ -87,7 +90,7 @@ export class DecorationService extends Disposable implements IDecorationService
this._bufferDecorationContainer.classList.add('xterm-decoration-container');
this._screenElement.appendChild(this._bufferDecorationContainer);
}
- const decoration = new BufferDecoration(this._instantiationService.createInstance(BufferService), decorationOptions, this._bufferDecorationContainer);
+ const decoration = new BufferDecoration(this._bufferService, decorationOptions, this._bufferDecorationContainer);
this._bufferDecorations.push(decoration);
decoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(decoration), 1));
this._queueRefresh();
@@ -207,6 +210,8 @@ export class BufferDecoration extends Disposable implements IDecoration {
private _onRender = new EventEmitter();
public get onRender(): IEvent { return this._onRender.event; }
+ private _altBufferIsActive: boolean = false;
+
public x: number;
public anchor: 'left' | 'right';
public width: number;
@@ -224,6 +229,9 @@ export class BufferDecoration extends Disposable implements IDecoration {
this.anchor = options.anchor || 'left';
this.width = options.width || 1;
this.height = options.height || 1;
+ this.register(this._bufferService.buffers.onBufferActivate(() => {
+ this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
+ }));
}
public render(renderService: IRenderService, shouldRecreate?: boolean): void {
@@ -271,7 +279,7 @@ export class BufferDecoration extends Disposable implements IDecoration {
this._element.style.display = 'none';
} else {
this._element.style.top = `${line * renderService.dimensions.actualCellHeight}px`;
- this._element.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
+ this._element.style.display = this._altBufferIsActive ? 'none' : 'block';
}
}
From 167a6fdbf6a602b930bd421f7792291c1e8600ca Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 9 Mar 2022 12:24:37 -0600
Subject: [PATCH 025/245] use position sticky
---
css/xterm.css | 5 ++---
demo/client.ts | 5 +++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/css/xterm.css b/css/xterm.css
index 3ae486c4..913cdb99 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -181,7 +181,7 @@
.xterm-decoration-scrollbar {
z-index: 7;
- position: fixed;
+ position: sticky;
top: 0px;
right: 0px;
width: 50px;
@@ -189,6 +189,5 @@
.xterm-decoration-scrollbar.demo-scrollbar {
height: 436px;
- left: 872px;
- top: 83px;
+ z-index:10;
}
diff --git a/demo/client.ts b/demo/client.ts
index 7eb9247a..f7de9692 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -553,8 +553,9 @@ function addDecoration() {
}
function addScrollbarDecoration() {
- document.querySelector('.xterm-decoration-scrollbar').classList.add('demo-scrollbar');
- term.registerDecoration({marker: term.addMarker(1), scrollbarDecorationColor: 'red'});
+ document.querySelector('.xterm-decoration-scrollbar')?.classList.add('demo-scrollbar');
+ const scrollbarDecorationCanvas = term.registerDecoration({marker: term.addMarker(1), scrollbarDecorationColor: 'red'});
+ scrollbarDecorationCanvas.element!.style.left = `${scrollbarDecorationCanvas.element!.nextElementSibling!.clientWidth + 89}px`;
term.registerDecoration({marker: term.addMarker(3), scrollbarDecorationColor: 'green'});
term.registerDecoration({marker: term.addMarker(5), scrollbarDecorationColor: 'blue'});
}
From bdfc82d1b78c5b656c4351bb56849ef5c5039e12 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 10 Mar 2022 10:25:34 -0500
Subject: [PATCH 026/245] delete unused import
---
src/browser/services/DecorationService.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 0a54adc6..d264b43c 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -7,7 +7,6 @@ import { addDisposableDomListener } from 'browser/Lifecycle';
import { IDecorationService, IRenderService } from 'browser/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
-import { BufferService } from 'common/services/BufferService';
import { IBufferService, IInstantiationService } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
From 60c11e87f67919d56a6d0314c01dfd60f4562c85 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 10 Mar 2022 23:40:00 -0500
Subject: [PATCH 027/245] start work
---
addons/xterm-addon-search/src/SearchAddon.ts | 130 +++++--------------
typings/xterm.d.ts | 2 +-
2 files changed, 33 insertions(+), 99 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 4651f6c7..4a1a6ef0 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -3,7 +3,7 @@
* @license MIT
*/
-import { Terminal, IBufferLine, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm';
+import { Terminal, IBufferLine, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm';
export interface ISearchOptions {
regex?: boolean;
@@ -40,7 +40,7 @@ const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
export class SearchAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
-
+ private _resultDecorations: IDecoration[] = [];
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -130,7 +130,7 @@ export class SearchAddon implements ITerminalAddon {
}
// Set selection and scroll if a result was found
- return this._selectResult(result);
+ return true;
}
/**
@@ -149,69 +149,23 @@ export class SearchAddon implements ITerminalAddon {
this._terminal.clearSelection();
return false;
}
-
- const isReverseSearch = true;
- let startRow = this._terminal.buffer.active.baseY + this._terminal.rows;
- let startCol = this._terminal.cols;
- let result: ISearchResult | undefined;
- const incremental = searchOptions ? searchOptions.incremental : false;
- let currentSelection: ISelectionPosition | undefined;
- if (this._terminal.hasSelection()) {
- currentSelection = this._terminal.getSelectionPosition()!;
- // Start from selection start if there is a selection
- startRow = currentSelection.startRow;
- startCol = currentSelection.startColumn;
- }
-
- this._initLinesCache();
- const searchPosition: ISearchPosition = {
- startRow,
- startCol
- };
-
- if (incremental) {
- // Try to expand selection to right first.
- result = this._findInLine(term, searchPosition, searchOptions, false);
- const isOldResultHighlighted = result && result.row === startRow && result.col === startCol;
- if (!isOldResultHighlighted) {
- // If selection was not able to be expanded to the right, then try reverse search
- if (currentSelection) {
- searchPosition.startRow = currentSelection.endRow;
- searchPosition.startCol = currentSelection.endColumn;
- }
- result = this._findInLine(term, searchPosition, searchOptions, true);
- }
- } else {
- result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
- }
-
- // Search from startRow - 1 to top
- if (!result) {
- searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols);
- for (let y = startRow - 1; y >= 0; y--) {
- searchPosition.startRow = y;
- result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
- if (result) {
- break;
- }
+ const results = [];
+ for (let i = this._terminal.buffer.active.viewportY; i < this._terminal.buffer.active.viewportY + this._terminal.rows; i++) {
+ const result = this._findInLine(term, { startCol: 0, startRow: i }, searchOptions);
+ if (result) {
+ results.push(result);
}
}
- // If we hit the top and didn't search from the very bottom wrap back down
- if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows)) {
- for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows); y >= startRow; y--) {
- searchPosition.startRow = y;
- result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
- if (result) {
- break;
- }
+ for (const result of results.filter(r => !!r && r.term.length)) {
+ const resultDecoration = this._showResultDecoration(result);
+ if (resultDecoration) {
+ // Add decoration
+ this._resultDecorations.push(resultDecoration);
}
}
-
- // If there is only one result, return true.
- if (!result && currentSelection) return true;
-
- // Set selection and scroll if a result was found
- return this._selectResult(result);
+ console.log(results);
+ console.log(this._resultDecorations);
+ return true;
}
/**
@@ -267,7 +221,7 @@ export class SearchAddon implements ITerminalAddon {
* @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left.
* @return The search result if it was found.
*/
- protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
+ protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}): ISearchResult | undefined {
const terminal = this._terminal!;
const row = searchPosition.startRow;
const col = searchPosition.startCol;
@@ -275,10 +229,6 @@ export class SearchAddon implements ITerminalAddon {
// Ignore wrapped lines, only consider on unwrapped line (first row of command string).
const firstLine = terminal.buffer.active.getLine(row);
if (firstLine?.isWrapped) {
- if (isReverseSearch) {
- searchPosition.startCol += terminal.cols;
- return;
- }
// This will iterate until we find the line start.
// When we find it, we will search using the calculated start column.
@@ -302,29 +252,13 @@ export class SearchAddon implements ITerminalAddon {
let resultIndex = -1;
if (searchOptions.regex) {
const searchRegex = RegExp(searchTerm, 'g');
- let foundTerm: RegExpExecArray | null;
- if (isReverseSearch) {
- // This loop will get the resultIndex of the _last_ regex match in the range 0..offset
- while (foundTerm = searchRegex.exec(searchStringLine.slice(0, offset))) {
- resultIndex = searchRegex.lastIndex - foundTerm[0].length;
- term = foundTerm[0];
- searchRegex.lastIndex -= (term.length - 1);
- }
- } else {
- foundTerm = searchRegex.exec(searchStringLine.slice(offset));
- if (foundTerm && foundTerm[0].length > 0) {
- resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
- term = foundTerm[0];
- }
+ const foundTerm = searchRegex.exec(searchStringLine.slice(offset));
+ if (foundTerm && foundTerm[0].length > 0) {
+ resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
+ term = foundTerm[0];
}
} else {
- if (isReverseSearch) {
- if (offset - searchTerm.length >= 0) {
- resultIndex = searchStringLine.lastIndexOf(searchTerm, offset - searchTerm.length);
- }
- } else {
- resultIndex = searchStringLine.indexOf(searchTerm, offset);
- }
+ resultIndex = searchStringLine.indexOf(searchTerm, offset);
}
if (resultIndex >= 0) {
@@ -448,19 +382,19 @@ export class SearchAddon implements ITerminalAddon {
* @param result The result to select.
* @return Whethera result was selected.
*/
- private _selectResult(result: ISearchResult | undefined): boolean {
+ private _showResultDecoration(result: ISearchResult | undefined): IDecoration | undefined {
const terminal = this._terminal!;
- if (!result) {
+ if (!result || result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
terminal.clearSelection();
- return false;
+ return;
}
- terminal.select(result.col, result.row, result.size);
- // If it is not in the viewport then we scroll else it just gets selected
- if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
- let scroll = result.row - terminal.buffer.active.viewportY;
- scroll -= Math.floor(terminal.rows / 2);
- terminal.scrollLines(scroll);
+ // TODO:
+ const marker = terminal.registerMarker(undefined, result.col, result.row);
+ if (!marker) {
+ return undefined;
}
- return true;
+ const findResultDecoration = terminal.registerDecoration({ marker, width: result.size });
+ findResultDecoration?.onRender((e) => console.log('rendered', e, result?.term, result?.col));
+ return findResultDecoration;
}
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 2cd4daa6..44106354 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -930,7 +930,7 @@ declare module 'xterm' {
* @param cursorYOffset The y position offset of the marker from the cursor.
* @returns The new marker or undefined.
*/
- registerMarker(cursorYOffset?: number): IMarker | undefined;
+ registerMarker(cursorYOffset?: number, col?: number, row?: number): IMarker | undefined;
/**
* @deprecated use `registerMarker` instead.
From 96574425c7a6363b4ec031ff821b36d90d05962f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 00:39:18 -0500
Subject: [PATCH 028/245] get it to sort of work
---
addons/xterm-addon-search/src/SearchAddon.ts | 14 +++++++++++---
src/browser/Terminal.ts | 7 +++++--
src/browser/Types.d.ts | 2 +-
src/browser/public/Terminal.ts | 7 +++++--
typings/xterm.d.ts | 2 +-
5 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 4a1a6ef0..1f1ff961 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -144,6 +144,8 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
+ this._resultDecorations.forEach(d => d.dispose());
+ this._resultDecorations = [];
if (!term || term.length === 0) {
this._terminal.clearSelection();
@@ -388,13 +390,19 @@ export class SearchAddon implements ITerminalAddon {
terminal.clearSelection();
return;
}
- // TODO:
- const marker = terminal.registerMarker(undefined, result.col, result.row);
+ const marker = terminal.registerMarker(undefined, result.row - 1);
if (!marker) {
return undefined;
}
const findResultDecoration = terminal.registerDecoration({ marker, width: result.size });
- findResultDecoration?.onRender((e) => console.log('rendered', e, result?.term, result?.col));
+ findResultDecoration?.onRender((e) => {
+ console.log('rendered', e, result?.term, result?.row);
+ e.style.backgroundColor = 'blue';
+ e.style.color = 'white';
+ e.style.opacity = '60%';
+ // TODO: use cell width here instead of 10
+ e.style.left = `${(result.col === 0 ? 0 : result.col - 1) * 10}px`;
+ });
return findResultDecoration;
}
}
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 08963933..69d5bfad 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -993,12 +993,15 @@ export class Terminal extends CoreTerminal implements ITerminal {
return this.buffer.markers;
}
- public addMarker(cursorYOffset: number): IMarker | undefined {
+ public addMarker(cursorYOffset: number, row?: number): IMarker | undefined {
// Disallow markers on the alt buffer
if (this.buffer !== this.buffers.normal) {
return;
}
-
+ if (row) {
+ console.log(row);
+ return this.buffer.addMarker(row + 1);
+ }
return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
}
diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts
index 35b52d62..c66b0928 100644
--- a/src/browser/Types.d.ts
+++ b/src/browser/Types.d.ts
@@ -60,7 +60,7 @@ export interface IPublicTerminal extends IDisposable {
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
deregisterCharacterJoiner(joinerId: number): void;
- addMarker(cursorYOffset: number): IMarker | undefined;
+ addMarker(cursorYOffset: number, col?: number, row?: number): IMarker | undefined;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
hasSelection(): boolean;
getSelection(): string;
diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts
index 1acde934..8c290fda 100644
--- a/src/browser/public/Terminal.ts
+++ b/src/browser/public/Terminal.ts
@@ -166,10 +166,13 @@ export class Terminal implements ITerminalApi {
this._checkProposedApi();
this._core.deregisterCharacterJoiner(joinerId);
}
- public registerMarker(cursorYOffset: number = 0): IMarker | undefined {
+ public registerMarker(cursorYOffset: number = 0, row?: number): IMarker | undefined {
this._checkProposedApi();
this._verifyIntegers(cursorYOffset);
- return this._core.addMarker(cursorYOffset);
+ if (row) {
+ this._verifyPositiveIntegers(row);
+ }
+ return this._core.addMarker(cursorYOffset, row);
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
this._checkProposedApi();
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 44106354..cc6dc8ad 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -930,7 +930,7 @@ declare module 'xterm' {
* @param cursorYOffset The y position offset of the marker from the cursor.
* @returns The new marker or undefined.
*/
- registerMarker(cursorYOffset?: number, col?: number, row?: number): IMarker | undefined;
+ registerMarker(cursorYOffset?: number, row?: number): IMarker | undefined;
/**
* @deprecated use `registerMarker` instead.
From fa4479c79724b2dffcce2ce15ce8d52a6fc8bdf0 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 12:01:19 -0500
Subject: [PATCH 029/245] use decoration width, find all and select next
---
addons/xterm-addon-search/src/SearchAddon.ts | 208 ++++++++++++++----
.../typings/xterm-addon-search.d.ts | 24 +-
demo/client.ts | 5 +
demo/index.html | 1 +
src/browser/Terminal.ts | 3 +-
5 files changed, 192 insertions(+), 49 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 1f1ff961..d26dd6be 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -3,7 +3,7 @@
* @license MIT
*/
-import { Terminal, IBufferLine, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm';
+import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm';
export interface ISearchOptions {
regex?: boolean;
@@ -41,6 +41,7 @@ const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
export class SearchAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
private _resultDecorations: IDecoration[] = [];
+ private _result: ISearchResult | undefined;
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -57,6 +58,52 @@ export class SearchAddon implements ITerminalAddon {
public dispose(): void { }
+ /**
+ * Find all instances of the term, selecting the next one with each
+ * enter. If it doesn't exist, do nothing.
+ * @param term The search term.
+ * @param searchOptions Search options.
+ * @return Whether a result was found.
+ */
+ public find(term: string, searchOptions?: ISearchOptions): boolean {
+ if (!this._terminal) {
+ throw new Error('Cannot use addon until it has been loaded');
+ }
+
+ if (!term || term.length === 0) {
+ this._terminal.clearSelection();
+ this._resultDecorations.forEach(d => d.dispose());
+ this._resultDecorations = [];
+ return false;
+ }
+
+ // new search, clear out the old decorations
+ this._resultDecorations.forEach(d => d.dispose());
+ this._resultDecorations = [];
+
+ const results: ISearchResult[] = [];
+ let found = this.findNext(term, searchOptions);
+ while (found && !results.find(r => r?.col === this._result?.col && r?.row === this._result?.row)) {
+ if (this._result) {
+ results.push(this._result);
+ }
+ found = this.findNext(term, searchOptions);
+ }
+
+ for (const result of results) {
+ const resultDecoration = this._showResultDecoration(result);
+ if (resultDecoration) {
+ // Add decoration
+ this._resultDecorations.push(resultDecoration);
+ }
+ }
+ if (results.length > 0) {
+ // this.findNext(term, searchOptions);
+ }
+ return true;
+ }
+
+
/**
* Find the next instance of the term, then scroll to and select it. If it
* doesn't exist, do nothing.
@@ -70,6 +117,7 @@ export class SearchAddon implements ITerminalAddon {
}
if (!term || term.length === 0) {
+ this._result = undefined;
this._terminal.clearSelection();
return false;
}
@@ -94,43 +142,42 @@ export class SearchAddon implements ITerminalAddon {
};
// Search startRow
- let result = this._findInLine(term, searchPosition, searchOptions);
-
+ this._result = this._findInLine(term, searchPosition, searchOptions);
// Search from startRow + 1 to end
- if (!result) {
+ if (!this._result) {
for (let y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) {
searchPosition.startRow = y;
searchPosition.startCol = 0;
// If the current line is wrapped line, increase index of column to ignore the previous scan
// Otherwise, reset beginning column index to zero with set new unwrapped line index
- result = this._findInLine(term, searchPosition, searchOptions);
- if (result) {
+ this._result = this._findInLine(term, searchPosition, searchOptions);
+ if (this._result) {
break;
}
}
}
// If we hit the bottom and didn't search from the very top wrap back up
- if (!result && startRow !== 0) {
+ if (!this._result && startRow !== 0) {
for (let y = 0; y < startRow; y++) {
searchPosition.startRow = y;
searchPosition.startCol = 0;
- result = this._findInLine(term, searchPosition, searchOptions);
- if (result) {
+ this._result = this._findInLine(term, searchPosition, searchOptions);
+ if (this._result) {
break;
}
}
}
// If there is only one result, wrap back and return selection if it exists.
- if (!result && currentSelection) {
+ if (!this._result && currentSelection) {
searchPosition.startRow = currentSelection.startRow;
searchPosition.startCol = 0;
- result = this._findInLine(term, searchPosition, searchOptions);
+ this._result = this._findInLine(term, searchPosition, searchOptions);
}
// Set selection and scroll if a result was found
- return true;
+ return this._selectResult(this._result);
}
/**
@@ -144,32 +191,77 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
- this._resultDecorations.forEach(d => d.dispose());
- this._resultDecorations = [];
if (!term || term.length === 0) {
this._terminal.clearSelection();
return false;
}
- const results = [];
- for (let i = this._terminal.buffer.active.viewportY; i < this._terminal.buffer.active.viewportY + this._terminal.rows; i++) {
- const result = this._findInLine(term, { startCol: 0, startRow: i }, searchOptions);
- if (result) {
- results.push(result);
+
+ const isReverseSearch = true;
+ let startRow = this._terminal.buffer.active.baseY + this._terminal.rows;
+ let startCol = this._terminal.cols;
+ let result: ISearchResult | undefined;
+ const incremental = searchOptions ? searchOptions.incremental : false;
+ let currentSelection: ISelectionPosition | undefined;
+ if (this._terminal.hasSelection()) {
+ currentSelection = this._terminal.getSelectionPosition()!;
+ // Start from selection start if there is a selection
+ startRow = currentSelection.startRow;
+ startCol = currentSelection.startColumn;
+ }
+
+ this._initLinesCache();
+ const searchPosition: ISearchPosition = {
+ startRow,
+ startCol
+ };
+
+ if (incremental) {
+ // Try to expand selection to right first.
+ result = this._findInLine(term, searchPosition, searchOptions, false);
+ const isOldResultHighlighted = result && result.row === startRow && result.col === startCol;
+ if (!isOldResultHighlighted) {
+ // If selection was not able to be expanded to the right, then try reverse search
+ if (currentSelection) {
+ searchPosition.startRow = currentSelection.endRow;
+ searchPosition.startCol = currentSelection.endColumn;
+ }
+ result = this._findInLine(term, searchPosition, searchOptions, true);
+ }
+ } else {
+ result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
+ }
+
+ // Search from startRow - 1 to top
+ if (!result) {
+ searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols);
+ for (let y = startRow - 1; y >= 0; y--) {
+ searchPosition.startRow = y;
+ result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
+ if (result) {
+ break;
+ }
}
}
- for (const result of results.filter(r => !!r && r.term.length)) {
- const resultDecoration = this._showResultDecoration(result);
- if (resultDecoration) {
- // Add decoration
- this._resultDecorations.push(resultDecoration);
+ // If we hit the top and didn't search from the very bottom wrap back down
+ if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows)) {
+ for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows); y >= startRow; y--) {
+ searchPosition.startRow = y;
+ result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
+ if (result) {
+ break;
+ }
}
}
- console.log(results);
- console.log(this._resultDecorations);
- return true;
+
+ // If there is only one result, return true.
+ if (!result && currentSelection) return true;
+
+ // Set selection and scroll if a result was found
+ return this._selectResult(result);
}
+
/**
* Sets up a line cache with a ttl
*/
@@ -223,7 +315,7 @@ export class SearchAddon implements ITerminalAddon {
* @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left.
* @return The search result if it was found.
*/
- protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}): ISearchResult | undefined {
+ protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
const terminal = this._terminal!;
const row = searchPosition.startRow;
const col = searchPosition.startCol;
@@ -231,6 +323,10 @@ export class SearchAddon implements ITerminalAddon {
// Ignore wrapped lines, only consider on unwrapped line (first row of command string).
const firstLine = terminal.buffer.active.getLine(row);
if (firstLine?.isWrapped) {
+ if (isReverseSearch) {
+ searchPosition.startCol += terminal.cols;
+ return;
+ }
// This will iterate until we find the line start.
// When we find it, we will search using the calculated start column.
@@ -254,13 +350,29 @@ export class SearchAddon implements ITerminalAddon {
let resultIndex = -1;
if (searchOptions.regex) {
const searchRegex = RegExp(searchTerm, 'g');
- const foundTerm = searchRegex.exec(searchStringLine.slice(offset));
- if (foundTerm && foundTerm[0].length > 0) {
- resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
- term = foundTerm[0];
+ let foundTerm: RegExpExecArray | null;
+ if (isReverseSearch) {
+ // This loop will get the resultIndex of the _last_ regex match in the range 0..offset
+ while (foundTerm = searchRegex.exec(searchStringLine.slice(0, offset))) {
+ resultIndex = searchRegex.lastIndex - foundTerm[0].length;
+ term = foundTerm[0];
+ searchRegex.lastIndex -= (term.length - 1);
+ }
+ } else {
+ foundTerm = searchRegex.exec(searchStringLine.slice(offset));
+ if (foundTerm && foundTerm[0].length > 0) {
+ resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
+ term = foundTerm[0];
+ }
}
} else {
- resultIndex = searchStringLine.indexOf(searchTerm, offset);
+ if (isReverseSearch) {
+ if (offset - searchTerm.length >= 0) {
+ resultIndex = searchStringLine.lastIndexOf(searchTerm, offset - searchTerm.length);
+ }
+ } else {
+ resultIndex = searchStringLine.indexOf(searchTerm, offset);
+ }
}
if (resultIndex >= 0) {
@@ -384,24 +496,42 @@ export class SearchAddon implements ITerminalAddon {
* @param result The result to select.
* @return Whethera result was selected.
*/
+ private _selectResult(result: ISearchResult | undefined): boolean {
+ const terminal = this._terminal!;
+ if (!result) {
+ terminal.clearSelection();
+ return false;
+ }
+ terminal.select(result.col, result.row, result.size);
+ // If it is not in the viewport then we scroll else it just gets selected
+ if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
+ let scroll = result.row - terminal.buffer.active.viewportY;
+ scroll -= Math.floor(terminal.rows / 2);
+ terminal.scrollLines(scroll);
+ }
+ return true;
+ }
+
+ /**
+ * Registers a decoration for the @param result
+ * and @returns the decoration or undefined if
+ * the marker has already been disposed of
+ */
private _showResultDecoration(result: ISearchResult | undefined): IDecoration | undefined {
const terminal = this._terminal!;
- if (!result || result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
+ if (!result) {
terminal.clearSelection();
return;
}
- const marker = terminal.registerMarker(undefined, result.row - 1);
+ const marker = terminal.registerMarker(undefined, result.row);
if (!marker) {
return undefined;
}
const findResultDecoration = terminal.registerDecoration({ marker, width: result.size });
findResultDecoration?.onRender((e) => {
- console.log('rendered', e, result?.term, result?.row);
e.style.backgroundColor = 'blue';
- e.style.color = 'white';
e.style.opacity = '60%';
- // TODO: use cell width here instead of 10
- e.style.left = `${(result.col === 0 ? 0 : result.col - 1) * 10}px`;
+ e.style.left = `${result.col * e.clientWidth}px`;
});
return findResultDecoration;
}
diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts
index f27aba78..228a5ffe 100644
--- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts
+++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts
@@ -49,20 +49,28 @@ declare module 'xterm-addon-search' {
*/
public dispose(): void;
+ /**
+ * Find all instances of the term, selecting the next one with each
+ * enter. If it doesn't exist, do nothing.
+ * @param term The search term.
+ * @param searchOptions The options for the search.
+ */
+ public find(term: string, searchOptions?: ISearchOptions): boolean;
+
/**
* Search forwards for the next result that matches the search term and
* options.
* @param term The search term.
* @param searchOptions The options for the search.
*/
- public findNext(term: string, searchOptions?: ISearchOptions): boolean;
+ public findNext(term: string, searchOptions?: ISearchOptions): boolean;
- /**
- * Search backwards for the previous result that matches the search term and
- * options.
- * @param term The search term.
- * @param searchOptions The options for the search.
- */
- public findPrevious(term: string, searchOptions?: ISearchOptions): boolean;
+ /**
+ * Search backwards for the previous result that matches the search term and
+ * options.
+ * @param term The search term.
+ * @param searchOptions The options for the search.
+ */
+ public findPrevious(term: string, searchOptions?: ISearchOptions): boolean;
}
}
diff --git a/demo/client.ts b/demo/client.ts
index aee23401..65bdc6ce 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -92,6 +92,7 @@ const addons: { [T in AddonType]: IDemoAddon} = {
const terminalContainer = document.getElementById('terminal-container');
const actionElements = {
+ find: document.querySelector('#find'),
findNext: document.querySelector('#find-next'),
findPrevious: document.querySelector('#find-previous')
};
@@ -199,6 +200,10 @@ function createTerminal(): void {
addDomListener(paddingElement, 'change', setPadding);
+ addDomListener(actionElements.find, 'keyup', (e) => {
+ addons.search.instance.find(actionElements.find.value, getSearchOptions(e));
+ });
+
addDomListener(actionElements.findNext, 'keyup', (e) => {
addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions(e));
});
diff --git a/demo/index.html b/demo/index.html
index e024222c..590bf1e6 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -38,6 +38,7 @@
Addons Control
SearchAddon
+
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 69d5bfad..4d546dcb 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -999,8 +999,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
return;
}
if (row) {
- console.log(row);
- return this.buffer.addMarker(row + 1);
+ return this.buffer.addMarker(row);
}
return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
}
From 372b6b633b6d0b291775482d8dc7c691a1f34329 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 12:13:43 -0500
Subject: [PATCH 030/245] set incremental search to false
---
addons/xterm-addon-search/src/SearchAddon.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index d26dd6be..a06f32e0 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -80,8 +80,9 @@ export class SearchAddon implements ITerminalAddon {
// new search, clear out the old decorations
this._resultDecorations.forEach(d => d.dispose());
this._resultDecorations = [];
-
const results: ISearchResult[] = [];
+ searchOptions = searchOptions || {};
+ searchOptions.incremental = false;
let found = this.findNext(term, searchOptions);
while (found && !results.find(r => r?.col === this._result?.col && r?.row === this._result?.row)) {
if (this._result) {
@@ -527,11 +528,12 @@ export class SearchAddon implements ITerminalAddon {
if (!marker) {
return undefined;
}
- const findResultDecoration = terminal.registerDecoration({ marker, width: result.size });
+ const findResultDecoration = terminal.registerDecoration({ marker });
findResultDecoration?.onRender((e) => {
e.style.backgroundColor = 'blue';
e.style.opacity = '60%';
e.style.left = `${result.col * e.clientWidth}px`;
+ e.style.width = `${e.clientWidth * result.term.length}px`;
});
return findResultDecoration;
}
From da2c59d1d0450d5a1f1677c2d323c642e5c8d938 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 12:24:36 -0500
Subject: [PATCH 031/245] remove unused variable
---
addons/xterm-addon-search/src/SearchAddon.ts | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index a06f32e0..e28b409a 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -42,6 +42,8 @@ export class SearchAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
private _resultDecorations: IDecoration[] = [];
private _result: ISearchResult | undefined;
+ private _reset: boolean = false;
+ private _cachedSearchTerm: string | undefined;
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -54,6 +56,7 @@ export class SearchAddon implements ITerminalAddon {
public activate(terminal: Terminal): void {
this._terminal = terminal;
+ this._terminal.onData(() => this._reset = true);
}
public dispose(): void { }
@@ -77,6 +80,12 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
+ if (!this._reset && term === this._cachedSearchTerm) {
+ return this.findNext(term, searchOptions);
+ }
+ this._reset = false;
+
+
// new search, clear out the old decorations
this._resultDecorations.forEach(d => d.dispose());
this._resultDecorations = [];
@@ -99,7 +108,7 @@ export class SearchAddon implements ITerminalAddon {
}
}
if (results.length > 0) {
- // this.findNext(term, searchOptions);
+ this._cachedSearchTerm = term;
}
return true;
}
From 453cb00807cb501dac96194006ac3a86a8de41cf Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 12:43:09 -0500
Subject: [PATCH 032/245] use a class
---
addons/xterm-addon-search/src/SearchAddon.ts | 10 ++++++----
css/xterm.css | 5 +++++
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index e28b409a..e50fdd17 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -539,10 +539,12 @@ export class SearchAddon implements ITerminalAddon {
}
const findResultDecoration = terminal.registerDecoration({ marker });
findResultDecoration?.onRender((e) => {
- e.style.backgroundColor = 'blue';
- e.style.opacity = '60%';
- e.style.left = `${result.col * e.clientWidth}px`;
- e.style.width = `${e.clientWidth * result.term.length}px`;
+ if (!e.classList.contains('xterm-find-result-decoration')) {
+ e.classList.add('xterm-find-result-decoration');
+ // decoration's clientWidth = actualCellWidth
+ e.style.left = `${e.clientWidth * result.col}px`;
+ e.style.width = `${e.clientWidth * result.term.length}px`;
+ }
});
return findResultDecoration;
}
diff --git a/css/xterm.css b/css/xterm.css
index ab3965b4..ded93074 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -178,3 +178,8 @@
z-index: 6;
position: absolute;
}
+
+.xterm-find-result-decoration {
+ background-color: blue;
+ opacity: 60%;
+}
\ No newline at end of file
From e7bbc4151afb795770e529a46de265b8764d38c9 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 13:10:26 -0500
Subject: [PATCH 033/245] start at cursor position and mandate a width gt 0
---
addons/xterm-addon-search/src/SearchAddon.ts | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index e50fdd17..5e089594 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -101,10 +101,11 @@ export class SearchAddon implements ITerminalAddon {
}
for (const result of results) {
- const resultDecoration = this._showResultDecoration(result);
- if (resultDecoration) {
- // Add decoration
- this._resultDecorations.push(resultDecoration);
+ if (result) {
+ const resultDecoration = this._showResultDecoration(result);
+ if (resultDecoration) {
+ this._resultDecorations.push(resultDecoration);
+ }
}
}
if (results.length > 0) {
@@ -142,6 +143,9 @@ export class SearchAddon implements ITerminalAddon {
currentSelection = this._terminal.getSelectionPosition()!;
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
+ } else {
+ startRow = this._terminal.buffer.active.cursorY;
+ startCol = this._terminal.buffer.active.cursorX;
}
this._initLinesCache();
@@ -527,19 +531,15 @@ export class SearchAddon implements ITerminalAddon {
* and @returns the decoration or undefined if
* the marker has already been disposed of
*/
- private _showResultDecoration(result: ISearchResult | undefined): IDecoration | undefined {
+ private _showResultDecoration(result: ISearchResult): IDecoration | undefined {
const terminal = this._terminal!;
- if (!result) {
- terminal.clearSelection();
- return;
- }
const marker = terminal.registerMarker(undefined, result.row);
if (!marker) {
return undefined;
}
const findResultDecoration = terminal.registerDecoration({ marker });
findResultDecoration?.onRender((e) => {
- if (!e.classList.contains('xterm-find-result-decoration')) {
+ if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0) {
e.classList.add('xterm-find-result-decoration');
// decoration's clientWidth = actualCellWidth
e.style.left = `${e.clientWidth * result.col}px`;
From b4de2787c7d0734774efcbe3d365bdcc5e1e258f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 13:13:05 -0500
Subject: [PATCH 034/245] hide decorations >= bufferService rows
---
src/browser/services/DecorationService.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index ed3c7224..a77409db 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -142,7 +142,7 @@ export class Decoration extends Disposable implements IDecoration {
return;
}
const line = this.marker.line - this._bufferService.buffers.active.ydisp;
- if (line < 0 || line > this._bufferService.rows) {
+ if (line < 0 || line >= this._bufferService.rows) {
// outside of viewport
this._element.style.display = 'none';
} else {
From 2f85448e512cc24b0914992c7688fd9cce7c5edd Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Fri, 11 Mar 2022 16:53:59 -0500
Subject: [PATCH 035/245] Update css/xterm.css
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
css/xterm.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/css/xterm.css b/css/xterm.css
index 913cdb99..215c1446 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -182,8 +182,8 @@
.xterm-decoration-scrollbar {
z-index: 7;
position: sticky;
- top: 0px;
- right: 0px;
+ top: 0;
+ right: 0;
width: 50px;
}
From 98a03dd477c182707b6e7dd5dfc0dd9f7dd6188f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 17:15:11 -0500
Subject: [PATCH 036/245] re-arrange dom structure
---
css/xterm.css | 11 +++--------
demo/client.ts | 3 +--
src/browser/Terminal.ts | 8 +-------
src/browser/services/DecorationService.ts | 11 ++++++++---
src/browser/services/Services.ts | 2 +-
5 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/css/xterm.css b/css/xterm.css
index 913cdb99..ef12f004 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -180,14 +180,9 @@
}
.xterm-decoration-scrollbar {
- z-index: 7;
- position: sticky;
+ z-index: 6;
+ position: absolute;
top: 0px;
right: 0px;
width: 50px;
-}
-
-.xterm-decoration-scrollbar.demo-scrollbar {
- height: 436px;
- z-index:10;
-}
+}
\ No newline at end of file
diff --git a/demo/client.ts b/demo/client.ts
index f7de9692..0b3ce37e 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -553,9 +553,8 @@ function addDecoration() {
}
function addScrollbarDecoration() {
- document.querySelector('.xterm-decoration-scrollbar')?.classList.add('demo-scrollbar');
const scrollbarDecorationCanvas = term.registerDecoration({marker: term.addMarker(1), scrollbarDecorationColor: 'red'});
- scrollbarDecorationCanvas.element!.style.left = `${scrollbarDecorationCanvas.element!.nextElementSibling!.clientWidth + 89}px`;
+ scrollbarDecorationCanvas.element!.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
term.registerDecoration({marker: term.addMarker(3), scrollbarDecorationColor: 'green'});
term.registerDecoration({marker: term.addMarker(5), scrollbarDecorationColor: 'blue'});
}
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 7dec6323..ba0fc93d 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -70,7 +70,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _viewportElement: HTMLElement | undefined;
private _helperContainer: HTMLElement | undefined;
private _compositionView: HTMLElement | undefined;
- private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
// private _visualBellTimer: number;
@@ -473,11 +472,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
this._viewportElement.classList.add('xterm-viewport');
fragment.appendChild(this._viewportElement);
- // TODO: make this opt in, must be done before the scroll area in order to show up
- this._scrollbarDecorationNode = document.createElement('canvas');
- this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
- this._viewportElement?.appendChild(this._scrollbarDecorationNode);
-
this._viewportScrollArea = document.createElement('div');
this._viewportScrollArea.classList.add('xterm-scroll-area');
this._viewportElement.appendChild(this._viewportScrollArea);
@@ -584,7 +578,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
- this.decorationService.attachToDom(this._renderService, this.screenElement, this._viewportElement, this._scrollbarDecorationNode);
+ this.decorationService.attachToDom(this._renderService, this.screenElement, this._viewportElement);
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index d264b43c..74489345 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -30,11 +30,10 @@ export class DecorationService extends Disposable implements IDecorationService
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
- public attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement, scrollbarDecorationNode: HTMLCanvasElement): void {
+ public attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement): void {
this._renderService = renderService;
this._screenElement = screenElement;
this._viewportElement = viewportElement;
- this._scrollbarDecorationNode = scrollbarDecorationNode;
this.register(this._renderService.onRenderedBufferChange(() => this._refresh()));
this.register(this._renderService.onDimensionsChange(() => this._refresh(true)));
@@ -97,9 +96,15 @@ export class DecorationService extends Disposable implements IDecorationService
}
private _registerScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
- if (!this._scrollbarDecorationNode || !this._viewportElement) {
+ if (!this._viewportElement?.parentElement) {
return;
}
+ if (!this._scrollbarDecorationNode) {
+ // TODO: make this opt in, must be done before the scroll area in order to show up
+ this._scrollbarDecorationNode = document.createElement('canvas');
+ this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
+ this._viewportElement.parentElement.appendChild(this._scrollbarDecorationNode);
+ }
if (!this._scrollbarDecorationCanvas) {
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
this._refreshScollbarDecorations();
diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts
index 6c9d29b5..2d1f3d03 100644
--- a/src/browser/services/Services.ts
+++ b/src/browser/services/Services.ts
@@ -119,6 +119,6 @@ export interface ICharacterJoinerService {
export const IDecorationService = createDecorator('DecorationService');
export interface IDecorationService extends IDisposable {
- attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement, scrollbarDecorationNode: HTMLCanvasElement): void;
+ attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement): void;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
}
From 1250ab637a67ef681ec03e5d96354c730c52e36f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 17:52:04 -0500
Subject: [PATCH 037/245] insert before
---
src/browser/services/DecorationService.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 74489345..2dddf825 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -103,7 +103,7 @@ export class DecorationService extends Disposable implements IDecorationService
// TODO: make this opt in, must be done before the scroll area in order to show up
this._scrollbarDecorationNode = document.createElement('canvas');
this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
- this._viewportElement.parentElement.appendChild(this._scrollbarDecorationNode);
+ this._viewportElement.parentElement.insertBefore(this._scrollbarDecorationNode, this._viewportElement);
}
if (!this._scrollbarDecorationCanvas) {
this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
@@ -129,9 +129,9 @@ export class DecorationService extends Disposable implements IDecorationService
return;
}
this._scrollbarDecorationNode.style.width = `${ScrollbarConstants.WIDTH}px`;
- this._scrollbarDecorationNode.style.height = `${this._viewportElement.clientHeight}px`;
+ this._scrollbarDecorationNode.style.height = `${this._screenElement!.clientHeight}px`;
this._scrollbarDecorationNode.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
- this._scrollbarDecorationNode.height = Math.floor(this._viewportElement.clientHeight * window.devicePixelRatio);
+ this._scrollbarDecorationNode.height = Math.floor(this._screenElement!.clientHeight * window.devicePixelRatio);
this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
for (const decoration of this._scrollbarDecorations) {
decoration.render();
From 4022277cec2fd75265a7faaf36c23139df9d65e1 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 21:52:16 -0500
Subject: [PATCH 038/245] scroll -> overviewRuler
---
demo/client.ts | 12 +++---
demo/index.html | 2 +-
src/browser/services/DecorationService.ts | 52 +++++++++++------------
typings/xterm.d.ts | 2 +-
4 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 0b3ce37e..586114d1 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -151,7 +151,7 @@ if (document.location.pathname === '/test') {
document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler);
document.getElementById('load-test').addEventListener('click', loadTest);
document.getElementById('add-decoration').addEventListener('click', addDecoration);
- document.getElementById('add-scrollbar-decoration').addEventListener('click', addScrollbarDecoration);
+ document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler);
}
function createTerminal(): void {
@@ -552,10 +552,10 @@ function addDecoration() {
});
}
-function addScrollbarDecoration() {
- const scrollbarDecorationCanvas = term.registerDecoration({marker: term.addMarker(1), scrollbarDecorationColor: 'red'});
- scrollbarDecorationCanvas.element!.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
- term.registerDecoration({marker: term.addMarker(3), scrollbarDecorationColor: 'green'});
- term.registerDecoration({marker: term.addMarker(5), scrollbarDecorationColor: 'blue'});
+function addOverviewRuler() {
+ const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerItemColor: 'red'});
+ canvas.element!.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
+ term.registerDecoration({marker: term.addMarker(3), overviewRulerItemColor: 'green'});
+ term.registerDecoration({marker: term.addMarker(5), overviewRulerItemColor: 'blue'});
}
diff --git a/demo/index.html b/demo/index.html
index dab52eec..d2b8d481 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -69,7 +69,7 @@
-
+
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 2dddf825..2e44b1ec 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -22,11 +22,11 @@ export class DecorationService extends Disposable implements IDecorationService
private _screenElement: HTMLElement | undefined;
private _viewportElement: HTMLElement | undefined;
private _bufferDecorationContainer: HTMLElement | undefined;
- private _scrollbarDecorationCanvas: CanvasRenderingContext2D | null = null;
- private _scrollbarDecorationNode: HTMLCanvasElement | undefined;
+ private _overviewRulerCtx: CanvasRenderingContext2D | null = null;
+ private _overviewRulerCanvas: HTMLCanvasElement | undefined;
private readonly _bufferDecorations: BufferDecoration[] = [];
- private _scrollbarDecorations: ScrollbarDecoration[] = [];
+ private _overviewRulerDecorations: ScrollbarDecoration[] = [];
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
@@ -39,7 +39,7 @@ export class DecorationService extends Disposable implements IDecorationService
this.register(this._renderService.onDimensionsChange(() => this._refresh(true)));
this.register(addDisposableDomListener(window, 'resize', () => this._refreshScollbarDecorations()));
this.register(this._bufferService.buffers.onBufferActivate(() => {
- this._scrollbarDecorationNode!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
+ this._overviewRulerCanvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
}));
}
@@ -47,8 +47,8 @@ export class DecorationService extends Disposable implements IDecorationService
if (decorationOptions.marker.isDisposed) {
return undefined;
}
- if (decorationOptions.scrollbarDecorationColor) {
- return this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.scrollbarDecorationColor);
+ if (decorationOptions.overviewRulerItemColor) {
+ return this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.overviewRulerItemColor);
}
return this._registerBufferDecoration(decorationOptions);
}
@@ -57,14 +57,14 @@ export class DecorationService extends Disposable implements IDecorationService
for (const bufferDecoration of this._bufferDecorations) {
bufferDecoration.dispose();
}
- for (const scrollbarDecoration of this._scrollbarDecorations) {
+ for (const scrollbarDecoration of this._overviewRulerDecorations) {
scrollbarDecoration.dispose();
}
if (this._screenElement && this._bufferDecorationContainer && this._screenElement.contains(this._bufferDecorationContainer)) {
this._screenElement.removeChild(this._bufferDecorationContainer);
}
- this._scrollbarDecorations = [];
- this._scrollbarDecorationNode?.remove();
+ this._overviewRulerDecorations = [];
+ this._overviewRulerCanvas?.remove();
}
private _refresh(shouldRecreate?: boolean): void {
@@ -99,19 +99,19 @@ export class DecorationService extends Disposable implements IDecorationService
if (!this._viewportElement?.parentElement) {
return;
}
- if (!this._scrollbarDecorationNode) {
+ if (!this._overviewRulerCanvas) {
// TODO: make this opt in, must be done before the scroll area in order to show up
- this._scrollbarDecorationNode = document.createElement('canvas');
- this._scrollbarDecorationNode.classList.add('xterm-decoration-scrollbar');
- this._viewportElement.parentElement.insertBefore(this._scrollbarDecorationNode, this._viewportElement);
+ this._overviewRulerCanvas = document.createElement('canvas');
+ this._overviewRulerCanvas.classList.add('xterm-decoration-scrollbar');
+ this._viewportElement.parentElement.insertBefore(this._overviewRulerCanvas, this._viewportElement);
}
- if (!this._scrollbarDecorationCanvas) {
- this._scrollbarDecorationCanvas = this._scrollbarDecorationNode.getContext('2d');
+ if (!this._overviewRulerCtx) {
+ this._overviewRulerCtx = this._overviewRulerCanvas.getContext('2d');
this._refreshScollbarDecorations();
}
- const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker, scrollbarDecorationColor: color }, this._scrollbarDecorationNode, this._scrollbarDecorationCanvas!);
- decoration.onDispose(() => this._scrollbarDecorations.splice(this._scrollbarDecorations.indexOf(decoration), 1));
- this._scrollbarDecorations.push(decoration);
+ const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker, overviewRulerItemColor: color }, this._overviewRulerCanvas, this._overviewRulerCtx!);
+ decoration.onDispose(() => this._overviewRulerDecorations.splice(this._overviewRulerDecorations.indexOf(decoration), 1));
+ this._overviewRulerDecorations.push(decoration);
return decoration;
}
@@ -125,15 +125,15 @@ export class DecorationService extends Disposable implements IDecorationService
}
private _refreshScollbarDecorations(): void {
- if (!this._scrollbarDecorationCanvas || !this._viewportElement || !this._scrollbarDecorationNode) {
+ if (!this._overviewRulerCtx || !this._viewportElement || !this._overviewRulerCanvas) {
return;
}
- this._scrollbarDecorationNode.style.width = `${ScrollbarConstants.WIDTH}px`;
- this._scrollbarDecorationNode.style.height = `${this._screenElement!.clientHeight}px`;
- this._scrollbarDecorationNode.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
- this._scrollbarDecorationNode.height = Math.floor(this._screenElement!.clientHeight * window.devicePixelRatio);
- this._scrollbarDecorationCanvas.clearRect(0, 0, this._scrollbarDecorationCanvas.canvas.width, this._scrollbarDecorationCanvas.canvas.height);
- for (const decoration of this._scrollbarDecorations) {
+ this._overviewRulerCanvas.style.width = `${ScrollbarConstants.WIDTH}px`;
+ this._overviewRulerCanvas.style.height = `${this._screenElement!.clientHeight}px`;
+ this._overviewRulerCanvas.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
+ this._overviewRulerCanvas.height = Math.floor(this._screenElement!.clientHeight * window.devicePixelRatio);
+ this._overviewRulerCtx.clearRect(0, 0, this._overviewRulerCtx.canvas.width, this._overviewRulerCtx.canvas.height);
+ for (const decoration of this._overviewRulerDecorations) {
decoration.render();
}
}
@@ -164,7 +164,7 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
) {
super();
this._marker = options.marker;
- this._color = options.scrollbarDecorationColor;
+ this._color = options.overviewRulerItemColor;
this._marker.onDispose(() => this.dispose());
this.render();
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 510c099f..cb952e70 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -475,7 +475,7 @@ declare module 'xterm' {
* When provided, renders the decoration in the scrollbar
* with the given color
*/
- scrollbarDecorationColor?: string;
+ overviewRulerItemColor?: string;
}
/**
From 692df151e5813270e6ee86f21eb822bddcda96bc Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 11 Mar 2022 22:34:17 -0500
Subject: [PATCH 039/245] part 1 of massive refactor
---
src/browser/services/DecorationService.ts | 211 +++++++++++++---------
1 file changed, 127 insertions(+), 84 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 2e44b1ec..2daa9e92 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -14,6 +14,110 @@ const enum ScrollbarConstants {
WIDTH = 7
}
+interface IDecorationRenderer {
+ refreshDecorations(shouldRecreate?: boolean): void;
+ registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
+}
+
+class BufferDecorationRenderer extends Disposable implements IDecorationRenderer {
+ private _decorationContainer: HTMLElement | undefined;
+ private readonly _decorations: BufferDecoration[] = [];
+ private _renderService: IRenderService | undefined;
+
+ constructor(
+ @IBufferService private readonly _bufferService: IBufferService,
+ private readonly _screenElement: HTMLElement) {
+ super();
+ this.register(this._bufferService.buffers.onBufferActivate(() => {
+ // this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
+ }));
+ }
+ public refreshDecorations(shouldRecreate?: boolean): void {
+ if (!this._renderService) {
+ return;
+ }
+ for (const decoration of this._decorations) {
+ decoration.render(this._renderService, shouldRecreate);
+ }
+ }
+ public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
+ if (this._screenElement && !this._decorationContainer) {
+ this._decorationContainer = document.createElement('div');
+ this._decorationContainer.classList.add('xterm-decoration-container');
+ this._screenElement.appendChild(this._decorationContainer);
+ }
+ const decoration = new BufferDecoration(this._bufferService, decorationOptions, this._decorationContainer);
+ this._decorations.push(decoration);
+ decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
+ this.refreshDecorations();
+ return decoration;
+ }
+ public override dispose(): void {
+ if (this._screenElement && this._decorationContainer && this._screenElement.contains(this._decorationContainer)) {
+ this._screenElement.removeChild(this._decorationContainer);
+ }
+ for (const bufferDecoration of this._decorations) {
+ bufferDecoration.dispose();
+ }
+ super.dispose();
+ }
+ public attachToDom(renderService: IRenderService): void {
+ this._renderService = renderService;
+ }
+}
+
+class OverviewRulerRenderer extends Disposable implements IDecorationRenderer {
+ private _canvas: HTMLCanvasElement | undefined;
+ private _ctx: CanvasRenderingContext2D | null = null;
+ private _decorations: ScrollbarDecoration[] = [];
+
+ constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService, private readonly _viewportElement: HTMLElement, private readonly _screenElement: HTMLElement) {
+ super();
+ this.register(this._bufferService.buffers.onBufferActivate(() => {
+ this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
+ }));
+ }
+ public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
+ if (!this._viewportElement.parentElement) {
+ return;
+ }
+ if (!this._canvas) {
+ this._canvas = document.createElement('canvas');
+ this._canvas.classList.add('xterm-decoration-scrollbar');
+ this._viewportElement.parentElement.insertBefore(this._canvas, this._viewportElement);
+ }
+ if (!this._ctx) {
+ this._ctx = this._canvas.getContext('2d');
+ this.refreshDecorations();
+ }
+ const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker: decorationOptions.marker, overviewRulerItemColor: decorationOptions.overviewRulerItemColor }, this._canvas, this._ctx!);
+ decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
+ this._decorations.push(decoration);
+ return decoration;
+ }
+ public refreshDecorations(): void {
+ if (!this._canvas || !this._ctx || !this._screenElement) {
+ return;
+ }
+ this._canvas.style.width = `${ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.height = `${this._screenElement.clientHeight}px`;
+ this._canvas.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
+ this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
+ for (const decoration of this._decorations) {
+ decoration.render();
+ }
+ }
+ public override dispose(): void {
+ for (const decoration of this._decorations) {
+ decoration.dispose();
+ }
+ this._decorations = [];
+ this._canvas?.remove();
+ super.dispose();
+ }
+}
+
export class DecorationService extends Disposable implements IDecorationService {
private _renderService: IRenderService | undefined;
@@ -21,55 +125,50 @@ export class DecorationService extends Disposable implements IDecorationService
private _screenElement: HTMLElement | undefined;
private _viewportElement: HTMLElement | undefined;
- private _bufferDecorationContainer: HTMLElement | undefined;
- private _overviewRulerCtx: CanvasRenderingContext2D | null = null;
- private _overviewRulerCanvas: HTMLCanvasElement | undefined;
- private readonly _bufferDecorations: BufferDecoration[] = [];
- private _overviewRulerDecorations: ScrollbarDecoration[] = [];
+ private _overviewRulerRenderer: OverviewRulerRenderer | undefined;
+ private _bufferDecorationRenderer: BufferDecorationRenderer | undefined;
- constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) { super(); }
+ constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) {
+ super();
+ }
public attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement): void {
this._renderService = renderService;
this._screenElement = screenElement;
this._viewportElement = viewportElement;
-
- this.register(this._renderService.onRenderedBufferChange(() => this._refresh()));
+ this.register(this._renderService.onRenderedBufferChange(() => this._queueRefresh()));
this.register(this._renderService.onDimensionsChange(() => this._refresh(true)));
- this.register(addDisposableDomListener(window, 'resize', () => this._refreshScollbarDecorations()));
- this.register(this._bufferService.buffers.onBufferActivate(() => {
- this._overviewRulerCanvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
- }));
+ this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
+ if (!this._bufferDecorationRenderer && this._viewportElement && this._screenElement) {
+ // TODO: allow registering before the viewport element exists
+ this._bufferDecorationRenderer = new BufferDecorationRenderer(this._bufferService, this._screenElement);
+ this._bufferDecorationRenderer.attachToDom(renderService);
+ }
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
if (decorationOptions.marker.isDisposed) {
return undefined;
}
- if (decorationOptions.overviewRulerItemColor) {
- return this._registerScrollbarDecoration(decorationOptions.marker, decorationOptions.overviewRulerItemColor);
+
+ if (decorationOptions.overviewRulerItemColor && this._viewportElement && this._screenElement) {
+ if (!this._overviewRulerRenderer) {
+ this._overviewRulerRenderer = new OverviewRulerRenderer(this._instantiationService, this._bufferService, this._viewportElement, this._screenElement);
+ }
+ return this._overviewRulerRenderer.registerDecoration(decorationOptions);
}
- return this._registerBufferDecoration(decorationOptions);
+ return this._bufferDecorationRenderer?.registerDecoration(decorationOptions);
}
public dispose(): void {
- for (const bufferDecoration of this._bufferDecorations) {
- bufferDecoration.dispose();
- }
- for (const scrollbarDecoration of this._overviewRulerDecorations) {
- scrollbarDecoration.dispose();
- }
- if (this._screenElement && this._bufferDecorationContainer && this._screenElement.contains(this._bufferDecorationContainer)) {
- this._screenElement.removeChild(this._bufferDecorationContainer);
- }
- this._overviewRulerDecorations = [];
- this._overviewRulerCanvas?.remove();
+ this._overviewRulerRenderer?.dispose();
+ this._bufferDecorationRenderer?.dispose();
}
private _refresh(shouldRecreate?: boolean): void {
- this._refreshBufferDecorations(shouldRecreate);
- this._refreshScollbarDecorations();
+ this._bufferDecorationRenderer?.refreshDecorations(shouldRecreate);
+ this._overviewRulerRenderer?.refreshDecorations();
}
private _queueRefresh(): void {
@@ -81,62 +180,6 @@ export class DecorationService extends Disposable implements IDecorationService
this._animationFrame = undefined;
});
}
-
- private _registerBufferDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (this._screenElement && !this._bufferDecorationContainer) {
- this._bufferDecorationContainer = document.createElement('div');
- this._bufferDecorationContainer.classList.add('xterm-decoration-container');
- this._screenElement.appendChild(this._bufferDecorationContainer);
- }
- const decoration = new BufferDecoration(this._bufferService, decorationOptions, this._bufferDecorationContainer);
- this._bufferDecorations.push(decoration);
- decoration.onDispose(() => this._bufferDecorations.splice(this._bufferDecorations.indexOf(decoration), 1));
- this._queueRefresh();
- return decoration;
- }
-
- private _registerScrollbarDecoration(marker: IMarker, color: string): IDecoration | undefined {
- if (!this._viewportElement?.parentElement) {
- return;
- }
- if (!this._overviewRulerCanvas) {
- // TODO: make this opt in, must be done before the scroll area in order to show up
- this._overviewRulerCanvas = document.createElement('canvas');
- this._overviewRulerCanvas.classList.add('xterm-decoration-scrollbar');
- this._viewportElement.parentElement.insertBefore(this._overviewRulerCanvas, this._viewportElement);
- }
- if (!this._overviewRulerCtx) {
- this._overviewRulerCtx = this._overviewRulerCanvas.getContext('2d');
- this._refreshScollbarDecorations();
- }
- const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker, overviewRulerItemColor: color }, this._overviewRulerCanvas, this._overviewRulerCtx!);
- decoration.onDispose(() => this._overviewRulerDecorations.splice(this._overviewRulerDecorations.indexOf(decoration), 1));
- this._overviewRulerDecorations.push(decoration);
- return decoration;
- }
-
- private _refreshBufferDecorations(shouldRecreate?: boolean): void {
- if (!this._renderService) {
- return;
- }
- for (const decoration of this._bufferDecorations) {
- decoration.render(this._renderService, shouldRecreate);
- }
- }
-
- private _refreshScollbarDecorations(): void {
- if (!this._overviewRulerCtx || !this._viewportElement || !this._overviewRulerCanvas) {
- return;
- }
- this._overviewRulerCanvas.style.width = `${ScrollbarConstants.WIDTH}px`;
- this._overviewRulerCanvas.style.height = `${this._screenElement!.clientHeight}px`;
- this._overviewRulerCanvas.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
- this._overviewRulerCanvas.height = Math.floor(this._screenElement!.clientHeight * window.devicePixelRatio);
- this._overviewRulerCtx.clearRect(0, 0, this._overviewRulerCtx.canvas.width, this._overviewRulerCtx.canvas.height);
- for (const decoration of this._overviewRulerDecorations) {
- decoration.render();
- }
- }
}
export class ScrollbarDecoration extends Disposable implements IDecoration {
From f025c0cf4e4df27234a137d1118e7b204d715283 Mon Sep 17 00:00:00 2001
From: Eugene Pankov
Date: Sat, 12 Mar 2022 20:56:02 +0100
Subject: [PATCH 040/245] ime: handle missing compositionend events for Sogou
IME
---
src/browser/Terminal.ts | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 08963933..f5684f84 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -92,6 +92,12 @@ export class Terminal extends CoreTerminal implements ITerminal {
*/
private _keyDownHandled: boolean = false;
+ /**
+ * Records whether a keydown event has occured since the last keyup event, i.e. whether a key
+ * is currently "pressed".
+ */
+ private _keyDownSeen: boolean = false;
+
/**
* Records whether the keypress event has already been handled and triggered a data event, if so
* the input event should not trigger a data event but should still print to the textarea so
@@ -1070,6 +1076,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
*/
protected _keyDown(event: KeyboardEvent): boolean | undefined {
this._keyDownHandled = false;
+ this._keyDownSeen = true;
if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) {
return false;
@@ -1155,6 +1162,8 @@ export class Terminal extends CoreTerminal implements ITerminal {
}
protected _keyUp(ev: KeyboardEvent): void {
+ this._keyDownSeen = false;
+
if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {
return;
}
@@ -1228,7 +1237,8 @@ export class Terminal extends CoreTerminal implements ITerminal {
protected _inputEvent(ev: InputEvent): boolean {
// Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to
// support reading out character input which can doubling up input characters
- if (ev.data && ev.inputType === 'insertText' && !ev.composed && !this.optionsService.rawOptions.screenReaderMode) {
+ // Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679
+ if (ev.data && ev.inputType === 'insertText' && (!ev.composed || !this._keyDownSeen) && !this.optionsService.rawOptions.screenReaderMode) {
if (this._keyPressHandled) {
return false;
}
From 9e598d9fba43562f42ff839183ee54e2e873b895 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 14 Mar 2022 09:30:30 -0400
Subject: [PATCH 041/245] allow setting width
---
src/browser/services/DecorationService.ts | 30 ++++++++++++++++++-----
typings/xterm.d.ts | 3 ++-
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index 2daa9e92..f3f5f1f2 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -70,8 +70,17 @@ class OverviewRulerRenderer extends Disposable implements IDecorationRenderer {
private _canvas: HTMLCanvasElement | undefined;
private _ctx: CanvasRenderingContext2D | null = null;
private _decorations: ScrollbarDecoration[] = [];
+ private _width: number | undefined;
+ private _anchor: 'right' | 'left' | undefined;
+ private _x: number | undefined;
- constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService, private readonly _viewportElement: HTMLElement, private readonly _screenElement: HTMLElement) {
+ constructor(
+ @IInstantiationService private readonly _instantiationService: IInstantiationService,
+ @IBufferService private readonly _bufferService: IBufferService,
+ @IRenderService private readonly _renderService: IRenderService,
+ private readonly _viewportElement: HTMLElement,
+ private readonly _screenElement: HTMLElement
+ ) {
super();
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
@@ -86,6 +95,10 @@ class OverviewRulerRenderer extends Disposable implements IDecorationRenderer {
this._canvas.classList.add('xterm-decoration-scrollbar');
this._viewportElement.parentElement.insertBefore(this._canvas, this._viewportElement);
}
+ this._width = decorationOptions.width;
+ this._anchor = decorationOptions.anchor;
+ this._x = decorationOptions.x;
+
if (!this._ctx) {
this._ctx = this._canvas.getContext('2d');
this.refreshDecorations();
@@ -96,13 +109,18 @@ class OverviewRulerRenderer extends Disposable implements IDecorationRenderer {
return decoration;
}
public refreshDecorations(): void {
- if (!this._canvas || !this._ctx || !this._screenElement) {
+ if (!this._canvas || !this._ctx || !this._screenElement || !this._renderService) {
return;
}
- this._canvas.style.width = `${ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.width = `${this._width || ScrollbarConstants.WIDTH}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor(ScrollbarConstants.WIDTH * window.devicePixelRatio);
+ this._canvas.width = Math.floor((this._width || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ if (this._anchor === 'right') {
+ this._canvas.style.right = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
+ } else {
+ this._canvas.style.left = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
+ }
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
for (const decoration of this._decorations) {
decoration.render();
@@ -148,13 +166,13 @@ export class DecorationService extends Disposable implements IDecorationService
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (decorationOptions.marker.isDisposed) {
+ if (decorationOptions.marker.isDisposed || !this._renderService) {
return undefined;
}
if (decorationOptions.overviewRulerItemColor && this._viewportElement && this._screenElement) {
if (!this._overviewRulerRenderer) {
- this._overviewRulerRenderer = new OverviewRulerRenderer(this._instantiationService, this._bufferService, this._viewportElement, this._screenElement);
+ this._overviewRulerRenderer = new OverviewRulerRenderer(this._instantiationService, this._bufferService, this._renderService, this._viewportElement, this._screenElement);
}
return this._overviewRulerRenderer.registerDecoration(decorationOptions);
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index cb952e70..00f4072f 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -461,7 +461,8 @@ declare module 'xterm' {
/**
* The width of the decoration in cells, which defaults to
- * cell width
+ * cell width or the width in pixels, when an overlayRulerItemColor
+ * is provided.
*/
width?: number;
From 3b279e4a4dd9cf49bab49d24f97a354dd30217e6 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 14 Mar 2022 09:52:12 -0400
Subject: [PATCH 042/245] round to the nearest pixel
---
src/browser/services/DecorationService.ts | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index f3f5f1f2..181588cd 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -28,9 +28,6 @@ class BufferDecorationRenderer extends Disposable implements IDecorationRenderer
@IBufferService private readonly _bufferService: IBufferService,
private readonly _screenElement: HTMLElement) {
super();
- this.register(this._bufferService.buffers.onBufferActivate(() => {
- // this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
- }));
}
public refreshDecorations(shouldRecreate?: boolean): void {
if (!this._renderService) {
@@ -207,9 +204,9 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
public isDisposed: boolean = false;
- public get element(): HTMLCanvasElement { return this._element!; }
+ public get element(): HTMLCanvasElement | undefined { return this._element; }
public get marker(): IMarker { return this._marker; }
- public get color(): string { return this._color!; }
+ public get color(): string | undefined { return this._color; }
private _onDispose = new EventEmitter();
public get onDispose(): IEvent { return this._onDispose.event; }
@@ -230,6 +227,9 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
this.render();
}
public render(): void {
+ if (!this.color) {
+ throw new Error('No color was provided for the overview ruler decoraiton');
+ }
if (!this._element) {
this._element = this._canvas;
}
@@ -237,21 +237,21 @@ export class ScrollbarDecoration extends Disposable implements IDecoration {
this._ctx.strokeStyle = this.color;
this._ctx.strokeRect(
0,
- this.element.height * (this.marker.line / this._bufferService.buffers.active.lines.length),
- this.element.width,
+ Math.round(this._element.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
+ this._element.width,
window.devicePixelRatio
);
- this._onRender.fire(this.element);
+ this._onRender.fire(this._element);
}
public override dispose(): void {
- if (this._isDisposed) {
+ if (this._isDisposed || !this._element) {
return;
}
this._ctx.clearRect(
0,
- this.element.height * (this.marker.line / this._bufferService.buffers.active.lines.length),
- this.element.width,
+ Math.round(this._element.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
+ this._element.width,
window.devicePixelRatio
);
this.isDisposed = true;
From ced06629d45129115fbb7e59de8dbd5a6eb16680 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 14 Mar 2022 15:11:20 -0400
Subject: [PATCH 043/245] large refactor, broken
---
.../Decorations/BufferDecorationRenderer.ts | 162 ++++++++
.../Decorations/OverviewRulerRenderer.ts | 150 ++++++++
src/browser/Terminal.ts | 28 +-
src/browser/Types.d.ts | 6 +
src/browser/services/DecorationService.ts | 361 ------------------
src/browser/services/Services.ts | 5 +-
src/browser/tsconfig.json | 2 +-
src/common/services/DecorationService.ts | 81 ++++
src/common/services/Services.ts | 9 +-
9 files changed, 431 insertions(+), 373 deletions(-)
create mode 100644 src/browser/Decorations/BufferDecorationRenderer.ts
create mode 100644 src/browser/Decorations/OverviewRulerRenderer.ts
delete mode 100644 src/browser/services/DecorationService.ts
create mode 100644 src/common/services/DecorationService.ts
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
new file mode 100644
index 00000000..56559ad9
--- /dev/null
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -0,0 +1,162 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { addDisposableDomListener } from 'browser/Lifecycle';
+import { IDecorationService, IRenderService } from 'browser/services/Services';
+import { IEvent, EventEmitter } from 'common/EventEmitter';
+import { Disposable } from 'common/Lifecycle';
+import { IBufferService } from 'common/services/Services';
+import { IMarker } from 'common/Types';
+import { IDecoration, IDecorationOptions } from 'xterm';
+
+export interface IDecorationRenderer {
+ refreshDecorations(shouldRecreate?: boolean): void;
+ renderDecoration(decoration: IDecoration, decorationOptions: IDecorationOptions): void;
+}
+
+export class BufferDecorationRenderer extends Disposable implements IDecorationRenderer {
+ private _decorationContainer: HTMLElement;
+ private readonly _decorations: BufferDecoration[] = [];
+ private _altBufferIsActive: boolean = false;
+
+ constructor(
+ @IBufferService private readonly _bufferService: IBufferService,
+ @IRenderService private readonly _renderService: IRenderService,
+ private readonly _decorationService: IDecorationService,
+ private readonly _screenElement: HTMLElement) {
+ super();
+ this._decorationContainer = document.createElement('div');
+ this._decorationContainer.classList.add('xterm-decoration-container');
+ this._screenElement.appendChild(this._decorationContainer);
+ this.register(this._renderService.onRenderedBufferChange(() => this.refreshDecorations()));
+ this.register(this._renderService.onDimensionsChange(() => this.refreshDecorations()));
+ this.register(addDisposableDomListener(window, 'resize', () => this.refreshDecorations()));
+ this.register(this._bufferService.buffers.onBufferActivate(() => {
+ this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
+ }));
+ this.register(this._decorationService.onDecorationRegistered(options => this.renderDecoration(options)));
+ }
+ public refreshDecorations(shouldRecreate?: boolean): void {
+ if (!this._renderService) {
+ return;
+ }
+ for (const decoration of this._decorations) {
+ decoration.render(this._decorationContainer, this._renderService, shouldRecreate);
+ }
+ }
+
+ public renderDecoration(decorationOptions: IDecorationOptions): void {
+ const decoration = new BufferDecoration(this._bufferService, decorationOptions);
+ if (this._decorationContainer && decoration.element && !this._decorationContainer.contains(decoration.element)) {
+ this._decorationContainer.append(decoration.element);
+ }
+ (decoration as BufferDecoration).render(this._decorationContainer, this._renderService, true);
+ }
+
+ public override dispose(): void {
+ if (this._screenElement && this._decorationContainer && this._screenElement.contains(this._decorationContainer)) {
+ this._screenElement.removeChild(this._decorationContainer);
+ }
+ for (const bufferDecoration of this._decorations) {
+ bufferDecoration.dispose();
+ }
+ super.dispose();
+ }
+}
+export class BufferDecoration extends Disposable implements IDecoration {
+ private readonly _marker: IMarker;
+ private _element: HTMLElement | undefined;
+ private _container: HTMLElement | undefined;
+ private _altBufferIsActive: boolean = false;
+
+ public isDisposed: boolean = false;
+
+ public get element(): HTMLElement | undefined { return this._element; }
+ public get marker(): IMarker { return this._marker; }
+
+ private _onDispose = new EventEmitter();
+ public get onDispose(): IEvent { return this._onDispose.event; }
+
+ private _onRender = new EventEmitter();
+ public get onRender(): IEvent { return this._onRender.event; }
+
+
+ public x: number;
+ public anchor: 'left' | 'right';
+ public width: number;
+ public height: number;
+
+ constructor(
+ private readonly _bufferService: IBufferService,
+ options: IDecorationOptions
+ ) {
+ super();
+ this.x = options.x ?? 0;
+ this._marker = options.marker;
+ this._marker.onDispose(() => this.dispose());
+ this.anchor = options.anchor || 'left';
+ this.width = options.width || 1;
+ this.height = options.height || 1;
+ }
+
+ public render(container: HTMLElement, renderService: IRenderService, shouldRecreate?: boolean): void {
+ this._container = container;
+ if (!this._element || shouldRecreate) {
+ this._createElement(renderService, shouldRecreate);
+ }
+ this._refreshStyle(renderService);
+ if (this._element) {
+ this._onRender.fire(this._element);
+ }
+ }
+
+ private _createElement(renderService: IRenderService, shouldRecreate?: boolean): void {
+ if (shouldRecreate && this._element && this._container && this._container.contains(this._element)) {
+ this._container.removeChild(this._element);
+ }
+ this._element = document.createElement('div');
+ this._element.classList.add('xterm-decoration');
+ this._element.style.width = `${this.width * renderService.dimensions.actualCellWidth}px`;
+ this._element.style.height = `${this.height * renderService.dimensions.actualCellHeight}px`;
+ this._element.style.top = `${(this.marker.line - this._bufferService.buffers.active.ydisp) * renderService.dimensions.actualCellHeight}px`;
+ this._element.style.lineHeight = `${renderService.dimensions.actualCellHeight}px`;
+
+ if (this.x && this.x > this._bufferService.cols) {
+ // exceeded the container width, so hide
+ this._element.style.display = 'none';
+ }
+ if (this.anchor === 'right') {
+ this._element.style.right = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
+ } else {
+ this._element.style.left = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
+ }
+ }
+
+ private _refreshStyle(renderService: IRenderService): void {
+ if (!this._element) {
+ return;
+ }
+ const line = this.marker.line - this._bufferService.buffers.active.ydisp;
+ if (line < 0 || line > this._bufferService.rows) {
+ // outside of viewport
+ this._element.style.display = 'none';
+ } else {
+ this._element.style.top = `${line * renderService.dimensions.actualCellHeight}px`;
+ this._element.style.display = this._altBufferIsActive ? 'none' : 'block';
+ }
+ }
+
+ public override dispose(): void {
+ if (this.isDisposed || !this._container) {
+ return;
+ }
+ if (this._element && this._container.contains(this._element)) {
+ this._container.removeChild(this._element);
+ }
+ this.isDisposed = true;
+ this._onDispose.fire();
+ }
+}
+
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
new file mode 100644
index 00000000..580a831a
--- /dev/null
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -0,0 +1,150 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { addDisposableDomListener } from 'browser/Lifecycle';
+import { IDecorationRenderer } from 'browser/Decorations/BufferDecorationRenderer';
+import { IDecorationService, IRenderService } from 'browser/services/Services';
+import { EventEmitter, IEvent } from 'common/EventEmitter';
+import { Disposable } from 'common/Lifecycle';
+import { IBufferService, IInstantiationService } from 'common/services/Services';
+import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
+const enum ScrollbarConstants {
+ WIDTH = 7
+}
+
+export class OverviewRulerRenderer extends Disposable implements IDecorationRenderer {
+ private _canvas: HTMLCanvasElement;
+ private _ctx: CanvasRenderingContext2D | null;
+ private _decorations: ScrollbarDecoration[] = [];
+ private _width: number | undefined;
+ private _anchor: 'right' | 'left' | undefined;
+ private _x: number | undefined;
+
+ constructor(
+ @IBufferService private readonly _bufferService: IBufferService,
+ @IRenderService private readonly _renderService: IRenderService,
+ @IDecorationService private readonly _decorationService: IDecorationService,
+ @IInstantiationService private readonly _instantiationService: IInstantiationService,
+ private readonly _viewportElement: HTMLElement,
+ private readonly _screenElement: HTMLElement
+ ) {
+ super();
+ this._canvas = document.createElement('canvas');
+ this._canvas.classList.add('xterm-decoration-scrollbar');
+ this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
+ this._ctx = this._canvas.getContext('2d');
+ this.refreshDecorations();
+ this.register(this._bufferService.buffers.onBufferActivate(() => {
+ this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
+ }));
+ this.register(this._renderService.onRenderedBufferChange(() => this.refreshDecorations()));
+ this.register(this._renderService.onDimensionsChange(() => this.refreshDecorations()));
+ this.register(addDisposableDomListener(window, 'resize', () => this.refreshDecorations()));
+ this.register(this._decorationService.onDecorationRegistered(e => this.renderDecoration(e)));
+ this.register(this._decorationService.onDecorationRemoved(d => d.dispose()));
+ }
+ public renderDecoration(decorationOptions: IDecorationOptions): void {
+ if (!this._ctx || !decorationOptions.overviewRulerItemColor) {
+ return;
+ }
+ const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker: decorationOptions.marker, overviewRulerItemColor: decorationOptions.overviewRulerItemColor });
+
+ this._ctx.lineWidth = 1;
+ this._ctx.strokeStyle = decorationOptions.overviewRulerItemColor;
+ this._ctx.strokeRect(
+ 0,
+ Math.round(this._canvas.height * (decoration.marker.line / this._bufferService.buffers.active.lines.length)),
+ this._canvas.width,
+ window.devicePixelRatio
+ );
+ }
+
+ public refreshDecorations(): void {
+ if (!this._ctx) {
+ return;
+ }
+ this._canvas.style.width = `${this._width || ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.height = `${this._screenElement.clientHeight}px`;
+ this._canvas.width = Math.floor((this._width || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
+ this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ if (this._anchor === 'right') {
+ this._canvas.style.right = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
+ } else {
+ this._canvas.style.left = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
+ }
+ this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
+ for (const decoration of this._decorations) {
+ decoration.render(this._ctx, this._canvas);
+ }
+ }
+ public override dispose(): void {
+ for (const decoration of this._decorations) {
+ decoration.dispose();
+ }
+ this._decorations = [];
+ this._canvas?.remove();
+ super.dispose();
+ }
+}
+class ScrollbarDecoration extends Disposable implements IDecoration {
+ private readonly _marker: IMarker;
+ private _ctx: CanvasRenderingContext2D | undefined;
+ private _canvas: HTMLCanvasElement | undefined;
+ private _color: string | undefined;
+
+ public isDisposed: boolean = false;
+
+ public get element(): HTMLCanvasElement | undefined { return this._canvas; }
+ public get marker(): IMarker { return this._marker; }
+ public get color(): string | undefined { return this._color; }
+
+ private _onDispose = new EventEmitter();
+ public get onDispose(): IEvent { return this._onDispose.event; }
+
+ private _onRender = new EventEmitter();
+ public get onRender(): IEvent { return this._onRender.event; }
+
+ constructor(
+ options: IDecorationOptions,
+ @IBufferService private readonly _bufferService: IBufferService
+ ) {
+ super();
+ this._marker = options.marker;
+ this._color = options.overviewRulerItemColor;
+ this._marker.onDispose(() => this.dispose());
+ }
+
+ public render(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void {
+ if (!this.color) {
+ throw new Error('No color was provided for the overview ruler decoraiton');
+ }
+ this._ctx = ctx;
+ this._canvas = canvas;
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = this.color;
+ ctx.strokeRect(
+ 0,
+ Math.round(canvas.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
+ canvas.width,
+ window.devicePixelRatio
+ );
+ this._onRender.fire(canvas);
+ }
+
+ public override dispose(): void {
+ if (this._isDisposed || !this._canvas || !this._ctx) {
+ return;
+ }
+ this._ctx.clearRect(
+ 0,
+ Math.round(this._canvas.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
+ this._canvas.width,
+ window.devicePixelRatio
+ );
+ this.isDisposed = true;
+ this._onDispose.fire();
+ super.dispose();
+ }
+}
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index ba0fc93d..39f0a68a 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -45,7 +45,7 @@ import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { ColorManager } from 'browser/ColorManager';
import { RenderService } from 'browser/services/RenderService';
-import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService, ICharacterJoinerService, IDecorationService } from 'browser/services/Services';
+import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService, ICharacterJoinerService } from 'browser/services/Services';
import { CharSizeService } from 'browser/services/CharSizeService';
import { IBuffer } from 'common/buffer/Types';
import { MouseService } from 'browser/services/MouseService';
@@ -55,7 +55,10 @@ import { CoreTerminal } from 'common/CoreTerminal';
import { color, rgba } from 'browser/Color';
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
import { toRgbString } from 'common/input/XParseColor';
-import { DecorationService } from 'browser/services/DecorationService';
+import { BufferDecorationRenderer } from 'browser/Decorations/BufferDecorationRenderer';
+import { OverviewRulerRenderer } from 'browser/Decorations/OverviewRulerRenderer';
+// import { IDecorationService } from 'common/services/Services';
+import { DecorationService } from 'common/services/DecorationService';
// Let it work inside Node.js for automated testing purposes.
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
@@ -71,6 +74,9 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _helperContainer: HTMLElement | undefined;
private _compositionView: HTMLElement | undefined;
+ private _overviewRulerRenderer: OverviewRulerRenderer | undefined;
+ private _bufferDecorationRenderer: BufferDecorationRenderer | undefined;
+
// private _visualBellTimer: number;
public browser: IBrowser = Browser as any;
@@ -81,6 +87,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _charSizeService: ICharSizeService | undefined;
private _mouseService: IMouseService | undefined;
private _renderService: IRenderService | undefined;
+ private _decorationService: DecorationService | undefined;
private _characterJoinerService: ICharacterJoinerService | undefined;
private _selectionService: ISelectionService | undefined;
private _soundService: ISoundService | undefined;
@@ -109,7 +116,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
public linkifier: ILinkifier;
public linkifier2: ILinkifier2;
public viewport: IViewport | undefined;
- public decorationService: IDecorationService;
private _compositionHelper: ICompositionHelper | undefined;
private _mouseZoneManager: IMouseZoneManager | undefined;
private _accessibilityManager: AccessibilityManager | undefined;
@@ -159,7 +165,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier = this._instantiationService.createInstance(Linkifier);
this.linkifier2 = this.register(this._instantiationService.createInstance(Linkifier2));
- this.decorationService = this.register(this._instantiationService.createInstance(DecorationService));
+ this._decorationService = this._instantiationService.createInstance(DecorationService);
// Setup InputHandler listeners
this.register(this._inputHandler.onRequestBell(() => this.bell()));
@@ -577,8 +583,16 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.register(this.onScroll(() => this._mouseZoneManager!.clearAll()));
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
-
- this.decorationService.attachToDom(this._renderService, this.screenElement, this._viewportElement);
+ if (this._decorationService) {
+ this._bufferDecorationRenderer = new BufferDecorationRenderer(this._bufferService, this._renderService, this._decorationService, this.screenElement);
+ }
+ // if (this.options.overviewRulerWidth && this._decorationService) {
+ // this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._viewportElement, this.screenElement);
+ // }
+ // this.optionsService.onOptionChange(() => {
+ // if (!this._overviewRulerRenderer && this.options.overviewRulerWidth && this._renderService && this._viewportElement && this.screenElement && this._decorationService) {
+ // this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._viewportElement, this.screenElement);
+ // }});
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
@@ -1004,7 +1018,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- return this.decorationService!.registerDecoration(decorationOptions);
+ return this._decorationService!.registerDecoration(decorationOptions);
}
/**
diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts
index 35b52d62..b8e1626e 100644
--- a/src/browser/Types.d.ts
+++ b/src/browser/Types.d.ts
@@ -9,6 +9,7 @@ import { ICoreTerminal, CharData, ITerminalOptions } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
import { IBuffer } from 'common/buffer/Types';
import { IFunctionIdentifier, IParams } from 'common/parser/Types';
+import { createDecorator } from 'common/services/ServiceRegistry';
export interface ITerminal extends IPublicTerminal, ICoreTerminal {
element: HTMLElement | undefined;
@@ -205,6 +206,11 @@ export interface ILinkifier {
registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number;
deregisterLinkMatcher(matcherId: number): boolean;
}
+export interface IDecorationService extends IDisposable {
+ readonly onDecorationRegistered: IEvent;
+ readonly onDecorationRemoved: IEvent;
+ registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
+}
interface ILinkState {
decorations: ILinkDecorations;
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
deleted file mode 100644
index 181588cd..00000000
--- a/src/browser/services/DecorationService.ts
+++ /dev/null
@@ -1,361 +0,0 @@
-/**
- * Copyright (c) 2022 The xterm.js authors. All rights reserved.
- * @license MIT
- */
-
-import { addDisposableDomListener } from 'browser/Lifecycle';
-import { IDecorationService, IRenderService } from 'browser/services/Services';
-import { EventEmitter, IEvent } from 'common/EventEmitter';
-import { Disposable } from 'common/Lifecycle';
-import { IBufferService, IInstantiationService } from 'common/services/Services';
-import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
-
-const enum ScrollbarConstants {
- WIDTH = 7
-}
-
-interface IDecorationRenderer {
- refreshDecorations(shouldRecreate?: boolean): void;
- registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
-}
-
-class BufferDecorationRenderer extends Disposable implements IDecorationRenderer {
- private _decorationContainer: HTMLElement | undefined;
- private readonly _decorations: BufferDecoration[] = [];
- private _renderService: IRenderService | undefined;
-
- constructor(
- @IBufferService private readonly _bufferService: IBufferService,
- private readonly _screenElement: HTMLElement) {
- super();
- }
- public refreshDecorations(shouldRecreate?: boolean): void {
- if (!this._renderService) {
- return;
- }
- for (const decoration of this._decorations) {
- decoration.render(this._renderService, shouldRecreate);
- }
- }
- public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (this._screenElement && !this._decorationContainer) {
- this._decorationContainer = document.createElement('div');
- this._decorationContainer.classList.add('xterm-decoration-container');
- this._screenElement.appendChild(this._decorationContainer);
- }
- const decoration = new BufferDecoration(this._bufferService, decorationOptions, this._decorationContainer);
- this._decorations.push(decoration);
- decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
- this.refreshDecorations();
- return decoration;
- }
- public override dispose(): void {
- if (this._screenElement && this._decorationContainer && this._screenElement.contains(this._decorationContainer)) {
- this._screenElement.removeChild(this._decorationContainer);
- }
- for (const bufferDecoration of this._decorations) {
- bufferDecoration.dispose();
- }
- super.dispose();
- }
- public attachToDom(renderService: IRenderService): void {
- this._renderService = renderService;
- }
-}
-
-class OverviewRulerRenderer extends Disposable implements IDecorationRenderer {
- private _canvas: HTMLCanvasElement | undefined;
- private _ctx: CanvasRenderingContext2D | null = null;
- private _decorations: ScrollbarDecoration[] = [];
- private _width: number | undefined;
- private _anchor: 'right' | 'left' | undefined;
- private _x: number | undefined;
-
- constructor(
- @IInstantiationService private readonly _instantiationService: IInstantiationService,
- @IBufferService private readonly _bufferService: IBufferService,
- @IRenderService private readonly _renderService: IRenderService,
- private readonly _viewportElement: HTMLElement,
- private readonly _screenElement: HTMLElement
- ) {
- super();
- this.register(this._bufferService.buffers.onBufferActivate(() => {
- this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
- }));
- }
- public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (!this._viewportElement.parentElement) {
- return;
- }
- if (!this._canvas) {
- this._canvas = document.createElement('canvas');
- this._canvas.classList.add('xterm-decoration-scrollbar');
- this._viewportElement.parentElement.insertBefore(this._canvas, this._viewportElement);
- }
- this._width = decorationOptions.width;
- this._anchor = decorationOptions.anchor;
- this._x = decorationOptions.x;
-
- if (!this._ctx) {
- this._ctx = this._canvas.getContext('2d');
- this.refreshDecorations();
- }
- const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker: decorationOptions.marker, overviewRulerItemColor: decorationOptions.overviewRulerItemColor }, this._canvas, this._ctx!);
- decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
- this._decorations.push(decoration);
- return decoration;
- }
- public refreshDecorations(): void {
- if (!this._canvas || !this._ctx || !this._screenElement || !this._renderService) {
- return;
- }
- this._canvas.style.width = `${this._width || ScrollbarConstants.WIDTH}px`;
- this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._width || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
- this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
- if (this._anchor === 'right') {
- this._canvas.style.right = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
- } else {
- this._canvas.style.left = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
- }
- this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
- for (const decoration of this._decorations) {
- decoration.render();
- }
- }
- public override dispose(): void {
- for (const decoration of this._decorations) {
- decoration.dispose();
- }
- this._decorations = [];
- this._canvas?.remove();
- super.dispose();
- }
-}
-
-export class DecorationService extends Disposable implements IDecorationService {
-
- private _renderService: IRenderService | undefined;
- private _animationFrame: number | undefined;
-
- private _screenElement: HTMLElement | undefined;
- private _viewportElement: HTMLElement | undefined;
-
- private _overviewRulerRenderer: OverviewRulerRenderer | undefined;
- private _bufferDecorationRenderer: BufferDecorationRenderer | undefined;
-
- constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService) {
- super();
- }
-
- public attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement): void {
- this._renderService = renderService;
- this._screenElement = screenElement;
- this._viewportElement = viewportElement;
- this.register(this._renderService.onRenderedBufferChange(() => this._queueRefresh()));
- this.register(this._renderService.onDimensionsChange(() => this._refresh(true)));
- this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
- if (!this._bufferDecorationRenderer && this._viewportElement && this._screenElement) {
- // TODO: allow registering before the viewport element exists
- this._bufferDecorationRenderer = new BufferDecorationRenderer(this._bufferService, this._screenElement);
- this._bufferDecorationRenderer.attachToDom(renderService);
- }
- }
-
- public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (decorationOptions.marker.isDisposed || !this._renderService) {
- return undefined;
- }
-
- if (decorationOptions.overviewRulerItemColor && this._viewportElement && this._screenElement) {
- if (!this._overviewRulerRenderer) {
- this._overviewRulerRenderer = new OverviewRulerRenderer(this._instantiationService, this._bufferService, this._renderService, this._viewportElement, this._screenElement);
- }
- return this._overviewRulerRenderer.registerDecoration(decorationOptions);
- }
- return this._bufferDecorationRenderer?.registerDecoration(decorationOptions);
- }
-
- public dispose(): void {
- this._overviewRulerRenderer?.dispose();
- this._bufferDecorationRenderer?.dispose();
- }
-
- private _refresh(shouldRecreate?: boolean): void {
- this._bufferDecorationRenderer?.refreshDecorations(shouldRecreate);
- this._overviewRulerRenderer?.refreshDecorations();
- }
-
- private _queueRefresh(): void {
- if (this._animationFrame !== undefined) {
- return;
- }
- this._animationFrame = window.requestAnimationFrame(() => {
- this._refresh();
- this._animationFrame = undefined;
- });
- }
-}
-
-export class ScrollbarDecoration extends Disposable implements IDecoration {
- private readonly _marker: IMarker;
- private _element: HTMLCanvasElement | undefined;
- private _color: string | undefined;
-
- public isDisposed: boolean = false;
-
- public get element(): HTMLCanvasElement | undefined { return this._element; }
- public get marker(): IMarker { return this._marker; }
- public get color(): string | undefined { return this._color; }
-
- private _onDispose = new EventEmitter();
- public get onDispose(): IEvent { return this._onDispose.event; }
-
- private _onRender = new EventEmitter();
- public get onRender(): IEvent { return this._onRender.event; }
-
- constructor(
- options: IDecorationOptions,
- private readonly _canvas: HTMLCanvasElement,
- private readonly _ctx: CanvasRenderingContext2D,
- @IBufferService private readonly _bufferService: IBufferService
- ) {
- super();
- this._marker = options.marker;
- this._color = options.overviewRulerItemColor;
- this._marker.onDispose(() => this.dispose());
- this.render();
- }
- public render(): void {
- if (!this.color) {
- throw new Error('No color was provided for the overview ruler decoraiton');
- }
- if (!this._element) {
- this._element = this._canvas;
- }
- this._ctx.lineWidth = 1;
- this._ctx.strokeStyle = this.color;
- this._ctx.strokeRect(
- 0,
- Math.round(this._element.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
- this._element.width,
- window.devicePixelRatio
- );
- this._onRender.fire(this._element);
- }
-
- public override dispose(): void {
- if (this._isDisposed || !this._element) {
- return;
- }
- this._ctx.clearRect(
- 0,
- Math.round(this._element.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
- this._element.width,
- window.devicePixelRatio
- );
- this.isDisposed = true;
- this._onDispose.fire();
- super.dispose();
- }
-}
-
-export class BufferDecoration extends Disposable implements IDecoration {
- private readonly _marker: IMarker;
- private _element: HTMLElement | undefined;
-
- public isDisposed: boolean = false;
-
- public get element(): HTMLElement | undefined { return this._element; }
- public get marker(): IMarker { return this._marker; }
-
- private _onDispose = new EventEmitter();
- public get onDispose(): IEvent { return this._onDispose.event; }
-
- private _onRender = new EventEmitter();
- public get onRender(): IEvent { return this._onRender.event; }
-
- private _altBufferIsActive: boolean = false;
-
- public x: number;
- public anchor: 'left' | 'right';
- public width: number;
- public height: number;
-
- constructor(
- private readonly _bufferService: IBufferService,
- options: IDecorationOptions,
- private readonly _container?: HTMLElement
- ) {
- super();
- this.x = options.x ?? 0;
- this._marker = options.marker;
- this._marker.onDispose(() => this.dispose());
- this.anchor = options.anchor || 'left';
- this.width = options.width || 1;
- this.height = options.height || 1;
- this.register(this._bufferService.buffers.onBufferActivate(() => {
- this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
- }));
- }
-
- public render(renderService: IRenderService, shouldRecreate?: boolean): void {
- if (!this._element || shouldRecreate) {
- this._createElement(renderService, shouldRecreate);
- }
- if (this._container && this._element && !this._container.contains(this._element)) {
- this._container.append(this._element);
- }
- this._refreshStyle(renderService);
- if (this._element) {
- this._onRender.fire(this._element);
- }
- }
-
- private _createElement(renderService: IRenderService, shouldRecreate?: boolean): void {
- if (shouldRecreate && this._element && this._container && this._container.contains(this._element)) {
- this._container.removeChild(this._element);
- }
- this._element = document.createElement('div');
- this._element.classList.add('xterm-decoration');
- this._element.style.width = `${this.width * renderService.dimensions.actualCellWidth}px`;
- this._element.style.height = `${this.height * renderService.dimensions.actualCellHeight}px`;
- this._element.style.top = `${(this.marker.line - this._bufferService.buffers.active.ydisp) * renderService.dimensions.actualCellHeight}px`;
- this._element.style.lineHeight = `${renderService.dimensions.actualCellHeight}px`;
-
- if (this.x && this.x > this._bufferService.cols) {
- // exceeded the container width, so hide
- this._element.style.display = 'none';
- }
- if (this.anchor === 'right') {
- this._element.style.right = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
- } else {
- this._element.style.left = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
- }
- }
-
- private _refreshStyle(renderService: IRenderService): void {
- if (!this._element) {
- return;
- }
- const line = this.marker.line - this._bufferService.buffers.active.ydisp;
- if (line < 0 || line > this._bufferService.rows) {
- // outside of viewport
- this._element.style.display = 'none';
- } else {
- this._element.style.top = `${line * renderService.dimensions.actualCellHeight}px`;
- this._element.style.display = this._altBufferIsActive ? 'none' : 'block';
- }
- }
-
- public override dispose(): void {
- if (this.isDisposed || !this._container) {
- return;
- }
- if (this._element && this._container.contains(this._element)) {
- this._container.removeChild(this._element);
- }
- this.isDisposed = true;
- this._onDispose.fire();
- }
-}
diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts
index 2d1f3d03..8587a5c9 100644
--- a/src/browser/services/Services.ts
+++ b/src/browser/services/Services.ts
@@ -115,10 +115,9 @@ export interface ICharacterJoinerService {
deregister(joinerId: number): boolean;
getJoinedCharacters(row: number): [number, number][];
}
-
-
export const IDecorationService = createDecorator('DecorationService');
export interface IDecorationService extends IDisposable {
- attachToDom(renderService: IRenderService, screenElement: HTMLElement, viewportElement: HTMLElement): void;
+ readonly onDecorationRegistered: IEvent;
+ readonly onDecorationRemoved: IEvent;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
}
diff --git a/src/browser/tsconfig.json b/src/browser/tsconfig.json
index 212aeab8..fc997477 100644
--- a/src/browser/tsconfig.json
+++ b/src/browser/tsconfig.json
@@ -18,7 +18,7 @@
"include": [
"./**/*",
"../../typings/xterm.d.ts"
- ],
+],
"references": [
{ "path": "../common" }
]
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
new file mode 100644
index 00000000..206d50e8
--- /dev/null
+++ b/src/common/services/DecorationService.ts
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2022 The xterm.js authors. All rights reserved.
+ * @license MIT
+ */
+
+
+import { EventEmitter } from 'common/EventEmitter';
+import { Disposable } from 'common/Lifecycle';
+import { IBufferService, IInstantiationService, IDecorationService } from 'common/services/Services';
+import { IDecorationOptions, IDecoration, IMarker, IDisposable, IEvent } from 'xterm';
+
+export class DecorationService extends Disposable implements IDecorationService {
+ private _animationFrame: number | undefined;
+ private _onDecorationRegistered = this.register(new EventEmitter());
+ public get onDecorationRegistered(): IEvent { return this._onDecorationRegistered.event; }
+ private _onDecorationRemoved = this.register(new EventEmitter());
+ public get onDecorationRemoved(): IEvent { return this._onDecorationRemoved.event; }
+ private _decorations: IDecoration[] = [];
+
+ constructor() {
+ super();
+ }
+
+ public registerDecoration(options: IDecorationOptions): IDecoration | undefined {
+ if (options.marker.isDisposed) {
+ return undefined;
+ }
+ const decoration = new Decoration(options);
+ if (decoration) {
+ decoration.onDispose(() => {
+ if (decoration) {
+ this._decorations.splice(this._decorations.indexOf(decoration), 1);
+ }
+ });
+ this._decorations.push(decoration);
+ this._onDecorationRegistered.fire(options);
+ decoration.onRender(d => {
+ decoration.setElement(d);
+ });
+ }
+ return decoration;
+ }
+
+ public dispose(): void {
+ for (const decoration of this._decorations) {
+ this._onDecorationRemoved.fire(decoration);
+ decoration.dispose();
+ }
+ this._decorations = [];
+ }
+
+ private _queueRefresh(): void {
+ if (this._animationFrame !== undefined) {
+ return;
+ }
+ this._animationFrame = window.requestAnimationFrame(() => {
+ // this._refresh();
+ this._animationFrame = undefined;
+ });
+ }
+}
+
+class Decoration implements IDecoration {
+ public marker: IMarker;
+ private _onRender = new EventEmitter();
+ public get onRender(): IEvent { return this._onRender.event; }
+ private _onDispose = new EventEmitter();
+ public get onDispose(): IEvent { return this._onDispose.event; }
+ public element: HTMLElement | undefined;
+ public isDisposed: boolean = false;
+ public dispose(): void {
+ throw new Error('Method not implemented.');
+ }
+ constructor(decorationOptions?: IDecorationOptions) {
+ this.marker = decorationOptions?.marker!;
+ this.element = undefined;
+ }
+ public setElement(element: HTMLElement): void {
+ this.element = element;
+ }
+}
diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts
index 90dca988..8bd272f0 100644
--- a/src/common/services/Services.ts
+++ b/src/common/services/Services.ts
@@ -5,8 +5,9 @@
import { IEvent } from 'common/EventEmitter';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
-import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource } from 'common/Types';
+import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable } from 'common/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
+import { IDecorationOptions, IDecoration } from 'xterm';
export const IBufferService = createDecorator('BufferService');
export interface IBufferService {
@@ -245,6 +246,7 @@ export interface ITerminalOptions {
windowsMode: boolean;
windowOptions: IWindowOptions;
wordSeparator: string;
+ overviewRulerWidth?: number;
[key: string]: any;
cancelEvents: boolean;
@@ -298,3 +300,8 @@ export interface IUnicodeVersionProvider {
readonly version: string;
wcwidth(ucs: number): 0 | 1 | 2;
}
+export interface IDecorationService extends IDisposable {
+ readonly onDecorationRegistered: IEvent;
+ readonly onDecorationRemoved: IEvent;
+ registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
+}
From 7dc3332b05d91d5a00857bfb361aab2f309578be Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 14 Mar 2022 15:50:40 -0400
Subject: [PATCH 044/245] commit before reverting
---
demo/client.ts | 7 ++++---
.../Decorations/BufferDecorationRenderer.ts | 10 +++++++---
src/browser/Terminal.ts | 14 +++++++-------
src/common/services/DecorationService.ts | 14 ++++----------
4 files changed, 22 insertions(+), 23 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 586114d1..0f2148c9 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -547,8 +547,9 @@ function loadTest() {
function addDecoration() {
const marker = term.addMarker(1);
const decoration = term.registerDecoration({ marker });
- decoration.onRender(() => {
- decoration.element.style.backgroundColor = 'red';
+ decoration.onRender((e) => {
+ console.log(e);
+ e.style.backgroundColor = 'red';
});
}
@@ -557,5 +558,5 @@ function addOverviewRuler() {
canvas.element!.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
term.registerDecoration({marker: term.addMarker(3), overviewRulerItemColor: 'green'});
term.registerDecoration({marker: term.addMarker(5), overviewRulerItemColor: 'blue'});
-}
+}
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 56559ad9..efb97c16 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -48,11 +48,14 @@ export class BufferDecorationRenderer extends Disposable implements IDecorationR
}
public renderDecoration(decorationOptions: IDecorationOptions): void {
- const decoration = new BufferDecoration(this._bufferService, decorationOptions);
- if (this._decorationContainer && decoration.element && !this._decorationContainer.contains(decoration.element)) {
- this._decorationContainer.append(decoration.element);
+ if (decorationOptions.overviewRulerItemColor) {
+ return;
}
+ const decoration = new BufferDecoration(this._bufferService, decorationOptions);
(decoration as BufferDecoration).render(this._decorationContainer, this._renderService, true);
+ if (this._decorationContainer && decoration.element && !this._decorationContainer.contains(decoration.element)) {
+ this._decorationContainer.append(decoration.element!);
+ }
}
public override dispose(): void {
@@ -108,6 +111,7 @@ export class BufferDecoration extends Disposable implements IDecoration {
}
this._refreshStyle(renderService);
if (this._element) {
+ console.log('firing on render');
this._onRender.fire(this._element);
}
}
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 39f0a68a..2c00d79b 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -586,13 +586,13 @@ export class Terminal extends CoreTerminal implements ITerminal {
if (this._decorationService) {
this._bufferDecorationRenderer = new BufferDecorationRenderer(this._bufferService, this._renderService, this._decorationService, this.screenElement);
}
- // if (this.options.overviewRulerWidth && this._decorationService) {
- // this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._viewportElement, this.screenElement);
- // }
- // this.optionsService.onOptionChange(() => {
- // if (!this._overviewRulerRenderer && this.options.overviewRulerWidth && this._renderService && this._viewportElement && this.screenElement && this._decorationService) {
- // this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._viewportElement, this.screenElement);
- // }});
+ if (this.options.overviewRulerWidth && this._decorationService) {
+ this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._instantiationService, this._viewportElement, this.screenElement);
+ }
+ this.optionsService.onOptionChange(() => {
+ if (!this._overviewRulerRenderer && this.options.overviewRulerWidth && this._renderService && this._viewportElement && this.screenElement && this._decorationService) {
+ this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._instantiationService, this._viewportElement, this.screenElement);
+ }});
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 206d50e8..1bd67e0c 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -6,8 +6,8 @@
import { EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
-import { IBufferService, IInstantiationService, IDecorationService } from 'common/services/Services';
-import { IDecorationOptions, IDecoration, IMarker, IDisposable, IEvent } from 'xterm';
+import { IDecorationService } from 'common/services/Services';
+import { IDecorationOptions, IDecoration, IMarker, IEvent } from 'xterm';
export class DecorationService extends Disposable implements IDecorationService {
private _animationFrame: number | undefined;
@@ -34,9 +34,6 @@ export class DecorationService extends Disposable implements IDecorationService
});
this._decorations.push(decoration);
this._onDecorationRegistered.fire(options);
- decoration.onRender(d => {
- decoration.setElement(d);
- });
}
return decoration;
}
@@ -71,11 +68,8 @@ class Decoration implements IDecoration {
public dispose(): void {
throw new Error('Method not implemented.');
}
- constructor(decorationOptions?: IDecorationOptions) {
- this.marker = decorationOptions?.marker!;
+ constructor(decorationOptions: IDecorationOptions) {
+ this.marker = decorationOptions?.marker;
this.element = undefined;
}
- public setElement(element: HTMLElement): void {
- this.element = element;
- }
}
From c8d1266f0ae82495f34be7ed6d742a472fb0df77 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Mon, 14 Mar 2022 15:24:53 -0700
Subject: [PATCH 045/245] Fix buffer decoration onRender, start using internal
decoration
---
demo/client.ts | 2 +-
.../Decorations/BufferDecorationRenderer.ts | 52 ++++++++++++-------
.../Decorations/OverviewRulerRenderer.ts | 14 ++---
src/browser/Terminal.ts | 5 +-
src/browser/Types.d.ts | 5 --
src/browser/services/Services.ts | 6 ---
src/common/services/DecorationService.ts | 28 +++++-----
src/common/services/Services.ts | 12 +++--
8 files changed, 70 insertions(+), 54 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 0f2148c9..55e97ed4 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -548,7 +548,7 @@ function addDecoration() {
const marker = term.addMarker(1);
const decoration = term.registerDecoration({ marker });
decoration.onRender((e) => {
- console.log(e);
+ console.log('onRender', e);
e.style.backgroundColor = 'red';
});
}
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index efb97c16..5b97130e 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -4,19 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import { addDisposableDomListener } from 'browser/Lifecycle';
-import { IDecorationService, IRenderService } from 'browser/services/Services';
+import { IRenderService } from 'browser/services/Services';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
-import { IBufferService } from 'common/services/Services';
+import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
import { IMarker } from 'common/Types';
import { IDecoration, IDecorationOptions } from 'xterm';
export interface IDecorationRenderer {
refreshDecorations(shouldRecreate?: boolean): void;
- renderDecoration(decoration: IDecoration, decorationOptions: IDecorationOptions): void;
+ renderDecoration(decoration: IInternalDecoration, decorationOptions: IDecorationOptions): void;
}
export class BufferDecorationRenderer extends Disposable implements IDecorationRenderer {
+ private _animationFrame: number | undefined;
private _decorationContainer: HTMLElement;
private readonly _decorations: BufferDecoration[] = [];
private _altBufferIsActive: boolean = false;
@@ -30,32 +31,40 @@ export class BufferDecorationRenderer extends Disposable implements IDecorationR
this._decorationContainer = document.createElement('div');
this._decorationContainer.classList.add('xterm-decoration-container');
this._screenElement.appendChild(this._decorationContainer);
- this.register(this._renderService.onRenderedBufferChange(() => this.refreshDecorations()));
- this.register(this._renderService.onDimensionsChange(() => this.refreshDecorations()));
- this.register(addDisposableDomListener(window, 'resize', () => this.refreshDecorations()));
+ this.register(this._renderService.onRenderedBufferChange(() => this._queueRefresh()));
+ this.register(this._renderService.onDimensionsChange(() => this._queueRefresh()));
+ this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
}));
this.register(this._decorationService.onDecorationRegistered(options => this.renderDecoration(options)));
}
- public refreshDecorations(shouldRecreate?: boolean): void {
- if (!this._renderService) {
+
+ private _queueRefresh(): void {
+ if (this._animationFrame !== undefined) {
return;
}
+ this._animationFrame = window.requestAnimationFrame(() => {
+ this.refreshDecorations();
+ this._animationFrame = undefined;
+ });
+ }
+
+ public refreshDecorations(shouldRecreate?: boolean): void {
+ console.log('refresh decorations', this._decorations.length);
for (const decoration of this._decorations) {
decoration.render(this._decorationContainer, this._renderService, shouldRecreate);
}
}
- public renderDecoration(decorationOptions: IDecorationOptions): void {
- if (decorationOptions.overviewRulerItemColor) {
- return;
- }
- const decoration = new BufferDecoration(this._bufferService, decorationOptions);
- (decoration as BufferDecoration).render(this._decorationContainer, this._renderService, true);
- if (this._decorationContainer && decoration.element && !this._decorationContainer.contains(decoration.element)) {
- this._decorationContainer.append(decoration.element!);
+ public renderDecoration(decoration: IInternalDecoration): void {
+ const bufferDecoration = new BufferDecoration(this._bufferService, decoration, decoration.options);
+ this._decorations.push(bufferDecoration);
+ // bufferDecoration.render(this._decorationContainer, this._renderService, true);
+ if (this._decorationContainer && bufferDecoration.element && !this._decorationContainer.contains(bufferDecoration.element)) {
+ this._decorationContainer.append(bufferDecoration.element!);
}
+ this._queueRefresh();
}
public override dispose(): void {
@@ -68,6 +77,7 @@ export class BufferDecorationRenderer extends Disposable implements IDecorationR
super.dispose();
}
}
+
export class BufferDecoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
private _element: HTMLElement | undefined;
@@ -85,7 +95,6 @@ export class BufferDecoration extends Disposable implements IDecoration {
private _onRender = new EventEmitter();
public get onRender(): IEvent { return this._onRender.event; }
-
public x: number;
public anchor: 'left' | 'right';
public width: number;
@@ -93,6 +102,7 @@ export class BufferDecoration extends Disposable implements IDecoration {
constructor(
private readonly _bufferService: IBufferService,
+ private readonly _internalDecoration: IInternalDecoration,
options: IDecorationOptions
) {
super();
@@ -107,16 +117,18 @@ export class BufferDecoration extends Disposable implements IDecoration {
public render(container: HTMLElement, renderService: IRenderService, shouldRecreate?: boolean): void {
this._container = container;
if (!this._element || shouldRecreate) {
- this._createElement(renderService, shouldRecreate);
+ const element = this._createElement(renderService, shouldRecreate);
+ this._container.appendChild(element);
}
this._refreshStyle(renderService);
if (this._element) {
console.log('firing on render');
this._onRender.fire(this._element);
+ this._internalDecoration.onRenderEmitter.fire(this._element!);
}
}
- private _createElement(renderService: IRenderService, shouldRecreate?: boolean): void {
+ private _createElement(renderService: IRenderService, shouldRecreate?: boolean): HTMLElement {
if (shouldRecreate && this._element && this._container && this._container.contains(this._element)) {
this._container.removeChild(this._element);
}
@@ -136,6 +148,8 @@ export class BufferDecoration extends Disposable implements IDecoration {
} else {
this._element.style.left = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
}
+
+ return this._element;
}
private _refreshStyle(renderService: IRenderService): void {
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 580a831a..5f5fc397 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -5,10 +5,10 @@
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IDecorationRenderer } from 'browser/Decorations/BufferDecorationRenderer';
-import { IDecorationService, IRenderService } from 'browser/services/Services';
+import { IRenderService } from 'browser/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
-import { IBufferService, IInstantiationService } from 'common/services/Services';
+import { IBufferService, IDecorationService, IInstantiationService, IInternalDecoration } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
const enum ScrollbarConstants {
WIDTH = 7
@@ -45,14 +45,16 @@ export class OverviewRulerRenderer extends Disposable implements IDecorationRend
this.register(this._decorationService.onDecorationRegistered(e => this.renderDecoration(e)));
this.register(this._decorationService.onDecorationRemoved(d => d.dispose()));
}
- public renderDecoration(decorationOptions: IDecorationOptions): void {
- if (!this._ctx || !decorationOptions.overviewRulerItemColor) {
+ public renderDecoration(decoration: IInternalDecoration): void {
+ if (!this._ctx || !decoration.options.overviewRulerItemColor) {
return;
}
- const decoration = this._instantiationService.createInstance(ScrollbarDecoration, { marker: decorationOptions.marker, overviewRulerItemColor: decorationOptions.overviewRulerItemColor });
+
+ // TODO: Does this do anything anymore?
+ this._instantiationService.createInstance(ScrollbarDecoration, { marker: decoration.options.marker, overviewRulerItemColor: decoration.options.overviewRulerItemColor });
this._ctx.lineWidth = 1;
- this._ctx.strokeStyle = decorationOptions.overviewRulerItemColor;
+ this._ctx.strokeStyle = decoration.options.overviewRulerItemColor;
this._ctx.strokeRect(
0,
Math.round(this._canvas.height * (decoration.marker.line / this._bufferService.buffers.active.lines.length)),
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 2c00d79b..d07b9c30 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -83,11 +83,14 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _customKeyEventHandler: CustomKeyEventHandler | undefined;
+ // TODO: Move into CoreTerminal.ts
+ // common services
+ private _decorationService: DecorationService;
+
// browser services
private _charSizeService: ICharSizeService | undefined;
private _mouseService: IMouseService | undefined;
private _renderService: IRenderService | undefined;
- private _decorationService: DecorationService | undefined;
private _characterJoinerService: ICharacterJoinerService | undefined;
private _selectionService: ISelectionService | undefined;
private _soundService: ISoundService | undefined;
diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts
index b8e1626e..8860bb41 100644
--- a/src/browser/Types.d.ts
+++ b/src/browser/Types.d.ts
@@ -206,11 +206,6 @@ export interface ILinkifier {
registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number;
deregisterLinkMatcher(matcherId: number): boolean;
}
-export interface IDecorationService extends IDisposable {
- readonly onDecorationRegistered: IEvent;
- readonly onDecorationRemoved: IEvent;
- registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
-}
interface ILinkState {
decorations: ILinkDecorations;
diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts
index 8587a5c9..1598ef02 100644
--- a/src/browser/services/Services.ts
+++ b/src/browser/services/Services.ts
@@ -115,9 +115,3 @@ export interface ICharacterJoinerService {
deregister(joinerId: number): boolean;
getJoinedCharacters(row: number): [number, number][];
}
-export const IDecorationService = createDecorator('DecorationService');
-export interface IDecorationService extends IDisposable {
- readonly onDecorationRegistered: IEvent;
- readonly onDecorationRemoved: IEvent;
- registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
-}
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 1bd67e0c..7c49c7b4 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -6,16 +6,16 @@
import { EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
-import { IDecorationService } from 'common/services/Services';
+import { IDecorationService, IInternalDecoration } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker, IEvent } from 'xterm';
export class DecorationService extends Disposable implements IDecorationService {
private _animationFrame: number | undefined;
- private _onDecorationRegistered = this.register(new EventEmitter());
- public get onDecorationRegistered(): IEvent { return this._onDecorationRegistered.event; }
- private _onDecorationRemoved = this.register(new EventEmitter());
- public get onDecorationRemoved(): IEvent { return this._onDecorationRemoved.event; }
- private _decorations: IDecoration[] = [];
+ private _onDecorationRegistered = this.register(new EventEmitter());
+ public get onDecorationRegistered(): IEvent { return this._onDecorationRegistered.event; }
+ private _onDecorationRemoved = this.register(new EventEmitter());
+ public get onDecorationRemoved(): IEvent { return this._onDecorationRemoved.event; }
+ private _decorations: IInternalDecoration[] = [];
constructor() {
super();
@@ -33,7 +33,7 @@ export class DecorationService extends Disposable implements IDecorationService
}
});
this._decorations.push(decoration);
- this._onDecorationRegistered.fire(options);
+ this._onDecorationRegistered.fire(decoration);
}
return decoration;
}
@@ -57,19 +57,21 @@ export class DecorationService extends Disposable implements IDecorationService
}
}
-class Decoration implements IDecoration {
+class Decoration implements IInternalDecoration {
public marker: IMarker;
- private _onRender = new EventEmitter();
- public get onRender(): IEvent { return this._onRender.event; }
+ public readonly onRenderEmitter = new EventEmitter();
+ public readonly onRender = this.onRenderEmitter.event;
private _onDispose = new EventEmitter();
- public get onDispose(): IEvent { return this._onDispose.event; }
+ public readonly onDispose = this._onDispose.event;
public element: HTMLElement | undefined;
public isDisposed: boolean = false;
public dispose(): void {
throw new Error('Method not implemented.');
}
- constructor(decorationOptions: IDecorationOptions) {
- this.marker = decorationOptions?.marker;
+ constructor(
+ public readonly options: IDecorationOptions
+ ) {
+ this.marker = options.marker;
this.element = undefined;
}
}
diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts
index 8bd272f0..62108ffb 100644
--- a/src/common/services/Services.ts
+++ b/src/common/services/Services.ts
@@ -3,7 +3,7 @@
* @license MIT
*/
-import { IEvent } from 'common/EventEmitter';
+import { IEvent, IEventEmitter } from 'common/EventEmitter';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable } from 'common/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
@@ -300,8 +300,14 @@ export interface IUnicodeVersionProvider {
readonly version: string;
wcwidth(ucs: number): 0 | 1 | 2;
}
+
+export const IDecorationService = createDecorator('DecorationService');
export interface IDecorationService extends IDisposable {
- readonly onDecorationRegistered: IEvent;
- readonly onDecorationRemoved: IEvent;
+ readonly onDecorationRegistered: IEvent;
+ readonly onDecorationRemoved: IEvent;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
}
+export interface IInternalDecoration extends IDecoration {
+ readonly options: IDecorationOptions;
+ readonly onRenderEmitter: IEventEmitter;
+}
From 4b615f48ff07627a031b0175b5039f8a03b9280e Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Mon, 14 Mar 2022 15:48:09 -0700
Subject: [PATCH 046/245] Reduce state in buffer decorations
---
.../Decorations/BufferDecorationRenderer.ts | 168 +++++-------------
.../Decorations/OverviewRulerRenderer.ts | 8 +-
src/common/services/DecorationService.ts | 2 +
src/common/services/Services.ts | 1 +
src/tsconfig-library-base.json | 3 +-
5 files changed, 55 insertions(+), 127 deletions(-)
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 5b97130e..9e3b2d72 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -5,39 +5,41 @@
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IRenderService } from 'browser/services/Services';
-import { IEvent, EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
-import { IMarker } from 'common/Types';
-import { IDecoration, IDecorationOptions } from 'xterm';
-export interface IDecorationRenderer {
- refreshDecorations(shouldRecreate?: boolean): void;
- renderDecoration(decoration: IInternalDecoration, decorationOptions: IDecorationOptions): void;
-}
+export class BufferDecorationRenderer extends Disposable {
+ private readonly _container: HTMLElement;
+ private readonly _decorationElements: Map = new Map();
-export class BufferDecorationRenderer extends Disposable implements IDecorationRenderer {
private _animationFrame: number | undefined;
- private _decorationContainer: HTMLElement;
- private readonly _decorations: BufferDecoration[] = [];
private _altBufferIsActive: boolean = false;
constructor(
@IBufferService private readonly _bufferService: IBufferService,
@IRenderService private readonly _renderService: IRenderService,
private readonly _decorationService: IDecorationService,
- private readonly _screenElement: HTMLElement) {
+ private readonly _screenElement: HTMLElement
+ ) {
super();
- this._decorationContainer = document.createElement('div');
- this._decorationContainer.classList.add('xterm-decoration-container');
- this._screenElement.appendChild(this._decorationContainer);
+
+ this._container = document.createElement('div');
+ this._container.classList.add('xterm-decoration-container');
+ this._screenElement.appendChild(this._container);
+
this.register(this._renderService.onRenderedBufferChange(() => this._queueRefresh()));
this.register(this._renderService.onDimensionsChange(() => this._queueRefresh()));
this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
}));
- this.register(this._decorationService.onDecorationRegistered(options => this.renderDecoration(options)));
+ this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
+ }
+
+ public override dispose(): void {
+ this._container.remove();
+ this._decorationElements.clear();
+ super.dispose();
}
private _queueRefresh(): void {
@@ -50,131 +52,53 @@ export class BufferDecorationRenderer extends Disposable implements IDecorationR
});
}
- public refreshDecorations(shouldRecreate?: boolean): void {
- console.log('refresh decorations', this._decorations.length);
- for (const decoration of this._decorations) {
- decoration.render(this._decorationContainer, this._renderService, shouldRecreate);
+ public refreshDecorations(): void {
+ for (const decoration of this._decorationService.decorations) {
+ this._renderDecoration(decoration);
}
}
- public renderDecoration(decoration: IInternalDecoration): void {
- const bufferDecoration = new BufferDecoration(this._bufferService, decoration, decoration.options);
- this._decorations.push(bufferDecoration);
- // bufferDecoration.render(this._decorationContainer, this._renderService, true);
- if (this._decorationContainer && bufferDecoration.element && !this._decorationContainer.contains(bufferDecoration.element)) {
- this._decorationContainer.append(bufferDecoration.element!);
- }
- this._queueRefresh();
- }
-
- public override dispose(): void {
- if (this._screenElement && this._decorationContainer && this._screenElement.contains(this._decorationContainer)) {
- this._screenElement.removeChild(this._decorationContainer);
- }
- for (const bufferDecoration of this._decorations) {
- bufferDecoration.dispose();
- }
- super.dispose();
- }
-}
-
-export class BufferDecoration extends Disposable implements IDecoration {
- private readonly _marker: IMarker;
- private _element: HTMLElement | undefined;
- private _container: HTMLElement | undefined;
- private _altBufferIsActive: boolean = false;
-
- public isDisposed: boolean = false;
-
- public get element(): HTMLElement | undefined { return this._element; }
- public get marker(): IMarker { return this._marker; }
-
- private _onDispose = new EventEmitter();
- public get onDispose(): IEvent { return this._onDispose.event; }
-
- private _onRender = new EventEmitter();
- public get onRender(): IEvent { return this._onRender.event; }
-
- public x: number;
- public anchor: 'left' | 'right';
- public width: number;
- public height: number;
-
- constructor(
- private readonly _bufferService: IBufferService,
- private readonly _internalDecoration: IInternalDecoration,
- options: IDecorationOptions
- ) {
- super();
- this.x = options.x ?? 0;
- this._marker = options.marker;
- this._marker.onDispose(() => this.dispose());
- this.anchor = options.anchor || 'left';
- this.width = options.width || 1;
- this.height = options.height || 1;
- }
-
- public render(container: HTMLElement, renderService: IRenderService, shouldRecreate?: boolean): void {
- this._container = container;
- if (!this._element || shouldRecreate) {
- const element = this._createElement(renderService, shouldRecreate);
+ private _renderDecoration(decoration: IInternalDecoration): void {
+ let element = this._decorationElements.get(decoration);
+ if (!element) {
+ element = this._createElement(decoration);
+ this._decorationElements.set(decoration, element);
this._container.appendChild(element);
}
- this._refreshStyle(renderService);
- if (this._element) {
- console.log('firing on render');
- this._onRender.fire(this._element);
- this._internalDecoration.onRenderEmitter.fire(this._element!);
- }
+ this._refreshStyle(decoration, element);
+ decoration.onRenderEmitter.fire(element);
}
- private _createElement(renderService: IRenderService, shouldRecreate?: boolean): HTMLElement {
- if (shouldRecreate && this._element && this._container && this._container.contains(this._element)) {
- this._container.removeChild(this._element);
- }
- this._element = document.createElement('div');
- this._element.classList.add('xterm-decoration');
- this._element.style.width = `${this.width * renderService.dimensions.actualCellWidth}px`;
- this._element.style.height = `${this.height * renderService.dimensions.actualCellHeight}px`;
- this._element.style.top = `${(this.marker.line - this._bufferService.buffers.active.ydisp) * renderService.dimensions.actualCellHeight}px`;
- this._element.style.lineHeight = `${renderService.dimensions.actualCellHeight}px`;
+ private _createElement(decoration: IInternalDecoration): HTMLElement {
+ const element = document.createElement('div');
+ element.classList.add('xterm-decoration');
+ element.style.width = `${(decoration.options.width || 1) * this._renderService.dimensions.actualCellWidth}px`;
+ element.style.height = `${(decoration.options.height || 1) * this._renderService.dimensions.actualCellHeight}px`;
+ element.style.top = `${(decoration.marker.line - this._bufferService.buffers.active.ydisp) * this._renderService.dimensions.actualCellHeight}px`;
+ element.style.lineHeight = `${this._renderService.dimensions.actualCellHeight}px`;
- if (this.x && this.x > this._bufferService.cols) {
+ const x = decoration.options.x ?? 0;
+ if (x && x > this._bufferService.cols) {
// exceeded the container width, so hide
- this._element.style.display = 'none';
+ element.style.display = 'none';
}
- if (this.anchor === 'right') {
- this._element.style.right = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
+ if ((decoration.options.anchor || 'left') === 'right') {
+ element.style.right = x ? `${x * this._renderService.dimensions.actualCellWidth}px` : '';
} else {
- this._element.style.left = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
+ element.style.left = x ? `${x * this._renderService.dimensions.actualCellWidth}px` : '';
}
- return this._element;
+ return element;
}
- private _refreshStyle(renderService: IRenderService): void {
- if (!this._element) {
- return;
- }
- const line = this.marker.line - this._bufferService.buffers.active.ydisp;
+ private _refreshStyle(decoration: IInternalDecoration, element: HTMLElement): void {
+ const line = decoration.marker.line - this._bufferService.buffers.active.ydisp;
if (line < 0 || line > this._bufferService.rows) {
// outside of viewport
- this._element.style.display = 'none';
+ element.style.display = 'none';
} else {
- this._element.style.top = `${line * renderService.dimensions.actualCellHeight}px`;
- this._element.style.display = this._altBufferIsActive ? 'none' : 'block';
+ element.style.top = `${line * this._renderService.dimensions.actualCellHeight}px`;
+ element.style.display = this._altBufferIsActive ? 'none' : 'block';
}
}
-
- public override dispose(): void {
- if (this.isDisposed || !this._container) {
- return;
- }
- if (this._element && this._container.contains(this._element)) {
- this._container.removeChild(this._element);
- }
- this.isDisposed = true;
- this._onDispose.fire();
- }
}
-
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 5f5fc397..2ee0970c 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -4,17 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import { addDisposableDomListener } from 'browser/Lifecycle';
-import { IDecorationRenderer } from 'browser/Decorations/BufferDecorationRenderer';
import { IRenderService } from 'browser/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
import { IBufferService, IDecorationService, IInstantiationService, IInternalDecoration } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
+
const enum ScrollbarConstants {
WIDTH = 7
}
-export class OverviewRulerRenderer extends Disposable implements IDecorationRenderer {
+export class OverviewRulerRenderer extends Disposable {
private _canvas: HTMLCanvasElement;
private _ctx: CanvasRenderingContext2D | null;
private _decorations: ScrollbarDecoration[] = [];
@@ -42,10 +42,10 @@ export class OverviewRulerRenderer extends Disposable implements IDecorationRend
this.register(this._renderService.onRenderedBufferChange(() => this.refreshDecorations()));
this.register(this._renderService.onDimensionsChange(() => this.refreshDecorations()));
this.register(addDisposableDomListener(window, 'resize', () => this.refreshDecorations()));
- this.register(this._decorationService.onDecorationRegistered(e => this.renderDecoration(e)));
+ this.register(this._decorationService.onDecorationRegistered(e => this.registerDecoration(e)));
this.register(this._decorationService.onDecorationRemoved(d => d.dispose()));
}
- public renderDecoration(decoration: IInternalDecoration): void {
+ public registerDecoration(decoration: IInternalDecoration): void {
if (!this._ctx || !decoration.options.overviewRulerItemColor) {
return;
}
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 7c49c7b4..af1d30b0 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -17,6 +17,8 @@ export class DecorationService extends Disposable implements IDecorationService
public get onDecorationRemoved(): IEvent { return this._onDecorationRemoved.event; }
private _decorations: IInternalDecoration[] = [];
+ public get decorations(): IterableIterator { return this._decorations.values(); }
+
constructor() {
super();
}
diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts
index 62108ffb..67bbb5ee 100644
--- a/src/common/services/Services.ts
+++ b/src/common/services/Services.ts
@@ -303,6 +303,7 @@ export interface IUnicodeVersionProvider {
export const IDecorationService = createDecorator('DecorationService');
export interface IDecorationService extends IDisposable {
+ readonly decorations: IterableIterator;
readonly onDecorationRegistered: IEvent;
readonly onDecorationRemoved: IEvent;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
diff --git a/src/tsconfig-library-base.json b/src/tsconfig-library-base.json
index e08695d7..00cecc06 100644
--- a/src/tsconfig-library-base.json
+++ b/src/tsconfig-library-base.json
@@ -4,6 +4,7 @@
"composite": true,
"strict": true,
"declarationMap": true,
- "experimentalDecorators": true
+ "experimentalDecorators": true,
+ "downlevelIteration": true
}
}
From 65b9a6cfc41889a53d11f92a3cd99005fc46ae18 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Mon, 14 Mar 2022 15:52:32 -0700
Subject: [PATCH 047/245] Fix dependency injection for decoration service
---
src/browser/Decorations/BufferDecorationRenderer.ts | 6 +++---
src/browser/Decorations/OverviewRulerRenderer.ts | 6 +++---
src/browser/Terminal.ts | 6 +++---
src/common/services/DecorationService.ts | 8 +++++---
src/common/services/Services.ts | 1 +
5 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 9e3b2d72..2f91d968 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -16,10 +16,10 @@ export class BufferDecorationRenderer extends Disposable {
private _altBufferIsActive: boolean = false;
constructor(
+ private readonly _screenElement: HTMLElement,
@IBufferService private readonly _bufferService: IBufferService,
- @IRenderService private readonly _renderService: IRenderService,
- private readonly _decorationService: IDecorationService,
- private readonly _screenElement: HTMLElement
+ @IDecorationService private readonly _decorationService: IDecorationService,
+ @IRenderService private readonly _renderService: IRenderService
) {
super();
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 2ee0970c..b880fa33 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -23,12 +23,12 @@ export class OverviewRulerRenderer extends Disposable {
private _x: number | undefined;
constructor(
+ private readonly _viewportElement: HTMLElement,
+ private readonly _screenElement: HTMLElement,
@IBufferService private readonly _bufferService: IBufferService,
- @IRenderService private readonly _renderService: IRenderService,
@IDecorationService private readonly _decorationService: IDecorationService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
- private readonly _viewportElement: HTMLElement,
- private readonly _screenElement: HTMLElement
+ @IRenderService private readonly _renderService: IRenderService
) {
super();
this._canvas = document.createElement('canvas');
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index d07b9c30..3d490d08 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -587,14 +587,14 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
if (this._decorationService) {
- this._bufferDecorationRenderer = new BufferDecorationRenderer(this._bufferService, this._renderService, this._decorationService, this.screenElement);
+ this._bufferDecorationRenderer = this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement);
}
if (this.options.overviewRulerWidth && this._decorationService) {
- this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._instantiationService, this._viewportElement, this.screenElement);
+ this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
}
this.optionsService.onOptionChange(() => {
if (!this._overviewRulerRenderer && this.options.overviewRulerWidth && this._renderService && this._viewportElement && this.screenElement && this._decorationService) {
- this._overviewRulerRenderer = new OverviewRulerRenderer(this._bufferService, this._renderService, this._decorationService, this._instantiationService, this._viewportElement, this.screenElement);
+ this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
}});
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index af1d30b0..693f8944 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -3,19 +3,21 @@
* @license MIT
*/
-
import { EventEmitter } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
import { IDecorationService, IInternalDecoration } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker, IEvent } from 'xterm';
export class DecorationService extends Disposable implements IDecorationService {
+ public serviceBrand: any;
+
+ private readonly _decorations: IInternalDecoration[] = [];
private _animationFrame: number | undefined;
+
private _onDecorationRegistered = this.register(new EventEmitter());
public get onDecorationRegistered(): IEvent { return this._onDecorationRegistered.event; }
private _onDecorationRemoved = this.register(new EventEmitter());
public get onDecorationRemoved(): IEvent { return this._onDecorationRemoved.event; }
- private _decorations: IInternalDecoration[] = [];
public get decorations(): IterableIterator { return this._decorations.values(); }
@@ -45,7 +47,7 @@ export class DecorationService extends Disposable implements IDecorationService
this._onDecorationRemoved.fire(decoration);
decoration.dispose();
}
- this._decorations = [];
+ this._decorations.length = 0;
}
private _queueRefresh(): void {
diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts
index 67bbb5ee..876d90bc 100644
--- a/src/common/services/Services.ts
+++ b/src/common/services/Services.ts
@@ -303,6 +303,7 @@ export interface IUnicodeVersionProvider {
export const IDecorationService = createDecorator('DecorationService');
export interface IDecorationService extends IDisposable {
+ serviceBrand: undefined;
readonly decorations: IterableIterator;
readonly onDecorationRegistered: IEvent;
readonly onDecorationRemoved: IEvent;
From 3dc4f1a7f2edbb06496494679e56d2a4aac18954 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Mon, 14 Mar 2022 15:56:27 -0700
Subject: [PATCH 048/245] Remove old decoration elements
---
src/browser/Decorations/BufferDecorationRenderer.ts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 2f91d968..74fc59a7 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -34,6 +34,7 @@ export class BufferDecorationRenderer extends Disposable {
this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
}));
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
+ this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
}
public override dispose(): void {
@@ -101,4 +102,10 @@ export class BufferDecorationRenderer extends Disposable {
element.style.display = this._altBufferIsActive ? 'none' : 'block';
}
}
+
+ private _removeDecoration(decoration: IInternalDecoration): void {
+ const element = this._decorationElements.get(decoration);
+ element?.remove();
+ this._decorationElements.delete(decoration);
+ }
}
From fc09730ffbbda2727f83534bdea2a793d3dd57d7 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Mon, 14 Mar 2022 16:07:30 -0700
Subject: [PATCH 049/245] Tidy up DecorationService
---
src/common/services/DecorationService.ts | 31 ++++++++----------------
1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 693f8944..f18ad9b5 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -12,7 +12,6 @@ export class DecorationService extends Disposable implements IDecorationService
public serviceBrand: any;
private readonly _decorations: IInternalDecoration[] = [];
- private _animationFrame: number | undefined;
private _onDecorationRegistered = this.register(new EventEmitter());
public get onDecorationRegistered(): IEvent { return this._onDecorationRegistered.event; }
@@ -49,33 +48,23 @@ export class DecorationService extends Disposable implements IDecorationService
}
this._decorations.length = 0;
}
-
- private _queueRefresh(): void {
- if (this._animationFrame !== undefined) {
- return;
- }
- this._animationFrame = window.requestAnimationFrame(() => {
- // this._refresh();
- this._animationFrame = undefined;
- });
- }
}
-class Decoration implements IInternalDecoration {
- public marker: IMarker;
- public readonly onRenderEmitter = new EventEmitter();
- public readonly onRender = this.onRenderEmitter.event;
- private _onDispose = new EventEmitter();
- public readonly onDispose = this._onDispose.event;
+class Decoration extends Disposable implements IInternalDecoration {
+ public readonly marker: IMarker;
public element: HTMLElement | undefined;
public isDisposed: boolean = false;
- public dispose(): void {
- throw new Error('Method not implemented.');
- }
+
+ public readonly onRenderEmitter = this.register(new EventEmitter());
+ public readonly onRender = this.onRenderEmitter.event;
+ private _onDispose = this.register(new EventEmitter());
+ public readonly onDispose = this._onDispose.event;
+
constructor(
public readonly options: IDecorationOptions
) {
+ super();
this.marker = options.marker;
- this.element = undefined;
+ // TODO: Make sure dispose doesn't need to do anything else?
}
}
From bfb048110feb3aa09c3055d58fc5a03cff2868e3 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 08:57:15 -0400
Subject: [PATCH 050/245] register service
---
demo/client.ts | 1 -
src/browser/Terminal.ts | 3 ++-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 55e97ed4..7eba89a5 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -548,7 +548,6 @@ function addDecoration() {
const marker = term.addMarker(1);
const decoration = term.registerDecoration({ marker });
decoration.onRender((e) => {
- console.log('onRender', e);
e.style.backgroundColor = 'red';
});
}
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 3d490d08..b1f96ee5 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -57,8 +57,8 @@ import { CharacterJoinerService } from 'browser/services/CharacterJoinerService'
import { toRgbString } from 'common/input/XParseColor';
import { BufferDecorationRenderer } from 'browser/Decorations/BufferDecorationRenderer';
import { OverviewRulerRenderer } from 'browser/Decorations/OverviewRulerRenderer';
-// import { IDecorationService } from 'common/services/Services';
import { DecorationService } from 'common/services/DecorationService';
+import { IDecorationService } from 'common/services/Services';
// Let it work inside Node.js for automated testing purposes.
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
@@ -169,6 +169,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier = this._instantiationService.createInstance(Linkifier);
this.linkifier2 = this.register(this._instantiationService.createInstance(Linkifier2));
this._decorationService = this._instantiationService.createInstance(DecorationService);
+ this._instantiationService.setService(IDecorationService, this._decorationService);
// Setup InputHandler listeners
this.register(this._inputHandler.onRequestBell(() => this.bell()));
From 079bbc8242307a828911b4ef4b7380c5e330441a Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 10:27:01 -0400
Subject: [PATCH 051/245] get it working in demo with overviewRulerwidth not
considered"
---
demo/client.ts | 4 +-
.../Decorations/OverviewRulerRenderer.ts | 158 +++++++-----------
src/browser/Terminal.ts | 19 +--
src/common/services/OptionsService.ts | 3 +-
typings/xterm.d.ts | 17 +-
5 files changed, 88 insertions(+), 113 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 7eba89a5..f6223f2c 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -554,8 +554,10 @@ function addDecoration() {
function addOverviewRuler() {
const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerItemColor: 'red'});
- canvas.element!.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
term.registerDecoration({marker: term.addMarker(3), overviewRulerItemColor: 'green'});
term.registerDecoration({marker: term.addMarker(5), overviewRulerItemColor: 'blue'});
+ canvas.onRender((e) => {
+ e.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
+ });
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index b880fa33..8a78bb78 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -5,36 +5,37 @@
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IRenderService } from 'browser/services/Services';
-import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
-import { IBufferService, IDecorationService, IInstantiationService, IInternalDecoration } from 'common/services/Services';
-import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
+import { IBufferService, IDecorationService, IInternalDecoration, IOptionsService } from 'common/services/Services';
const enum ScrollbarConstants {
- WIDTH = 7
+ WIDTH = 15
}
export class OverviewRulerRenderer extends Disposable {
private _canvas: HTMLCanvasElement;
private _ctx: CanvasRenderingContext2D | null;
- private _decorations: ScrollbarDecoration[] = [];
- private _width: number | undefined;
- private _anchor: 'right' | 'left' | undefined;
- private _x: number | undefined;
+ private readonly _decorationElements: Map = new Map();
+
+ private _animationFrame: number | undefined;
constructor(
private readonly _viewportElement: HTMLElement,
private readonly _screenElement: HTMLElement,
@IBufferService private readonly _bufferService: IBufferService,
@IDecorationService private readonly _decorationService: IDecorationService,
- @IInstantiationService private readonly _instantiationService: IInstantiationService,
- @IRenderService private readonly _renderService: IRenderService
+ @IRenderService private readonly _renderService: IRenderService,
+ @IOptionsService private readonly _optionsService: IOptionsService
) {
super();
this._canvas = document.createElement('canvas');
this._canvas.classList.add('xterm-decoration-scrollbar');
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
this._ctx = this._canvas.getContext('2d');
+ this._canvas.style.width = `${this._optionsService.options.overviewRulerWidth || ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.height = `${this._screenElement.clientHeight}px`;
+ this._canvas.width = Math.floor((this._optionsService.options.overviewRulerWidth|| ScrollbarConstants.WIDTH)* window.devicePixelRatio);
+ this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
this.refreshDecorations();
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
@@ -42,111 +43,80 @@ export class OverviewRulerRenderer extends Disposable {
this.register(this._renderService.onRenderedBufferChange(() => this.refreshDecorations()));
this.register(this._renderService.onDimensionsChange(() => this.refreshDecorations()));
this.register(addDisposableDomListener(window, 'resize', () => this.refreshDecorations()));
- this.register(this._decorationService.onDecorationRegistered(e => this.registerDecoration(e)));
- this.register(this._decorationService.onDecorationRemoved(d => d.dispose()));
+ this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
+ this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
}
- public registerDecoration(decoration: IInternalDecoration): void {
- if (!this._ctx || !decoration.options.overviewRulerItemColor) {
+
+ public override dispose(): void {
+ for (const decoration of this._decorationElements) {
+ this._ctx?.clearRect(
+ 0,
+ Math.round(this._canvas.height * (decoration[0].marker.line / this._bufferService.buffers.active.lines.length)),
+ this._canvas.width,
+ window.devicePixelRatio
+ );
+ }
+ this._decorationElements.clear();
+ this._canvas?.remove();
+ super.dispose();
+ }
+
+ private _refreshStyle(decoration: IInternalDecoration): void {
+ if (!this._ctx) {
+ return;
+ }
+ if (decoration.options.anchor === 'right') {
+ this._canvas.style.right = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
+ } else {
+ this._canvas.style.left = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
+ }
+ if (!decoration.options.overviewRulerItemColor) {
+ this._decorationElements.delete(decoration);
return;
}
-
- // TODO: Does this do anything anymore?
- this._instantiationService.createInstance(ScrollbarDecoration, { marker: decoration.options.marker, overviewRulerItemColor: decoration.options.overviewRulerItemColor });
-
this._ctx.lineWidth = 1;
this._ctx.strokeStyle = decoration.options.overviewRulerItemColor;
this._ctx.strokeRect(
0,
- Math.round(this._canvas.height * (decoration.marker.line / this._bufferService.buffers.active.lines.length)),
+ Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
this._canvas.width,
window.devicePixelRatio
);
}
public refreshDecorations(): void {
- if (!this._ctx) {
- return;
- }
- this._canvas.style.width = `${this._width || ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.width = `${this._canvas.width || ScrollbarConstants.WIDTH}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._width || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
+ this._canvas.width = Math.floor((this._canvas.width || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
- if (this._anchor === 'right') {
- this._canvas.style.right = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
- } else {
- this._canvas.style.left = this._x ? `${this._x * this._renderService.dimensions.actualCellWidth}px` : '';
- }
- this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
- for (const decoration of this._decorations) {
- decoration.render(this._ctx, this._canvas);
+
+ for (const decoration of this._decorationService.decorations) {
+ this._renderDecoration(decoration);
}
}
- public override dispose(): void {
- for (const decoration of this._decorations) {
- decoration.dispose();
+
+ private _renderDecoration(decoration: IInternalDecoration): void {
+ const element = this._decorationElements.get(decoration);
+ if (!element) {
+ this._decorationElements.set(decoration, this._canvas);
}
- this._decorations = [];
- this._canvas?.remove();
- super.dispose();
- }
-}
-class ScrollbarDecoration extends Disposable implements IDecoration {
- private readonly _marker: IMarker;
- private _ctx: CanvasRenderingContext2D | undefined;
- private _canvas: HTMLCanvasElement | undefined;
- private _color: string | undefined;
-
- public isDisposed: boolean = false;
-
- public get element(): HTMLCanvasElement | undefined { return this._canvas; }
- public get marker(): IMarker { return this._marker; }
- public get color(): string | undefined { return this._color; }
-
- private _onDispose = new EventEmitter();
- public get onDispose(): IEvent { return this._onDispose.event; }
-
- private _onRender = new EventEmitter();
- public get onRender(): IEvent { return this._onRender.event; }
-
- constructor(
- options: IDecorationOptions,
- @IBufferService private readonly _bufferService: IBufferService
- ) {
- super();
- this._marker = options.marker;
- this._color = options.overviewRulerItemColor;
- this._marker.onDispose(() => this.dispose());
+ this._refreshStyle(decoration);
+ decoration.onRenderEmitter.fire(this._canvas);
}
- public render(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void {
- if (!this.color) {
- throw new Error('No color was provided for the overview ruler decoraiton');
- }
- this._ctx = ctx;
- this._canvas = canvas;
- ctx.lineWidth = 1;
- ctx.strokeStyle = this.color;
- ctx.strokeRect(
- 0,
- Math.round(canvas.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
- canvas.width,
- window.devicePixelRatio
- );
- this._onRender.fire(canvas);
- }
-
- public override dispose(): void {
- if (this._isDisposed || !this._canvas || !this._ctx) {
+ private _queueRefresh(): void {
+ if (this._animationFrame !== undefined) {
return;
}
- this._ctx.clearRect(
- 0,
- Math.round(this._canvas.height * (this.marker.line / this._bufferService.buffers.active.lines.length)),
- this._canvas.width,
- window.devicePixelRatio
- );
- this.isDisposed = true;
- this._onDispose.fire();
- super.dispose();
+ this._animationFrame = window.requestAnimationFrame(() => {
+ this.refreshDecorations();
+ this._animationFrame = undefined;
+ });
+ }
+
+ private _removeDecoration(decoration: IInternalDecoration): void {
+ const element = this._decorationElements.get(decoration);
+ element?.remove();
+ this._decorationElements.delete(decoration);
}
}
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index b1f96ee5..6d0aabb1 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -75,7 +75,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _compositionView: HTMLElement | undefined;
private _overviewRulerRenderer: OverviewRulerRenderer | undefined;
- private _bufferDecorationRenderer: BufferDecorationRenderer | undefined;
// private _visualBellTimer: number;
@@ -587,16 +586,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.register(this.onScroll(() => this._mouseZoneManager!.clearAll()));
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
- if (this._decorationService) {
- this._bufferDecorationRenderer = this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement);
- }
- if (this.options.overviewRulerWidth && this._decorationService) {
- this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
- }
- this.optionsService.onOptionChange(() => {
- if (!this._overviewRulerRenderer && this.options.overviewRulerWidth && this._renderService && this._viewportElement && this.screenElement && this._decorationService) {
- this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
- }});
+ this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement);
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
@@ -614,6 +604,13 @@ export class Terminal extends CoreTerminal implements ITerminal {
this._accessibilityManager = new AccessibilityManager(this, this._renderService);
}
+ // if (this.options.overviewRulerWidth) {
+ this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
+ // }
+ this.optionsService.onOptionChange(() => {
+ if (!this._overviewRulerRenderer && this.options.overviewRulerWidth && this._viewportElement && this.screenElement) {
+ this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
+ }});
// Measure the character size
this._charSizeService.measure();
diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts
index 43fe9981..4008a431 100644
--- a/src/common/services/OptionsService.ts
+++ b/src/common/services/OptionsService.ts
@@ -52,7 +52,8 @@ export const DEFAULT_OPTIONS: Readonly = {
altClickMovesCursor: true,
convertEol: false,
termName: 'xterm',
- cancelEvents: false
+ cancelEvents: false,
+ overviewRulerWidth: undefined
};
const FONT_WEIGHT_OPTIONS: Extract[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'];
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 00f4072f..986e77c9 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -266,6 +266,11 @@ declare module 'xterm' {
* All features are disabled by default for security reasons.
*/
windowOptions?: IWindowOptions;
+
+ /**
+ * The width, in pixels, of the canvas for the overview ruler.
+ */
+ overviewRulerWidth?: number;
}
/**
@@ -394,7 +399,7 @@ declare module 'xterm' {
}
/**
- * Represents a disposable with an
+ * Represents a disposable with an
* @param onDispose event listener and
* @param isDisposed property.
*/
@@ -436,7 +441,7 @@ declare module 'xterm' {
/**
* Options provided when registering a decoration
- * containing a @param marker, @param anchor,
+ * containing a @param marker, @param anchor,
* @param x offset from the anchor, @param width in cells
* and @param height in cells.
*/
@@ -455,19 +460,19 @@ declare module 'xterm' {
/**
* The x position offset relative to the anchor
- */
+ */
x?: number;
/**
- * The width of the decoration in cells, which defaults to
+ * The width of the decoration in cells, which defaults to
* cell width or the width in pixels, when an overlayRulerItemColor
* is provided.
*/
width?: number;
/**
- * The height of the decoration in cells, which defaults to
+ * The height of the decoration in cells, which defaults to
* cell height
*/
height?: number;
@@ -946,7 +951,7 @@ declare module 'xterm' {
/**
* (EXPERIMENTAL) Adds a decoration to the terminal using
- * @param decorationOptions, which takes a marker and an optional anchor,
+ * @param decorationOptions, which takes a marker and an optional anchor,
* width, height, and x offset from the anchor. Returns the decoration or
* undefined if the alt buffer is active or the marker has already been disposed of.
* @throws when options include a negative x offset.
From 3e47c96445e6baa448707ff3456ed09da166b6fa Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 10:31:01 -0400
Subject: [PATCH 052/245] get demo to work the right way
---
demo/client.ts | 1 +
src/browser/Terminal.ts | 6 +++---
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index f6223f2c..e1347e3d 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -553,6 +553,7 @@ function addDecoration() {
}
function addOverviewRuler() {
+ term.options['overviewRulerWidth'] = 15;
const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerItemColor: 'red'});
term.registerDecoration({marker: term.addMarker(3), overviewRulerItemColor: 'green'});
term.registerDecoration({marker: term.addMarker(5), overviewRulerItemColor: 'blue'});
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 6d0aabb1..c2fb1500 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -604,9 +604,9 @@ export class Terminal extends CoreTerminal implements ITerminal {
this._accessibilityManager = new AccessibilityManager(this, this._renderService);
}
- // if (this.options.overviewRulerWidth) {
- this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
- // }
+ if (this.options.overviewRulerWidth) {
+ this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
+ }
this.optionsService.onOptionChange(() => {
if (!this._overviewRulerRenderer && this.options.overviewRulerWidth && this._viewportElement && this.screenElement) {
this._overviewRulerRenderer = this._instantiationService.createInstance(OverviewRulerRenderer, this._viewportElement, this.screenElement);
From 39f9c3507bcd856441e3bf1db627550b164fc625 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 10:37:38 -0400
Subject: [PATCH 053/245] rename css, fix background color getting applied
---
css/xterm.css | 4 ++--
demo/client.ts | 4 +++-
src/browser/Decorations/OverviewRulerRenderer.ts | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/css/xterm.css b/css/xterm.css
index 44bb062a..0ca59399 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -179,10 +179,10 @@
position: absolute;
}
-.xterm-decoration-scrollbar {
+.xterm-decoration-overview-ruler {
z-index: 7;
position: absolute;
top: 0;
right: 0;
width: 50px;
-}
\ No newline at end of file
+}
diff --git a/demo/client.ts b/demo/client.ts
index e1347e3d..b80e2e76 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -548,7 +548,9 @@ function addDecoration() {
const marker = term.addMarker(1);
const decoration = term.registerDecoration({ marker });
decoration.onRender((e) => {
- e.style.backgroundColor = 'red';
+ if (e.classList.value === 'xterm-decoration') {
+ e.style.backgroundColor = 'red';
+ }
});
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 8a78bb78..083db5e9 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -29,7 +29,7 @@ export class OverviewRulerRenderer extends Disposable {
) {
super();
this._canvas = document.createElement('canvas');
- this._canvas.classList.add('xterm-decoration-scrollbar');
+ this._canvas.classList.add('xterm-decoration-overview-ruler');
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
this._ctx = this._canvas.getContext('2d');
this._canvas.style.width = `${this._optionsService.options.overviewRulerWidth || ScrollbarConstants.WIDTH}px`;
From 4ef5f6ae86d8c25d625a108a95608f0cbd2ac149 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 10:53:32 -0400
Subject: [PATCH 054/245] add listener for on option change
---
src/browser/Decorations/OverviewRulerRenderer.ts | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 083db5e9..7d8475f2 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -45,6 +45,11 @@ export class OverviewRulerRenderer extends Disposable {
this.register(addDisposableDomListener(window, 'resize', () => this.refreshDecorations()));
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
+ this.register(this._optionsService.onOptionChange(o => {
+ if (o === 'overviewRulerWidth') {
+ this.refreshDecorations();
+ }
+ }));
}
public override dispose(): void {
@@ -85,9 +90,9 @@ export class OverviewRulerRenderer extends Disposable {
}
public refreshDecorations(): void {
- this._canvas.style.width = `${this._canvas.width || ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.width = `${this._optionsService.options.overviewRulerWidth || ScrollbarConstants.WIDTH}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._canvas.width || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
+ this._canvas.width = Math.floor((this._optionsService.options.overviewRulerWidth || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
for (const decoration of this._decorationService.decorations) {
From 520d5dd9eb848db6f4ae5949ea59d3abe1c53e19 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 11:37:20 -0400
Subject: [PATCH 055/245] delete unused css
---
css/xterm.css | 1 -
1 file changed, 1 deletion(-)
diff --git a/css/xterm.css b/css/xterm.css
index 0ca59399..7432fbb1 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -184,5 +184,4 @@
position: absolute;
top: 0;
right: 0;
- width: 50px;
}
From f98b4ce1bafae14b63f32342e429c0dc41ecd242 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 12:16:33 -0400
Subject: [PATCH 056/245] properly dispose of buffer decoration
---
src/browser/Decorations/BufferDecorationRenderer.ts | 1 +
src/common/services/DecorationService.ts | 7 ++++++-
test/api/Terminal.api.ts | 2 +-
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 74fc59a7..26166c77 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -63,6 +63,7 @@ export class BufferDecorationRenderer extends Disposable {
let element = this._decorationElements.get(decoration);
if (!element) {
element = this._createElement(decoration);
+ decoration.onDispose(() => this._removeDecoration(decoration));
this._decorationElements.set(decoration, element);
this._container.appendChild(element);
}
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index f18ad9b5..d561e91b 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -65,6 +65,11 @@ class Decoration extends Disposable implements IInternalDecoration {
) {
super();
this.marker = options.marker;
- // TODO: Make sure dispose doesn't need to do anything else?
+ this.marker.onDispose(() => this.dispose());
+ }
+ public override dispose(): void {
+ this.element?.remove();
+ this._onDispose.fire();
+ super.dispose();
}
}
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index ee6a0cfa..a73cdc19 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -574,7 +574,7 @@ describe('API Integration Tests', function(): void {
await page.evaluate(`window.term.scrollLines(10)`);
await page.evaluate(`window.term.addMarker(3)`);
await page.evaluate(`window.term.addMarker(4)`);
- await page.evaluate(`
+ await page.evaluate(`
for (let i = 0; i < window.term.markers.length; ++i) {
const marker = window.term.markers[i];
marker.onDispose(() => window.disposeStack.push(marker));
From 80e7963dbe6288a9109adb5190092cc85f45e36c Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 12:43:03 -0400
Subject: [PATCH 057/245] fix tests and add one
---
test/api/Terminal.api.ts | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index a73cdc19..c1412223 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -732,7 +732,7 @@ describe('API Integration Tests', function(): void {
});
describe('registerDecoration', () => {
- it('should register decorations and render them', async () => {
+ it('should register decorations and not render them', async () => {
await openTerminal(page);
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
@@ -742,6 +742,18 @@ describe('API Integration Tests', function(): void {
await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
await page.evaluate(`window.term.resize(10, 5)`);
+ assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 0);
+ });
+ it('should register decorations and render them when open is called', async () => {
+ await openTerminal(page);
+ await writeSync(page, '\\n\\n\\n\\n');
+ await writeSync(page, '\\n\\n\\n\\n');
+ await writeSync(page, '\\n\\n\\n\\n');
+ await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
+ await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
+ await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 2);
});
it('on resize should dispose of the old decoration and create a new one', async () => {
@@ -750,6 +762,7 @@ describe('API Integration Tests', function(): void {
await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.marker = window.term.addMarker(1)`);
await page.evaluate(`window.decoration = window.term.registerDecoration({ marker: window.marker })`);
+ await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
await page.evaluate(`window.term.resize(10, 5)`);
assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 1);
});
From e7e9d5ca4c5ef799b20f05d099592656944749e4 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 12:56:34 -0400
Subject: [PATCH 058/245] remove test
---
demo/client.ts | 6 +++---
src/browser/Decorations/BufferDecorationRenderer.ts | 3 +++
src/browser/Decorations/OverviewRulerRenderer.ts | 4 ++--
src/common/services/DecorationService.ts | 3 ++-
test/api/Terminal.api.ts | 12 ------------
typings/xterm.d.ts | 8 +++++---
6 files changed, 15 insertions(+), 21 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index b80e2e76..b219b733 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -556,9 +556,9 @@ function addDecoration() {
function addOverviewRuler() {
term.options['overviewRulerWidth'] = 15;
- const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerItemColor: 'red'});
- term.registerDecoration({marker: term.addMarker(3), overviewRulerItemColor: 'green'});
- term.registerDecoration({marker: term.addMarker(5), overviewRulerItemColor: 'blue'});
+ const canvas = term.registerDecoration({marker: term.addMarker(1), { color }: 'red'});
+ term.registerDecoration({marker: term.addMarker(3), { color }: 'green'});
+ term.registerDecoration({marker: term.addMarker(5), { color }: 'blue'});
canvas.onRender((e) => {
e.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
});
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 26166c77..8821ccc1 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -106,6 +106,9 @@ export class BufferDecorationRenderer extends Disposable {
private _removeDecoration(decoration: IInternalDecoration): void {
const element = this._decorationElements.get(decoration);
+ if (element && this._container && this._container.contains(element)) {
+ this._container.removeChild(element);
+ }
element?.remove();
this._decorationElements.delete(decoration);
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 7d8475f2..45f30c35 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -75,12 +75,12 @@ export class OverviewRulerRenderer extends Disposable {
} else {
this._canvas.style.left = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
}
- if (!decoration.options.overviewRulerItemColor) {
+ if (!decoration.options.overviewRulerOptions?.color) {
this._decorationElements.delete(decoration);
return;
}
this._ctx.lineWidth = 1;
- this._ctx.strokeStyle = decoration.options.overviewRulerItemColor;
+ this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
this._ctx.strokeRect(
0,
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index d561e91b..9d7e3314 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -68,8 +68,9 @@ class Decoration extends Disposable implements IInternalDecoration {
this.marker.onDispose(() => this.dispose());
}
public override dispose(): void {
- this.element?.remove();
this._onDispose.fire();
+ this.element?.remove();
+ this.element = undefined;
super.dispose();
}
}
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index c1412223..0a6732d7 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -744,18 +744,6 @@ describe('API Integration Tests', function(): void {
await page.evaluate(`window.term.resize(10, 5)`);
assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 0);
});
- it('should register decorations and render them when open is called', async () => {
- await openTerminal(page);
- await writeSync(page, '\\n\\n\\n\\n');
- await writeSync(page, '\\n\\n\\n\\n');
- await writeSync(page, '\\n\\n\\n\\n');
- await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
- await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
- await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
- assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 2);
- });
it('on resize should dispose of the old decoration and create a new one', async () => {
await openTerminal(page);
await writeSync(page, '\\n\\n\\n\\n');
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 986e77c9..c6062967 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -478,10 +478,12 @@ declare module 'xterm' {
height?: number;
/**
- * When provided, renders the decoration in the scrollbar
- * with the given color
+ * Renders the decoration in the scrollbar
+ * with the given @param color and optional @param position.
+ * If @param position is not set, it will span the full @param overviewRulerWidth, which
+ * must be provided via @TerminalOptions for this to work.
*/
- overviewRulerItemColor?: string;
+ overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right'}
}
/**
From df929f2dd5b65390e82f422c4125cd75eae629ba Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 13:07:49 -0400
Subject: [PATCH 059/245] add position
---
demo/client.ts | 9 ++++++---
src/browser/Decorations/OverviewRulerRenderer.ts | 8 +++++---
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index b219b733..70588213 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -556,9 +556,12 @@ function addDecoration() {
function addOverviewRuler() {
term.options['overviewRulerWidth'] = 15;
- const canvas = term.registerDecoration({marker: term.addMarker(1), { color }: 'red'});
- term.registerDecoration({marker: term.addMarker(3), { color }: 'green'});
- term.registerDecoration({marker: term.addMarker(5), { color }: 'blue'});
+ const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: 'red' }});
+ term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: 'green' }});
+ term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: 'blue' }});
+ term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: 'red', position: 'left' }});
+ term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: 'green', position: 'center' }});
+ term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: 'blue', position: 'right' }});
canvas.onRender((e) => {
e.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
});
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 45f30c35..eb58bc10 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -67,7 +67,7 @@ export class OverviewRulerRenderer extends Disposable {
}
private _refreshStyle(decoration: IInternalDecoration): void {
- if (!this._ctx) {
+ if (!this._ctx || !this._optionsService.options.overviewRulerWidth) {
return;
}
if (decoration.options.anchor === 'right') {
@@ -81,10 +81,12 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.lineWidth = 1;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
+ const size = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
+ const position = decoration.options.overviewRulerOptions.position;
this._ctx.strokeRect(
- 0,
+ !position || position === 'left' ? 0 : position === 'right' ? size * 2 + 1: size,
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- this._canvas.width,
+ !position ? this._canvas.width : position === 'center' ? size + 1 : size,
window.devicePixelRatio
);
}
From bd21c6afa59f9c8b8517365df89caaa4ac60ff0d Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 13:15:43 -0400
Subject: [PATCH 060/245] adjust test
---
demo/client.ts | 2 +-
test/api/Terminal.api.ts | 14 ++------------
2 files changed, 3 insertions(+), 13 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 70588213..f3f11806 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -555,7 +555,7 @@ function addDecoration() {
}
function addOverviewRuler() {
- term.options['overviewRulerWidth'] = 15;
+ term.options['overviewRulerWidth'] = 13;
const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: 'red' }});
term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: 'green' }});
term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: 'blue' }});
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index 0a6732d7..f13b2ada 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -732,7 +732,7 @@ describe('API Integration Tests', function(): void {
});
describe('registerDecoration', () => {
- it('should register decorations and not render them', async () => {
+ it('should register decorations and render them', async () => {
await openTerminal(page);
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
@@ -742,17 +742,7 @@ describe('API Integration Tests', function(): void {
await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
await page.evaluate(`window.term.resize(10, 5)`);
- assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 0);
- });
- it('on resize should dispose of the old decoration and create a new one', async () => {
- await openTerminal(page);
- await writeSync(page, '\\n\\n\\n\\n');
- await writeSync(page, '\\n\\n\\n\\n');
- await page.evaluate(`window.marker = window.term.addMarker(1)`);
- await page.evaluate(`window.decoration = window.term.registerDecoration({ marker: window.marker })`);
- await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
- await page.evaluate(`window.term.resize(10, 5)`);
- assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 1);
+ assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 2);
});
it('should return undefined when the marker has already been disposed of', async () => {
await openTerminal(page);
From 71649f5e407f0fc1b6bf55966bd2b32794f1aff0 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 13:21:34 -0400
Subject: [PATCH 061/245] fix tests
---
test/api/Terminal.api.ts | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index f13b2ada..f2abf2f8 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -731,31 +731,26 @@ describe('API Integration Tests', function(): void {
await pollFor(page, `window.term._core._renderService.dimensions.actualCellWidth > 0`, true);
});
- describe('registerDecoration', () => {
- it('should register decorations and render them', async () => {
- await openTerminal(page);
- await writeSync(page, '\\n\\n\\n\\n');
- await writeSync(page, '\\n\\n\\n\\n');
- await writeSync(page, '\\n\\n\\n\\n');
+ describe.only('registerDecoration', () => {
+ it('should register decorations and render them when terminal open is called', async () => {
+ await page.evaluate(`window.term = new Terminal({})`);
+ await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
+ await page.waitForSelector('.xterm-text-layer');
await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
- await page.evaluate(`window.term.resize(10, 5)`);
+ await openTerminal(page);
assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 2);
});
it('should return undefined when the marker has already been disposed of', async () => {
await openTerminal(page);
- await writeSync(page, '\\n\\n\\n\\n');
- await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.marker = window.term.addMarker(1)`);
await page.evaluate(`window.marker.dispose()`);
assert.equal(await page.evaluate(`window.decoration = window.term.registerDecoration({ marker: window.marker });`), undefined);
});
it('should throw when a negative x offset is provided', async () => {
await openTerminal(page);
- await writeSync(page, '\\n\\n\\n\\n');
- await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.marker = window.term.addMarker(1)`);
await page.evaluate(`
try {
From adaee5e495785769d8752a075e630445e87ab0bb Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 13:23:09 -0400
Subject: [PATCH 062/245] use poll for instead
---
test/api/Terminal.api.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index f2abf2f8..0e50e7fc 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -741,13 +741,13 @@ describe('API Integration Tests', function(): void {
await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
await openTerminal(page);
- assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 2);
+ await pollFor(page, `document.querySelectorAll('.xterm-screen .xterm-decoration').length`, 2);
});
it('should return undefined when the marker has already been disposed of', async () => {
await openTerminal(page);
await page.evaluate(`window.marker = window.term.addMarker(1)`);
await page.evaluate(`window.marker.dispose()`);
- assert.equal(await page.evaluate(`window.decoration = window.term.registerDecoration({ marker: window.marker });`), undefined);
+ await pollFor(page, `window.decoration = window.term.registerDecoration({ marker: window.marker });`, undefined);
});
it('should throw when a negative x offset is provided', async () => {
await openTerminal(page);
From 1905d7d8cad1f318887bb38065e95344e28a8e3d Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 13:28:50 -0400
Subject: [PATCH 063/245] add overview ruler tests
---
test/api/Terminal.api.ts | 82 ++++++++++++++++++++++++++--------------
1 file changed, 54 insertions(+), 28 deletions(-)
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index 0e50e7fc..43f1e97d 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -731,35 +731,61 @@ describe('API Integration Tests', function(): void {
await pollFor(page, `window.term._core._renderService.dimensions.actualCellWidth > 0`, true);
});
- describe.only('registerDecoration', () => {
- it('should register decorations and render them when terminal open is called', async () => {
- await page.evaluate(`window.term = new Terminal({})`);
- await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
- await page.waitForSelector('.xterm-text-layer');
- await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
- await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
- await openTerminal(page);
- await pollFor(page, `document.querySelectorAll('.xterm-screen .xterm-decoration').length`, 2);
+ describe('registerDecoration', () => {
+ describe('bufferDecorations', () => {
+ it('should register decorations and render them when terminal open is called', async () => {
+ await page.evaluate(`window.term = new Terminal({})`);
+ await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
+ await page.waitForSelector('.xterm-text-layer');
+ await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
+ await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
+ await openTerminal(page);
+ await pollFor(page, `document.querySelectorAll('.xterm-screen .xterm-decoration').length`, 2);
+ });
+ it('should return undefined when the marker has already been disposed of', async () => {
+ await openTerminal(page);
+ await page.evaluate(`window.marker = window.term.addMarker(1)`);
+ await page.evaluate(`window.marker.dispose()`);
+ await pollFor(page, `window.decoration = window.term.registerDecoration({ marker: window.marker });`, undefined);
+ });
+ it('should throw when a negative x offset is provided', async () => {
+ await openTerminal(page);
+ await page.evaluate(`window.marker = window.term.addMarker(1)`);
+ await page.evaluate(`
+ try {
+ window.decoration = window.term.registerDecoration({ marker: window.marker, x: -2 });
+ } catch (e) {
+ window.throwMessage = e.message;
+ }
+ `);
+ await pollFor(page, 'window.throwMessage', 'This API only accepts positive integers');
+ });
});
- it('should return undefined when the marker has already been disposed of', async () => {
- await openTerminal(page);
- await page.evaluate(`window.marker = window.term.addMarker(1)`);
- await page.evaluate(`window.marker.dispose()`);
- await pollFor(page, `window.decoration = window.term.registerDecoration({ marker: window.marker });`, undefined);
- });
- it('should throw when a negative x offset is provided', async () => {
- await openTerminal(page);
- await page.evaluate(`window.marker = window.term.addMarker(1)`);
- await page.evaluate(`
- try {
- window.decoration = window.term.registerDecoration({ marker: window.marker, x: -2 });
- } catch (e) {
- window.throwMessage = e.message;
- }
- `);
- await pollFor(page, 'window.throwMessage', 'This API only accepts positive integers');
+ describe('overviewRulerDecorations', () => {
+ it('should not add an overview ruler when width is not set', async () => {
+ await page.evaluate(`window.term = new Terminal({})`);
+ await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
+ await page.waitForSelector('.xterm-text-layer');
+ await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
+ await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker1, overviewRulerOptions: { color: 'red' } })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker2, overviewRulerOptions: { color: 'blue' } })`);
+ await openTerminal(page);
+ await pollFor(page, `document.querySelectorAll('.xterm-decoration-overview-ruler').length`, 0);
+ });
+ it('should add an overview ruler when width is set', async () => {
+ await page.evaluate(`window.term = new Terminal({ overviewRulerWidth: 15 })`);
+ await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
+ await page.waitForSelector('.xterm-text-layer');
+ await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
+ await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker1, overviewRulerOptions: { color: 'red' } })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker2, overviewRulerOptions: { color: 'blue' } })`);
+ await openTerminal(page);
+ await pollFor(page, `document.querySelectorAll('.xterm-decoration-overview-ruler').length`, 1);
+ });
});
});
From 21263a5f60f390e33644a3be0d851719a7a0b8b3 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 14:54:37 -0400
Subject: [PATCH 064/245] cleanup
---
demo/client.ts | 12 ++++++------
.../Decorations/BufferDecorationRenderer.ts | 8 +++-----
src/browser/Decorations/OverviewRulerRenderer.ts | 16 +++++++---------
src/common/services/DecorationService.ts | 3 ---
typings/xterm.d.ts | 2 +-
5 files changed, 17 insertions(+), 24 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index f3f11806..047cd602 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -556,12 +556,12 @@ function addDecoration() {
function addOverviewRuler() {
term.options['overviewRulerWidth'] = 13;
- const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: 'red' }});
- term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: 'green' }});
- term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: 'blue' }});
- term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: 'red', position: 'left' }});
- term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: 'green', position: 'center' }});
- term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: 'blue', position: 'right' }});
+ const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929' }});
+ term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234' }});
+ term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf' }});
+ term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#ef2929', position: 'left' }});
+ term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
+ term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' }});
canvas.onRender((e) => {
e.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
});
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 8821ccc1..116c09a5 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -64,6 +64,8 @@ export class BufferDecorationRenderer extends Disposable {
if (!element) {
element = this._createElement(decoration);
decoration.onDispose(() => this._removeDecoration(decoration));
+ decoration.marker.onDispose(() => decoration.dispose());
+ decoration.element = element;
this._decorationElements.set(decoration, element);
this._container.appendChild(element);
}
@@ -105,11 +107,7 @@ export class BufferDecorationRenderer extends Disposable {
}
private _removeDecoration(decoration: IInternalDecoration): void {
- const element = this._decorationElements.get(decoration);
- if (element && this._container && this._container.contains(element)) {
- this._container.removeChild(element);
- }
- element?.remove();
+ this._decorationElements.get(decoration)?.remove();
this._decorationElements.delete(decoration);
}
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index eb58bc10..a9439d9f 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -8,15 +8,13 @@ import { IRenderService } from 'browser/services/Services';
import { Disposable } from 'common/Lifecycle';
import { IBufferService, IDecorationService, IInternalDecoration, IOptionsService } from 'common/services/Services';
-const enum ScrollbarConstants {
- WIDTH = 15
-}
-
export class OverviewRulerRenderer extends Disposable {
private _canvas: HTMLCanvasElement;
private _ctx: CanvasRenderingContext2D | null;
private readonly _decorationElements: Map = new Map();
-
+ private get _width(): number {
+ return this._optionsService.options.overviewRulerWidth || 0;
+ }
private _animationFrame: number | undefined;
constructor(
@@ -32,9 +30,9 @@ export class OverviewRulerRenderer extends Disposable {
this._canvas.classList.add('xterm-decoration-overview-ruler');
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
this._ctx = this._canvas.getContext('2d');
- this._canvas.style.width = `${this._optionsService.options.overviewRulerWidth || ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.width = `${this._width}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._optionsService.options.overviewRulerWidth|| ScrollbarConstants.WIDTH)* window.devicePixelRatio);
+ this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
this.refreshDecorations();
this.register(this._bufferService.buffers.onBufferActivate(() => {
@@ -92,9 +90,9 @@ export class OverviewRulerRenderer extends Disposable {
}
public refreshDecorations(): void {
- this._canvas.style.width = `${this._optionsService.options.overviewRulerWidth || ScrollbarConstants.WIDTH}px`;
+ this._canvas.style.width = `${this._width}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._optionsService.options.overviewRulerWidth || ScrollbarConstants.WIDTH)* window.devicePixelRatio);
+ this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
for (const decoration of this._decorationService.decorations) {
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 9d7e3314..911fd369 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -65,12 +65,9 @@ class Decoration extends Disposable implements IInternalDecoration {
) {
super();
this.marker = options.marker;
- this.marker.onDispose(() => this.dispose());
}
public override dispose(): void {
this._onDispose.fire();
- this.element?.remove();
- this.element = undefined;
super.dispose();
}
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index c6062967..0dce1ce6 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -436,7 +436,7 @@ declare module 'xterm' {
* after the first _onRender call, or undefined if accessed before
* that.
*/
- readonly element: HTMLElement | undefined;
+ element: HTMLElement | undefined;
}
/**
From a93f81c270798e2d37ec2c615f34eed6cd031790 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 14:57:34 -0400
Subject: [PATCH 065/245] Update typings/xterm.d.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
typings/xterm.d.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 0dce1ce6..0703021a 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -432,8 +432,8 @@ declare module 'xterm' {
readonly onRender: IEvent;
/**
- * The HTMLElement that gets created or drawn to (for scrollbar decorations)
- * after the first _onRender call, or undefined if accessed before
+ * The element that the decoration is rendered to. This will be undefined
+ * until it is rendered for the first time by @{link IDecoration.onRender}.
* that.
*/
element: HTMLElement | undefined;
From 8488f1ad6312c36c8f39307836d4782c522a8653 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 14:59:09 -0400
Subject: [PATCH 066/245] Update
src/browser/Decorations/OverviewRulerRenderer.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/browser/Decorations/OverviewRulerRenderer.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index a9439d9f..87ec3589 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -73,7 +73,7 @@ export class OverviewRulerRenderer extends Disposable {
} else {
this._canvas.style.left = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
}
- if (!decoration.options.overviewRulerOptions?.color) {
+ if (!decoration.options.overviewRulerOptions) {
this._decorationElements.delete(decoration);
return;
}
From 5b8777267a5566e7d52395e4008588241f2ee153 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 14:59:17 -0400
Subject: [PATCH 067/245] Update typings/xterm.d.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
typings/xterm.d.ts | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 0703021a..193f2ab9 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -465,9 +465,7 @@ declare module 'xterm' {
/**
- * The width of the decoration in cells, which defaults to
- * cell width or the width in pixels, when an overlayRulerItemColor
- * is provided.
+ * The width of the decoration in cells, defaults to 1.
*/
width?: number;
From 45ecc837e40740e3c01b0b787783d1380045c334 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 14:59:23 -0400
Subject: [PATCH 068/245] Update typings/xterm.d.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
typings/xterm.d.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 193f2ab9..05a1e786 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -470,8 +470,7 @@ declare module 'xterm' {
width?: number;
/**
- * The height of the decoration in cells, which defaults to
- * cell height
+ * The height of the decoration in cells, defaults to 1.
*/
height?: number;
From b146ffd30f3a72652404dcefc221af456d1a93a2 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 14:59:43 -0400
Subject: [PATCH 069/245] Update
src/browser/Decorations/OverviewRulerRenderer.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/browser/Decorations/OverviewRulerRenderer.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 87ec3589..04d2cb47 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -79,7 +79,8 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.lineWidth = 1;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
- const size = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
+ const outerSize = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
+ const innerSize = Math.ceil(this._optionsService.options.overviewRulerWidth / 3);
const position = decoration.options.overviewRulerOptions.position;
this._ctx.strokeRect(
!position || position === 'left' ? 0 : position === 'right' ? size * 2 + 1: size,
From fdc2bc49604280985bdf094905abab6421d59880 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 14:59:48 -0400
Subject: [PATCH 070/245] Update typings/xterm.d.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
typings/xterm.d.ts | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 05a1e786..b7c89319 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -475,10 +475,11 @@ declare module 'xterm' {
height?: number;
/**
- * Renders the decoration in the scrollbar
- * with the given @param color and optional @param position.
- * If @param position is not set, it will span the full @param overviewRulerWidth, which
- * must be provided via @TerminalOptions for this to work.
+ * When defined, renders the decoration in the overview ruler to the right
+ * of the terminal. {@link ITerminalOptions.overviewRulerWidth} must be set
+ * in order to see the overview ruler.
+ * @param color The color of the decoration.
+ * @param position The position of the decoration.
*/
overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right'}
}
From cee2411fb445885461fdac2e619c31db41b85155 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 15:00:05 -0400
Subject: [PATCH 071/245] more cleanup
---
.../Decorations/OverviewRulerRenderer.ts | 29 ++++++++++---------
typings/xterm.d.ts | 12 ++++----
2 files changed, 21 insertions(+), 20 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index a9439d9f..54862fb0 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -9,8 +9,8 @@ import { Disposable } from 'common/Lifecycle';
import { IBufferService, IDecorationService, IInternalDecoration, IOptionsService } from 'common/services/Services';
export class OverviewRulerRenderer extends Disposable {
- private _canvas: HTMLCanvasElement;
- private _ctx: CanvasRenderingContext2D | null;
+ private readonly _canvas: HTMLCanvasElement;
+ private readonly _ctx: CanvasRenderingContext2D;
private readonly _decorationElements: Map = new Map();
private get _width(): number {
return this._optionsService.options.overviewRulerWidth || 0;
@@ -29,23 +29,27 @@ export class OverviewRulerRenderer extends Disposable {
this._canvas = document.createElement('canvas');
this._canvas.classList.add('xterm-decoration-overview-ruler');
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
- this._ctx = this._canvas.getContext('2d');
+ const ctx = this._canvas.getContext('2d');
+ if (!ctx) {
+ throw new Error('Ctx cannot be null');
+ } else {
+ this._ctx = ctx;
+ }
this._canvas.style.width = `${this._width}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
- this.refreshDecorations();
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
}));
- this.register(this._renderService.onRenderedBufferChange(() => this.refreshDecorations()));
- this.register(this._renderService.onDimensionsChange(() => this.refreshDecorations()));
- this.register(addDisposableDomListener(window, 'resize', () => this.refreshDecorations()));
+ this.register(this._renderService.onRenderedBufferChange(() => this._queueRefresh()));
+ this.register(this._renderService.onDimensionsChange(() => this._queueRefresh()));
+ this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
this.register(this._optionsService.onOptionChange(o => {
if (o === 'overviewRulerWidth') {
- this.refreshDecorations();
+ this._queueRefresh();
}
}));
}
@@ -65,9 +69,6 @@ export class OverviewRulerRenderer extends Disposable {
}
private _refreshStyle(decoration: IInternalDecoration): void {
- if (!this._ctx || !this._optionsService.options.overviewRulerWidth) {
- return;
- }
if (decoration.options.anchor === 'right') {
this._canvas.style.right = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
} else {
@@ -79,7 +80,7 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.lineWidth = 1;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
- const size = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
+ const size = Math.floor(this._width / 3);
const position = decoration.options.overviewRulerOptions.position;
this._ctx.strokeRect(
!position || position === 'left' ? 0 : position === 'right' ? size * 2 + 1: size,
@@ -89,7 +90,7 @@ export class OverviewRulerRenderer extends Disposable {
);
}
- public refreshDecorations(): void {
+ private _refreshDecorations(): void {
this._canvas.style.width = `${this._width}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
@@ -114,7 +115,7 @@ export class OverviewRulerRenderer extends Disposable {
return;
}
this._animationFrame = window.requestAnimationFrame(() => {
- this.refreshDecorations();
+ this._refreshDecorations();
this._animationFrame = undefined;
});
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 0dce1ce6..e1c51934 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -450,18 +450,18 @@ declare module 'xterm' {
* The line in the terminal where
* the decoration will be displayed
*/
- marker: IMarker;
+ readonly marker: IMarker;
/*
* Where the decoration will be anchored -
* defaults to the left edge
*/
- anchor?: 'right' | 'left';
+ readonly anchor?: 'right' | 'left';
/**
* The x position offset relative to the anchor
*/
- x?: number;
+ readonly x?: number;
/**
@@ -469,13 +469,13 @@ declare module 'xterm' {
* cell width or the width in pixels, when an overlayRulerItemColor
* is provided.
*/
- width?: number;
+ readonly width?: number;
/**
* The height of the decoration in cells, which defaults to
* cell height
*/
- height?: number;
+ readonly height?: number;
/**
* Renders the decoration in the scrollbar
@@ -483,7 +483,7 @@ declare module 'xterm' {
* If @param position is not set, it will span the full @param overviewRulerWidth, which
* must be provided via @TerminalOptions for this to work.
*/
- overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right'}
+ readonly overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right'}
}
/**
From a39facfe28a6d555b3a60338fb7fcc08118ce506 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Tue, 15 Mar 2022 12:42:47 -0700
Subject: [PATCH 072/245] Restore cursor style too during serialize
Fixes #3677
---
.../src/SerializeAddon.test.ts | 231 +++++++++---------
.../src/SerializeAddon.ts | 18 +-
2 files changed, 134 insertions(+), 115 deletions(-)
diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.test.ts b/addons/xterm-addon-serialize/src/SerializeAddon.test.ts
index e35751d1..67b83884 100644
--- a/addons/xterm-addon-serialize/src/SerializeAddon.test.ts
+++ b/addons/xterm-addon-serialize/src/SerializeAddon.test.ts
@@ -43,7 +43,7 @@ class TestSelectionService {
}
}
-describe('xterm-addon-serialize html', () => {
+describe('xterm-addon-serialize', () => {
let cm: ColorManager;
let dom: jsdom.JSDOM;
let document: Document;
@@ -83,123 +83,132 @@ describe('xterm-addon-serialize html', () => {
(terminal as any)._core._selectionService = selectionService;
});
- it('empty terminal with selection turned off', () => {
- const output = serializeAddon.serializeAsHTML();
- assert.notEqual(output, '');
- assert.equal((output.match(/ {10}<\/span><\/div>/g) || []).length, 2);
- });
-
- it('empty terminal with no selection', () => {
- const output = serializeAddon.serializeAsHTML({
- onlySelection: true
+ describe('text', () => {
+ it('restoring cursor styles', async () => {
+ await writeP(terminal, sgr('32') + '> ' + sgr('0'));
+ assert.equal(serializeAddon.serialize(), '\u001b[32m> \u001b[0m');
});
- assert.equal(output, '');
});
- it('basic terminal with selection', async () => {
- await writeP(terminal, ' terminal ');
- terminal.select(1, 0, 8);
-
- const output = serializeAddon.serializeAsHTML({
- onlySelection: true
+ describe('html', () => {
+ it('empty terminal with selection turned off', () => {
+ const output = serializeAddon.serializeAsHTML();
+ assert.notEqual(output, '');
+ assert.equal((output.match(/ {10}<\/span><\/div>/g) || []).length, 2);
});
- assert.equal((output.match(/terminal<\/span><\/div>/g) || []).length, 1, output);
- });
- it('cells with bold styling', async () => {
- await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with italic styling', async () => {
- await writeP(terminal, ' ' + sgr('3') + 'terminal' + sgr('23') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with inverse styling', async () => {
- await writeP(terminal, ' ' + sgr('7') + 'terminal' + sgr('27') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with underline styling', async () => {
- await writeP(terminal, ' ' + sgr('4') + 'terminal' + sgr('24') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with invisible styling', async () => {
- await writeP(terminal, ' ' + sgr('8') + 'terminal' + sgr('28') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with dim styling', async () => {
- await writeP(terminal, ' ' + sgr('2') + 'terminal' + sgr('22') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with strikethrough styling', async () => {
- await writeP(terminal, ' ' + sgr('9') + 'terminal' + sgr('29') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with combined styling', async () => {
- await writeP(terminal, sgr('1') + ' ' + sgr('9') + 'termi' + sgr('22') + 'nal' + sgr('29') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/ <\/span>/g) || []).length, 1, output);
- assert.equal((output.match(/termi<\/span>/g) || []).length, 1, output);
- assert.equal((output.match(/nal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with color styling', async () => {
- await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('cells with background styling', async () => {
- await writeP(terminal, ' ' + sgr('48;5;46') + 'terminal' + sgr('49') + ' ');
-
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
- });
-
- it('empty terminal with default options', async () => {
- const output = serializeAddon.serializeAsHTML();
- assert.equal((output.match(/color: #000000; background-color: #ffffff; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
- });
-
- it('empty terminal with custom options', async () => {
- terminal.options.fontFamily = 'verdana';
- terminal.options.fontSize = 20;
- terminal.options.theme = {
- foreground: '#ff00ff',
- background: '#00ff00'
- };
- const output = serializeAddon.serializeAsHTML({
- includeGlobalBackground: true
+ it('empty terminal with no selection', () => {
+ const output = serializeAddon.serializeAsHTML({
+ onlySelection: true
+ });
+ assert.equal(output, '');
});
- assert.equal((output.match(/color: #ff00ff; background-color: #00ff00; font-family: verdana; font-size: 20px;/g) || []).length, 1, output);
- });
- it('empty terminal with background included', async () => {
- const output = serializeAddon.serializeAsHTML({
- includeGlobalBackground: true
+ it('basic terminal with selection', async () => {
+ await writeP(terminal, ' terminal ');
+ terminal.select(1, 0, 8);
+
+ const output = serializeAddon.serializeAsHTML({
+ onlySelection: true
+ });
+ assert.equal((output.match(/terminal<\/span><\/div>/g) || []).length, 1, output);
+ });
+
+ it('cells with bold styling', async () => {
+ await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with italic styling', async () => {
+ await writeP(terminal, ' ' + sgr('3') + 'terminal' + sgr('23') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with inverse styling', async () => {
+ await writeP(terminal, ' ' + sgr('7') + 'terminal' + sgr('27') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with underline styling', async () => {
+ await writeP(terminal, ' ' + sgr('4') + 'terminal' + sgr('24') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with invisible styling', async () => {
+ await writeP(terminal, ' ' + sgr('8') + 'terminal' + sgr('28') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with dim styling', async () => {
+ await writeP(terminal, ' ' + sgr('2') + 'terminal' + sgr('22') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with strikethrough styling', async () => {
+ await writeP(terminal, ' ' + sgr('9') + 'terminal' + sgr('29') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with combined styling', async () => {
+ await writeP(terminal, sgr('1') + ' ' + sgr('9') + 'termi' + sgr('22') + 'nal' + sgr('29') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/ <\/span>/g) || []).length, 1, output);
+ assert.equal((output.match(/termi<\/span>/g) || []).length, 1, output);
+ assert.equal((output.match(/nal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with color styling', async () => {
+ await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('cells with background styling', async () => {
+ await writeP(terminal, ' ' + sgr('48;5;46') + 'terminal' + sgr('49') + ' ');
+
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/terminal<\/span>/g) || []).length, 1, output);
+ });
+
+ it('empty terminal with default options', async () => {
+ const output = serializeAddon.serializeAsHTML();
+ assert.equal((output.match(/color: #000000; background-color: #ffffff; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
+ });
+
+ it('empty terminal with custom options', async () => {
+ terminal.options.fontFamily = 'verdana';
+ terminal.options.fontSize = 20;
+ terminal.options.theme = {
+ foreground: '#ff00ff',
+ background: '#00ff00'
+ };
+ const output = serializeAddon.serializeAsHTML({
+ includeGlobalBackground: true
+ });
+ assert.equal((output.match(/color: #ff00ff; background-color: #00ff00; font-family: verdana; font-size: 20px;/g) || []).length, 1, output);
+ });
+
+ it('empty terminal with background included', async () => {
+ const output = serializeAddon.serializeAsHTML({
+ includeGlobalBackground: true
+ });
+ assert.equal((output.match(/color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
});
- assert.equal((output.match(/color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
});
});
diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.ts b/addons/xterm-addon-serialize/src/SerializeAddon.ts
index 8d6f8b36..ed71e4e1 100644
--- a/addons/xterm-addon-serialize/src/SerializeAddon.ts
+++ b/addons/xterm-addon-serialize/src/SerializeAddon.ts
@@ -7,6 +7,7 @@
import { Terminal, ITerminalAddon, IBuffer, IBufferCell, IBufferRange } from 'xterm';
import { IColorSet } from 'browser/Types';
+import { IAttributeData } from 'common/Types';
function constrain(value: number, low: number, high: number): number {
return Math.max(low, Math.min(value, high));
@@ -62,17 +63,17 @@ abstract class BaseSerializeHandler {
protected _serializeString(): string { return ''; }
}
-function equalFg(cell1: IBufferCell, cell2: IBufferCell): boolean {
+function equalFg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean {
return cell1.getFgColorMode() === cell2.getFgColorMode()
&& cell1.getFgColor() === cell2.getFgColor();
}
-function equalBg(cell1: IBufferCell, cell2: IBufferCell): boolean {
+function equalBg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean {
return cell1.getBgColorMode() === cell2.getBgColorMode()
&& cell1.getBgColor() === cell2.getBgColor();
}
-function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
+function equalFlags(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean {
return cell1.isInverse() === cell2.isInverse()
&& cell1.isBold() === cell2.isBold()
&& cell1.isUnderline() === cell2.isUnderline()
@@ -229,7 +230,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
this._nullCellCount = 0;
}
- private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): number[] {
+ private _diffStyle(cell: IBufferCell | IAttributeData, oldCell: IBufferCell): number[] {
const sgrSeq: number[] = [];
const fgChanged = !equalFg(cell, oldCell);
const bgChanged = !equalBg(cell, oldCell);
@@ -393,6 +394,15 @@ class StringSerializeHandler extends BaseSerializeHandler {
moveRight(realCursorCol - this._lastCursorCol);
}
+ // Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677
+ // HACK: Internal API access since it's awkward to expose this in the API and serialize will
+ // likely be the only consumer
+ const curAttrData: IAttributeData = (this._terminal as any)._core._inputHandler._curAttrData;
+ const sgrSeq = this._diffStyle(curAttrData, this._cursorStyle);
+ if (sgrSeq.length > 0) {
+ content += `\u001b[${sgrSeq.join(';')}m`;
+ }
+
return content;
}
}
From 4cc0db42734895b7bd82ac5d151c56af6c20076a Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 17:06:10 -0400
Subject: [PATCH 073/245] Update
src/browser/Decorations/OverviewRulerRenderer.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/browser/Decorations/OverviewRulerRenderer.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index d6ac4941..a797a746 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -122,8 +122,7 @@ export class OverviewRulerRenderer extends Disposable {
}
private _removeDecoration(decoration: IInternalDecoration): void {
- const element = this._decorationElements.get(decoration);
- element?.remove();
+ this._decorationElements.get(decoration)?.remove();
this._decorationElements.delete(decoration);
}
}
From e7baccdbcd200cd8da9cda970c1c3fb3ab4b1d2a Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 17:07:01 -0400
Subject: [PATCH 074/245] Update src/browser/Terminal.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/browser/Terminal.ts | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index c2fb1500..98ed8b9a 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -82,11 +82,8 @@ export class Terminal extends CoreTerminal implements ITerminal {
private _customKeyEventHandler: CustomKeyEventHandler | undefined;
- // TODO: Move into CoreTerminal.ts
- // common services
- private _decorationService: DecorationService;
-
// browser services
+ private _decorationService: DecorationService;
private _charSizeService: ICharSizeService | undefined;
private _mouseService: IMouseService | undefined;
private _renderService: IRenderService | undefined;
From a9a6920b1303abb824abc9ae223ddc80221f19ef Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 17:08:01 -0400
Subject: [PATCH 075/245] more cleanup
---
src/browser/Decorations/BufferDecorationRenderer.ts | 1 +
typings/xterm.d.ts | 7 ++-----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 116c09a5..7cc2ee83 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -109,5 +109,6 @@ export class BufferDecorationRenderer extends Disposable {
private _removeDecoration(decoration: IInternalDecoration): void {
this._decorationElements.get(decoration)?.remove();
this._decorationElements.delete(decoration);
+ decoration.dispose();
}
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index a86d80c8..940e3a48 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -439,11 +439,8 @@ declare module 'xterm' {
element: HTMLElement | undefined;
}
- /**
- * Options provided when registering a decoration
- * containing a @param marker, @param anchor,
- * @param x offset from the anchor, @param width in cells
- * and @param height in cells.
+ /*
+ * Options that define the presentation of the decoration.
*/
export interface IDecorationOptions {
/**
From 8ccbef850eeab0f94c829e71596835e4ab1f5da6 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 17:38:15 -0400
Subject: [PATCH 076/245] only update canvas dimensions when needed
---
.../Decorations/BufferDecorationRenderer.ts | 1 -
.../Decorations/OverviewRulerRenderer.ts | 27 +++++++++----------
2 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 7cc2ee83..116c09a5 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -109,6 +109,5 @@ export class BufferDecorationRenderer extends Disposable {
private _removeDecoration(decoration: IInternalDecoration): void {
this._decorationElements.get(decoration)?.remove();
this._decorationElements.delete(decoration);
- decoration.dispose();
}
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index a797a746..d6d450c3 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -35,16 +35,13 @@ export class OverviewRulerRenderer extends Disposable {
} else {
this._ctx = ctx;
}
- this._canvas.style.width = `${this._width}px`;
- this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
- this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ this._queueRefresh(true);
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
}));
this.register(this._renderService.onRenderedBufferChange(() => this._queueRefresh()));
- this.register(this._renderService.onDimensionsChange(() => this._queueRefresh()));
- this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh()));
+ this.register(this._renderService.onDimensionsChange(() => this._queueRefresh(true)));
+ this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh(true)));
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
this.register(this._optionsService.onOptionChange(o => {
@@ -91,12 +88,14 @@ export class OverviewRulerRenderer extends Disposable {
);
}
- private _refreshDecorations(): void {
- this._canvas.style.width = `${this._width}px`;
- this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
- this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
-
+ private _refreshDecorations(updateCanvasDimensions?: boolean): void {
+ if (updateCanvasDimensions) {
+ this._canvas.style.width = `${this._width}px`;
+ this._canvas.style.height = `${this._screenElement.clientHeight}px`;
+ this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
+ this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ }
+ this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
for (const decoration of this._decorationService.decorations) {
this._renderDecoration(decoration);
}
@@ -111,12 +110,12 @@ export class OverviewRulerRenderer extends Disposable {
decoration.onRenderEmitter.fire(this._canvas);
}
- private _queueRefresh(): void {
+ private _queueRefresh(updateCanvasDimensions?: boolean): void {
if (this._animationFrame !== undefined) {
return;
}
this._animationFrame = window.requestAnimationFrame(() => {
- this._refreshDecorations();
+ this._refreshDecorations(updateCanvasDimensions);
this._animationFrame = undefined;
});
}
From 53c432df272bbf65de0d2b9a70c47343260e3fce Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 17:58:44 -0400
Subject: [PATCH 077/245] add condition for when to update anchor
---
demo/client.ts | 2 +-
.../Decorations/OverviewRulerRenderer.ts | 53 ++++++++++++-------
2 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 047cd602..d8d792f1 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -563,7 +563,7 @@ function addOverviewRuler() {
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' }});
canvas.onRender((e) => {
- e.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 5}px`;
+ e.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 1}px`;
});
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index d6d450c3..865ac990 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -8,6 +8,16 @@ import { IRenderService } from 'browser/services/Services';
import { Disposable } from 'common/Lifecycle';
import { IBufferService, IDecorationService, IInternalDecoration, IOptionsService } from 'common/services/Services';
+// This is used to reduce memory usage
+// when refreshStyle is called
+// by storing and updating
+// the sizes of the decorations to be drawn
+const workArray = new Uint16Array(3);
+const enum WorkIndex {
+ OUTER_SIZE = 0,
+ INNER_SIZE = 0
+}
+
export class OverviewRulerRenderer extends Disposable {
private readonly _canvas: HTMLCanvasElement;
private readonly _ctx: CanvasRenderingContext2D;
@@ -40,15 +50,21 @@ export class OverviewRulerRenderer extends Disposable {
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
}));
this.register(this._renderService.onRenderedBufferChange(() => this._queueRefresh()));
- this.register(this._renderService.onDimensionsChange(() => this._queueRefresh(true)));
+ this.register(this._renderService.onDimensionsChange(() => this._queueRefresh(true, true)));
this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh(true)));
- this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
+ this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh(undefined, true)));
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
this.register(this._optionsService.onOptionChange(o => {
- if (o === 'overviewRulerWidth') {
+ if (o === 'overviewRulerWidth' && this._optionsService.options.overviewRulerWidth) {
+ workArray[WorkIndex.OUTER_SIZE] = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
+ workArray[WorkIndex.INNER_SIZE] = Math.ceil(this._optionsService.options.overviewRulerWidth / 3);
this._queueRefresh();
}
}));
+ if (this._optionsService.options.overviewRulerWidth) {
+ workArray[WorkIndex.OUTER_SIZE] = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
+ workArray[WorkIndex.INNER_SIZE] = Math.ceil(this._optionsService.options.overviewRulerWidth / 3);
+ }
}
public override dispose(): void {
@@ -65,11 +81,13 @@ export class OverviewRulerRenderer extends Disposable {
super.dispose();
}
- private _refreshStyle(decoration: IInternalDecoration): void {
- if (decoration.options.anchor === 'right') {
- this._canvas.style.right = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
- } else {
- this._canvas.style.left = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
+ private _refreshStyle(decoration: IInternalDecoration, updateAnchor?: boolean): void {
+ if (updateAnchor) {
+ if (decoration.options.anchor === 'right') {
+ this._canvas.style.right = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
+ } else {
+ this._canvas.style.left = decoration.options.x ? `${decoration.options.x * this._renderService.dimensions.actualCellWidth}px` : '';
+ }
}
if (!decoration.options.overviewRulerOptions) {
this._decorationElements.delete(decoration);
@@ -77,18 +95,15 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.lineWidth = 1;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
- const outerSize = Math.floor(this._width / 3);
- const innerSize = Math.ceil(this._width / 3);
- const position = decoration.options.overviewRulerOptions.position;
this._ctx.strokeRect(
- !position || position === 'left' ? 0 : position === 'right' ? outerSize + innerSize: outerSize,
+ !decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? workArray[WorkIndex.OUTER_SIZE] + workArray[WorkIndex.INNER_SIZE]: workArray[WorkIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- !position ? this._canvas.width : position === 'center' ? innerSize : outerSize,
+ !decoration.options.overviewRulerOptions.position ? this._canvas.width : decoration.options.overviewRulerOptions.position === 'center' ? workArray[WorkIndex.INNER_SIZE] : workArray[WorkIndex.OUTER_SIZE],
window.devicePixelRatio
);
}
- private _refreshDecorations(updateCanvasDimensions?: boolean): void {
+ private _refreshDecorations(updateCanvasDimensions?: boolean, updateAnchor?: boolean): void {
if (updateCanvasDimensions) {
this._canvas.style.width = `${this._width}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
@@ -97,25 +112,25 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
for (const decoration of this._decorationService.decorations) {
- this._renderDecoration(decoration);
+ this._renderDecoration(decoration, updateAnchor);
}
}
- private _renderDecoration(decoration: IInternalDecoration): void {
+ private _renderDecoration(decoration: IInternalDecoration, updateAnchor?: boolean): void {
const element = this._decorationElements.get(decoration);
if (!element) {
this._decorationElements.set(decoration, this._canvas);
}
- this._refreshStyle(decoration);
+ this._refreshStyle(decoration, updateAnchor);
decoration.onRenderEmitter.fire(this._canvas);
}
- private _queueRefresh(updateCanvasDimensions?: boolean): void {
+ private _queueRefresh(updateCanvasDimensions?: boolean, updateAnchor?: boolean): void {
if (this._animationFrame !== undefined) {
return;
}
this._animationFrame = window.requestAnimationFrame(() => {
- this._refreshDecorations(updateCanvasDimensions);
+ this._refreshDecorations(updateCanvasDimensions, updateAnchor);
this._animationFrame = undefined;
});
}
From b05db37ac13590bd125ad4014dee84e5b0b789c3 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 18:12:19 -0400
Subject: [PATCH 078/245] adjust size based on position
---
demo/client.ts | 7 ++-----
src/browser/Decorations/OverviewRulerRenderer.ts | 2 +-
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index d8d792f1..3d3a3c3f 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -555,15 +555,12 @@ function addDecoration() {
}
function addOverviewRuler() {
- term.options['overviewRulerWidth'] = 13;
- const canvas = term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929' }});
+ term.options['overviewRulerWidth'] = 12;
+ term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929' }});
term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234' }});
term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#ef2929', position: 'left' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' }});
- canvas.onRender((e) => {
- e.style.left = `${document.querySelector('.xterm-viewport').clientWidth + 1}px`;
- });
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 865ac990..e22a3257 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -93,7 +93,7 @@ export class OverviewRulerRenderer extends Disposable {
this._decorationElements.delete(decoration);
return;
}
- this._ctx.lineWidth = 1;
+ this._ctx.lineWidth = !decoration.options.overviewRulerOptions.position ? 2 : 6;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
this._ctx.strokeRect(
!decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? workArray[WorkIndex.OUTER_SIZE] + workArray[WorkIndex.INNER_SIZE]: workArray[WorkIndex.OUTER_SIZE],
From d92e63648269f3df7efb0635e677e3b4c1e51866 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 18:12:48 -0400
Subject: [PATCH 079/245] Update src/browser/Terminal.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/browser/Terminal.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 98ed8b9a..d69e5d6f 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -583,7 +583,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.register(this.onScroll(() => this._mouseZoneManager!.clearAll()));
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
- this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement);
+ this.register(this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement));
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
From 035a7f710ed59623572fd4983ab94492950da293 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 18:12:58 -0400
Subject: [PATCH 080/245] Update src/browser/Terminal.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/browser/Terminal.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index d69e5d6f..31ef1287 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -1016,7 +1016,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- return this._decorationService!.registerDecoration(decorationOptions);
+ return this._decorationService.registerDecoration(decorationOptions);
}
/**
From 5777fbc60809fbc271f7f24424c58017a87ee4b5 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 18:13:10 -0400
Subject: [PATCH 081/245] Update src/browser/tsconfig.json
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
src/browser/tsconfig.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/browser/tsconfig.json b/src/browser/tsconfig.json
index fc997477..212aeab8 100644
--- a/src/browser/tsconfig.json
+++ b/src/browser/tsconfig.json
@@ -18,7 +18,7 @@
"include": [
"./**/*",
"../../typings/xterm.d.ts"
-],
+ ],
"references": [
{ "path": "../common" }
]
From 6f5008dba691b89943b468cc91321bb5d95d6af0 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 18:13:23 -0400
Subject: [PATCH 082/245] Update typings/xterm.d.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
typings/xterm.d.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 940e3a48..64ef2a08 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -268,7 +268,8 @@ declare module 'xterm' {
windowOptions?: IWindowOptions;
/**
- * The width, in pixels, of the canvas for the overview ruler.
+ * The width, in pixels, of the canvas for the overview ruler. The overview
+ * ruler will be hidden when not set.
*/
overviewRulerWidth?: number;
}
From a4f830aa5d521cd8acfa8e9a920793806757f6fb Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Tue, 15 Mar 2022 18:13:36 -0400
Subject: [PATCH 083/245] Update typings/xterm.d.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
typings/xterm.d.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 64ef2a08..24ba940e 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -400,7 +400,7 @@ declare module 'xterm' {
}
/**
- * Represents a disposable with an
+ * Represents a disposable that tracks is disposed state.
* @param onDispose event listener and
* @param isDisposed property.
*/
From 22d889ab07ff0b879e6f4408eed5c5cdeb8b088a Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 19:15:59 -0400
Subject: [PATCH 084/245] use this._width instead of options
---
.../Decorations/OverviewRulerRenderer.ts | 21 +++++++++----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index e22a3257..bffdbb66 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -12,8 +12,8 @@ import { IBufferService, IDecorationService, IInternalDecoration, IOptionsServic
// when refreshStyle is called
// by storing and updating
// the sizes of the decorations to be drawn
-const workArray = new Uint16Array(3);
-const enum WorkIndex {
+const renderSizes = new Uint16Array(3);
+const enum SizeIndex {
OUTER_SIZE = 0,
INNER_SIZE = 0
}
@@ -55,16 +55,14 @@ export class OverviewRulerRenderer extends Disposable {
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh(undefined, true)));
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
this.register(this._optionsService.onOptionChange(o => {
- if (o === 'overviewRulerWidth' && this._optionsService.options.overviewRulerWidth) {
- workArray[WorkIndex.OUTER_SIZE] = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
- workArray[WorkIndex.INNER_SIZE] = Math.ceil(this._optionsService.options.overviewRulerWidth / 3);
+ if (o === 'overviewRulerWidth') {
+ renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._width / 3);
+ renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._width / 3);
this._queueRefresh();
}
}));
- if (this._optionsService.options.overviewRulerWidth) {
- workArray[WorkIndex.OUTER_SIZE] = Math.floor(this._optionsService.options.overviewRulerWidth / 3);
- workArray[WorkIndex.INNER_SIZE] = Math.ceil(this._optionsService.options.overviewRulerWidth / 3);
- }
+ renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._width / 3);
+ renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._width / 3);
}
public override dispose(): void {
@@ -95,10 +93,11 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.lineWidth = !decoration.options.overviewRulerOptions.position ? 2 : 6;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
+
this._ctx.strokeRect(
- !decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? workArray[WorkIndex.OUTER_SIZE] + workArray[WorkIndex.INNER_SIZE]: workArray[WorkIndex.OUTER_SIZE],
+ !decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- !decoration.options.overviewRulerOptions.position ? this._canvas.width : decoration.options.overviewRulerOptions.position === 'center' ? workArray[WorkIndex.INNER_SIZE] : workArray[WorkIndex.OUTER_SIZE],
+ !decoration.options.overviewRulerOptions.position ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
window.devicePixelRatio
);
}
From 963ed594591d21bdfa0fa95ae4e61c677d64c4da Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 19:29:33 -0400
Subject: [PATCH 085/245] tweak demo
---
demo/client.ts | 2 +-
src/browser/Decorations/OverviewRulerRenderer.ts | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 3d3a3c3f..bbec94c0 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -555,7 +555,7 @@ function addDecoration() {
}
function addOverviewRuler() {
- term.options['overviewRulerWidth'] = 12;
+ term.options['overviewRulerWidth'] = 15;
term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929' }});
term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234' }});
term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf' }});
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index bffdbb66..a46f9872 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -93,7 +93,6 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.lineWidth = !decoration.options.overviewRulerOptions.position ? 2 : 6;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
-
this._ctx.strokeRect(
!decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
From c277fb742d03be07b87d29a519fb5dee41e158c8 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 20:50:13 -0400
Subject: [PATCH 086/245] add overviewRuler to addDecoration
---
demo/client.ts | 4 +++-
src/browser/Decorations/OverviewRulerRenderer.ts | 3 ++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index bbec94c0..b8023f27 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -545,13 +545,15 @@ function loadTest() {
}
function addDecoration() {
+ term.options['overviewRulerWidth'] = 15;
const marker = term.addMarker(1);
const decoration = term.registerDecoration({ marker });
decoration.onRender((e) => {
if (e.classList.value === 'xterm-decoration') {
- e.style.backgroundColor = 'red';
+ e.style.backgroundColor = '#ef2929';
}
});
+ term.registerDecoration({marker, overviewRulerOptions: { color: '#ef2929'}})
}
function addOverviewRuler() {
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index a46f9872..9f8ada32 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -61,6 +61,7 @@ export class OverviewRulerRenderer extends Disposable {
this._queueRefresh();
}
}));
+ console.log(this._width/3);
renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._width / 3);
renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._width / 3);
}
@@ -96,7 +97,7 @@ export class OverviewRulerRenderer extends Disposable {
this._ctx.strokeRect(
!decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- !decoration.options.overviewRulerOptions.position ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
+ !decoration.options.overviewRulerOptions.position ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
window.devicePixelRatio
);
}
From a0a7abbf497b3af8439e3d801e9b01dd6095d45f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Tue, 15 Mar 2022 21:05:14 -0400
Subject: [PATCH 087/245] add overview options to decoration in demo
---
demo/client.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index b8023f27..2ae649e4 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -547,13 +547,12 @@ function loadTest() {
function addDecoration() {
term.options['overviewRulerWidth'] = 15;
const marker = term.addMarker(1);
- const decoration = term.registerDecoration({ marker });
+ const decoration = term.registerDecoration({ marker, overviewRulerOptions: { color: '#ef2929'} });
decoration.onRender((e) => {
if (e.classList.value === 'xterm-decoration') {
e.style.backgroundColor = '#ef2929';
}
});
- term.registerDecoration({marker, overviewRulerOptions: { color: '#ef2929'}})
}
function addOverviewRuler() {
From 8b55497adff94d99fab18ef43d0b2ccbda131b63 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Wed, 16 Mar 2022 07:50:01 -0700
Subject: [PATCH 088/245] Remove unwanted console.log
---
src/browser/Decorations/OverviewRulerRenderer.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 9f8ada32..53f1a99c 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -61,7 +61,6 @@ export class OverviewRulerRenderer extends Disposable {
this._queueRefresh();
}
}));
- console.log(this._width/3);
renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._width / 3);
renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._width / 3);
}
From 1bab173e99885eb70e70e3a0c7396c99fa55c6b6 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 12:45:18 -0400
Subject: [PATCH 089/245] fix problems
---
addons/xterm-addon-search/src/SearchAddon.ts | 2 +-
src/browser/services/DecorationService.ts | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 5e089594..104ca532 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -537,7 +537,7 @@ export class SearchAddon implements ITerminalAddon {
if (!marker) {
return undefined;
}
- const findResultDecoration = terminal.registerDecoration({ marker });
+ const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'yellow' } });
findResultDecoration?.onRender((e) => {
if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0) {
e.classList.add('xterm-find-result-decoration');
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
index a77409db..80f7f0d8 100644
--- a/src/browser/services/DecorationService.ts
+++ b/src/browser/services/DecorationService.ts
@@ -3,10 +3,10 @@
* @license MIT
*/
-import { IDecorationService, IRenderService } from 'browser/services/Services';
+import { IRenderService } from 'browser/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
-import { IBufferService, IInstantiationService } from 'common/services/Services';
+import { IBufferService, IInstantiationService, IDecorationService } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
export class DecorationService extends Disposable implements IDecorationService {
From 40b8854df4fcb2de238a8213f3ab168e9ae18a69 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 12:47:46 -0400
Subject: [PATCH 090/245] rm bad decoration service
---
src/browser/services/DecorationService.ts | 164 ----------------------
1 file changed, 164 deletions(-)
delete mode 100644 src/browser/services/DecorationService.ts
diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts
deleted file mode 100644
index 80f7f0d8..00000000
--- a/src/browser/services/DecorationService.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
- * Copyright (c) 2022 The xterm.js authors. All rights reserved.
- * @license MIT
- */
-
-import { IRenderService } from 'browser/services/Services';
-import { EventEmitter, IEvent } from 'common/EventEmitter';
-import { Disposable } from 'common/Lifecycle';
-import { IBufferService, IInstantiationService, IDecorationService } from 'common/services/Services';
-import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
-
-export class DecorationService extends Disposable implements IDecorationService {
-
- private readonly _decorations: Decoration[] = [];
- private _container: HTMLElement | undefined;
- private _screenElement: HTMLElement | undefined;
- private _renderService: IRenderService | undefined;
- private _animationFrame: number | undefined;
-
- constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { super(); }
-
- public attachToDom(screenElement: HTMLElement, renderService: IRenderService): void {
- this._renderService = renderService;
- this._screenElement = screenElement;
- this._container = document.createElement('div');
- this._container.classList.add('xterm-decoration-container');
- screenElement.appendChild(this._container);
- this.register(this._renderService.onRenderedBufferChange(() => this.refresh()));
- this.register(this._renderService.onDimensionsChange(() => this.refresh(true)));
- }
-
- public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- if (decorationOptions.marker.isDisposed || !this._container) {
- return undefined;
- }
- const decoration = this._instantiationService.createInstance(Decoration, decorationOptions, this._container);
- this._decorations.push(decoration);
- decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
- this._queueRefresh();
- return decoration;
- }
-
- private _queueRefresh(): void {
- if (this._animationFrame !== undefined) {
- return;
- }
- this._animationFrame = window.requestAnimationFrame(() => {
- this.refresh();
- this._animationFrame = undefined;
- });
- }
-
- public refresh(shouldRecreate?: boolean): void {
- if (!this._renderService) {
- return;
- }
- for (const decoration of this._decorations) {
- decoration.render(this._renderService, shouldRecreate);
- }
- }
-
- public dispose(): void {
- for (const decoration of this._decorations) {
- decoration.dispose();
- }
- if (this._screenElement && this._container && this._screenElement.contains(this._container)) {
- this._screenElement.removeChild(this._container);
- }
- }
-}
-export class Decoration extends Disposable implements IDecoration {
- private readonly _marker: IMarker;
- private _element: HTMLElement | undefined;
-
- public isDisposed: boolean = false;
-
- public get element(): HTMLElement | undefined { return this._element; }
- public get marker(): IMarker { return this._marker; }
-
- private _onDispose = new EventEmitter();
- public get onDispose(): IEvent { return this._onDispose.event; }
-
- private _onRender = new EventEmitter();
- public get onRender(): IEvent { return this._onRender.event; }
-
- public x: number;
- public anchor: 'left' | 'right';
- public width: number;
- public height: number;
-
- constructor(
- options: IDecorationOptions,
- private readonly _container: HTMLElement,
- @IBufferService private readonly _bufferService: IBufferService
- ) {
- super();
- this.x = options.x ?? 0;
- this._marker = options.marker;
- this._marker.onDispose(() => this.dispose());
- this.anchor = options.anchor || 'left';
- this.width = options.width || 1;
- this.height = options.height || 1;
- }
-
- public render(renderService: IRenderService, shouldRecreate?: boolean): void {
- if (!this._element || shouldRecreate) {
- this._createElement(renderService, shouldRecreate);
- }
- if (this._container && this._element && !this._container.contains(this._element)) {
- this._container.append(this._element);
- }
- this._refreshStyle(renderService);
- if (this._element) {
- this._onRender.fire(this._element);
- }
- }
-
- private _createElement(renderService: IRenderService, shouldRecreate?: boolean): void {
- if (shouldRecreate && this._element && this._container.contains(this._element)) {
- this._container.removeChild(this._element);
- }
- this._element = document.createElement('div');
- this._element.classList.add('xterm-decoration');
- this._element.style.width = `${this.width * renderService.dimensions.actualCellWidth}px`;
- this._element.style.height = `${this.height * renderService.dimensions.actualCellHeight}px`;
- this._element.style.top = `${(this.marker.line - this._bufferService.buffers.active.ydisp) * renderService.dimensions.actualCellHeight}px`;
- this._element.style.lineHeight = `${renderService.dimensions.actualCellHeight}px`;
-
- if (this.x && this.x > this._bufferService.cols) {
- // exceeded the container width, so hide
- this._element.style.display = 'none';
- }
- if (this.anchor === 'right') {
- this._element.style.right = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
- } else {
- this._element.style.left = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
- }
- }
-
- private _refreshStyle(renderService: IRenderService): void {
- if (!this._element) {
- return;
- }
- const line = this.marker.line - this._bufferService.buffers.active.ydisp;
- if (line < 0 || line >= this._bufferService.rows) {
- // outside of viewport
- this._element.style.display = 'none';
- } else {
- this._element.style.top = `${line * renderService.dimensions.actualCellHeight}px`;
- this._element.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
- }
- }
-
- public override dispose(): void {
- if (this.isDisposed) {
- return;
- }
- if (this._element && this._container.contains(this._element)) {
- this._container.removeChild(this._element);
- }
- this.isDisposed = true;
- this._onDispose.fire();
- }
-}
From 05eb12e6dd051c7c845e524a6d2b05e16ade2f4b Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 13:03:21 -0400
Subject: [PATCH 091/245] start work on selection color
---
addons/xterm-addon-search/src/SearchAddon.ts | 24 ++++++++++++++------
css/xterm.css | 2 +-
2 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 104ca532..1e54ee3b 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -44,6 +44,7 @@ export class SearchAddon implements ITerminalAddon {
private _result: ISearchResult | undefined;
private _reset: boolean = false;
private _cachedSearchTerm: string | undefined;
+ private _cachedResults: ISearchResult[] = [];
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -81,6 +82,11 @@ export class SearchAddon implements ITerminalAddon {
}
if (!this._reset && term === this._cachedSearchTerm) {
+ // this._resultDecorations.forEach(d => d.dispose());
+ // this._resultDecorations = [];
+ // for (const decoration of this._cachedResults) {
+ // this._showResultDecoration(decoration);
+ // }
return this.findNext(term, searchOptions);
}
this._reset = false;
@@ -89,18 +95,17 @@ export class SearchAddon implements ITerminalAddon {
// new search, clear out the old decorations
this._resultDecorations.forEach(d => d.dispose());
this._resultDecorations = [];
- const results: ISearchResult[] = [];
searchOptions = searchOptions || {};
searchOptions.incremental = false;
let found = this.findNext(term, searchOptions);
- while (found && !results.find(r => r?.col === this._result?.col && r?.row === this._result?.row)) {
+ while (found && !this._cachedResults.find(r => r?.col === this._result?.col && r?.row === this._result?.row)) {
if (this._result) {
- results.push(this._result);
+ this._cachedResults.push(this._result);
}
found = this.findNext(term, searchOptions);
}
- for (const result of results) {
+ for (const result of this._cachedResults) {
if (result) {
const resultDecoration = this._showResultDecoration(result);
if (resultDecoration) {
@@ -108,7 +113,7 @@ export class SearchAddon implements ITerminalAddon {
}
}
}
- if (results.length > 0) {
+ if (this._cachedResults.length > 0) {
this._cachedSearchTerm = term;
}
return true;
@@ -517,6 +522,10 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
terminal.select(result.col, result.row, result.size);
+ const marker = terminal.registerMarker(undefined, result.row);
+ if (marker) {
+ terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'yellow' } });
+ }
// If it is not in the viewport then we scroll else it just gets selected
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
let scroll = result.row - terminal.buffer.active.viewportY;
@@ -537,9 +546,10 @@ export class SearchAddon implements ITerminalAddon {
if (!marker) {
return undefined;
}
- const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'yellow' } });
+ terminal.options.overviewRulerWidth = 10;
+ const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue' } });
findResultDecoration?.onRender((e) => {
- if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0) {
+ if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0 && !e.classList.contains('xterm-decoration-overview-ruler')) {
e.classList.add('xterm-find-result-decoration');
// decoration's clientWidth = actualCellWidth
e.style.left = `${e.clientWidth * result.col}px`;
diff --git a/css/xterm.css b/css/xterm.css
index 9b9c47f8..3160a26e 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -180,7 +180,7 @@
}
.xterm-find-result-decoration {
- background-color: yellow;
+ background-color: blue;
opacity: 60%;
}
.xterm-decoration-overview-ruler {
From 1a9965ef01bba481c6e1b6d62d13e8c0f80bc269 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 13:20:48 -0400
Subject: [PATCH 092/245] fix #3683
---
src/browser/Decorations/OverviewRulerRenderer.ts | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 53f1a99c..f62c6647 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -15,7 +15,7 @@ import { IBufferService, IDecorationService, IInternalDecoration, IOptionsServic
const renderSizes = new Uint16Array(3);
const enum SizeIndex {
OUTER_SIZE = 0,
- INNER_SIZE = 0
+ INNER_SIZE = 1
}
export class OverviewRulerRenderer extends Disposable {
@@ -91,13 +91,14 @@ export class OverviewRulerRenderer extends Disposable {
this._decorationElements.delete(decoration);
return;
}
- this._ctx.lineWidth = !decoration.options.overviewRulerOptions.position ? 2 : 6;
+ this._ctx.lineWidth = 1;
this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
- this._ctx.strokeRect(
+ this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
+ this._ctx.fillRect(
!decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- !decoration.options.overviewRulerOptions.position ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
- window.devicePixelRatio
+ !decoration.options.overviewRulerOptions.position ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
+ window.devicePixelRatio * (!decoration.options.overviewRulerOptions.position ? 2 : 6)
);
}
From e293d29fd3306fc04b9a0b6fa5308eb16bb4f4d7 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 13:31:03 -0400
Subject: [PATCH 093/245] add a comment
---
src/browser/Decorations/OverviewRulerRenderer.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index f62c6647..aa91cad9 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -92,13 +92,13 @@ export class OverviewRulerRenderer extends Disposable {
return;
}
this._ctx.lineWidth = 1;
- this._ctx.strokeStyle = decoration.options.overviewRulerOptions.color;
this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
!decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
!decoration.options.overviewRulerOptions.position ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
- window.devicePixelRatio * (!decoration.options.overviewRulerOptions.position ? 2 : 6)
+ // when a position is provided, the element has less width, so increase its height
+ window.devicePixelRatio * (decoration.options.overviewRulerOptions.position ? 6 : 2)
);
}
From a4212e98d29973388eedf32dc2d762578f4c8c1c Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 13:40:06 -0400
Subject: [PATCH 094/245] fix #3686
---
demo/client.ts | 6 +-----
src/browser/Decorations/OverviewRulerRenderer.ts | 1 -
2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 2ae649e4..67e27759 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -548,11 +548,7 @@ function addDecoration() {
term.options['overviewRulerWidth'] = 15;
const marker = term.addMarker(1);
const decoration = term.registerDecoration({ marker, overviewRulerOptions: { color: '#ef2929'} });
- decoration.onRender((e) => {
- if (e.classList.value === 'xterm-decoration') {
- e.style.backgroundColor = '#ef2929';
- }
- });
+ decoration.onRender((e) => e.style.backgroundColor = '#ef2929');
}
function addOverviewRuler() {
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index aa91cad9..b34b592d 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -121,7 +121,6 @@ export class OverviewRulerRenderer extends Disposable {
this._decorationElements.set(decoration, this._canvas);
}
this._refreshStyle(decoration, updateAnchor);
- decoration.onRenderEmitter.fire(this._canvas);
}
private _queueRefresh(updateCanvasDimensions?: boolean, updateAnchor?: boolean): void {
From 6162ae9ffe0acca27a5bcbd2808242c8d034eccc Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 14:33:51 -0400
Subject: [PATCH 095/245] allow changing the color of overview decorations
---
src/browser/Decorations/OverviewRulerRenderer.ts | 2 +-
src/common/services/DecorationService.ts | 2 ++
typings/xterm.d.ts | 5 +++++
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index b34b592d..b63a5aec 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -92,7 +92,7 @@ export class OverviewRulerRenderer extends Disposable {
return;
}
this._ctx.lineWidth = 1;
- this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
+ this._ctx.fillStyle = decoration.overviewRulerDecorationColor || decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
!decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 911fd369..328c51d5 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -60,6 +60,8 @@ class Decoration extends Disposable implements IInternalDecoration {
private _onDispose = this.register(new EventEmitter());
public readonly onDispose = this._onDispose.event;
+ public overviewRulerDecorationColor: string | undefined;
+
constructor(
public readonly options: IDecorationOptions
) {
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 24ba940e..88b2856e 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -438,6 +438,11 @@ declare module 'xterm' {
* that.
*/
element: HTMLElement | undefined;
+
+ /**
+ * The color for the decoration.
+ */
+ overviewRulerDecorationColor: string | undefined;
}
/*
From fb13f812f7106a561ad4704dc2b517f17591de18 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 14:37:12 -0400
Subject: [PATCH 096/245] tweak description
---
typings/xterm.d.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 88b2856e..4853ac48 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -440,7 +440,9 @@ declare module 'xterm' {
element: HTMLElement | undefined;
/**
- * The color for the decoration.
+ * The color to be used for the overview ruler decoration.
+ * This will only take effect when @param overviewRulerOptions
+ * were provided initially.
*/
overviewRulerDecorationColor: string | undefined;
}
From f4593b76df7c50c30ce343231b1310316dc22f7f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 15:20:13 -0400
Subject: [PATCH 097/245] use options
---
src/browser/Decorations/OverviewRulerRenderer.ts | 2 +-
src/common/services/DecorationService.ts | 2 --
typings/xterm.d.ts | 12 +++++++++---
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index b63a5aec..620e4dbb 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -92,7 +92,7 @@ export class OverviewRulerRenderer extends Disposable {
return;
}
this._ctx.lineWidth = 1;
- this._ctx.fillStyle = decoration.overviewRulerDecorationColor || decoration.options.overviewRulerOptions.color;
+ this._ctx.fillStyle = decoration.overviewRulerOptions?.color || decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
!decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 328c51d5..911fd369 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -60,8 +60,6 @@ class Decoration extends Disposable implements IInternalDecoration {
private _onDispose = this.register(new EventEmitter());
public readonly onDispose = this._onDispose.event;
- public overviewRulerDecorationColor: string | undefined;
-
constructor(
public readonly options: IDecorationOptions
) {
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 4853ac48..2254df1c 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -440,13 +440,19 @@ declare module 'xterm' {
element: HTMLElement | undefined;
/**
- * The color to be used for the overview ruler decoration.
+ * The options for the overview ruler that can be updated.
* This will only take effect when @param overviewRulerOptions
* were provided initially.
*/
- overviewRulerDecorationColor: string | undefined;
+ overviewRulerOptions?: IDecorationOverviewRulerOptions;
}
+
+interface IDecorationOverviewRulerOptions {
+ color: string;
+ position?: 'left' | 'center' | 'right';
+}
+
/*
* Options that define the presentation of the decoration.
*/
@@ -486,7 +492,7 @@ declare module 'xterm' {
* @param color The color of the decoration.
* @param position The position of the decoration.
*/
- readonly overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right'}
+ overviewRulerOptions?: IDecorationOverviewRulerOptions
}
/**
From f92d1f46a6717f00ec0ac188b1b5e6f00b811d65 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 15:36:03 -0400
Subject: [PATCH 098/245] only allow updating color
---
typings/xterm.d.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 2254df1c..f67c30e3 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -444,7 +444,7 @@ declare module 'xterm' {
* This will only take effect when @param overviewRulerOptions
* were provided initially.
*/
- overviewRulerOptions?: IDecorationOverviewRulerOptions;
+ overviewRulerOptions?: Pick;
}
From 6899f9dc4d74e327d58b8832c73e007d3061dfa4 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 15:55:15 -0400
Subject: [PATCH 099/245] tweak jsdoc
---
typings/xterm.d.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index f67c30e3..77f0b4ef 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -434,15 +434,15 @@ declare module 'xterm' {
/**
* The element that the decoration is rendered to. This will be undefined
- * until it is rendered for the first time by @{link IDecoration.onRender}.
+ * until it is rendered for the first time by {@link IDecoration.onRender}.
* that.
*/
element: HTMLElement | undefined;
/**
* The options for the overview ruler that can be updated.
- * This will only take effect when @param overviewRulerOptions
- * were provided initially.
+ * This will only take effect when
+ * @param overviewRulerOptions were provided initially.
*/
overviewRulerOptions?: Pick;
}
From 450ee0c6a52ecf22dab31703f09a8ff7a28a36d1 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 15:58:06 -0400
Subject: [PATCH 100/245] tweak api
---
typings/xterm.d.ts | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 77f0b4ef..deaecb79 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -441,17 +441,20 @@ declare module 'xterm' {
/**
* The options for the overview ruler that can be updated.
- * This will only take effect when
- * @param overviewRulerOptions were provided initially.
+ * This will only take effect when {@link IDecorationOptions.overviewRulerOptions}
+ * were provided initially.
*/
overviewRulerOptions?: Pick;
}
-interface IDecorationOverviewRulerOptions {
- color: string;
- position?: 'left' | 'center' | 'right';
-}
+ /**
+ * Overview ruler decoration options
+ */
+ interface IDecorationOverviewRulerOptions {
+ color: string;
+ position?: 'left' | 'center' | 'right';
+ }
/*
* Options that define the presentation of the decoration.
From 26f8fce5387c68d446d145f0e6cb789fd7eedd43 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 16 Mar 2022 17:23:33 -0400
Subject: [PATCH 101/245] update color when select next happens
---
addons/xterm-addon-search/src/SearchAddon.ts | 25 ++++++++++----------
css/xterm.css | 2 +-
2 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 1e54ee3b..67e1f800 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -44,7 +44,7 @@ export class SearchAddon implements ITerminalAddon {
private _result: ISearchResult | undefined;
private _reset: boolean = false;
private _cachedSearchTerm: string | undefined;
- private _cachedResults: ISearchResult[] = [];
+ private _selectedDecoration: IDecoration | undefined;
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -82,11 +82,9 @@ export class SearchAddon implements ITerminalAddon {
}
if (!this._reset && term === this._cachedSearchTerm) {
- // this._resultDecorations.forEach(d => d.dispose());
- // this._resultDecorations = [];
- // for (const decoration of this._cachedResults) {
- // this._showResultDecoration(decoration);
- // }
+ for (const decoration of this._resultDecorations) {
+ decoration.overviewRulerOptions = { color: 'orange' };
+ }
return this.findNext(term, searchOptions);
}
this._reset = false;
@@ -94,18 +92,18 @@ export class SearchAddon implements ITerminalAddon {
// new search, clear out the old decorations
this._resultDecorations.forEach(d => d.dispose());
- this._resultDecorations = [];
+ const results: ISearchResult[] = [];
searchOptions = searchOptions || {};
searchOptions.incremental = false;
let found = this.findNext(term, searchOptions);
- while (found && !this._cachedResults.find(r => r?.col === this._result?.col && r?.row === this._result?.row)) {
+ while (found && !results.find(r => r?.col === this._result?.col && r?.row === this._result?.row)) {
if (this._result) {
- this._cachedResults.push(this._result);
+ results.push(this._result);
}
found = this.findNext(term, searchOptions);
}
- for (const result of this._cachedResults) {
+ for (const result of results) {
if (result) {
const resultDecoration = this._showResultDecoration(result);
if (resultDecoration) {
@@ -113,7 +111,7 @@ export class SearchAddon implements ITerminalAddon {
}
}
}
- if (this._cachedResults.length > 0) {
+ if (results.length > 0) {
this._cachedSearchTerm = term;
}
return true;
@@ -517,6 +515,7 @@ export class SearchAddon implements ITerminalAddon {
*/
private _selectResult(result: ISearchResult | undefined): boolean {
const terminal = this._terminal!;
+ this._selectedDecoration?.dispose();
if (!result) {
terminal.clearSelection();
return false;
@@ -524,7 +523,7 @@ export class SearchAddon implements ITerminalAddon {
terminal.select(result.col, result.row, result.size);
const marker = terminal.registerMarker(undefined, result.row);
if (marker) {
- terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'yellow' } });
+ this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue' } });
}
// If it is not in the viewport then we scroll else it just gets selected
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
@@ -547,7 +546,7 @@ export class SearchAddon implements ITerminalAddon {
return undefined;
}
terminal.options.overviewRulerWidth = 10;
- const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue' } });
+ const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'orange' } });
findResultDecoration?.onRender((e) => {
if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0 && !e.classList.contains('xterm-decoration-overview-ruler')) {
e.classList.add('xterm-find-result-decoration');
diff --git a/css/xterm.css b/css/xterm.css
index 3160a26e..f8f85f9e 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -180,7 +180,7 @@
}
.xterm-find-result-decoration {
- background-color: blue;
+ background-color: grey;
opacity: 60%;
}
.xterm-decoration-overview-ruler {
From 90f64ba6ccd45378cbb91c87af7cd216e3e03029 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 09:52:41 -0400
Subject: [PATCH 102/245] get it to work in the demo
---
addons/xterm-addon-search/src/SearchAddon.ts | 6 +++++-
src/browser/Decorations/BufferDecorationRenderer.ts | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 67e1f800..8027cd73 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -92,6 +92,7 @@ export class SearchAddon implements ITerminalAddon {
// new search, clear out the old decorations
this._resultDecorations.forEach(d => d.dispose());
+ console.log('clearing');
const results: ISearchResult[] = [];
searchOptions = searchOptions || {};
searchOptions.incremental = false;
@@ -102,7 +103,7 @@ export class SearchAddon implements ITerminalAddon {
}
found = this.findNext(term, searchOptions);
}
-
+ console.log(results);
for (const result of results) {
if (result) {
const resultDecoration = this._showResultDecoration(result);
@@ -541,7 +542,10 @@ export class SearchAddon implements ITerminalAddon {
*/
private _showResultDecoration(result: ISearchResult): IDecoration | undefined {
const terminal = this._terminal!;
+ // for demo to work
+ // const marker = terminal.registerMarker(undefined, result.row);
const marker = terminal.registerMarker(undefined, result.row);
+ console.log(result.row, marker?.line);
if (!marker) {
return undefined;
}
diff --git a/src/browser/Decorations/BufferDecorationRenderer.ts b/src/browser/Decorations/BufferDecorationRenderer.ts
index 116c09a5..22dc73e9 100644
--- a/src/browser/Decorations/BufferDecorationRenderer.ts
+++ b/src/browser/Decorations/BufferDecorationRenderer.ts
@@ -97,7 +97,7 @@ export class BufferDecorationRenderer extends Disposable {
private _refreshStyle(decoration: IInternalDecoration, element: HTMLElement): void {
const line = decoration.marker.line - this._bufferService.buffers.active.ydisp;
- if (line < 0 || line > this._bufferService.rows) {
+ if (line < 0 || line >= this._bufferService.rows) {
// outside of viewport
element.style.display = 'none';
} else {
From c2145d5125cb794cd60b4cec6cd4b33cd585479e Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 10:38:44 -0400
Subject: [PATCH 103/245] on dispose, clear the canvas at that position
---
addons/xterm-addon-search/src/SearchAddon.ts | 17 +++++++++--------
.../Decorations/OverviewRulerRenderer.ts | 16 ++++++++++------
2 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 8027cd73..05c6892d 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -62,6 +62,14 @@ export class SearchAddon implements ITerminalAddon {
public dispose(): void { }
+ public clear(): void {
+ this._terminal?.clearSelection();
+ this._resultDecorations.forEach(d => d.dispose());
+ this._selectedDecoration?.dispose();
+ this._resultDecorations = [];
+ this._reset = true;
+ }
+
/**
* Find all instances of the term, selecting the next one with each
* enter. If it doesn't exist, do nothing.
@@ -75,9 +83,7 @@ export class SearchAddon implements ITerminalAddon {
}
if (!term || term.length === 0) {
- this._terminal.clearSelection();
- this._resultDecorations.forEach(d => d.dispose());
- this._resultDecorations = [];
+ this.clear();
return false;
}
@@ -92,7 +98,6 @@ export class SearchAddon implements ITerminalAddon {
// new search, clear out the old decorations
this._resultDecorations.forEach(d => d.dispose());
- console.log('clearing');
const results: ISearchResult[] = [];
searchOptions = searchOptions || {};
searchOptions.incremental = false;
@@ -103,7 +108,6 @@ export class SearchAddon implements ITerminalAddon {
}
found = this.findNext(term, searchOptions);
}
- console.log(results);
for (const result of results) {
if (result) {
const resultDecoration = this._showResultDecoration(result);
@@ -542,10 +546,7 @@ export class SearchAddon implements ITerminalAddon {
*/
private _showResultDecoration(result: ISearchResult): IDecoration | undefined {
const terminal = this._terminal!;
- // for demo to work
- // const marker = terminal.registerMarker(undefined, result.row);
const marker = terminal.registerMarker(undefined, result.row);
- console.log(result.row, marker?.line);
if (!marker) {
return undefined;
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 620e4dbb..824f27ef 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -67,12 +67,7 @@ export class OverviewRulerRenderer extends Disposable {
public override dispose(): void {
for (const decoration of this._decorationElements) {
- this._ctx?.clearRect(
- 0,
- Math.round(this._canvas.height * (decoration[0].marker.line / this._bufferService.buffers.active.lines.length)),
- this._canvas.width,
- window.devicePixelRatio
- );
+ decoration[0].dispose();
}
this._decorationElements.clear();
this._canvas?.remove();
@@ -119,6 +114,15 @@ export class OverviewRulerRenderer extends Disposable {
const element = this._decorationElements.get(decoration);
if (!element) {
this._decorationElements.set(decoration, this._canvas);
+ decoration.onDispose(() => {
+ this._ctx?.clearRect(
+ !decoration!.options!.overviewRulerOptions?.position || decoration!.options!.overviewRulerOptions?.position === 'left' ? 0 : decoration!.options!.overviewRulerOptions?.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
+ Math.round(this._canvas.height * (decoration!.options!.marker.line / this._bufferService.buffers.active.lines.length)),
+ !decoration!.options!.overviewRulerOptions?.position ? this._width : decoration!.options!.overviewRulerOptions?.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
+ // when a position is provided, the element has less width, so increase its height
+ window.devicePixelRatio * (decoration!.options!.overviewRulerOptions?.position ? 6 : 2)
+ );
+ });
}
this._refreshStyle(decoration, updateAnchor);
}
From de56d04ae51bca703f863b3f426d9972ee752288 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 11:08:40 -0400
Subject: [PATCH 104/245] return decoration from show result
---
addons/xterm-addon-search/src/SearchAddon.ts | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 05c6892d..16430896 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -528,7 +528,7 @@ export class SearchAddon implements ITerminalAddon {
terminal.select(result.col, result.row, result.size);
const marker = terminal.registerMarker(undefined, result.row);
if (marker) {
- this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue' } });
+ this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue', position: 'center' } });
}
// If it is not in the viewport then we scroll else it just gets selected
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
@@ -550,10 +550,11 @@ export class SearchAddon implements ITerminalAddon {
if (!marker) {
return undefined;
}
+ // TODO: remove/move?
terminal.options.overviewRulerWidth = 10;
- const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'orange' } });
+ const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'orange', position: 'center' } });
findResultDecoration?.onRender((e) => {
- if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0 && !e.classList.contains('xterm-decoration-overview-ruler')) {
+ if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0) {
e.classList.add('xterm-find-result-decoration');
// decoration's clientWidth = actualCellWidth
e.style.left = `${e.clientWidth * result.col}px`;
From 03bc29e0ed3acca117b7badd6b16eb25cae018f0 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 15:02:57 -0400
Subject: [PATCH 105/245] fix #3691
---
src/browser/Decorations/OverviewRulerRenderer.ts | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index aa91cad9..bbf0995d 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -67,12 +67,7 @@ export class OverviewRulerRenderer extends Disposable {
public override dispose(): void {
for (const decoration of this._decorationElements) {
- this._ctx?.clearRect(
- 0,
- Math.round(this._canvas.height * (decoration[0].marker.line / this._bufferService.buffers.active.lines.length)),
- this._canvas.width,
- window.devicePixelRatio
- );
+ decoration[0].dispose();
}
this._decorationElements.clear();
this._canvas?.remove();
@@ -119,6 +114,15 @@ export class OverviewRulerRenderer extends Disposable {
const element = this._decorationElements.get(decoration);
if (!element) {
this._decorationElements.set(decoration, this._canvas);
+ decoration.onDispose(() => {
+ this._ctx?.clearRect(
+ !decoration!.options!.overviewRulerOptions?.position || decoration!.options!.overviewRulerOptions?.position === 'left' ? 0 : decoration!.options!.overviewRulerOptions?.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
+ Math.round(this._canvas.height * (decoration!.options!.marker.line / this._bufferService.buffers.active.lines.length)),
+ !decoration!.options!.overviewRulerOptions?.position ? this._width : decoration!.options!.overviewRulerOptions?.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
+ // when a position is provided, the element has less width, so increase its height
+ window.devicePixelRatio * (decoration!.options!.overviewRulerOptions?.position ? 6 : 2)
+ );
+ });
}
this._refreshStyle(decoration, updateAnchor);
decoration.onRenderEmitter.fire(this._canvas);
From c70be89bc24f8207e92676374ee7bd8c8db7dea4 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 15:14:35 -0400
Subject: [PATCH 106/245] fix #3690
---
demo/client.ts | 6 +++---
src/browser/Decorations/OverviewRulerRenderer.ts | 7 +++----
test/api/Terminal.api.ts | 8 ++++----
typings/xterm.d.ts | 2 +-
4 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 2ae649e4..3e305aef 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -557,9 +557,9 @@ function addDecoration() {
function addOverviewRuler() {
term.options['overviewRulerWidth'] = 15;
- term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929' }});
- term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234' }});
- term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf' }});
+ term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929', position: 'full' }});
+ term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234', position: 'full' }});
+ term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf', position: 'full' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#ef2929', position: 'left' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' }});
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index aa91cad9..d6fec810 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -94,11 +94,10 @@ export class OverviewRulerRenderer extends Disposable {
this._ctx.lineWidth = 1;
this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
- !decoration.options.overviewRulerOptions.position || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
+ decoration.options.overviewRulerOptions.position === 'full' || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- !decoration.options.overviewRulerOptions.position ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
- // when a position is provided, the element has less width, so increase its height
- window.devicePixelRatio * (decoration.options.overviewRulerOptions.position ? 6 : 2)
+ decoration.options.overviewRulerOptions.position === 'full' ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
+ window.devicePixelRatio * (decoration.options.overviewRulerOptions.position === 'full' ? 2 : 6)
);
}
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index 43f1e97d..b1582be8 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -770,8 +770,8 @@ describe('API Integration Tests', function(): void {
await page.waitForSelector('.xterm-text-layer');
await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker1, overviewRulerOptions: { color: 'red' } })`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker2, overviewRulerOptions: { color: 'blue' } })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker1, overviewRulerOptions: { color: 'red', position: 'full' } })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker2, overviewRulerOptions: { color: 'blue', position: 'full' } })`);
await openTerminal(page);
await pollFor(page, `document.querySelectorAll('.xterm-decoration-overview-ruler').length`, 0);
});
@@ -781,8 +781,8 @@ describe('API Integration Tests', function(): void {
await page.waitForSelector('.xterm-text-layer');
await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker1, overviewRulerOptions: { color: 'red' } })`);
- await page.evaluate(`window.term.registerDecoration({ marker: window.marker2, overviewRulerOptions: { color: 'blue' } })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker1, overviewRulerOptions: { color: 'red', position: 'full' } })`);
+ await page.evaluate(`window.term.registerDecoration({ marker: window.marker2, overviewRulerOptions: { color: 'blue', position: 'full' } })`);
await openTerminal(page);
await pollFor(page, `document.querySelectorAll('.xterm-decoration-overview-ruler').length`, 1);
});
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 24ba940e..1278309d 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -479,7 +479,7 @@ declare module 'xterm' {
* @param color The color of the decoration.
* @param position The position of the decoration.
*/
- readonly overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right'}
+ readonly overviewRulerOptions?: { color: string; position: 'left' | 'center' | 'right' | 'full'}
}
/**
From 731432a85201d28ed1f9c95c5e26ca28e28fdbd8 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 15:16:28 -0400
Subject: [PATCH 107/245] set fallback
---
src/common/services/DecorationService.ts | 3 +++
typings/xterm.d.ts | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts
index 911fd369..6c750236 100644
--- a/src/common/services/DecorationService.ts
+++ b/src/common/services/DecorationService.ts
@@ -65,6 +65,9 @@ class Decoration extends Disposable implements IInternalDecoration {
) {
super();
this.marker = options.marker;
+ if (this.options.overviewRulerOptions && !this.options.overviewRulerOptions.position) {
+ this.options.overviewRulerOptions.position = 'full';
+ }
}
public override dispose(): void {
this._onDispose.fire();
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 1278309d..3ddef4d3 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -479,7 +479,7 @@ declare module 'xterm' {
* @param color The color of the decoration.
* @param position The position of the decoration.
*/
- readonly overviewRulerOptions?: { color: string; position: 'left' | 'center' | 'right' | 'full'}
+ readonly overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right' | 'full'}
}
/**
From 5a6a275a53f127077859c19555e86bff425cc543 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 15:18:21 -0400
Subject: [PATCH 108/245] use interface
---
demo/client.ts | 6 +++---
typings/xterm.d.ts | 4 +++-
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 3e305aef..2ae649e4 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -557,9 +557,9 @@ function addDecoration() {
function addOverviewRuler() {
term.options['overviewRulerWidth'] = 15;
- term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929', position: 'full' }});
- term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234', position: 'full' }});
- term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf', position: 'full' }});
+ term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929' }});
+ term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234' }});
+ term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#ef2929', position: 'left' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' }});
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 3ddef4d3..87b30288 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -7,6 +7,8 @@
* to be stable and consumed by external programs.
*/
+import { IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model';
+
///
declare module 'xterm' {
@@ -479,7 +481,7 @@ declare module 'xterm' {
* @param color The color of the decoration.
* @param position The position of the decoration.
*/
- readonly overviewRulerOptions?: { color: string; position?: 'left' | 'center' | 'right' | 'full'}
+ readonly overviewRulerOptions?: IModelDecorationOverviewRulerOptions
}
/**
From ba4252bc0bb4743b3d5ad7390d38d4080ba90b7d Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 15:21:29 -0400
Subject: [PATCH 109/245] fix merge result
---
typings/xterm.d.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index be9ff75d..410cef46 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -446,8 +446,7 @@ declare module 'xterm' {
* This will only take effect when {@link IDecorationOptions.overviewRulerOptions}
* were provided initially.
*/
- overviewRulerOptions?: Pick<
- Options, 'color'>;
+ overviewRulerOptions?: Pick;
}
From bb2193676b483eec739eaef84c8c524841937eb4 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 16:20:47 -0400
Subject: [PATCH 110/245] fix problems
---
src/browser/Decorations/OverviewRulerRenderer.ts | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index d497b14b..24a5173b 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -87,7 +87,7 @@ export class OverviewRulerRenderer extends Disposable {
return;
}
this._ctx.lineWidth = 1;
- this._ctx.fillStyle = decoration.overviewRulerOptions?.color || decoration.options.overviewRulerOptions.color;
+ this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
decoration.options.overviewRulerOptions.position === 'full' || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
@@ -113,15 +113,7 @@ export class OverviewRulerRenderer extends Disposable {
const element = this._decorationElements.get(decoration);
if (!element) {
this._decorationElements.set(decoration, this._canvas);
- decoration.onDispose(() => {
- this._ctx?.clearRect(
- !decoration!.options!.overviewRulerOptions?.position || decoration!.options!.overviewRulerOptions?.position === 'left' ? 0 : decoration!.options!.overviewRulerOptions?.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
- Math.round(this._canvas.height * (decoration!.options!.marker.line / this._bufferService.buffers.active.lines.length)),
- !decoration!.options!.overviewRulerOptions?.position ? this._width : decoration!.options!.overviewRulerOptions?.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
- // when a position is provided, the element has less width, so increase its height
- window.devicePixelRatio * (decoration!.options!.overviewRulerOptions?.position ? 6 : 2)
- );
- });
+ decoration[0].onDispose(() => (this._queueRefresh()));
}
this._refreshStyle(decoration, updateAnchor);
}
From 8f1dcecb5088bdaa8e0e5951e6d25d6d1c1ad1cf Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 16:23:37 -0400
Subject: [PATCH 111/245] dispose of decoration
---
src/browser/Decorations/OverviewRulerRenderer.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 24a5173b..1998d18a 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -113,7 +113,7 @@ export class OverviewRulerRenderer extends Disposable {
const element = this._decorationElements.get(decoration);
if (!element) {
this._decorationElements.set(decoration, this._canvas);
- decoration[0].onDispose(() => (this._queueRefresh()));
+ decoration.onDispose(() => this._queueRefresh());
}
this._refreshStyle(decoration, updateAnchor);
}
From a05cdbd48e15987ebe105c9805c9bd3af1ddd3f3 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 16:30:55 -0400
Subject: [PATCH 112/245] Remove accidental import
---
typings/xterm.d.ts | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 410cef46..4f06d5f5 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -7,8 +7,6 @@
* to be stable and consumed by external programs.
*/
-import { IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model';
-
///
declare module 'xterm' {
@@ -432,14 +430,14 @@ declare module 'xterm' {
* is rendered, returns the dom element
* associated with the decoration.
*/
- readonly onRender: IEvent;
+ readonly onRender: IEvent;
/**
* The element that the decoration is rendered to. This will be undefined
* until it is rendered for the first time by {@link IDecoration.onRender}.
* that.
*/
- element: HTMLElement | undefined;
+ element: HTMLLIElement | undefined;
/**
* The options for the overview ruler that can be updated.
@@ -679,7 +677,7 @@ declare module 'xterm' {
/**
* The element containing the terminal.
*/
- readonly element: HTMLElement | undefined;
+ readonly element: HTMLLIElement | undefined;
/**
* The textarea that accepts input for the terminal.
From 02e2727fb527265a304f87a13f9a442702270774 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Thu, 17 Mar 2022 16:36:34 -0400
Subject: [PATCH 113/245] fix typos
---
typings/xterm.d.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 4f06d5f5..d1eb3890 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -430,14 +430,14 @@ declare module 'xterm' {
* is rendered, returns the dom element
* associated with the decoration.
*/
- readonly onRender: IEvent;
+ readonly onRender: IEvent;
/**
* The element that the decoration is rendered to. This will be undefined
* until it is rendered for the first time by {@link IDecoration.onRender}.
* that.
*/
- element: HTMLLIElement | undefined;
+ element: HTMLElement | undefined;
/**
* The options for the overview ruler that can be updated.
@@ -677,7 +677,7 @@ declare module 'xterm' {
/**
* The element containing the terminal.
*/
- readonly element: HTMLLIElement | undefined;
+ readonly element: HTMLElement | undefined;
/**
* The textarea that accepts input for the terminal.
From 9bc5090bac5bb4209b14ebba6a2099ecd275cc2a Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 11:47:26 -0400
Subject: [PATCH 114/245] use a map
---
addons/xterm-addon-search/src/SearchAddon.ts | 42 +++++++++++---------
1 file changed, 24 insertions(+), 18 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 16430896..9a3bfb4a 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -40,9 +40,9 @@ const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
export class SearchAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
- private _resultDecorations: IDecoration[] = [];
+ private _resultDecorations: Map = new Map();
private _result: ISearchResult | undefined;
- private _reset: boolean = false;
+ private _dataChanged: boolean = false;
private _cachedSearchTerm: string | undefined;
private _selectedDecoration: IDecoration | undefined;
/**
@@ -57,17 +57,18 @@ export class SearchAddon implements ITerminalAddon {
public activate(terminal: Terminal): void {
this._terminal = terminal;
- this._terminal.onData(() => this._reset = true);
+ // TODO: should this be using on buffer changed instead?
+ this._terminal.onData(() => this._dataChanged = true);
}
public dispose(): void { }
public clear(): void {
this._terminal?.clearSelection();
- this._resultDecorations.forEach(d => d.dispose());
- this._selectedDecoration?.dispose();
- this._resultDecorations = [];
- this._reset = true;
+ this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
+ this._resultDecorations.clear();
+ this._cachedSearchTerm = undefined;
+ this._dataChanged = true;
}
/**
@@ -86,18 +87,18 @@ export class SearchAddon implements ITerminalAddon {
this.clear();
return false;
}
-
- if (!this._reset && term === this._cachedSearchTerm) {
- for (const decoration of this._resultDecorations) {
- decoration.overviewRulerOptions = { color: 'orange' };
- }
+ if (!this._dataChanged && term === this._cachedSearchTerm) {
return this.findNext(term, searchOptions);
}
- this._reset = false;
-
+ if (this._dataChanged && term === this._cachedSearchTerm) {
+ // TODO:
+ // add to decorations instead of starting from scratch
+ // by looking at resultDecoration.keys()[resultDecoration.length]
+ // to ybase
+ }
// new search, clear out the old decorations
- this._resultDecorations.forEach(d => d.dispose());
+ this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
const results: ISearchResult[] = [];
searchOptions = searchOptions || {};
searchOptions.incremental = false;
@@ -112,10 +113,15 @@ export class SearchAddon implements ITerminalAddon {
if (result) {
const resultDecoration = this._showResultDecoration(result);
if (resultDecoration) {
- this._resultDecorations.push(resultDecoration);
+ const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
+ decorationsForLine.push(resultDecoration);
+ this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
}
}
}
+ if (this._dataChanged) {
+ this._dataChanged = false;
+ }
if (results.length > 0) {
this._cachedSearchTerm = term;
}
@@ -528,7 +534,7 @@ export class SearchAddon implements ITerminalAddon {
terminal.select(result.col, result.row, result.size);
const marker = terminal.registerMarker(undefined, result.row);
if (marker) {
- this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue', position: 'center' } });
+ this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue' } });
}
// If it is not in the viewport then we scroll else it just gets selected
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
@@ -552,7 +558,7 @@ export class SearchAddon implements ITerminalAddon {
}
// TODO: remove/move?
terminal.options.overviewRulerWidth = 10;
- const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'orange', position: 'center' } });
+ const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color: 'orange', position: 'center' } });
findResultDecoration?.onRender((e) => {
if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0) {
e.classList.add('xterm-find-result-decoration');
From a1468d0f589f5ca3e2b99f8c89de05af43bca25f Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 13:12:32 -0400
Subject: [PATCH 115/245] use start row and col
---
addons/xterm-addon-search/src/SearchAddon.ts | 63 +++++++++++---------
1 file changed, 36 insertions(+), 27 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 9a3bfb4a..b28e2bdd 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -10,6 +10,8 @@ export interface ISearchOptions {
wholeWord?: boolean;
caseSensitive?: boolean;
incremental?: boolean;
+ startRow?: number;
+ startCol?: number;
}
export interface ISearchPosition {
@@ -40,11 +42,12 @@ const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
export class SearchAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
- private _resultDecorations: Map = new Map();
private _result: ISearchResult | undefined;
private _dataChanged: boolean = false;
private _cachedSearchTerm: string | undefined;
private _selectedDecoration: IDecoration | undefined;
+ private _resultDecorations: Map = new Map();
+ private _searchResults: Map = new Map();
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -65,6 +68,7 @@ export class SearchAddon implements ITerminalAddon {
public clear(): void {
this._terminal?.clearSelection();
+ this._searchResults.clear();
this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
this._resultDecorations.clear();
this._cachedSearchTerm = undefined;
@@ -87,42 +91,47 @@ export class SearchAddon implements ITerminalAddon {
this.clear();
return false;
}
- if (!this._dataChanged && term === this._cachedSearchTerm) {
- return this.findNext(term, searchOptions);
- }
- if (this._dataChanged && term === this._cachedSearchTerm) {
- // TODO:
- // add to decorations instead of starting from scratch
- // by looking at resultDecoration.keys()[resultDecoration.length]
- // to ybase
+
+ if (term === this._cachedSearchTerm) {
+ if (!this._dataChanged) {
+ return this.findNext(term, searchOptions);
+ }
+ // set start row to avoid redoing work
+ searchOptions = searchOptions || {};
+ const key = Array.from(this._searchResults.keys()).pop()?.split('-');
+ if (key?.length === 2) {
+ searchOptions.startRow = Number.parseInt(key[0]) + 1;
+ searchOptions.startCol = Number.parseInt(key[1]);
+ console.log(searchOptions.startRow, searchOptions.startCol);
+ }
+ } else {
+ // new search, clear out the old decorations
+ this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
+ this._resultDecorations.clear();
+ this._searchResults.clear();
+ searchOptions = searchOptions || {};
}
- // new search, clear out the old decorations
- this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
- const results: ISearchResult[] = [];
- searchOptions = searchOptions || {};
searchOptions.incremental = false;
let found = this.findNext(term, searchOptions);
- while (found && !results.find(r => r?.col === this._result?.col && r?.row === this._result?.row)) {
+ while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
if (this._result) {
- results.push(this._result);
+ this._searchResults.set(`${this._result.row}-${this._result.col}`, this._result);
}
found = this.findNext(term, searchOptions);
}
- for (const result of results) {
- if (result) {
- const resultDecoration = this._showResultDecoration(result);
- if (resultDecoration) {
- const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
- decorationsForLine.push(resultDecoration);
- this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
- }
+ this._searchResults.forEach(result => {
+ const resultDecoration = this._showResultDecoration(result);
+ if (resultDecoration) {
+ const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
+ decorationsForLine.push(resultDecoration);
+ this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
}
- }
+ });
if (this._dataChanged) {
this._dataChanged = false;
}
- if (results.length > 0) {
+ if (this._searchResults.size > 0) {
this._cachedSearchTerm = term;
}
return true;
@@ -147,8 +156,8 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
- let startCol = 0;
- let startRow = 0;
+ let startCol = searchOptions?.startCol || 0;
+ let startRow = searchOptions?.startRow || 0;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
const incremental = searchOptions ? searchOptions.incremental : false;
From 6c4f8b42dd83f8e5ab279dc6bfa7bf2eaba013fb Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 13:29:59 -0400
Subject: [PATCH 116/245] append to results
---
addons/xterm-addon-search/src/SearchAddon.ts | 9 +++------
typings/xterm.d.ts | 2 --
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index b28e2bdd..38792414 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -91,25 +91,22 @@ export class SearchAddon implements ITerminalAddon {
this.clear();
return false;
}
-
+ searchOptions = searchOptions || {};
if (term === this._cachedSearchTerm) {
if (!this._dataChanged) {
return this.findNext(term, searchOptions);
}
- // set start row to avoid redoing work
- searchOptions = searchOptions || {};
+ // set start row and col to avoid redoing work
const key = Array.from(this._searchResults.keys()).pop()?.split('-');
if (key?.length === 2) {
searchOptions.startRow = Number.parseInt(key[0]) + 1;
searchOptions.startCol = Number.parseInt(key[1]);
- console.log(searchOptions.startRow, searchOptions.startCol);
}
} else {
// new search, clear out the old decorations
this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
this._resultDecorations.clear();
this._searchResults.clear();
- searchOptions = searchOptions || {};
}
searchOptions.incremental = false;
@@ -166,7 +163,7 @@ export class SearchAddon implements ITerminalAddon {
currentSelection = this._terminal.getSelectionPosition()!;
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
- } else {
+ } else if (!startRow) {
startRow = this._terminal.buffer.active.cursorY;
startCol = this._terminal.buffer.active.cursorX;
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index d0149c19..363cede3 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -7,8 +7,6 @@
* to be stable and consumed by external programs.
*/
-import { IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model';
-
///
declare module 'xterm' {
From f43afde8df4f4e2b40ba6aff9cd3c1eaa1decf79 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Fri, 18 Mar 2022 10:42:26 -0700
Subject: [PATCH 117/245] Respect device pixel ratio in overview ruler
---
.../Decorations/OverviewRulerRenderer.ts | 45 ++++++++++++++-----
1 file changed, 33 insertions(+), 12 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 1998d18a..af894ebf 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -18,6 +18,13 @@ const enum SizeIndex {
INNER_SIZE = 1
}
+const positionHeights = {
+ full: 2,
+ left: 6,
+ center: 6,
+ right: 6
+};
+
export class OverviewRulerRenderer extends Disposable {
private readonly _canvas: HTMLCanvasElement;
private readonly _ctx: CanvasRenderingContext2D;
@@ -38,6 +45,7 @@ export class OverviewRulerRenderer extends Disposable {
super();
this._canvas = document.createElement('canvas');
this._canvas.classList.add('xterm-decoration-overview-ruler');
+ this._refreshCanvasDimensions();
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
const ctx = this._canvas.getContext('2d');
if (!ctx) {
@@ -56,13 +64,14 @@ export class OverviewRulerRenderer extends Disposable {
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
this.register(this._optionsService.onOptionChange(o => {
if (o === 'overviewRulerWidth') {
- renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._width / 3);
- renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._width / 3);
+ renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
+ renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
this._queueRefresh();
}
}));
- renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._width / 3);
- renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._width / 3);
+ console.log('width', this._canvas.width);
+ renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
+ renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
}
public override dispose(): void {
@@ -89,19 +98,31 @@ export class OverviewRulerRenderer extends Disposable {
this._ctx.lineWidth = 1;
this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
- decoration.options.overviewRulerOptions.position === 'full' || decoration.options.overviewRulerOptions.position === 'left' ? 0 : decoration.options.overviewRulerOptions.position === 'right' ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]: renderSizes[SizeIndex.OUTER_SIZE],
- Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- decoration.options.overviewRulerOptions.position === 'full' ? this._width : decoration.options.overviewRulerOptions.position === 'center' ? renderSizes[SizeIndex.INNER_SIZE] : renderSizes[SizeIndex.OUTER_SIZE],
- window.devicePixelRatio * (decoration.options.overviewRulerOptions.position === 'full' ? 2 : 6)
+ /* x */ decoration.options.overviewRulerOptions.position === 'full' || decoration.options.overviewRulerOptions.position === 'left'
+ ? 0
+ : decoration.options.overviewRulerOptions.position === 'right'
+ ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]
+ : renderSizes[SizeIndex.OUTER_SIZE],
+ /* y */ Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
+ /* w */ decoration.options.overviewRulerOptions.position === 'full'
+ ? this._canvas.width
+ : decoration.options.overviewRulerOptions.position === 'center'
+ ? renderSizes[SizeIndex.INNER_SIZE]
+ : renderSizes[SizeIndex.OUTER_SIZE],
+ /* h */ window.devicePixelRatio * positionHeights[decoration.options.overviewRulerOptions.position!]
);
}
+ private _refreshCanvasDimensions(): void {
+ this._canvas.style.width = `${this._width}px`;
+ this._canvas.style.height = `${this._screenElement.clientHeight}px`;
+ this._canvas.width = Math.floor(this._width * window.devicePixelRatio);
+ this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ }
+
private _refreshDecorations(updateCanvasDimensions?: boolean, updateAnchor?: boolean): void {
if (updateCanvasDimensions) {
- this._canvas.style.width = `${this._width}px`;
- this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor((this._width)* window.devicePixelRatio);
- this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ this._refreshCanvasDimensions();
}
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
for (const decoration of this._decorationService.decorations) {
From 4a661da57bd61417f49c030490d417bad30c7e11 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Fri, 18 Mar 2022 10:50:28 -0700
Subject: [PATCH 118/245] Use object properties for fast access to
precalculated draw args
---
.../Decorations/OverviewRulerRenderer.ts | 69 +++++++++++++------
1 file changed, 49 insertions(+), 20 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index af894ebf..3a1c127a 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -18,11 +18,25 @@ const enum SizeIndex {
INNER_SIZE = 1
}
-const positionHeights = {
- full: 2,
- left: 6,
- center: 6,
- right: 6
+const drawHeight = {
+ full: 0,
+ left: 0,
+ center: 0,
+ right: 0
+};
+
+const drawWidth = {
+ full: 0,
+ left: 0,
+ center: 0,
+ right: 0
+};
+
+const drawX = {
+ full: 0,
+ left: 0,
+ center: 0,
+ right: 0
};
export class OverviewRulerRenderer extends Disposable {
@@ -64,14 +78,16 @@ export class OverviewRulerRenderer extends Disposable {
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
this.register(this._optionsService.onOptionChange(o => {
if (o === 'overviewRulerWidth') {
- renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
- renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
+ // renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
+ // renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
+ this._refreshDrawConstants();
this._queueRefresh();
}
}));
console.log('width', this._canvas.width);
- renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
- renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
+ this._refreshDrawConstants();
+ // renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
+ // renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
}
public override dispose(): void {
@@ -83,6 +99,26 @@ export class OverviewRulerRenderer extends Disposable {
super.dispose();
}
+ private _refreshDrawConstants(): void {
+ // width
+ const outerWidth = Math.floor(this._canvas.width / 3);
+ const innerWidth = Math.ceil(this._canvas.width / 3);
+ drawWidth.full = this._canvas.width;
+ drawWidth.left = outerWidth;
+ drawWidth.center = innerWidth;
+ drawWidth.right = outerWidth;
+ // height
+ drawHeight.full = Math.round(2 * window.devicePixelRatio);
+ drawHeight.left = Math.round(6 * window.devicePixelRatio);
+ drawHeight.center = Math.round(6 * window.devicePixelRatio);
+ drawHeight.right = Math.round(6 * window.devicePixelRatio);
+ // x
+ drawX.full = 0;
+ drawX.left = 0;
+ drawX.center = drawWidth.left;
+ drawX.right = drawWidth.left + drawWidth.center;
+ }
+
private _refreshStyle(decoration: IInternalDecoration, updateAnchor?: boolean): void {
if (updateAnchor) {
if (decoration.options.anchor === 'right') {
@@ -98,18 +134,10 @@ export class OverviewRulerRenderer extends Disposable {
this._ctx.lineWidth = 1;
this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
- /* x */ decoration.options.overviewRulerOptions.position === 'full' || decoration.options.overviewRulerOptions.position === 'left'
- ? 0
- : decoration.options.overviewRulerOptions.position === 'right'
- ? renderSizes[SizeIndex.OUTER_SIZE] + renderSizes[SizeIndex.INNER_SIZE]
- : renderSizes[SizeIndex.OUTER_SIZE],
+ /* x */ drawX[decoration.options.overviewRulerOptions.position!],
/* y */ Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
- /* w */ decoration.options.overviewRulerOptions.position === 'full'
- ? this._canvas.width
- : decoration.options.overviewRulerOptions.position === 'center'
- ? renderSizes[SizeIndex.INNER_SIZE]
- : renderSizes[SizeIndex.OUTER_SIZE],
- /* h */ window.devicePixelRatio * positionHeights[decoration.options.overviewRulerOptions.position!]
+ /* w */ drawWidth[decoration.options.overviewRulerOptions.position!],
+ /* h */ drawHeight[decoration.options.overviewRulerOptions.position!]
);
}
@@ -118,6 +146,7 @@ export class OverviewRulerRenderer extends Disposable {
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
this._canvas.width = Math.floor(this._width * window.devicePixelRatio);
this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ this._refreshDrawConstants();
}
private _refreshDecorations(updateCanvasDimensions?: boolean, updateAnchor?: boolean): void {
From 816220ebd2195c72240a3683e6f2a1e079b57824 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Fri, 18 Mar 2022 10:51:02 -0700
Subject: [PATCH 119/245] Round dpr for more crisp decorations
---
src/browser/Decorations/OverviewRulerRenderer.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 3a1c127a..c38b5377 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -144,8 +144,8 @@ export class OverviewRulerRenderer extends Disposable {
private _refreshCanvasDimensions(): void {
this._canvas.style.width = `${this._width}px`;
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
- this._canvas.width = Math.floor(this._width * window.devicePixelRatio);
- this._canvas.height = Math.floor(this._screenElement.clientHeight * window.devicePixelRatio);
+ this._canvas.width = Math.round(this._width * window.devicePixelRatio);
+ this._canvas.height = Math.round(this._screenElement.clientHeight * window.devicePixelRatio);
this._refreshDrawConstants();
}
From 77622f8919fe5be92abe767784f08d133ebbe1d2 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Fri, 18 Mar 2022 10:54:11 -0700
Subject: [PATCH 120/245] Align full with other decorations
Fixes #3692
---
demo/client.ts | 2 ++
src/browser/Decorations/OverviewRulerRenderer.ts | 5 ++++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/demo/client.ts b/demo/client.ts
index 67e27759..2b622372 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -559,5 +559,7 @@ function addOverviewRuler() {
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#ef2929', position: 'left' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' }});
+ term.registerDecoration({marker: term.addMarker(10), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
+ term.registerDecoration({marker: term.addMarker(10), overviewRulerOptions: { color: '#ffffff80', position: 'full' }});
}
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index c38b5377..0344fe5f 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -135,7 +135,10 @@ export class OverviewRulerRenderer extends Disposable {
this._ctx.fillStyle = decoration.options.overviewRulerOptions.color;
this._ctx.fillRect(
/* x */ drawX[decoration.options.overviewRulerOptions.position!],
- /* y */ Math.round(this._canvas.height * (decoration.options.marker.line / this._bufferService.buffers.active.lines.length)),
+ /* y */ Math.round(
+ (this._canvas.height - 1) * // -1 to ensure at least 2px are allowed for decoration on last line
+ (decoration.options.marker.line / this._bufferService.buffers.active.lines.length) - drawHeight[decoration.options.overviewRulerOptions.position!] / 2
+ ),
/* w */ drawWidth[decoration.options.overviewRulerOptions.position!],
/* h */ drawHeight[decoration.options.overviewRulerOptions.position!]
);
From b5f208294d45f2ffee02181437360432be4e940d Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Fri, 18 Mar 2022 10:56:49 -0700
Subject: [PATCH 121/245] Clean up
---
.../Decorations/OverviewRulerRenderer.ts | 19 ++-----------------
1 file changed, 2 insertions(+), 17 deletions(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 0344fe5f..87f10181 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -8,30 +8,20 @@ import { IRenderService } from 'browser/services/Services';
import { Disposable } from 'common/Lifecycle';
import { IBufferService, IDecorationService, IInternalDecoration, IOptionsService } from 'common/services/Services';
-// This is used to reduce memory usage
-// when refreshStyle is called
-// by storing and updating
-// the sizes of the decorations to be drawn
-const renderSizes = new Uint16Array(3);
-const enum SizeIndex {
- OUTER_SIZE = 0,
- INNER_SIZE = 1
-}
-
+// Helper objects to avoid excessive calculation and garbage collection during rendering. These are
+// static values for each render and can be accessed using the decoration position as the key.
const drawHeight = {
full: 0,
left: 0,
center: 0,
right: 0
};
-
const drawWidth = {
full: 0,
left: 0,
center: 0,
right: 0
};
-
const drawX = {
full: 0,
left: 0,
@@ -78,16 +68,11 @@ export class OverviewRulerRenderer extends Disposable {
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
this.register(this._optionsService.onOptionChange(o => {
if (o === 'overviewRulerWidth') {
- // renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
- // renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
this._refreshDrawConstants();
this._queueRefresh();
}
}));
- console.log('width', this._canvas.width);
this._refreshDrawConstants();
- // renderSizes[SizeIndex.OUTER_SIZE] = Math.floor(this._canvas.width / 3);
- // renderSizes[SizeIndex.INNER_SIZE] = Math.ceil(this._canvas.width / 3);
}
public override dispose(): void {
From 39d1546753e18615a20a41f50b9e06434db33f29 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Fri, 18 Mar 2022 10:59:10 -0700
Subject: [PATCH 122/245] Draw full decorations on top
Fixes #3696
---
src/browser/Decorations/OverviewRulerRenderer.ts | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/browser/Decorations/OverviewRulerRenderer.ts b/src/browser/Decorations/OverviewRulerRenderer.ts
index 87f10181..aa09db56 100644
--- a/src/browser/Decorations/OverviewRulerRenderer.ts
+++ b/src/browser/Decorations/OverviewRulerRenderer.ts
@@ -143,7 +143,14 @@ export class OverviewRulerRenderer extends Disposable {
}
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
for (const decoration of this._decorationService.decorations) {
- this._renderDecoration(decoration, updateAnchor);
+ if (decoration.options.overviewRulerOptions!.position !== 'full') {
+ this._renderDecoration(decoration, updateAnchor);
+ }
+ }
+ for (const decoration of this._decorationService.decorations) {
+ if (decoration.options.overviewRulerOptions!.position === 'full') {
+ this._renderDecoration(decoration, updateAnchor);
+ }
}
}
From 47ec31361c547d952781afc0cbb591247a31ed9a Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 14:14:02 -0400
Subject: [PATCH 123/245] support colors
---
addons/xterm-addon-search/src/SearchAddon.ts | 43 ++++++++++++--------
demo/client.ts | 5 ++-
2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 38792414..00e6830f 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -12,6 +12,8 @@ export interface ISearchOptions {
incremental?: boolean;
startRow?: number;
startCol?: number;
+ overviewRulerResultDecorationColor?: string;
+ overviewRulerSelectionDecorationColor?: string;
}
export interface ISearchPosition {
@@ -117,14 +119,17 @@ export class SearchAddon implements ITerminalAddon {
}
found = this.findNext(term, searchOptions);
}
- this._searchResults.forEach(result => {
- const resultDecoration = this._showResultDecoration(result);
- if (resultDecoration) {
- const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
- decorationsForLine.push(resultDecoration);
- this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
- }
- });
+ if (searchOptions.overviewRulerResultDecorationColor && searchOptions.overviewRulerSelectionDecorationColor) {
+ this._searchResults.forEach(result => {
+ const resultDecoration = this._showResultDecoration(result, searchOptions!.overviewRulerResultDecorationColor!);
+ if (resultDecoration) {
+ const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
+ decorationsForLine.push(resultDecoration);
+ this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
+ }
+ });
+ }
+
if (this._dataChanged) {
this._dataChanged = false;
}
@@ -211,7 +216,7 @@ export class SearchAddon implements ITerminalAddon {
}
// Set selection and scroll if a result was found
- return this._selectResult(this._result);
+ return this._selectResult(this._result, searchOptions?.overviewRulerSelectionDecorationColor);
}
/**
@@ -292,7 +297,7 @@ export class SearchAddon implements ITerminalAddon {
if (!result && currentSelection) return true;
// Set selection and scroll if a result was found
- return this._selectResult(result);
+ return this._selectResult(result, searchOptions?.overviewRulerSelectionDecorationColor);
}
@@ -530,7 +535,7 @@ export class SearchAddon implements ITerminalAddon {
* @param result The result to select.
* @return Whethera result was selected.
*/
- private _selectResult(result: ISearchResult | undefined): boolean {
+ private _selectResult(result: ISearchResult | undefined, color?: string): boolean {
const terminal = this._terminal!;
this._selectedDecoration?.dispose();
if (!result) {
@@ -538,10 +543,13 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
terminal.select(result.col, result.row, result.size);
- const marker = terminal.registerMarker(undefined, result.row);
- if (marker) {
- this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: 'blue' } });
+ if (color) {
+ const marker = terminal.registerMarker(undefined, result.row);
+ if (marker) {
+ this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color } });
+ }
}
+
// If it is not in the viewport then we scroll else it just gets selected
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
let scroll = result.row - terminal.buffer.active.viewportY;
@@ -556,15 +564,14 @@ export class SearchAddon implements ITerminalAddon {
* and @returns the decoration or undefined if
* the marker has already been disposed of
*/
- private _showResultDecoration(result: ISearchResult): IDecoration | undefined {
+ private _showResultDecoration(result: ISearchResult, color: string): IDecoration | undefined {
const terminal = this._terminal!;
const marker = terminal.registerMarker(undefined, result.row);
if (!marker) {
return undefined;
}
- // TODO: remove/move?
- terminal.options.overviewRulerWidth = 10;
- const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color: 'orange', position: 'center' } });
+
+ const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color, position: 'center' } });
findResultDecoration?.onRender((e) => {
if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0) {
e.classList.add('xterm-find-result-decoration');
diff --git a/demo/client.ts b/demo/client.ts
index 692ad8e5..404435c4 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -108,7 +108,9 @@ function getSearchOptions(e: KeyboardEvent): ISearchOptions {
regex: (document.getElementById('regex') as HTMLInputElement).checked,
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked,
- incremental: e.key !== `Enter`
+ incremental: e.key !== `Enter`,
+ overviewRulerResultDecorationColor: '#555753',
+ overviewRulerSelectionDecorationColor: '#ef2929'
};
}
@@ -202,6 +204,7 @@ function createTerminal(): void {
addDomListener(paddingElement, 'change', setPadding);
addDomListener(actionElements.find, 'keyup', (e) => {
+ term.options.overviewRulerWidth = 10;
addons.search.instance.find(actionElements.find.value, getSearchOptions(e));
});
From 01fd14867327ba241de363a1beff956eb7a1e9df Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 14:46:49 -0400
Subject: [PATCH 124/245] clean up
---
addons/xterm-addon-search/src/SearchAddon.ts | 6 +++---
css/xterm.css | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 00e6830f..32441f1c 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -62,7 +62,6 @@ export class SearchAddon implements ITerminalAddon {
public activate(terminal: Terminal): void {
this._terminal = terminal;
- // TODO: should this be using on buffer changed instead?
this._terminal.onData(() => this._dataChanged = true);
}
@@ -578,8 +577,9 @@ export class SearchAddon implements ITerminalAddon {
// decoration's clientWidth = actualCellWidth
e.style.left = `${e.clientWidth * result.col}px`;
e.style.width = `${e.clientWidth * result.term.length}px`;
- }
- });
+ e.style.backgroundColor = color;
+ e.style.color = color;
+ }});
return findResultDecoration;
}
}
diff --git a/css/xterm.css b/css/xterm.css
index f8f85f9e..aa58915c 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -180,9 +180,9 @@
}
.xterm-find-result-decoration {
- background-color: grey;
opacity: 60%;
}
+
.xterm-decoration-overview-ruler {
z-index: 7;
position: absolute;
From 8e68c67a0c57d2e0a12cbb5078c8095e1b353689 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 15:07:33 -0400
Subject: [PATCH 125/245] applyStyles
---
addons/xterm-addon-search/src/SearchAddon.ts | 25 +++++++++++++-------
1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 32441f1c..8a8acf06 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -546,6 +546,7 @@ export class SearchAddon implements ITerminalAddon {
const marker = terminal.registerMarker(undefined, result.row);
if (marker) {
this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color } });
+ this._selectedDecoration?.onRender((e) => this._applyStyles(e, color, result));
}
}
@@ -558,6 +559,20 @@ export class SearchAddon implements ITerminalAddon {
return true;
}
+ private _applyStyles(element: HTMLElement, color: string, result: ISearchResult): void {
+ if (element.clientWidth <= 0) {
+ return;
+ }
+ if (!element.classList.contains('xterm-find-result-decoration')) {
+ element.classList.add('xterm-find-result-decoration');
+ // decoration's clientWidth = actualCellWidth
+ element.style.left = `${element.clientWidth * result.col}px`;
+ element.style.width = `${element.clientWidth * result.term.length}px`;
+ element.style.backgroundColor = color;
+ element.style.color = color;
+ }
+ }
+
/**
* Registers a decoration for the @param result
* and @returns the decoration or undefined if
@@ -571,15 +586,7 @@ export class SearchAddon implements ITerminalAddon {
}
const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color, position: 'center' } });
- findResultDecoration?.onRender((e) => {
- if (!e.classList.contains('xterm-find-result-decoration') && result.term.length && e.clientWidth > 0) {
- e.classList.add('xterm-find-result-decoration');
- // decoration's clientWidth = actualCellWidth
- e.style.left = `${e.clientWidth * result.col}px`;
- e.style.width = `${e.clientWidth * result.term.length}px`;
- e.style.backgroundColor = color;
- e.style.color = color;
- }});
+ findResultDecoration?.onRender((e) => this._applyStyles(e, color, result));
return findResultDecoration;
}
}
From 66dcd1694c915bcf56e08b354d0037407b0e4194 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 15:13:49 -0400
Subject: [PATCH 126/245] clean up jsdoc
---
addons/xterm-addon-search/src/SearchAddon.ts | 21 +++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 8a8acf06..d6ec1bb7 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -120,7 +120,7 @@ export class SearchAddon implements ITerminalAddon {
}
if (searchOptions.overviewRulerResultDecorationColor && searchOptions.overviewRulerSelectionDecorationColor) {
this._searchResults.forEach(result => {
- const resultDecoration = this._showResultDecoration(result, searchOptions!.overviewRulerResultDecorationColor!);
+ const resultDecoration = this._createResultDecoration(result, searchOptions!.overviewRulerResultDecorationColor!);
if (resultDecoration) {
const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
decorationsForLine.push(resultDecoration);
@@ -532,7 +532,7 @@ export class SearchAddon implements ITerminalAddon {
/**
* Selects and scrolls to a result.
* @param result The result to select.
- * @return Whethera result was selected.
+ * @return Whether a result was selected.
*/
private _selectResult(result: ISearchResult | undefined, color?: string): boolean {
const terminal = this._terminal!;
@@ -559,6 +559,13 @@ export class SearchAddon implements ITerminalAddon {
return true;
}
+ /**
+ * Applies styles to the decoration when it is rendered
+ * @param element the decoration's element
+ * @param color the color to apply
+ * @param result the search result associated with the decoration
+ * @returns
+ */
private _applyStyles(element: HTMLElement, color: string, result: ISearchResult): void {
if (element.clientWidth <= 0) {
return;
@@ -574,17 +581,17 @@ export class SearchAddon implements ITerminalAddon {
}
/**
- * Registers a decoration for the @param result
- * and @returns the decoration or undefined if
- * the marker has already been disposed of
+ * Creates a decoration for the result and applies styles
+ * @param result the search result for which to create the decoration
+ * @param color the color to use for the decoration
+ * @returns the {@link IDecoration} or undefined if the marker has already been disposed of
*/
- private _showResultDecoration(result: ISearchResult, color: string): IDecoration | undefined {
+ private _createResultDecoration(result: ISearchResult, color: string): IDecoration | undefined {
const terminal = this._terminal!;
const marker = terminal.registerMarker(undefined, result.row);
if (!marker) {
return undefined;
}
-
const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color, position: 'center' } });
findResultDecoration?.onRender((e) => this._applyStyles(e, color, result));
return findResultDecoration;
From 15c2432b6fb20594806c74d900437169012acd44 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 15:23:49 -0400
Subject: [PATCH 127/245] jsdoc
---
typings/xterm.d.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 363cede3..451da2e8 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -954,6 +954,7 @@ declare module 'xterm' {
* (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the
* alt buffer is active, undefined is returned.
* @param cursorYOffset The y position offset of the marker from the cursor.
+ * @param row The y position of the marker.
* @returns The new marker or undefined.
*/
registerMarker(cursorYOffset?: number, row?: number): IMarker | undefined;
From 85683a457d119bc156ca1b8543d457e1f3324896 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 15:27:50 -0400
Subject: [PATCH 128/245] add to col, not row
---
addons/xterm-addon-search/src/SearchAddon.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index d6ec1bb7..201c7420 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -100,8 +100,8 @@ export class SearchAddon implements ITerminalAddon {
// set start row and col to avoid redoing work
const key = Array.from(this._searchResults.keys()).pop()?.split('-');
if (key?.length === 2) {
- searchOptions.startRow = Number.parseInt(key[0]) + 1;
- searchOptions.startCol = Number.parseInt(key[1]);
+ searchOptions.startRow = Number.parseInt(key[0]);
+ searchOptions.startCol = Number.parseInt(key[1]) + 1;
}
} else {
// new search, clear out the old decorations
From dd503785bef8d68dc9450f68a7f6da36b27f6cc9 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Fri, 18 Mar 2022 15:28:16 -0400
Subject: [PATCH 129/245] Update css/xterm.css
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
css/xterm.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/css/xterm.css b/css/xterm.css
index aa58915c..b9382ccc 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -180,7 +180,7 @@
}
.xterm-find-result-decoration {
- opacity: 60%;
+ opacity: 0.6;
}
.xterm-decoration-overview-ruler {
From 8328fcac52ec64320a57a41defd93a4d5634e037 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 15:32:24 -0400
Subject: [PATCH 130/245] track disposable
---
addons/xterm-addon-search/src/SearchAddon.ts | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 201c7420..f0d64eb9 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -50,6 +50,7 @@ export class SearchAddon implements ITerminalAddon {
private _selectedDecoration: IDecoration | undefined;
private _resultDecorations: Map = new Map();
private _searchResults: Map = new Map();
+ private _onDataDisposable: IDisposable | undefined;
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -62,10 +63,13 @@ export class SearchAddon implements ITerminalAddon {
public activate(terminal: Terminal): void {
this._terminal = terminal;
- this._terminal.onData(() => this._dataChanged = true);
+ this._onDataDisposable = this._terminal.onData(() => this._dataChanged = true);
}
- public dispose(): void { }
+ public dispose(): void {
+ this.clear();
+ this._onDataDisposable?.dispose();
+ }
public clear(): void {
this._terminal?.clearSelection();
@@ -576,7 +580,6 @@ export class SearchAddon implements ITerminalAddon {
element.style.left = `${element.clientWidth * result.col}px`;
element.style.width = `${element.clientWidth * result.term.length}px`;
element.style.backgroundColor = color;
- element.style.color = color;
}
}
From fef30bdba457b03e2d0f7c204b1e127449086dba Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 15:38:31 -0400
Subject: [PATCH 131/245] revert row register marker changes
---
addons/xterm-addon-search/src/SearchAddon.ts | 4 ++--
src/browser/Terminal.ts | 5 +----
src/browser/Types.d.ts | 2 +-
src/browser/public/Terminal.ts | 7 ++-----
typings/xterm.d.ts | 3 +--
5 files changed, 7 insertions(+), 14 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index f0d64eb9..90749e92 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -547,7 +547,7 @@ export class SearchAddon implements ITerminalAddon {
}
terminal.select(result.col, result.row, result.size);
if (color) {
- const marker = terminal.registerMarker(undefined, result.row);
+ const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (marker) {
this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color } });
this._selectedDecoration?.onRender((e) => this._applyStyles(e, color, result));
@@ -591,7 +591,7 @@ export class SearchAddon implements ITerminalAddon {
*/
private _createResultDecoration(result: ISearchResult, color: string): IDecoration | undefined {
const terminal = this._terminal!;
- const marker = terminal.registerMarker(undefined, result.row);
+ const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (!marker) {
return undefined;
}
diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts
index 496b8dfa..5803e8c4 100644
--- a/src/browser/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -1012,14 +1012,11 @@ export class Terminal extends CoreTerminal implements ITerminal {
return this.buffer.markers;
}
- public addMarker(cursorYOffset: number, row?: number): IMarker | undefined {
+ public addMarker(cursorYOffset: number): IMarker | undefined {
// Disallow markers on the alt buffer
if (this.buffer !== this.buffers.normal) {
return;
}
- if (row) {
- return this.buffer.addMarker(row);
- }
return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
}
diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts
index 6e98eff6..8860bb41 100644
--- a/src/browser/Types.d.ts
+++ b/src/browser/Types.d.ts
@@ -61,7 +61,7 @@ export interface IPublicTerminal extends IDisposable {
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
deregisterCharacterJoiner(joinerId: number): void;
- addMarker(cursorYOffset: number, col?: number, row?: number): IMarker | undefined;
+ addMarker(cursorYOffset: number): IMarker | undefined;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
hasSelection(): boolean;
getSelection(): string;
diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts
index 8c290fda..1acde934 100644
--- a/src/browser/public/Terminal.ts
+++ b/src/browser/public/Terminal.ts
@@ -166,13 +166,10 @@ export class Terminal implements ITerminalApi {
this._checkProposedApi();
this._core.deregisterCharacterJoiner(joinerId);
}
- public registerMarker(cursorYOffset: number = 0, row?: number): IMarker | undefined {
+ public registerMarker(cursorYOffset: number = 0): IMarker | undefined {
this._checkProposedApi();
this._verifyIntegers(cursorYOffset);
- if (row) {
- this._verifyPositiveIntegers(row);
- }
- return this._core.addMarker(cursorYOffset, row);
+ return this._core.addMarker(cursorYOffset);
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
this._checkProposedApi();
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 451da2e8..d1eb3890 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -954,10 +954,9 @@ declare module 'xterm' {
* (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the
* alt buffer is active, undefined is returned.
* @param cursorYOffset The y position offset of the marker from the cursor.
- * @param row The y position of the marker.
* @returns The new marker or undefined.
*/
- registerMarker(cursorYOffset?: number, row?: number): IMarker | undefined;
+ registerMarker(cursorYOffset?: number): IMarker | undefined;
/**
* @deprecated use `registerMarker` instead.
From 61e5c10fd30b73f73f9ebb6714582e4f26dd0907 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 19:07:39 -0400
Subject: [PATCH 132/245] pull apart and refactor findNext
---
addons/xterm-addon-search/src/SearchAddon.ts | 96 ++++++++++----------
demo/client.ts | 8 +-
demo/index.html | 2 +-
typings/xterm.d.ts | 12 +++
4 files changed, 63 insertions(+), 55 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 90749e92..f592b78e 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -10,10 +10,9 @@ export interface ISearchOptions {
wholeWord?: boolean;
caseSensitive?: boolean;
incremental?: boolean;
+ highlightAllMatches?: boolean;
startRow?: number;
startCol?: number;
- overviewRulerResultDecorationColor?: string;
- overviewRulerSelectionDecorationColor?: string;
}
export interface ISearchPosition {
@@ -72,6 +71,7 @@ export class SearchAddon implements ITerminalAddon {
}
public clear(): void {
+ this._selectedDecoration?.dispose();
this._terminal?.clearSelection();
this._searchResults.clear();
this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
@@ -81,17 +81,23 @@ export class SearchAddon implements ITerminalAddon {
}
/**
- * Find all instances of the term, selecting the next one with each
- * enter. If it doesn't exist, do nothing.
+ * Find the next instance of the term, then scroll to and select it. If it
+ * doesn't exist, do nothing.
* @param term The search term.
* @param searchOptions Search options.
* @return Whether a result was found.
*/
- public find(term: string, searchOptions?: ISearchOptions): boolean {
+ public findNext(term: string, searchOptions?: ISearchOptions): boolean {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
+ return searchOptions?.highlightAllMatches ? this._highlightAllMatches(term, searchOptions) : this._findAndSelectNext(term, searchOptions);
+ }
+ private _highlightAllMatches(term: string, searchOptions: ISearchOptions): boolean {
+ if (!this._terminal) {
+ throw new Error('cannot find all matches with no terminal');
+ }
if (!term || term.length === 0) {
this.clear();
return false;
@@ -99,7 +105,7 @@ export class SearchAddon implements ITerminalAddon {
searchOptions = searchOptions || {};
if (term === this._cachedSearchTerm) {
if (!this._dataChanged) {
- return this.findNext(term, searchOptions);
+ return this._findAndSelectNext(term, searchOptions);
}
// set start row and col to avoid redoing work
const key = Array.from(this._searchResults.keys()).pop()?.split('-');
@@ -113,51 +119,45 @@ export class SearchAddon implements ITerminalAddon {
this._resultDecorations.clear();
this._searchResults.clear();
}
-
+ if (!this._terminal.options.overviewRulerWidth) {
+ this._terminal.options.overviewRulerWidth = 10;
+ }
+ if (!this._terminal.options.findResultDecorationColor) {
+ this._terminal.options.findResultDecorationColor = '#555753';
+ }
+ if (!this._terminal.options.findResultSelectedDecorationColor) {
+ this._terminal.options.findResultSelectedDecorationColor = '#ef2929';
+ }
searchOptions.incremental = false;
- let found = this.findNext(term, searchOptions);
+ let found = this._findAndSelectNext(term, searchOptions);
while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
if (this._result) {
this._searchResults.set(`${this._result.row}-${this._result.col}`, this._result);
}
- found = this.findNext(term, searchOptions);
+ found = this._findAndSelectNext(term, searchOptions);
}
- if (searchOptions.overviewRulerResultDecorationColor && searchOptions.overviewRulerSelectionDecorationColor) {
- this._searchResults.forEach(result => {
- const resultDecoration = this._createResultDecoration(result, searchOptions!.overviewRulerResultDecorationColor!);
- if (resultDecoration) {
- const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
- decorationsForLine.push(resultDecoration);
- this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
- }
- });
- }
-
+ this._searchResults.forEach(result => {
+ const resultDecoration = this._createResultDecoration(result);
+ if (resultDecoration) {
+ const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
+ decorationsForLine.push(resultDecoration);
+ this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
+ }
+ });
if (this._dataChanged) {
this._dataChanged = false;
}
if (this._searchResults.size > 0) {
this._cachedSearchTerm = term;
}
- return true;
+ return this._searchResults.size > 0;
}
-
- /**
- * Find the next instance of the term, then scroll to and select it. If it
- * doesn't exist, do nothing.
- * @param term The search term.
- * @param searchOptions Search options.
- * @return Whether a result was found.
- */
- public findNext(term: string, searchOptions?: ISearchOptions): boolean {
- if (!this._terminal) {
- throw new Error('Cannot use addon until it has been loaded');
- }
-
- if (!term || term.length === 0) {
+ private _findAndSelectNext(term: string, searchOptions?: ISearchOptions): boolean {
+ if (!this._terminal || !term || term.length === 0) {
this._result = undefined;
- this._terminal.clearSelection();
+ this._terminal?.clearSelection();
+ this.clear();
return false;
}
@@ -219,9 +219,8 @@ export class SearchAddon implements ITerminalAddon {
}
// Set selection and scroll if a result was found
- return this._selectResult(this._result, searchOptions?.overviewRulerSelectionDecorationColor);
+ return this._selectResult(this._result, searchOptions?.highlightAllMatches);
}
-
/**
* Find the previous instance of the term, then scroll to and select it. If it
* doesn't exist, do nothing.
@@ -300,7 +299,7 @@ export class SearchAddon implements ITerminalAddon {
if (!result && currentSelection) return true;
// Set selection and scroll if a result was found
- return this._selectResult(result, searchOptions?.overviewRulerSelectionDecorationColor);
+ return this._selectResult(result, searchOptions?.highlightAllMatches);
}
@@ -538,7 +537,7 @@ export class SearchAddon implements ITerminalAddon {
* @param result The result to select.
* @return Whether a result was selected.
*/
- private _selectResult(result: ISearchResult | undefined, color?: string): boolean {
+ private _selectResult(result: ISearchResult | undefined, highlightAllMatches?: boolean): boolean {
const terminal = this._terminal!;
this._selectedDecoration?.dispose();
if (!result) {
@@ -546,11 +545,11 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
terminal.select(result.col, result.row, result.size);
- if (color) {
+ if (this._terminal?.options.findResultSelectedDecorationColor && highlightAllMatches) {
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (marker) {
- this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color } });
- this._selectedDecoration?.onRender((e) => this._applyStyles(e, color, result));
+ this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: this._terminal!.options.findResultSelectedDecorationColor } });
+ this._selectedDecoration?.onRender((e) => this._applyStyles(e, this._terminal!.options.findResultSelectedDecorationColor!, result));
}
}
@@ -589,14 +588,17 @@ export class SearchAddon implements ITerminalAddon {
* @param color the color to use for the decoration
* @returns the {@link IDecoration} or undefined if the marker has already been disposed of
*/
- private _createResultDecoration(result: ISearchResult, color: string): IDecoration | undefined {
+ private _createResultDecoration(result: ISearchResult): IDecoration | undefined {
const terminal = this._terminal!;
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
- if (!marker) {
+ if (!marker || !this._terminal?.options.findResultDecorationColor) {
return undefined;
}
- const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color, position: 'center' } });
- findResultDecoration?.onRender((e) => this._applyStyles(e, color, result));
+ const findResultDecoration = terminal.registerDecoration(
+ { marker,
+ overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color: this._terminal.options.findResultDecorationColor, position: 'center' }
+ });
+ findResultDecoration?.onRender((e) => this._applyStyles(e, this._terminal!.options.findResultDecorationColor!, result));
return findResultDecoration;
}
}
diff --git a/demo/client.ts b/demo/client.ts
index 404435c4..98aa2e31 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -109,8 +109,7 @@ function getSearchOptions(e: KeyboardEvent): ISearchOptions {
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked,
incremental: e.key !== `Enter`,
- overviewRulerResultDecorationColor: '#555753',
- overviewRulerSelectionDecorationColor: '#ef2929'
+ highlightAllMatches: (document.getElementById('highlight-all-matches') as HTMLInputElement).checked,
};
}
@@ -203,11 +202,6 @@ function createTerminal(): void {
addDomListener(paddingElement, 'change', setPadding);
- addDomListener(actionElements.find, 'keyup', (e) => {
- term.options.overviewRulerWidth = 10;
- addons.search.instance.find(actionElements.find.value, getSearchOptions(e));
- });
-
addDomListener(actionElements.findNext, 'keyup', (e) => {
addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions(e));
});
diff --git a/demo/index.html b/demo/index.html
index 13748e9b..b89726de 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -38,12 +38,12 @@
Addons Control
SearchAddon
-
+
SerializeAddon
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index d1eb3890..3513feb7 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -272,6 +272,18 @@ declare module 'xterm' {
* ruler will be hidden when not set.
*/
overviewRulerWidth?: number;
+
+ /**
+ * The color for all find result decorations
+ * in the overview ruler
+ */
+ findResultDecorationColor?: string;
+
+ /**
+ * The color for the currently selected decoration
+ * when all matches are displayed
+ */
+ findResultSelectedDecorationColor?: string;
}
/**
From e79c21d4b2c490f0d9a70263a91f20efe6fe5007 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 19:24:27 -0400
Subject: [PATCH 133/245] get it to work for previous too
---
addons/xterm-addon-search/src/SearchAddon.ts | 53 +++++++++++---------
1 file changed, 30 insertions(+), 23 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index f592b78e..627c4a63 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -91,10 +91,10 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
- return searchOptions?.highlightAllMatches ? this._highlightAllMatches(term, searchOptions) : this._findAndSelectNext(term, searchOptions);
+ return searchOptions?.highlightAllMatches ? this._highlightAllMatches(term, searchOptions, 'next') : this._findAndSelectNext(term, searchOptions);
}
- private _highlightAllMatches(term: string, searchOptions: ISearchOptions): boolean {
+ private _highlightAllMatches(term: string, searchOptions: ISearchOptions, type: 'next' | 'previous'): boolean {
if (!this._terminal) {
throw new Error('cannot find all matches with no terminal');
}
@@ -129,12 +129,12 @@ export class SearchAddon implements ITerminalAddon {
this._terminal.options.findResultSelectedDecorationColor = '#ef2929';
}
searchOptions.incremental = false;
- let found = this._findAndSelectNext(term, searchOptions);
+ let found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
if (this._result) {
this._searchResults.set(`${this._result.row}-${this._result.col}`, this._result);
}
- found = this._findAndSelectNext(term, searchOptions);
+ found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
}
this._searchResults.forEach(result => {
const resultDecoration = this._createResultDecoration(result);
@@ -232,16 +232,24 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
+ return searchOptions?.highlightAllMatches ? this._highlightAllMatches(term, searchOptions, 'previous') : this._findAndSelectPrevious(term, searchOptions);
+ }
- if (!term || term.length === 0) {
- this._terminal.clearSelection();
+ private _findAndSelectPrevious(term: string, searchOptions?: ISearchOptions): boolean {
+ if (!this._terminal) {
+ throw new Error('Cannot use addon until it has been loaded');
+ }
+
+ if (!this._terminal || !term || term.length === 0) {
+ this._result = undefined;
+ this._terminal?.clearSelection();
+ this.clear();
return false;
}
const isReverseSearch = true;
- let startRow = this._terminal.buffer.active.baseY + this._terminal.rows;
- let startCol = this._terminal.cols;
- let result: ISearchResult | undefined;
+ let startRow = searchOptions?.startRow || this._terminal.buffer.active.baseY + this._terminal.rows;
+ let startCol = searchOptions?.startCol || this._terminal.cols;
const incremental = searchOptions ? searchOptions.incremental : false;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
@@ -259,50 +267,49 @@ export class SearchAddon implements ITerminalAddon {
if (incremental) {
// Try to expand selection to right first.
- result = this._findInLine(term, searchPosition, searchOptions, false);
- const isOldResultHighlighted = result && result.row === startRow && result.col === startCol;
+ this._result = this._findInLine(term, searchPosition, searchOptions, false);
+ const isOldResultHighlighted = this._result && this._result.row === startRow && this._result.col === startCol;
if (!isOldResultHighlighted) {
// If selection was not able to be expanded to the right, then try reverse search
if (currentSelection) {
searchPosition.startRow = currentSelection.endRow;
searchPosition.startCol = currentSelection.endColumn;
}
- result = this._findInLine(term, searchPosition, searchOptions, true);
+ this._result = this._findInLine(term, searchPosition, searchOptions, true);
}
} else {
- result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
+ this._result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
}
// Search from startRow - 1 to top
- if (!result) {
+ if (!this._result) {
searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols);
for (let y = startRow - 1; y >= 0; y--) {
searchPosition.startRow = y;
- result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
- if (result) {
+ this._result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
+ if (this._result) {
break;
}
}
}
// If we hit the top and didn't search from the very bottom wrap back down
- if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows)) {
+ if (!this._result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows)) {
for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows); y >= startRow; y--) {
searchPosition.startRow = y;
- result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
- if (result) {
+ this._result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
+ if (this._result) {
break;
}
}
}
// If there is only one result, return true.
- if (!result && currentSelection) return true;
+ if (!this._result && currentSelection) return true;
- // Set selection and scroll if a result was found
- return this._selectResult(result, searchOptions?.highlightAllMatches);
+ // Set selection and scroll if a this._result was found
+ return this._selectResult(this._result, searchOptions?.highlightAllMatches);
}
-
/**
* Sets up a line cache with a ttl
*/
From a0aae62cb7f1344bed280bbe15602cc9f6fe7ec4 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Fri, 18 Mar 2022 19:43:00 -0400
Subject: [PATCH 134/245] get rid of start col end col
---
addons/xterm-addon-search/src/SearchAddon.ts | 39 ++++++++++++++------
1 file changed, 27 insertions(+), 12 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 627c4a63..422f060b 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -11,8 +11,6 @@ export interface ISearchOptions {
caseSensitive?: boolean;
incremental?: boolean;
highlightAllMatches?: boolean;
- startRow?: number;
- startCol?: number;
}
export interface ISearchPosition {
@@ -50,6 +48,7 @@ export class SearchAddon implements ITerminalAddon {
private _resultDecorations: Map = new Map();
private _searchResults: Map = new Map();
private _onDataDisposable: IDisposable | undefined;
+ private _addToPrior: boolean = false;
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -107,12 +106,7 @@ export class SearchAddon implements ITerminalAddon {
if (!this._dataChanged) {
return this._findAndSelectNext(term, searchOptions);
}
- // set start row and col to avoid redoing work
- const key = Array.from(this._searchResults.keys()).pop()?.split('-');
- if (key?.length === 2) {
- searchOptions.startRow = Number.parseInt(key[0]);
- searchOptions.startCol = Number.parseInt(key[1]) + 1;
- }
+ this._addToPrior = true;
} else {
// new search, clear out the old decorations
this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
@@ -147,6 +141,9 @@ export class SearchAddon implements ITerminalAddon {
if (this._dataChanged) {
this._dataChanged = false;
}
+ if (this._addToPrior) {
+ this._addToPrior = false;
+ }
if (this._searchResults.size > 0) {
this._cachedSearchTerm = term;
}
@@ -161,8 +158,16 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
- let startCol = searchOptions?.startCol || 0;
- let startRow = searchOptions?.startRow || 0;
+ let startCol = 0;
+ let startRow = 0;
+ if (searchOptions?.highlightAllMatches && this._addToPrior) {
+ // set start row and col to avoid redoing work
+ const key = Array.from(this._searchResults.keys()).pop()?.split('-');
+ if (key?.length === 2) {
+ startRow = Number.parseInt(key[0]);
+ startCol = Number.parseInt(key[1]) + 1;
+ }
+ }
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
const incremental = searchOptions ? searchOptions.incremental : false;
@@ -247,9 +252,19 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
+ let startRow = this._terminal.buffer.active.baseY + this._terminal.rows;
+ let startCol = this._terminal.cols;
const isReverseSearch = true;
- let startRow = searchOptions?.startRow || this._terminal.buffer.active.baseY + this._terminal.rows;
- let startCol = searchOptions?.startCol || this._terminal.cols;
+ // if (searchOptions?.highlightAllMatches && this._addToPrior) {
+ // // set start row and col to avoid redoing work
+ // // TODO: fix this will mess with the order that they're iterated through
+ // const key = Array.from(this._searchResults.keys()).pop()?.split('-');
+ // if (key?.length === 2) {
+ // startRow = Number.parseInt(key[0]);
+ // startCol = Number.parseInt(key[1]) + 1;
+ // }
+ // }
+
const incremental = searchOptions ? searchOptions.incremental : false;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
From 8225ecd3bad9b66df1e7341822c1a34c54bf8e96 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 21 Mar 2022 10:24:46 -0400
Subject: [PATCH 135/245] refactor from highlightAllMatches -> decorations
---
addons/xterm-addon-search/src/SearchAddon.ts | 41 ++++++++++----------
demo/client.ts | 2 +-
typings/xterm.d.ts | 12 ------
3 files changed, 21 insertions(+), 34 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 422f060b..f7cf5175 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -10,7 +10,12 @@ export interface ISearchOptions {
wholeWord?: boolean;
caseSensitive?: boolean;
incremental?: boolean;
- highlightAllMatches?: boolean;
+ decorations?: IDecorationColor;
+}
+
+interface IDecorationColor {
+ matchColor: string;
+ selectedColor: string;
}
export interface ISearchPosition {
@@ -90,7 +95,7 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
- return searchOptions?.highlightAllMatches ? this._highlightAllMatches(term, searchOptions, 'next') : this._findAndSelectNext(term, searchOptions);
+ return searchOptions?.decorations ? this._highlightAllMatches(term, searchOptions, 'next') : this._findAndSelectNext(term, searchOptions);
}
private _highlightAllMatches(term: string, searchOptions: ISearchOptions, type: 'next' | 'previous'): boolean {
@@ -116,12 +121,6 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal.options.overviewRulerWidth) {
this._terminal.options.overviewRulerWidth = 10;
}
- if (!this._terminal.options.findResultDecorationColor) {
- this._terminal.options.findResultDecorationColor = '#555753';
- }
- if (!this._terminal.options.findResultSelectedDecorationColor) {
- this._terminal.options.findResultSelectedDecorationColor = '#ef2929';
- }
searchOptions.incremental = false;
let found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
@@ -131,7 +130,7 @@ export class SearchAddon implements ITerminalAddon {
found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
}
this._searchResults.forEach(result => {
- const resultDecoration = this._createResultDecoration(result);
+ const resultDecoration = this._createResultDecoration(result, searchOptions.decorations);
if (resultDecoration) {
const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
decorationsForLine.push(resultDecoration);
@@ -160,7 +159,7 @@ export class SearchAddon implements ITerminalAddon {
let startCol = 0;
let startRow = 0;
- if (searchOptions?.highlightAllMatches && this._addToPrior) {
+ if (searchOptions?.decorations && this._addToPrior) {
// set start row and col to avoid redoing work
const key = Array.from(this._searchResults.keys()).pop()?.split('-');
if (key?.length === 2) {
@@ -224,7 +223,7 @@ export class SearchAddon implements ITerminalAddon {
}
// Set selection and scroll if a result was found
- return this._selectResult(this._result, searchOptions?.highlightAllMatches);
+ return this._selectResult(this._result, searchOptions?.decorations);
}
/**
* Find the previous instance of the term, then scroll to and select it. If it
@@ -237,7 +236,7 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
- return searchOptions?.highlightAllMatches ? this._highlightAllMatches(term, searchOptions, 'previous') : this._findAndSelectPrevious(term, searchOptions);
+ return searchOptions?.decorations ? this._highlightAllMatches(term, searchOptions, 'previous') : this._findAndSelectPrevious(term, searchOptions);
}
private _findAndSelectPrevious(term: string, searchOptions?: ISearchOptions): boolean {
@@ -322,7 +321,7 @@ export class SearchAddon implements ITerminalAddon {
if (!this._result && currentSelection) return true;
// Set selection and scroll if a this._result was found
- return this._selectResult(this._result, searchOptions?.highlightAllMatches);
+ return this._selectResult(this._result, searchOptions?.decorations);
}
/**
@@ -559,7 +558,7 @@ export class SearchAddon implements ITerminalAddon {
* @param result The result to select.
* @return Whether a result was selected.
*/
- private _selectResult(result: ISearchResult | undefined, highlightAllMatches?: boolean): boolean {
+ private _selectResult(result: ISearchResult | undefined, decorations?: IDecorationColor): boolean {
const terminal = this._terminal!;
this._selectedDecoration?.dispose();
if (!result) {
@@ -567,11 +566,11 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
terminal.select(result.col, result.row, result.size);
- if (this._terminal?.options.findResultSelectedDecorationColor && highlightAllMatches) {
+ if (decorations?.selectedColor) {
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (marker) {
- this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: this._terminal!.options.findResultSelectedDecorationColor } });
- this._selectedDecoration?.onRender((e) => this._applyStyles(e, this._terminal!.options.findResultSelectedDecorationColor!, result));
+ this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: decorations.selectedColor } });
+ this._selectedDecoration?.onRender((e) => this._applyStyles(e, decorations.selectedColor, result));
}
}
@@ -610,17 +609,17 @@ export class SearchAddon implements ITerminalAddon {
* @param color the color to use for the decoration
* @returns the {@link IDecoration} or undefined if the marker has already been disposed of
*/
- private _createResultDecoration(result: ISearchResult): IDecoration | undefined {
+ private _createResultDecoration(result: ISearchResult, decorations?: IDecorationColor): IDecoration | undefined {
const terminal = this._terminal!;
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
- if (!marker || !this._terminal?.options.findResultDecorationColor) {
+ if (!marker || !decorations?.matchColor) {
return undefined;
}
const findResultDecoration = terminal.registerDecoration(
{ marker,
- overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color: this._terminal.options.findResultDecorationColor, position: 'center' }
+ overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color: decorations.matchColor, position: 'center' }
});
- findResultDecoration?.onRender((e) => this._applyStyles(e, this._terminal!.options.findResultDecorationColor!, result));
+ findResultDecoration?.onRender((e) => this._applyStyles(e, decorations.matchColor, result));
return findResultDecoration;
}
}
diff --git a/demo/client.ts b/demo/client.ts
index 98aa2e31..faf3e0be 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -109,7 +109,7 @@ function getSearchOptions(e: KeyboardEvent): ISearchOptions {
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked,
incremental: e.key !== `Enter`,
- highlightAllMatches: (document.getElementById('highlight-all-matches') as HTMLInputElement).checked,
+ decorations: (document.getElementById('highlight-all-matches') as HTMLInputElement).checked ? { matchColor: '#555753', selectedColor: '#ef2929' } : undefined
};
}
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 3513feb7..d1eb3890 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -272,18 +272,6 @@ declare module 'xterm' {
* ruler will be hidden when not set.
*/
overviewRulerWidth?: number;
-
- /**
- * The color for all find result decorations
- * in the overview ruler
- */
- findResultDecorationColor?: string;
-
- /**
- * The color for the currently selected decoration
- * when all matches are displayed
- */
- findResultSelectedDecorationColor?: string;
}
/**
From 12a999dfe051422006bcd6a69b85dc004d79b8fb Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 21 Mar 2022 10:54:01 -0400
Subject: [PATCH 136/245] get rid of start row/col code
---
addons/xterm-addon-search/src/SearchAddon.ts | 42 ++++----------------
1 file changed, 8 insertions(+), 34 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index f7cf5175..7ec081f5 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -53,7 +53,6 @@ export class SearchAddon implements ITerminalAddon {
private _resultDecorations: Map = new Map();
private _searchResults: Map = new Map();
private _onDataDisposable: IDisposable | undefined;
- private _addToPrior: boolean = false;
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -107,17 +106,14 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
searchOptions = searchOptions || {};
- if (term === this._cachedSearchTerm) {
- if (!this._dataChanged) {
- return this._findAndSelectNext(term, searchOptions);
- }
- this._addToPrior = true;
- } else {
- // new search, clear out the old decorations
- this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
- this._resultDecorations.clear();
- this._searchResults.clear();
+ if (term === this._cachedSearchTerm && !this._dataChanged) {
+ return type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
}
+ // new search, clear out the old decorations
+ this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
+ this._resultDecorations.clear();
+ this._searchResults.clear();
+
if (!this._terminal.options.overviewRulerWidth) {
this._terminal.options.overviewRulerWidth = 10;
}
@@ -137,12 +133,7 @@ export class SearchAddon implements ITerminalAddon {
this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
}
});
- if (this._dataChanged) {
- this._dataChanged = false;
- }
- if (this._addToPrior) {
- this._addToPrior = false;
- }
+ this._dataChanged = false;
if (this._searchResults.size > 0) {
this._cachedSearchTerm = term;
}
@@ -159,14 +150,6 @@ export class SearchAddon implements ITerminalAddon {
let startCol = 0;
let startRow = 0;
- if (searchOptions?.decorations && this._addToPrior) {
- // set start row and col to avoid redoing work
- const key = Array.from(this._searchResults.keys()).pop()?.split('-');
- if (key?.length === 2) {
- startRow = Number.parseInt(key[0]);
- startCol = Number.parseInt(key[1]) + 1;
- }
- }
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
const incremental = searchOptions ? searchOptions.incremental : false;
@@ -254,15 +237,6 @@ export class SearchAddon implements ITerminalAddon {
let startRow = this._terminal.buffer.active.baseY + this._terminal.rows;
let startCol = this._terminal.cols;
const isReverseSearch = true;
- // if (searchOptions?.highlightAllMatches && this._addToPrior) {
- // // set start row and col to avoid redoing work
- // // TODO: fix this will mess with the order that they're iterated through
- // const key = Array.from(this._searchResults.keys()).pop()?.split('-');
- // if (key?.length === 2) {
- // startRow = Number.parseInt(key[0]);
- // startCol = Number.parseInt(key[1]) + 1;
- // }
- // }
const incremental = searchOptions ? searchOptions.incremental : false;
let currentSelection: ISelectionPosition | undefined;
From bec4cb37d4031dadbbeff495d0d41436d2d03e06 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 21 Mar 2022 10:58:44 -0400
Subject: [PATCH 137/245] set data changed to false if true
---
addons/xterm-addon-search/src/SearchAddon.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 7ec081f5..33e38e95 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -133,7 +133,9 @@ export class SearchAddon implements ITerminalAddon {
this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
}
});
- this._dataChanged = false;
+ if (this._dataChanged) {
+ this._dataChanged = false;
+ }
if (this._searchResults.size > 0) {
this._cachedSearchTerm = term;
}
From 8ff34a6aee1e2f083056af15dc4c06822d3c7003 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 21 Mar 2022 11:17:24 -0400
Subject: [PATCH 138/245] update decorations dynamically when the buffer
changes
---
addons/xterm-addon-search/src/SearchAddon.ts | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 33e38e95..457e0853 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -53,6 +53,7 @@ export class SearchAddon implements ITerminalAddon {
private _resultDecorations: Map = new Map();
private _searchResults: Map = new Map();
private _onDataDisposable: IDisposable | undefined;
+ private _lastSearchOptions: ISearchOptions | undefined;
/**
* translateBufferLineToStringWithWrap is a fairly expensive call.
* We memoize the calls into an array that has a time based ttl.
@@ -65,7 +66,13 @@ export class SearchAddon implements ITerminalAddon {
public activate(terminal: Terminal): void {
this._terminal = terminal;
- this._onDataDisposable = this._terminal.onData(() => this._dataChanged = true);
+ this._onDataDisposable = this._terminal.onData(() => {
+ //TODO: debounce
+ this._dataChanged = true;
+ if (this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) {
+ this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions,'previous');
+ }
+ });
}
public dispose(): void {
@@ -94,10 +101,11 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
+ this._lastSearchOptions = searchOptions;
return searchOptions?.decorations ? this._highlightAllMatches(term, searchOptions, 'next') : this._findAndSelectNext(term, searchOptions);
}
- private _highlightAllMatches(term: string, searchOptions: ISearchOptions, type: 'next' | 'previous'): boolean {
+ private _highlightAllMatches(term: string, searchOptions: ISearchOptions, selectionType: 'next' | 'previous'): boolean {
if (!this._terminal) {
throw new Error('cannot find all matches with no terminal');
}
@@ -107,7 +115,7 @@ export class SearchAddon implements ITerminalAddon {
}
searchOptions = searchOptions || {};
if (term === this._cachedSearchTerm && !this._dataChanged) {
- return type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
+ return selectionType === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
}
// new search, clear out the old decorations
this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
@@ -118,12 +126,12 @@ export class SearchAddon implements ITerminalAddon {
this._terminal.options.overviewRulerWidth = 10;
}
searchOptions.incremental = false;
- let found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
+ let found = selectionType === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
if (this._result) {
this._searchResults.set(`${this._result.row}-${this._result.col}`, this._result);
}
- found = type === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
+ found = selectionType === 'next' ? this._findAndSelectNext(term, searchOptions) : this._findAndSelectPrevious(term, searchOptions);
}
this._searchResults.forEach(result => {
const resultDecoration = this._createResultDecoration(result, searchOptions.decorations);
@@ -221,6 +229,7 @@ export class SearchAddon implements ITerminalAddon {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
+ this._lastSearchOptions = searchOptions;
return searchOptions?.decorations ? this._highlightAllMatches(term, searchOptions, 'previous') : this._findAndSelectPrevious(term, searchOptions);
}
From 1098173908af0894e2637467ea9b233debe3c6c5 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 21 Mar 2022 11:45:45 -0400
Subject: [PATCH 139/245] use set timeout
---
addons/xterm-addon-search/src/SearchAddon.ts | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 457e0853..4bca88cc 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -67,11 +67,12 @@ export class SearchAddon implements ITerminalAddon {
public activate(terminal: Terminal): void {
this._terminal = terminal;
this._onDataDisposable = this._terminal.onData(() => {
- //TODO: debounce
this._dataChanged = true;
- if (this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) {
- this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions,'previous');
- }
+ setTimeout(() => {
+ if (this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) {
+ this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions,'previous');
+ }
+ }, 200);
});
}
From 97e4af3707e2343d0d8770d3dbed2a03929bbc02 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 21 Mar 2022 13:07:25 -0400
Subject: [PATCH 140/245] only highlight all matches when decorations are
requested
---
addons/xterm-addon-search/src/SearchAddon.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 4bca88cc..6e4b5f9b 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -69,7 +69,7 @@ export class SearchAddon implements ITerminalAddon {
this._onDataDisposable = this._terminal.onData(() => {
this._dataChanged = true;
setTimeout(() => {
- if (this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) {
+ if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) {
this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions,'previous');
}
}, 200);
From 4cea6b3f4136f5626a00229b68ade83076776971 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Mon, 21 Mar 2022 13:35:56 -0400
Subject: [PATCH 141/245] revert a breaking change
---
addons/xterm-addon-search/src/SearchAddon.ts | 3 ---
1 file changed, 3 deletions(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 6e4b5f9b..6b84421d 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -169,9 +169,6 @@ export class SearchAddon implements ITerminalAddon {
currentSelection = this._terminal.getSelectionPosition()!;
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
- } else if (!startRow) {
- startRow = this._terminal.buffer.active.cursorY;
- startCol = this._terminal.buffer.active.cursorX;
}
this._initLinesCache();
From 64d5af38f5fbea10958c205ebb7556d19f107da0 Mon Sep 17 00:00:00 2001
From: Megan Rogge
Date: Mon, 21 Mar 2022 15:35:01 -0400
Subject: [PATCH 142/245] Update addons/xterm-addon-search/src/SearchAddon.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
---
addons/xterm-addon-search/src/SearchAddon.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts
index 6b84421d..c2089f11 100644
--- a/addons/xterm-addon-search/src/SearchAddon.ts
+++ b/addons/xterm-addon-search/src/SearchAddon.ts
@@ -70,7 +70,7 @@ export class SearchAddon implements ITerminalAddon {
this._dataChanged = true;
setTimeout(() => {
if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) {
- this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions,'previous');
+ this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions, 'previous');
}
}, 200);
});
From e8aa4eba75522877af05799020b91b7b2829ab76 Mon Sep 17 00:00:00 2001
From: meganrogge