Merge pull request #5867 from Tyriar/inst_const_enuym
Add disposable/const enum agent instructions, convert a lot of top level consts to const enums
This commit is contained in:
@@ -57,6 +57,13 @@ export class MyAddon implements ITerminalAddon {
|
|||||||
- Proposed APIs require `allowProposedApi: true` option
|
- Proposed APIs require `allowProposedApi: true` option
|
||||||
- Constructor-only options (cols, rows) cannot be changed after instantiation
|
- Constructor-only options (cols, rows) cannot be changed after instantiation
|
||||||
|
|
||||||
|
**Disposable Management**:
|
||||||
|
- When a disposable object can be replaced over time, prefer a registered `MutableDisposable` over manual dispose/reassign logic.
|
||||||
|
- Register it on the owning class (for example, `this._register(new MutableDisposable())`) and assign through `.value`; this automatically disposes the previous value and avoids accidentally leaking resources.
|
||||||
|
|
||||||
|
**TypeScript Constants**:
|
||||||
|
- Prefer `const enum` over top-level `const` declarations for primitive constants when appropriate, since values are inlined and avoid runtime property lookups.
|
||||||
|
|
||||||
**Testing Utilities**: Use `TestUtils.ts` helpers:
|
**Testing Utilities**: Use `TestUtils.ts` helpers:
|
||||||
- `openTerminal(ctx, options)` for setup
|
- `openTerminal(ctx, options)` for setup
|
||||||
- `pollFor(page, fn, expectedValue)` for async assertions
|
- `pollFor(page, fn, expectedValue)` for async assertions
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ interface ITerminalDimensions {
|
|||||||
cols: number;
|
cols: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MINIMUM_COLS = 2;
|
const enum Constants {
|
||||||
const MINIMUM_ROWS = 1;
|
MINIMUM_COLS = 2,
|
||||||
|
MINIMUM_ROWS = 1
|
||||||
|
}
|
||||||
|
|
||||||
function getWindow(e: Node): Window {
|
function getWindow(e: Node): Window {
|
||||||
if (e?.ownerDocument?.defaultView) {
|
if (e?.ownerDocument?.defaultView) {
|
||||||
@@ -86,8 +88,8 @@ export class FitAddon implements ITerminalAddon, IFitApi {
|
|||||||
const availableHeight = parentElementHeight - elementPaddingVer;
|
const availableHeight = parentElementHeight - elementPaddingVer;
|
||||||
const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth;
|
const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth;
|
||||||
const geometry = {
|
const geometry = {
|
||||||
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / dims.css.cell.width)),
|
cols: Math.max(Constants.MINIMUM_COLS, Math.floor(availableWidth / dims.css.cell.width)),
|
||||||
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / dims.css.cell.height))
|
rows: Math.max(Constants.MINIMUM_ROWS, Math.floor(availableHeight / dims.css.cell.height))
|
||||||
};
|
};
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ import { IDisposable } from '@xterm/xterm';
|
|||||||
import { ICellSize, ImageLayer, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types';
|
import { ICellSize, ImageLayer, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types';
|
||||||
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
|
||||||
|
|
||||||
const PLACEHOLDER_LENGTH = 4096;
|
const enum Constants {
|
||||||
const PLACEHOLDER_HEIGHT = 24;
|
PLACEHOLDER_LENGTH = 4096,
|
||||||
|
PLACEHOLDER_HEIGHT = 24
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ImageRenderer - terminal frontend extension:
|
* ImageRenderer - terminal frontend extension:
|
||||||
@@ -110,7 +112,7 @@ export class ImageRenderer extends Disposable implements IDisposable {
|
|||||||
public showPlaceholder(value: boolean): void {
|
public showPlaceholder(value: boolean): void {
|
||||||
if (value) {
|
if (value) {
|
||||||
if (!this._placeholder && this.cellSize.height !== -1) {
|
if (!this._placeholder && this.cellSize.height !== -1) {
|
||||||
this._createPlaceHolder(Math.max(this.cellSize.height + 1, PLACEHOLDER_HEIGHT));
|
this._createPlaceHolder(Math.max(this.cellSize.height + 1, Constants.PLACEHOLDER_HEIGHT));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._placeholderBitmap?.close();
|
this._placeholderBitmap?.close();
|
||||||
@@ -249,7 +251,7 @@ export class ImageRenderer extends Disposable implements IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._placeholder) {
|
if (!this._placeholder) {
|
||||||
this._createPlaceHolder(Math.max(height + 1, PLACEHOLDER_HEIGHT));
|
this._createPlaceHolder(Math.max(height + 1, Constants.PLACEHOLDER_HEIGHT));
|
||||||
} else if (height >= this._placeholder!.height) {
|
} else if (height >= this._placeholder!.height) {
|
||||||
this._createPlaceHolder(height + 1);
|
this._createPlaceHolder(height + 1);
|
||||||
}
|
}
|
||||||
@@ -379,7 +381,7 @@ export class ImageRenderer extends Disposable implements IDisposable {
|
|||||||
return this._layers.has(layer);
|
return this._layers.has(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createPlaceHolder(height: number = PLACEHOLDER_HEIGHT): void {
|
private _createPlaceHolder(height: number = Constants.PLACEHOLDER_HEIGHT): void {
|
||||||
this._placeholderBitmap?.close();
|
this._placeholderBitmap?.close();
|
||||||
this._placeholderBitmap = undefined;
|
this._placeholderBitmap = undefined;
|
||||||
|
|
||||||
@@ -403,7 +405,7 @@ export class ImageRenderer extends Disposable implements IDisposable {
|
|||||||
ctx.putImageData(imgData, 0, 0);
|
ctx.putImageData(imgData, 0, 0);
|
||||||
|
|
||||||
// create placeholder line, width aligned to blueprint width
|
// create placeholder line, width aligned to blueprint width
|
||||||
const width = (screen.width + bWidth - 1) & ~(bWidth - 1) || PLACEHOLDER_LENGTH;
|
const width = (screen.width + bWidth - 1) & ~(bWidth - 1) || Constants.PLACEHOLDER_LENGTH;
|
||||||
this._placeholder = ImageRenderer.createCanvas(this.document, width, height);
|
this._placeholder = ImageRenderer.createCanvas(this.document, width, height);
|
||||||
const ctx2 = this._placeholder.getContext('2d', { alpha: false });
|
const ctx2 = this._placeholder.getContext('2d', { alpha: false });
|
||||||
if (!ctx2) {
|
if (!ctx2) {
|
||||||
|
|||||||
@@ -16,24 +16,23 @@ import {
|
|||||||
IKittyCommand,
|
IKittyCommand,
|
||||||
IPendingTransmission,
|
IPendingTransmission,
|
||||||
IKittyImageData,
|
IKittyImageData,
|
||||||
BYTES_PER_PIXEL_RGB,
|
KittyPixelConstants,
|
||||||
BYTES_PER_PIXEL_RGBA,
|
|
||||||
ALPHA_OPAQUE,
|
|
||||||
parseKittyCommand
|
parseKittyCommand
|
||||||
} from './KittyGraphicsTypes';
|
} from './KittyGraphicsTypes';
|
||||||
|
|
||||||
// Memory limit for base64 decoder (4MB, same as IIPHandler)
|
const enum Constants {
|
||||||
const DECODER_KEEP_DATA = 4194304;
|
// Memory limit for base64 decoder (4MB, same as IIPHandler)
|
||||||
const DECODER_INITIAL_DATA = 4194304; // 4MB
|
DECODER_KEEP_DATA = 4194304,
|
||||||
|
DECODER_INITIAL_DATA = 4194304, // 4MB
|
||||||
|
// Local mirror of const enum (esbuild can't inline const enums from external packages)
|
||||||
|
DECODER_OK = 0,
|
||||||
|
// Maximum control data size
|
||||||
|
MAX_CONTROL_DATA_SIZE = 512,
|
||||||
|
// Semicolon codepoint
|
||||||
|
SEMICOLON = 0x3B
|
||||||
|
}
|
||||||
|
|
||||||
// Local mirror of const enum (esbuild can't inline const enums from external packages)
|
const DECODER_OK = Constants.DECODER_OK as unknown as DecodeStatus.OK;
|
||||||
const DECODER_OK: DecodeStatus.OK = 0;
|
|
||||||
|
|
||||||
// Maximum control data size
|
|
||||||
const MAX_CONTROL_DATA_SIZE = 512;
|
|
||||||
|
|
||||||
// Semicolon codepoint
|
|
||||||
const SEMICOLON = 0x3B;
|
|
||||||
|
|
||||||
// Kitty graphics protocol handler with streaming base64 decoding.
|
// Kitty graphics protocol handler with streaming base64 decoding.
|
||||||
export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDisposable {
|
export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDisposable {
|
||||||
@@ -50,7 +49,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
private _inControlData = true;
|
private _inControlData = true;
|
||||||
|
|
||||||
// Buffer for control data.
|
// Buffer for control data.
|
||||||
private _controlData = new Uint32Array(MAX_CONTROL_DATA_SIZE);
|
private _controlData = new Uint32Array(Constants.MAX_CONTROL_DATA_SIZE);
|
||||||
private _controlLength = 0;
|
private _controlLength = 0;
|
||||||
|
|
||||||
// Pre-calculated encoded size limit
|
// Pre-calculated encoded size limit
|
||||||
@@ -77,7 +76,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
// Convert decoded size limit -> max encoded bytes.
|
// Convert decoded size limit -> max encoded bytes.
|
||||||
this._maxEncodedBytes = Math.ceil(this._opts.kittySizeLimit * 4 / 3);
|
this._maxEncodedBytes = Math.ceil(this._opts.kittySizeLimit * 4 / 3);
|
||||||
// ensure we preallocate more than configured limit while using 4mb initial size.
|
// ensure we preallocate more than configured limit while using 4mb initial size.
|
||||||
this._initialEncodedBytes = Math.min(DECODER_INITIAL_DATA, this._maxEncodedBytes);
|
this._initialEncodedBytes = Math.min(Constants.DECODER_INITIAL_DATA, this._maxEncodedBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(): void {
|
public reset(): void {
|
||||||
@@ -129,7 +128,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
// Scan for semicolon
|
// Scan for semicolon
|
||||||
let controlEnd = end;
|
let controlEnd = end;
|
||||||
for (let i = start; i < end; i++) {
|
for (let i = start; i < end; i++) {
|
||||||
if (data[i] === SEMICOLON) {
|
if (data[i] === Constants.SEMICOLON) {
|
||||||
this._inControlData = false;
|
this._inControlData = false;
|
||||||
controlEnd = i;
|
controlEnd = i;
|
||||||
break;
|
break;
|
||||||
@@ -138,7 +137,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
|
|
||||||
// Copy control data
|
// Copy control data
|
||||||
const copyLength = controlEnd - start;
|
const copyLength = controlEnd - start;
|
||||||
if (this._controlLength + copyLength > MAX_CONTROL_DATA_SIZE) {
|
if (this._controlLength + copyLength > Constants.MAX_CONTROL_DATA_SIZE) {
|
||||||
this._aborted = true;
|
this._aborted = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -201,7 +200,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
this._activeDecoder = pending.decoder;
|
this._activeDecoder = pending.decoder;
|
||||||
}
|
}
|
||||||
if (!this._activeDecoder) {
|
if (!this._activeDecoder) {
|
||||||
this._activeDecoder = new Base64Decoder(DECODER_KEEP_DATA, this._maxEncodedBytes, this._initialEncodedBytes);
|
this._activeDecoder = new Base64Decoder(Constants.DECODER_KEEP_DATA, this._maxEncodedBytes, this._initialEncodedBytes);
|
||||||
this._activeDecoder.init();
|
this._activeDecoder.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,7 +483,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bytesPerPixel = format === KittyFormat.RGBA ? BYTES_PER_PIXEL_RGBA : BYTES_PER_PIXEL_RGB;
|
const bytesPerPixel = format === KittyFormat.RGBA ? KittyPixelConstants.BYTES_PER_PIXEL_RGBA : KittyPixelConstants.BYTES_PER_PIXEL_RGB;
|
||||||
const expectedBytes = width * height * bytesPerPixel;
|
const expectedBytes = width * height * bytesPerPixel;
|
||||||
|
|
||||||
if (bytes.length < expectedBytes) {
|
if (bytes.length < expectedBytes) {
|
||||||
@@ -723,7 +722,7 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
throw new Error('Width and height required for raw pixel data');
|
throw new Error('Width and height required for raw pixel data');
|
||||||
}
|
}
|
||||||
|
|
||||||
const bytesPerPixel = image.format === KittyFormat.RGBA ? BYTES_PER_PIXEL_RGBA : BYTES_PER_PIXEL_RGB;
|
const bytesPerPixel = image.format === KittyFormat.RGBA ? KittyPixelConstants.BYTES_PER_PIXEL_RGBA : KittyPixelConstants.BYTES_PER_PIXEL_RGB;
|
||||||
const expectedBytes = width * height * bytesPerPixel;
|
const expectedBytes = width * height * bytesPerPixel;
|
||||||
|
|
||||||
if (bytes.length < expectedBytes) {
|
if (bytes.length < expectedBytes) {
|
||||||
@@ -734,13 +733,13 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
|
|
||||||
if (image.format === KittyFormat.RGBA) {
|
if (image.format === KittyFormat.RGBA) {
|
||||||
// RGBA: use bytes directly — no copy needed
|
// RGBA: use bytes directly — no copy needed
|
||||||
return createImageBitmap(new ImageData(new Uint8ClampedArray(bytes.buffer as ArrayBuffer, bytes.byteOffset, pixelCount * BYTES_PER_PIXEL_RGBA), width, height));
|
return createImageBitmap(new ImageData(new Uint8ClampedArray(bytes.buffer as ArrayBuffer, bytes.byteOffset, pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA), width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
// RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration).
|
// RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration).
|
||||||
// 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster.
|
// 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster.
|
||||||
// Assumes little-endian (all modern browsers/Node.js).
|
// Assumes little-endian (all modern browsers/Node.js).
|
||||||
const data = new Uint8ClampedArray(pixelCount * BYTES_PER_PIXEL_RGBA);
|
const data = new Uint8ClampedArray(pixelCount * KittyPixelConstants.BYTES_PER_PIXEL_RGBA);
|
||||||
const src32 = new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4));
|
const src32 = new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4));
|
||||||
const dst32 = new Uint32Array(data.buffer);
|
const dst32 = new Uint32Array(data.buffer);
|
||||||
const alignedPixels = pixelCount & ~3; // round down to multiple of 4
|
const alignedPixels = pixelCount & ~3; // round down to multiple of 4
|
||||||
@@ -759,15 +758,15 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDispos
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle remaining 1–3 pixels
|
// Handle remaining 1–3 pixels
|
||||||
let srcByte = alignedPixels * BYTES_PER_PIXEL_RGB;
|
let srcByte = alignedPixels * KittyPixelConstants.BYTES_PER_PIXEL_RGB;
|
||||||
let dstByte = alignedPixels * BYTES_PER_PIXEL_RGBA;
|
let dstByte = alignedPixels * KittyPixelConstants.BYTES_PER_PIXEL_RGBA;
|
||||||
for (let i = alignedPixels; i < pixelCount; i++) {
|
for (let i = alignedPixels; i < pixelCount; i++) {
|
||||||
data[dstByte] = bytes[srcByte];
|
data[dstByte] = bytes[srcByte];
|
||||||
data[dstByte + 1] = bytes[srcByte + 1];
|
data[dstByte + 1] = bytes[srcByte + 1];
|
||||||
data[dstByte + 2] = bytes[srcByte + 2];
|
data[dstByte + 2] = bytes[srcByte + 2];
|
||||||
data[dstByte + 3] = ALPHA_OPAQUE;
|
data[dstByte + 3] = KittyPixelConstants.ALPHA_OPAQUE;
|
||||||
srcByte += BYTES_PER_PIXEL_RGB;
|
srcByte += KittyPixelConstants.BYTES_PER_PIXEL_RGB;
|
||||||
dstByte += BYTES_PER_PIXEL_RGBA;
|
dstByte += KittyPixelConstants.BYTES_PER_PIXEL_RGBA;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createImageBitmap(new ImageData(data, width, height));
|
return createImageBitmap(new ImageData(data, width, height));
|
||||||
|
|||||||
@@ -82,9 +82,11 @@ export const enum KittyKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pixel format constants
|
// Pixel format constants
|
||||||
export const BYTES_PER_PIXEL_RGB = 3;
|
export const enum KittyPixelConstants {
|
||||||
export const BYTES_PER_PIXEL_RGBA = 4;
|
BYTES_PER_PIXEL_RGB = 3,
|
||||||
export const ALPHA_OPAQUE = 255;
|
BYTES_PER_PIXEL_RGBA = 4,
|
||||||
|
ALPHA_OPAQUE = 255
|
||||||
|
}
|
||||||
|
|
||||||
// Parsed Kitty graphics command.
|
// Parsed Kitty graphics command.
|
||||||
export interface IKittyCommand {
|
export interface IKittyCommand {
|
||||||
|
|||||||
@@ -78,9 +78,11 @@ void main() {
|
|||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const INDICES_PER_CELL = 11;
|
const enum Constants {
|
||||||
const BYTES_PER_CELL = INDICES_PER_CELL * Float32Array.BYTES_PER_ELEMENT;
|
INDICES_PER_CELL = 11,
|
||||||
const CELL_POSITION_INDICES = 2;
|
BYTES_PER_CELL = INDICES_PER_CELL * 4/* Float32Array.BYTES_PER_ELEMENT */,
|
||||||
|
CELL_POSITION_INDICES = 2
|
||||||
|
}
|
||||||
|
|
||||||
// Work variables to avoid garbage collection
|
// Work variables to avoid garbage collection
|
||||||
let $i = 0;
|
let $i = 0;
|
||||||
@@ -160,22 +162,22 @@ export class GlyphRenderer extends Disposable {
|
|||||||
this._register(toDisposable(() => gl.deleteBuffer(this._attributesBuffer)));
|
this._register(toDisposable(() => gl.deleteBuffer(this._attributesBuffer)));
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
|
||||||
gl.enableVertexAttribArray(VertexAttribLocations.OFFSET);
|
gl.enableVertexAttribArray(VertexAttribLocations.OFFSET);
|
||||||
gl.vertexAttribPointer(VertexAttribLocations.OFFSET, 2, gl.FLOAT, false, BYTES_PER_CELL, 0);
|
gl.vertexAttribPointer(VertexAttribLocations.OFFSET, 2, gl.FLOAT, false, Constants.BYTES_PER_CELL, 0);
|
||||||
gl.vertexAttribDivisor(VertexAttribLocations.OFFSET, 1);
|
gl.vertexAttribDivisor(VertexAttribLocations.OFFSET, 1);
|
||||||
gl.enableVertexAttribArray(VertexAttribLocations.SIZE);
|
gl.enableVertexAttribArray(VertexAttribLocations.SIZE);
|
||||||
gl.vertexAttribPointer(VertexAttribLocations.SIZE, 2, gl.FLOAT, false, BYTES_PER_CELL, 2 * Float32Array.BYTES_PER_ELEMENT);
|
gl.vertexAttribPointer(VertexAttribLocations.SIZE, 2, gl.FLOAT, false, Constants.BYTES_PER_CELL, 2 * Float32Array.BYTES_PER_ELEMENT);
|
||||||
gl.vertexAttribDivisor(VertexAttribLocations.SIZE, 1);
|
gl.vertexAttribDivisor(VertexAttribLocations.SIZE, 1);
|
||||||
gl.enableVertexAttribArray(VertexAttribLocations.TEXPAGE);
|
gl.enableVertexAttribArray(VertexAttribLocations.TEXPAGE);
|
||||||
gl.vertexAttribPointer(VertexAttribLocations.TEXPAGE, 1, gl.FLOAT, false, BYTES_PER_CELL, 4 * Float32Array.BYTES_PER_ELEMENT);
|
gl.vertexAttribPointer(VertexAttribLocations.TEXPAGE, 1, gl.FLOAT, false, Constants.BYTES_PER_CELL, 4 * Float32Array.BYTES_PER_ELEMENT);
|
||||||
gl.vertexAttribDivisor(VertexAttribLocations.TEXPAGE, 1);
|
gl.vertexAttribDivisor(VertexAttribLocations.TEXPAGE, 1);
|
||||||
gl.enableVertexAttribArray(VertexAttribLocations.TEXCOORD);
|
gl.enableVertexAttribArray(VertexAttribLocations.TEXCOORD);
|
||||||
gl.vertexAttribPointer(VertexAttribLocations.TEXCOORD, 2, gl.FLOAT, false, BYTES_PER_CELL, 5 * Float32Array.BYTES_PER_ELEMENT);
|
gl.vertexAttribPointer(VertexAttribLocations.TEXCOORD, 2, gl.FLOAT, false, Constants.BYTES_PER_CELL, 5 * Float32Array.BYTES_PER_ELEMENT);
|
||||||
gl.vertexAttribDivisor(VertexAttribLocations.TEXCOORD, 1);
|
gl.vertexAttribDivisor(VertexAttribLocations.TEXCOORD, 1);
|
||||||
gl.enableVertexAttribArray(VertexAttribLocations.TEXSIZE);
|
gl.enableVertexAttribArray(VertexAttribLocations.TEXSIZE);
|
||||||
gl.vertexAttribPointer(VertexAttribLocations.TEXSIZE, 2, gl.FLOAT, false, BYTES_PER_CELL, 7 * Float32Array.BYTES_PER_ELEMENT);
|
gl.vertexAttribPointer(VertexAttribLocations.TEXSIZE, 2, gl.FLOAT, false, Constants.BYTES_PER_CELL, 7 * Float32Array.BYTES_PER_ELEMENT);
|
||||||
gl.vertexAttribDivisor(VertexAttribLocations.TEXSIZE, 1);
|
gl.vertexAttribDivisor(VertexAttribLocations.TEXSIZE, 1);
|
||||||
gl.enableVertexAttribArray(VertexAttribLocations.CELL_POSITION);
|
gl.enableVertexAttribArray(VertexAttribLocations.CELL_POSITION);
|
||||||
gl.vertexAttribPointer(VertexAttribLocations.CELL_POSITION, 2, gl.FLOAT, false, BYTES_PER_CELL, 9 * Float32Array.BYTES_PER_ELEMENT);
|
gl.vertexAttribPointer(VertexAttribLocations.CELL_POSITION, 2, gl.FLOAT, false, Constants.BYTES_PER_CELL, 9 * Float32Array.BYTES_PER_ELEMENT);
|
||||||
gl.vertexAttribDivisor(VertexAttribLocations.CELL_POSITION, 1);
|
gl.vertexAttribDivisor(VertexAttribLocations.CELL_POSITION, 1);
|
||||||
|
|
||||||
// Setup static uniforms
|
// Setup static uniforms
|
||||||
@@ -222,12 +224,12 @@ export class GlyphRenderer extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void {
|
private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void {
|
||||||
$i = (y * this._terminal.cols + x) * INDICES_PER_CELL;
|
$i = (y * this._terminal.cols + x) * Constants.INDICES_PER_CELL;
|
||||||
|
|
||||||
// Exit early if this is a null character, allow space character to continue as it may have
|
// Exit early if this is a null character, allow space character to continue as it may have
|
||||||
// underline/strikethrough styles
|
// underline/strikethrough styles
|
||||||
if (code === NULL_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) {
|
if (code === NULL_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) {
|
||||||
array.fill(0, $i, $i + INDICES_PER_CELL - 1 - CELL_POSITION_INDICES);
|
array.fill(0, $i, $i + Constants.INDICES_PER_CELL - 1 - Constants.CELL_POSITION_INDICES);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +290,7 @@ export class GlyphRenderer extends Disposable {
|
|||||||
|
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
const terminal = this._terminal;
|
const terminal = this._terminal;
|
||||||
const newCount = terminal.cols * terminal.rows * INDICES_PER_CELL;
|
const newCount = terminal.cols * terminal.rows * Constants.INDICES_PER_CELL;
|
||||||
|
|
||||||
// Clear vertices
|
// Clear vertices
|
||||||
if (this._vertices.count !== newCount) {
|
if (this._vertices.count !== newCount) {
|
||||||
@@ -310,7 +312,7 @@ export class GlyphRenderer extends Disposable {
|
|||||||
for (let x = 0; x < terminal.cols; x++) {
|
for (let x = 0; x < terminal.cols; x++) {
|
||||||
this._vertices.attributes[i + 9] = x / terminal.cols;
|
this._vertices.attributes[i + 9] = x / terminal.cols;
|
||||||
this._vertices.attributes[i + 10] = y / terminal.rows;
|
this._vertices.attributes[i + 10] = y / terminal.rows;
|
||||||
i += INDICES_PER_CELL;
|
i += Constants.INDICES_PER_CELL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,8 +348,8 @@ export class GlyphRenderer extends Disposable {
|
|||||||
// - So we don't send vertices for all the line-ending whitespace to the GPU
|
// - So we don't send vertices for all the line-ending whitespace to the GPU
|
||||||
let bufferLength = 0;
|
let bufferLength = 0;
|
||||||
for (let y = 0; y < renderModel.lineLengths.length; y++) {
|
for (let y = 0; y < renderModel.lineLengths.length; y++) {
|
||||||
const si = y * this._terminal.cols * INDICES_PER_CELL;
|
const si = y * this._terminal.cols * Constants.INDICES_PER_CELL;
|
||||||
const sub = this._vertices.attributes.subarray(si, si + renderModel.lineLengths[y] * INDICES_PER_CELL);
|
const sub = this._vertices.attributes.subarray(si, si + renderModel.lineLengths[y] * Constants.INDICES_PER_CELL);
|
||||||
activeBuffer.set(sub, bufferLength);
|
activeBuffer.set(sub, bufferLength);
|
||||||
bufferLength += sub.length;
|
bufferLength += sub.length;
|
||||||
}
|
}
|
||||||
@@ -364,7 +366,7 @@ export class GlyphRenderer extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw the viewport
|
// Draw the viewport
|
||||||
gl.drawElementsInstanced(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_BYTE, 0, bufferLength / INDICES_PER_CELL);
|
gl.drawElementsInstanced(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_BYTE, 0, bufferLength / Constants.INDICES_PER_CELL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAtlas(atlas: ITextureAtlas): void {
|
public setAtlas(atlas: ITextureAtlas): void {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { Attributes, FgFlags } from 'common/buffer/Constants';
|
|||||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||||
import { IColor } from 'common/Types';
|
import { IColor } from 'common/Types';
|
||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
|
import { RenderModelConstants } from './RenderModel';
|
||||||
import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types';
|
import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types';
|
||||||
import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils';
|
import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils';
|
||||||
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||||
@@ -217,9 +217,9 @@ export class RectangleRenderer extends Disposable {
|
|||||||
currentFg = 0;
|
currentFg = 0;
|
||||||
currentInverse = false;
|
currentInverse = false;
|
||||||
for (x = 0; x < terminal.cols; x++) {
|
for (x = 0; x < terminal.cols; x++) {
|
||||||
modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
modelIndex = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
||||||
bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET];
|
bg = model.cells[modelIndex + RenderModelConstants.BG_OFFSET];
|
||||||
fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET];
|
fg = model.cells[modelIndex + RenderModelConstants.FG_OFFSET];
|
||||||
inverse = !!(fg & FgFlags.INVERSE);
|
inverse = !!(fg & FgFlags.INVERSE);
|
||||||
if (bg !== currentBg || (fg !== currentFg && (currentInverse || inverse))) {
|
if (bg !== currentBg || (fg !== currentFg && (currentInverse || inverse))) {
|
||||||
// A rectangle needs to be drawn if going from non-default to another color
|
// A rectangle needs to be drawn if going from non-default to another color
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import { ICursorRenderModel, IRenderModel } from './Types';
|
|||||||
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
||||||
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
||||||
|
|
||||||
export const RENDER_MODEL_INDICIES_PER_CELL = 4;
|
export const enum RenderModelConstants {
|
||||||
export const RENDER_MODEL_BG_OFFSET = 1;
|
INDICIES_PER_CELL = 4,
|
||||||
export const RENDER_MODEL_FG_OFFSET = 2;
|
BG_OFFSET = 1,
|
||||||
export const RENDER_MODEL_EXT_OFFSET = 3;
|
FG_OFFSET = 2,
|
||||||
|
EXT_OFFSET = 3
|
||||||
|
}
|
||||||
|
|
||||||
export const COMBINED_CHAR_BIT_MASK = 0x80000000;
|
export const COMBINED_CHAR_BIT_MASK = 0x80000000;
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ export class RenderModel implements IRenderModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public resize(cols: number, rows: number): void {
|
public resize(cols: number, rows: number): void {
|
||||||
const indexCount = cols * rows * RENDER_MODEL_INDICIES_PER_CELL;
|
const indexCount = cols * rows * RenderModelConstants.INDICIES_PER_CELL;
|
||||||
if (indexCount !== this.cells.length) {
|
if (indexCount !== this.cells.length) {
|
||||||
this.cells = new Uint32Array(indexCount);
|
this.cells = new Uint32Array(indexCount);
|
||||||
this.lineLengths = new Uint32Array(rows);
|
this.lineLengths = new Uint32Array(rows);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { ICoreService, IDecorationService, IOptionsService } from 'common/servic
|
|||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
import { GlyphRenderer } from './GlyphRenderer';
|
import { GlyphRenderer } from './GlyphRenderer';
|
||||||
import { RectangleRenderer } from './RectangleRenderer';
|
import { RectangleRenderer } from './RectangleRenderer';
|
||||||
import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL, RenderModel } from './RenderModel';
|
import { COMBINED_CHAR_BIT_MASK, RenderModel, RenderModelConstants } from './RenderModel';
|
||||||
import { IWebGL2RenderingContext, type ITextureAtlas } from './Types';
|
import { IWebGL2RenderingContext, type ITextureAtlas } from './Types';
|
||||||
import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
|
import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
|
||||||
import { IRenderLayer } from './renderLayer/Types';
|
import { IRenderLayer } from './renderLayer/Types';
|
||||||
@@ -493,7 +493,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
chars = cell.getChars();
|
chars = cell.getChars();
|
||||||
code = cell.getCode();
|
code = cell.getCode();
|
||||||
i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
i = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
||||||
|
|
||||||
if (!rowHasBlinkingCells && cell.isBlink()) {
|
if (!rowHasBlinkingCells && cell.isBlink()) {
|
||||||
rowHasBlinkingCells = true;
|
rowHasBlinkingCells = true;
|
||||||
@@ -538,9 +538,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Nothing has changed, no updates needed
|
// Nothing has changed, no updates needed
|
||||||
if (this._model.cells[i] === code &&
|
if (this._model.cells[i] === code &&
|
||||||
this._model.cells[i + RENDER_MODEL_BG_OFFSET] === this._cellColorResolver.result.bg &&
|
this._model.cells[i + RenderModelConstants.BG_OFFSET] === this._cellColorResolver.result.bg &&
|
||||||
this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._cellColorResolver.result.fg &&
|
this._model.cells[i + RenderModelConstants.FG_OFFSET] === this._cellColorResolver.result.fg &&
|
||||||
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] === this._cellColorResolver.result.ext) {
|
this._model.cells[i + RenderModelConstants.EXT_OFFSET] === this._cellColorResolver.result.ext) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,9 +553,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Cache the results in the model
|
// Cache the results in the model
|
||||||
this._model.cells[i] = code;
|
this._model.cells[i] = code;
|
||||||
this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
|
this._model.cells[i + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg;
|
||||||
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
|
this._model.cells[i + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg;
|
||||||
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
|
this._model.cells[i + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext;
|
||||||
|
|
||||||
width = cell.getWidth();
|
width = cell.getWidth();
|
||||||
this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, width, lastBg);
|
this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, width, lastBg);
|
||||||
@@ -566,14 +566,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Null out non-first cells
|
// Null out non-first cells
|
||||||
for (x++; x <= lastCharX; x++) {
|
for (x++; x <= lastCharX; x++) {
|
||||||
j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
|
||||||
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
|
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
|
||||||
this._model.cells[j] = NULL_CELL_CODE;
|
this._model.cells[j] = NULL_CELL_CODE;
|
||||||
// Don't re-resolve the cell color since multi-colored ligature backgrounds are not
|
// Don't re-resolve the cell color since multi-colored ligature backgrounds are not
|
||||||
// supported
|
// supported
|
||||||
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
|
this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg;
|
||||||
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
|
this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg;
|
||||||
this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
|
this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext;
|
||||||
}
|
}
|
||||||
x--; // Go back to the previous update cell for next iteration
|
x--; // Go back to the previous update cell for next iteration
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import { IBuffer } from 'common/buffer/Types';
|
|||||||
import { IInstantiationService } from 'common/services/Services';
|
import { IInstantiationService } from 'common/services/Services';
|
||||||
import { addDisposableListener } from 'browser/Dom';
|
import { addDisposableListener } from 'browser/Dom';
|
||||||
|
|
||||||
const MAX_ROWS_TO_READ = 20;
|
const enum Constants {
|
||||||
|
MAX_ROWS_TO_READ = 20
|
||||||
|
}
|
||||||
|
|
||||||
const enum BoundaryPosition {
|
const enum BoundaryPosition {
|
||||||
TOP,
|
TOP,
|
||||||
@@ -137,7 +139,7 @@ export class AccessibilityManager extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleChar(char: string): void {
|
private _handleChar(char: string): void {
|
||||||
if (this._liveRegionLineCount < MAX_ROWS_TO_READ + 1) {
|
if (this._liveRegionLineCount < Constants.MAX_ROWS_TO_READ + 1) {
|
||||||
if (this._charsToConsume.length > 0) {
|
if (this._charsToConsume.length > 0) {
|
||||||
// Have the screen reader ignore the char if it was just input
|
// Have the screen reader ignore the char if it was just input
|
||||||
const shiftedChar = this._charsToConsume.shift();
|
const shiftedChar = this._charsToConsume.shift();
|
||||||
@@ -150,7 +152,7 @@ export class AccessibilityManager extends Disposable {
|
|||||||
|
|
||||||
if (char === '\n') {
|
if (char === '\n') {
|
||||||
this._liveRegionLineCount++;
|
this._liveRegionLineCount++;
|
||||||
if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) {
|
if (this._liveRegionLineCount === Constants.MAX_ROWS_TO_READ + 1) {
|
||||||
this._liveRegion.textContent = Strings.tooMuchOutput.get();
|
this._liveRegion.textContent = Strings.tooMuchOutput.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,15 @@ import { Emitter } from 'common/Event';
|
|||||||
import { addDisposableListener } from 'browser/Dom';
|
import { addDisposableListener } from 'browser/Dom';
|
||||||
|
|
||||||
|
|
||||||
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
const enum Constants {
|
||||||
const ROW_CONTAINER_CLASS = 'xterm-rows';
|
TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-',
|
||||||
const FG_CLASS_PREFIX = 'xterm-fg-';
|
ROW_CONTAINER_CLASS = 'xterm-rows',
|
||||||
const BG_CLASS_PREFIX = 'xterm-bg-';
|
FG_CLASS_PREFIX = 'xterm-fg-',
|
||||||
const FOCUS_CLASS = 'xterm-focus';
|
BG_CLASS_PREFIX = 'xterm-bg-',
|
||||||
const SELECTION_CLASS = 'xterm-selection';
|
FOCUS_CLASS = 'xterm-focus',
|
||||||
const CURSOR_BLINK_IDLE_CLASS = 'xterm-cursor-blink-idle';
|
SELECTION_CLASS = 'xterm-selection',
|
||||||
|
CURSOR_BLINK_IDLE_CLASS = 'xterm-cursor-blink-idle'
|
||||||
|
}
|
||||||
|
|
||||||
let nextTerminalId = 1;
|
let nextTerminalId = 1;
|
||||||
|
|
||||||
@@ -76,12 +78,12 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._rowContainer = this._document.createElement('div');
|
this._rowContainer = this._document.createElement('div');
|
||||||
this._rowContainer.classList.add(ROW_CONTAINER_CLASS);
|
this._rowContainer.classList.add(Constants.ROW_CONTAINER_CLASS);
|
||||||
this._rowContainer.style.lineHeight = 'normal';
|
this._rowContainer.style.lineHeight = 'normal';
|
||||||
this._rowContainer.setAttribute('aria-hidden', 'true');
|
this._rowContainer.setAttribute('aria-hidden', 'true');
|
||||||
this._refreshRowElements(this._bufferService.cols, this._bufferService.rows);
|
this._refreshRowElements(this._bufferService.cols, this._bufferService.rows);
|
||||||
this._selectionContainer = this._document.createElement('div');
|
this._selectionContainer = this._document.createElement('div');
|
||||||
this._selectionContainer.classList.add(SELECTION_CLASS);
|
this._selectionContainer.classList.add(Constants.SELECTION_CLASS);
|
||||||
this._selectionContainer.setAttribute('aria-hidden', 'true');
|
this._selectionContainer.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
this.dimensions = createRenderDimensions();
|
this.dimensions = createRenderDimensions();
|
||||||
@@ -93,7 +95,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document);
|
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document);
|
||||||
|
|
||||||
this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
this._element.classList.add(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass);
|
||||||
this._screenElement.appendChild(this._rowContainer);
|
this._screenElement.appendChild(this._rowContainer);
|
||||||
this._screenElement.appendChild(this._selectionContainer);
|
this._screenElement.appendChild(this._selectionContainer);
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
));
|
));
|
||||||
|
|
||||||
this._register(toDisposable(() => {
|
this._register(toDisposable(() => {
|
||||||
this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
this._element.classList.remove(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass);
|
||||||
|
|
||||||
// Outside influences such as React unmounts may manipulate the DOM before our disposal.
|
// Outside influences such as React unmounts may manipulate the DOM before our disposal.
|
||||||
// https://github.com/xtermjs/xterm.js/issues/2960
|
// https://github.com/xtermjs/xterm.js/issues/2960
|
||||||
@@ -160,7 +162,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles =
|
const styles =
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} span {` +
|
||||||
` display: inline-block;` + // TODO: find workaround for inline-block (creates ~20% render penalty)
|
` display: inline-block;` + // TODO: find workaround for inline-block (creates ~20% render penalty)
|
||||||
` height: 100%;` +
|
` height: 100%;` +
|
||||||
` vertical-align: top;` +
|
` vertical-align: top;` +
|
||||||
@@ -181,7 +183,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Base CSS
|
// Base CSS
|
||||||
let styles =
|
let styles =
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} {` +
|
||||||
// Disabling pointer events circumvents a browser behavior that prevents `click` events from
|
// Disabling pointer events circumvents a browser behavior that prevents `click` events from
|
||||||
// being delivered if the target element is replaced during the click. This happened due to
|
// being delivered if the target element is replaced during the click. This happened due to
|
||||||
// refresh() being called during the mousedown handler to start a selection.
|
// refresh() being called during the mousedown handler to start a selection.
|
||||||
@@ -189,14 +191,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
` color: ${colors.foreground.css};` +
|
` color: ${colors.foreground.css};` +
|
||||||
`}`;
|
`}`;
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}, ${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}, ${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} span {` +
|
||||||
` font-family: ${this._optionsService.rawOptions.fontFamily};` +
|
` font-family: ${this._optionsService.rawOptions.fontFamily};` +
|
||||||
` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
|
` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
|
||||||
` font-kerning: none;` +
|
` font-kerning: none;` +
|
||||||
` white-space: pre` +
|
` white-space: pre` +
|
||||||
`}`;
|
`}`;
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .xterm-dim {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .xterm-dim {` +
|
||||||
` color: ${color.multiplyOpacity(colors.foreground, 0.5).css};` +
|
` color: ${color.multiplyOpacity(colors.foreground, 0.5).css};` +
|
||||||
`}`;
|
`}`;
|
||||||
// Text styles
|
// Text styles
|
||||||
@@ -242,70 +244,70 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
`}`;
|
`}`;
|
||||||
// Cursor
|
// Cursor
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
||||||
` animation: ${blinkAnimationUnderlineId} 1s step-end infinite;` +
|
` animation: ${blinkAnimationUnderlineId} 1s step-end infinite;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
|
||||||
` animation: ${blinkAnimationBarId} 1s step-end infinite;` +
|
` animation: ${blinkAnimationBarId} 1s step-end infinite;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||||
` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
|
` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
|
||||||
`}` +
|
`}` +
|
||||||
// Disable cursor blinking when idle
|
// Disable cursor blinking when idle
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
|
||||||
` animation: none !important;` +
|
` animation: none !important;` +
|
||||||
`}` +
|
`}` +
|
||||||
// !important helps fix an issue where the cursor will not render on top of the selection,
|
// !important helps fix an issue where the cursor will not render on top of the selection,
|
||||||
// however it's very hard to fix this issue and retain the blink animation without the use of
|
// however it's very hard to fix this issue and retain the blink animation without the use of
|
||||||
// !important. So this edge case fails when cursor blink is on.
|
// !important. So this edge case fails when cursor blink is on.
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||||
` background-color: ${colors.cursor.css};` +
|
` background-color: ${colors.cursor.css};` +
|
||||||
` color: ${colors.cursorAccent.css};` +
|
` color: ${colors.cursorAccent.css};` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS}:not(.${RowCss.CURSOR_BLINK_CLASS}) {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS}:not(.${RowCss.CURSOR_BLINK_CLASS}) {` +
|
||||||
` background-color: ${colors.cursor.css} !important;` +
|
` background-color: ${colors.cursor.css} !important;` +
|
||||||
` color: ${colors.cursorAccent.css} !important;` +
|
` color: ${colors.cursorAccent.css} !important;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` +
|
||||||
` outline: 1px solid ${colors.cursor.css};` +
|
` outline: 1px solid ${colors.cursor.css};` +
|
||||||
` outline-offset: -1px;` +
|
` outline-offset: -1px;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
|
||||||
` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` +
|
` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
||||||
` border-bottom: 1px ${colors.cursor.css};` +
|
` border-bottom: 1px ${colors.cursor.css};` +
|
||||||
` border-bottom-style: solid;` +
|
` border-bottom-style: solid;` +
|
||||||
` height: calc(100% - 1px);` +
|
` height: calc(100% - 1px);` +
|
||||||
`}`;
|
`}`;
|
||||||
// Selection
|
// Selection
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${SELECTION_CLASS} {` +
|
`${this._terminalSelector} .${Constants.SELECTION_CLASS} {` +
|
||||||
` position: absolute;` +
|
` position: absolute;` +
|
||||||
` top: 0;` +
|
` top: 0;` +
|
||||||
` left: 0;` +
|
` left: 0;` +
|
||||||
` z-index: 1;` +
|
` z-index: 1;` +
|
||||||
` pointer-events: none;` +
|
` pointer-events: none;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector}.focus .${SELECTION_CLASS} div {` +
|
`${this._terminalSelector}.focus .${Constants.SELECTION_CLASS} div {` +
|
||||||
` position: absolute;` +
|
` position: absolute;` +
|
||||||
` background-color: ${colors.selectionBackgroundOpaque.css};` +
|
` background-color: ${colors.selectionBackgroundOpaque.css};` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${SELECTION_CLASS} div {` +
|
`${this._terminalSelector} .${Constants.SELECTION_CLASS} div {` +
|
||||||
` position: absolute;` +
|
` position: absolute;` +
|
||||||
` background-color: ${colors.selectionInactiveBackgroundOpaque.css};` +
|
` background-color: ${colors.selectionInactiveBackgroundOpaque.css};` +
|
||||||
`}`;
|
`}`;
|
||||||
// Colors
|
// Colors
|
||||||
for (const [i, c] of colors.ansi.entries()) {
|
for (const [i, c] of colors.ansi.entries()) {
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
|
`${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
|
||||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` +
|
`${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${i}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` +
|
||||||
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
|
`${this._terminalSelector} .${Constants.BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
|
||||||
}
|
}
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` +
|
`${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` +
|
||||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
|
`${this._terminalSelector} .${Constants.FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${RowCss.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._terminalSelector} .${Constants.BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
|
||||||
|
|
||||||
this._themeStyleElement.textContent = styles;
|
this._themeStyleElement.textContent = styles;
|
||||||
}
|
}
|
||||||
@@ -361,13 +363,13 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public handleBlur(): void {
|
public handleBlur(): void {
|
||||||
this._rowContainer.classList.remove(FOCUS_CLASS);
|
this._rowContainer.classList.remove(Constants.FOCUS_CLASS);
|
||||||
this._cursorBlinkStateManager.pause();
|
this._cursorBlinkStateManager.pause();
|
||||||
this.renderRows(0, this._bufferService.rows - 1);
|
this.renderRows(0, this._bufferService.rows - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleFocus(): void {
|
public handleFocus(): void {
|
||||||
this._rowContainer.classList.add(FOCUS_CLASS);
|
this._rowContainer.classList.add(Constants.FOCUS_CLASS);
|
||||||
this._cursorBlinkStateManager.resume();
|
this._cursorBlinkStateManager.resume();
|
||||||
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||||
}
|
}
|
||||||
@@ -561,7 +563,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get _terminalSelector(): string {
|
private get _terminalSelector(): string {
|
||||||
return `.${TERMINAL_CLASS_PREFIX}${this._terminalClass}`;
|
return `.${Constants.TERMINAL_CLASS_PREFIX}${this._terminalClass}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleLinkHover(e: ILinkifierEvent): void {
|
private _handleLinkHover(e: ILinkifierEvent): void {
|
||||||
@@ -667,7 +669,7 @@ class CursorBlinkStateManager {
|
|||||||
|
|
||||||
public restartBlinkAnimation(): void {
|
public restartBlinkAnimation(): void {
|
||||||
if (this._isIdlePaused) {
|
if (this._isIdlePaused) {
|
||||||
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
this._rowContainer.classList.remove(Constants.CURSOR_BLINK_IDLE_CLASS);
|
||||||
}
|
}
|
||||||
this._resetIdleTimer();
|
this._resetIdleTimer();
|
||||||
}
|
}
|
||||||
@@ -679,7 +681,7 @@ class CursorBlinkStateManager {
|
|||||||
|
|
||||||
public resume(): void {
|
public resume(): void {
|
||||||
this._isIdlePaused = false;
|
this._isIdlePaused = false;
|
||||||
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
this._rowContainer.classList.remove(Constants.CURSOR_BLINK_IDLE_CLASS);
|
||||||
this._resetIdleTimer();
|
this._resetIdleTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,7 +701,7 @@ class CursorBlinkStateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _stopBlinkingDueToIdle(): void {
|
private _stopBlinkingDueToIdle(): void {
|
||||||
this._rowContainer.classList.add(CURSOR_BLINK_IDLE_CLASS);
|
this._rowContainer.classList.add(Constants.CURSOR_BLINK_IDLE_CLASS);
|
||||||
this._isIdlePaused = true;
|
this._isIdlePaused = true;
|
||||||
this._idleTimeout = undefined;
|
this._idleTimeout = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import * as platform from 'common/Platform';
|
|||||||
import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, IScrollEvent, Scrollable, ScrollbarVisibility } from './scrollable';
|
import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, IScrollEvent, Scrollable, ScrollbarVisibility } from './scrollable';
|
||||||
// import 'vs/css!./media/scrollbars';
|
// import 'vs/css!./media/scrollbars';
|
||||||
|
|
||||||
const HIDE_TIMEOUT = 500;
|
const enum Constants {
|
||||||
const SCROLL_WHEEL_SENSITIVITY = 50;
|
HIDE_TIMEOUT = 500,
|
||||||
|
SCROLL_WHEEL_SENSITIVITY = 50
|
||||||
|
}
|
||||||
|
|
||||||
class MouseWheelClassifierItem {
|
class MouseWheelClassifierItem {
|
||||||
public timestamp: number;
|
public timestamp: number;
|
||||||
@@ -404,12 +406,12 @@ export class SmoothScrollableElement extends Widget {
|
|||||||
|
|
||||||
let desiredScrollPosition: INewScrollPosition = {};
|
let desiredScrollPosition: INewScrollPosition = {};
|
||||||
if (deltaY) {
|
if (deltaY) {
|
||||||
const deltaScrollTop = SCROLL_WHEEL_SENSITIVITY * deltaY;
|
const deltaScrollTop = Constants.SCROLL_WHEEL_SENSITIVITY * deltaY;
|
||||||
const desiredScrollTop = futureScrollPosition.scrollTop - (deltaScrollTop < 0 ? Math.floor(deltaScrollTop) : Math.ceil(deltaScrollTop));
|
const desiredScrollTop = futureScrollPosition.scrollTop - (deltaScrollTop < 0 ? Math.floor(deltaScrollTop) : Math.ceil(deltaScrollTop));
|
||||||
this._verticalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollTop);
|
this._verticalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollTop);
|
||||||
}
|
}
|
||||||
if (deltaX) {
|
if (deltaX) {
|
||||||
const deltaScrollLeft = SCROLL_WHEEL_SENSITIVITY * deltaX;
|
const deltaScrollLeft = Constants.SCROLL_WHEEL_SENSITIVITY * deltaX;
|
||||||
const desiredScrollLeft = futureScrollPosition.scrollLeft - (deltaScrollLeft < 0 ? Math.floor(deltaScrollLeft) : Math.ceil(deltaScrollLeft));
|
const desiredScrollLeft = futureScrollPosition.scrollLeft - (deltaScrollLeft < 0 ? Math.floor(deltaScrollLeft) : Math.ceil(deltaScrollLeft));
|
||||||
this._horizontalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollLeft);
|
this._horizontalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollLeft);
|
||||||
}
|
}
|
||||||
@@ -533,7 +535,7 @@ export class SmoothScrollableElement extends Widget {
|
|||||||
|
|
||||||
private _scheduleHide(): void {
|
private _scheduleHide(): void {
|
||||||
if (!this._mouseIsOver && !this._isDragging) {
|
if (!this._mouseIsOver && !this._isDragging) {
|
||||||
this._hideTimeout.cancelAndSet(() => this._hide(), HIDE_TIMEOUT);
|
this._hideTimeout.cancelAndSet(() => this._hide(), Constants.HIDE_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,27 +18,26 @@ import { IBuffer } from 'common/buffer/Types';
|
|||||||
import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';
|
import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';
|
||||||
import { Emitter } from 'common/Event';
|
import { Emitter } from 'common/Event';
|
||||||
|
|
||||||
/**
|
const enum Constants {
|
||||||
* The number of pixels the mouse needs to be above or below the viewport in
|
/**
|
||||||
* order to scroll at the maximum speed.
|
* The number of pixels the mouse needs to be above or below the viewport in
|
||||||
*/
|
* order to scroll at the maximum speed.
|
||||||
const DRAG_SCROLL_MAX_THRESHOLD = 50;
|
*/
|
||||||
|
DRAG_SCROLL_MAX_THRESHOLD = 50,
|
||||||
/**
|
/**
|
||||||
* The maximum scrolling speed
|
* The maximum scrolling speed
|
||||||
*/
|
*/
|
||||||
const DRAG_SCROLL_MAX_SPEED = 15;
|
DRAG_SCROLL_MAX_SPEED = 15,
|
||||||
|
/**
|
||||||
/**
|
* The number of milliseconds between drag scroll updates.
|
||||||
* The number of milliseconds between drag scroll updates.
|
*/
|
||||||
*/
|
DRAG_SCROLL_INTERVAL = 50,
|
||||||
const DRAG_SCROLL_INTERVAL = 50;
|
/**
|
||||||
|
* The maximum amount of time that can have elapsed for an alt click to move the
|
||||||
/**
|
* cursor.
|
||||||
* The maximum amount of time that can have elapsed for an alt click to move the
|
*/
|
||||||
* cursor.
|
ALT_CLICK_MOVE_CURSOR_TIME = 500
|
||||||
*/
|
}
|
||||||
const ALT_CLICK_MOVE_CURSOR_TIME = 500;
|
|
||||||
|
|
||||||
const NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);
|
const NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);
|
||||||
const ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');
|
const ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');
|
||||||
@@ -424,9 +423,9 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|||||||
offset -= terminalHeight;
|
offset -= terminalHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD);
|
offset = Math.min(Math.max(offset, -Constants.DRAG_SCROLL_MAX_THRESHOLD), Constants.DRAG_SCROLL_MAX_THRESHOLD);
|
||||||
offset /= DRAG_SCROLL_MAX_THRESHOLD;
|
offset /= Constants.DRAG_SCROLL_MAX_THRESHOLD;
|
||||||
return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1));
|
return (offset / Math.abs(offset)) + Math.round(offset * (Constants.DRAG_SCROLL_MAX_SPEED - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -500,7 +499,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|||||||
this._screenElement.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);
|
this._screenElement.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);
|
||||||
this._screenElement.ownerDocument.addEventListener('mouseup', this._mouseUpListener);
|
this._screenElement.ownerDocument.addEventListener('mouseup', this._mouseUpListener);
|
||||||
}
|
}
|
||||||
this._dragScrollIntervalTimer = this._coreBrowserService.window.setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);
|
this._dragScrollIntervalTimer = this._coreBrowserService.window.setInterval(() => this._dragScroll(), Constants.DRAG_SCROLL_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -675,7 +674,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the
|
* The callback that occurs every Constants.DRAG_SCROLL_INTERVAL ms that does the
|
||||||
* scrolling of the viewport.
|
* scrolling of the viewport.
|
||||||
*/
|
*/
|
||||||
private _dragScroll(): void {
|
private _dragScroll(): void {
|
||||||
@@ -713,7 +712,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|||||||
|
|
||||||
this._removeMouseDownListeners();
|
this._removeMouseDownListeners();
|
||||||
|
|
||||||
if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey && this._optionsService.rawOptions.altClickMovesCursor) {
|
if (this.selectionText.length <= 1 && timeElapsed < Constants.ALT_CLICK_MOVE_CURSOR_TIME && event.altKey && this._optionsService.rawOptions.altClickMovesCursor) {
|
||||||
if (this._bufferService.buffer.ybase === this._bufferService.buffer.ydisp) {
|
if (this._bufferService.buffer.ybase === this._bufferService.buffer.ydisp) {
|
||||||
const coordinates = this._mouseCoordsService.getCoords(
|
const coordinates = this._mouseCoordsService.getCoords(
|
||||||
event,
|
event,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from 'common/services/Services';
|
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from 'common/services/Services';
|
||||||
import { InstantiationService } from 'common/services/InstantiationService';
|
import { InstantiationService } from 'common/services/InstantiationService';
|
||||||
import { LogService } from 'common/services/LogService';
|
import { LogService } from 'common/services/LogService';
|
||||||
import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';
|
import { BufferService, BufferServiceConstants } from 'common/services/BufferService';
|
||||||
import { OptionsService } from 'common/services/OptionsService';
|
import { OptionsService } from 'common/services/OptionsService';
|
||||||
import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from 'common/Types';
|
import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from 'common/Types';
|
||||||
import { CoreService } from 'common/services/CoreService';
|
import { CoreService } from 'common/services/CoreService';
|
||||||
@@ -173,8 +173,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = Math.max(x, MINIMUM_COLS);
|
x = Math.max(x, BufferServiceConstants.MINIMUM_COLS);
|
||||||
y = Math.max(y, MINIMUM_ROWS);
|
y = Math.max(y, BufferServiceConstants.MINIMUM_ROWS);
|
||||||
|
|
||||||
// Flush pending writes before resize to avoid race conditions where async
|
// Flush pending writes before resize to avoid race conditions where async
|
||||||
// writes are processed with incorrect dimensions
|
// writes are processed with incorrect dimensions
|
||||||
|
|||||||
+18
-20
@@ -41,12 +41,13 @@ const GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-':
|
|||||||
/**
|
/**
|
||||||
* Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
|
* Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
|
||||||
*/
|
*/
|
||||||
const MAX_PARSEBUFFER_LENGTH = 131072;
|
const enum Constants {
|
||||||
|
MAX_PARSEBUFFER_LENGTH = 131072,
|
||||||
/**
|
/** Limit length of title and icon name stacks. */
|
||||||
* Limit length of title and icon name stacks.
|
STACK_LIMIT = 10,
|
||||||
*/
|
// create a warning log if an async handler takes longer than the limit (in ms)
|
||||||
const STACK_LIMIT = 10;
|
SLOW_ASYNC_LIMIT = 5000
|
||||||
|
}
|
||||||
|
|
||||||
// map params to window option
|
// map params to window option
|
||||||
function paramToWindowOption(n: number, opts: IWindowOptions): boolean {
|
function paramToWindowOption(n: number, opts: IWindowOptions): boolean {
|
||||||
@@ -85,9 +86,6 @@ export enum WindowsOptionsReportType {
|
|||||||
GET_CELL_SIZE_PIXELS = 1
|
GET_CELL_SIZE_PIXELS = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a warning log if an async handler takes longer than the limit (in ms)
|
|
||||||
const SLOW_ASYNC_LIMIT = 5000;
|
|
||||||
|
|
||||||
// Work variables to avoid garbage collection
|
// Work variables to avoid garbage collection
|
||||||
let $temp = 0;
|
let $temp = 0;
|
||||||
|
|
||||||
@@ -393,7 +391,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
if (this._logService.logLevel <= LogLevelEnum.WARN) {
|
if (this._logService.logLevel <= LogLevelEnum.WARN) {
|
||||||
let slowTimeout: ReturnType<typeof setTimeout> | undefined;
|
let slowTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||||
const slowPromise = new Promise<never>((_res, rej) => {
|
const slowPromise = new Promise<never>((_res, rej) => {
|
||||||
slowTimeout = setTimeout(() => rej('#SLOW_TIMEOUT'), SLOW_ASYNC_LIMIT);
|
slowTimeout = setTimeout(() => rej('#SLOW_TIMEOUT'), Constants.SLOW_ASYNC_LIMIT);
|
||||||
});
|
});
|
||||||
Promise.race([p, slowPromise])
|
Promise.race([p, slowPromise])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -407,7 +405,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
if (err !== '#SLOW_TIMEOUT') {
|
if (err !== '#SLOW_TIMEOUT') {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
console.warn(`async parser handler taking longer than ${SLOW_ASYNC_LIMIT} ms`);
|
console.warn(`async parser handler taking longer than ${Constants.SLOW_ASYNC_LIMIT} ms`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,8 +443,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
cursorStartX = this._parseStack.cursorStartX;
|
cursorStartX = this._parseStack.cursorStartX;
|
||||||
cursorStartY = this._parseStack.cursorStartY;
|
cursorStartY = this._parseStack.cursorStartY;
|
||||||
this._parseStack.paused = false;
|
this._parseStack.paused = false;
|
||||||
if (data.length > MAX_PARSEBUFFER_LENGTH) {
|
if (data.length > Constants.MAX_PARSEBUFFER_LENGTH) {
|
||||||
start = this._parseStack.position + MAX_PARSEBUFFER_LENGTH;
|
start = this._parseStack.position + Constants.MAX_PARSEBUFFER_LENGTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,8 +461,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
|
|
||||||
// resize input buffer if needed
|
// resize input buffer if needed
|
||||||
if (this._parseBuffer.length < data.length) {
|
if (this._parseBuffer.length < data.length) {
|
||||||
if (this._parseBuffer.length < MAX_PARSEBUFFER_LENGTH) {
|
if (this._parseBuffer.length < Constants.MAX_PARSEBUFFER_LENGTH) {
|
||||||
this._parseBuffer = new Uint32Array(Math.min(data.length, MAX_PARSEBUFFER_LENGTH));
|
this._parseBuffer = new Uint32Array(Math.min(data.length, Constants.MAX_PARSEBUFFER_LENGTH));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,9 +473,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process big data in smaller chunks
|
// process big data in smaller chunks
|
||||||
if (data.length > MAX_PARSEBUFFER_LENGTH) {
|
if (data.length > Constants.MAX_PARSEBUFFER_LENGTH) {
|
||||||
for (let i = start; i < data.length; i += MAX_PARSEBUFFER_LENGTH) {
|
for (let i = start; i < data.length; i += Constants.MAX_PARSEBUFFER_LENGTH) {
|
||||||
const end = i + MAX_PARSEBUFFER_LENGTH < data.length ? i + MAX_PARSEBUFFER_LENGTH : data.length;
|
const end = i + Constants.MAX_PARSEBUFFER_LENGTH < data.length ? i + Constants.MAX_PARSEBUFFER_LENGTH : data.length;
|
||||||
const len = (typeof data === 'string')
|
const len = (typeof data === 'string')
|
||||||
? this._stringDecoder.decode(data.substring(i, end), this._parseBuffer)
|
? this._stringDecoder.decode(data.substring(i, end), this._parseBuffer)
|
||||||
: this._utf8Decoder.decode(data.subarray(i, end), this._parseBuffer);
|
: this._utf8Decoder.decode(data.subarray(i, end), this._parseBuffer);
|
||||||
@@ -2955,13 +2953,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
case 22: // PushTitle
|
case 22: // PushTitle
|
||||||
if (second === 0 || second === 2) {
|
if (second === 0 || second === 2) {
|
||||||
this._windowTitleStack.push(this._windowTitle);
|
this._windowTitleStack.push(this._windowTitle);
|
||||||
if (this._windowTitleStack.length > STACK_LIMIT) {
|
if (this._windowTitleStack.length > Constants.STACK_LIMIT) {
|
||||||
this._windowTitleStack.shift();
|
this._windowTitleStack.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (second === 0 || second === 1) {
|
if (second === 0 || second === 1) {
|
||||||
this._iconNameStack.push(this._iconName);
|
this._iconNameStack.push(this._iconName);
|
||||||
if (this._iconNameStack.length > STACK_LIMIT) {
|
if (this._iconNameStack.length > Constants.STACK_LIMIT) {
|
||||||
this._iconNameStack.shift();
|
this._iconNameStack.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,25 +9,26 @@ import { CellData } from 'common/buffer/CellData';
|
|||||||
import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
|
import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
|
||||||
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
||||||
|
|
||||||
/**
|
// Buffer memory layout:
|
||||||
* buffer memory layout:
|
//
|
||||||
*
|
// [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21)
|
||||||
* | uint32_t | uint32_t | uint32_t |
|
// [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8)
|
||||||
* | `content` | `FG` | `BG` |
|
// [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8)
|
||||||
* | wcwidth(2) comb(1) codepoint(21) | flags(8) R(8) G(8) B(8) | flags(8) R(8) G(8) B(8) |
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
const enum Constants {
|
||||||
/** typed array slots taken by one cell */
|
/** The number of 32 bit array indices taken by one cell. */
|
||||||
const CELL_SIZE = 3;
|
CELL_INDICIES = 3,
|
||||||
|
/** Factor when to cleanup underlying array buffer after shrinking. */
|
||||||
|
CLEANUP_THRESHOLD = 2
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cell member indices.
|
* Cell member indices.
|
||||||
*
|
*
|
||||||
* Direct access:
|
* Direct access:
|
||||||
* `content = data[column * CELL_SIZE + Cell.CONTENT];`
|
* `content = data[column * Constants.CELL_INDICIES + Cell.CONTENT];`
|
||||||
* `fg = data[column * CELL_SIZE + Cell.FG];`
|
* `fg = data[column * Constants.CELL_INDICIES + Cell.FG];`
|
||||||
* `bg = data[column * CELL_SIZE + Cell.BG];`
|
* `bg = data[column * Constants.CELL_INDICIES + Cell.BG];`
|
||||||
*/
|
*/
|
||||||
const enum Cell {
|
const enum Cell {
|
||||||
CONTENT = 0,
|
CONTENT = 0,
|
||||||
@@ -41,9 +42,6 @@ export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData());
|
|||||||
let $startIndex = 0;
|
let $startIndex = 0;
|
||||||
const $workCell = new CellData();
|
const $workCell = new CellData();
|
||||||
|
|
||||||
/** Factor when to cleanup underlying array buffer after shrinking. */
|
|
||||||
const CLEANUP_THRESHOLD = 2;
|
|
||||||
|
|
||||||
export interface IBufferLineStringCacheEntry {
|
export interface IBufferLineStringCacheEntry {
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
isTrimmed: boolean;
|
isTrimmed: boolean;
|
||||||
@@ -84,7 +82,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
fillCellData?: ICellData,
|
fillCellData?: ICellData,
|
||||||
public isWrapped: boolean = false
|
public isWrapped: boolean = false
|
||||||
) {
|
) {
|
||||||
this._data = new Uint32Array(cols * CELL_SIZE);
|
this._data = new Uint32Array(cols * Constants.CELL_INDICIES);
|
||||||
const cell = fillCellData ?? CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
const cell = fillCellData ?? CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||||
for (let i = 0; i < cols; ++i) {
|
for (let i = 0; i < cols; ++i) {
|
||||||
this.setCell(i, cell);
|
this.setCell(i, cell);
|
||||||
@@ -97,10 +95,10 @@ export class BufferLine implements IBufferLine {
|
|||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public get(index: number): CharData {
|
public get(index: number): CharData {
|
||||||
const content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
const content = this._data[index * Constants.CELL_INDICIES + Cell.CONTENT];
|
||||||
const cp = content & Content.CODEPOINT_MASK;
|
const cp = content & Content.CODEPOINT_MASK;
|
||||||
return [
|
return [
|
||||||
this._data[index * CELL_SIZE + Cell.FG],
|
this._data[index * Constants.CELL_INDICIES + Cell.FG],
|
||||||
(content & Content.IS_COMBINED_MASK)
|
(content & Content.IS_COMBINED_MASK)
|
||||||
? this._combined[index]
|
? this._combined[index]
|
||||||
: (cp) ? stringFromCodePoint(cp) : '',
|
: (cp) ? stringFromCodePoint(cp) : '',
|
||||||
@@ -117,12 +115,12 @@ export class BufferLine implements IBufferLine {
|
|||||||
*/
|
*/
|
||||||
public set(index: number, value: CharData): void {
|
public set(index: number, value: CharData): void {
|
||||||
this._invalidateStringCache();
|
this._invalidateStringCache();
|
||||||
this._data[index * CELL_SIZE + Cell.FG] = value[CHAR_DATA_ATTR_INDEX];
|
this._data[index * Constants.CELL_INDICIES + Cell.FG] = value[CHAR_DATA_ATTR_INDEX];
|
||||||
if (value[CHAR_DATA_CHAR_INDEX].length > 1) {
|
if (value[CHAR_DATA_CHAR_INDEX].length > 1) {
|
||||||
this._combined[index] = value[1];
|
this._combined[index] = value[1];
|
||||||
this._data[index * CELL_SIZE + Cell.CONTENT] = index | Content.IS_COMBINED_MASK | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = index | Content.IS_COMBINED_MASK | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
||||||
} else {
|
} else {
|
||||||
this._data[index * CELL_SIZE + Cell.CONTENT] = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,22 +129,22 @@ export class BufferLine implements IBufferLine {
|
|||||||
* use these when only one value is needed, otherwise use `loadCell`
|
* use these when only one value is needed, otherwise use `loadCell`
|
||||||
*/
|
*/
|
||||||
public getWidth(index: number): number {
|
public getWidth(index: number): number {
|
||||||
return this._data[index * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT;
|
return this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] >> Content.WIDTH_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test whether content has width. */
|
/** Test whether content has width. */
|
||||||
public hasWidth(index: number): number {
|
public hasWidth(index: number): number {
|
||||||
return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.WIDTH_MASK;
|
return this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] & Content.WIDTH_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get FG cell component. */
|
/** Get FG cell component. */
|
||||||
public getFg(index: number): number {
|
public getFg(index: number): number {
|
||||||
return this._data[index * CELL_SIZE + Cell.FG];
|
return this._data[index * Constants.CELL_INDICIES + Cell.FG];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get BG cell component. */
|
/** Get BG cell component. */
|
||||||
public getBg(index: number): number {
|
public getBg(index: number): number {
|
||||||
return this._data[index * CELL_SIZE + Cell.BG];
|
return this._data[index * Constants.CELL_INDICIES + Cell.BG];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,7 +153,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
* from real empty cells.
|
* from real empty cells.
|
||||||
*/
|
*/
|
||||||
public hasContent(index: number): number {
|
public hasContent(index: number): number {
|
||||||
return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK;
|
return this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] & Content.HAS_CONTENT_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,7 +162,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
* a single UTF32 codepoint or the last codepoint of a combined string.
|
* a single UTF32 codepoint or the last codepoint of a combined string.
|
||||||
*/
|
*/
|
||||||
public getCodePoint(index: number): number {
|
public getCodePoint(index: number): number {
|
||||||
const content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
const content = this._data[index * Constants.CELL_INDICIES + Cell.CONTENT];
|
||||||
if (content & Content.IS_COMBINED_MASK) {
|
if (content & Content.IS_COMBINED_MASK) {
|
||||||
return this._combined[index].charCodeAt(this._combined[index].length - 1);
|
return this._combined[index].charCodeAt(this._combined[index].length - 1);
|
||||||
}
|
}
|
||||||
@@ -173,12 +171,12 @@ export class BufferLine implements IBufferLine {
|
|||||||
|
|
||||||
/** Test whether the cell contains a combined string. */
|
/** Test whether the cell contains a combined string. */
|
||||||
public isCombined(index: number): number {
|
public isCombined(index: number): number {
|
||||||
return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.IS_COMBINED_MASK;
|
return this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] & Content.IS_COMBINED_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the string content of the cell. */
|
/** Returns the string content of the cell. */
|
||||||
public getString(index: number): string {
|
public getString(index: number): string {
|
||||||
const content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
const content = this._data[index * Constants.CELL_INDICIES + Cell.CONTENT];
|
||||||
if (content & Content.IS_COMBINED_MASK) {
|
if (content & Content.IS_COMBINED_MASK) {
|
||||||
return this._combined[index];
|
return this._combined[index];
|
||||||
}
|
}
|
||||||
@@ -191,7 +189,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
|
|
||||||
/** Get state of protected flag. */
|
/** Get state of protected flag. */
|
||||||
public isProtected(index: number): number {
|
public isProtected(index: number): number {
|
||||||
return this._data[index * CELL_SIZE + Cell.BG] & BgFlags.PROTECTED;
|
return this._data[index * Constants.CELL_INDICIES + Cell.BG] & BgFlags.PROTECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,7 +197,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
* to GC as it significantly reduced the amount of new objects/references needed.
|
* to GC as it significantly reduced the amount of new objects/references needed.
|
||||||
*/
|
*/
|
||||||
public loadCell(index: number, cell: ICellData): ICellData {
|
public loadCell(index: number, cell: ICellData): ICellData {
|
||||||
$startIndex = index * CELL_SIZE;
|
$startIndex = index * Constants.CELL_INDICIES;
|
||||||
cell.content = this._data[$startIndex + Cell.CONTENT];
|
cell.content = this._data[$startIndex + Cell.CONTENT];
|
||||||
cell.fg = this._data[$startIndex + Cell.FG];
|
cell.fg = this._data[$startIndex + Cell.FG];
|
||||||
cell.bg = this._data[$startIndex + Cell.BG];
|
cell.bg = this._data[$startIndex + Cell.BG];
|
||||||
@@ -223,9 +221,9 @@ export class BufferLine implements IBufferLine {
|
|||||||
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
||||||
this._extendedAttrs[index] = cell.extended;
|
this._extendedAttrs[index] = cell.extended;
|
||||||
}
|
}
|
||||||
this._data[index * CELL_SIZE + Cell.CONTENT] = cell.content;
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = cell.content;
|
||||||
this._data[index * CELL_SIZE + Cell.FG] = cell.fg;
|
this._data[index * Constants.CELL_INDICIES + Cell.FG] = cell.fg;
|
||||||
this._data[index * CELL_SIZE + Cell.BG] = cell.bg;
|
this._data[index * Constants.CELL_INDICIES + Cell.BG] = cell.bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -238,9 +236,9 @@ export class BufferLine implements IBufferLine {
|
|||||||
if (attrs.bg & BgFlags.HAS_EXTENDED) {
|
if (attrs.bg & BgFlags.HAS_EXTENDED) {
|
||||||
this._extendedAttrs[index] = attrs.extended;
|
this._extendedAttrs[index] = attrs.extended;
|
||||||
}
|
}
|
||||||
this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
|
||||||
this._data[index * CELL_SIZE + Cell.FG] = attrs.fg;
|
this._data[index * Constants.CELL_INDICIES + Cell.FG] = attrs.fg;
|
||||||
this._data[index * CELL_SIZE + Cell.BG] = attrs.bg;
|
this._data[index * Constants.CELL_INDICIES + Cell.BG] = attrs.bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -251,7 +249,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
*/
|
*/
|
||||||
public addCodepointToCell(index: number, codePoint: number, width: number): void {
|
public addCodepointToCell(index: number, codePoint: number, width: number): void {
|
||||||
this._invalidateStringCache();
|
this._invalidateStringCache();
|
||||||
let content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
let content = this._data[index * Constants.CELL_INDICIES + Cell.CONTENT];
|
||||||
if (content & Content.IS_COMBINED_MASK) {
|
if (content & Content.IS_COMBINED_MASK) {
|
||||||
// we already have a combined string, simply add
|
// we already have a combined string, simply add
|
||||||
this._combined[index] += stringFromCodePoint(codePoint);
|
this._combined[index] += stringFromCodePoint(codePoint);
|
||||||
@@ -273,7 +271,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
content &= ~Content.WIDTH_MASK;
|
content &= ~Content.WIDTH_MASK;
|
||||||
content |= width << Content.WIDTH_SHIFT;
|
content |= width << Content.WIDTH_SHIFT;
|
||||||
}
|
}
|
||||||
this._data[index * CELL_SIZE + Cell.CONTENT] = content;
|
this._data[index * Constants.CELL_INDICIES + Cell.CONTENT] = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertCells(pos: number, n: number, fillCellData: ICellData): void {
|
public insertCells(pos: number, n: number, fillCellData: ICellData): void {
|
||||||
@@ -369,14 +367,14 @@ export class BufferLine implements IBufferLine {
|
|||||||
* The underlying array buffer will not change if there is still enough space
|
* The underlying array buffer will not change if there is still enough space
|
||||||
* to hold the new buffer line data.
|
* to hold the new buffer line data.
|
||||||
* Returns a boolean indicating, whether a `cleanupMemory` call would free
|
* Returns a boolean indicating, whether a `cleanupMemory` call would free
|
||||||
* excess memory (true after shrinking > CLEANUP_THRESHOLD).
|
* excess memory (true after shrinking > Constants.CLEANUP_THRESHOLD).
|
||||||
*/
|
*/
|
||||||
public resize(cols: number, fillCellData: ICellData): boolean {
|
public resize(cols: number, fillCellData: ICellData): boolean {
|
||||||
this._invalidateStringCache();
|
this._invalidateStringCache();
|
||||||
if (cols === this.length) {
|
if (cols === this.length) {
|
||||||
return this._data.length * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength;
|
return this._data.length * 4 * Constants.CLEANUP_THRESHOLD < this._data.buffer.byteLength;
|
||||||
}
|
}
|
||||||
const uint32Cells = cols * CELL_SIZE;
|
const uint32Cells = cols * Constants.CELL_INDICIES;
|
||||||
if (cols > this.length) {
|
if (cols > this.length) {
|
||||||
if (this._data.buffer.byteLength >= uint32Cells * 4) {
|
if (this._data.buffer.byteLength >= uint32Cells * 4) {
|
||||||
// optimization: avoid alloc and data copy if buffer has enough room
|
// optimization: avoid alloc and data copy if buffer has enough room
|
||||||
@@ -411,17 +409,17 @@ export class BufferLine implements IBufferLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.length = cols;
|
this.length = cols;
|
||||||
return uint32Cells * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength;
|
return uint32Cells * 4 * Constants.CLEANUP_THRESHOLD < this._data.buffer.byteLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup underlying array buffer.
|
* Cleanup underlying array buffer.
|
||||||
* A cleanup will be triggered if the array buffer exceeds the actual used
|
* A cleanup will be triggered if the array buffer exceeds the actual used
|
||||||
* memory by a factor of CLEANUP_THRESHOLD.
|
* memory by a factor of Constants.CLEANUP_THRESHOLD.
|
||||||
* Returns 0 or 1 indicating whether a cleanup happened.
|
* Returns 0 or 1 indicating whether a cleanup happened.
|
||||||
*/
|
*/
|
||||||
public cleanupMemory(): number {
|
public cleanupMemory(): number {
|
||||||
if (this._data.length * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength) {
|
if (this._data.length * 4 * Constants.CLEANUP_THRESHOLD < this._data.buffer.byteLength) {
|
||||||
const data = new Uint32Array(this._data.length);
|
const data = new Uint32Array(this._data.length);
|
||||||
data.set(this._data);
|
data.set(this._data);
|
||||||
this._data = data;
|
this._data = data;
|
||||||
@@ -487,8 +485,8 @@ export class BufferLine implements IBufferLine {
|
|||||||
|
|
||||||
public getTrimmedLength(): number {
|
public getTrimmedLength(): number {
|
||||||
for (let i = this.length - 1; i >= 0; --i) {
|
for (let i = this.length - 1; i >= 0; --i) {
|
||||||
if ((this._data[i * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK)) {
|
if ((this._data[i * Constants.CELL_INDICIES + Cell.CONTENT] & Content.HAS_CONTENT_MASK)) {
|
||||||
return i + (this._data[i * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT);
|
return i + (this._data[i * Constants.CELL_INDICIES + Cell.CONTENT] >> Content.WIDTH_SHIFT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -496,8 +494,8 @@ export class BufferLine implements IBufferLine {
|
|||||||
|
|
||||||
public getNoBgTrimmedLength(): number {
|
public getNoBgTrimmedLength(): number {
|
||||||
for (let i = this.length - 1; i >= 0; --i) {
|
for (let i = this.length - 1; i >= 0; --i) {
|
||||||
if ((this._data[i * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK) || (this._data[i * CELL_SIZE + Cell.BG] & Attributes.CM_MASK)) {
|
if ((this._data[i * Constants.CELL_INDICIES + Cell.CONTENT] & Content.HAS_CONTENT_MASK) || (this._data[i * Constants.CELL_INDICIES + Cell.BG] & Attributes.CM_MASK)) {
|
||||||
return i + (this._data[i * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT);
|
return i + (this._data[i * Constants.CELL_INDICIES + Cell.CONTENT] >> Content.WIDTH_SHIFT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -508,19 +506,19 @@ export class BufferLine implements IBufferLine {
|
|||||||
const srcData = src._data;
|
const srcData = src._data;
|
||||||
if (applyInReverse) {
|
if (applyInReverse) {
|
||||||
for (let cell = length - 1; cell >= 0; cell--) {
|
for (let cell = length - 1; cell >= 0; cell--) {
|
||||||
for (let i = 0; i < CELL_SIZE; i++) {
|
for (let i = 0; i < Constants.CELL_INDICIES; i++) {
|
||||||
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i];
|
this._data[(destCol + cell) * Constants.CELL_INDICIES + i] = srcData[(srcCol + cell) * Constants.CELL_INDICIES + i];
|
||||||
}
|
}
|
||||||
if (srcData[(srcCol + cell) * CELL_SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
if (srcData[(srcCol + cell) * Constants.CELL_INDICIES + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
||||||
this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell];
|
this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let cell = 0; cell < length; cell++) {
|
for (let cell = 0; cell < length; cell++) {
|
||||||
for (let i = 0; i < CELL_SIZE; i++) {
|
for (let i = 0; i < Constants.CELL_INDICIES; i++) {
|
||||||
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i];
|
this._data[(destCol + cell) * Constants.CELL_INDICIES + i] = srcData[(srcCol + cell) * Constants.CELL_INDICIES + i];
|
||||||
}
|
}
|
||||||
if (srcData[(srcCol + cell) * CELL_SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
if (srcData[(srcCol + cell) * Constants.CELL_INDICIES + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
||||||
this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell];
|
this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -574,7 +572,7 @@ export class BufferLine implements IBufferLine {
|
|||||||
}
|
}
|
||||||
let result = '';
|
let result = '';
|
||||||
while (startCol < endCol) {
|
while (startCol < endCol) {
|
||||||
const content = this._data[startCol * CELL_SIZE + Cell.CONTENT];
|
const content = this._data[startCol * Constants.CELL_INDICIES + Cell.CONTENT];
|
||||||
const cp = content & Content.CODEPOINT_MASK;
|
const cp = content & Content.CODEPOINT_MASK;
|
||||||
const chars = (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR;
|
const chars = (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR;
|
||||||
result += chars;
|
result += chars;
|
||||||
|
|||||||
@@ -9,30 +9,30 @@ import { Emitter } from 'common/Event';
|
|||||||
|
|
||||||
declare const setTimeout: (handler: () => void, timeout?: number) => void;
|
declare const setTimeout: (handler: () => void, timeout?: number) => void;
|
||||||
|
|
||||||
/**
|
const enum Constants {
|
||||||
* Safety watermark to avoid memory exhaustion and browser engine crash on fast data input.
|
/**
|
||||||
* Enable flow control to avoid this limit and make sure that your backend correctly
|
* Safety watermark to avoid memory exhaustion and browser engine crash on fast data input.
|
||||||
* propagates this to the underlying pty. (see docs for further instructions)
|
* Enable flow control to avoid this limit and make sure that your backend correctly
|
||||||
* Since this limit is meant as a safety parachute to prevent browser crashs,
|
* propagates this to the underlying pty. (see docs for further instructions)
|
||||||
* it is set to a very high number. Typically xterm.js gets unresponsive with
|
* Since this limit is meant as a safety parachute to prevent browser crashs,
|
||||||
* a 100 times lower number (>500 kB).
|
* it is set to a very high number. Typically xterm.js gets unresponsive with
|
||||||
*/
|
* a 100 times lower number (>500 kB).
|
||||||
const DISCARD_WATERMARK = 50000000; // ~50 MB
|
*/
|
||||||
|
DISCARD_WATERMARK = 50000000, // ~50 MB
|
||||||
/**
|
/**
|
||||||
* The max number of ms to spend on writes before allowing the renderer to
|
* The max number of ms to spend on writes before allowing the renderer to
|
||||||
* catch up with a 0ms setTimeout. A value of < 33 to keep us close to
|
* catch up with a 0ms setTimeout. A value of < 33 to keep us close to
|
||||||
* 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
|
* 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
|
||||||
* depends on the time it takes for the renderer to draw the frame.
|
* depends on the time it takes for the renderer to draw the frame.
|
||||||
*/
|
*/
|
||||||
const WRITE_TIMEOUT_MS = 12;
|
WRITE_TIMEOUT_MS = 12,
|
||||||
|
/**
|
||||||
/**
|
* Threshold of max held chunks in the write buffer, that were already processed.
|
||||||
* Threshold of max held chunks in the write buffer, that were already processed.
|
* This is a tradeoff between extensive write buffer shifts (bad runtime) and high
|
||||||
* This is a tradeoff between extensive write buffer shifts (bad runtime) and high
|
* memory consumption by data thats not used anymore.
|
||||||
* memory consumption by data thats not used anymore.
|
*/
|
||||||
*/
|
WRITE_BUFFER_LENGTH_THRESHOLD = 50
|
||||||
const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
|
}
|
||||||
|
|
||||||
export class WriteBuffer extends Disposable {
|
export class WriteBuffer extends Disposable {
|
||||||
private _writeBuffer: (string | Uint8Array)[] = [];
|
private _writeBuffer: (string | Uint8Array)[] = [];
|
||||||
@@ -133,7 +133,7 @@ export class WriteBuffer extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public write(data: string | Uint8Array, callback?: () => void): void {
|
public write(data: string | Uint8Array, callback?: () => void): void {
|
||||||
if (this._pendingData > DISCARD_WATERMARK) {
|
if (this._pendingData > Constants.DISCARD_WATERMARK) {
|
||||||
throw new Error('write data discarded, use flow control to avoid losing data');
|
throw new Error('write data discarded, use flow control to avoid losing data');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ export class WriteBuffer extends Disposable {
|
|||||||
* effectively lowering the redrawing needs, schematically:
|
* effectively lowering the redrawing needs, schematically:
|
||||||
*
|
*
|
||||||
* macroTask _innerWrite:
|
* macroTask _innerWrite:
|
||||||
* if (performance.now() - (lastTime | 0) < WRITE_TIMEOUT_MS):
|
* if (performance.now() - (lastTime | 0) < Constants.WRITE_TIMEOUT_MS):
|
||||||
* schedule microTask _innerWrite(lastTime)
|
* schedule microTask _innerWrite(lastTime)
|
||||||
* else:
|
* else:
|
||||||
* schedule macroTask _innerWrite(0)
|
* schedule macroTask _innerWrite(0)
|
||||||
@@ -218,7 +218,7 @@ export class WriteBuffer extends Disposable {
|
|||||||
* responsibility to slice hard work), but we can at least schedule a screen update as we
|
* responsibility to slice hard work), but we can at least schedule a screen update as we
|
||||||
* gain control.
|
* gain control.
|
||||||
*/
|
*/
|
||||||
const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= WRITE_TIMEOUT_MS
|
const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS
|
||||||
? setTimeout(() => this._innerWrite(0, r))
|
? setTimeout(() => this._innerWrite(0, r))
|
||||||
: this._innerWrite(startTime, r);
|
: this._innerWrite(startTime, r);
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ export class WriteBuffer extends Disposable {
|
|||||||
* current microtask queue (executed before setTimeout).
|
* current microtask queue (executed before setTimeout).
|
||||||
*/
|
*/
|
||||||
// const continuation: (r: boolean) => void = performance.now() - startTime >=
|
// const continuation: (r: boolean) => void = performance.now() - startTime >=
|
||||||
// WRITE_TIMEOUT_MS
|
// Constants.WRITE_TIMEOUT_MS
|
||||||
// ? r => setTimeout(() => this._innerWrite(0, r))
|
// ? r => setTimeout(() => this._innerWrite(0, r))
|
||||||
// : r => this._innerWrite(startTime, r);
|
// : r => this._innerWrite(startTime, r);
|
||||||
|
|
||||||
@@ -255,14 +255,14 @@ export class WriteBuffer extends Disposable {
|
|||||||
this._bufferOffset++;
|
this._bufferOffset++;
|
||||||
this._pendingData -= data.length;
|
this._pendingData -= data.length;
|
||||||
|
|
||||||
if (performance.now() - startTime >= WRITE_TIMEOUT_MS) {
|
if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this._writeBuffer.length > this._bufferOffset) {
|
if (this._writeBuffer.length > this._bufferOffset) {
|
||||||
// Allow renderer to catch up before processing the next batch
|
// Allow renderer to catch up before processing the next batch
|
||||||
// trim already processed chunks if we are above threshold
|
// trim already processed chunks if we are above threshold
|
||||||
if (this._bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
|
if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) {
|
||||||
this._writeBuffer = this._writeBuffer.slice(this._bufferOffset);
|
this._writeBuffer = this._writeBuffer.slice(this._bufferOffset);
|
||||||
this._callbacks = this._callbacks.slice(this._bufferOffset);
|
this._callbacks = this._callbacks.slice(this._bufferOffset);
|
||||||
this._bufferOffset = 0;
|
this._bufferOffset = 0;
|
||||||
|
|||||||
@@ -4,10 +4,16 @@
|
|||||||
*/
|
*/
|
||||||
import { IParams, ParamsArray } from 'common/parser/Types';
|
import { IParams, ParamsArray } from 'common/parser/Types';
|
||||||
|
|
||||||
// max value supported for a single param/subparam (clamped to positive int32 range)
|
const enum Constants {
|
||||||
const MAX_VALUE = 0x7FFFFFFF;
|
/**
|
||||||
// max allowed subparams for a single sequence (hardcoded limitation)
|
* Max value supported for a single param/subparam (clamped to positive int32 range)
|
||||||
const MAX_SUBPARAMS = 256;
|
*/
|
||||||
|
MAX_VALUE = 0x7FFFFFFF,
|
||||||
|
/**
|
||||||
|
* Max allowed subparams for a single sequence (hardcoded limitation)
|
||||||
|
*/
|
||||||
|
MAX_SUBPARAMS = 256
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Params storage class.
|
* Params storage class.
|
||||||
@@ -70,7 +76,7 @@ export class Params implements IParams {
|
|||||||
* @param maxSubParamsLength max length of storable sub parameters
|
* @param maxSubParamsLength max length of storable sub parameters
|
||||||
*/
|
*/
|
||||||
constructor(public maxLength: number = 32, public maxSubParamsLength: number = 32) {
|
constructor(public maxLength: number = 32, public maxSubParamsLength: number = 32) {
|
||||||
if (maxSubParamsLength > MAX_SUBPARAMS) {
|
if (maxSubParamsLength > Constants.MAX_SUBPARAMS) {
|
||||||
throw new Error('maxSubParamsLength must not be greater than 256');
|
throw new Error('maxSubParamsLength must not be greater than 256');
|
||||||
}
|
}
|
||||||
this.params = new Int32Array(maxLength);
|
this.params = new Int32Array(maxLength);
|
||||||
@@ -159,7 +165,7 @@ export class Params implements IParams {
|
|||||||
throw new Error('values lesser than -1 are not allowed');
|
throw new Error('values lesser than -1 are not allowed');
|
||||||
}
|
}
|
||||||
this._subParamsIdx[this.length] = this._subParamsLength << 8 | this._subParamsLength;
|
this._subParamsIdx[this.length] = this._subParamsLength << 8 | this._subParamsLength;
|
||||||
this.params[this.length++] = value > MAX_VALUE ? MAX_VALUE : value;
|
this.params[this.length++] = value > Constants.MAX_VALUE ? Constants.MAX_VALUE : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,7 +187,7 @@ export class Params implements IParams {
|
|||||||
if (value < -1) {
|
if (value < -1) {
|
||||||
throw new Error('values lesser than -1 are not allowed');
|
throw new Error('values lesser than -1 are not allowed');
|
||||||
}
|
}
|
||||||
this._subParams[this._subParamsLength++] = value > MAX_VALUE ? MAX_VALUE : value;
|
this._subParams[this._subParamsLength++] = value > Constants.MAX_VALUE ? Constants.MAX_VALUE : value;
|
||||||
this._subParamsIdx[this.length - 1]++;
|
this._subParamsIdx[this.length - 1]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +243,6 @@ export class Params implements IParams {
|
|||||||
|
|
||||||
const store = this._digitIsSub ? this._subParams : this.params;
|
const store = this._digitIsSub ? this._subParams : this.params;
|
||||||
const cur = store[length - 1];
|
const cur = store[length - 1];
|
||||||
store[length - 1] = ~cur ? Math.min(cur * 10 + value, MAX_VALUE) : value;
|
store[length - 1] = ~cur ? Math.min(cur * 10 + value, Constants.MAX_VALUE) : value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
|||||||
import { IBufferService, ILogService, IOptionsService, type IBufferResizeEvent } from 'common/services/Services';
|
import { IBufferService, ILogService, IOptionsService, type IBufferResizeEvent } from 'common/services/Services';
|
||||||
import { Emitter } from 'common/Event';
|
import { Emitter } from 'common/Event';
|
||||||
|
|
||||||
export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
|
export const enum BufferServiceConstants {
|
||||||
export const MINIMUM_ROWS = 1;
|
MINIMUM_COLS = 2, // Less than 2 can mess with wide chars
|
||||||
|
MINIMUM_ROWS = 1
|
||||||
|
}
|
||||||
|
|
||||||
export class BufferService extends Disposable implements IBufferService {
|
export class BufferService extends Disposable implements IBufferService {
|
||||||
public serviceBrand: any;
|
public serviceBrand: any;
|
||||||
@@ -37,8 +39,8 @@ export class BufferService extends Disposable implements IBufferService {
|
|||||||
@ILogService logService: ILogService
|
@ILogService logService: ILogService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.cols = Math.max(optionsService.rawOptions.cols || 0, MINIMUM_COLS);
|
this.cols = Math.max(optionsService.rawOptions.cols || 0, BufferServiceConstants.MINIMUM_COLS);
|
||||||
this.rows = Math.max(optionsService.rawOptions.rows || 0, MINIMUM_ROWS);
|
this.rows = Math.max(optionsService.rawOptions.rows || 0, BufferServiceConstants.MINIMUM_ROWS);
|
||||||
this.buffers = this._register(new BufferSet(optionsService, this, logService));
|
this.buffers = this._register(new BufferSet(optionsService, this, logService));
|
||||||
this._register(this.buffers.onBufferActivate(e => {
|
this._register(this.buffers.onBufferActivate(e => {
|
||||||
this._onScroll.fire(e.activeBuffer.ydisp);
|
this._onScroll.fire(e.activeBuffer.ydisp);
|
||||||
|
|||||||
@@ -11,13 +11,15 @@
|
|||||||
|
|
||||||
import { IServiceIdentifier } from 'common/services/Services';
|
import { IServiceIdentifier } from 'common/services/Services';
|
||||||
|
|
||||||
const DI_TARGET = 'di$target';
|
const enum Constants {
|
||||||
const DI_DEPENDENCIES = 'di$dependencies';
|
DI_TARGET = 'di$target',
|
||||||
|
DI_DEPENDENCIES = 'di$dependencies'
|
||||||
|
}
|
||||||
|
|
||||||
export const serviceRegistry: Map<string, IServiceIdentifier<any>> = new Map();
|
export const serviceRegistry: Map<string, IServiceIdentifier<any>> = new Map();
|
||||||
|
|
||||||
export function getServiceDependencies(ctor: any): { id: IServiceIdentifier<any>, index: number, optional: boolean }[] {
|
export function getServiceDependencies(ctor: any): { id: IServiceIdentifier<any>, index: number, optional: boolean }[] {
|
||||||
return ctor[DI_DEPENDENCIES] || [];
|
return ctor[Constants.DI_DEPENDENCIES] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createDecorator<T>(id: string): IServiceIdentifier<T> {
|
export function createDecorator<T>(id: string): IServiceIdentifier<T> {
|
||||||
@@ -40,10 +42,10 @@ export function createDecorator<T>(id: string): IServiceIdentifier<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function storeServiceDependency(id: Function, target: Function, index: number): void {
|
function storeServiceDependency(id: Function, target: Function, index: number): void {
|
||||||
if ((target as any)[DI_TARGET] === target) {
|
if ((target as any)[Constants.DI_TARGET] === target) {
|
||||||
(target as any)[DI_DEPENDENCIES].push({ id, index });
|
(target as any)[Constants.DI_DEPENDENCIES].push({ id, index });
|
||||||
} else {
|
} else {
|
||||||
(target as any)[DI_DEPENDENCIES] = [{ id, index }];
|
(target as any)[Constants.DI_DEPENDENCIES] = [{ id, index }];
|
||||||
(target as any)[DI_TARGET] = target;
|
(target as any)[Constants.DI_TARGET] = target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user