Merge branch 'master' into clusters
This commit is contained in:
+12
-1
@@ -163,6 +163,16 @@
|
||||
"jsdoc/check-param-names": 1,
|
||||
"jsdoc/no-multi-asterisks": 1,
|
||||
"keyword-spacing": "warn",
|
||||
"max-len": [
|
||||
"warn",
|
||||
{
|
||||
"code": 1000, // Don't enforce for code
|
||||
"comments": 100,
|
||||
"ignoreTrailingComments": true,
|
||||
"ignoreUrls": true,
|
||||
"ignorePattern": "^ *((?<vt_comment>(//|\\*) @vt)|(?<table_comment>\\* \\| )|(?<commented_code>// ))"
|
||||
}
|
||||
],
|
||||
"new-parens": "warn",
|
||||
"no-duplicate-imports": "warn",
|
||||
"no-else-return": [
|
||||
@@ -229,7 +239,8 @@
|
||||
{
|
||||
"files": ["**/*.test.ts"],
|
||||
"rules": {
|
||||
"object-curly-spacing": "off"
|
||||
"object-curly-spacing": "off",
|
||||
"max-len": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"jsdoc"
|
||||
],
|
||||
"rules": {
|
||||
"no-extra-semi": "error",
|
||||
"@typescript-eslint/array-type": [
|
||||
"warn",
|
||||
{
|
||||
"default": "array",
|
||||
"readonly": "generic"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"warn",
|
||||
{
|
||||
"allowExpressions": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/indent": [
|
||||
"warn",
|
||||
2
|
||||
],
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"warn",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": true
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "comma",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"warn",
|
||||
{ "selector": "typeLike", "format": ["PascalCase"] },
|
||||
{ "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] }
|
||||
],
|
||||
"@typescript-eslint/prefer-namespace-keyword": "warn",
|
||||
"@typescript-eslint/type-annotation-spacing": "warn",
|
||||
"@typescript-eslint/quotes": [
|
||||
"warn",
|
||||
"single",
|
||||
{ "allowTemplateLiterals": true }
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"comma-dangle": [
|
||||
"warn",
|
||||
{
|
||||
"objects": "never",
|
||||
"arrays": "never",
|
||||
"functions": "never"
|
||||
}
|
||||
],
|
||||
"curly": [
|
||||
"warn",
|
||||
"multi-line"
|
||||
],
|
||||
"eol-last": "warn",
|
||||
"eqeqeq": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"jsdoc/check-alignment": 1,
|
||||
"jsdoc/check-param-names": 1,
|
||||
"keyword-spacing": "warn",
|
||||
"max-len": [
|
||||
"warn",
|
||||
{
|
||||
"code": 1000, // Don't enforce for code
|
||||
"comments": 80,
|
||||
"ignoreUrls": true,
|
||||
"ignorePattern": "^ *(?<ps_description>\\* Ps=)"
|
||||
}
|
||||
],
|
||||
"no-irregular-whitespace": "warn",
|
||||
"no-trailing-spaces": "warn",
|
||||
"object-curly-spacing": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"spaced-comment": [
|
||||
"warn",
|
||||
"always",
|
||||
{
|
||||
"markers": ["/"],
|
||||
"exceptions": ["-"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,8 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._cursorRenderers = {
|
||||
'bar': this._renderBarCursor.bind(this),
|
||||
'block': this._renderBlockCursor.bind(this),
|
||||
'underline': this._renderUnderlineCursor.bind(this)
|
||||
'underline': this._renderUnderlineCursor.bind(this),
|
||||
'outline': this._renderOutlineCursor.bind(this)
|
||||
};
|
||||
this.register(optionsService.onOptionChange(() => this._handleOptionsChanged()));
|
||||
this._handleOptionsChanged();
|
||||
@@ -150,7 +151,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._themeService.colors.cursor.css;
|
||||
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
||||
this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell);
|
||||
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
||||
if (cursorInactiveStyle && cursorInactiveStyle !== 'none') {
|
||||
this._cursorRenderers[cursorInactiveStyle](cursorX, viewportRelativeCursorY, this._cell);
|
||||
}
|
||||
this._ctx.restore();
|
||||
this._state.x = cursorX;
|
||||
this._state.y = viewportRelativeCursorY;
|
||||
@@ -231,7 +235,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._ctx.restore();
|
||||
}
|
||||
|
||||
private _renderBlurCursor(x: number, y: number, cell: ICellData): void {
|
||||
private _renderOutlineCursor(x: number, y: number, cell: ICellData): void {
|
||||
this._ctx.save();
|
||||
this._ctx.strokeStyle = this._themeService.colors.cursor.css;
|
||||
this._strokeRectAtCell(x, y, cell.getWidth(), 1);
|
||||
|
||||
@@ -23,7 +23,8 @@ export interface IHeaderFields {
|
||||
height?: string;
|
||||
// Optional, defaults to 1 respecting aspect ratio (width takes precedence).
|
||||
preserveAspectRatio?: number;
|
||||
// Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded (download not supported).
|
||||
// Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded
|
||||
// (download not supported).
|
||||
inline?: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,6 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
private _highlightDecorations: IHighlight[] = [];
|
||||
private _selectedDecoration: IHighlight | undefined;
|
||||
private _highlightLimit: number;
|
||||
private _onDataDisposable: IDisposable | undefined;
|
||||
private _onResizeDisposable: IDisposable | undefined;
|
||||
private _lastSearchOptions: ISearchOptions | undefined;
|
||||
private _highlightTimeout: number | undefined;
|
||||
/**
|
||||
@@ -93,13 +91,9 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
|
||||
public activate(terminal: Terminal): void {
|
||||
this._terminal = terminal;
|
||||
this._onDataDisposable = this.register(this._terminal.onWriteParsed(() => this._updateMatches()));
|
||||
this._onResizeDisposable = this.register(this._terminal.onResize(() => this._updateMatches()));
|
||||
this.register(toDisposable(() => {
|
||||
this.clearDecorations();
|
||||
this._onDataDisposable?.dispose();
|
||||
this._onResizeDisposable?.dispose();
|
||||
}));
|
||||
this.register(this._terminal.onWriteParsed(() => this._updateMatches()));
|
||||
this.register(this._terminal.onResize(() => this._updateMatches()));
|
||||
this.register(toDisposable(() => this.clearDecorations()));
|
||||
}
|
||||
|
||||
private _updateMatches(): void {
|
||||
@@ -440,7 +434,8 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
|
||||
/**
|
||||
* A found substring is a whole word if it doesn't have an alphanumeric character directly adjacent to it.
|
||||
* A found substring is a whole word if it doesn't have an alphanumeric character directly
|
||||
* adjacent to it.
|
||||
* @param searchIndex starting indext of the potential whole word substring
|
||||
* @param line entire string in which the potential whole word was found
|
||||
* @param term the substring that starts at searchIndex
|
||||
@@ -451,14 +446,15 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a line for a search term. Takes the provided terminal line and searches the text line, which may contain
|
||||
* subsequent terminal lines if the text is wrapped. If the provided line number is part of a wrapped text line that
|
||||
* started on an earlier line then it is skipped since it will be properly searched when the terminal line that the
|
||||
* text starts on is searched.
|
||||
* Searches a line for a search term. Takes the provided terminal line and searches the text line,
|
||||
* which may contain subsequent terminal lines if the text is wrapped. If the provided line number
|
||||
* is part of a wrapped text line that started on an earlier line then it is skipped since it will
|
||||
* be properly searched when the terminal line that the text starts on is searched.
|
||||
* @param term The search term.
|
||||
* @param searchPosition The position to start the search.
|
||||
* @param searchOptions Search options.
|
||||
* @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left.
|
||||
* @param isReverseSearch Whether the search should start from the right side of the terminal and
|
||||
* search to the left.
|
||||
* @returns The search result if it was found.
|
||||
*/
|
||||
protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
|
||||
@@ -526,7 +522,8 @@ export class SearchAddon extends Disposable implements ITerminalAddon {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the row number and search index if needed since a "line" of text can span multiple rows
|
||||
// Adjust the row number and search index if needed since a "line" of text can span multiple
|
||||
// rows
|
||||
let startRowOffset = 0;
|
||||
while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) {
|
||||
startRowOffset++;
|
||||
|
||||
@@ -131,7 +131,8 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell();
|
||||
private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell();
|
||||
protected _rowEnd(row: number, isLastRow: boolean): void {
|
||||
// if there is colorful empty cell at line end, whe must pad it back, or the the color block will missing
|
||||
// if there is colorful empty cell at line end, whe must pad it back, or the the color block
|
||||
// will missing
|
||||
if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) {
|
||||
// use clear right to set background.
|
||||
this._currentRow += `\u001b[${this._nullCellCount}X`;
|
||||
@@ -292,7 +293,8 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
|
||||
const sgrSeq = this._diffStyle(cell, this._cursorStyle);
|
||||
|
||||
// the empty cell style is only assumed to be changed when background changed, because foreground is always 0.
|
||||
// the empty cell style is only assumed to be changed when background changed, because
|
||||
// foreground is always 0.
|
||||
const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0;
|
||||
|
||||
/**
|
||||
|
||||
@@ -255,7 +255,7 @@ export class RectangleRenderer extends Disposable {
|
||||
let offset: number;
|
||||
let rectangleCount = 0;
|
||||
|
||||
if (cursor.style === 'bar' || cursor.style === 'blur') {
|
||||
if (cursor.style === 'bar' || cursor.style === 'outline') {
|
||||
// Left edge
|
||||
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
||||
this._addRectangleFloat(
|
||||
@@ -268,7 +268,7 @@ export class RectangleRenderer extends Disposable {
|
||||
this._cursorFloat
|
||||
);
|
||||
}
|
||||
if (cursor.style === 'underline' || cursor.style === 'blur') {
|
||||
if (cursor.style === 'underline' || cursor.style === 'outline') {
|
||||
// Bottom edge
|
||||
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
||||
this._addRectangleFloat(
|
||||
@@ -281,7 +281,7 @@ export class RectangleRenderer extends Disposable {
|
||||
this._cursorFloat
|
||||
);
|
||||
}
|
||||
if (cursor.style === 'blur') {
|
||||
if (cursor.style === 'outline') {
|
||||
// Top edge
|
||||
offset = rectangleCount++ * INDICES_PER_RECTANGLE;
|
||||
this._addRectangleFloat(
|
||||
|
||||
+3
-1
@@ -16,11 +16,13 @@ export interface ICursorRenderModel {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
style: string;
|
||||
style: CursorStyle;
|
||||
cursorWidth: number;
|
||||
dpr: number;
|
||||
}
|
||||
|
||||
export type CursorStyle = 'outline' | 'block' | 'bar' | 'underline' | 'none';
|
||||
|
||||
export interface IWebGL2RenderingContext extends WebGLRenderingContext {
|
||||
vertexAttribDivisor(index: number, divisor: number): void;
|
||||
createVertexArray(): IWebGLVertexArrayObject;
|
||||
|
||||
@@ -461,15 +461,17 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
y: this._terminal.buffer.active.cursorY,
|
||||
width: cell.getWidth(),
|
||||
style: this._coreBrowserService.isFocused ?
|
||||
(terminal.options.cursorStyle || 'block') : 'blur',
|
||||
(terminal.options.cursorStyle || 'block') : terminal.options.cursorInactiveStyle,
|
||||
cursorWidth: terminal.options.cursorWidth,
|
||||
dpr: this._devicePixelRatio
|
||||
};
|
||||
lastCursorX = cursorX + cell.getWidth() - 1;
|
||||
}
|
||||
if (x >= cursorX && x <= lastCursorX &&
|
||||
this._coreBrowserService.isFocused &&
|
||||
(terminal.options.cursorStyle || 'block') === 'block') {
|
||||
((this._coreBrowserService.isFocused &&
|
||||
(terminal.options.cursorStyle || 'block') === 'block') ||
|
||||
(this._coreBrowserService.isFocused === false &&
|
||||
terminal.options.cursorInactiveStyle === 'block'))) {
|
||||
this._cellColorResolver.result.fg =
|
||||
Attributes.CM_RGB | (this._themeService.colors.cursorAccent.rgba >> 8 & Attributes.RGB_MASK);
|
||||
this._cellColorResolver.result.bg =
|
||||
|
||||
@@ -789,7 +789,7 @@ describe('WebGL Renderer Integration Tests', async () => {
|
||||
await pollFor(page, () => getCellColor(6, 2), [0xad, 0x7f, 0xa8, 255]);
|
||||
await pollFor(page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]);
|
||||
await pollFor(page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]);
|
||||
// Setting and check for minimum contrast values, note that these are note
|
||||
// Setting and check for minimum contrast values, note that these are not
|
||||
// exact to the contrast ratio, if the increase luminance algorithm
|
||||
// changes then these will probably fail
|
||||
await page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
|
||||
@@ -858,7 +858,7 @@ describe('WebGL Renderer Integration Tests', async () => {
|
||||
await pollFor(page, () => getCellColor(6, 2), [0xad, 0x7f, 0xa8, 255]);
|
||||
await pollFor(page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]);
|
||||
await pollFor(page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]);
|
||||
// Setting and check for minimum contrast values, note that these are note
|
||||
// Setting and check for minimum contrast values, note that these are not
|
||||
// exact to the contrast ratio, if the increase luminance algorithm
|
||||
// changes then these will probably fail
|
||||
await page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
|
||||
@@ -879,6 +879,75 @@ describe('WebGL Renderer Integration Tests', async () => {
|
||||
await pollFor(page, () => getCellColor(7, 2), [13, 67, 67, 255]);
|
||||
await pollFor(page, () => getCellColor(8, 2), [64, 64, 64, 255]);
|
||||
});
|
||||
|
||||
itWebgl('should enforce half the contrast for dim cells', async () => {
|
||||
const theme: ITheme = {
|
||||
background: '#ffffff',
|
||||
black: '#2e3436',
|
||||
red: '#cc0000',
|
||||
green: '#4e9a06',
|
||||
yellow: '#c4a000',
|
||||
blue: '#3465a4',
|
||||
magenta: '#75507b',
|
||||
cyan: '#06989a',
|
||||
white: '#d3d7cf',
|
||||
brightBlack: '#555753',
|
||||
brightRed: '#ef2929',
|
||||
brightGreen: '#8ae234',
|
||||
brightYellow: '#fce94f',
|
||||
brightBlue: '#729fcf',
|
||||
brightMagenta: '#ad7fa8',
|
||||
brightCyan: '#34e2e2',
|
||||
brightWhite: '#eeeeec'
|
||||
};
|
||||
await page.evaluate(`
|
||||
window.term.options.theme = ${JSON.stringify(theme)};
|
||||
window.term.options.minimumContrastRatio = 1;
|
||||
`);
|
||||
// Block characters ignore block elements so a different char is used here
|
||||
await writeSync(page,
|
||||
'\\x1b[2m' +
|
||||
`\\x1b[30m■\\x1b[31m■\\x1b[32m■\\x1b[33m■\\x1b[34m■\\x1b[35m■\\x1b[36m■\\x1b[37m■\\r\\n` +
|
||||
`\\x1b[90m■\\x1b[91m■\\x1b[92m■\\x1b[93m■\\x1b[94m■\\x1b[95m■\\x1b[96m■\\x1b[97m■`
|
||||
);
|
||||
// Validate before minimumContrastRatio is applied
|
||||
await pollFor(page, () => getCellColor(1, 1), [Math.floor((255 + 0x2e) / 2), Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x36) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(2, 1), [Math.floor((255 + 0xcc) / 2), Math.floor((255 + 0x00) / 2), Math.floor((255 + 0x00) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(3, 1), [Math.floor((255 + 0x4e) / 2), Math.floor((255 + 0x9a) / 2), Math.floor((255 + 0x06) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(4, 1), [Math.floor((255 + 0xc4) / 2), Math.floor((255 + 0xa0) / 2), Math.floor((255 + 0x00) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(5, 1), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x65) / 2), Math.floor((255 + 0xa4) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(6, 1), [Math.floor((255 + 0x75) / 2), Math.floor((255 + 0x50) / 2), Math.floor((255 + 0x7b) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(7, 1), [Math.floor((255 + 0x06) / 2), Math.floor((255 + 0x98) / 2), Math.floor((255 + 0x9a) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(8, 1), [Math.floor((255 + 0xd3) / 2), Math.floor((255 + 0xd7) / 2), Math.floor((255 + 0xcf) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(1, 2), [Math.floor((255 + 0x55) / 2), Math.floor((255 + 0x57) / 2), Math.floor((255 + 0x53) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(2, 2), [Math.floor((255 + 0xef) / 2), Math.floor((255 + 0x29) / 2), Math.floor((255 + 0x29) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(3, 2), [Math.floor((255 + 0x8a) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0x34) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(4, 2), [Math.floor((255 + 0xfc) / 2), Math.floor((255 + 0xe9) / 2), Math.floor((255 + 0x4f) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(5, 2), [Math.floor((255 + 0x72) / 2), Math.floor((255 + 0x9f) / 2), Math.floor((255 + 0xcf) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]);
|
||||
await pollFor(page, () => getCellColor(8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]);
|
||||
// Setting and check for minimum contrast values, note that these are not
|
||||
// exact to the contrast ratio, if the increase luminance algorithm
|
||||
// changes then these will probably fail
|
||||
await page.evaluate(`window.term.options.minimumContrastRatio = 10;`);
|
||||
await pollFor(page, () => getCellColor(1, 1), [150, 153, 154, 255]);
|
||||
await pollFor(page, () => getCellColor(2, 1), [229, 127, 127, 255]);
|
||||
await pollFor(page, () => getCellColor(3, 1), [63, 124, 4, 255]);
|
||||
await pollFor(page, () => getCellColor(4, 1), [127, 104, 0, 255]);
|
||||
await pollFor(page, () => getCellColor(5, 1), [153, 178, 209, 255]);
|
||||
await pollFor(page, () => getCellColor(6, 1), [186, 167, 189, 255]);
|
||||
await pollFor(page, () => getCellColor(7, 1), [4, 122, 124, 255]);
|
||||
await pollFor(page, () => getCellColor(8, 1), [110, 112, 108, 255]);
|
||||
await pollFor(page, () => getCellColor(1, 2), [170, 171, 169, 255]);
|
||||
await pollFor(page, () => getCellColor(2, 2), [215, 36, 36, 255]);
|
||||
await pollFor(page, () => getCellColor(3, 2), [72, 117, 25, 255]);
|
||||
await pollFor(page, () => getCellColor(4, 2), [117, 109, 36, 255]);
|
||||
await pollFor(page, () => getCellColor(5, 2), [72, 103, 135, 255]);
|
||||
await pollFor(page, () => getCellColor(6, 2), [125, 91, 121, 255]);
|
||||
await pollFor(page, () => getCellColor(7, 2), [25, 117, 117, 255]);
|
||||
await pollFor(page, () => getCellColor(8, 2), [111, 111, 110, 255]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectionBackground', async () => {
|
||||
|
||||
+9
-3
@@ -33,7 +33,9 @@ jobs:
|
||||
exit $EXIT_CODE
|
||||
displayName: 'Unit tests'
|
||||
- script: yarn lint
|
||||
displayName: 'Lint'
|
||||
displayName: 'Lint code'
|
||||
- script: yarn lint-api
|
||||
displayName: 'Lint API'
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: Cobertura
|
||||
@@ -58,7 +60,9 @@ jobs:
|
||||
- script: yarn test-unit --forbid-only
|
||||
displayName: 'Unit tests'
|
||||
- script: yarn lint
|
||||
displayName: 'Lint'
|
||||
displayName: 'Lint code'
|
||||
- script: yarn lint-api
|
||||
displayName: 'Lint API'
|
||||
|
||||
- job: Windows
|
||||
pool:
|
||||
@@ -78,7 +82,9 @@ jobs:
|
||||
- script: yarn test-unit --forbid-only
|
||||
displayName: 'Unit tests'
|
||||
- script: yarn lint
|
||||
displayName: 'Lint'
|
||||
displayName: 'Lint code'
|
||||
- script: yarn lint-api
|
||||
displayName: 'Lint API'
|
||||
|
||||
- job: Linux_IntegrationTests
|
||||
pool:
|
||||
|
||||
@@ -430,6 +430,7 @@ function initOptions(term: TerminalType): void {
|
||||
];
|
||||
const stringOptions = {
|
||||
cursorStyle: ['block', 'underline', 'bar'],
|
||||
cursorInactiveStyle: ['outline', 'block', 'bar', 'underline', 'none'],
|
||||
fastScrollModifier: ['none', 'alt', 'ctrl', 'shift'],
|
||||
fontFamily: null,
|
||||
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
|
||||
+3
-1
@@ -30,6 +30,7 @@
|
||||
"start": "node demo/start",
|
||||
"start-debug": "node --inspect-brk demo/start",
|
||||
"lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
|
||||
"lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
|
||||
"test": "npm run test-unit",
|
||||
"posttest": "npm run lint",
|
||||
"test-api": "npm run test-api-chromium",
|
||||
@@ -43,9 +44,10 @@
|
||||
"prepare": "npm run setup",
|
||||
"setup": "npm run build",
|
||||
"presetup": "node ./bin/install-addons.js",
|
||||
"postsetup": "cd addons/xterm-addon-image && npm run inwasm -- -S",
|
||||
"postsetup": "npm run inwasm",
|
||||
"prepublishOnly": "npm run package",
|
||||
"watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput",
|
||||
"inwasm": "cd addons/xterm-addon-image && npm run inwasm -- -S",
|
||||
"benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json",
|
||||
"benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/test/benchmark/*benchmark.js",
|
||||
"benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/test/benchmark/*benchmark.js",
|
||||
|
||||
@@ -109,7 +109,8 @@ export class Linkifier2 extends Disposable implements ILinkifier2 {
|
||||
}
|
||||
|
||||
private _handleHover(position: IBufferCellPosition): void {
|
||||
// TODO: This currently does not cache link provider results across wrapped lines, activeLine should be something like `activeRange: {startY, endY}`
|
||||
// TODO: This currently does not cache link provider results across wrapped lines, activeLine
|
||||
// should be something like `activeRange: {startY, endY}`
|
||||
// Check if we need to clear the link
|
||||
if (this._activeLine !== position.y || this._wasResized) {
|
||||
this._clearCurrentLink();
|
||||
|
||||
@@ -104,7 +104,8 @@ export class OscLinkProvider implements ILinkProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle fetching and returning other link ranges to underline other links with the same id
|
||||
// TODO: Handle fetching and returning other link ranges to underline other links with the same
|
||||
// id
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,6 +529,8 @@ export class MockThemeService implements IThemeService{
|
||||
css.toColor('#ad7fa8'),
|
||||
css.toColor('#34e2e2'),
|
||||
css.toColor('#eeeeec')
|
||||
]
|
||||
],
|
||||
selectionBackgroundOpaque: css.toColor('#ff0000'),
|
||||
selectionInactiveBackgroundOpaque: css.toColor('#00ff00')
|
||||
} as any;
|
||||
}
|
||||
|
||||
Vendored
+3
@@ -136,7 +136,10 @@ export interface IColorSet {
|
||||
selectionInactiveBackgroundTransparent: IColor;
|
||||
selectionInactiveBackgroundOpaque: IColor;
|
||||
ansi: IColor[];
|
||||
/** Maps original colors to colors that respect minimum contrast ratio. */
|
||||
contrastCache: IColorContrastCache;
|
||||
/** Maps original colors to colors that respect _half_ of the minimum contrast ratio. */
|
||||
halfContrastCache: IColorContrastCache;
|
||||
}
|
||||
|
||||
export type ReadonlyColorSet = Readonly<Omit<IColorSet, 'ansi'>> & { ansi: Readonly<Pick<IColorSet, 'ansi'>['ansi']> };
|
||||
|
||||
@@ -65,8 +65,8 @@ export class Viewport extends Disposable implements IViewport {
|
||||
super();
|
||||
|
||||
// Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar.
|
||||
// Unfortunately the overlay scrollbar would be hidden underneath the screen element in that case,
|
||||
// therefore we account for a standard amount to make it visible
|
||||
// Unfortunately the overlay scrollbar would be hidden underneath the screen element in that
|
||||
// case, therefore we account for a standard amount to make it visible
|
||||
this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;
|
||||
this.register(addDisposableDomListener(this._viewportElement, 'scroll', this._handleScroll.bind(this)));
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ export class BufferDecorationRenderer extends Disposable {
|
||||
decoration.element = element;
|
||||
this._decorationElements.set(decoration, element);
|
||||
this._container.appendChild(element);
|
||||
decoration.onDispose(() => {
|
||||
this._decorationElements.delete(decoration);
|
||||
element!.remove();
|
||||
});
|
||||
}
|
||||
element.style.top = `${line * this._renderService.dimensions.css.cell.height}px`;
|
||||
element.style.display = this._altBufferIsActive ? 'none' : 'block';
|
||||
|
||||
@@ -197,23 +197,20 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`}`;
|
||||
// Cursor
|
||||
styles +=
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} ,` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} ,` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} ` +
|
||||
`{` +
|
||||
` outline: 1px solid ${colors.cursor.css};` +
|
||||
` outline-offset: -1px;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}:not(.${RowCss.CURSOR_STYLE_BLOCK_CLASS}) {` +
|
||||
` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` background-color: ${colors.cursor.css};` +
|
||||
` color: ${colors.cursorAccent.css};` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` +
|
||||
` outline: 1px solid ${colors.cursor.css};` +
|
||||
` outline-offset: -1px;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
|
||||
` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` +
|
||||
`}` +
|
||||
@@ -392,7 +389,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
public clear(): void {
|
||||
for (const e of this._rowElements) {
|
||||
/**
|
||||
* NOTE: This used to be `e.innerText = '';` but that doesn't work when using `jsdom` and `@testing-library/react`
|
||||
* NOTE: This used to be `e.innerText = '';` but that doesn't work when using `jsdom` and
|
||||
* `@testing-library/react`
|
||||
*
|
||||
* references:
|
||||
* - https://github.com/testing-library/react-testing-library/issues/1146
|
||||
@@ -408,6 +406,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
const cursorX = Math.min(buffer.x, this._bufferService.cols - 1);
|
||||
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
|
||||
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
||||
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
||||
|
||||
for (let y = start; y <= end; y++) {
|
||||
const row = y + buffer.ydisp;
|
||||
@@ -422,6 +421,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
row,
|
||||
row === cursorAbsoluteY,
|
||||
cursorStyle,
|
||||
cursorInactiveStyle,
|
||||
cursorX,
|
||||
cursorBlink,
|
||||
this.dimensions.css.cell.width,
|
||||
@@ -474,6 +474,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
const cursorX = Math.min(buffer.x, cols - 1);
|
||||
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
|
||||
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
||||
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
||||
|
||||
// refresh rows within link range
|
||||
for (let i = y; i <= y2; ++i) {
|
||||
@@ -489,6 +490,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
row,
|
||||
row === cursorAbsoluteY,
|
||||
cursorStyle,
|
||||
cursorInactiveStyle,
|
||||
cursorX,
|
||||
cursorBlink,
|
||||
this.dimensions.css.cell.width,
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('DomRendererRowFactory', () => {
|
||||
|
||||
describe('createRow', () => {
|
||||
it('should not create anything for an empty row', () => {
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
''
|
||||
);
|
||||
@@ -50,7 +50,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, '語', 2, '語'.charCodeAt(0)]));
|
||||
// There should be no element for the following "empty" cell
|
||||
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '', 0, 0]));
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>語</span>'
|
||||
);
|
||||
@@ -58,7 +58,7 @@ describe('DomRendererRowFactory', () => {
|
||||
|
||||
it('should add class for cursor and cursor style', () => {
|
||||
for (const style of ['block', 'bar', 'underline']) {
|
||||
const spans = rowFactory.createRow(lineData, 0, true, style, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, true, style, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span class="xterm-cursor xterm-cursor-${style}"> </span>`
|
||||
);
|
||||
@@ -66,18 +66,42 @@ describe('DomRendererRowFactory', () => {
|
||||
});
|
||||
|
||||
it('should add class for cursor blink', () => {
|
||||
const spans = rowFactory.createRow(lineData, 0, true, 'block', 0, true, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, true, 'block', undefined, 0, true, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span class="xterm-cursor xterm-cursor-blink xterm-cursor-block"> </span>`
|
||||
);
|
||||
});
|
||||
|
||||
it('should add class for inactive cursor', () => {
|
||||
const coreBrowserService = new MockCoreBrowserService();
|
||||
coreBrowserService.isFocused = false;
|
||||
const rowFactory = new DomRendererRowFactory(
|
||||
dom.window.document,
|
||||
new MockCharacterJoinerService(),
|
||||
new MockOptionsService({ drawBoldTextInBrightColors: true }),
|
||||
coreBrowserService,
|
||||
new MockCoreService(),
|
||||
new MockDecorationService(),
|
||||
new MockThemeService()
|
||||
);
|
||||
for (const inactiveStyle of ['outline', 'block', 'bar', 'underline', 'none']){
|
||||
const spans = rowFactory.createRow(lineData, 0, true, 'block', inactiveStyle, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
if (inactiveStyle === 'none') {
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span class="xterm-cursor"> </span>`);
|
||||
} else {
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span class="xterm-cursor xterm-cursor-${inactiveStyle}"> </span>`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('attributes', () => {
|
||||
it('should add class for bold', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.BOLD;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bold">a</span>'
|
||||
);
|
||||
@@ -87,7 +111,7 @@ describe('DomRendererRowFactory', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.ITALIC;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-italic">a</span>'
|
||||
);
|
||||
@@ -97,7 +121,7 @@ describe('DomRendererRowFactory', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.DIM;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-dim">a</span>'
|
||||
);
|
||||
@@ -110,7 +134,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
|
||||
cell.extended.underlineStyle = UnderlineStyle.SINGLE;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-underline-1">a</span>'
|
||||
);
|
||||
@@ -121,7 +145,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
|
||||
cell.extended.underlineStyle = UnderlineStyle.DOUBLE;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-underline-2">a</span>'
|
||||
);
|
||||
@@ -132,7 +156,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
|
||||
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-underline-3">a</span>'
|
||||
);
|
||||
@@ -143,7 +167,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
|
||||
cell.extended.underlineStyle = UnderlineStyle.DOTTED;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-underline-4">a</span>'
|
||||
);
|
||||
@@ -154,7 +178,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
|
||||
cell.extended.underlineStyle = UnderlineStyle.DASHED;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-underline-5">a</span>'
|
||||
);
|
||||
@@ -165,7 +189,7 @@ describe('DomRendererRowFactory', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.OVERLINE;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-overline">a</span>'
|
||||
);
|
||||
@@ -175,7 +199,7 @@ describe('DomRendererRowFactory', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.STRIKETHROUGH;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-strikethrough">a</span>'
|
||||
);
|
||||
@@ -188,7 +212,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.fg &= ~Attributes.PCOLOR_MASK;
|
||||
cell.fg |= i;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span class="xterm-fg-${i}">a</span>`
|
||||
);
|
||||
@@ -202,7 +226,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.bg &= ~Attributes.PCOLOR_MASK;
|
||||
cell.bg |= i;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span class="xterm-bg-${i}">a</span>`
|
||||
);
|
||||
@@ -214,7 +238,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.fg |= Attributes.CM_P16 | 2 | FgFlags.INVERSE;
|
||||
cell.bg |= Attributes.CM_P16 | 1;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bg-2 xterm-fg-1">a</span>'
|
||||
);
|
||||
@@ -225,7 +249,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.fg |= FgFlags.INVERSE;
|
||||
cell.bg |= Attributes.CM_P16 | 1;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bg-257 xterm-fg-1">a</span>'
|
||||
);
|
||||
@@ -235,7 +259,7 @@ describe('DomRendererRowFactory', () => {
|
||||
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
cell.fg |= Attributes.CM_P16 | 1 | FgFlags.INVERSE;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bg-1 xterm-fg-257">a</span>'
|
||||
);
|
||||
@@ -248,7 +272,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.fg &= ~Attributes.PCOLOR_MASK;
|
||||
cell.fg |= i;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
`<span class="xterm-bold xterm-fg-${i + 8}">a</span>`
|
||||
);
|
||||
@@ -260,7 +284,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3;
|
||||
cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span style="background-color:#040506;color:#010203;">a</span>'
|
||||
);
|
||||
@@ -271,7 +295,7 @@ describe('DomRendererRowFactory', () => {
|
||||
cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3 | FgFlags.INVERSE;
|
||||
cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6;
|
||||
lineData.setCell(0, cell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span style="background-color:#010203;color:#040506;">a</span>'
|
||||
);
|
||||
@@ -283,17 +307,17 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
|
||||
rowFactory.handleSelectionChanged([1, 0], [2, 0], false);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>a</span><span class="xterm-decoration-top">b</span>'
|
||||
'<span>a</span><span style="background-color:#ff0000;" class="xterm-decoration-top">b</span>'
|
||||
);
|
||||
});
|
||||
it('should force whitespace cells to be rendered above the background', () => {
|
||||
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
rowFactory.handleSelectionChanged([0, 0], [2, 0], false);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-decoration-top"> </span><span class="xterm-decoration-top">a</span>'
|
||||
'<span style="background-color:#ff0000;" class="xterm-decoration-top"> a</span>'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -308,7 +332,7 @@ describe('DomRendererRowFactory', () => {
|
||||
});
|
||||
|
||||
it('should not create anything for an empty row', () => {
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
''
|
||||
);
|
||||
@@ -318,7 +342,7 @@ describe('DomRendererRowFactory', () => {
|
||||
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(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)]));
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>abc</span>'
|
||||
);
|
||||
@@ -329,7 +353,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '€', 1, '€'.charCodeAt(0)]));
|
||||
lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)]));
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>a</span><span style="letter-spacing: 3px;">€</span><span>c</span>'
|
||||
);
|
||||
@@ -344,7 +368,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(1, aColor1);
|
||||
lineData.setCell(2, bColor2);
|
||||
lineData.setCell(3, bColor2);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-fg-1">aa</span><span class="xterm-fg-2">bb</span>'
|
||||
);
|
||||
@@ -356,7 +380,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'X', 1, 'X'.charCodeAt(0)]));
|
||||
lineData.setCell(3, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
|
||||
lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
|
||||
const spans = rowFactory.createRow(lineData, 0, true, undefined, 2, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, true, undefined, undefined, 2, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>aa</span><span class="xterm-cursor xterm-cursor-block">X</span><span>bb</span>'
|
||||
);
|
||||
@@ -369,7 +393,7 @@ describe('DomRendererRowFactory', () => {
|
||||
nullCell.bg = Attributes.CM_P16 | 2;
|
||||
lineData.setCell(3, nullCell);
|
||||
lineData.setCell(4, nullCell);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span> </span><span class="xterm-bg-1"> </span><span class="xterm-bg-2"> </span>'
|
||||
);
|
||||
@@ -379,23 +403,23 @@ describe('DomRendererRowFactory', () => {
|
||||
const nullCell = lineData.loadCell(0, new CellData());
|
||||
nullCell.bg = Attributes.CM_P16 | 1;
|
||||
lineData.setCell(0, nullCell);
|
||||
let spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
let spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bg-1"> </span>'
|
||||
);
|
||||
lineData.setCell(1, nullCell);
|
||||
spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bg-1"> </span>'
|
||||
);
|
||||
lineData.setCell(2, nullCell);
|
||||
lineData.setCell(3, nullCell);
|
||||
spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bg-1"> </span>'
|
||||
);
|
||||
lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span class="xterm-bg-1"> </span><span>a</span>'
|
||||
);
|
||||
@@ -410,7 +434,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)]));
|
||||
lineData.setCell(3, CellData.fromCharData([DEFAULT_ATTR, '語', 2, 'c'.charCodeAt(0)]));
|
||||
lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, '𝄞', 1, 'c'.charCodeAt(0)]));
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>a</span><span style="letter-spacing: 3px;">€</span><span>c語</span><span style="letter-spacing: -2px;">𝄞</span>'
|
||||
);
|
||||
@@ -424,7 +448,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'x', 1, 'x'.charCodeAt(0)]));
|
||||
lineData.setCell(5, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
|
||||
lineData.setCell(6, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>aa</span><span style="text-decoration: underline;">xxx</span><span>bb</span>'
|
||||
);
|
||||
@@ -435,7 +459,7 @@ describe('DomRendererRowFactory', () => {
|
||||
lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'x', 1, 'x'.charCodeAt(0)]));
|
||||
lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'x', 1, 'x'.charCodeAt(0)]));
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span>aa</span><span style="text-decoration: underline;">x x</span>'
|
||||
);
|
||||
@@ -445,7 +469,7 @@ describe('DomRendererRowFactory', () => {
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
lineData.setCell(i, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
}
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -100, 100);
|
||||
const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -100, 100);
|
||||
assert.equal(extractHtml(spans),
|
||||
'<span style="text-decoration: underline;">aaaaaaaaaa</span>'
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
||||
import { excludeFromContrastRatioDemands } from 'browser/renderer/shared/RendererUtils';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
||||
import { IColorContrastCache } from 'browser/Types';
|
||||
|
||||
|
||||
export const enum RowCss {
|
||||
@@ -26,6 +27,7 @@ export const enum RowCss {
|
||||
CURSOR_CLASS = 'xterm-cursor',
|
||||
CURSOR_BLINK_CLASS = 'xterm-cursor-blink',
|
||||
CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block',
|
||||
CURSOR_STYLE_OUTLINE_CLASS = 'xterm-cursor-outline',
|
||||
CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar',
|
||||
CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'
|
||||
}
|
||||
@@ -61,6 +63,7 @@ export class DomRendererRowFactory {
|
||||
row: number,
|
||||
isCursorRow: boolean,
|
||||
cursorStyle: string | undefined,
|
||||
cursorInactiveStyle: string | undefined,
|
||||
cursorX: number,
|
||||
cursorBlink: boolean,
|
||||
cellWidth: number,
|
||||
@@ -86,6 +89,7 @@ export class DomRendererRowFactory {
|
||||
let oldExt = 0;
|
||||
let oldLinkHover: number | boolean = false;
|
||||
let oldSpacing = 0;
|
||||
let oldIsInSelection: boolean = false;
|
||||
let spacing = 0;
|
||||
const classes: string[] = [];
|
||||
|
||||
@@ -151,16 +155,24 @@ export class DomRendererRowFactory {
|
||||
/**
|
||||
* chars can only be merged on existing span if:
|
||||
* - existing span only contains mergeable chars (cellAmount != 0)
|
||||
* - fg/bg/ul did not change
|
||||
* - char not part of a selection
|
||||
* - bg did not change (or both are in selection)
|
||||
* - fg did not change (or both are in selection and selection fg is set)
|
||||
* - ext did not change
|
||||
* - underline from hover state did not change
|
||||
* - cell content renders to same letter-spacing
|
||||
* - cell is not cursor
|
||||
*/
|
||||
if (
|
||||
cellAmount
|
||||
&& cell.bg === oldBg && cell.fg === oldFg && cell.extended.ext === oldExt
|
||||
&& !isInSelection
|
||||
&& (
|
||||
(isInSelection && oldIsInSelection)
|
||||
|| (!isInSelection && cell.bg === oldBg)
|
||||
)
|
||||
&& (
|
||||
(isInSelection && oldIsInSelection && colors.selectionForeground)
|
||||
|| (!(isInSelection && oldIsInSelection && colors.selectionForeground) && cell.fg === oldFg)
|
||||
)
|
||||
&& cell.extended.ext === oldExt
|
||||
&& isLinkHover === oldLinkHover
|
||||
&& spacing === oldSpacing
|
||||
&& !isCursorCell
|
||||
@@ -191,6 +203,7 @@ export class DomRendererRowFactory {
|
||||
oldExt = cell.extended.ext;
|
||||
oldLinkHover = isLinkHover;
|
||||
oldSpacing = spacing;
|
||||
oldIsInSelection = isInSelection;
|
||||
|
||||
if (isJoined) {
|
||||
// The DOM renderer colors the background of the cursor but for ligatures all cells are
|
||||
@@ -203,16 +216,37 @@ export class DomRendererRowFactory {
|
||||
|
||||
if (!this._coreService.isCursorHidden && isCursorCell) {
|
||||
classes.push(RowCss.CURSOR_CLASS);
|
||||
if (cursorBlink) {
|
||||
classes.push(RowCss.CURSOR_BLINK_CLASS);
|
||||
if (this._coreBrowserService.isFocused) {
|
||||
if (cursorBlink) {
|
||||
classes.push(RowCss.CURSOR_BLINK_CLASS);
|
||||
}
|
||||
classes.push(
|
||||
cursorStyle === 'bar'
|
||||
? RowCss.CURSOR_STYLE_BAR_CLASS
|
||||
: cursorStyle === 'underline'
|
||||
? RowCss.CURSOR_STYLE_UNDERLINE_CLASS
|
||||
: RowCss.CURSOR_STYLE_BLOCK_CLASS
|
||||
);
|
||||
} else {
|
||||
if (cursorInactiveStyle) {
|
||||
switch (cursorInactiveStyle) {
|
||||
case 'outline':
|
||||
classes.push(RowCss.CURSOR_STYLE_OUTLINE_CLASS);
|
||||
break;
|
||||
case 'block':
|
||||
classes.push(RowCss.CURSOR_STYLE_BLOCK_CLASS);
|
||||
break;
|
||||
case 'bar':
|
||||
classes.push(RowCss.CURSOR_STYLE_BAR_CLASS);
|
||||
break;
|
||||
case 'underline':
|
||||
classes.push(RowCss.CURSOR_STYLE_UNDERLINE_CLASS);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
classes.push(
|
||||
cursorStyle === 'bar'
|
||||
? RowCss.CURSOR_STYLE_BAR_CLASS
|
||||
: cursorStyle === 'underline'
|
||||
? RowCss.CURSOR_STYLE_UNDERLINE_CLASS
|
||||
: RowCss.CURSOR_STYLE_BLOCK_CLASS
|
||||
);
|
||||
}
|
||||
|
||||
if (cell.isBold()) {
|
||||
@@ -262,7 +296,8 @@ export class DomRendererRowFactory {
|
||||
classes.push(RowCss.STRIKETHROUGH_CLASS);
|
||||
}
|
||||
|
||||
// apply link hover underline late, effectively overrides any previous text-decoration settings
|
||||
// apply link hover underline late, effectively overrides any previous text-decoration
|
||||
// settings
|
||||
if (isLinkHover) {
|
||||
charElement.style.textDecoration = 'underline';
|
||||
}
|
||||
@@ -303,22 +338,26 @@ export class DomRendererRowFactory {
|
||||
isTop = d.options.layer === 'top';
|
||||
});
|
||||
|
||||
// Apply selection foreground if applicable
|
||||
if (!isTop) {
|
||||
if (colors.selectionForeground && isInSelection) {
|
||||
// Apply selection
|
||||
if (!isTop && isInSelection) {
|
||||
// If in the selection, force the element to be above the selection to improve contrast and
|
||||
// support opaque selections. The applies background is not actually needed here as
|
||||
// selection is drawn in a seperate container, the main purpose of this to ensuring minimum
|
||||
// contrast ratio
|
||||
bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque;
|
||||
bg = bgOverride.rgba >> 8 & 0xFFFFFF;
|
||||
bgColorMode = Attributes.CM_RGB;
|
||||
// Since an opaque selection is being rendered, the selection pretends to be a decoration to
|
||||
// ensure text is drawn above the selection.
|
||||
isTop = true;
|
||||
// Apply selection foreground if applicable
|
||||
if (colors.selectionForeground) {
|
||||
fgColorMode = Attributes.CM_RGB;
|
||||
fg = colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
|
||||
fgOverride = colors.selectionForeground;
|
||||
}
|
||||
}
|
||||
|
||||
// If in the selection, force the element to be above the selection to improve contrast and
|
||||
// support opaque selections
|
||||
if (isInSelection) {
|
||||
bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque;
|
||||
isTop = true;
|
||||
}
|
||||
|
||||
// If it's a top decoration, render above the selection
|
||||
if (isTop) {
|
||||
classes.push('xterm-decoration-top');
|
||||
@@ -392,7 +431,7 @@ export class DomRendererRowFactory {
|
||||
}
|
||||
|
||||
// exclude conditions for cell merging - never merge these
|
||||
if (!isCursorCell && !isInSelection && !isJoined && !isDecorated) {
|
||||
if (!isCursorCell && !isJoined && !isDecorated) {
|
||||
cellAmount++;
|
||||
} else {
|
||||
charElement.textContent = text;
|
||||
@@ -420,15 +459,19 @@ export class DomRendererRowFactory {
|
||||
}
|
||||
|
||||
// Try get from cache first, only use the cache when there are no decoration overrides
|
||||
const cache = this._getContrastCache(cell);
|
||||
let adjustedColor: IColor | undefined | null = undefined;
|
||||
if (!bgOverride && !fgOverride) {
|
||||
adjustedColor = this._themeService.colors.contrastCache.getColor(bg.rgba, fg.rgba);
|
||||
adjustedColor = cache.getColor(bg.rgba, fg.rgba);
|
||||
}
|
||||
|
||||
// Calculate and store in cache
|
||||
if (adjustedColor === undefined) {
|
||||
adjustedColor = color.ensureContrastRatio(bgOverride || bg, fgOverride || fg, this._optionsService.rawOptions.minimumContrastRatio);
|
||||
this._themeService.colors.contrastCache.setColor((bgOverride || bg).rgba, (fgOverride || fg).rgba, adjustedColor ?? null);
|
||||
// Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from
|
||||
// non-dim cells
|
||||
const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1);
|
||||
adjustedColor = color.ensureContrastRatio(bgOverride || bg, fgOverride || fg, ratio);
|
||||
cache.setColor((bgOverride || bg).rgba, (fgOverride || fg).rgba, adjustedColor ?? null);
|
||||
}
|
||||
|
||||
if (adjustedColor) {
|
||||
@@ -439,6 +482,13 @@ export class DomRendererRowFactory {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _getContrastCache(cell: ICellData): IColorContrastCache {
|
||||
if (cell.isDim()) {
|
||||
return this._themeService.colors.halfContrastCache;
|
||||
}
|
||||
return this._themeService.colors.contrastCache;
|
||||
}
|
||||
|
||||
private _addStyle(element: HTMLElement, style: string): void {
|
||||
element.setAttribute('style', `${element.getAttribute('style') || ''}${style};`);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number
|
||||
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
|
||||
// dynamic character atlas.
|
||||
ansi: colors.ansi.slice(),
|
||||
contrastCache: colors.contrastCache
|
||||
contrastCache: colors.contrastCache,
|
||||
halfContrastCache: colors.halfContrastCache
|
||||
};
|
||||
return {
|
||||
customGlyphs: options.customGlyphs,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { isFirefox, isLegacyEdge } from 'common/Platform';
|
||||
export const INVERTED_DEFAULT_COLOR = 257;
|
||||
|
||||
export const DIM_OPACITY = 0.5;
|
||||
// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge would
|
||||
// result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly
|
||||
// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge
|
||||
// would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly
|
||||
// unaligned Powerline fonts (PR 3356#issuecomment-850928179).
|
||||
export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic';
|
||||
|
||||
@@ -349,7 +349,8 @@ const enum VectorType {
|
||||
* not been patched with powerline characters and also to get pixel perfect rendering as rendering
|
||||
* issues can occur around AA/SPAA.
|
||||
*
|
||||
* The line variants draw beyond the cell and get clipped to ensure the end of the line is not visible.
|
||||
* The line variants draw beyond the cell and get clipped to ensure the end of the line is not
|
||||
* visible.
|
||||
*
|
||||
* Original symbols defined in https://github.com/powerline/fontpatcher
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@ import { FourKeyMap } from 'common/MultiKeyMap';
|
||||
import { IdleTaskQueue } from 'common/TaskQueue';
|
||||
import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, IRequestRedrawEvent, ITextureAtlas } from 'browser/renderer/shared/Types';
|
||||
import { EventEmitter } from 'common/EventEmitter';
|
||||
import { IColorContrastCache } from 'browser/Types';
|
||||
|
||||
/**
|
||||
* A shared object which is used to draw nothing for a particular cell.
|
||||
@@ -298,7 +299,7 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
case Attributes.CM_DEFAULT:
|
||||
default:
|
||||
if (inverse) {
|
||||
result = this._config.colors.foreground;
|
||||
result = color.opaque(this._config.colors.foreground);
|
||||
} else {
|
||||
result = this._config.colors.background;
|
||||
}
|
||||
@@ -309,8 +310,7 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
}
|
||||
|
||||
private _getForegroundColor(bg: number, bgColorMode: number, bgColor: number, fg: number, fgColorMode: number, fgColor: number, inverse: boolean, dim: boolean, bold: boolean, excludeFromContrastRatioDemands: boolean): IColor {
|
||||
// TODO: Pass dim along to get min contrast?
|
||||
const minimumContrastColor = this._getMinimumContrastColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, false, bold, excludeFromContrastRatioDemands);
|
||||
const minimumContrastColor = this._getMinimumContrastColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, false, bold, dim, excludeFromContrastRatioDemands);
|
||||
if (minimumContrastColor) {
|
||||
return minimumContrastColor;
|
||||
}
|
||||
@@ -385,23 +385,26 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
}
|
||||
}
|
||||
|
||||
private _getMinimumContrastColor(bg: number, bgColorMode: number, bgColor: number, fg: number, fgColorMode: number, fgColor: number, inverse: boolean, bold: boolean, excludeFromContrastRatioDemands: boolean): IColor | undefined {
|
||||
private _getMinimumContrastColor(bg: number, bgColorMode: number, bgColor: number, fg: number, fgColorMode: number, fgColor: number, inverse: boolean, bold: boolean, dim: boolean, excludeFromContrastRatioDemands: boolean): IColor | undefined {
|
||||
if (this._config.minimumContrastRatio === 1 || excludeFromContrastRatioDemands) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try get from cache first
|
||||
const adjustedColor = this._config.colors.contrastCache.getColor(bg, fg);
|
||||
const cache = this._getContrastCache(dim);
|
||||
const adjustedColor = cache.getColor(bg, fg);
|
||||
if (adjustedColor !== undefined) {
|
||||
return adjustedColor || undefined;
|
||||
}
|
||||
|
||||
const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse);
|
||||
const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, inverse, bold);
|
||||
const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio);
|
||||
// Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from
|
||||
// non-dim cells
|
||||
const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio / (dim ? 2 : 1));
|
||||
|
||||
if (!result) {
|
||||
this._config.colors.contrastCache.setColor(bg, fg, null);
|
||||
cache.setColor(bg, fg, null);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -410,11 +413,18 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
(result >> 16) & 0xFF,
|
||||
(result >> 8) & 0xFF
|
||||
);
|
||||
this._config.colors.contrastCache.setColor(bg, fg, color);
|
||||
cache.setColor(bg, fg, color);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
private _getContrastCache(dim: boolean): IColorContrastCache {
|
||||
if (dim) {
|
||||
return this._config.colors.halfContrastCache;
|
||||
}
|
||||
return this._config.colors.contrastCache;
|
||||
}
|
||||
|
||||
private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean = false): IRasterizedGlyph {
|
||||
const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars;
|
||||
|
||||
@@ -466,8 +476,8 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
|
||||
// draw the background
|
||||
const backgroundColor = this._getBackgroundColor(bgColorMode, bgColor, inverse, dim);
|
||||
// Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, regardless of
|
||||
// transparency in backgroundColor
|
||||
// Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha,
|
||||
// regardless of transparency in backgroundColor
|
||||
this._tmpCtx.globalCompositeOperation = 'copy';
|
||||
this._tmpCtx.fillStyle = backgroundColor.css;
|
||||
this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height);
|
||||
@@ -532,10 +542,9 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
// Underline style/stroke
|
||||
this._tmpCtx.beginPath();
|
||||
const xLeft = padding;
|
||||
const yTop = Math.ceil(padding + this._config.deviceCharHeight) - yOffset;
|
||||
const yMid = padding + this._config.deviceCharHeight + lineWidth - yOffset;
|
||||
const yBot = Math.ceil(padding + this._config.deviceCharHeight + lineWidth * 2) - yOffset;
|
||||
const ySpace = lineWidth * 2;
|
||||
const yTop = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0);
|
||||
const yMid = yTop + lineWidth;
|
||||
const yBot = yTop + lineWidth * 2;
|
||||
|
||||
for (let i = 0; i < chWidth; i++) {
|
||||
this._tmpCtx.save();
|
||||
@@ -544,17 +553,10 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
const xChMid = xChLeft + this._config.deviceCellWidth / 2;
|
||||
switch (this._workAttributeData.extended.underlineStyle) {
|
||||
case UnderlineStyle.DOUBLE:
|
||||
if (restrictToCellHeight) {
|
||||
this._tmpCtx.moveTo(xChLeft, yTop - ySpace);
|
||||
this._tmpCtx.lineTo(xChRight, yTop - ySpace);
|
||||
this._tmpCtx.moveTo(xChLeft, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
} else {
|
||||
this._tmpCtx.moveTo(xChLeft, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
this._tmpCtx.moveTo(xChLeft, yBot);
|
||||
this._tmpCtx.lineTo(xChRight, yBot);
|
||||
}
|
||||
this._tmpCtx.moveTo(xChLeft, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
this._tmpCtx.moveTo(xChLeft, yBot);
|
||||
this._tmpCtx.lineTo(xChRight, yBot);
|
||||
break;
|
||||
case UnderlineStyle.CURLY:
|
||||
// Choose the bezier top and bottom based on the device pixel ratio, the curly line is
|
||||
@@ -567,7 +569,8 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
const clipRegion = new Path2D();
|
||||
clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop);
|
||||
this._tmpCtx.clip(clipRegion);
|
||||
// Start 1/2 cell before and end 1/2 cells after to ensure a smooth curve with other cells
|
||||
// Start 1/2 cell before and end 1/2 cells after to ensure a smooth curve with other
|
||||
// cells
|
||||
this._tmpCtx.moveTo(xChLeft - this._config.deviceCellWidth / 2, yMid);
|
||||
this._tmpCtx.bezierCurveTo(
|
||||
xChLeft - this._config.deviceCellWidth / 2, yCurlyTop,
|
||||
|
||||
@@ -60,7 +60,8 @@ interface IMeasureResult {
|
||||
height: number;
|
||||
}
|
||||
|
||||
// TODO: For supporting browsers we should also provide a CanvasCharDimensionsProvider that uses ctx.measureText
|
||||
// TODO: For supporting browsers we should also provide a CanvasCharDimensionsProvider that uses
|
||||
// ctx.measureText
|
||||
class DomMeasureStrategy implements IMeasureStrategy {
|
||||
private _result: IMeasureResult = { width: 0, height: 0 };
|
||||
private _measureElement: HTMLElement;
|
||||
|
||||
@@ -35,7 +35,12 @@ describe('ThemeService', () => {
|
||||
describe('constructor', () => {
|
||||
it('should fill all colors with values', () => {
|
||||
for (const key of Object.keys(themeService.colors)) {
|
||||
if (key !== 'ansi' && key !== 'contrastCache' && key !== 'selectionForeground') {
|
||||
if (![
|
||||
'ansi',
|
||||
'contrastCache',
|
||||
'halfContrastCache',
|
||||
'selectionForeground'
|
||||
].includes(key)) {
|
||||
// A #rrggbb or rgba(...)
|
||||
assert.ok((themeService.colors as any)[key].css.length >= 7);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,8 @@ export class ThemeService extends Disposable implements IThemeService {
|
||||
public serviceBrand: undefined;
|
||||
|
||||
private _colors: IColorSet;
|
||||
private _contrastCache: IColorContrastCache;
|
||||
private _contrastCache: IColorContrastCache = new ColorContrastCache();
|
||||
private _halfContrastCache: IColorContrastCache = new ColorContrastCache();
|
||||
private _restoreColors!: IRestoreColorSet;
|
||||
|
||||
public get colors(): ReadonlyColorSet { return this._colors; }
|
||||
@@ -94,7 +95,6 @@ export class ThemeService extends Disposable implements IThemeService {
|
||||
) {
|
||||
super();
|
||||
|
||||
this._contrastCache = new ColorContrastCache();
|
||||
this._colors = {
|
||||
foreground: DEFAULT_FOREGROUND,
|
||||
background: DEFAULT_BACKGROUND,
|
||||
@@ -106,7 +106,8 @@ export class ThemeService extends Disposable implements IThemeService {
|
||||
selectionInactiveBackgroundTransparent: DEFAULT_SELECTION,
|
||||
selectionInactiveBackgroundOpaque: color.blend(DEFAULT_BACKGROUND, DEFAULT_SELECTION),
|
||||
ansi: DEFAULT_ANSI_COLORS.slice(),
|
||||
contrastCache: this._contrastCache
|
||||
contrastCache: this._contrastCache,
|
||||
halfContrastCache: this._halfContrastCache
|
||||
};
|
||||
this._updateRestoreColors();
|
||||
this._setTheme(this._optionsService.rawOptions.theme);
|
||||
@@ -172,6 +173,7 @@ export class ThemeService extends Disposable implements IThemeService {
|
||||
}
|
||||
// Clear our the cache
|
||||
this._contrastCache.clear();
|
||||
this._halfContrastCache.clear();
|
||||
this._updateRestoreColors();
|
||||
this._onChangeColors.fire(this.colors);
|
||||
}
|
||||
|
||||
+2
-1
@@ -106,7 +106,8 @@ export namespace color {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions where the source type is "css" (string: '#rgb', '#rgba', '#rrggbb', '#rrggbbaa').
|
||||
* Helper functions where the source type is "css" (string: '#rgb', '#rgba', '#rrggbb',
|
||||
* '#rrggbbaa').
|
||||
*/
|
||||
export namespace css {
|
||||
let $ctx: CanvasRenderingContext2D | undefined;
|
||||
|
||||
+40
-32
@@ -421,11 +421,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* - undefined (void):
|
||||
* all handlers were sync, no stack save, continue normally with next chunk
|
||||
* - Promise\<boolean\>:
|
||||
* execution stopped at async handler, stack saved, continue with
|
||||
* same chunk and the promise resolve value as `promiseResult` until the method returns `undefined`
|
||||
* execution stopped at async handler, stack saved, continue with same chunk and the promise
|
||||
* resolve value as `promiseResult` until the method returns `undefined`
|
||||
*
|
||||
* Note: This method should only be called by `Terminal.write` to ensure correct execution order and
|
||||
* proper continuation of async parser handlers.
|
||||
* Note: This method should only be called by `Terminal.write` to ensure correct execution order
|
||||
* and proper continuation of async parser handlers.
|
||||
*/
|
||||
public parse(data: string | Uint8Array, promiseResult?: boolean): void | Promise<boolean> {
|
||||
let result: void | Promise<boolean>;
|
||||
@@ -804,12 +804,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// find last taken cell - last cell can have 3 different states:
|
||||
// - hasContent(true) + hasWidth(1): narrow char - we are done
|
||||
// - hasWidth(0): second part of wide char - we are done
|
||||
// - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one cell further back
|
||||
// - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one
|
||||
// cell further back
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
|
||||
if (line.hasWidth(this._activeBuffer.x) && !line.hasContent(this._activeBuffer.x)) {
|
||||
this._activeBuffer.x--;
|
||||
// We do this only once, since width=1 + hasContent=false currently happens only once before
|
||||
// early wrapping of a wide char.
|
||||
// We do this only once, since width=1 + hasContent=false currently happens only once
|
||||
// before early wrapping of a wide char.
|
||||
// This needs to be fixed once we support graphemes taking more than 2 cells.
|
||||
}
|
||||
}
|
||||
@@ -1164,8 +1165,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to reset cells in a terminal row.
|
||||
* The cell gets replaced with the eraseChar of the terminal and the isWrapped property is set to false.
|
||||
* Helper method to reset cells in a terminal row. The cell gets replaced with the eraseChar of
|
||||
* the terminal and the isWrapped property is set to false.
|
||||
* @param y row index
|
||||
*/
|
||||
private _resetBufferLine(y: number, respectProtect: boolean = false): void {
|
||||
@@ -1362,8 +1363,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Insert Ps (Blank) Character(s) (default = 1) (ICH).
|
||||
*
|
||||
* @vt: #Y CSI ICH "Insert Characters" "CSI Ps @" "Insert `Ps` (blank) characters (default = 1)."
|
||||
* The ICH sequence inserts `Ps` blank characters. The cursor remains at the beginning of the blank characters.
|
||||
* Text between the cursor and right margin moves to the right. Characters moved past the right margin are lost.
|
||||
* The ICH sequence inserts `Ps` blank characters. The cursor remains at the beginning of the
|
||||
* blank characters. Text between the cursor and right margin moves to the right. Characters moved
|
||||
* past the right margin are lost.
|
||||
*
|
||||
*
|
||||
* FIXME: check against xterm - should not work outside of scroll margins (see VT520 manual)
|
||||
@@ -1388,8 +1390,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Delete Ps Character(s) (default = 1) (DCH).
|
||||
*
|
||||
* @vt: #Y CSI DCH "Delete Character" "CSI Ps P" "Delete `Ps` characters (default=1)."
|
||||
* As characters are deleted, the remaining characters between the cursor and right margin move to the left.
|
||||
* Character attributes move with the characters. The terminal adds blank characters at the right margin.
|
||||
* As characters are deleted, the remaining characters between the cursor and right margin move to
|
||||
* the left. Character attributes move with the characters. The terminal adds blank characters at
|
||||
* the right margin.
|
||||
*
|
||||
*
|
||||
* FIXME: check against xterm - should not work outside of scroll margins (see VT520 manual)
|
||||
@@ -1514,9 +1517,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.
|
||||
*
|
||||
* @vt: #Y CSI DECIC "Insert Columns" "CSI Ps ' }" "Insert `Ps` columns at cursor position."
|
||||
* DECIC inserts `Ps` times blank columns at the cursor position for all lines with the scroll margins,
|
||||
* moving content to the right. Content at the right margin is lost.
|
||||
* DECIC has no effect outside the scrolling margins.
|
||||
* DECIC inserts `Ps` times blank columns at the cursor position for all lines with the scroll
|
||||
* margins, moving content to the right. Content at the right margin is lost. DECIC has no effect
|
||||
* outside the scrolling margins.
|
||||
*/
|
||||
public insertColumns(params: IParams): boolean {
|
||||
if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
|
||||
@@ -1597,12 +1600,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* - wrap around is respected
|
||||
* - any valid sequence resets the carried forward char
|
||||
*
|
||||
* Note: To get reset on a valid sequence working correctly without much runtime penalty,
|
||||
* the preceding codepoint is stored on the parser in `this.print` and reset during `parser.parse`.
|
||||
* Note: To get reset on a valid sequence working correctly without much runtime penalty, the
|
||||
* preceding codepoint is stored on the parser in `this.print` and reset during `parser.parse`.
|
||||
*
|
||||
* @vt: #Y CSI REP "Repeat Preceding Character" "CSI Ps b" "Repeat preceding character `Ps` times (default=1)."
|
||||
* REP repeats the previous character `Ps` times advancing the cursor, also wrapping if DECAWM is set.
|
||||
* REP has no effect if the sequence does not follow a printable ASCII character
|
||||
* REP repeats the previous character `Ps` times advancing the cursor, also wrapping if DECAWM is
|
||||
* set. REP has no effect if the sequence does not follow a printable ASCII character
|
||||
* (NOOP for any other sequence in between or NON ASCII characters).
|
||||
*/
|
||||
public repeatPrecedingCharacter(params: IParams): boolean {
|
||||
@@ -2463,7 +2466,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* | 5 | Dashed underline. | #Y |
|
||||
* | other | Single underline. Same as `SGR 4 m`. | #Y |
|
||||
*
|
||||
* Extended colors are supported for foreground (Ps=38), background (Ps=48) and underline (Ps=58) as follows:
|
||||
* Extended colors are supported for foreground (Ps=38), background (Ps=48) and underline (Ps=58)
|
||||
* as follows:
|
||||
*
|
||||
* | Ps + 1 | Meaning | Support |
|
||||
* | ------ | ------------------------------------------------------------- | ------- |
|
||||
@@ -2673,8 +2677,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* http://vt100.net/docs/vt220-rm/table4-10.html
|
||||
*
|
||||
* @vt: #Y CSI DECSTR "Soft Terminal Reset" "CSI ! p" "Reset several terminal attributes to initial state."
|
||||
* There are two terminal reset sequences - RIS and DECSTR. While RIS performs almost a full terminal bootstrap,
|
||||
* DECSTR only resets certain attributes. For most needs DECSTR should be sufficient.
|
||||
* There are two terminal reset sequences - RIS and DECSTR. While RIS performs almost a full
|
||||
* terminal bootstrap, DECSTR only resets certain attributes. For most needs DECSTR should be
|
||||
* sufficient.
|
||||
*
|
||||
* The following terminal attributes are reset to default values:
|
||||
* - IRM is reset (dafault = false)
|
||||
@@ -2899,7 +2904,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Icon name is not supported. For Window Title see below.
|
||||
*
|
||||
* @vt: #Y OSC 2 "Set Windows Title" "OSC 2 ; Pt BEL" "Set window title."
|
||||
* xterm.js does not manipulate the title directly, instead exposes changes via the event `Terminal.onTitleChange`.
|
||||
* xterm.js does not manipulate the title directly, instead exposes changes via the event
|
||||
* `Terminal.onTitleChange`.
|
||||
*/
|
||||
public setTitle(data: string): boolean {
|
||||
this._windowTitle = data;
|
||||
@@ -2920,9 +2926,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* OSC 4; <num> ; <text> ST (set ANSI color <num> to <text>)
|
||||
*
|
||||
* @vt: #Y OSC 4 "Set ANSI color" "OSC 4 ; c ; spec BEL" "Change color number `c` to the color specified by `spec`."
|
||||
* `c` is the color index between 0 and 255. The color format of `spec` is derived from `XParseColor` (see OSC 10 for supported formats).
|
||||
* There may be multipe `c ; spec` pairs present in the same instruction.
|
||||
* If `spec` contains `?` the terminal returns a sequence with the currently set color.
|
||||
* `c` is the color index between 0 and 255. The color format of `spec` is derived from
|
||||
* `XParseColor` (see OSC 10 for supported formats). There may be multipe `c ; spec` pairs present
|
||||
* in the same instruction. If `spec` contains `?` the terminal returns a sequence with the
|
||||
* currently set color.
|
||||
*/
|
||||
public setOrReportIndexedColor(data: string): boolean {
|
||||
const event: IColorEvent = [];
|
||||
@@ -2962,9 +2969,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*
|
||||
* @vt: #Y OSC 8 "Create hyperlink" "OSC 8 ; params ; uri BEL" "Create a hyperlink to `uri` using `params`."
|
||||
* `uri` is a hyperlink starting with `http://`, `https://`, `ftp://`, `file://` or `mailto://`. `params` is an
|
||||
* optional list of key=value assignments, separated by the : character. Example: `id=xyz123:foo=bar:baz=quux`.
|
||||
* Currently only the id key is defined. Cells that share the same ID and URI share hover feedback.
|
||||
* Use `OSC 8 ; ; BEL` to finish the current hyperlink.
|
||||
* optional list of key=value assignments, separated by the : character.
|
||||
* Example: `id=xyz123:foo=bar:baz=quux`.
|
||||
* Currently only the id key is defined. Cells that share the same ID and URI share hover
|
||||
* feedback. Use `OSC 8 ; ; BEL` to finish the current hyperlink.
|
||||
*/
|
||||
public setHyperlink(data: string): boolean {
|
||||
const args = data.split(';');
|
||||
@@ -3351,8 +3359,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
|
||||
*
|
||||
* @vt: #P[Limited support, see below.] DCS DECRQSS "Request Selection or Setting" "DCS $ q Pt ST" "Request several terminal settings."
|
||||
* Response is in the form `ESC P 1 $ r Pt ST` for valid requests, where `Pt` contains the corresponding CSI string,
|
||||
* `ESC P 0 ST` for invalid requests.
|
||||
* Response is in the form `ESC P 1 $ r Pt ST` for valid requests, where `Pt` contains the
|
||||
* corresponding CSI string, `ESC P 0 ST` for invalid requests.
|
||||
*
|
||||
* Supported requests and responses:
|
||||
*
|
||||
|
||||
@@ -104,4 +104,18 @@ describe('SortedList', () => {
|
||||
{ key: 10 }
|
||||
]);
|
||||
});
|
||||
describe('values', () => {
|
||||
it('should iterate correctly when list items change during iteration', () => {
|
||||
list.insert(1);
|
||||
list.insert(2);
|
||||
list.insert(3);
|
||||
list.insert(4);
|
||||
const visited: number[] = [];
|
||||
for (const item of list.values()) {
|
||||
visited.push(item);
|
||||
list.delete(item);
|
||||
}
|
||||
deepStrictEqual(visited, [1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -89,7 +89,8 @@ export class SortedList<T> {
|
||||
}
|
||||
|
||||
public values(): IterableIterator<T> {
|
||||
return this._array.values();
|
||||
// Duplicate the array to avoid issues when _array changes while iterating
|
||||
return [...this._array].values();
|
||||
}
|
||||
|
||||
private _search(key: number): number {
|
||||
|
||||
Vendored
+2
@@ -38,6 +38,8 @@ export interface ITerminalOptions extends IPublicTerminalOptions {
|
||||
|
||||
export type CursorStyle = 'block' | 'underline' | 'bar';
|
||||
|
||||
export type CursorInactiveStyle = 'outline' | 'block' | 'bar' | 'underline' | 'none';
|
||||
|
||||
export type XtermListener = (...args: any[]) => void;
|
||||
|
||||
/**
|
||||
|
||||
@@ -270,8 +270,8 @@ export class Buffer implements IBuffer {
|
||||
private _batchedMemoryCleanup(): boolean {
|
||||
let normalRun = true;
|
||||
if (this._memoryCleanupPosition >= this.lines.length) {
|
||||
// cleanup made it once through all lines, thus rescan in loop below to also catch shifted lines,
|
||||
// which should finish rather quick if there are no more cleanups pending
|
||||
// cleanup made it once through all lines, thus rescan in loop below to also catch shifted
|
||||
// lines, which should finish rather quick if there are no more cleanups pending
|
||||
this._memoryCleanupPosition = 0;
|
||||
normalRun = false;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ export class Marker implements IMarker {
|
||||
private static _nextId = 1;
|
||||
|
||||
public isDisposed: boolean = false;
|
||||
private _disposables: IDisposable[] = [];
|
||||
private readonly _disposables: IDisposable[] = [];
|
||||
|
||||
private _id: number = Marker._nextId++;
|
||||
private readonly _id: number = Marker._nextId++;
|
||||
public get id(): number { return this._id; }
|
||||
|
||||
private readonly _onDispose = this.register(new EventEmitter<void>());
|
||||
|
||||
@@ -380,7 +380,8 @@ export function evaluateKeyboardEvent(
|
||||
result.type = KeyboardResultType.SELECT_ALL;
|
||||
}
|
||||
} else if (ev.key && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.keyCode >= 48 && ev.key.length === 1) {
|
||||
// Include only keys that that result in a _single_ character; don't include num lock, volume up, etc.
|
||||
// Include only keys that that result in a _single_ character; don't include num lock,
|
||||
// volume up, etc.
|
||||
result.key = ev.key;
|
||||
} else if (ev.key && ev.ctrlKey) {
|
||||
if (ev.key === '_') { // ^_
|
||||
|
||||
@@ -28,8 +28,8 @@ export function utf32ToString(data: Uint32Array, start: number = 0, end: number
|
||||
for (let i = start; i < end; ++i) {
|
||||
let codepoint = data[i];
|
||||
if (codepoint > 0xFFFF) {
|
||||
// JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate pair
|
||||
// conversion rules:
|
||||
// JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate
|
||||
// pair conversion rules:
|
||||
// - subtract 0x10000 from code point, leaving a 20 bit number
|
||||
// - add high 10 bits to 0xD800 --> first surrogate
|
||||
// - add low 10 bits to 0xDC00 --> second surrogate
|
||||
|
||||
@@ -181,9 +181,10 @@ export class WriteBuffer extends Disposable {
|
||||
|
||||
/**
|
||||
* If a promise takes long to resolve, we should schedule continuation behind setTimeout.
|
||||
* This might already be too late, if our .then enters really late (executor + prev thens took very long).
|
||||
* This cannot be solved here for the handler itself (it is the handlers responsibility to slice hard work),
|
||||
* but we can at least schedule a screen update as we gain control.
|
||||
* This might already be too late, if our .then enters really late (executor + prev thens
|
||||
* took very long). This cannot be solved here for the handler itself (it is the handlers
|
||||
* responsibility to slice hard work), but we can at least schedule a screen update as we
|
||||
* gain control.
|
||||
*/
|
||||
const continuation: (r: boolean) => void = (r: boolean) => Date.now() - startTime >= WRITE_TIMEOUT_MS
|
||||
? setTimeout(() => this._innerWrite(0, r))
|
||||
|
||||
@@ -532,9 +532,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
} else {
|
||||
if (promiseResult === undefined || this._parseStack.state === ParserStackType.FAIL) {
|
||||
/**
|
||||
* Reject further parsing on improper continuation after pausing.
|
||||
* This is a really bad condition with screwed up execution order and prolly messed up
|
||||
* terminal state, therefore we exit hard with an exception and reject any further parsing.
|
||||
* Reject further parsing on improper continuation after pausing. This is a really bad
|
||||
* condition with screwed up execution order and prolly messed up terminal state,
|
||||
* therefore we exit hard with an exception and reject any further parsing.
|
||||
*
|
||||
* Note: With `Terminal.write` usage this exception should never occur, as the top level
|
||||
* calls are guaranteed to handle async conditions properly. If you ever encounter this
|
||||
@@ -542,9 +542,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
* `InputHandler.parse` or `EscapeSequenceParser.parse` synchronously without waiting for
|
||||
* continuation of a running async handler.
|
||||
*
|
||||
* It is possible to get rid of this error by calling `reset`. But dont rely on that,
|
||||
* as the pending async handler still might mess up the terminal later. Instead fix the faulty
|
||||
* async handling, so this error will not be thrown anymore.
|
||||
* It is possible to get rid of this error by calling `reset`. But dont rely on that, as
|
||||
* the pending async handler still might mess up the terminal later. Instead fix the
|
||||
* faulty async handling, so this error will not be thrown anymore.
|
||||
*/
|
||||
this._parseStack.state = ParserStackType.FAIL;
|
||||
throw new Error('improper continuation due to previous async handler, giving up parsing');
|
||||
|
||||
@@ -43,7 +43,8 @@ export class BufferService extends Disposable implements IBufferService {
|
||||
this.cols = cols;
|
||||
this.rows = rows;
|
||||
this.buffers.resize(cols, rows);
|
||||
// TODO: This doesn't fire when scrollback changes - add a resize event to BufferSet and forward event
|
||||
// TODO: This doesn't fire when scrollback changes - add a resize event to BufferSet and forward
|
||||
// event
|
||||
this._onResize.fire({ cols, rows });
|
||||
}
|
||||
|
||||
|
||||
@@ -35,12 +35,7 @@ export class DecorationService extends Disposable implements IDecorationService
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.register(toDisposable(() => {
|
||||
for (const d of this._decorations.values()) {
|
||||
this._onDecorationRemoved.fire(d);
|
||||
}
|
||||
this.reset();
|
||||
}));
|
||||
this.register(toDisposable(() => this.reset()));
|
||||
}
|
||||
|
||||
public registerDecoration(options: IDecorationOptions): IDecoration | undefined {
|
||||
@@ -92,13 +87,6 @@ export class DecorationService extends Disposable implements IDecorationService
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
for (const d of this._decorations.values()) {
|
||||
this._onDecorationRemoved.fire(d);
|
||||
}
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
class Decoration extends Disposable implements IInternalDecoration {
|
||||
|
||||
@@ -15,6 +15,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
|
||||
cursorBlink: false,
|
||||
cursorStyle: 'block',
|
||||
cursorWidth: 1,
|
||||
cursorInactiveStyle: 'outline',
|
||||
customGlyphs: true,
|
||||
drawBoldTextInBrightColors: true,
|
||||
fastScrollModifier: 'alt',
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
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, IColor, CursorStyle, IOscLinkData } from 'common/Types';
|
||||
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable, IColor, CursorStyle, CursorInactiveStyle, IOscLinkData } from 'common/Types';
|
||||
import { createDecorator } from 'common/services/ServiceRegistry';
|
||||
import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty, ILogger } from 'xterm';
|
||||
|
||||
@@ -212,6 +212,7 @@ export interface ITerminalOptions {
|
||||
cursorBlink?: boolean;
|
||||
cursorStyle?: CursorStyle;
|
||||
cursorWidth?: number;
|
||||
cursorInactiveStyle?: CursorInactiveStyle;
|
||||
customGlyphs?: boolean;
|
||||
disableStdin?: boolean;
|
||||
drawBoldTextInBrightColors?: boolean;
|
||||
|
||||
Vendored
+56
-38
@@ -19,7 +19,8 @@ declare module 'xterm-headless' {
|
||||
export interface ITerminalOptions {
|
||||
/**
|
||||
* Whether to allow the use of proposed API. When false, any usage of APIs
|
||||
* marked as experimental/proposed will throw an error. The default is false.
|
||||
* marked as experimental/proposed will throw an error. The default is
|
||||
* false.
|
||||
*/
|
||||
allowProposedApi?: boolean;
|
||||
|
||||
@@ -63,13 +64,13 @@ declare module 'xterm-headless' {
|
||||
cursorWidth?: number;
|
||||
|
||||
/**
|
||||
* Whether to draw custom glyphs for block element and box drawing characters instead of using
|
||||
* the font. This should typically result in better rendering with continuous lines, even when
|
||||
* line height and letter spacing is used. Note that this doesn't work with the DOM renderer
|
||||
* which renders all characters using the font. The default is true.
|
||||
* Whether to draw custom glyphs for block element and box drawing
|
||||
* characters instead of using the font. This should typically result in
|
||||
* better rendering with continuous lines, even when line height and letter
|
||||
* spacing is used. Note that this doesn't work with the DOM renderer which
|
||||
* renders all characters using the font. The default is true.
|
||||
*/
|
||||
customGlyphs?: boolean;
|
||||
|
||||
/**
|
||||
* Whether input should be disabled.
|
||||
*/
|
||||
@@ -217,9 +218,9 @@ declare module 'xterm-headless' {
|
||||
windowsPty?: IWindowsPty;
|
||||
|
||||
/**
|
||||
* A string containing all characters that are considered word separated by the
|
||||
* double click to select work logic.
|
||||
*/
|
||||
* A string containing all characters that are considered word separated by
|
||||
* the double click to select work logic.
|
||||
*/
|
||||
wordSeparator?: string;
|
||||
|
||||
/**
|
||||
@@ -314,23 +315,23 @@ declare module 'xterm-headless' {
|
||||
*/
|
||||
export interface ILogger {
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* debug.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to debug.
|
||||
*/
|
||||
debug(message: string, ...args: any[]): void;
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* info or below.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to info or below.
|
||||
*/
|
||||
info(message: string, ...args: any[]): void;
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* warn or below.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to warn or below.
|
||||
*/
|
||||
warn(message: string, ...args: any[]): void;
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* error or below.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to error or below.
|
||||
*/
|
||||
error(message: string | Error, ...args: any[]): void;
|
||||
}
|
||||
@@ -355,29 +356,32 @@ declare module 'xterm-headless' {
|
||||
* is trimmed and lines are added or removed. This is a single line that may
|
||||
* be part of a larger wrapped line.
|
||||
*/
|
||||
export interface IMarker extends IDisposable {
|
||||
export interface IMarker extends IDisposableWithEvent {
|
||||
/**
|
||||
* A unique identifier for this marker.
|
||||
*/
|
||||
readonly id: number;
|
||||
|
||||
/**
|
||||
* Whether this marker is disposed.
|
||||
*/
|
||||
readonly isDisposed: boolean;
|
||||
|
||||
/**
|
||||
* The actual line index in the buffer at this point in time. This is set to
|
||||
* -1 if the marker has been disposed.
|
||||
*/
|
||||
readonly line: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a disposable that tracks is disposed state.
|
||||
*/
|
||||
export interface IDisposableWithEvent extends IDisposable {
|
||||
/**
|
||||
* Event listener to get notified when the marker gets disposed. Automatic disposal
|
||||
* might happen for a marker, that got invalidated by scrolling out or removal of
|
||||
* a line from the buffer.
|
||||
* Event listener to get notified when this gets disposed.
|
||||
*/
|
||||
onDispose: IEvent<void>;
|
||||
|
||||
/**
|
||||
* Whether this is disposed.
|
||||
*/
|
||||
readonly isDisposed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,7 +401,8 @@ declare module 'xterm-headless' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable various window manipulation and report features (CSI Ps ; Ps ; Ps t).
|
||||
* Enable various window manipulation and report features
|
||||
* (`CSI Ps ; Ps ; Ps t`).
|
||||
*
|
||||
* Most settings have no default implementation, as they heavily rely on
|
||||
* the embedding environment.
|
||||
@@ -417,10 +422,10 @@ declare module 'xterm-headless' {
|
||||
*
|
||||
* Note on security:
|
||||
* Most features are meant to deal with some information of the host machine
|
||||
* where the terminal runs on. This is seen as a security risk possibly leaking
|
||||
* sensitive data of the host to the program in the terminal. Therefore all options
|
||||
* (even those without a default implementation) are guarded by the boolean flag
|
||||
* and disabled by default.
|
||||
* where the terminal runs on. This is seen as a security risk possibly
|
||||
* leaking sensitive data of the host to the program in the terminal.
|
||||
* Therefore all options (even those without a default implementation) are
|
||||
* guarded by the boolean flag and disabled by default.
|
||||
*/
|
||||
export interface IWindowOptions {
|
||||
/**
|
||||
@@ -601,23 +606,36 @@ declare module 'xterm-headless' {
|
||||
readonly modes: IModes;
|
||||
|
||||
/**
|
||||
* Gets or sets the terminal options. This supports setting multiple options.
|
||||
* Gets or sets the terminal options. This supports setting multiple
|
||||
* options.
|
||||
*
|
||||
* @example Get a single option
|
||||
* ```typescript
|
||||
* ```ts
|
||||
* console.log(terminal.options.fontSize);
|
||||
* ```
|
||||
*
|
||||
* @example Set a single option
|
||||
* ```typescript
|
||||
* @example Set a single option:
|
||||
* ```ts
|
||||
* terminal.options.fontSize = 12;
|
||||
* ```
|
||||
* Note that for options that are object, a new object must be used in order
|
||||
* to take effect as a reference comparison will be done:
|
||||
* ```ts
|
||||
* const newValue = terminal.options.theme;
|
||||
* newValue.background = '#000000';
|
||||
*
|
||||
* // This won't work
|
||||
* terminal.options.theme = newValue;
|
||||
*
|
||||
* // This will work
|
||||
* terminal.options.theme = { ...newValue };
|
||||
* ```
|
||||
*
|
||||
* @example Set multiple options
|
||||
* ```typescript
|
||||
* ```ts
|
||||
* terminal.options = {
|
||||
* fontSize: 12,
|
||||
* fontFamily: 'Courier New',
|
||||
* fontFamily: 'Courier New'
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
@@ -1283,6 +1301,6 @@ declare module 'xterm-headless' {
|
||||
/**
|
||||
* Auto-Wrap Mode (DECAWM): `CSI ? 7 h`
|
||||
*/
|
||||
readonly wraparoundMode: boolean
|
||||
readonly wraparoundMode: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+126
-108
@@ -26,7 +26,8 @@ declare module 'xterm' {
|
||||
export interface ITerminalOptions {
|
||||
/**
|
||||
* Whether to allow the use of proposed API. When false, any usage of APIs
|
||||
* marked as experimental/proposed will throw an error. The default is false.
|
||||
* marked as experimental/proposed will throw an error. The default is
|
||||
* false.
|
||||
*/
|
||||
allowProposedApi?: boolean;
|
||||
|
||||
@@ -60,7 +61,7 @@ declare module 'xterm' {
|
||||
cursorBlink?: boolean;
|
||||
|
||||
/**
|
||||
* The style of the cursor.
|
||||
* The style of the cursor when the terminal is focused.
|
||||
*/
|
||||
cursorStyle?: 'block' | 'underline' | 'bar';
|
||||
|
||||
@@ -70,10 +71,16 @@ declare module 'xterm' {
|
||||
cursorWidth?: number;
|
||||
|
||||
/**
|
||||
* Whether to draw custom glyphs for block element and box drawing characters instead of using
|
||||
* the font. This should typically result in better rendering with continuous lines, even when
|
||||
* line height and letter spacing is used. Note that this doesn't work with the DOM renderer
|
||||
* which renders all characters using the font. The default is true.
|
||||
* The style of the cursor when the terminal is not focused.
|
||||
*/
|
||||
cursorInactiveStyle?: 'outline' | 'block' | 'bar' | 'underline' | 'none';
|
||||
|
||||
/**
|
||||
* Whether to draw custom glyphs for block element and box drawing
|
||||
* characters instead of using the font. This should typically result in
|
||||
* better rendering with continuous lines, even when line height and letter
|
||||
* spacing is used. Note that this doesn't work with the DOM renderer which
|
||||
* renders all characters using the font. The default is true.
|
||||
*/
|
||||
customGlyphs?: boolean;
|
||||
|
||||
@@ -275,9 +282,9 @@ declare module 'xterm' {
|
||||
windowsPty?: IWindowsPty;
|
||||
|
||||
/**
|
||||
* A string containing all characters that are considered word separated by the
|
||||
* double click to select work logic.
|
||||
*/
|
||||
* A string containing all characters that are considered word separated by
|
||||
* the double click to select work logic.
|
||||
*/
|
||||
wordSeparator?: string;
|
||||
|
||||
/**
|
||||
@@ -325,7 +332,10 @@ declare module 'xterm' {
|
||||
selectionBackground?: string;
|
||||
/** The selection foreground color */
|
||||
selectionForeground?: string;
|
||||
/** The selection background color when the terminal does not have focus (can be transparent) */
|
||||
/**
|
||||
* The selection background color when the terminal does not have focus (can
|
||||
* be transparent)
|
||||
*/
|
||||
selectionInactiveBackground?: string;
|
||||
/** ANSI black (eg. `\x1b[30m`) */
|
||||
black?: string;
|
||||
@@ -382,23 +392,23 @@ declare module 'xterm' {
|
||||
*/
|
||||
export interface ILogger {
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* debug.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to debug.
|
||||
*/
|
||||
debug(message: string, ...args: any[]): void;
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* info or below.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to info or below.
|
||||
*/
|
||||
info(message: string, ...args: any[]): void;
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* warn or below.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to warn or below.
|
||||
*/
|
||||
warn(message: string, ...args: any[]): void;
|
||||
/**
|
||||
* Log a debug message, this will only be called if {@link ITerminalOptions.logLevel} is set to
|
||||
* error or below.
|
||||
* Log a debug message, this will only be called if
|
||||
* {@link ITerminalOptions.logLevel} is set to error or below.
|
||||
*/
|
||||
error(message: string | Error, ...args: any[]): void;
|
||||
}
|
||||
@@ -438,8 +448,6 @@ declare module 'xterm' {
|
||||
|
||||
/**
|
||||
* Represents a disposable that tracks is disposed state.
|
||||
* @param onDispose event listener and
|
||||
* @param isDisposed property.
|
||||
*/
|
||||
export interface IDisposableWithEvent extends IDisposable {
|
||||
/**
|
||||
@@ -454,7 +462,8 @@ declare module 'xterm' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a decoration in the terminal that is associated with a particular marker and DOM element.
|
||||
* Represents a decoration in the terminal that is associated with a
|
||||
* particular marker and DOM element.
|
||||
*/
|
||||
export interface IDecoration extends IDisposableWithEvent {
|
||||
/*
|
||||
@@ -477,9 +486,9 @@ declare module 'xterm' {
|
||||
element: HTMLElement | undefined;
|
||||
|
||||
/**
|
||||
* The options for the overview ruler that can be updated.
|
||||
* This will only take effect when {@link IDecorationOptions.overviewRulerOptions}
|
||||
* were provided initially.
|
||||
* The options for the overview ruler that can be updated. This will only
|
||||
* take effect when {@link IDecorationOptions.overviewRulerOptions} were
|
||||
* provided initially.
|
||||
*/
|
||||
options: Pick<IDecorationOptions, 'overviewRulerOptions'>;
|
||||
}
|
||||
@@ -526,24 +535,26 @@ declare module 'xterm' {
|
||||
readonly height?: number;
|
||||
|
||||
/**
|
||||
* The background color of the cell(s). When 2 decorations both set the foreground color the
|
||||
* last registered decoration will be used. Only the `#RRGGBB` format is supported.
|
||||
* The background color of the cell(s). When 2 decorations both set the
|
||||
* foreground color the last registered decoration will be used. Only the
|
||||
* `#RRGGBB` format is supported.
|
||||
*/
|
||||
readonly backgroundColor?: string;
|
||||
|
||||
/**
|
||||
* The foreground color of the cell(s). When 2 decorations both set the foreground color the
|
||||
* last registered decoration will be used. Only the `#RRGGBB` format is supported.
|
||||
* The foreground color of the cell(s). When 2 decorations both set the
|
||||
* foreground color the last registered decoration will be used. Only the
|
||||
* `#RRGGBB` format is supported.
|
||||
*/
|
||||
readonly foregroundColor?: string;
|
||||
|
||||
/**
|
||||
* What layer to render the decoration at when {@link backgroundColor} or
|
||||
* {@link foregroundColor} are used. `'bottom'` will render under the selection, `'top`' will
|
||||
* render above the selection\*.
|
||||
* {@link foregroundColor} are used. `'bottom'` will render under the
|
||||
* selection, `'top`' will render above the selection\*.
|
||||
*
|
||||
* *\* The selection will render on top regardless of layer on the canvas renderer due to how
|
||||
* it renders selection separately.*
|
||||
* *\* The selection will render on top regardless of layer on the canvas
|
||||
* renderer due to how it renders selection separately.*
|
||||
*/
|
||||
readonly layer?: 'bottom' | 'top';
|
||||
|
||||
@@ -554,7 +565,7 @@ declare module 'xterm' {
|
||||
* @param color The color of the decoration.
|
||||
* @param position The position of the decoration.
|
||||
*/
|
||||
overviewRulerOptions?: IDecorationOverviewRulerOptions
|
||||
overviewRulerOptions?: IDecorationOverviewRulerOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,7 +585,8 @@ declare module 'xterm' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable various window manipulation and report features (CSI Ps ; Ps ; Ps t).
|
||||
* Enable various window manipulation and report features
|
||||
* (`CSI Ps ; Ps ; Ps t`).
|
||||
*
|
||||
* Most settings have no default implementation, as they heavily rely on
|
||||
* the embedding environment.
|
||||
@@ -594,10 +606,10 @@ declare module 'xterm' {
|
||||
*
|
||||
* Note on security:
|
||||
* Most features are meant to deal with some information of the host machine
|
||||
* where the terminal runs on. This is seen as a security risk possibly leaking
|
||||
* sensitive data of the host to the program in the terminal. Therefore all options
|
||||
* (even those without a default implementation) are guarded by the boolean flag
|
||||
* and disabled by default.
|
||||
* where the terminal runs on. This is seen as a security risk possibly
|
||||
* leaking sensitive data of the host to the program in the terminal.
|
||||
* Therefore all options (even those without a default implementation) are
|
||||
* guarded by the boolean flag and disabled by default.
|
||||
*/
|
||||
export interface IWindowOptions {
|
||||
/**
|
||||
@@ -865,9 +877,9 @@ declare module 'xterm' {
|
||||
onData: IEvent<string>;
|
||||
|
||||
/**
|
||||
* Adds an event listener for when a key is pressed. The event value contains the
|
||||
* string that will be sent in the data event as well as the DOM event that
|
||||
* triggered it.
|
||||
* Adds an event listener for when a key is pressed. The event value
|
||||
* contains the string that will be sent in the data event as well as the
|
||||
* DOM event that triggered it.
|
||||
* @returns an `IDisposable` to stop listening.
|
||||
*/
|
||||
onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>;
|
||||
@@ -1035,10 +1047,11 @@ declare module 'xterm' {
|
||||
|
||||
/**
|
||||
* (EXPERIMENTAL) Adds a decoration to the terminal using
|
||||
* @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.
|
||||
* @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.
|
||||
*/
|
||||
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
|
||||
|
||||
@@ -1144,7 +1157,8 @@ declare module 'xterm' {
|
||||
writeln(data: string | Uint8Array, callback?: () => void): void;
|
||||
|
||||
/**
|
||||
* Writes text to the terminal, performing the necessary transformations for pasted text.
|
||||
* Writes text to the terminal, performing the necessary transformations for
|
||||
* pasted text.
|
||||
* @param data The text to write to the terminal.
|
||||
*/
|
||||
paste(data: string): void;
|
||||
@@ -1158,10 +1172,10 @@ declare module 'xterm' {
|
||||
refresh(start: number, end: number): void;
|
||||
|
||||
/**
|
||||
* Clears the texture atlas of the canvas renderer if it's active. Doing this will force a
|
||||
* redraw of all glyphs which can workaround issues causing the texture to become corrupt, for
|
||||
* example Chromium/Nvidia has an issue where the texture gets messed up when resuming the OS
|
||||
* from sleep.
|
||||
* Clears the texture atlas of the canvas renderer if it's active. Doing
|
||||
* this will force a redraw of all glyphs which can workaround issues
|
||||
* causing the texture to become corrupt, for example Chromium/Nvidia has an
|
||||
* issue where the texture gets messed up when resuming the OS from sleep.
|
||||
*/
|
||||
clearTextureAtlas(): void;
|
||||
|
||||
@@ -1232,32 +1246,34 @@ declare module 'xterm' {
|
||||
* @param text The text of the link.
|
||||
* @param range The buffer range of the link.
|
||||
*/
|
||||
activate(event: MouseEvent, text: string, range: IBufferRange): void;
|
||||
activate(event: MouseEvent, text: string, range: IBufferRange): void;
|
||||
|
||||
/**
|
||||
* Called when the mouse hovers the link. To use this to create a DOM-based hover tooltip,
|
||||
* create the hover element within `Terminal.element` and add the `xterm-hover` class to it,
|
||||
* that will cause mouse events to not fall through and activate other links.
|
||||
* @param event The mouse event triggering the callback.
|
||||
* @param text The text of the link.
|
||||
* @param range The buffer range of the link.
|
||||
*/
|
||||
hover?(event: MouseEvent, text: string, range: IBufferRange): void;
|
||||
/**
|
||||
* Called when the mouse hovers the link. To use this to create a DOM-based
|
||||
* hover tooltip, create the hover element within `Terminal.element` and
|
||||
* add the `xterm-hover` class to it, that will cause mouse events to not
|
||||
* fall through and activate other links.
|
||||
* @param event The mouse event triggering the callback.
|
||||
* @param text The text of the link.
|
||||
* @param range The buffer range of the link.
|
||||
*/
|
||||
hover?(event: MouseEvent, text: string, range: IBufferRange): void;
|
||||
|
||||
/**
|
||||
* Called when the mouse leaves the link.
|
||||
* @param event The mouse event triggering the callback.
|
||||
* @param text The text of the link.
|
||||
* @param range The buffer range of the link.
|
||||
*/
|
||||
leave?(event: MouseEvent, text: string, range: IBufferRange): void;
|
||||
/**
|
||||
* Called when the mouse leaves the link.
|
||||
* @param event The mouse event triggering the callback.
|
||||
* @param text The text of the link.
|
||||
* @param range The buffer range of the link.
|
||||
*/
|
||||
leave?(event: MouseEvent, text: string, range: IBufferRange): void;
|
||||
|
||||
/**
|
||||
* Whether to receive non-HTTP URLs from LinkProvider. When false, any usage of non-HTTP URLs
|
||||
* will be ignored. Enabling this option without proper protection in `activate` function
|
||||
* may cause security issues such as XSS.
|
||||
*/
|
||||
allowNonHttpProtocols?: boolean;
|
||||
/**
|
||||
* Whether to receive non-HTTP URLs from LinkProvider. When false, any
|
||||
* usage of non-HTTP URLs will be ignored. Enabling this option without
|
||||
* proper protection in `activate` function may cause security issues such
|
||||
* as XSS.
|
||||
*/
|
||||
allowNonHttpProtocols?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1289,9 +1305,9 @@ declare module 'xterm' {
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* What link decorations to show when hovering the link, this property is tracked and changes
|
||||
* made after the link is provided will trigger changes. If not set, all decroations will be
|
||||
* enabled.
|
||||
* What link decorations to show when hovering the link, this property is
|
||||
* tracked and changes made after the link is provided will trigger changes.
|
||||
* If not set, all decroations will be enabled.
|
||||
*/
|
||||
decorations?: ILinkDecorations;
|
||||
|
||||
@@ -1303,9 +1319,10 @@ declare module 'xterm' {
|
||||
activate(event: MouseEvent, text: string): void;
|
||||
|
||||
/**
|
||||
* Called when the mouse hovers the link. To use this to create a DOM-based hover tooltip,
|
||||
* create the hover element within `Terminal.element` and add the `xterm-hover` class to it,
|
||||
* that will cause mouse events to not fall through and activate other links.
|
||||
* Called when the mouse hovers the link. To use this to create a DOM-based
|
||||
* hover tooltip, create the hover element within `Terminal.element` and add
|
||||
* the `xterm-hover` class to it, that will cause mouse events to not fall
|
||||
* through and activate other links.
|
||||
* @param event The mouse event triggering the callback.
|
||||
* @param text The text of the link.
|
||||
*/
|
||||
@@ -1429,7 +1446,8 @@ declare module 'xterm' {
|
||||
|
||||
export interface IBufferElementProvider {
|
||||
/**
|
||||
* Provides a document fragment or HTMLElement containing the buffer elements.
|
||||
* Provides a document fragment or HTMLElement containing the buffer
|
||||
* elements.
|
||||
*/
|
||||
provideBufferElements(): DocumentFragment | HTMLElement;
|
||||
}
|
||||
@@ -1676,43 +1694,43 @@ declare module 'xterm' {
|
||||
export interface IParser {
|
||||
/**
|
||||
* Adds a handler for CSI escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {final: 'm'} for SGR.
|
||||
* @param id Specifies the function identifier under which the callback gets
|
||||
* registered, e.g. {final: 'm'} for SGR.
|
||||
* @param callback The function to handle the sequence. The callback is
|
||||
* called with the numerical params. If the sequence has subparams the
|
||||
* array will contain subarrays with their numercial values.
|
||||
* Return `true` if the sequence was handled, `false` if the parser should try
|
||||
* a previous handler. The most recently added handler is tried first.
|
||||
* called with the numerical params. If the sequence has subparams the array
|
||||
* will contain subarrays with their numercial values. Return `true` if the
|
||||
* sequence was handled, `false` if the parser should try a previous
|
||||
* handler. The most recently added handler is tried first.
|
||||
* @returns An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean | Promise<boolean>): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for DCS escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {intermediates: '$' final: 'q'} for DECRQSS.
|
||||
* @param id Specifies the function identifier under which the callback gets
|
||||
* registered, e.g. {intermediates: '$' final: 'q'} for DECRQSS.
|
||||
* @param callback The function to handle the sequence. Note that the
|
||||
* function will only be called once if the sequence finished sucessfully.
|
||||
* There is currently no way to intercept smaller data chunks, data chunks
|
||||
* will be stored up until the sequence is finished. Since DCS sequences
|
||||
* are not limited by the amount of data this might impose a problem for
|
||||
* big payloads. Currently xterm.js limits DCS payload to 10 MB
|
||||
* which should give enough room for most use cases.
|
||||
* The function gets the payload and numerical parameters as arguments.
|
||||
* Return `true` if the sequence was handled, `false` if the parser should try
|
||||
* a previous handler. The most recently added handler is tried first.
|
||||
* will be stored up until the sequence is finished. Since DCS sequences are
|
||||
* not limited by the amount of data this might impose a problem for big
|
||||
* payloads. Currently xterm.js limits DCS payload to 10 MB which should
|
||||
* give enough room for most use cases. The function gets the payload and
|
||||
* numerical parameters as arguments. Return `true` if the sequence was
|
||||
* handled, `false` if the parser should try a previous handler. The most
|
||||
* recently added handler is tried first.
|
||||
* @returns An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean | Promise<boolean>): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for ESC escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {intermediates: '%' final: 'G'} for
|
||||
* default charset selection.
|
||||
* @param id Specifies the function identifier under which the callback gets
|
||||
* registered, e.g. {intermediates: '%' final: 'G'} for default charset
|
||||
* selection.
|
||||
* @param callback The function to handle the sequence.
|
||||
* Return `true` if the sequence was handled, `false` if the parser should try
|
||||
* a previous handler. The most recently added handler is tried first.
|
||||
* Return `true` if the sequence was handled, `false` if the parser should
|
||||
* try a previous handler. The most recently added handler is tried first.
|
||||
* @returns An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerEscHandler(id: IFunctionIdentifier, handler: () => boolean | Promise<boolean>): IDisposable;
|
||||
@@ -1723,13 +1741,13 @@ declare module 'xterm' {
|
||||
* @param callback The function to handle the sequence. Note that the
|
||||
* function will only be called once if the sequence finished sucessfully.
|
||||
* There is currently no way to intercept smaller data chunks, data chunks
|
||||
* will be stored up until the sequence is finished. Since OSC sequences
|
||||
* are not limited by the amount of data this might impose a problem for
|
||||
* big payloads. Currently xterm.js limits OSC payload to 10 MB
|
||||
* which should give enough room for most use cases.
|
||||
* The callback is called with OSC data string.
|
||||
* Return `true` if the sequence was handled, `false` if the parser should try
|
||||
* a previous handler. The most recently added handler is tried first.
|
||||
* will be stored up until the sequence is finished. Since OSC sequences are
|
||||
* not limited by the amount of data this might impose a problem for big
|
||||
* payloads. Currently xterm.js limits OSC payload to 10 MB which should
|
||||
* give enough room for most use cases. The callback is called with OSC data
|
||||
* string. Return `true` if the sequence was handled, `false` if the parser
|
||||
* should try a previous handler. The most recently added handler is tried
|
||||
* first.
|
||||
* @returns An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable;
|
||||
@@ -1816,6 +1834,6 @@ declare module 'xterm' {
|
||||
/**
|
||||
* Auto-Wrap Mode (DECAWM): `CSI ? 7 h`
|
||||
*/
|
||||
readonly wraparoundMode: boolean
|
||||
readonly wraparoundMode: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user