mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #3285 from Tyriar/dom_ligatures
Support for ligatures in DOM renderer
This commit is contained in:
@@ -36,7 +36,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.xterm {
|
.xterm {
|
||||||
font-feature-settings: "liga" 0;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
|
|||||||
+2
-1
@@ -157,7 +157,8 @@ function createTerminal(): void {
|
|||||||
|
|
||||||
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0;
|
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0;
|
||||||
term = new Terminal({
|
term = new Terminal({
|
||||||
windowsMode: isWindows
|
windowsMode: isWindows,
|
||||||
|
fontFamily: 'Fira Code, courier-new, courier, monospace'
|
||||||
} as ITerminalOptions);
|
} as ITerminalOptions);
|
||||||
|
|
||||||
// Load addons
|
// Load addons
|
||||||
|
|||||||
@@ -555,7 +555,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|||||||
|
|
||||||
private _createRenderer(): IRenderer {
|
private _createRenderer(): IRenderer {
|
||||||
switch (this.options.rendererType) {
|
switch (this.options.rendererType) {
|
||||||
case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager!.colors, this.screenElement!, this.linkifier, this.linkifier2, this._instantiationService);
|
case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager!.colors, this.screenElement!, this.linkifier, this.linkifier2);
|
||||||
case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier, this.linkifier2);
|
case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier, this.linkifier2);
|
||||||
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
|
import { IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
|
||||||
import { IEvent, EventEmitter } from 'common/EventEmitter';
|
import { IEvent, EventEmitter } from 'common/EventEmitter';
|
||||||
import { ICharSizeService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services';
|
import { ICharacterJoinerService, ICharSizeService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services';
|
||||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/Types';
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/Types';
|
||||||
import { IColorSet, ILinkMatcherOptions, ITerminal, ILinkifier, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper, CharacterJoinerHandler } from 'browser/Types';
|
import { IColorSet, ILinkMatcherOptions, ITerminal, ILinkifier, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper, CharacterJoinerHandler } from 'browser/Types';
|
||||||
import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
|
import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
|
||||||
@@ -411,3 +411,16 @@ export class MockRenderService implements IRenderService {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MockCharacterJoinerService implements ICharacterJoinerService {
|
||||||
|
public serviceBrand: undefined;
|
||||||
|
public register(handler: (text: string) => [number, number][]): number {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public deregister(joinerId: number): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public getJoinedCharacters(row: number): [number, number][] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class Renderer extends Disposable implements IRenderer {
|
|||||||
private readonly _screenElement: HTMLElement,
|
private readonly _screenElement: HTMLElement,
|
||||||
linkifier: ILinkifier,
|
linkifier: ILinkifier,
|
||||||
linkifier2: ILinkifier2,
|
linkifier2: ILinkifier2,
|
||||||
instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@IBufferService private readonly _bufferService: IBufferService,
|
@IBufferService private readonly _bufferService: IBufferService,
|
||||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||||
@IOptionsService private readonly _optionsService: IOptionsService
|
@IOptionsService private readonly _optionsService: IOptionsService
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
|||||||
import { Disposable } from 'common/Lifecycle';
|
import { Disposable } from 'common/Lifecycle';
|
||||||
import { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types';
|
import { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types';
|
||||||
import { ICharSizeService } from 'browser/services/Services';
|
import { ICharSizeService } from 'browser/services/Services';
|
||||||
import { IOptionsService, IBufferService } from 'common/services/Services';
|
import { IOptionsService, IBufferService, IInstantiationService } from 'common/services/Services';
|
||||||
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||||
import { color } from 'browser/Color';
|
import { color } from 'browser/Color';
|
||||||
import { removeElementFromParent } from 'browser/Dom';
|
import { removeElementFromParent } from 'browser/Dom';
|
||||||
@@ -49,6 +49,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
private readonly _viewportElement: HTMLElement,
|
private readonly _viewportElement: HTMLElement,
|
||||||
private readonly _linkifier: ILinkifier,
|
private readonly _linkifier: ILinkifier,
|
||||||
private readonly _linkifier2: ILinkifier2,
|
private readonly _linkifier2: ILinkifier2,
|
||||||
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||||
@IBufferService private readonly _bufferService: IBufferService
|
@IBufferService private readonly _bufferService: IBufferService
|
||||||
@@ -80,7 +81,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
this._updateDimensions();
|
this._updateDimensions();
|
||||||
this._injectCss();
|
this._injectCss();
|
||||||
|
|
||||||
this._rowFactory = new DomRendererRowFactory(document, this._optionsService, this._colors);
|
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document, this._colors);
|
||||||
|
|
||||||
this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
||||||
this._screenElement.appendChild(this._rowContainer);
|
this._screenElement.appendChild(this._rowContainer);
|
||||||
@@ -364,7 +365,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
const row = y + this._bufferService.buffer.ydisp;
|
const row = y + this._bufferService.buffer.ydisp;
|
||||||
const lineData = this._bufferService.buffer.lines.get(row);
|
const lineData = this._bufferService.buffer.lines.get(row);
|
||||||
const cursorStyle = this._optionsService.options.cursorStyle;
|
const cursorStyle = this._optionsService.options.cursorStyle;
|
||||||
rowElement.appendChild(this._rowFactory.createRow(lineData!, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols));
|
rowElement.appendChild(this._rowFactory.createRow(lineData!, row, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, this._bufferService.cols));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { IBufferLine } from 'common/Types';
|
|||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
import { MockOptionsService } from 'common/TestUtils.test';
|
import { MockOptionsService } from 'common/TestUtils.test';
|
||||||
import { css } from 'browser/Color';
|
import { css } from 'browser/Color';
|
||||||
|
import { MockCharacterJoinerService } from 'browser/TestUtils.test';
|
||||||
|
|
||||||
describe('DomRendererRowFactory', () => {
|
describe('DomRendererRowFactory', () => {
|
||||||
let dom: jsdom.JSDOM;
|
let dom: jsdom.JSDOM;
|
||||||
@@ -20,7 +21,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dom = new jsdom.JSDOM('');
|
dom = new jsdom.JSDOM('');
|
||||||
rowFactory = new DomRendererRowFactory(dom.window.document, new MockOptionsService({ drawBoldTextInBrightColors: true }), {
|
rowFactory = new DomRendererRowFactory(dom.window.document, {
|
||||||
background: css.toColor('#010101'),
|
background: css.toColor('#010101'),
|
||||||
foreground: css.toColor('#020202'),
|
foreground: css.toColor('#020202'),
|
||||||
ansi: [
|
ansi: [
|
||||||
@@ -43,13 +44,13 @@ describe('DomRendererRowFactory', () => {
|
|||||||
css.toColor('#34e2e2'),
|
css.toColor('#34e2e2'),
|
||||||
css.toColor('#eeeeec')
|
css.toColor('#eeeeec')
|
||||||
]
|
]
|
||||||
} as any);
|
} as any, new MockCharacterJoinerService(), new MockOptionsService({ drawBoldTextInBrightColors: true }));
|
||||||
lineData = createEmptyLineData(2);
|
lineData = createEmptyLineData(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createRow', () => {
|
describe('createRow', () => {
|
||||||
it('should not create anything for an empty row', () => {
|
it('should not create anything for an empty row', () => {
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
@@ -59,7 +60,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, '語', 2, '語'.charCodeAt(0)]));
|
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, '語', 2, '語'.charCodeAt(0)]));
|
||||||
// There should be no element for the following "empty" cell
|
// There should be no element for the following "empty" cell
|
||||||
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '', 0, 0]));
|
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '', 0, 0]));
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span style="width: 10px;">語</span>'
|
'<span style="width: 10px;">語</span>'
|
||||||
);
|
);
|
||||||
@@ -67,7 +68,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
|
|
||||||
it('should add class for cursor and cursor style', () => {
|
it('should add class for cursor and cursor style', () => {
|
||||||
for (const style of ['block', 'bar', 'underline']) {
|
for (const style of ['block', 'bar', 'underline']) {
|
||||||
const fragment = rowFactory.createRow(lineData, true, style, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, true, style, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-cursor xterm-cursor-${style}"> </span>`
|
`<span class="xterm-cursor xterm-cursor-${style}"> </span>`
|
||||||
);
|
);
|
||||||
@@ -75,7 +76,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add class for cursor blink', () => {
|
it('should add class for cursor blink', () => {
|
||||||
const fragment = rowFactory.createRow(lineData, true, 'block', 0, true, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, true, 'block', 0, true, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-cursor xterm-cursor-blink xterm-cursor-block"> </span>`
|
`<span class="xterm-cursor xterm-cursor-blink xterm-cursor-block"> </span>`
|
||||||
);
|
);
|
||||||
@@ -84,7 +85,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
it('should not render cells that go beyond the terminal\'s columns', () => {
|
it('should not render cells that go beyond the terminal\'s columns', () => {
|
||||||
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
||||||
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
|
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 1);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 1);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span>a</span>'
|
'<span>a</span>'
|
||||||
);
|
);
|
||||||
@@ -95,7 +96,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.BOLD;
|
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.BOLD;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-bold">a</span>'
|
'<span class="xterm-bold">a</span>'
|
||||||
);
|
);
|
||||||
@@ -105,7 +106,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.ITALIC;
|
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.ITALIC;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-italic">a</span>'
|
'<span class="xterm-italic">a</span>'
|
||||||
);
|
);
|
||||||
@@ -115,7 +116,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.DIM;
|
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.DIM;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-dim">a</span>'
|
'<span class="xterm-dim">a</span>'
|
||||||
);
|
);
|
||||||
@@ -125,7 +126,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
|
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-underline">a</span>'
|
'<span class="xterm-underline">a</span>'
|
||||||
);
|
);
|
||||||
@@ -138,7 +139,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
cell.fg &= ~Attributes.PCOLOR_MASK;
|
cell.fg &= ~Attributes.PCOLOR_MASK;
|
||||||
cell.fg |= i;
|
cell.fg |= i;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-fg-${i}">a</span>`
|
`<span class="xterm-fg-${i}">a</span>`
|
||||||
);
|
);
|
||||||
@@ -152,7 +153,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
cell.bg &= ~Attributes.PCOLOR_MASK;
|
cell.bg &= ~Attributes.PCOLOR_MASK;
|
||||||
cell.bg |= i;
|
cell.bg |= i;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-bg-${i}">a</span>`
|
`<span class="xterm-bg-${i}">a</span>`
|
||||||
);
|
);
|
||||||
@@ -164,7 +165,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
cell.fg |= Attributes.CM_P16 | 2 | FgFlags.INVERSE;
|
cell.fg |= Attributes.CM_P16 | 2 | FgFlags.INVERSE;
|
||||||
cell.bg |= Attributes.CM_P16 | 1;
|
cell.bg |= Attributes.CM_P16 | 1;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-fg-1 xterm-bg-2">a</span>'
|
'<span class="xterm-fg-1 xterm-bg-2">a</span>'
|
||||||
);
|
);
|
||||||
@@ -175,7 +176,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
cell.fg |= FgFlags.INVERSE;
|
cell.fg |= FgFlags.INVERSE;
|
||||||
cell.bg |= Attributes.CM_P16 | 1;
|
cell.bg |= Attributes.CM_P16 | 1;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-fg-1 xterm-bg-257">a</span>'
|
'<span class="xterm-fg-1 xterm-bg-257">a</span>'
|
||||||
);
|
);
|
||||||
@@ -185,7 +186,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
cell.fg |= Attributes.CM_P16 | 1 | FgFlags.INVERSE;
|
cell.fg |= Attributes.CM_P16 | 1 | FgFlags.INVERSE;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-fg-257 xterm-bg-1">a</span>'
|
'<span class="xterm-fg-257 xterm-bg-1">a</span>'
|
||||||
);
|
);
|
||||||
@@ -198,7 +199,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
cell.fg &= ~Attributes.PCOLOR_MASK;
|
cell.fg &= ~Attributes.PCOLOR_MASK;
|
||||||
cell.fg |= i;
|
cell.fg |= i;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-bold xterm-fg-${i + 8}">a</span>`
|
`<span class="xterm-bold xterm-fg-${i + 8}">a</span>`
|
||||||
);
|
);
|
||||||
@@ -210,7 +211,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3;
|
cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3;
|
||||||
cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6;
|
cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span style="color:#010203;background-color:#040506;">a</span>'
|
'<span style="color:#010203;background-color:#040506;">a</span>'
|
||||||
);
|
);
|
||||||
@@ -221,7 +222,7 @@ describe('DomRendererRowFactory', () => {
|
|||||||
cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3 | FgFlags.INVERSE;
|
cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3 | FgFlags.INVERSE;
|
||||||
cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6;
|
cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6;
|
||||||
lineData.setCell(0, cell);
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, false, 5, 20);
|
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span style="color:#040506;background-color:#010203;">a</span>'
|
'<span style="color:#040506;background-color:#010203;">a</span>'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { CellData } from 'common/buffer/CellData';
|
|||||||
import { IOptionsService } from 'common/services/Services';
|
import { IOptionsService } from 'common/services/Services';
|
||||||
import { color, rgba } from 'browser/Color';
|
import { color, rgba } from 'browser/Color';
|
||||||
import { IColorSet, IColor } from 'browser/Types';
|
import { IColorSet, IColor } from 'browser/Types';
|
||||||
|
import { ICharacterJoinerService } from 'browser/services/Services';
|
||||||
|
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
||||||
|
|
||||||
export const BOLD_CLASS = 'xterm-bold';
|
export const BOLD_CLASS = 'xterm-bold';
|
||||||
export const DIM_CLASS = 'xterm-dim';
|
export const DIM_CLASS = 'xterm-dim';
|
||||||
@@ -26,8 +28,9 @@ export class DomRendererRowFactory {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _document: Document,
|
private readonly _document: Document,
|
||||||
private readonly _optionsService: IOptionsService,
|
private _colors: IColorSet,
|
||||||
private _colors: IColorSet
|
@ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService,
|
||||||
|
@IOptionsService private readonly _optionsService: IOptionsService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +38,11 @@ export class DomRendererRowFactory {
|
|||||||
this._colors = colors;
|
this._colors = colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createRow(lineData: IBufferLine, isCursorRow: boolean, cursorStyle: string | undefined, cursorX: number, cursorBlink: boolean, cellWidth: number, cols: number): DocumentFragment {
|
public createRow(lineData: IBufferLine, row: number, isCursorRow: boolean, cursorStyle: string | undefined, cursorX: number, cursorBlink: boolean, cellWidth: number, cols: number): DocumentFragment {
|
||||||
const fragment = this._document.createDocumentFragment();
|
const fragment = this._document.createDocumentFragment();
|
||||||
|
|
||||||
|
const joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
|
||||||
|
console.log('joinedRanges', joinedRanges.map(e => e[0] + '->' + e[1]).join(','));
|
||||||
// Find the line length first, this prevents the need to output a bunch of
|
// Find the line length first, this prevents the need to output a bunch of
|
||||||
// empty cells at the end. This cannot easily be integrated into the main
|
// empty cells at the end. This cannot easily be integrated into the main
|
||||||
// loop below because of the colCount feature (which can be removed after we
|
// loop below because of the colCount feature (which can be removed after we
|
||||||
@@ -53,18 +58,59 @@ export class DomRendererRowFactory {
|
|||||||
|
|
||||||
for (let x = 0; x < lineLength; x++) {
|
for (let x = 0; x < lineLength; x++) {
|
||||||
lineData.loadCell(x, this._workCell);
|
lineData.loadCell(x, this._workCell);
|
||||||
const width = this._workCell.getWidth();
|
let width = this._workCell.getWidth();
|
||||||
|
|
||||||
// The character to the left is a wide character, drawing is owned by the char at x-1
|
// The character to the left is a wide character, drawing is owned by the char at x-1
|
||||||
if (width === 0) {
|
if (width === 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If true, indicates that the current character(s) to draw were joined.
|
||||||
|
let isJoined = false;
|
||||||
|
let lastCharX = x;
|
||||||
|
|
||||||
|
// Process any joined character ranges as needed. Because of how the
|
||||||
|
// ranges are produced, we know that they are valid for the characters
|
||||||
|
// and attributes of our input.
|
||||||
|
let cell = this._workCell;
|
||||||
|
if (joinedRanges.length > 0 && x === joinedRanges[0][0]) {
|
||||||
|
isJoined = true;
|
||||||
|
const range = joinedRanges.shift()!;
|
||||||
|
|
||||||
|
// We already know the exact start and end column of the joined range,
|
||||||
|
// so we get the string and width representing it directly
|
||||||
|
|
||||||
|
cell = new JoinedCellData(
|
||||||
|
this._workCell,
|
||||||
|
lineData.translateToString(true, range[0], range[1]),
|
||||||
|
range[1] - range[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Skip over the cells occupied by this range in the loop
|
||||||
|
lastCharX = range[1] - 1;
|
||||||
|
|
||||||
|
// Recalculate width
|
||||||
|
width = cell.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
const charElement = this._document.createElement('span');
|
const charElement = this._document.createElement('span');
|
||||||
if (width > 1) {
|
if (width > 1) {
|
||||||
charElement.style.width = `${cellWidth * width}px`;
|
charElement.style.width = `${cellWidth * width}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isJoined) {
|
||||||
|
// Ligatures in the DOM renderer must use display inline, as they may not show with
|
||||||
|
// inline-block if they are outside the bounds of the element
|
||||||
|
charElement.style.display = 'inline';
|
||||||
|
|
||||||
|
// The DOM renderer colors the background of the cursor but for ligatures all cells are
|
||||||
|
// joined. The workaround here is to show a cursor around the whole ligature so it shows up,
|
||||||
|
// the cursor looks the same when on any character of the ligature though
|
||||||
|
if (cursorX >= x && cursorX <= lastCharX) {
|
||||||
|
cursorX = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isCursorRow && x === cursorX) {
|
if (isCursorRow && x === cursorX) {
|
||||||
charElement.classList.add(CURSOR_CLASS);
|
charElement.classList.add(CURSOR_CLASS);
|
||||||
|
|
||||||
@@ -85,33 +131,33 @@ export class DomRendererRowFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._workCell.isBold()) {
|
if (cell.isBold()) {
|
||||||
charElement.classList.add(BOLD_CLASS);
|
charElement.classList.add(BOLD_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._workCell.isItalic()) {
|
if (cell.isItalic()) {
|
||||||
charElement.classList.add(ITALIC_CLASS);
|
charElement.classList.add(ITALIC_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._workCell.isDim()) {
|
if (cell.isDim()) {
|
||||||
charElement.classList.add(DIM_CLASS);
|
charElement.classList.add(DIM_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._workCell.isUnderline()) {
|
if (cell.isUnderline()) {
|
||||||
charElement.classList.add(UNDERLINE_CLASS);
|
charElement.classList.add(UNDERLINE_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._workCell.isInvisible()) {
|
if (cell.isInvisible()) {
|
||||||
charElement.textContent = WHITESPACE_CELL_CHAR;
|
charElement.textContent = WHITESPACE_CELL_CHAR;
|
||||||
} else {
|
} else {
|
||||||
charElement.textContent = this._workCell.getChars() || WHITESPACE_CELL_CHAR;
|
charElement.textContent = cell.getChars() || WHITESPACE_CELL_CHAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fg = this._workCell.getFgColor();
|
let fg = cell.getFgColor();
|
||||||
let fgColorMode = this._workCell.getFgColorMode();
|
let fgColorMode = cell.getFgColorMode();
|
||||||
let bg = this._workCell.getBgColor();
|
let bg = cell.getBgColor();
|
||||||
let bgColorMode = this._workCell.getBgColorMode();
|
let bgColorMode = cell.getBgColorMode();
|
||||||
const isInverse = !!this._workCell.isInverse();
|
const isInverse = !!cell.isInverse();
|
||||||
if (isInverse) {
|
if (isInverse) {
|
||||||
const temp = fg;
|
const temp = fg;
|
||||||
fg = bg;
|
fg = bg;
|
||||||
@@ -125,7 +171,7 @@ export class DomRendererRowFactory {
|
|||||||
switch (fgColorMode) {
|
switch (fgColorMode) {
|
||||||
case Attributes.CM_P16:
|
case Attributes.CM_P16:
|
||||||
case Attributes.CM_P256:
|
case Attributes.CM_P256:
|
||||||
if (this._workCell.isBold() && fg < 8 && this._optionsService.options.drawBoldTextInBrightColors) {
|
if (cell.isBold() && fg < 8 && this._optionsService.options.drawBoldTextInBrightColors) {
|
||||||
fg += 8;
|
fg += 8;
|
||||||
}
|
}
|
||||||
if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg])) {
|
if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg])) {
|
||||||
@@ -168,6 +214,8 @@ export class DomRendererRowFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fragment.appendChild(charElement);
|
fragment.appendChild(charElement);
|
||||||
|
|
||||||
|
x = lastCharX;
|
||||||
}
|
}
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export class ServiceCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class InstantiationService implements IInstantiationService {
|
export class InstantiationService implements IInstantiationService {
|
||||||
|
public serviceBrand: undefined;
|
||||||
|
|
||||||
private readonly _services: ServiceCollection = new ServiceCollection();
|
private readonly _services: ServiceCollection = new ServiceCollection();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ type GetLeadingNonServiceArgs<Args> =
|
|||||||
|
|
||||||
export const IInstantiationService = createDecorator<IInstantiationService>('InstantiationService');
|
export const IInstantiationService = createDecorator<IInstantiationService>('InstantiationService');
|
||||||
export interface IInstantiationService {
|
export interface IInstantiationService {
|
||||||
|
serviceBrand: undefined;
|
||||||
|
|
||||||
setService<T>(id: IServiceIdentifier<T>, instance: T): void;
|
setService<T>(id: IServiceIdentifier<T>, instance: T): void;
|
||||||
getService<T>(id: IServiceIdentifier<T>): T | undefined;
|
getService<T>(id: IServiceIdentifier<T>): T | undefined;
|
||||||
createInstance<Ctor extends new (...args: any[]) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
|
createInstance<Ctor extends new (...args: any[]) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
|
||||||
|
|||||||
Reference in New Issue
Block a user