Skip to content

Commit d60431f

Browse files
authored
Merge branch 'master' into electron-13-sogou-fix
2 parents f025c0c + f38d7d8 commit d60431f

File tree

17 files changed

+602
-341
lines changed

17 files changed

+602
-341
lines changed

addons/xterm-addon-serialize/src/SerializeAddon.test.ts

Lines changed: 98 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class TestSelectionService {
4343
}
4444
}
4545

46-
describe('xterm-addon-serialize html', () => {
46+
describe('xterm-addon-serialize', () => {
4747
let cm: ColorManager;
4848
let dom: jsdom.JSDOM;
4949
let document: Document;
@@ -83,123 +83,132 @@ describe('xterm-addon-serialize html', () => {
8383
(terminal as any)._core._selectionService = selectionService;
8484
});
8585

86-
it('empty terminal with selection turned off', () => {
87-
const output = serializeAddon.serializeAsHTML();
88-
assert.notEqual(output, '');
89-
assert.equal((output.match(/<div><span> {10}<\/span><\/div>/g) || []).length, 2);
86+
describe('text', () => {
87+
it('restoring cursor styles', async () => {
88+
await writeP(terminal, sgr('32') + '> ' + sgr('0'));
89+
assert.equal(serializeAddon.serialize(), '\u001b[32m> \u001b[0m');
90+
});
9091
});
9192

92-
it('empty terminal with no selection', () => {
93-
const output = serializeAddon.serializeAsHTML({
94-
onlySelection: true
93+
describe('html', () => {
94+
it('empty terminal with selection turned off', () => {
95+
const output = serializeAddon.serializeAsHTML();
96+
assert.notEqual(output, '');
97+
assert.equal((output.match(/<div><span> {10}<\/span><\/div>/g) || []).length, 2);
98+
});
99+
100+
it('empty terminal with no selection', () => {
101+
const output = serializeAddon.serializeAsHTML({
102+
onlySelection: true
103+
});
104+
assert.equal(output, '');
95105
});
96-
assert.equal(output, '');
97-
});
98106

99-
it('basic terminal with selection', async () => {
100-
await writeP(terminal, ' terminal ');
101-
terminal.select(1, 0, 8);
107+
it('basic terminal with selection', async () => {
108+
await writeP(terminal, ' terminal ');
109+
terminal.select(1, 0, 8);
102110

103-
const output = serializeAddon.serializeAsHTML({
104-
onlySelection: true
111+
const output = serializeAddon.serializeAsHTML({
112+
onlySelection: true
113+
});
114+
assert.equal((output.match(/<div><span>terminal<\/span><\/div>/g) || []).length, 1, output);
105115
});
106-
assert.equal((output.match(/<div><span>terminal<\/span><\/div>/g) || []).length, 1, output);
107-
});
108116

109-
it('cells with bold styling', async () => {
110-
await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' ');
117+
it('cells with bold styling', async () => {
118+
await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' ');
111119

112-
const output = serializeAddon.serializeAsHTML();
113-
assert.equal((output.match(/<span style='font-weight: bold;'>terminal<\/span>/g) || []).length, 1, output);
114-
});
120+
const output = serializeAddon.serializeAsHTML();
121+
assert.equal((output.match(/<span style='font-weight: bold;'>terminal<\/span>/g) || []).length, 1, output);
122+
});
115123

116-
it('cells with italic styling', async () => {
117-
await writeP(terminal, ' ' + sgr('3') + 'terminal' + sgr('23') + ' ');
124+
it('cells with italic styling', async () => {
125+
await writeP(terminal, ' ' + sgr('3') + 'terminal' + sgr('23') + ' ');
118126

119-
const output = serializeAddon.serializeAsHTML();
120-
assert.equal((output.match(/<span style='font-style: italic;'>terminal<\/span>/g) || []).length, 1, output);
121-
});
127+
const output = serializeAddon.serializeAsHTML();
128+
assert.equal((output.match(/<span style='font-style: italic;'>terminal<\/span>/g) || []).length, 1, output);
129+
});
122130

123-
it('cells with inverse styling', async () => {
124-
await writeP(terminal, ' ' + sgr('7') + 'terminal' + sgr('27') + ' ');
131+
it('cells with inverse styling', async () => {
132+
await writeP(terminal, ' ' + sgr('7') + 'terminal' + sgr('27') + ' ');
125133

126-
const output = serializeAddon.serializeAsHTML();
127-
assert.equal((output.match(/<span style='color: #000000; background-color: #BFBFBF;'>terminal<\/span>/g) || []).length, 1, output);
128-
});
134+
const output = serializeAddon.serializeAsHTML();
135+
assert.equal((output.match(/<span style='color: #000000; background-color: #BFBFBF;'>terminal<\/span>/g) || []).length, 1, output);
136+
});
129137

130-
it('cells with underline styling', async () => {
131-
await writeP(terminal, ' ' + sgr('4') + 'terminal' + sgr('24') + ' ');
138+
it('cells with underline styling', async () => {
139+
await writeP(terminal, ' ' + sgr('4') + 'terminal' + sgr('24') + ' ');
132140

133-
const output = serializeAddon.serializeAsHTML();
134-
assert.equal((output.match(/<span style='text-decoration: underline;'>terminal<\/span>/g) || []).length, 1, output);
135-
});
141+
const output = serializeAddon.serializeAsHTML();
142+
assert.equal((output.match(/<span style='text-decoration: underline;'>terminal<\/span>/g) || []).length, 1, output);
143+
});
136144

137-
it('cells with invisible styling', async () => {
138-
await writeP(terminal, ' ' + sgr('8') + 'terminal' + sgr('28') + ' ');
145+
it('cells with invisible styling', async () => {
146+
await writeP(terminal, ' ' + sgr('8') + 'terminal' + sgr('28') + ' ');
139147

140-
const output = serializeAddon.serializeAsHTML();
141-
assert.equal((output.match(/<span style='visibility: hidden;'>terminal<\/span>/g) || []).length, 1, output);
142-
});
148+
const output = serializeAddon.serializeAsHTML();
149+
assert.equal((output.match(/<span style='visibility: hidden;'>terminal<\/span>/g) || []).length, 1, output);
150+
});
143151

144-
it('cells with dim styling', async () => {
145-
await writeP(terminal, ' ' + sgr('2') + 'terminal' + sgr('22') + ' ');
152+
it('cells with dim styling', async () => {
153+
await writeP(terminal, ' ' + sgr('2') + 'terminal' + sgr('22') + ' ');
146154

147-
const output = serializeAddon.serializeAsHTML();
148-
assert.equal((output.match(/<span style='opacity: 0.5;'>terminal<\/span>/g) || []).length, 1, output);
149-
});
155+
const output = serializeAddon.serializeAsHTML();
156+
assert.equal((output.match(/<span style='opacity: 0.5;'>terminal<\/span>/g) || []).length, 1, output);
157+
});
150158

151-
it('cells with strikethrough styling', async () => {
152-
await writeP(terminal, ' ' + sgr('9') + 'terminal' + sgr('29') + ' ');
159+
it('cells with strikethrough styling', async () => {
160+
await writeP(terminal, ' ' + sgr('9') + 'terminal' + sgr('29') + ' ');
153161

154-
const output = serializeAddon.serializeAsHTML();
155-
assert.equal((output.match(/<span style='text-decoration: line-through;'>terminal<\/span>/g) || []).length, 1, output);
156-
});
162+
const output = serializeAddon.serializeAsHTML();
163+
assert.equal((output.match(/<span style='text-decoration: line-through;'>terminal<\/span>/g) || []).length, 1, output);
164+
});
157165

158-
it('cells with combined styling', async () => {
159-
await writeP(terminal, sgr('1') + ' ' + sgr('9') + 'termi' + sgr('22') + 'nal' + sgr('29') + ' ');
166+
it('cells with combined styling', async () => {
167+
await writeP(terminal, sgr('1') + ' ' + sgr('9') + 'termi' + sgr('22') + 'nal' + sgr('29') + ' ');
160168

161-
const output = serializeAddon.serializeAsHTML();
162-
assert.equal((output.match(/<span style='font-weight: bold;'> <\/span>/g) || []).length, 1, output);
163-
assert.equal((output.match(/<span style='font-weight: bold; text-decoration: line-through;'>termi<\/span>/g) || []).length, 1, output);
164-
assert.equal((output.match(/<span style='text-decoration: line-through;'>nal<\/span>/g) || []).length, 1, output);
165-
});
169+
const output = serializeAddon.serializeAsHTML();
170+
assert.equal((output.match(/<span style='font-weight: bold;'> <\/span>/g) || []).length, 1, output);
171+
assert.equal((output.match(/<span style='font-weight: bold; text-decoration: line-through;'>termi<\/span>/g) || []).length, 1, output);
172+
assert.equal((output.match(/<span style='text-decoration: line-through;'>nal<\/span>/g) || []).length, 1, output);
173+
});
166174

167-
it('cells with color styling', async () => {
168-
await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' ');
175+
it('cells with color styling', async () => {
176+
await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' ');
169177

170-
const output = serializeAddon.serializeAsHTML();
171-
assert.equal((output.match(/<span style='color: #00ff00;'>terminal<\/span>/g) || []).length, 1, output);
172-
});
178+
const output = serializeAddon.serializeAsHTML();
179+
assert.equal((output.match(/<span style='color: #00ff00;'>terminal<\/span>/g) || []).length, 1, output);
180+
});
173181

174-
it('cells with background styling', async () => {
175-
await writeP(terminal, ' ' + sgr('48;5;46') + 'terminal' + sgr('49') + ' ');
182+
it('cells with background styling', async () => {
183+
await writeP(terminal, ' ' + sgr('48;5;46') + 'terminal' + sgr('49') + ' ');
176184

177-
const output = serializeAddon.serializeAsHTML();
178-
assert.equal((output.match(/<span style='background-color: #00ff00;'>terminal<\/span>/g) || []).length, 1, output);
179-
});
185+
const output = serializeAddon.serializeAsHTML();
186+
assert.equal((output.match(/<span style='background-color: #00ff00;'>terminal<\/span>/g) || []).length, 1, output);
187+
});
180188

181-
it('empty terminal with default options', async () => {
182-
const output = serializeAddon.serializeAsHTML();
183-
assert.equal((output.match(/color: #000000; background-color: #ffffff; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
184-
});
189+
it('empty terminal with default options', async () => {
190+
const output = serializeAddon.serializeAsHTML();
191+
assert.equal((output.match(/color: #000000; background-color: #ffffff; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
192+
});
185193

186-
it('empty terminal with custom options', async () => {
187-
terminal.options.fontFamily = 'verdana';
188-
terminal.options.fontSize = 20;
189-
terminal.options.theme = {
190-
foreground: '#ff00ff',
191-
background: '#00ff00'
192-
};
193-
const output = serializeAddon.serializeAsHTML({
194-
includeGlobalBackground: true
195-
});
196-
assert.equal((output.match(/color: #ff00ff; background-color: #00ff00; font-family: verdana; font-size: 20px;/g) || []).length, 1, output);
197-
});
194+
it('empty terminal with custom options', async () => {
195+
terminal.options.fontFamily = 'verdana';
196+
terminal.options.fontSize = 20;
197+
terminal.options.theme = {
198+
foreground: '#ff00ff',
199+
background: '#00ff00'
200+
};
201+
const output = serializeAddon.serializeAsHTML({
202+
includeGlobalBackground: true
203+
});
204+
assert.equal((output.match(/color: #ff00ff; background-color: #00ff00; font-family: verdana; font-size: 20px;/g) || []).length, 1, output);
205+
});
198206

199-
it('empty terminal with background included', async () => {
200-
const output = serializeAddon.serializeAsHTML({
201-
includeGlobalBackground: true
207+
it('empty terminal with background included', async () => {
208+
const output = serializeAddon.serializeAsHTML({
209+
includeGlobalBackground: true
210+
});
211+
assert.equal((output.match(/color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
202212
});
203-
assert.equal((output.match(/color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;/g) || []).length, 1, output);
204213
});
205214
});

addons/xterm-addon-serialize/src/SerializeAddon.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import { Terminal, ITerminalAddon, IBuffer, IBufferCell, IBufferRange } from 'xterm';
99
import { IColorSet } from 'browser/Types';
10+
import { IAttributeData } from 'common/Types';
1011

1112
function constrain(value: number, low: number, high: number): number {
1213
return Math.max(low, Math.min(value, high));
@@ -62,17 +63,17 @@ abstract class BaseSerializeHandler {
6263
protected _serializeString(): string { return ''; }
6364
}
6465

65-
function equalFg(cell1: IBufferCell, cell2: IBufferCell): boolean {
66+
function equalFg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean {
6667
return cell1.getFgColorMode() === cell2.getFgColorMode()
6768
&& cell1.getFgColor() === cell2.getFgColor();
6869
}
6970

70-
function equalBg(cell1: IBufferCell, cell2: IBufferCell): boolean {
71+
function equalBg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean {
7172
return cell1.getBgColorMode() === cell2.getBgColorMode()
7273
&& cell1.getBgColor() === cell2.getBgColor();
7374
}
7475

75-
function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
76+
function equalFlags(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean {
7677
return cell1.isInverse() === cell2.isInverse()
7778
&& cell1.isBold() === cell2.isBold()
7879
&& cell1.isUnderline() === cell2.isUnderline()
@@ -229,7 +230,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
229230
this._nullCellCount = 0;
230231
}
231232

232-
private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): number[] {
233+
private _diffStyle(cell: IBufferCell | IAttributeData, oldCell: IBufferCell): number[] {
233234
const sgrSeq: number[] = [];
234235
const fgChanged = !equalFg(cell, oldCell);
235236
const bgChanged = !equalBg(cell, oldCell);
@@ -393,6 +394,15 @@ class StringSerializeHandler extends BaseSerializeHandler {
393394
moveRight(realCursorCol - this._lastCursorCol);
394395
}
395396

397+
// Restore the cursor's current style, see https://github.com/xtermjs/xterm.js/issues/3677
398+
// HACK: Internal API access since it's awkward to expose this in the API and serialize will
399+
// likely be the only consumer
400+
const curAttrData: IAttributeData = (this._terminal as any)._core._inputHandler._curAttrData;
401+
const sgrSeq = this._diffStyle(curAttrData, this._cursorStyle);
402+
if (sgrSeq.length > 0) {
403+
content += `\u001b[${sgrSeq.join(';')}m`;
404+
}
405+
396406
return content;
397407
}
398408
}

css/xterm.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,10 @@
178178
z-index: 6;
179179
position: absolute;
180180
}
181+
182+
.xterm-decoration-overview-ruler {
183+
z-index: 7;
184+
position: absolute;
185+
top: 0;
186+
right: 0;
187+
}

demo/client.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ if (document.location.pathname === '/test') {
151151
document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler);
152152
document.getElementById('load-test').addEventListener('click', loadTest);
153153
document.getElementById('add-decoration').addEventListener('click', addDecoration);
154+
document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler);
154155
}
155156

156157
function createTerminal(): void {
@@ -544,9 +545,19 @@ function loadTest() {
544545
}
545546

546547
function addDecoration() {
548+
term.options['overviewRulerWidth'] = 15;
547549
const marker = term.addMarker(1);
548-
const decoration = term.registerDecoration({ marker });
549-
decoration.onRender(() => {
550-
decoration.element.style.backgroundColor = 'red';
551-
});
550+
const decoration = term.registerDecoration({ marker, overviewRulerOptions: { color: '#ef2929'} });
551+
decoration.onRender((e) => e.style.backgroundColor = '#ef2929');
552+
}
553+
554+
function addOverviewRuler() {
555+
term.options['overviewRulerWidth'] = 15;
556+
term.registerDecoration({marker: term.addMarker(1), overviewRulerOptions: { color: '#ef2929' }});
557+
term.registerDecoration({marker: term.addMarker(3), overviewRulerOptions: { color: '#8ae234' }});
558+
term.registerDecoration({marker: term.addMarker(5), overviewRulerOptions: { color: '#729fcf' }});
559+
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#ef2929', position: 'left' }});
560+
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#8ae234', position: 'center' }});
561+
term.registerDecoration({marker: term.addMarker(7), overviewRulerOptions: { color: '#729fcf', position: 'right' }});
552562
}
563+

demo/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ <h3>Test</h3>
6969
<button id="custom-glyph" title="Write custom box drawing and block element characters to the terminal">Test custom glyphs</button>
7070
<button id="load-test" title="Write several MB of data to simulate a lot of data coming from the process">Load test</button>
7171
<button id="add-decoration" title="Add a decoration to the terminal">Decoration</button>
72+
<button id="add-overview-ruler" title="Add an overview ruler to the terminal">Add Overview Ruler</button>
7273
</div>
7374
</div>
7475
</div>

0 commit comments

Comments
 (0)