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 2/3] 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 3/3] 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;