Merge branch 'master' into clusters

This commit is contained in:
Per Bothner
2023-06-27 13:31:23 -07:00
committed by GitHub
24 changed files with 467 additions and 98 deletions
+23 -19
View File
@@ -57,7 +57,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
protected _inputHandler: InputHandler;
private _writeBuffer: WriteBuffer;
private _windowsMode: IDisposable | undefined;
private _windowsWrappingHeuristics: IDisposable | undefined;
private readonly _onBinary = this.register(new EventEmitter<string>());
public readonly onBinary = this._onBinary.event;
@@ -131,7 +131,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this.register(forwardEvent(this.coreService.onBinary, this._onBinary));
this.register(this.coreService.onRequestScrollToBottom(() => this.scrollToBottom()));
this.register(this.coreService.onUserInput(() => this._writeBuffer.handleUserInput()));
this.register(this.optionsService.onSpecificOptionChange('windowsMode', e => this._handleWindowsModeOptionChange(e)));
this.register(this.optionsService.onMultipleOptionChange(['windowsMode', 'windowsPty'], () => this._handleWindowsPtyOptionChange()));
this.register(this._bufferService.onScroll(event => {
this._onScroll.fire({ position: this._bufferService.buffer.ydisp, source: ScrollSource.TERMINAL });
this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop, this._bufferService.buffer.scrollBottom);
@@ -146,8 +146,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this.register(forwardEvent(this._writeBuffer.onWriteParsed, this._onWriteParsed));
this.register(toDisposable(() => {
this._windowsMode?.dispose();
this._windowsMode = undefined;
this._windowsWrappingHeuristics?.dispose();
this._windowsWrappingHeuristics = undefined;
}));
}
@@ -250,9 +250,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
}
protected _setup(): void {
if (this.optionsService.rawOptions.windowsMode) {
this._enableWindowsMode();
}
this._handleWindowsPtyOptionChange();
}
public reset(): void {
@@ -263,30 +261,36 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this.coreMouseService.reset();
}
private _handleWindowsModeOptionChange(value: boolean): void {
private _handleWindowsPtyOptionChange(): void {
let value = false;
const windowsPty = this.optionsService.rawOptions.windowsPty;
if (windowsPty && windowsPty.buildNumber !== undefined && windowsPty.buildNumber !== undefined) {
value = !!(windowsPty.backend === 'conpty' && windowsPty.buildNumber < 21376);
} else if (this.optionsService.rawOptions.windowsMode) {
value = true;
}
if (value) {
this._enableWindowsMode();
this._enableWindowsWrappingHeuristics();
} else {
this._windowsMode?.dispose();
this._windowsMode = undefined;
this._windowsWrappingHeuristics?.dispose();
this._windowsWrappingHeuristics = undefined;
}
}
protected _enableWindowsMode(): void {
if (!this._windowsMode) {
protected _enableWindowsWrappingHeuristics(): void {
if (!this._windowsWrappingHeuristics) {
const disposables: IDisposable[] = [];
disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));
disposables.push(this.registerCsiHandler({ final: 'H' }, () => {
updateWindowsModeWrappedState(this._bufferService);
return false;
}));
this._windowsMode = {
dispose: () => {
for (const d of disposables) {
d.dispose();
}
this._windowsWrappingHeuristics = toDisposable(() => {
for (const d of disposables) {
d.dispose();
}
};
});
}
}
}
+3 -3
View File
@@ -2222,7 +2222,7 @@ describe('InputHandler', () => {
});
it('ANSI 2 (keyboard action mode)', async () => {
await inputHandler.parseP('\x1b[2$p');
assert.deepEqual(reportStack.pop(), '\x1b[2;3$y'); // always set
assert.deepEqual(reportStack.pop(), '\x1b[2;4$y'); // always reset
});
it('ANSI 4 (insert mode)', async () => {
await inputHandler.parseP('\x1b[4$p');
@@ -2236,7 +2236,7 @@ describe('InputHandler', () => {
});
it('ANSI 12 (send/receive)', async () => {
await inputHandler.parseP('\x1b[12$p');
assert.deepEqual(reportStack.pop(), '\x1b[12;4$y'); // always reset
assert.deepEqual(reportStack.pop(), '\x1b[12;3$y'); // always set
});
it('ANSI 20 (newline mode)', async () => {
await inputHandler.parseP('\x1b[20$p');
@@ -2280,7 +2280,7 @@ describe('InputHandler', () => {
});
it('DEC privates perma modes', async () => {
// [mode number, state value]
const perma = [[3, 0], [8, 3], [1005, 4], [1015, 4], [1048, 1]];
const perma = [[3, 0], [8, 3], [67, 4], [1005, 4], [1015, 4], [1048, 1]];
for (const [mode, value] of perma) {
await inputHandler.parseP(`\x1b[?${mode}$p`);
assert.deepEqual(reportStack.pop(), `\x1b[?${mode};${value}$y`);
+9 -6
View File
@@ -1163,10 +1163,12 @@ export class InputHandler extends Disposable implements IInputHandler {
* @param y row index
*/
private _resetBufferLine(y: number, respectProtect: boolean = false): void {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()), respectProtect);
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
line.isWrapped = false;
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y);
if (line) {
line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()), respectProtect);
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
line.isWrapped = false;
}
}
/**
@@ -2234,9 +2236,9 @@ export class InputHandler extends Disposable implements IInputHandler {
const p = params.params[0];
if (ansi) {
if (p === 2) return f(p, V.PERMANENTLY_SET);
if (p === 2) return f(p, V.PERMANENTLY_RESET);
if (p === 4) return f(p, b2v(cs.modes.insertMode));
if (p === 12) return f(p, V.PERMANENTLY_RESET);
if (p === 12) return f(p, V.PERMANENTLY_SET);
if (p === 20) return f(p, b2v(opts.convertEol));
return f(p, V.NOT_RECOGNIZED);
}
@@ -2251,6 +2253,7 @@ export class InputHandler extends Disposable implements IInputHandler {
if (p === 25) return f(p, b2v(!cs.isCursorHidden));
if (p === 45) return f(p, b2v(dm.reverseWraparound));
if (p === 66) return f(p, b2v(dm.applicationKeypad));
if (p === 67) return f(p, V.PERMANENTLY_RESET);
if (p === 1000) return f(p, b2v(mouseProtocol === 'VT200'));
if (p === 1002) return f(p, b2v(mouseProtocol === 'DRAG'));
if (p === 1003) return f(p, b2v(mouseProtocol === 'ANY'));
+5 -1
View File
@@ -177,7 +177,7 @@ export class Buffer implements IBuffer {
if (this._rows < newRows) {
for (let y = this._rows; y < newRows; y++) {
if (this.lines.length < newRows + this.ybase) {
if (this._optionsService.rawOptions.windowsMode) {
if (this._optionsService.rawOptions.windowsMode || this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) {
// Just add the new missing rows on Windows as conpty reprints the screen with it's
// view of the world. Once a line enters scrollback for conpty it remains there
this.lines.push(new BufferLine(newCols, nullCell));
@@ -290,6 +290,10 @@ export class Buffer implements IBuffer {
}
private get _isReflowEnabled(): boolean {
const windowsPty = this._optionsService.rawOptions.windowsPty;
if (windowsPty && windowsPty.buildNumber) {
return this._hasScrollback && windowsPty.backend === 'conpty' && windowsPty.buildNumber >= 21376;
}
return this._hasScrollback && !this._optionsService.rawOptions.windowsMode;
}
+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);
}
}
}
+6
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,
@@ -43,6 +44,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
rightClickSelectsWord: isMac,
windowOptions: {},
windowsMode: false,
windowsPty: {},
wordSeparator: ' ()[]{}\',"`',
altClickMovesCursor: true,
convertEol: false,
@@ -177,12 +179,16 @@ export class OptionsService extends Disposable implements IOptionsService {
if (value <= 0) {
throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
}
break;
case 'rows':
case 'cols':
if (!value && value !== 0) {
throw new Error(`${key} must be numeric, value: ${value}`);
}
break;
case 'windowsPty':
value = value ?? {};
break;
}
return value;
}
+4 -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 } 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;
@@ -242,6 +243,7 @@ export interface ITerminalOptions {
tabStopWidth?: number;
theme?: ITheme;
windowsMode?: boolean;
windowsPty?: IWindowsPty;
windowOptions?: IWindowOptions;
wordSeparator?: string;
overviewRulerWidth?: number;