Skip to content

Commit db925ad

Browse files
authored
Merge branch 'master' into di-enhancement
2 parents a1b438c + ce095b3 commit db925ad

File tree

21 files changed

+173
-108
lines changed

21 files changed

+173
-108
lines changed

.vscode/settings.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22
"files.associations": {
33
".eslintrc.json.typings": "jsonc"
44
},
5-
// Hide output files from the file explorer, comment this out to see the build output
65
"files.exclude": {
7-
"**/.nyc_output": true,
8-
"**/lib": true,
9-
"**/dist": true,
10-
"**/out": true,
11-
"**/out-*": true,
6+
"**/.nyc_output": true
127
},
138
"typescript.preferences.importModuleSpecifier": "non-relative",
149
"typescript.preferences.quoteStyle": "single",

addons/addon-ligatures/src/LigaturesAddon.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,21 @@ export class LigaturesAddon implements ITerminalAddon , ILigaturesApi {
3131
}
3232

3333
public activate(terminal: Terminal): void {
34+
if (!terminal.element) {
35+
throw new Error('Cannot activate LigaturesAddon before open is called');
36+
}
3437
this._terminal = terminal;
3538
this._characterJoinerId = enableLigatures(terminal, this._fallbackLigatures);
39+
terminal.element.style.fontFeatureSettings = '"liga" on, "calt" on';
3640
}
3741

3842
public dispose(): void {
3943
if (this._characterJoinerId !== undefined) {
4044
this._terminal?.deregisterCharacterJoiner(this._characterJoinerId);
4145
this._characterJoinerId = undefined;
4246
}
47+
if (this._terminal?.element) {
48+
this._terminal.element.style.fontFeatureSettings = '';
49+
}
4350
}
4451
}

addons/addon-ligatures/webpack.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ module.exports = {
3131
},
3232
mode: 'production',
3333
externals: {
34-
'fs': 'fs',
35-
'path': 'path',
36-
'stream': 'stream',
37-
'util': 'util'
34+
'fs': 'commonjs fs',
35+
'path': 'commonjs path',
36+
'stream': 'commonjs stream',
37+
'util': 'commonjs util'
3838
},
3939
resolve: {
4040
// The ligature modules contains fallbacks for node environments, we never want to browserify them

addons/addon-webgl/src/WebglRenderer.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -314,14 +314,6 @@ export class WebglRenderer extends Disposable implements IRenderer {
314314
this._updateCursorBlink();
315315
}
316316

317-
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
318-
return -1;
319-
}
320-
321-
public deregisterCharacterJoiner(joinerId: number): boolean {
322-
return false;
323-
}
324-
325317
public renderRows(start: number, end: number): void {
326318
if (!this._isAttached) {
327319
if (this._coreBrowserService.window.document.body.contains(this._core.screenElement!) && this._charSizeService.width && this._charSizeService.height) {
@@ -362,7 +354,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
362354
}
363355

364356
private _updateCursorBlink(): void {
365-
if (this._terminal.options.cursorBlink) {
357+
if (this._coreService.decPrivateModes.cursorBlink ?? this._terminal.options.cursorBlink) {
366358
this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => {
367359
this._requestRedrawCursor();
368360
}, this._coreBrowserService);
@@ -395,6 +387,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
395387
let j: number;
396388
start = clamp(start, terminal.rows - 1, 0);
397389
end = clamp(end, terminal.rows - 1, 0);
390+
const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? terminal.options.cursorStyle ?? 'block';
398391

399392
const cursorY = this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY;
400393
const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp;
@@ -458,18 +451,18 @@ export class WebglRenderer extends Disposable implements IRenderer {
458451
x: cursorX,
459452
y: viewportRelativeCursorY,
460453
width: cell.getWidth(),
461-
style: this._coreBrowserService.isFocused ?
462-
(terminal.options.cursorStyle || 'block') : terminal.options.cursorInactiveStyle,
454+
style: this._coreBrowserService.isFocused ? cursorStyle : terminal.options.cursorInactiveStyle,
463455
cursorWidth: terminal.options.cursorWidth,
464456
dpr: this._devicePixelRatio
465457
};
466458
lastCursorX = cursorX + cell.getWidth() - 1;
467459
}
468460
if (x >= cursorX && x <= lastCursorX &&
469461
((this._coreBrowserService.isFocused &&
470-
(terminal.options.cursorStyle || 'block') === 'block') ||
462+
cursorStyle === 'block') ||
471463
(this._coreBrowserService.isFocused === false &&
472-
terminal.options.cursorInactiveStyle === 'block'))) {
464+
terminal.options.cursorInactiveStyle === 'block'))
465+
) {
473466
this._cellColorResolver.result.fg =
474467
Attributes.CM_RGB | (this._themeService.colors.cursorAccent.rgba >> 8 & Attributes.RGB_MASK);
475468
this._cellColorResolver.result.bg =
@@ -510,14 +503,17 @@ export class WebglRenderer extends Disposable implements IRenderer {
510503
cell = this._workCell;
511504

512505
// Null out non-first cells
513-
for (x++; x < lastCharX; x++) {
506+
for (x++; x <= lastCharX; x++) {
514507
j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
515508
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
516509
this._model.cells[j] = NULL_CELL_CODE;
510+
// Don't re-resolve the cell color since multi-colored ligature backgrounds are not
511+
// supported
517512
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
518513
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
519514
this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
520515
}
516+
x--; // Go back to the previous update cell for next iteration
521517
}
522518
}
523519
}

demo/client.ts

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ if ('WebAssembly' in window) {
1616
ImageAddon = imageAddon.ImageAddon;
1717
}
1818

19-
import { Terminal, ITerminalOptions, type IDisposable } from '@xterm/xterm';
19+
import { Terminal, ITerminalOptions, type IDisposable, type ITheme } from '@xterm/xterm';
2020
import { AttachAddon } from '@xterm/addon-attach';
2121
import { ClipboardAddon } from '@xterm/addon-clipboard';
2222
import { FitAddon } from '@xterm/addon-fit';
@@ -131,7 +131,7 @@ const xtermjsTheme = {
131131
brightCyan: '#72F0FF',
132132
white: '#F8F8F8',
133133
brightWhite: '#FFFFFF'
134-
};
134+
} satisfies ITheme;
135135
function setPadding(): void {
136136
term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px';
137137
addons.fit.instance.fit();
@@ -366,14 +366,19 @@ function createTerminal(): void {
366366
// Set terminal size again to set the specific dimensions on the demo
367367
updateTerminalSize();
368368

369-
const res = await fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, { method: 'POST' });
370-
const processId = await res.text();
371-
pid = processId;
372-
socketURL += processId;
373-
socket = new WebSocket(socketURL);
374-
socket.onopen = runRealTerminal;
375-
socket.onclose = runFakeTerminal;
376-
socket.onerror = runFakeTerminal;
369+
const useRealTerminal = document.getElementById('use-real-terminal');
370+
if (useRealTerminal instanceof HTMLInputElement && !useRealTerminal.checked) {
371+
runFakeTerminal();
372+
} else {
373+
const res = await fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, { method: 'POST' });
374+
const processId = await res.text();
375+
pid = processId;
376+
socketURL += processId;
377+
socket = new WebSocket(socketURL);
378+
socket.onopen = runRealTerminal;
379+
socket.onclose = runFakeTerminal;
380+
socket.onerror = runFakeTerminal;
381+
}
377382
}, 0);
378383
}
379384

@@ -1261,9 +1266,9 @@ function addVtButtons(): void {
12611266

12621267
const element = document.createElement('button');
12631268
element.textContent = name;
1264-
writeCsi.split('');
1265-
const prefix = writeCsi.length === 2 ? writeCsi[0] : '';
1266-
const suffix = writeCsi[writeCsi.length - 1];
1269+
const writeCsiSplit = writeCsi.split('|');
1270+
const prefix = writeCsiSplit.length === 2 ? writeCsiSplit[0] : '';
1271+
const suffix = writeCsiSplit[writeCsiSplit.length - 1];
12671272
element.addEventListener(`click`, () => term.write(csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`)));
12681273

12691274
const desc = document.createElement('span');
@@ -1276,22 +1281,23 @@ function addVtButtons(): void {
12761281
}
12771282
const vtFragment = document.createDocumentFragment();
12781283
const buttonSpecs: { [key: string]: { label: string, description: string, paramCount?: number }} = {
1279-
A: { label: 'CUU ↑', description: 'Cursor Up Ps Times' },
1280-
B: { label: 'CUD ↓', description: 'Cursor Down Ps Times' },
1281-
C: { label: 'CUF →', description: 'Cursor Forward Ps Times' },
1282-
D: { label: 'CUB ←', description: 'Cursor Backward Ps Times' },
1283-
E: { label: 'CNL', description: 'Cursor Next Line Ps Times' },
1284-
F: { label: 'CPL', description: 'Cursor Preceding Line Ps Times' },
1285-
G: { label: 'CHA', description: 'Cursor Character Absolute' },
1286-
H: { label: 'CUP', description: 'Cursor Position [row;column]', paramCount: 2 },
1287-
I: { label: 'CHT', description: 'Cursor Forward Tabulation Ps tab stops' },
1288-
J: { label: 'ED', description: 'Erase in Display' },
1289-
'?J': { label: 'DECSED', description: 'Erase in Display' },
1290-
K: { label: 'EL', description: 'Erase in Line' },
1291-
'?K': { label: 'DECSEL', description: 'Erase in Line' },
1292-
L: { label: 'IL', description: 'Insert Ps Line(s)' },
1293-
M: { label: 'DL', description: 'Delete Ps Line(s)' },
1294-
P: { label: 'DCH', description: 'Delete Ps Character(s)' }
1284+
A: { label: 'CUU ↑', description: 'Cursor Up Ps Times' },
1285+
B: { label: 'CUD ↓', description: 'Cursor Down Ps Times' },
1286+
C: { label: 'CUF →', description: 'Cursor Forward Ps Times' },
1287+
D: { label: 'CUB ←', description: 'Cursor Backward Ps Times' },
1288+
E: { label: 'CNL', description: 'Cursor Next Line Ps Times' },
1289+
F: { label: 'CPL', description: 'Cursor Preceding Line Ps Times' },
1290+
G: { label: 'CHA', description: 'Cursor Character Absolute' },
1291+
H: { label: 'CUP', description: 'Cursor Position [row;column]', paramCount: 2 },
1292+
I: { label: 'CHT', description: 'Cursor Forward Tabulation Ps tab stops' },
1293+
J: { label: 'ED', description: 'Erase in Display' },
1294+
'?|J': { label: 'DECSED', description: 'Erase in Display' },
1295+
K: { label: 'EL', description: 'Erase in Line' },
1296+
'?|K': { label: 'DECSEL', description: 'Erase in Line' },
1297+
L: { label: 'IL', description: 'Insert Ps Line(s)' },
1298+
M: { label: 'DL', description: 'Delete Ps Line(s)' },
1299+
P: { label: 'DCH', description: 'Delete Ps Character(s)' },
1300+
' q': { label: 'DECSCUSR', description: 'Set Cursor Style', paramCount: 1 }
12951301
};
12961302
for (const s of Object.keys(buttonSpecs)) {
12971303
const spec = buttonSpecs[s];

demo/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ <h3>Test</h3>
8181
<div style="display: inline-block; margin-right: 16px;">
8282
<dl>
8383
<dt>Lifecycle</dt>
84+
<dd><label for="use-real-terminal"><input type="checkbox" checked id="use-real-terminal" title="This is used to real vs fake terminals" />Use real terminal</label></dd>
8485
<dd><button id="dispose" title="This is used to testing memory leaks">Dispose terminal</button></dd>
8586
<dd><button id="create-new-window" title="This is used to test rendering in other windows">Create terminal in new window</button></dd>
8687

src/browser/CoreBrowserTerminal.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
437437
this.screenElement.appendChild(this._helperContainer);
438438
fragment.appendChild(this.screenElement);
439439

440-
this.textarea = this._document.createElement('textarea');
440+
const textarea = this.textarea = this._document.createElement('textarea');
441441
this.textarea.classList.add('xterm-helper-textarea');
442442
this.textarea.setAttribute('aria-label', Strings.promptLabel.get());
443443
if (!Browser.isChromeOS) {
@@ -449,6 +449,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
449449
this.textarea.setAttribute('autocapitalize', 'off');
450450
this.textarea.setAttribute('spellcheck', 'false');
451451
this.textarea.tabIndex = 0;
452+
this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin));
453+
this.textarea.readOnly = this.optionsService.rawOptions.disableStdin;
452454

453455
// Register the core browser service before the generic textarea handlers are registered so it
454456
// handles them first. Otherwise the renderers may use the wrong focus state.

src/browser/renderer/dom/DomRenderer.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/se
1313
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
1414
import { color } from 'common/Color';
1515
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
16-
import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services';
16+
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
1717
import { Emitter } from 'vs/base/common/event';
1818

1919

@@ -59,6 +59,7 @@ export class DomRenderer extends Disposable implements IRenderer {
5959
@ICharSizeService private readonly _charSizeService: ICharSizeService,
6060
@IOptionsService private readonly _optionsService: IOptionsService,
6161
@IBufferService private readonly _bufferService: IBufferService,
62+
@ICoreService private readonly _coreService: ICoreService,
6263
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
6364
@IThemeService private readonly _themeService: IThemeService
6465
) {
@@ -161,6 +162,10 @@ export class DomRenderer extends Disposable implements IRenderer {
161162
// Base CSS
162163
let styles =
163164
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
165+
// Disabling pointer events circumvents a browser behavior that prevents `click` events from
166+
// being delivered if the target element is replaced during the click. This happened due to
167+
// refresh() being called during the mousedown handler to start a selection.
168+
` pointer-events: none;` +
164169
` color: ${colors.foreground.css};` +
165170
` font-family: ${this._optionsService.rawOptions.fontFamily};` +
166171
` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
@@ -437,8 +442,8 @@ export class DomRenderer extends Disposable implements IRenderer {
437442
const buffer = this._bufferService.buffer;
438443
const cursorAbsoluteY = buffer.ybase + buffer.y;
439444
const cursorX = Math.min(buffer.x, this._bufferService.cols - 1);
440-
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
441-
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
445+
const cursorBlink = this._coreService.decPrivateModes.cursorBlink ?? this._optionsService.rawOptions.cursorBlink;
446+
const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? this._optionsService.rawOptions.cursorStyle;
442447
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
443448

444449
for (let y = start; y <= end; y++) {

src/browser/services/ThemeService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ export class ThemeService extends Disposable implements IThemeService {
8282
const colors = this._colors;
8383
colors.foreground = parseColor(theme.foreground, DEFAULT_FOREGROUND);
8484
colors.background = parseColor(theme.background, DEFAULT_BACKGROUND);
85-
colors.cursor = parseColor(theme.cursor, DEFAULT_CURSOR);
86-
colors.cursorAccent = parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT);
85+
colors.cursor = color.blend(colors.background, parseColor(theme.cursor, DEFAULT_CURSOR));
86+
colors.cursorAccent = color.blend(colors.background, parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT));
8787
colors.selectionBackgroundTransparent = parseColor(theme.selectionBackground, DEFAULT_SELECTION);
8888
colors.selectionBackgroundOpaque = color.blend(colors.background, colors.selectionBackgroundTransparent);
8989
colors.selectionInactiveBackgroundTransparent = parseColor(theme.selectionInactiveBackground, colors.selectionBackgroundTransparent);

src/common/InputHandler.test.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -202,38 +202,38 @@ describe('InputHandler', () => {
202202
describe('setCursorStyle', () => {
203203
it('should call Terminal.setOption with correct params', () => {
204204
inputHandler.setCursorStyle(Params.fromArray([0]));
205-
assert.equal(optionsService.options['cursorStyle'], 'block');
206-
assert.equal(optionsService.options['cursorBlink'], true);
205+
assert.equal(coreService.decPrivateModes.cursorStyle, undefined);
206+
assert.equal(coreService.decPrivateModes.cursorBlink, undefined);
207207

208208
optionsService.options = clone(DEFAULT_OPTIONS);
209209
inputHandler.setCursorStyle(Params.fromArray([1]));
210-
assert.equal(optionsService.options['cursorStyle'], 'block');
211-
assert.equal(optionsService.options['cursorBlink'], true);
210+
assert.equal(coreService.decPrivateModes.cursorStyle, 'block');
211+
assert.equal(coreService.decPrivateModes.cursorBlink, true);
212212

213213
optionsService.options = clone(DEFAULT_OPTIONS);
214214
inputHandler.setCursorStyle(Params.fromArray([2]));
215-
assert.equal(optionsService.options['cursorStyle'], 'block');
216-
assert.equal(optionsService.options['cursorBlink'], false);
215+
assert.equal(coreService.decPrivateModes.cursorStyle, 'block');
216+
assert.equal(coreService.decPrivateModes.cursorBlink, false);
217217

218218
optionsService.options = clone(DEFAULT_OPTIONS);
219219
inputHandler.setCursorStyle(Params.fromArray([3]));
220-
assert.equal(optionsService.options['cursorStyle'], 'underline');
221-
assert.equal(optionsService.options['cursorBlink'], true);
220+
assert.equal(coreService.decPrivateModes.cursorStyle, 'underline');
221+
assert.equal(coreService.decPrivateModes.cursorBlink, true);
222222

223223
optionsService.options = clone(DEFAULT_OPTIONS);
224224
inputHandler.setCursorStyle(Params.fromArray([4]));
225-
assert.equal(optionsService.options['cursorStyle'], 'underline');
226-
assert.equal(optionsService.options['cursorBlink'], false);
225+
assert.equal(coreService.decPrivateModes.cursorStyle, 'underline');
226+
assert.equal(coreService.decPrivateModes.cursorBlink, false);
227227

228228
optionsService.options = clone(DEFAULT_OPTIONS);
229229
inputHandler.setCursorStyle(Params.fromArray([5]));
230-
assert.equal(optionsService.options['cursorStyle'], 'bar');
231-
assert.equal(optionsService.options['cursorBlink'], true);
230+
assert.equal(coreService.decPrivateModes.cursorStyle, 'bar');
231+
assert.equal(coreService.decPrivateModes.cursorBlink, true);
232232

233233
optionsService.options = clone(DEFAULT_OPTIONS);
234234
inputHandler.setCursorStyle(Params.fromArray([6]));
235-
assert.equal(optionsService.options['cursorStyle'], 'bar');
236-
assert.equal(optionsService.options['cursorBlink'], false);
235+
assert.equal(coreService.decPrivateModes.cursorStyle, 'bar');
236+
assert.equal(coreService.decPrivateModes.cursorBlink, false);
237237
});
238238
});
239239
describe('setMode', () => {

0 commit comments

Comments
 (0)