Merge branch 'master' into webgl-cursor

This commit is contained in:
Daniel Imms
2023-07-28 13:42:47 -07:00
committed by GitHub
14 changed files with 1522 additions and 1470 deletions
+12 -2
View File
@@ -87,7 +87,12 @@
],
"@typescript-eslint/naming-convention": [
"warn",
{ "selector": "default", "format": ["camelCase"] },
{ "selector": "default", "format": ["camelCase"],
"filter": {
"regex": "^[a-z]",
"match": true
}
},
// variableLike
{ "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "variable", "filter": "^I.+Service$", "format": ["PascalCase"], "prefix": ["I"] },
@@ -96,7 +101,12 @@
{ "selector": "memberLike", "modifiers": ["protected"], "format": ["camelCase"], "leadingUnderscore": "require" },
{ "selector": "enumMember", "format": ["UPPER_CASE"] },
// memberLike - Allow enum-like objects to use UPPER_CASE
{ "selector": "property", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "property", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"],
"filter": {
"regex": "^[a-z]",
"match": true
}
},
// restrict on* naming for events only
{ "selector": "method", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"], "custom": {
"regex": "^on[A-Z].+",
+4
View File
@@ -28,6 +28,10 @@ opening an issue, read these pointers.
- Be polite. Issues with an indignant or belligerent tone tend to be moved to the
bottom of the pile.
## Answering discussion questions
Issues are only meant to track (likely) feature requests and bugs. We use [GitHub Discussions](https://github.com/xtermjs/xterm.js/discussions) for general Q&A as well as discussing possible features. If you want to help out, many questions get asked over at the [discussions page](https://github.com/xtermjs/xterm.js/discussions) which could use an expert as the core team is often stretched thin.
## Contributing code
You can find issues to work on by looking at the [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) issues. It's a good idea to comment on the issue saying that you're taking it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute:
+3
View File
@@ -216,6 +216,9 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**Super Terminal**](https://github.com/bugwheels94/super-terminal): It is a http based terminal for developers who dont like repetition and save time.
- [**graSSHopper**](https://grasshopper.coding.kiwi): A simple SSH client with file explorer, history and many more features.
- [**DomTerm**](https://domterm.org/xtermjs.html): Tiles and tabs. Detachable sessions (like tmux). [Remote connections](https://domterm.org/Remoting-over-ssh.html) using a nice ssh wrapper with predictive echo. Qt, Electron, Tauri/Wry, or desktop browser front-ends. Choose between xterm.js engine (faster) or native DomTerm (more functionality and graphics) - or both.
- [**Cloudtutor.io**](https://cloudtutor.io): innovative online learning platform that offers users access to an interactive lab.
- [**Helix Editor Playground**](https://github.com/tomgroenwoldt/helix-editor-playground): Online playground for the terminal based helix editor.
- [**Coder**](https://github.com/coder/coder): Self-Hosted Remote Development Environments
- [And much more...](https://github.com/xtermjs/xterm.js/network/dependents?package_id=UGFja2FnZS0xNjYzMjc4OQ%3D%3D)
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only.
@@ -368,6 +368,11 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
protected _drawChars(cell: ICellData, x: number, y: number): void {
const chars = cell.getChars();
this._cellColorResolver.resolve(cell, x, this._bufferService.buffer.ydisp + y);
if (!this._charAtlas) {
return;
}
let glyph: IRasterizedGlyph;
if (chars && chars.length > 1) {
glyph = this._charAtlas.getRasterizedGlyphCombinedChar(chars, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
+4 -4
View File
@@ -62,12 +62,12 @@
"@types/utf8": "^3.0.0",
"@types/webpack": "^5.28.0",
"@types/ws": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^5.3.0",
"@typescript-eslint/parser": "^5.3.0",
"@typescript-eslint/eslint-plugin": "^6.2.00",
"@typescript-eslint/parser": "^6.2.00",
"chai": "^4.3.4",
"cross-env": "^7.0.3",
"deep-equal": "^2.0.5",
"eslint": "^8.1.0",
"eslint": "^8.45.0",
"eslint-plugin-jsdoc": "^39.3.6",
"express": "^4.17.1",
"express-ws": "^5.0.2",
@@ -81,7 +81,7 @@
"source-map-loader": "^3.0.0",
"source-map-support": "^0.5.20",
"ts-loader": "^9.3.1",
"typescript": "4.7",
"typescript": "^5.1.6",
"utf8": "^3.0.0",
"webpack": "^5.61.0",
"webpack-cli": "^4.9.1",
+11 -12
View File
@@ -13,6 +13,7 @@ import { color } from 'common/Color';
import { EventEmitter } from 'common/EventEmitter';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services';
import { createStyle, IStyleSheet } from './StyleSheet';
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
const ROW_CONTAINER_CLASS = 'xterm-rows';
@@ -32,8 +33,8 @@ export class DomRenderer extends Disposable implements IRenderer {
private _rowFactory: DomRendererRowFactory;
private _terminalClass: number = nextTerminalId++;
private _themeStyleElement!: HTMLStyleElement;
private _dimensionsStyleElement!: HTMLStyleElement;
private _themeStyle!: IStyleSheet;
private _dimensionsStyle!: IStyleSheet;
private _rowContainer: HTMLElement;
private _rowElements: HTMLElement[] = [];
private _selectionContainer: HTMLElement;
@@ -88,8 +89,8 @@ export class DomRenderer extends Disposable implements IRenderer {
// https://github.com/xtermjs/xterm.js/issues/2960
this._rowContainer.remove();
this._selectionContainer.remove();
this._themeStyleElement.remove();
this._dimensionsStyleElement.remove();
this._themeStyle.dispose();
this._dimensionsStyle.dispose();
}));
}
@@ -116,9 +117,8 @@ export class DomRenderer extends Disposable implements IRenderer {
element.style.overflow = 'hidden';
}
if (!this._dimensionsStyleElement) {
this._dimensionsStyleElement = document.createElement('style');
this._screenElement.appendChild(this._dimensionsStyleElement);
if (!this._dimensionsStyle) {
this._dimensionsStyle = createStyle(this._screenElement);
}
const styles =
@@ -129,7 +129,7 @@ export class DomRenderer extends Disposable implements IRenderer {
` width: ${this.dimensions.css.cell.width}px` +
`}`;
this._dimensionsStyleElement.textContent = styles;
this._dimensionsStyle.setCss(styles);
this._selectionContainer.style.height = this._viewportElement.style.height;
this._screenElement.style.width = `${this.dimensions.css.canvas.width}px`;
@@ -137,9 +137,8 @@ export class DomRenderer extends Disposable implements IRenderer {
}
private _injectCss(colors: ReadonlyColorSet): void {
if (!this._themeStyleElement) {
this._themeStyleElement = document.createElement('style');
this._screenElement.appendChild(this._themeStyleElement);
if (!this._themeStyle) {
this._themeStyle = createStyle(this._screenElement);
}
// Base CSS
@@ -236,7 +235,7 @@ export class DomRenderer extends Disposable implements IRenderer {
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
this._themeStyleElement.textContent = styles;
this._themeStyle.setCss(styles);
}
public handleDevicePixelRatioChange(): void {
+39
View File
@@ -0,0 +1,39 @@
export interface IStyleSheet {
dispose: () => void;
setCss: (value: string) => void;
}
const createCssStyleSheet = (): IStyleSheet => {
const sheet = new CSSStyleSheet();
document.adoptedStyleSheets.push(sheet);
return {
dispose() {
const index = document.adoptedStyleSheets.indexOf(sheet);
document.adoptedStyleSheets.splice(index, 1);
},
setCss(css) {
sheet.replaceSync(css);
}
};
};
const createStyleElement = (parent: HTMLElement): IStyleSheet => {
const element = document.createElement('style');
parent.append(element);
return {
dispose() {
element.remove();
},
setCss(css) {
element.textContent = css;
}
};
};
export const createStyle = (parent: HTMLElement): IStyleSheet => {
try {
return createCssStyleSheet();
} catch {
return createStyleElement(parent);
}
};
+9 -9
View File
@@ -1950,14 +1950,14 @@ describe('InputHandler', () => {
inputHandler.onColor(ev => stack.push(ev));
// single color query
await inputHandler.parseP('\x1b]4;0;?\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 0 }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 0 as ColorIndex }]]);
stack.length = 0;
await inputHandler.parseP('\x1b]4;123;?\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 123 }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 123 as ColorIndex }]]);
stack.length = 0;
// multiple queries
await inputHandler.parseP('\x1b]4;0;?;123;?\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 0 }, { type: ColorRequestType.REPORT, index: 123 }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 0 as ColorIndex }, { type: ColorRequestType.REPORT, index: 123 as ColorIndex }]]);
stack.length = 0;
});
it('4: set color events', async () => {
@@ -1965,32 +1965,32 @@ describe('InputHandler', () => {
inputHandler.onColor(ev => stack.push(ev));
// single color query
await inputHandler.parseP('\x1b]4;0;rgb:01/02/03\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [1, 2, 3] }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0 as ColorIndex, color: [1, 2, 3] }]]);
stack.length = 0;
await inputHandler.parseP('\x1b]4;123;#aabbcc\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 123, color: [170, 187, 204] }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 123 as ColorIndex, color: [170, 187, 204] }]]);
stack.length = 0;
// multiple queries
await inputHandler.parseP('\x1b]4;0;rgb:aa/bb/cc;123;#001122\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123, color: [0, 17, 34] }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0 as ColorIndex, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123 as ColorIndex, color: [0, 17, 34] }]]);
stack.length = 0;
});
it('4: should ignore invalid values', async () => {
const stack: IColorEvent[] = [];
inputHandler.onColor(ev => stack.push(ev));
await inputHandler.parseP('\x1b]4;0;rgb:aa/bb/cc;45;rgb:1/22/333;123;#001122\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123, color: [0, 17, 34] }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0 as ColorIndex, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123 as ColorIndex, color: [0, 17, 34] }]]);
stack.length = 0;
});
it('104: restore events', async () => {
const stack: IColorEvent[] = [];
inputHandler.onColor(ev => stack.push(ev));
await inputHandler.parseP('\x1b]104;0\x07\x1b]104;43\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: 0 }], [{ type: ColorRequestType.RESTORE, index: 43 }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: 0 as ColorIndex }], [{ type: ColorRequestType.RESTORE, index: 43 as ColorIndex }]]);
stack.length = 0;
// multiple in one command
await inputHandler.parseP('\x1b]104;0;43\x07');
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: 0 }, { type: ColorRequestType.RESTORE, index: 43 }]]);
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: 0 as ColorIndex }, { type: ColorRequestType.RESTORE, index: 43 as ColorIndex }]]);
stack.length = 0;
// full ANSI table restore
await inputHandler.parseP('\x1b]104\x07');
+12 -11
View File
@@ -33,7 +33,8 @@ const LOG_PREFIX = 'xterm.js: ';
export class LogService extends Disposable implements ILogService {
public serviceBrand: any;
public logLevel: LogLevelEnum = LogLevelEnum.OFF;
private _logLevel: LogLevelEnum = LogLevelEnum.OFF;
public get logLevel(): LogLevelEnum { return this._logLevel; }
constructor(
@IOptionsService private readonly _optionsService: IOptionsService
@@ -44,7 +45,7 @@ export class LogService extends Disposable implements ILogService {
}
private _updateLogLevel(): void {
this.logLevel = optionsKeyToLogLevel[this._optionsService.rawOptions.logLevel];
this._logLevel = optionsKeyToLogLevel[this._optionsService.rawOptions.logLevel];
}
private _evalLazyOptionalParams(optionalParams: any[]): void {
@@ -57,30 +58,30 @@ export class LogService extends Disposable implements ILogService {
private _log(type: LogType, message: string, optionalParams: any[]): void {
this._evalLazyOptionalParams(optionalParams);
type.call(console, LOG_PREFIX + message, ...optionalParams);
type.call(console, (this._optionsService.options.logger ? '' : LOG_PREFIX) + message, ...optionalParams);
}
public debug(message: string, ...optionalParams: any[]): void {
if (this.logLevel <= LogLevelEnum.DEBUG) {
this._log(console.log, message, optionalParams);
if (this._logLevel <= LogLevelEnum.DEBUG) {
this._log(this._optionsService.options.logger?.debug.bind(this._optionsService.options.logger) ?? console.log, message, optionalParams);
}
}
public info(message: string, ...optionalParams: any[]): void {
if (this.logLevel <= LogLevelEnum.INFO) {
this._log(console.info, message, optionalParams);
if (this._logLevel <= LogLevelEnum.INFO) {
this._log(this._optionsService.options.logger?.info.bind(this._optionsService.options.logger) ?? console.info, message, optionalParams);
}
}
public warn(message: string, ...optionalParams: any[]): void {
if (this.logLevel <= LogLevelEnum.WARN) {
this._log(console.warn, message, optionalParams);
if (this._logLevel <= LogLevelEnum.WARN) {
this._log(this._optionsService.options.logger?.warn.bind(this._optionsService.options.logger) ?? console.warn, message, optionalParams);
}
}
public error(message: string, ...optionalParams: any[]): void {
if (this.logLevel <= LogLevelEnum.ERROR) {
this._log(console.error, message, optionalParams);
if (this._logLevel <= LogLevelEnum.ERROR) {
this._log(this._optionsService.options.logger?.error.bind(this._optionsService.options.logger) ?? console.error, message, optionalParams);
}
}
}
+1
View File
@@ -27,6 +27,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
letterSpacing: 0,
linkHandler: null,
logLevel: 'info',
logger: null,
scrollback: 1000,
scrollOnUserInput: true,
scrollSensitivity: 1,
+3 -2
View File
@@ -7,7 +7,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 { createDecorator } from 'common/services/ServiceRegistry';
import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty } from 'xterm';
import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty, ILogger } from 'xterm';
export const IBufferService = createDecorator<IBufferService>('BufferService');
export interface IBufferService {
@@ -157,7 +157,7 @@ export const ILogService = createDecorator<ILogService>('LogService');
export interface ILogService {
serviceBrand: undefined;
logLevel: LogLevelEnum;
readonly logLevel: LogLevelEnum;
debug(message: any, ...optionalParams: any[]): void;
info(message: any, ...optionalParams: any[]): void;
@@ -229,6 +229,7 @@ export interface ITerminalOptions {
lineHeight?: number;
linkHandler?: ILinkHandler | null;
logLevel?: LogLevel;
logger?: ILogger | null;
macOptionIsMeta?: boolean;
macOptionClickForcesSelection?: boolean;
minimumContrastRatio?: number;
+31
View File
@@ -107,6 +107,11 @@ declare module 'xterm-headless' {
*/
logLevel?: LogLevel;
/**
* A logger to use instead of `console`.
*/
logger?: ILogger | null;
/**
* Whether to treat option as the meta key.
*/
@@ -304,6 +309,32 @@ declare module 'xterm-headless' {
buildNumber?: number;
}
/**
* A replacement logger for `console`.
*/
export interface ILogger {
/**
* 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.
*/
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.
*/
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.
*/
error(message: string | Error, ...args: any[]): void;
}
/**
* An object that can be disposed via a dispose function.
*/
+31
View File
@@ -152,6 +152,11 @@ declare module 'xterm' {
*/
logLevel?: LogLevel;
/**
* A logger to use instead of `console`.
*/
logger?: ILogger | null;
/**
* Whether to treat option as the meta key.
*/
@@ -365,6 +370,32 @@ declare module 'xterm' {
buildNumber?: number;
}
/**
* A replacement logger for `console`.
*/
export interface ILogger {
/**
* 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.
*/
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.
*/
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.
*/
error(message: string | Error, ...args: any[]): void;
}
/**
* An object that can be disposed via a dispose function.
*/
+1357 -1430
View File
File diff suppressed because it is too large Load Diff