Compare commits

..
Author SHA1 Message Date
Daniel ImmsandCursor 3395d051a9 fix(parser): WASM scratch layout and scan cache for chunked parse
Place parser state above the linked transition table so scans emit ops
correctly, and preserve or clear the WASM scan cache across nested and
reused parse buffers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 15:31:23 -07:00
Daniel ImmsandCursor 31b8409120 fix(parser): chunked WASM scan, op coalescing, and benchmark runtime paths
The WASM scanner could fill its fixed 4096-op buffer on large inputs (e.g. 5MB
of newlines or OSC traffic) and previously failed or appeared hung. ESC op
coalescing also corrupted the lengths field used for dispatch, causing millions
of redundant handler invocations on OSC benchmark data.

WASM scanner (parser.c / parser.h):
- Add scan_offset to ParserScanHeader; scan(offset, length) resumes after a
  partial fill instead of returning -1 for the whole buffer.
- Return early from scan when the op buffer is full; all emit sites use
  emit_or_stop() so scan_offset records where to continue.
- Coalesce consecutive OP_EXECUTE and OP_ESC ops with the same code/ident;
  lengths holds repeat count, starts holds the input index for ESC.
- Keep CSI/DCS hook layout unchanged (lengths = final byte index, starts =
  param arena offset).

TypeScript dispatch (EscapeSequenceParserWasm.ts):
- Drive parsing via runWasmScanChunks(): scan a slice, dispatch ops, advance
  inputOffset to nextOffset until the chunk is consumed.
- Fix ESC dispatch: chunkPos uses starts (input index); escCount uses lengths
  (repeat count). Avoid treating the CSI-style final-byte index as a loop bound.
- Throw if nextOffset does not advance to catch stuck scans.

EscapeSequenceParser.ts:
- Extend _scanCache for chunked scan state; reset() clears inputOffset and
  cached scan data so later parses are not skipped.

Benchmarks:
- Import compiled modules from ../../out/common and ../../out/browser so
  npm run benchmark works without NODE_PATH after path-mapping removal.
- Update benchmark skill notes accordingly.

Build:
- build_parser_wasm.sh selects a clang with wasm32 support (Homebrew LLVM on
  macOS when Xcode clang lacks the target).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 15:06:00 -07:00
Cursor AgentandDaniel Imms 3eca55a80d feat(parser): WASM VT500 scanner with ScanResult SOA dispatch
- Add C WASM scanner emitting structure-of-arrays ops (kinds, starts, lengths, aux, params)
- Integrate scan+dispatch in EscapeSequenceParser with async handler resume
- Fix CSI fast-path final byte index, ERROR state reporting, and precedingJoinState for REP
- Add build pipeline (build_parser_wasm.sh, transition table generator, wasm embed)

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-31 21:35:02 +00:00
96 changed files with 2144 additions and 773 deletions
+1 -1
View File
@@ -25,4 +25,4 @@ When writing benchmark instructions:
## Notes
- Benchmarks run from built JS in `out-test/benchmark/*.benchmark.js`.
- Keep `NODE_PATH=./out` (handled by the npm script).
- Benchmark sources import compiled modules from `../../out/common/` and `../../out/browser/` (run `npm run build` first).
+1 -3
View File
@@ -1,7 +1,6 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [
"import",
"typescript"
],
"categories": {
@@ -105,8 +104,7 @@
"typescript/no-wrapper-object-types": "error",
"typescript/prefer-as-const": "error",
"typescript/prefer-namespace-keyword": "error",
"typescript/triple-slash-reference": "error",
"import/no-cycle": "error"
"typescript/triple-slash-reference": "error"
},
"overrides": [
{
+1 -1
View File
@@ -39,7 +39,7 @@ The preferred way to report bugs or request features is to use
It's pretty common for maintainers of large open source projects to suffer from burnout, especially when needing to triage a large number of incoming issues instead of actually building things. Here are some of the steps we take to try mitigate this:
- Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transferred there without further comment and core maintainers may or may not participate in discussions.
- Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transfered there without further comment and core maintainers may or may not participate in discussions.
- Issues are strictly for well defined features or bugs that are _actionable_.
- Sometimes features are out of scope. A common example of this is a niche feature that the pricipal implementation ([VS Code](https://code.visualstudio.com/)) won't leverage and therefore would be difficult to maintain and likely suffer from bitrot. The reporter may not agree with this, but you could always create an addon if that works or maintain your own fork if it comes to that.
- If a feature does not have a clear way forward or needs more discussion it may be closed ro moved to a discussion.
+1 -1
View File
@@ -82,7 +82,7 @@ declare module '@xterm/addon-clipboard' {
/**
* The clipboard provider interface that enables xterm.js to access the browser clipboard.
*
* Note that this clipboard provider does not implement own cut buffers as xterm does,
* Note this this clipboard provider does not implement own cut buffers as xterm does,
* but redirect the selection parameter always to navigator.clipboard.
*/
export class BrowserClipboardProvider implements IClipboardProvider{
+6 -6
View File
@@ -74,14 +74,14 @@ export class FitAddon implements ITerminalAddon, IFitApi {
: (this._terminal.options.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH));
const parentElementStyle = _getComputedStyle(this._terminal.element.parentElement);
const parentElementHeight = Math.max(0, parseInt(parentElementStyle.getPropertyValue('height'), 10) || 0);
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'), 10) || 0);
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
const elementStyle = _getComputedStyle(this._terminal.element);
const elementPadding = {
top: parseInt(elementStyle.getPropertyValue('padding-top'), 10) || 0,
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom'), 10) || 0,
right: parseInt(elementStyle.getPropertyValue('padding-right'), 10) || 0,
left: parseInt(elementStyle.getPropertyValue('padding-left'), 10) || 0
top: parseInt(elementStyle.getPropertyValue('padding-top')),
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
right: parseInt(elementStyle.getPropertyValue('padding-right')),
left: parseInt(elementStyle.getPropertyValue('padding-left'))
};
const elementPaddingVer = elementPadding.top + elementPadding.bottom;
const elementPaddingHor = elementPadding.right + elementPadding.left;
+3 -3
View File
@@ -222,8 +222,8 @@ export class IIPHandler implements IOscHandler, IResetHandler {
private _dim(s: string, total: number, cdim: number): number {
if (s === 'auto') return 0;
if (s.endsWith('%')) return parseInt(s.slice(0, -1), 10) * total / 100;
if (s.endsWith('px')) return parseInt(s.slice(0, -2), 10);
return parseInt(s, 10) * cdim;
if (s.endsWith('%')) return parseInt(s.slice(0, -1)) * total / 100;
if (s.endsWith('px')) return parseInt(s.slice(0, -2));
return parseInt(s) * cdim;
}
}
+1 -2
View File
@@ -9,8 +9,7 @@ import { IDisposable, IMarker, Terminal } from '@xterm/xterm';
import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants';
import type { AttributeData } from 'common/buffer/AttributeData';
import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from 'common/parser/Types';
import type { IInputHandler } from 'common/Types';
import type { IBufferLine, IExtendedAttrs } from 'common/buffer/Types';
import type { IBufferLine, IExtendedAttrs, IInputHandler } from 'common/Types';
import type { ITerminal, ReadonlyColorSet } from 'browser/Types';
import type { IRenderDimensions } from 'browser/renderer/shared/Types';
import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
@@ -168,7 +168,7 @@ export function parseKittyCommand(data: string): IKittyCommand {
cmd.deleteSelector = value;
continue;
}
const numValue = parseInt(value, 10);
const numValue = parseInt(value);
switch (key) {
case KittyKey.FORMAT: cmd.format = numValue; break;
case KittyKey.ID: cmd.id = numValue; break;
+1 -1
View File
@@ -21,7 +21,7 @@ const CACHE_SIZE = 100000;
/**
* Enable ligature support for the provided Terminal instance. To function
* properly, this must be called after `open()` is called on the terminal. If
* properly, this must be called after `open()` is called on the therminal. If
* the font currently in use supports ligatures, the terminal will automatically
* start to render them.
* @param term Terminal instance from xterm.js
+4 -5
View File
@@ -7,8 +7,7 @@
import type { IBuffer, IBufferCell, IBufferRange, ITerminalAddon, Terminal } from '@xterm/xterm';
import type { IHTMLSerializeOptions, SerializeAddon as ISerializeApi, ISerializeOptions, ISerializeRange } from '@xterm/addon-serialize';
import { IColor } from 'common/Types';
import { IAttributeData } from 'common/buffer/Types';
import { IAttributeData, IColor } from 'common/Types';
import { DEFAULT_ANSI_COLORS } from 'browser/Types';
import { UnderlineStyle } from 'common/buffer/Constants';
@@ -173,7 +172,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell();
private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell();
protected _rowEnd(row: number, isLastRow: boolean): void {
// if there is colorful empty cell at line end, we must pad it back, or the color block
// if there is colorful empty cell at line end, whe must pad it back, or the the color block
// will missing
if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) {
// use clear right to set background.
@@ -221,7 +220,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
// you can't use control sequence to move cursor to (x === row)
(thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) &&
// change background of the first wrapped cell also affects BCE
// so we mark it as invalid to simplify the process to determine line separator
// so we mark it as invalid to simply the process to determine line separator
equalBg(thisRowLastChar, nextRowFirstChar)
) {
isValid = true;
@@ -233,7 +232,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
isNextRowFirstCharDoubleWidth &&
(thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) &&
// change background of the first wrapped cell also affects BCE
// so we mark it as invalid to simplify the process to determine line separator
// so we mark it as invalid to simply the process to determine line separator
equalBg(thisRowLastChar, nextRowFirstChar) &&
equalBg(thisRowLastSecondChar, nextRowFirstChar)
) {
@@ -192,7 +192,7 @@ test.describe('SerializeAddon', () => {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags at the end, serialize will use \x1b[0m to clear instead of the specific disable sequence
sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence
sgr(INVERSE) + line,
sgr(BOLD) + line,
sgr(UNDERLINED) + line,
+1 -1
View File
@@ -3,7 +3,7 @@ import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { ReadonlyColorSet } from 'browser/Types';
import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants';
import { IDecorationService, IOptionsService } from 'common/services/Services';
import { ICellData } from 'common/buffer/Types';
import { ICellData } from 'common/Types';
import { Terminal } from '@xterm/xterm';
import { rgba } from 'common/Color';
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
+1 -1
View File
@@ -1125,7 +1125,7 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol
// were covered (fg=#8ae234, bg=#c4a000).
const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12);
// Set alpha channel of relevant pixels to 0
// Set alpha channel of relevent pixels to 0
let isEmpty = true;
for (let offset = 0; offset < imageData.data.length; offset += 4) {
// Check exact match
+1 -1
View File
@@ -40,7 +40,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi
};
const gl = document.createElement('canvas').getContext('webgl2', contextAttributes) as IWebGL2RenderingContext;
if (!gl) {
throw new Error('WebGL2 is only supported on Safari 16 and above');
throw new Error('Webgl2 is only supported on Safari 16 and above');
}
}
super();
+3 -3
View File
@@ -10,7 +10,7 @@ import { CursorBlinkStateManager } from './CursorBlinkStateManager';
import { observeDevicePixelDimensions } from './DevicePixelObserver';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { CharData, IBufferLine, ICellData } from 'common/buffer/Types';
import { CharData, IBufferLine, ICellData } from 'common/Types';
import { AttributeData } from 'common/buffer/AttributeData';
import { CellData } from 'common/buffer/CellData';
import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
@@ -664,10 +664,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height;
this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width;
// The size of the canvas on the page. It's important that this rounds to nearest integer
// The the size of the canvas on the page. It's important that this rounds to nearest integer
// and not ceils as browsers often have floating point precision issues where
// `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when
// it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too
// it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too
// large for the canvas element size.
this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio);
this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio);
@@ -221,8 +221,8 @@ function drawPathDefinitionCharacter(
const rx = parseFloat(args[0]) * deviceCellWidth;
const ry = parseFloat(args[1]) * deviceCellHeight;
const xAxisRotation = parseFloat(args[2]) * Math.PI / 180;
const largeArcFlag = parseInt(args[3], 10);
const sweepFlag = parseInt(args[4], 10);
const largeArcFlag = parseInt(args[3]);
const sweepFlag = parseInt(args[4]);
const x = xOffset + parseFloat(args[5]) * deviceCellWidth;
const y = yOffset + parseFloat(args[6]) * deviceCellHeight;
drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y);
+2 -2
View File
@@ -23,12 +23,12 @@ declare module '@xterm/addon-webgl' {
public readonly onChangeTextureAtlas: IEvent<HTMLCanvasElement>;
/**
* An event that is fired when a new page is added to the texture atlas.
* An event that is fired when the a new page is added to the texture atlas.
*/
public readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
/**
* An event that is fired when a page is removed from the texture atlas.
* An event that is fired when the a page is removed from the texture atlas.
*/
public readonly onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Copyright (c) 2026 The xterm.js authors. All rights reserved.
# @license MIT
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
if [[ ! -f src/common/parser/wasm/transition_table.h ]]; then
npm run build
node bin/gen_parser_transition_table.mjs
fi
WASM_DIR=src/common/parser/wasm
OUT="$WASM_DIR/parser.wasm"
# Apple Clang (Xcode CLT) has no wasm32 target; prefer Homebrew LLVM when needed.
wasm_clang_has_target() {
"$1" --print-targets 2>/dev/null | grep -q 'wasm32'
}
resolve_wasm_toolchain() {
local clang_bin=clang
if wasm_clang_has_target clang; then
:
else
local llvm_prefix=""
if command -v brew >/dev/null 2>&1; then
llvm_prefix="$(brew --prefix llvm 2>/dev/null || true)"
fi
for prefix in "$llvm_prefix" /opt/homebrew/opt/llvm /usr/local/opt/llvm; do
if [[ -n "$prefix" && -x "$prefix/bin/clang" ]] && wasm_clang_has_target "$prefix/bin/clang"; then
clang_bin="$prefix/bin/clang"
local lld_prefix=""
if command -v brew >/dev/null 2>&1; then
lld_prefix="$(brew --prefix lld 2>/dev/null || true)"
fi
for lld_dir in "$lld_prefix/bin" /opt/homebrew/opt/lld/bin /usr/local/opt/lld/bin; do
if [[ -n "$lld_dir" && -x "$lld_dir/lld" ]]; then
PATH="$lld_dir:$(dirname "$clang_bin"):$PATH"
break
fi
done
PATH="$(dirname "$clang_bin"):$PATH"
break
fi
done
if ! wasm_clang_has_target "$clang_bin"; then
echo "error: no clang with wasm32 support found (macOS: brew install llvm lld)" >&2
exit 1
fi
fi
WASM_CLANG="$clang_bin"
}
resolve_wasm_toolchain
"$WASM_CLANG" --target=wasm32-unknown-unknown -O2 -nostdlib -fuse-ld=lld \
-Wl,--no-entry \
-Wl,--export-memory \
-Wl,--export=reset \
-Wl,--export=scan \
-Wl,--export=probe_action \
-Wl,--export=set_state \
-Wl,--export=sync_params_from \
-Wl,--export=export_params_to \
-Wl,--export=get_params_len \
-Wl,--export=get_subparams_len \
-Wl,--export=get_subparams_ptr \
-Wl,--export=get_subparams_idx_ptr \
-Wl,--export=get_input_ptr \
-Wl,--export=get_kinds_ptr \
-Wl,--export=get_starts_ptr \
-Wl,--export=get_lengths_ptr \
-Wl,--export=get_aux_ptr \
-Wl,--export=get_param_starts_ptr \
-Wl,--export=get_param_counts_ptr \
-Wl,--export=get_params_ptr \
-Wl,--export=get_state_ptr \
-Wl,--export=get_header_ptr \
-Wl,--initial-memory=67108864 \
-Wl,--max-memory=67108864 \
"$WASM_DIR/parser.c" -o "$OUT"
node bin/embed_parser_wasm.mjs
echo "Built $OUT"
+23
View File
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { readFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
const wasm = readFileSync(join(root, 'src/common/parser/wasm/parser.wasm'));
const b64 = wasm.toString('base64');
const out = `/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
/** Base64-encoded parser.wasm (generated by bin/build_parser_wasm.sh) */
export const PARSER_WASM_BASE64 = '${b64}';\n`;
writeFileSync(join(root, 'src/common/parser/wasm/parser.wasm.embed.ts'), out);
console.info(`Embedded parser.wasm (${wasm.length} bytes)`);
+34
View File
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
// @ts-check
import { writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
// Import compiled transition table
const { VT500_TRANSITION_TABLE } = await import(join(root, 'out/common/parser/EscapeSequenceParser.js'));
const table = VT500_TRANSITION_TABLE.table;
let out = '/* Auto-generated by bin/gen_parser_transition_table.mjs - do not edit */\n';
out += `#ifndef PARSER_TRANSITION_TABLE_H\n#define PARSER_TRANSITION_TABLE_H\n\n`;
out += `#include <stdint.h>\n\n`;
out += `#define PARSER_TABLE_SIZE ${table.length}\n\n`;
out += `static const uint16_t PARSER_TRANSITION_TABLE[PARSER_TABLE_SIZE] = {\n`;
for (let i = 0; i < table.length; i++) {
if (i % 16 === 0) out += ' ';
out += `0x${table[i].toString(16)}`;
out += i < table.length - 1 ? ', ' : '';
if (i % 16 === 15) out += '\n';
}
if (table.length % 16 !== 0) out += '\n';
out += `};\n\n#endif\n`;
const headerPath = join(root, 'src/common/parser/wasm/transition_table.h');
writeFileSync(headerPath, out);
console.info(`Wrote ${headerPath} (${table.length} entries)`);
+1
View File
@@ -34,6 +34,7 @@
"start": "node demo/start",
"dev": "concurrently -k -p [{name}] -n tsc,esbuild,esbuild-demo-client,esbuild-demo-server,server -c blue,yellow,cyan,green,magenta \"npm:tsc-watch\" \"npm:esbuild-watch\" \"npm:esbuild-demo-client-watch\" \"npm:esbuild-demo-server-watch\" \"npm:start\"",
"build": "npm run tsc",
"build:parser-wasm": "bash bin/build_parser_wasm.sh",
"watch": "npm run tsc-watch",
"tsc": "tsgo -b ./tsconfig.all.json",
"tsc-watch": "tsgo -b -w ./tsconfig.all.json --preserveWatchOutput",
+1 -1
View File
@@ -102,7 +102,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
private _keyDownHandled: boolean = false;
/**
* Records whether a keydown event has occurred since the last keyup event, i.e. whether a key
* Records whether a keydown event has occured since the last keyup event, i.e. whether a key
* is currently "pressed".
*/
private _keyDownSeen: boolean = false;
+1 -2
View File
@@ -8,8 +8,7 @@ import { OscLinkProvider } from './OscLinkProvider';
import { ILink } from './Types';
import { createCellData, MockBufferService, MockOptionsService } from '../common/TestUtils.test';
import { IBufferService, IOscLinkService } from '../common/services/Services';
import { IOscLinkData } from '../common/Types';
import { IBufferLine } from '../common/buffer/Types';
import { IBufferLine, IOscLinkData } from '../common/Types';
class TestOscLinkService implements IOscLinkService {
public serviceBrand: any;
+1 -1
View File
@@ -6,7 +6,7 @@
import { IBufferRange, ILink } from './Types';
import { ILinkProvider } from './services/Services';
import { CellData } from '../common/buffer/CellData';
import { IBufferLine } from '../common/buffer/Types';
import { IBufferLine } from '../common/Types';
import { IBufferService, IOptionsService, IOscLinkService } from '../common/services/Services';
export class OscLinkProvider implements ILinkProvider {
+1 -1
View File
@@ -9,7 +9,7 @@ import { assert } from 'chai';
import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine';
import { CellData } from '../common/buffer/CellData';
import { MockUnicodeService, createCellData } from '../common/TestUtils.test';
import { IMarker } from '../common/buffer/Types';
import { IMarker } from '../common/Types';
const INIT_COLS = 80;
const INIT_ROWS = 24;
+2 -3
View File
@@ -7,9 +7,8 @@ import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration, I
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from './renderer/shared/Types';
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from './Types';
import { IAttributeData, IBuffer, IBufferLine, IBufferSet, ICellData } from '../common/buffer/Types';
import { ICircularList } from '../common/CircularList';
import { XtermListener, ICharset, ITerminalOptions, ColorIndex } from '../common/Types';
import { IBuffer, IBufferSet } from '../common/buffer/Types';
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from '../common/Types';
import { Buffer } from '../common/buffer/Buffer';
import * as Browser from '../common/Platform';
import { CoreBrowserTerminal } from './CoreBrowserTerminal';
+1 -1
View File
@@ -47,7 +47,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer {
// enough time to pass before refreshing again.
const refreshRequestTime: number = performance.now();
if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) {
// Enough time has elapsed since the last refresh; refresh immediately
// Enough time has lapsed since the last refresh; refresh immediately
this._lastRefreshMs = refreshRequestTime;
this._innerRefresh();
} else if (!this._additionalRefreshRequested) {
+2 -3
View File
@@ -3,9 +3,8 @@
* @license MIT
*/
import { IColor, ITerminalOptions } from '../common/Types';
import { CharData, IBuffer } from '../common/buffer/Types';
import { ICoreTerminal } from '../common/CoreTerminal';
import { CharData, IColor, ICoreTerminal, ITerminalOptions } from '../common/Types';
import { IBuffer } from '../common/buffer/Types';
import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm';
import { channels, css } from '../common/Color';
import type { IEvent } from '../common/Event';
+5 -5
View File
@@ -6,8 +6,8 @@
export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyle'>, event: {clientX: number, clientY: number}, element: HTMLElement): [number, number] {
const rect = element.getBoundingClientRect();
const elementStyle = window.getComputedStyle(element);
const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'), 10);
const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'), 10);
const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'));
const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'));
return [
event.clientX - rect.left - leftPadding,
event.clientY - rect.top - topPadding
@@ -22,7 +22,7 @@ export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyl
* @param event The mouse event.
* @param element The terminal's container element.
* @param colCount The number of columns in the terminal.
* @param rowCount The number of rows in the terminal.
* @param rowCount The number of rows n the terminal.
* @param hasValidCharSize Whether there is a valid character size available.
* @param cssCellWidth The cell width device pixel render dimensions.
* @param cssCellHeight The cell height device pixel render dimensions.
@@ -31,7 +31,7 @@ export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyl
* select that cell and the right half will select the next cell.
*/
export function getCoords(window: Pick<Window, 'getComputedStyle'>, event: Pick<MouseEvent, 'clientX' | 'clientY'>, element: HTMLElement, colCount: number, rowCount: number, hasValidCharSize: boolean, cssCellWidth: number, cssCellHeight: number, isSelection?: boolean): [number, number] | undefined {
// Coordinates cannot be measured if there is no valid character size.
// Coordinates cannot be measured if there are no valid
if (!hasValidCharSize) {
return undefined;
}
@@ -45,7 +45,7 @@ export function getCoords(window: Pick<Window, 'getComputedStyle'>, event: Pick<
coords[1] = Math.ceil(coords[1] / cssCellHeight);
// Ensure coordinates are within the terminal viewport. Note that selections
// need an additional point of precision to cover the end point (as characters
// need an addition point of precision to cover the end point (as characters
// cover half of one char and half of the next).
coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0));
coords[1] = Math.min(Math.max(coords[1], 1), rowCount);
+2 -2
View File
@@ -33,7 +33,7 @@ let nextTerminalId = 1;
/**
* The standard renderer and fallback for when the webgl addon is slow. This is not meant to be
* particularly fast and will even lack some features such as custom glyphs, however this is more
* particularly fast and will even lack some features such as custom glyphs, hoever this is more
* reliable as webgl may not work on some machines.
*/
export class DomRenderer extends Disposable implements IRenderer {
@@ -430,7 +430,7 @@ export class DomRenderer extends Disposable implements IRenderer {
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._bufferService.cols, middleRowsCount));
// Draw final row
if (viewportCappedStartRow !== viewportCappedEndRow) {
// Only draw viewportEndRow if it's not the same as viewportStartRow
// Only draw viewportEndRow if it's not the same as viewporttartRow
const finalEndCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, finalEndCol));
}
@@ -9,7 +9,7 @@ import { DomRendererRowFactory } from './DomRendererRowFactory';
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from '../../../common/buffer/Constants';
import { BufferLine, DEFAULT_ATTR_DATA } from '../../../common/buffer/BufferLine';
import { BufferLineStringCache } from '../../../common/buffer/BufferLineStringCache';
import { IBufferLine } from '../../../common/buffer/Types';
import { IBufferLine } from '../../../common/Types';
import { CellData } from '../../../common/buffer/CellData';
import { MockCoreService, MockDecorationService, MockOptionsService, createCellData, NULL_CELL_DATA } from '../../../common/TestUtils.test';
import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from '../../TestUtils.test';
@@ -3,8 +3,7 @@
* @license MIT
*/
import { IColor } from '../../../common/Types';
import { IBufferLine, ICellData } from '../../../common/buffer/Types';
import { IBufferLine, ICellData, IColor } from '../../../common/Types';
import { INVERTED_DEFAULT_COLOR } from '../shared/Constants';
import { WHITESPACE_CELL_CHAR, Attributes } from '../../../common/buffer/Constants';
import { CellData } from '../../../common/buffer/CellData';
@@ -380,7 +379,7 @@ export class DomRendererRowFactory {
if (!isTop && isInSelection) {
// If in the selection, force the element to be above the selection to improve contrast and
// support opaque selections. The applies background is not actually needed here as
// selection is drawn in a separate container; the main purpose of this is to ensure minimum
// selection is drawn in a seperate container, the main purpose of this to ensuring minimum
// contrast ratio
bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque;
bg = bgOverride.rgba >> 8 & 0xFFFFFF;
+1 -1
View File
@@ -217,7 +217,7 @@ export class StandardWheelEvent {
let shouldFactorDPR: boolean = false;
if (platform.isChrome) {
const chromeVersionMatch = navigator.userAgent.match(/Chrome\/(\d+)/);
const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1], 10) : 123;
const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1]) : 123;
shouldFactorDPR = chromeMajorVersion <= 122;
}
+1 -1
View File
@@ -88,7 +88,7 @@ export class SelectionModel {
return [startPlusLength, this.selectionStart[1]];
}
// Ensure the word/line is selected after a double/triple click
// Ensure the the word/line is selected after a double/triple click
if (this.selectionStartLength) {
// Select the larger of the two when start and end are on the same line
if (this.selectionEnd[1] === this.selectionStart[1]) {
@@ -8,7 +8,7 @@ import { ICharacterJoinerService } from './Services';
import { CharacterJoinerService } from './CharacterJoinerService';
import { BufferLine } from '../../common/buffer/BufferLine';
import { BufferLineStringCache } from '../../common/buffer/BufferLineStringCache';
import { IBufferLine } from '../../common/buffer/Types';
import { IBufferLine } from '../../common/Types';
import { CellData } from '../../common/buffer/CellData';
import { MockBufferService, createCellData } from '../../common/TestUtils.test';
@@ -3,7 +3,7 @@
* @license MIT
*/
import { CharData, IBufferLine, ICellData } from '../../common/buffer/Types';
import { IBufferLine, ICellData, CharData } from '../../common/Types';
import { ICharacterJoiner } from '../Types';
import { AttributeData } from '../../common/buffer/AttributeData';
import { WHITESPACE_CELL_CHAR, Content } from '../../common/buffer/Constants';
+1 -1
View File
@@ -176,7 +176,7 @@ describe('MouseService _triggerMouseEvent', () => {
for (let i = 0; i < bufferService.cols; ++i) {
assert.equal(trigger({ col: i, row: 0, x: 0, y: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true);
if (i > 222) {
// suppress mouse reports if we are out of addressable range (max. 222)
// supress mouse reports if we are out of addressible range (max. 222)
assert.deepEqual(toBytes(reports.pop()), []);
} else {
assert.deepEqual(toBytes(reports.pop()), [0x1b, 0x5b, 0x4d, 0x20, i + 33, 0x21]);
@@ -6,13 +6,14 @@
import { assert } from 'chai';
import { SelectionService, SelectionMode } from './SelectionService';
import { SelectionModel } from '../selection/SelectionModel';
import { IBuffer, IBufferLine } from '../../common/buffer/Types';
import { IBufferLine } from '../../common/Types';
import { MockBufferService, MockOptionsService, MockCoreService, MockMouseStateService, createCellData } from '../../common/TestUtils.test';
import { BufferLine } from '../../common/buffer/BufferLine';
import { BufferLineStringCache } from '../../common/buffer/BufferLineStringCache';
import { IBufferService, IOptionsService } from '../../common/services/Services';
import { MockCoreBrowserService, MockMouseService, MockRenderService } from '../TestUtils.test';
import { CellData } from '../../common/buffer/CellData';
import { IBuffer } from '../../common/buffer/Types';
import { IRenderService } from './Services';
const TEST_STRING_CACHE = new BufferLineStringCache();
+4 -4
View File
@@ -11,10 +11,10 @@ import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from
import { ICoreBrowserService, IMouseCoordsService, IRenderService, ISelectionService } from './Services';
import { Disposable, MutableDisposable, toDisposable } from '../../common/Lifecycle';
import * as Browser from '../../common/Platform';
import { IDisposable } from '../../common/Types';
import { IBuffer, IBufferLine, ICellData } from '../../common/buffer/Types';
import { IBufferLine, ICellData, IDisposable } from '../../common/Types';
import { getRangeLength } from '../../common/buffer/BufferRange';
import { CellData } from '../../common/buffer/CellData';
import { IBuffer } from '../../common/buffer/Types';
import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from '../../common/services/Services';
import { Emitter } from '../../common/Event';
@@ -927,7 +927,7 @@ export class SelectionService extends Disposable implements ISelectionService {
// Incremenet the end index so it is at the start of the next character
endIndex++;
// Calculate the start _column_, converting the string indexes back to
// Calculate the start _column_, converting the the string indexes back to
// column coordinates.
let start =
startIndex // The index of the selection's start char in the line string
@@ -935,7 +935,7 @@ export class SelectionService extends Disposable implements ISelectionService {
- leftWideCharCount // The number of wide chars left of the initial char
+ leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)
// Calculate the length in _columns_, converting the string indexes back
// Calculate the length in _columns_, converting the the string indexes back
// to column coordinates.
let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols
endIndex // The index of the selection's end char in the line string
+1 -1
View File
@@ -132,7 +132,7 @@ export class ThemeService extends Disposable implements IThemeService {
colors.ansi[i + 16] = parseColor(theme.extendedAnsi[i], DEFAULT_ANSI_COLORS[i + 16]);
}
}
// Clear the cache
// Clear our the cache
this._contrastCache.clear();
this._halfContrastCache.clear();
this._updateRestoreColors();
+2 -23
View File
@@ -3,8 +3,9 @@
* @license MIT
*/
import { ICircularList } from './Types';
import { Disposable } from './Lifecycle';
import { Emitter, type IEvent } from './Event';
import { Emitter } from './Event';
export interface IInsertEvent {
index: number;
@@ -16,28 +17,6 @@ export interface IDeleteEvent {
amount: number;
}
export interface ICircularList<T> {
length: number;
maxLength: number;
isFull: boolean;
onDeleteEmitter: Emitter<IDeleteEvent>;
onDelete: IEvent<IDeleteEvent>;
onInsertEmitter: Emitter<IInsertEvent>;
onInsert: IEvent<IInsertEvent>;
onTrimEmitter: Emitter<number>;
onTrim: IEvent<number>;
get(index: number): T | undefined;
set(index: number, value: T): void;
push(value: T): void;
recycle(): T;
pop(): T | undefined;
splice(start: number, deleteCount: number, ...items: T[]): void;
trimStart(count: number): void;
shiftElements(start: number, count: number, offset: number): void;
}
/**
* Represents a circular list; a list with a maximum size that wraps around when push is called,
* overriding values at the start of the list.
+4 -4
View File
@@ -177,9 +177,9 @@ export namespace css {
// Formats: rgb() or rgba()
const rgbaMatch = css.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);
if (rgbaMatch) {
$r = parseInt(rgbaMatch[1], 10);
$g = parseInt(rgbaMatch[2], 10);
$b = parseInt(rgbaMatch[3], 10);
$r = parseInt(rgbaMatch[1]);
$g = parseInt(rgbaMatch[2]);
$b = parseInt(rgbaMatch[3]);
$a = Math.round((rgbaMatch[5] === undefined ? 1 : parseFloat(rgbaMatch[5])) * 0xFF);
return channels.toColor($r, $g, $b, $a);
}
@@ -373,7 +373,7 @@ export function toPaddedHex(c: number): string {
/**
* Gets the contrast ratio between two relative luminance values.
* @param l1 The first relative luminance.
* @param l2 The second relative luminance.
* @param l2 The first relative luminance.
* @see https://www.w3.org/TR/WCAG20/#contrast-ratiodef
*/
export function contrastRatio(l1: number, l2: number): number {
+4 -20
View File
@@ -21,20 +21,19 @@
* http://linux.die.net/man/7/urxvt
*/
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, IOscLinkService } from './services/Services';
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, IMouseStateService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from './services/Services';
import { InstantiationService } from './services/InstantiationService';
import { LogService } from './services/LogService';
import { BufferService, BufferServiceConstants } from './services/BufferService';
import { OptionsService } from './services/OptionsService';
import { IDisposable, IScrollEvent, ITerminalOptions, IParams } from './Types';
import { IAttributeData, IBufferSet } from './buffer/Types';
import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from './Types';
import { CoreService } from './services/CoreService';
import { MouseStateService } from './services/MouseStateService';
import { UnicodeV6 } from './input/UnicodeV6';
import { UnicodeService } from './services/UnicodeService';
import { CharsetService } from './services/CharsetService';
import { updateWindowsModeWrappedState } from './WindowsMode';
import { IFunctionIdentifier } from './parser/Types';
import { IFunctionIdentifier, IParams } from './parser/Types';
import { IBufferSet } from './buffer/Types';
import { InputHandler } from './InputHandler';
import { WriteBuffer } from './input/WriteBuffer';
import { OscLinkService } from './services/OscLinkService';
@@ -44,20 +43,6 @@ import { Disposable, MutableDisposable, toDisposable } from './Lifecycle';
// Only trigger this warning a single time per session
let hasWriteSyncWarnHappened = false;
export interface ICoreTerminal {
mouseStateService: IMouseStateService;
coreService: ICoreService;
optionsService: IOptionsService;
unicodeService: IUnicodeService;
buffers: IBufferSet;
options: Required<ITerminalOptions>;
registerCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean | Promise<boolean>): IDisposable;
registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean | Promise<boolean>): IDisposable;
registerEscHandler(id: IFunctionIdentifier, callback: () => boolean | Promise<boolean>): IDisposable;
registerOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable;
registerApcHandler(id: IFunctionIdentifier, callback: (data: string) => boolean | Promise<boolean>): IDisposable;
}
export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
protected readonly _instantiationService: IInstantiationService;
protected readonly _bufferService: IBufferService;
@@ -131,7 +116,6 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this.mouseStateService = this._register(this._instantiationService.createInstance(MouseStateService));
this._instantiationService.setService(IMouseStateService, this.mouseStateService);
this.unicodeService = this._register(this._instantiationService.createInstance(UnicodeService));
this.unicodeService.register(new UnicodeV6());
this._instantiationService.setService(IUnicodeService, this.unicodeService);
this._charsetService = this._instantiationService.createInstance(CharsetService);
this._instantiationService.setService(ICharsetService, this._charsetService);
+2 -3
View File
@@ -5,8 +5,7 @@
import { assert } from 'chai';
import { InputHandler } from './InputHandler';
import { IColorEvent, ColorRequestType, SpecialColorIndex } from './Types';
import { IAttributeData, IBufferLine } from './buffer/Types';
import { IBufferLine, IAttributeData, IColorEvent, ColorRequestType, SpecialColorIndex } from './Types';
import { DEFAULT_ATTR_DATA } from './buffer/BufferLine';
import { CellData } from './buffer/CellData';
import { Attributes, BgFlags, UnderlineStyle } from './buffer/Constants';
@@ -1241,7 +1240,7 @@ describe('InputHandler', () => {
await inputHandler.parseP('\x1b[e');
assert.deepEqual(getCursor(bufferService), [8, 5]);
});
describe('should clamp cursor into addressable range', () => {
describe('should clamp cursor into addressible range', () => {
it('CUF', async () => {
bufferService.buffer.x = 10000;
bufferService.buffer.y = 10000;
+6 -6
View File
@@ -4,8 +4,7 @@
* @license MIT
*/
import { IInputHandler, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from './Types';
import { IAttributeData, IBuffer } from './buffer/Types';
import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from './Types';
import { C0, C1 } from './data/EscapeSequences';
import { CHARSETS, DEFAULT_CHARSET } from './data/Charsets';
import { EscapeSequenceParser } from './parser/EscapeSequenceParser';
@@ -21,6 +20,7 @@ import { UnicodeService } from './services/UnicodeService';
import { OscHandler } from './parser/OscParser';
import { DcsHandler } from './parser/DcsParser';
import { ApcHandler } from './parser/ApcParser';
import { IBuffer } from './buffer/Types';
import { parseColor } from './input/XParseColor';
import { Emitter } from './Event';
import { XTERM_VERSION } from './Version';
@@ -273,7 +273,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.setExecuteHandler(C0.HT, () => this.tab());
this._parser.setExecuteHandler(C0.SO, () => this.shiftOut());
this._parser.setExecuteHandler(C0.SI, () => this.shiftIn());
// FIXME: What to do with missing? Old code just added those to print.
// FIXME: What do to with missing? Old code just added those to print.
this._parser.setExecuteHandler(C1.IND, () => this.index());
this._parser.setExecuteHandler(C1.NEL, () => this.nextLine());
@@ -550,7 +550,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// get charset replacement character
// charset is only defined for ASCII, therefore we only
// search for a replacement char if code < 127
// search for an replacement char if code < 127
if (code < 127 && charset) {
const ch = charset[String.fromCharCode(code)];
if (ch) {
@@ -3065,7 +3065,7 @@ export class InputHandler extends Disposable implements IInputHandler {
const idx = slots.shift() as string;
const spec = slots.shift() as string;
if (/^\d+$/.exec(idx)) {
const index = parseInt(idx, 10);
const index = parseInt(idx);
if (isValidColorIndex(index)) {
if (spec === '?') {
event.push({ type: ColorRequestType.REPORT, index });
@@ -3228,7 +3228,7 @@ export class InputHandler extends Disposable implements IInputHandler {
const slots = data.split(';');
for (let i = 0; i < slots.length; ++i) {
if (/^\d+$/.exec(slots[i])) {
const index = parseInt(slots[i], 10);
const index = parseInt(slots[i]);
if (isValidColorIndex(index)) {
event.push({ type: ColorRequestType.RESTORE, index });
}
+2 -2
View File
@@ -41,10 +41,10 @@ export function getSafariVersion(): number {
if (majorVersion === null || majorVersion.length < 2) {
return 0;
}
return parseInt(majorVersion[1], 10);
return parseInt(majorVersion[1]);
}
// Find the user's platform. We use this to interpret the meta key
// Find the users platform. We use this to interpret the meta key
// and ISO third level shifts.
// http://stackoverflow.com/q/19877924/577598
export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform);
+1 -1
View File
@@ -8,7 +8,7 @@ import type { ILogService } from './services/Services';
interface ITaskQueue {
/**
* Adds a task to the queue which will run in a future idle callback.
* To avoid perceivable stalls on the main thread, tasks with heavy workload
* To avoid perceivable stalls on the mainthread, tasks with heavy workload
* should split their work into smaller pieces and return `true` to get
* called again until the work is done (on falsy return value).
*/
+2 -2
View File
@@ -6,9 +6,9 @@
import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IMouseStateService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService, type IBufferResizeEvent } from './services/Services';
import { UnicodeService } from './services/UnicodeService';
import { DEFAULT_OPTIONS } from './services/OptionsService';
import { IAttributeData, IBuffer, IBufferLine, IBufferSet, IExtendedAttrs } from './buffer/Types';
import { IBufferSet, IBuffer } from './buffer/Types';
import { BufferSet } from './buffer/BufferSet';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IOscLinkData, IDisposable } from './Types';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IAttributeData, IOscLinkData, IDisposable, IBufferLine, IExtendedAttrs } from './Types';
import { UnicodeV6 } from './input/UnicodeV6';
import { IDecorationOptions, IDecoration } from '@xterm/xterm';
import { Emitter, type IEvent } from './Event';
+185 -27
View File
@@ -3,32 +3,26 @@
* @license MIT
*/
import { IDeleteEvent, IInsertEvent } from './CircularList';
import { UnderlineStyle } from './buffer/Constants';
import { IBufferSet } from './buffer/Types';
import { IParams } from './parser/Types';
import { IMouseStateService, ICoreService, IOptionsService, IUnicodeService } from './services/Services';
import { IFunctionIdentifier, ITerminalOptions as IPublicTerminalOptions } from '@xterm/xterm';
import type { IEvent } from './Event';
import type { Emitter, IEvent } from './Event';
/** sequence params serialized to js arrays */
export type ParamsArray = (number | number[])[];
/** Interface of Params storage class. */
export interface IParams {
/** from ctor */
maxLength: number;
maxSubParamsLength: number;
/** param values and its length */
params: Int32Array;
length: number;
/** methods */
clone(): IParams;
toArray(): ParamsArray;
reset(): void;
resetZdm(): void;
addParam(value: number): void;
addSubParam(value: number): void;
hasSubParams(idx: number): boolean;
getSubParams(idx: number): Int32Array | null;
getSubParamsAll(): {[idx: number]: Int32Array};
export interface ICoreTerminal {
mouseStateService: IMouseStateService;
coreService: ICoreService;
optionsService: IOptionsService;
unicodeService: IUnicodeService;
buffers: IBufferSet;
options: Required<ITerminalOptions>;
registerCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean | Promise<boolean>): IDisposable;
registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean | Promise<boolean>): IDisposable;
registerEscHandler(id: IFunctionIdentifier, callback: () => boolean | Promise<boolean>): IDisposable;
registerOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable;
registerApcHandler(id: IFunctionIdentifier, callback: (data: string) => boolean | Promise<boolean>): IDisposable;
}
export interface IDisposable {
@@ -68,6 +62,28 @@ export interface IScrollEvent {
position: number;
}
export interface ICircularList<T> {
length: number;
maxLength: number;
isFull: boolean;
onDeleteEmitter: Emitter<IDeleteEvent>;
onDelete: IEvent<IDeleteEvent>;
onInsertEmitter: Emitter<IInsertEvent>;
onInsert: IEvent<IInsertEvent>;
onTrimEmitter: Emitter<number>;
onTrim: IEvent<number>;
get(index: number): T | undefined;
set(index: number, value: T): void;
push(value: T): void;
recycle(): T;
pop(): T | undefined;
splice(start: number, deleteCount: number, ...items: T[]): void;
trimStart(count: number): void;
shiftElements(start: number, count: number, offset: number): void;
}
export const enum KeyboardResultType {
SEND_KEY,
SELECT_ALL,
@@ -85,12 +101,24 @@ export interface ICharset {
[key: string]: string | undefined;
}
export type CharData = [attr: number, char: string, width: number, code: number];
export interface IColor {
readonly css: string;
readonly rgba: number; // 32-bit int with rgba in each byte
}
export type IColorRGB = [red: number, green: number, blue: number];
export interface IExtendedAttrs {
ext: number;
underlineStyle: UnderlineStyle;
underlineColor: number;
underlineVariantOffset: number;
urlId: number;
clone(): IExtendedAttrs;
isEmpty(): boolean;
}
/**
* Tracks the current hyperlink. Since these are treated as extended attirbutes, these get passed on
* to the linkifier when anything is printed. Doing it this way ensures that even when the cursor
@@ -102,6 +130,136 @@ export interface IOscLinkData {
uri: string;
}
/**
* An object that represents all attributes of a cell.
*/
export interface IAttributeData {
/**
* "fg" is a 32-bit unsigned integer that stores the foreground color of the cell in the 24 least
* significant bits and additional flags in the remaining 8 bits.
*/
fg: number;
/**
* "bg" is a 32-bit unsigned integer that stores the background color of the cell in the 24 least
* significant bits and additional flags in the remaining 8 bits.
*/
bg: number;
/**
* "extended", aka "ext", stores extended attributes beyond those available in fg and bg. This
* data is optional on a cell and encodes less common data.
*/
extended: IExtendedAttrs;
clone(): IAttributeData;
// flags
isInverse(): number;
isBold(): number;
isUnderline(): number;
isBlink(): number;
isInvisible(): number;
isItalic(): number;
isDim(): number;
isStrikethrough(): number;
isProtected(): number;
isOverline(): number;
/**
* The color mode of the foreground color which determines how to decode {@link getFgColor},
* possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16},
* {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}.
*/
getFgColorMode(): number;
/**
* The color mode of the background color which determines how to decode {@link getBgColor},
* possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16},
* {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}.
*/
getBgColorMode(): number;
isFgRGB(): boolean;
isBgRGB(): boolean;
isFgPalette(): boolean;
isBgPalette(): boolean;
isFgDefault(): boolean;
isBgDefault(): boolean;
isAttributeDefault(): boolean;
/**
* Gets an integer representation of the foreground color, how to decode the color depends on the
* color mode {@link getFgColorMode}.
*/
getFgColor(): number;
/**
* Gets an integer representation of the background color, how to decode the color depends on the
* color mode {@link getBgColorMode}.
*/
getBgColor(): number;
// extended attrs
hasExtendedAttrs(): number;
updateExtended(): void;
getUnderlineColor(): number;
getUnderlineColorMode(): number;
isUnderlineColorRGB(): boolean;
isUnderlineColorPalette(): boolean;
isUnderlineColorDefault(): boolean;
getUnderlineStyle(): number;
getUnderlineVariantOffset(): number;
}
/** Cell data */
export interface ICellData extends IAttributeData {
content: number;
combinedData: string;
isCombined(): number;
getWidth(): number;
getChars(): string;
getCode(): number;
setFromCharData(value: CharData): void;
getAsCharData(): CharData;
}
/**
* Interface for a line in the terminal buffer.
*/
export interface IBufferLine {
length: number;
isWrapped: boolean;
get(index: number): CharData;
set(index: number, value: CharData): void;
loadCell(index: number, cell: ICellData): ICellData;
setCell(index: number, cell: ICellData): void;
setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void;
addCodepointToCell(index: number, codePoint: number, width: number): void;
insertCells(pos: number, n: number, ch: ICellData): void;
deleteCells(pos: number, n: number, fill: ICellData): void;
replaceCells(start: number, end: number, fill: ICellData, respectProtect?: boolean): void;
resize(cols: number, fill: ICellData): boolean;
cleanupMemory(): number;
fill(fillCellData: ICellData, respectProtect?: boolean): void;
copyFrom(line: IBufferLine): void;
clone(): IBufferLine;
getTrimmedLength(): number;
getNoBgTrimmedLength(): number;
translateToString(trimRight?: boolean, startCol?: number, endCol?: number, outColumns?: number[]): string;
/* direct access to cell attrs */
getWidth(index: number): number;
hasWidth(index: number): number;
getFg(index: number): number;
getBg(index: number): number;
hasContent(index: number): number;
getCodePoint(index: number): number;
isCombined(index: number): number;
getString(index: number): string;
}
export interface IMarker extends IDisposable {
readonly id: number;
readonly isDisposed: boolean;
readonly line: number;
onDispose: IEvent<void>;
}
export interface IModes {
insertMode: boolean;
}
@@ -181,7 +339,7 @@ export interface ICoreMouseEvent {
x: number;
y: number;
/**
* Button the action occurred. Due to restrictions of the tracking protocols
* Button the action occured. Due to restrictions of the tracking protocols
* it is not possible to report multiple buttons at once.
* Wheel is treated as a button.
* There are invalid combinations of buttons and actions possible
@@ -234,9 +392,9 @@ export interface ICoreMouseProtocol {
/**
* CoreMouseEncoding
* The tracking encoding can be registered and activated at the MouseStateService.
* If a ICoreMouseEvent passes all protocol restrictions it will be encoded
* If a ICoreMouseEvent passes all procotol restrictions it will be encoded
* with the active encoding and sent out.
* Note: Returning an empty string will suppress sending a mouse report,
* Note: Returning an empty string will supress sending a mouse report,
* which can be used to skip creating falsey reports in limited encodings
* (DEFAULT only supports up to 223 1-based as coord value).
*/
+2 -3
View File
@@ -3,8 +3,7 @@
* @license MIT
*/
import { IColorRGB } from '../Types';
import { IAttributeData, IExtendedAttrs } from './Types';
import { IAttributeData, IColorRGB, IExtendedAttrs } from '../Types';
import { Attributes, FgFlags, BgFlags, UnderlineStyle, ExtFlags } from './Constants';
export class AttributeData implements IAttributeData {
@@ -204,7 +203,7 @@ export class ExtendedAttrs implements IExtendedAttrs {
/**
* Convenient method to indicate whether the object holds no additional information,
* that needs to be persistent in the buffer.
* that needs to be persistant in the buffer.
*/
public isEmpty(): boolean {
return this.underlineStyle === UnderlineStyle.NONE && this._urlId === 0;
+5 -5
View File
@@ -6,8 +6,7 @@
import { CircularList, IInsertEvent } from '../CircularList';
import { Disposable, toDisposable } from '../Lifecycle';
import { IdleTaskQueue } from '../TaskQueue';
import { ICharset } from '../Types';
import { IAttributeData, IBuffer, IBufferLine, ICellData } from './Types';
import { IAttributeData, IBufferLine, ICellData, ICharset } from '../Types';
import { ExtendedAttrs } from './AttributeData';
import { BufferLine, DEFAULT_ATTR_DATA } from './BufferLine';
import { BufferLineStringCache } from './BufferLineStringCache';
@@ -15,6 +14,7 @@ import { getWrappedLineTrimmedLength, reflowLargerApplyNewLayout, reflowLargerCr
import { CellData } from './CellData';
import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE, WHITESPACE_CELL_WIDTH } from './Constants';
import { Marker } from './Marker';
import { IBuffer } from './Types';
import { DEFAULT_CHARSET } from '../data/Charsets';
import { IBufferService, ILogService, IOptionsService } from '../services/Services';
@@ -142,7 +142,7 @@ export class Buffer extends Disposable implements IBuffer {
}
/**
* Clears the buffer to its initial state, discarding all previous data.
* Clears the buffer to it's initial state, discarding all previous data.
*/
public clear(): void {
this._stringCache.clear();
@@ -197,7 +197,7 @@ export class Buffer extends Disposable implements IBuffer {
for (let y = this._rows; y < newRows; y++) {
if (this.lines.length < newRows + this.ybase) {
if (this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) {
// Just add the new missing rows on Windows as conpty reprints the screen with its
// Just add the new missing rows on Windows as conpty reprints the screen with it's
// view of the world. Once a line enters scrollback for conpty it remains there
this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false));
} else {
@@ -578,7 +578,7 @@ export class Buffer extends Disposable implements IBuffer {
* @param i The index to start setting up tab stops from.
*/
public setupTabStops(i?: number): void {
if (i !== undefined) {
if (i !== null && i !== undefined) {
if (!this.tabs[i]) {
i = this.prevStop(i);
}
+1 -1
View File
@@ -6,7 +6,7 @@ import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content,
import { BufferLine } from './BufferLine';
import { BufferLineStringCache } from './BufferLineStringCache';
import { CellData } from './CellData';
import { CharData, IBufferLine, ICellData } from './Types';
import { CharData, IBufferLine, ICellData } from '../Types';
import { assert } from 'chai';
import { AttributeData } from './AttributeData';
import { createCellData, NULL_CELL_DATA, extendedAttributes } from '../TestUtils.test';
+1 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from './Types';
import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from '../Types';
import { AttributeData } from './AttributeData';
import { CellData } from './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 './Constants';
+3 -3
View File
@@ -5,7 +5,7 @@
import { BufferLine } from './BufferLine';
import { CircularList } from '../CircularList';
import { IBufferLine, ICellData } from './Types';
import { IBufferLine, ICellData } from '../Types';
export interface INewLayoutResult {
layout: number[];
@@ -13,7 +13,7 @@ export interface INewLayoutResult {
}
/**
* Evaluates and returns indexes to be removed after reflowLarger occurs. Lines will be removed
* Evaluates and returns indexes to be removed after a reflow larger occurs. Lines will be removed
* when a wrapped line unwraps.
* @param lines The buffer lines.
* @param oldCols The columns before resize
@@ -109,7 +109,7 @@ export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, o
}
/**
* Creates and returns the new layout for lines given an array of indexes to be removed.
* Creates and return the new layout for lines given an array of indexes to be removed.
* @param lines The buffer lines.
* @param toRemove The indexes to remove.
*/
+2 -1
View File
@@ -4,8 +4,9 @@
*/
import { Disposable, MutableDisposable } from '../Lifecycle';
import { IAttributeData } from '../Types';
import { Buffer } from './Buffer';
import { IAttributeData, IBuffer, IBufferSet } from './Types';
import { IBuffer, IBufferSet } from './Types';
import { IBufferService, ILogService, IOptionsService } from '../services/Services';
import { Emitter } from '../Event';
+1 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { CharData, ICellData, IExtendedAttrs } from './Types';
import { CharData, ICellData, IExtendedAttrs } from '../Types';
import { stringFromCodePoint } from '../input/TextDecoder';
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, Content } from './Constants';
import { AttributeData, ExtendedAttrs } from './AttributeData';
+13 -13
View File
@@ -24,7 +24,7 @@ export const NULL_CELL_CODE = 0;
/**
* Whitespace cell.
* This is meant as a replacement for empty cells when needed
* during rendering lines to preserve correct alignment.
* during rendering lines to preserve correct aligment.
*/
export const WHITESPACE_CELL_CHAR = ' ';
export const WHITESPACE_CELL_WIDTH = 1;
@@ -36,18 +36,18 @@ export const WHITESPACE_CELL_CODE = 32;
export const enum Content {
/**
* bit 1..21 codepoint, max allowed in UTF32 is 0x10FFFF (21 bits taken)
* read: `codepoint = content & Content.CODEPOINT_MASK;`
* write: `content |= codepoint & Content.CODEPOINT_MASK;`
* read: `codepoint = content & Content.codepointMask;`
* write: `content |= codepoint & Content.codepointMask;`
* shortcut if precondition `codepoint <= 0x10FFFF` is met:
* `content |= codepoint;`
*/
CODEPOINT_MASK = 0x1FFFFF,
/**
* bit 22 flag indicating whether a cell contains combined content
* read: `isCombined = content & Content.IS_COMBINED_MASK;`
* set: `content |= Content.IS_COMBINED_MASK;`
* clear: `content &= ~Content.IS_COMBINED_MASK;`
* bit 22 flag indication whether a cell contains combined content
* read: `isCombined = content & Content.isCombined;`
* set: `content |= Content.isCombined;`
* clear: `content &= ~Content.isCombined;`
*/
IS_COMBINED_MASK = 0x200000, // 1 << 21
@@ -55,19 +55,19 @@ export const enum Content {
* bit 1..22 mask to check whether a cell contains any string data
* we need to check for codepoint and isCombined bits to see
* whether a cell contains anything
* read: `isEmpty = !(content & Content.HAS_CONTENT_MASK)`
* read: `isEmpty = !(content & Content.hasContent)`
*/
HAS_CONTENT_MASK = 0x3FFFFF,
/**
* bit 23..24 wcwidth value of cell, takes 2 bits (ranges from 0..2)
* read: `width = (content & Content.WIDTH_MASK) >> Content.WIDTH_SHIFT;`
* `hasWidth = content & Content.WIDTH_MASK;`
* read: `width = (content & Content.widthMask) >> Content.widthShift;`
* `hasWidth = content & Content.widthMask;`
* as long as wcwidth is highest value in `content`:
* `width = content >> Content.WIDTH_SHIFT;`
* write: `content |= (width << Content.WIDTH_SHIFT) & Content.WIDTH_MASK;`
* `width = content >> Content.widthShift;`
* write: `content |= (width << Content.widthShift) & Content.widthMask;`
* shortcut if precondition `0 <= width <= 3` is met:
* `content |= width << Content.WIDTH_SHIFT;`
* `content |= width << Content.widthShift;`
*/
WIDTH_MASK = 0xC00000, // 3 << 22
WIDTH_SHIFT = 22
+3 -3
View File
@@ -3,9 +3,9 @@
* @license MIT
*/
import { dispose, IDisposable } from '../Lifecycle';
import { IMarker } from './Types';
import { IDisposable, IMarker } from '../Types';
import { Emitter } from '../Event';
import { dispose } from '../Lifecycle';
export class Marker implements IMarker {
private static _nextId = 1;
@@ -30,7 +30,7 @@ export class Marker implements IMarker {
}
this.isDisposed = true;
this.line = -1;
// Emit before super.dispose such that dispose listeners get a chance to react
// Emit before super.dispose such that dispose listeners get a change to react
this._onDispose.fire();
dispose(this._disposables);
this._disposables.length = 0;
+1 -147
View File
@@ -3,158 +3,12 @@
* @license MIT
*/
import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker, ICharset, IDisposable } from '../Types';
import type { IEvent } from '../Event';
import type { ICircularList } from '../CircularList';
import type { ICharset } from '../Types';
import type { IDisposable } from '../Lifecycle';
import type { UnderlineStyle } from './Constants';
// BufferIndex denotes a position in the buffer: [rowIndex, colIndex]
export type BufferIndex = [number, number];
export type CharData = [attr: number, char: string, width: number, code: number];
export interface IExtendedAttrs {
ext: number;
underlineStyle: UnderlineStyle;
underlineColor: number;
underlineVariantOffset: number;
urlId: number;
clone(): IExtendedAttrs;
isEmpty(): boolean;
}
/**
* An object that represents all attributes of a cell.
*/
export interface IAttributeData {
/**
* "fg" is a 32-bit unsigned integer that stores the foreground color of the cell in the 24 least
* significant bits and additional flags in the remaining 8 bits.
*/
fg: number;
/**
* "bg" is a 32-bit unsigned integer that stores the background color of the cell in the 24 least
* significant bits and additional flags in the remaining 8 bits.
*/
bg: number;
/**
* "extended", aka "ext", stores extended attributes beyond those available in fg and bg. This
* data is optional on a cell and encodes less common data.
*/
extended: IExtendedAttrs;
clone(): IAttributeData;
// flags
isInverse(): number;
isBold(): number;
isUnderline(): number;
isBlink(): number;
isInvisible(): number;
isItalic(): number;
isDim(): number;
isStrikethrough(): number;
isProtected(): number;
isOverline(): number;
/**
* The color mode of the foreground color which determines how to decode {@link getFgColor},
* possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16},
* {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}.
*/
getFgColorMode(): number;
/**
* The color mode of the background color which determines how to decode {@link getBgColor},
* possible values include {@link Attributes.CM_DEFAULT}, {@link Attributes.CM_P16},
* {@link Attributes.CM_P256} and {@link Attributes.CM_RGB}.
*/
getBgColorMode(): number;
isFgRGB(): boolean;
isBgRGB(): boolean;
isFgPalette(): boolean;
isBgPalette(): boolean;
isFgDefault(): boolean;
isBgDefault(): boolean;
isAttributeDefault(): boolean;
/**
* Gets an integer representation of the foreground color, how to decode the color depends on the
* color mode {@link getFgColorMode}.
*/
getFgColor(): number;
/**
* Gets an integer representation of the background color, how to decode the color depends on the
* color mode {@link getBgColorMode}.
*/
getBgColor(): number;
// extended attrs
hasExtendedAttrs(): number;
updateExtended(): void;
getUnderlineColor(): number;
getUnderlineColorMode(): number;
isUnderlineColorRGB(): boolean;
isUnderlineColorPalette(): boolean;
isUnderlineColorDefault(): boolean;
getUnderlineStyle(): number;
getUnderlineVariantOffset(): number;
}
/** Cell data */
export interface ICellData extends IAttributeData {
content: number;
combinedData: string;
isCombined(): number;
getWidth(): number;
getChars(): string;
getCode(): number;
setFromCharData(value: CharData): void;
getAsCharData(): CharData;
}
/**
* Interface for a line in the terminal buffer.
*/
export interface IBufferLine {
length: number;
isWrapped: boolean;
get(index: number): CharData;
set(index: number, value: CharData): void;
loadCell(index: number, cell: ICellData): ICellData;
setCell(index: number, cell: ICellData): void;
setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void;
addCodepointToCell(index: number, codePoint: number, width: number): void;
insertCells(pos: number, n: number, ch: ICellData): void;
deleteCells(pos: number, n: number, fill: ICellData): void;
replaceCells(start: number, end: number, fill: ICellData, respectProtect?: boolean): void;
resize(cols: number, fill: ICellData): boolean;
cleanupMemory(): number;
fill(fillCellData: ICellData, respectProtect?: boolean): void;
copyFrom(line: IBufferLine): void;
clone(): IBufferLine;
getTrimmedLength(): number;
getNoBgTrimmedLength(): number;
translateToString(trimRight?: boolean, startCol?: number, endCol?: number, outColumns?: number[]): string;
/* direct access to cell attrs */
getWidth(index: number): number;
hasWidth(index: number): number;
getFg(index: number): number;
getBg(index: number): number;
hasContent(index: number): number;
getCodePoint(index: number): number;
isCombined(index: number): number;
getString(index: number): string;
}
export interface IMarker extends IDisposable {
readonly id: number;
readonly isDisposed: boolean;
readonly line: number;
onDispose: IEvent<void>;
}
export interface IBuffer {
readonly lines: ICircularList<IBufferLine>;
ydisp: number;
+1 -1
View File
@@ -46,7 +46,7 @@ export function evaluateKeyboardEvent(
// Whether to cancel event propagation (NOTE: this may not be needed since the event is
// canceled at the end of keyDown
cancel: false,
// The new key event to emit
// The new key even to emit
key: undefined
};
const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0);
-1
View File
@@ -132,7 +132,6 @@ export class UnicodeV6 implements IUnicodeVersionProvider {
public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties {
let width = this.wcwidth(codepoint);
let shouldJoin = width === 0 && preceding !== 0;
// HACK: Ideally this file would not depend on the service which uses it
if (shouldJoin) {
const oldWidth = UnicodeService.extractWidth(preceding);
if (oldWidth === 0) {
+1 -1
View File
@@ -267,7 +267,7 @@ export class WriteBuffer extends Disposable {
* this condition here, also the renderer has no way to spot nonsense updates either.
* FIXME: A proper fix for this would track the FPS at the renderer entry level separately.
*
* If favoring of FPS shows bad throughput impact, use the following instead. It favors
* If favoring of FPS shows bad throughtput impact, use the following instead. It favors
* throughput by eval'ing `startTime` upfront pulling at least one more chunk into the
* current microtask queue (executed before setTimeout).
*/
+23 -23
View File
@@ -77,10 +77,10 @@ class TestEscapeSequenceParser extends EscapeSequenceParser {
public get realParams(): IParams {
return this._params;
}
public get collect(): string {
public get collectStr(): string {
return this.identToString(this._collect);
}
public set collect(value: string) {
public set collectStr(value: string) {
this._collect = 0;
for (let i = 0; i < value.length; ++i) {
this._collect <<= 8;
@@ -248,19 +248,19 @@ describe('EscapeSequenceParser', () => {
assert.equal(parser.currentState, ParserState.GROUND);
assert.equal(parser.osc, '');
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
});
it('reset states', () => {
parser.currentState = 124;
parser.osc = '#';
parser.params = [123];
parser.collect = '#';
parser.collectStr = '#';
parser.reset();
assert.equal(parser.currentState, ParserState.GROUND);
assert.equal(parser.osc, '');
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
});
});
describe('state transitions and actions', () => {
@@ -327,11 +327,11 @@ describe('EscapeSequenceParser', () => {
for (state in states) {
parser.currentState = state;
parser.params = [23];
parser.collect = '#';
parser.collectStr = '#';
parse(parser, '\x1b');
assert.equal(parser.currentState, ParserState.ESCAPE);
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
parser.reset();
}
});
@@ -383,7 +383,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.ESCAPE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -419,7 +419,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -442,21 +442,21 @@ describe('EscapeSequenceParser', () => {
// C0
parser.currentState = ParserState.ESCAPE;
parser.params = [123];
parser.collect = '#';
parser.collectStr = '#';
parse(parser, '[');
assert.equal(parser.currentState, ParserState.CSI_ENTRY);
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
parser.reset();
// C1
for (state in states) {
parser.currentState = state;
parser.params = [123];
parser.collect = '#';
parser.collectStr = '#';
parse(parser, '\x9b');
assert.equal(parser.currentState, ParserState.CSI_ENTRY);
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
parser.reset();
}
});
@@ -517,7 +517,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_PARAM);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -582,7 +582,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -593,7 +593,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_PARAM;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -619,7 +619,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_INTERMEDIATE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -848,7 +848,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_PARAM);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -930,7 +930,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -941,7 +941,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_PARAM;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -965,7 +965,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_INTERMEDIATE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -976,7 +976,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_INTERMEDIATE;
parse(parser, '\x20' + chars[i]);
assert.equal(parser.currentState, ParserState.DCS_IGNORE);
assert.equal(parser.collect, '\x20');
assert.equal(parser.collectStr, '\x20');
parser.reset();
}
});
@@ -1079,7 +1079,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.APC_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.APC_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
+25 -360
View File
@@ -11,6 +11,8 @@ import { Params } from './Params';
import { OscParser } from './OscParser';
import { DcsParser } from './DcsParser';
import { ApcParser } from './ApcParser';
import { parseWithWasmScanner } from './EscapeSequenceParserWasm';
import { WasmEscapeScanner } from './WasmEscapeScanner';
/**
* VT commands done by the parser
@@ -18,7 +20,7 @@ import { ApcParser } from './ApcParser';
// @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence."
// @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence."
// @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence."
// @vt: #Y ESC ST "String Terminator" "ESC \\" "Terminator used for string type sequences."
// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences."
// @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message."
// @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence."
// @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence."
@@ -243,7 +245,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
*
* This parser is currently hardcoded to operate in ZDM (Zero Default Mode)
* as suggested by the original parser, thus empty parameters are set to 0.
* This is not in line with the latest ECMA-48 specification
* This this is not in line with the latest ECMA-48 specification
* (ZDM was part of the early specs and got completely removed later on).
*
* Other than the original parser from vt100.net this parser supports
@@ -269,6 +271,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
protected _params: Params;
protected _collect: number;
public get collect(): number { return this._collect; }
public set collect(value: number) { this._collect = value; }
// handler lookup containers
protected _printHandler: PrintHandlerType;
protected _executeHandlers: { [flag: number]: ExecuteHandlerType };
@@ -297,6 +302,15 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
chunkPos: 0
};
protected _scanCache: {
opIndex: number;
stateBeforeScan?: number;
inputOffset?: number;
chunkStart?: number;
scan?: import('./ScanTypes').ScanResult;
data?: Uint32Array;
} = { opIndex: 0 };
constructor(
protected readonly _transitions: TransitionTable = VT500_TRANSITION_TABLE
) {
@@ -494,12 +508,20 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
*/
public reset(): void {
this.currentState = this.initialState;
WasmEscapeScanner.initSync();
WasmEscapeScanner.reset();
this._oscParser.reset();
this._dcsParser.reset();
this._apcParser.reset();
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
this._scanCache.opIndex = 0;
this._scanCache.inputOffset = 0;
this._scanCache.chunkStart = undefined;
this._scanCache.scan = undefined;
this._scanCache.data = undefined;
this._scanCache.stateBeforeScan = undefined;
// abort pending continuation from async handler
// Here the RESET type indicates, that the next parse call will
// ignore any saved stack, instead continues sync with next codepoint from GROUND
@@ -572,363 +594,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
* ```
*/
public parse(data: Uint32Array, length: number, promiseResult?: boolean): void | Promise<boolean> {
let code: number;
let transition: number;
let start = 0;
let handlerResult: void | boolean | Promise<boolean>;
// resume from async handler
if (this._parseStack.state) {
// allow sync parser reset even in continuation mode
// Note: can be used to recover parser from improper continuation error below
if (this._parseStack.state === ParserStackType.RESET) {
this._parseStack.state = ParserStackType.NONE;
start = this._parseStack.chunkPos + 1; // continue with next codepoint in GROUND
} else {
if (promiseResult === undefined || this._parseStack.state === ParserStackType.FAIL) {
/**
* Reject further parsing on improper continuation after pausing. This is a really bad
* condition with screwed up execution order and prolly messed up terminal state,
* therefore we exit hard with an exception and reject any further parsing.
*
* Note: With `Terminal.write` usage this exception should never occur, as the top level
* calls are guaranteed to handle async conditions properly. If you ever encounter this
* exception in your terminal integration it indicates, that you injected data chunks to
* `InputHandler.parse` or `EscapeSequenceParser.parse` synchronously without waiting for
* continuation of a running async handler.
*
* It is possible to get rid of this error by calling `reset`. But dont rely on that, as
* the pending async handler still might mess up the terminal later. Instead fix the
* faulty async handling, so this error will not be thrown anymore.
*/
this._parseStack.state = ParserStackType.FAIL;
throw new Error('improper continuation due to previous async handler, giving up parsing');
}
// we have to resume the old handler loop if:
// - return value of the promise was `false`
// - handlers are not exhausted yet
const handlers = this._parseStack.handlers;
let handlerPos = this._parseStack.handlerPos - 1;
switch (this._parseStack.state) {
case ParserStackType.CSI:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as CsiHandlerType[])[handlerPos](this._params);
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
this._parseStack.handlers = [];
break;
case ParserStackType.ESC:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as EscHandlerType[])[handlerPos]();
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
this._parseStack.handlers = [];
break;
case ParserStackType.DCS:
code = data[this._parseStack.chunkPos];
handlerResult = this._dcsParser.unhook(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) {
return handlerResult;
}
if (code === 0x1b) this._parseStack.transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
break;
case ParserStackType.OSC:
code = data[this._parseStack.chunkPos];
handlerResult = this._oscParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) {
return handlerResult;
}
if (code === 0x1b) this._parseStack.transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
break;
case ParserStackType.APC:
code = data[this._parseStack.chunkPos];
handlerResult = this._apcParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) {
return handlerResult;
}
if (code === 0x1b) this._parseStack.transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
break;
}
// cleanup before continuing with the main sync loop
this._parseStack.state = ParserStackType.NONE;
start = this._parseStack.chunkPos + 1;
this.precedingJoinState = 0;
this.currentState = this._parseStack.transition & TableAccess.TRANSITION_STATE_MASK;
}
}
// continue with main sync loop
// process input string
for (let i = start; i < length; ++i) {
code = data[i];
// EXE fast-path: common control bytes (0x00-0x17) in non-payload states
if (code < 0x18 && this.currentState <= ParserState.CSI_IGNORE) {
(this._executeHandlersArr[code] ?? this._executeHandlerFb)(code);
this.precedingJoinState = 0;
continue;
}
// CSI fast-path: collapse ESC [ into a single entry, parse params+final in a tight loop
if (code === 0x1b
&& this.currentState < ParserState.OSC_STRING
&& i + 2 < length && data[i + 1] === 0x5b
) {
this._params.resetZdm();
this._collect = 0;
let k = i + 2;
let ch = data[k];
if (ch >= 0x3c && ch <= 0x3f) {
this._collect = ch;
k++;
}
let csiDone = false;
for (; k < length; k++) {
ch = data[k];
if (ch >= 0x30 && ch <= 0x39) {
this._params.addDigit(ch - 48);
} else if (ch === 0x3b) {
this._params.addParam(0);
} else if (ch === 0x3a) {
this._params.addSubParam(-1);
} else if (ch >= 0x40 && ch <= 0x7e) {
const handlers = this._csiHandlers[this._collect << 8 | ch];
let j = handlers ? handlers.length - 1 : -1;
for (; j >= 0; j--) {
handlerResult = handlers[j](this._params);
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
transition = ParserAction.CSI_DISPATCH << TableAccess.TRANSITION_ACTION_SHIFT | ParserState.GROUND;
this._preserveStack(ParserStackType.CSI, handlers, j, transition, k);
return handlerResult;
}
}
if (j < 0) {
this._csiHandlerFb(this._collect << 8 | ch, this._params);
}
this.precedingJoinState = 0;
i = k;
this.currentState = ParserState.GROUND;
csiDone = true;
break;
} else {
break;
}
}
if (!csiDone) {
i = k - 1;
this.currentState = ParserState.CSI_PARAM;
}
continue;
}
// normal transition & action lookup
transition = this._transitions.table[
this.currentState << TableAccess.INDEX_STATE_SHIFT |
(code < NON_ASCII_PRINTABLE ? code : NON_ASCII_PRINTABLE)
];
switch (transition >> TableAccess.TRANSITION_ACTION_SHIFT) {
case ParserAction.PRINT:
// Note: 0x20 (SP) is included, 0x7F (DEL) is excluded
let c = i;
const l4 = length - 4;
while (c < l4
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
) {}
if (c >= l4) {
while (c < length && data[c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)) {
c++;
}
}
this._printHandler(data, i, c);
i = c - 1;
break;
case ParserAction.EXECUTE:
if (this._executeHandlers[code]) this._executeHandlers[code]();
else this._executeHandlerFb(code);
this.precedingJoinState = 0;
break;
case ParserAction.IGNORE:
break;
case ParserAction.ERROR:
const inject: IParsingState = this._errorHandler(
{
position: i,
code,
currentState: this.currentState,
collect: this._collect,
params: this._params,
abort: false
});
if (inject.abort) return;
// inject values: currently not implemented
break;
case ParserAction.CSI_DISPATCH:
// Trigger CSI Handler
const handlers = this._csiHandlers[this._collect << 8 | code];
let j = handlers ? handlers.length - 1 : -1;
for (; j >= 0; j--) {
// true means success and to stop bubbling
// a promise indicates an async handler that needs to finish before progressing
handlerResult = handlers[j](this._params);
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._preserveStack(ParserStackType.CSI, handlers, j, transition, i);
return handlerResult;
}
}
if (j < 0) {
this._csiHandlerFb(this._collect << 8 | code, this._params);
}
this.precedingJoinState = 0;
break;
case ParserAction.PARAM:
// inner loop: digits (0x30 - 0x39) and ; (0x3b) and : (0x3a)
do {
switch (code) {
case 0x3b:
this._params.addParam(0); // ZDM
break;
case 0x3a:
this._params.addSubParam(-1);
break;
default: // 0x30 - 0x39
this._params.addDigit(code - 48);
}
} while (++i < length && (code = data[i]) > 0x2f && code < 0x3c);
i--;
break;
case ParserAction.COLLECT:
this._collect <<= 8;
this._collect |= code;
break;
case ParserAction.ESC_DISPATCH:
const handlersEsc = this._escHandlers[this._collect << 8 | code];
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
for (; jj >= 0; jj--) {
// true means success and to stop bubbling
// a promise indicates an async handler that needs to finish before progressing
handlerResult = handlersEsc[jj]();
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._preserveStack(ParserStackType.ESC, handlersEsc, jj, transition, i);
return handlerResult;
}
}
if (jj < 0) {
this._escHandlerFb(this._collect << 8 | code);
}
this.precedingJoinState = 0;
break;
case ParserAction.CLEAR:
this._params.resetZdm();
this._collect = 0;
break;
case ParserAction.DCS_HOOK:
this._dcsParser.hook(this._collect << 8 | code, this._params);
break;
case ParserAction.DCS_PUT:
// inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f
// unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort)
for (let j = i + 1; ; ++j) {
if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
this._dcsParser.put(data, i, j);
i = j - 1;
break;
}
}
break;
case ParserAction.DCS_UNHOOK:
handlerResult = this._dcsParser.unhook(code !== 0x18 && code !== 0x1a);
if (handlerResult) {
this._preserveStack(ParserStackType.DCS, [], 0, transition, i);
return handlerResult;
}
if (code === 0x1b) transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
break;
case ParserAction.OSC_START:
this._oscParser.start();
break;
case ParserAction.OSC_PUT:
// inner loop: 0x20 (SP) included, 0x7F (DEL) included
for (let j = i + 1; ; j++) {
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
this._oscParser.put(data, i, j);
i = j - 1;
break;
}
}
break;
case ParserAction.OSC_END:
handlerResult = this._oscParser.end(code !== 0x18 && code !== 0x1a);
if (handlerResult) {
this._preserveStack(ParserStackType.OSC, [], 0, transition, i);
return handlerResult;
}
if (code === 0x1b) transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
break;
case ParserAction.APC_START:
this._apcParser.start(this._collect << 8 | code);
break;
case ParserAction.APC_PUT:
// inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c
// allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE
for (let j = i + 1; ; ++j) {
if (j < length && (
(data[j] >= 0x20 && data[j] < 0x7f) || (data[j] >= 0x08 && data[j] < 0x0e) || data[j] >= NON_ASCII_PRINTABLE
)) continue;
this._apcParser.put(data, i, j);
i = j - 1;
break;
}
break;
case ParserAction.APC_END:
handlerResult = this._apcParser.end(code !== 0x18 && code !== 0x1a);
if (handlerResult) {
this._preserveStack(ParserStackType.APC, [], 0, transition, i);
return handlerResult;
}
if (code === 0x1b) transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
break;
}
this.currentState = transition & TableAccess.TRANSITION_STATE_MASK;
}
return parseWithWasmScanner(this as unknown as import('./EscapeSequenceParserWasm').IWasmParseHost, data, length, promiseResult, this._scanCache);
}
}
@@ -0,0 +1,417 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ParserState } from './Constants';
import { Params } from './Params';
import { Op, ScanResult } from './ScanTypes';
import { WasmEscapeScanner } from './WasmEscapeScanner';
import {
CsiHandlerType,
EscHandlerType,
IParams,
IParsingState,
IParserStackState,
ParserStackType,
ResumableHandlersType
} from './Types';
export interface IWasmParseHost {
currentState: number;
collect: number;
precedingJoinState: number;
readonly _params: Params;
readonly _parseStack: IParserStackState;
readonly _csiHandlers: { [ident: number]: CsiHandlerType[] | undefined };
readonly _escHandlers: { [ident: number]: EscHandlerType[] | undefined };
readonly _executeHandlers: { [flag: number]: (() => void) | undefined };
readonly _executeHandlersArr: Array<((code: number) => void) | undefined>;
_csiHandlerFb(ident: number, params: IParams): void;
_escHandlerFb(ident: number): void;
_executeHandlerFb(code: number): void;
_printHandler(data: Uint32Array, start: number, end: number): void;
_oscParser: { start(): void; put(data: Uint32Array, start: number, end: number): void; end(success: boolean, promiseResult?: boolean): void | Promise<boolean> };
_dcsParser: { hook(ident: number, params: IParams): void; put(data: Uint32Array, start: number, end: number): void; unhook(success: boolean, promiseResult?: boolean): void | Promise<boolean> };
_apcParser: { start(ident: number): void; put(data: Uint32Array, start: number, end: number): void; end(success: boolean, promiseResult?: boolean): void | Promise<boolean> };
_preserveStack(state: ParserStackType, handlers: ResumableHandlersType, handlerPos: number, transition: number, chunkPos: number): void;
_errorHandler(state: IParsingState): IParsingState;
}
let wasmReady = false;
function ensureWasm(): void {
if (!wasmReady) {
WasmEscapeScanner.initSync();
wasmReady = true;
}
}
function loadParams(host: IWasmParseHost, scan: ScanResult, opIndex: number): void {
const start = scan.paramStarts[opIndex] ?? 0;
const count = scan.paramCounts[opIndex] ?? 0;
if (count === 0) {
host._params.resetZdm();
return;
}
let idx = start;
host._params.resetZdm();
for (let p = 0; p < count; p++) {
const main = scan.params[idx++] | 0;
if (p === 0) {
host._params.params[0] = main;
host._params.length = 1;
} else {
host._params.addParam(main);
}
while (idx < scan.params.length && (scan.params[idx] & 0x80000000) !== 0) {
const sv = scan.params[idx++] & 0x7FFFFFFF;
host._params.addSubParam(sv === 0x7FFFFFFF ? -1 : sv);
}
}
}
/** True when a chunked WASM scan of `data` is still in progress (e.g. nested parse during print). */
function isActiveWasmParse(scanCache: IWasmScanCache, data: Uint32Array, length: number): boolean {
return scanCache.data === data
&& (scanCache.inputOffset ?? 0) < length
&& (scanCache.scan !== undefined || scanCache.chunkStart !== undefined);
}
export function parseWithWasmScanner(
host: IWasmParseHost,
data: Uint32Array,
length: number,
promiseResult: boolean | undefined,
scanCache: IWasmScanCache
): void | Promise<boolean> {
ensureWasm();
// Resume async handler dispatch without rescanning
if (host._parseStack.state) {
return resumeWasmParse(host, data, length, promiseResult, scanCache);
}
if (!isActiveWasmParse(scanCache, data, length)) {
scanCache.stateBeforeScan = host.currentState;
scanCache.inputOffset = 0;
scanCache.scan = undefined;
scanCache.opIndex = 0;
scanCache.data = undefined;
scanCache.chunkStart = undefined;
}
return runWasmScanChunks(host, data, length, promiseResult, scanCache);
}
interface IWasmScanCache {
scan?: ScanResult;
data?: Uint32Array;
opIndex: number;
stateBeforeScan?: number;
inputOffset?: number;
chunkStart?: number;
}
function runWasmScanChunks(
host: IWasmParseHost,
data: Uint32Array,
length: number,
promiseResult: boolean | undefined,
scanCache: IWasmScanCache
): void | Promise<boolean> {
let pos = scanCache.inputOffset ?? 0;
const stateBeforeScan = scanCache.stateBeforeScan ?? host.currentState;
while (pos < length) {
if (!scanCache.scan || scanCache.data !== data || scanCache.chunkStart !== pos) {
WasmEscapeScanner.syncParserState(host.currentState, host.collect, host._params);
const scan = WasmEscapeScanner.scan(data, length, pos);
scanCache.scan = scan;
scanCache.data = data;
scanCache.chunkStart = pos;
scanCache.opIndex = 0;
host.currentState = WasmEscapeScanner.currentState;
host.collect = WasmEscapeScanner.collect;
}
const scan = scanCache.scan;
if (!scan) {
throw new Error('Wasm parser scan cache missing during chunked parse');
}
const result = dispatchScanOps(host, data, scan, scanCache.opIndex, scanCache, promiseResult, stateBeforeScan);
if (result instanceof Promise) {
scanCache.inputOffset = pos;
return result;
}
const nextOffset = scan.nextOffset;
if (nextOffset <= pos) {
throw new Error(`Wasm parser scan did not advance (stuck at input offset ${pos})`);
}
pos = nextOffset;
scanCache.inputOffset = pos;
scanCache.scan = undefined;
scanCache.opIndex = 0;
}
scanCache.inputOffset = length;
scanCache.scan = undefined;
scanCache.data = undefined;
scanCache.chunkStart = undefined;
}
function resumeWasmParse(
host: IWasmParseHost,
data: Uint32Array,
length: number,
promiseResult: boolean | undefined,
scanCache: IWasmScanCache
): void | Promise<boolean> {
if (host._parseStack.state === ParserStackType.RESET) {
host._parseStack.state = ParserStackType.NONE;
scanCache.opIndex = host._parseStack.chunkPos + 1;
host._parseStack.chunkPos = 0;
if (!scanCache.scan || scanCache.data !== data) {
return parseWithWasmScanner(host, data, length, promiseResult, scanCache);
}
return runWasmScanChunks(host, data, length, promiseResult, scanCache);
}
if (promiseResult === undefined || host._parseStack.state === ParserStackType.FAIL) {
host._parseStack.state = ParserStackType.FAIL;
throw new Error('improper continuation due to previous async handler, giving up parsing');
}
const handlers = host._parseStack.handlers;
let handlerPos = host._parseStack.handlerPos - 1;
let handlerResult: void | boolean | Promise<boolean>;
const scan = scanCache.scan;
const opIndex = scanCache.opIndex;
if (!scan) {
throw new Error('missing scan cache on wasm parser resume');
}
switch (host._parseStack.state) {
case ParserStackType.CSI:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as CsiHandlerType[])[handlerPos](host._params);
if (handlerResult === true) break;
if (handlerResult instanceof Promise) {
host._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
host._parseStack.handlers = [];
scanCache.opIndex = opIndex + 1;
break;
case ParserStackType.ESC:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as EscHandlerType[])[handlerPos]();
if (handlerResult === true) break;
if (handlerResult instanceof Promise) {
host._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
host._parseStack.handlers = [];
scanCache.opIndex = opIndex + 1;
break;
case ParserStackType.DCS: {
const code = data[host._parseStack.chunkPos];
handlerResult = host._dcsParser.unhook(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) return handlerResult;
if (code === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
scanCache.opIndex = opIndex + 1;
break;
}
case ParserStackType.OSC: {
const code = data[host._parseStack.chunkPos];
handlerResult = host._oscParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) return handlerResult;
if (code === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
scanCache.opIndex = opIndex + 1;
break;
}
case ParserStackType.APC: {
const code = data[host._parseStack.chunkPos];
handlerResult = host._apcParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) return handlerResult;
if (code === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
scanCache.opIndex = opIndex + 1;
break;
}
}
host._parseStack.state = ParserStackType.NONE;
host.precedingJoinState = 0;
return runWasmScanChunks(host, data, length, promiseResult, scanCache);
}
function dispatchScanOps(
host: IWasmParseHost,
data: Uint32Array,
scan: ScanResult,
fromOp: number,
scanCache: IWasmScanCache,
promiseResult: boolean | undefined,
stateBeforeScan: number
): void | Promise<boolean> {
let handlerResult: void | boolean | Promise<boolean>;
for (let o = fromOp; o < scan.opCount; o++) {
scanCache.opIndex = o;
const kind = scan.kinds[o];
const start = scan.starts[o];
const len = scan.lengths[o];
const aux = scan.aux[o];
const chunkPos = kind === Op.Csi ? len : start;
switch (kind) {
case Op.Print:
host._printHandler(data, start, start + len);
break;
case Op.Execute: {
const code = aux;
const count = len > 0 ? len : 1;
for (let n = 0; n < count; n++) {
if (code < 0x18) {
const exe = host._executeHandlersArr[code];
if (exe) exe(code);
else host._executeHandlerFb(code);
} else if (host._executeHandlers[code]) {
host._executeHandlers[code]!();
} else {
host._executeHandlerFb(code);
}
}
host.precedingJoinState = 0;
break;
}
case Op.Csi: {
host.collect = aux >> 8;
loadParams(host, scan, o);
const handlers = host._csiHandlers[aux];
let j = handlers ? handlers.length - 1 : -1;
for (; j >= 0; j--) {
handlerResult = handlers![j](host._params);
if (handlerResult === true) break;
if (handlerResult instanceof Promise) {
host._preserveStack(ParserStackType.CSI, handlers!, j, 0, chunkPos);
return handlerResult;
}
}
if (j < 0) host._csiHandlerFb(aux, host._params);
host.precedingJoinState = 0;
break;
}
case Op.Esc: {
host.collect = aux >> 8;
loadParams(host, scan, o);
const handlersEsc = host._escHandlers[aux];
const escCount = len > 0 ? len : 1;
escLoop: for (let rep = 0; rep < escCount; rep++) {
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
for (; jj >= 0; jj--) {
handlerResult = handlersEsc![jj]();
if (handlerResult === true) continue escLoop;
if (handlerResult instanceof Promise) {
host._preserveStack(ParserStackType.ESC, handlersEsc!, jj, 0, chunkPos);
return handlerResult;
}
}
if (jj < 0) host._escHandlerFb(aux);
}
host.precedingJoinState = 0;
break;
}
case Op.OscStart:
host._oscParser.start();
break;
case Op.OscPut:
if (len > 0) host._oscParser.put(data, start, start + len);
break;
case Op.OscEnd: {
const term = aux;
handlerResult = host._oscParser.end(term !== 0x18 && term !== 0x1a);
if (handlerResult) {
host._preserveStack(ParserStackType.OSC, [], 0, 0, start);
return handlerResult;
}
if (term === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
host.precedingJoinState = 0;
break;
}
case Op.DcsHook: {
host.collect = aux >> 8;
loadParams(host, scan, o);
host._dcsParser.hook(aux, host._params);
break;
}
case Op.DcsPut:
if (len > 0) host._dcsParser.put(data, start, start + len);
break;
case Op.DcsUnhook: {
const term = aux;
handlerResult = host._dcsParser.unhook(term !== 0x18 && term !== 0x1a);
if (handlerResult) {
host._preserveStack(ParserStackType.DCS, [], 0, 0, start);
return handlerResult;
}
host._params.resetZdm();
host.collect = 0;
host.precedingJoinState = 0;
break;
}
case Op.ApcStart:
host._apcParser.start(aux);
break;
case Op.ApcPut:
if (len > 0) host._apcParser.put(data, start, start + len);
break;
case Op.ApcEnd: {
const term = aux;
handlerResult = host._apcParser.end(term !== 0x18 && term !== 0x1a);
if (handlerResult) {
host._preserveStack(ParserStackType.APC, [], 0, 0, start);
return handlerResult;
}
host._params.resetZdm();
host.collect = 0;
host.precedingJoinState = 0;
break;
}
case Op.Error: {
WasmEscapeScanner.exportParamsTo(host._params);
const inject = host._errorHandler({
position: start,
code: aux & 0xFFFF,
currentState: aux >> 16,
collect: WasmEscapeScanner.collect,
params: host._params,
abort: false
});
if (inject.abort) return;
break;
}
}
}
scanCache.opIndex = scan.opCount;
host.currentState = WasmEscapeScanner.currentState;
host.collect = WasmEscapeScanner.collect;
WasmEscapeScanner.exportParamsTo(host._params);
}
+1 -1
View File
@@ -172,7 +172,7 @@ export class Params implements IParams {
* Add a sub parameter value.
* The sub parameter is automatically associated with the last parameter value.
* Thus it is not possible to add a subparameter without any parameter added yet.
* `Params` only stores up to `maxSubParamsLength` sub parameters, any later
* `Params` only stores up to `subParamsLength` sub parameters, any later
* sub parameter will be ignored.
*/
public addSubParam(value: number): void {
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
export const Op = {
Print: 0,
Execute: 1,
Esc: 2,
Csi: 3,
Osc: 4,
Dcs: 5,
Apc: 6,
DcsHook: 7,
DcsPut: 8,
DcsUnhook: 9,
OscStart: 10,
OscPut: 11,
OscEnd: 12,
ApcStart: 13,
ApcPut: 14,
ApcEnd: 15,
Error: 16
} as const;
export type OpKind = typeof Op[keyof typeof Op];
export type ScanResult = {
kinds: Uint8Array;
starts: Uint32Array;
lengths: Uint32Array;
aux: Uint32Array;
params: Uint32Array;
paramStarts: Uint32Array;
paramCounts: Uint16Array;
opCount: number;
nextOffset: number;
};
+28 -4
View File
@@ -3,10 +3,12 @@
* @license MIT
*/
import { IDisposable, IParams, ParamsArray } from '../Types';
import { IDisposable } from '../Types';
import { ParserState } from './Constants';
export type { IParams, ParamsArray };
/** sequence params serialized to js arrays */
export type ParamsArray = (number | number[])[];
/** Params constructor type. */
export interface IParamsConstructor {
@@ -16,6 +18,28 @@ export interface IParamsConstructor {
fromArray(values: ParamsArray): IParams;
}
/** Interface of Params storage class. */
export interface IParams {
/** from ctor */
maxLength: number;
maxSubParamsLength: number;
/** param values and its length */
params: Int32Array;
length: number;
/** methods */
clone(): IParams;
toArray(): ParamsArray;
reset(): void;
resetZdm(): void;
addParam(value: number): void;
addSubParam(value: number): void;
hasSubParams(idx: number): boolean;
getSubParams(idx: number): Int32Array | null;
getSubParamsAll(): {[idx: number]: Int32Array};
}
/**
* Internal state of EscapeSequenceParser.
* Used as argument of the error handler to allow
@@ -234,8 +258,8 @@ export interface IApcParser extends ISubParser<IApcHandler, ApcFallbackHandlerTy
/**
* Interface to denote a specific ESC, CSI or DCS handler slot.
* The values are used to create an integer representation during handler
* registration before passed to the subparsers as `ident`.
* The values are used to create an integer respresentation during handler
* regristation before passed to the subparsers as `ident`.
* The integer translation is made to allow a faster handler access
* in `EscapeSequenceParser.parse`.
*/
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { StringToUtf32 } from '../input/TextDecoder';
import { Op } from './ScanTypes';
import { WasmEscapeScanner } from './WasmEscapeScanner';
describe('WasmEscapeScanner', () => {
before(() => {
WasmEscapeScanner.initSync();
});
beforeEach(() => {
WasmEscapeScanner.reset();
});
it('scans hello with SGR sequences', () => {
const input = 'hello\x1b[31mred\x1b[0m';
const data = new Uint32Array(input.length);
const len = new StringToUtf32().decode(input, data);
const scan = WasmEscapeScanner.scan(data, len);
assert.equal(scan.opCount, 4);
assert.deepEqual(Array.from(scan.kinds), [Op.Print, Op.Csi, Op.Print, Op.Csi]);
assert.deepEqual(Array.from(scan.starts), [0, 0, 10, 1]);
// CSI/ESC: lengths hold final-byte index; print ops use run length
assert.deepEqual(Array.from(scan.lengths), [5, 9, 3, 16]);
assert.deepEqual(Array.from(scan.aux), [0, 'm'.charCodeAt(0), 0, 'm'.charCodeAt(0)]);
assert.deepEqual(Array.from(scan.paramStarts), [0, 0, 0, 1]);
assert.deepEqual(Array.from(scan.paramCounts), [0, 1, 0, 1]);
assert.deepEqual(Array.from(scan.params), [31, 0]);
});
});
+208
View File
@@ -0,0 +1,208 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Params } from './Params';
import { Op, ScanResult } from './ScanTypes';
import { PARSER_WASM_BASE64 } from './wasm/parser.wasm.embed';
export { Op };
const WASM_IMPORTS = {
env: {
memory: new WebAssembly.Memory({ initial: 1024, maximum: 1024 })
}
};
let wasmModule: WebAssembly.Instance | undefined;
let wasmExports: {
memory: WebAssembly.Memory;
reset: () => void;
scan: (offset: number, length: number) => number;
set_state: (state: number, collect: number) => void;
get_input_ptr: () => number;
get_kinds_ptr: () => number;
get_starts_ptr: () => number;
get_lengths_ptr: () => number;
get_aux_ptr: () => number;
get_param_starts_ptr: () => number;
get_param_counts_ptr: () => number;
get_params_ptr: () => number;
get_state_ptr: () => number;
get_header_ptr: () => number;
sync_params_from: (srcPtr: number, len: number) => void;
export_params_to: (dstPtr: number, maxLen: number) => void;
get_params_len: () => number;
get_subparams_len: () => number;
get_subparams_ptr: () => number;
get_subparams_idx_ptr: () => number;
};
async function initWasm(): Promise<void> {
if (wasmModule) return;
const bytes = Uint8Array.from(atob(PARSER_WASM_BASE64), c => c.charCodeAt(0));
const mod = await WebAssembly.instantiate(bytes, WASM_IMPORTS);
wasmModule = mod.instance;
wasmExports = wasmModule.exports as typeof wasmExports;
}
function ensureWasm(): typeof wasmExports {
if (!wasmExports) {
throw new Error('WasmEscapeScanner not initialized. Call WasmEscapeScanner.init() first.');
}
return wasmExports;
}
export class WasmEscapeScanner {
private static _initPromise: Promise<void> | undefined;
private static _syncReady = false;
public static init(): Promise<void> {
if (!this._initPromise) {
this._initPromise = initWasm().then(() => {
this._syncReady = true;
wasmExports.reset();
});
}
return this._initPromise;
}
/** Synchronous init for unit tests (Node supports sync instantiate). */
public static initSync(): void {
if (this._syncReady) return;
const binary = globalThis.atob(PARSER_WASM_BASE64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
const mod = new WebAssembly.Module(bytes);
wasmModule = new WebAssembly.Instance(mod, WASM_IMPORTS);
wasmExports = wasmModule.exports as typeof wasmExports;
wasmExports.reset();
this._syncReady = true;
}
public static reset(): void {
ensureWasm().reset();
}
public static get currentState(): number {
const mem = new Uint32Array(ensureWasm().memory.buffer);
const statePtr = ensureWasm().get_state_ptr() >> 2;
return mem[statePtr];
}
public static set currentState(value: number) {
const ex = ensureWasm();
const mem = new Uint32Array(ex.memory.buffer);
const statePtr = ex.get_state_ptr() >> 2;
ex.set_state(value, mem[statePtr + 1]);
}
public static get collect(): number {
const mem = new Uint32Array(ensureWasm().memory.buffer);
return mem[(ensureWasm().get_state_ptr() >> 2) + 1];
}
public static set collect(value: number) {
const ex = ensureWasm();
ex.set_state(memState(ex), value);
}
public static syncParserState(currentState: number, collect: number, params: Params): void {
const ex = ensureWasm();
ex.set_state(currentState, collect);
if (params.length === 0) {
ex.sync_params_from(ex.get_params_ptr(), 0);
return;
}
const mem = ex.memory.buffer;
const paramsPtr = ex.get_params_ptr();
const view = new Int32Array(mem, paramsPtr, params.length);
for (let i = 0; i < params.length; i++) {
view[i] = params.params[i];
}
ex.sync_params_from(paramsPtr, params.length);
}
public static exportParamsTo(params: Params): void {
const ex = ensureWasm();
const len = ex.get_params_len();
params.reset();
if (len === 0) {
params.resetZdm();
return;
}
const mem = ex.memory.buffer;
const dstPtr = ex.get_params_ptr();
ex.export_params_to(dstPtr, len);
const pView = new Int32Array(mem, dstPtr, len);
const subLen = ex.get_subparams_len();
const subView = subLen > 0 ? new Int32Array(mem, ex.get_subparams_ptr(), subLen) : undefined;
const idxView = new Uint16Array(mem, ex.get_subparams_idx_ptr(), len);
params.resetZdm();
params.params[0] = pView[0];
params.length = 1;
if (subView && len > 0) {
const start0 = idxView[0] >> 8;
const end0 = idxView[0] & 0xFF;
for (let k = start0; k < end0; k++) {
params.addSubParam(subView[k]);
}
}
for (let i = 1; i < len; i++) {
params.addParam(pView[i]);
if (subView) {
const start = idxView[i] >> 8;
const end = idxView[i] & 0xFF;
for (let k = start; k < end; k++) {
params.addSubParam(subView[k]);
}
}
}
}
public static scan(data: Uint32Array, length: number, offset = 0): ScanResult {
const ex = ensureWasm();
const inputPtr = ex.get_input_ptr();
const mem = ex.memory.buffer;
const inputView = new Uint32Array(mem, inputPtr, length);
inputView.set(data.subarray(0, length));
const opCount = ex.scan(offset, length);
if (opCount < 0) {
throw new Error('Wasm parser op buffer overflow');
}
const count = opCount;
const kinds = new Uint8Array(mem, ex.get_kinds_ptr(), count);
const starts = new Uint32Array(mem, ex.get_starts_ptr(), count);
const lengths = new Uint32Array(mem, ex.get_lengths_ptr(), count);
const aux = new Uint32Array(mem, ex.get_aux_ptr(), count);
const paramStarts = new Uint32Array(mem, ex.get_param_starts_ptr(), count);
const paramCounts = new Uint16Array(mem, ex.get_param_counts_ptr(), count);
const headerView = new Uint32Array(mem, ex.get_header_ptr(), 4);
const paramsLen = headerView[2];
const nextOffset = headerView[3];
const params = new Uint32Array(mem, ex.get_params_ptr(), paramsLen);
return {
kinds: kinds.slice(),
starts: starts.slice(),
lengths: lengths.slice(),
aux: aux.slice(),
params: params.slice(),
paramStarts: paramStarts.slice(),
paramCounts: paramCounts.slice(),
opCount: count,
nextOffset
};
}
}
function memState(ex: typeof wasmExports): number {
const mem = new Uint32Array(ex.memory.buffer);
return mem[ex.get_state_ptr() >> 2];
}
+505
View File
@@ -0,0 +1,505 @@
/* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT */
#include "parser.h"
#include "transition_table.h"
#include <stddef.h>
/* Static transition table is linked at 65536; scratch must sit above it. */
#define MEM_BASE 65536
#define PARSER_TABLE_BYTE_SIZE (PARSER_TABLE_SIZE * sizeof(uint16_t))
#define MEM_STATE (((MEM_BASE + PARSER_TABLE_BYTE_SIZE) + 255u) & ~255u)
/* ParserWasmState is ~2.3KB; keep header and op buffers past it */
#define MEM_HEADER (MEM_STATE + 2048)
#define MEM_KINDS (MEM_HEADER + 64)
#define MEM_STARTS (MEM_KINDS + PARSER_MAX_OPS)
#define MEM_LENGTHS (MEM_STARTS + PARSER_MAX_OPS * 4)
#define MEM_AUX (MEM_LENGTHS + PARSER_MAX_OPS * 4)
#define MEM_PARAM_STARTS (MEM_AUX + PARSER_MAX_OPS * 4)
#define MEM_PARAM_COUNTS (MEM_PARAM_STARTS + PARSER_MAX_OPS * 4)
#define MEM_PARAMS_ARENA (MEM_PARAM_COUNTS + PARSER_MAX_OPS * 2) /* uint16 param counts */
#define MEM_INPUT_BASE (MEM_PARAMS_ARENA + PARSER_MAX_PARAMS * 4)
static ParserWasmState *state(void) { return (ParserWasmState *)(uintptr_t)MEM_STATE; }
static ParserScanHeader *header(void) { return (ParserScanHeader *)(uintptr_t)MEM_HEADER; }
static uint8_t *kinds(void) { return (uint8_t *)(uintptr_t)MEM_KINDS; }
static uint32_t *starts(void) { return (uint32_t *)(uintptr_t)MEM_STARTS; }
static uint32_t *lengths(void) { return (uint32_t *)(uintptr_t)MEM_LENGTHS; }
static uint32_t *aux(void) { return (uint32_t *)(uintptr_t)MEM_AUX; }
static uint32_t *param_starts(void) { return (uint32_t *)(uintptr_t)MEM_PARAM_STARTS; }
static uint16_t *param_counts(void) { return (uint16_t *)(uintptr_t)MEM_PARAM_COUNTS; }
static int32_t *params_arena(void) { return (int32_t *)(uintptr_t)MEM_PARAMS_ARENA; }
static uint32_t *input(void) { return (uint32_t *)(uintptr_t)MEM_INPUT_BASE; }
static uint32_t transition_lookup(uint32_t st, uint32_t code) {
uint32_t idx = code < PARSER_NON_ASCII_PRINTABLE ? code : PARSER_NON_ASCII_PRINTABLE;
return PARSER_TRANSITION_TABLE[st << PARSER_TABLE_INDEX_STATE_SHIFT | idx];
}
static void params_reset_zdm(ParserWasmState *s) {
s->params_len = 1;
s->subparams_len = 0;
s->reject_digits = 0;
s->reject_sub_digits = 0;
s->digit_is_sub = 0;
s->subparams_idx[0] = 0;
s->params[0] = 0;
}
static void params_reset(ParserWasmState *s) {
s->params_len = 0;
s->subparams_len = 0;
s->reject_digits = 0;
s->reject_sub_digits = 0;
s->digit_is_sub = 0;
}
static void params_add_param(ParserWasmState *s, int32_t value) {
s->digit_is_sub = 0;
if (s->params_len >= PARSER_PARAMS_MAX_LEN) {
s->reject_digits = 1;
return;
}
s->subparams_idx[s->params_len] = (uint16_t)(s->subparams_len << 8 | s->subparams_len);
if (value < 0) value = 0;
if (value > 0x7FFFFFFF) value = 0x7FFFFFFF;
s->params[s->params_len++] = value;
}
static void params_add_subparam(ParserWasmState *s, int32_t value) {
s->digit_is_sub = 1;
if (!s->params_len) return;
if (s->reject_digits || s->subparams_len >= PARSER_SUBPARAMS_MAX_LEN) {
s->reject_sub_digits = 1;
return;
}
if (value < 0) value = -1;
if (value > 0x7FFFFFFF) value = 0x7FFFFFFF;
s->subparams[s->subparams_len++] = value;
s->subparams_idx[s->params_len - 1]++;
}
static void params_add_digit(ParserWasmState *s, int32_t digit) {
uint32_t len;
if (s->reject_digits) return;
len = s->digit_is_sub ? s->subparams_len : s->params_len;
if (!len) return;
if (s->digit_is_sub && s->reject_sub_digits) return;
int32_t *store = s->digit_is_sub ? s->subparams : s->params;
int32_t cur = store[len - 1];
store[len - 1] = cur >= 0 ? (cur * 10 + digit > 0x7FFFFFFF ? 0x7FFFFFFF : cur * 10 + digit) : digit;
}
static void copy_params_to_arena(uint32_t op_idx, ParserWasmState *s) {
uint32_t start = header()->params_arena_len;
param_starts()[op_idx] = start;
param_counts()[op_idx] = (uint16_t)s->params_len;
for (uint32_t i = 0; i < s->params_len; i++) {
if (header()->params_arena_len >= PARSER_MAX_PARAMS) return;
params_arena()[header()->params_arena_len++] = s->params[i];
uint16_t idx = s->subparams_idx[i];
uint32_t sub_start = idx >> 8;
uint32_t sub_end = idx & 0xFF;
for (uint32_t k = sub_start; k < sub_end; k++) {
if (header()->params_arena_len >= PARSER_MAX_PARAMS) return;
int32_t sv = s->subparams[k];
uint32_t encoded = 0x80000000u | (uint32_t)(sv < 0 ? 0x7FFFFFFF : sv);
params_arena()[header()->params_arena_len++] = encoded;
}
}
}
static int emit_op(uint8_t kind, uint32_t start, uint32_t length, uint32_t aux_val, ParserWasmState *s) {
uint32_t idx = header()->op_count;
if (idx > 0) {
uint32_t prev = idx - 1;
if (kind == OP_EXECUTE && kinds()[prev] == OP_EXECUTE && aux()[prev] == aux_val) {
lengths()[prev]++;
return (int)prev;
}
if (kind == OP_ESC && kinds()[prev] == OP_ESC && aux()[prev] == aux_val) {
lengths()[prev]++;
return (int)prev;
}
}
if (idx >= PARSER_MAX_OPS) return -1;
kinds()[idx] = kind;
starts()[idx] = start;
if ((kind == OP_EXECUTE || kind == OP_ESC) && length == 0) {
lengths()[idx] = 1;
} else {
lengths()[idx] = length;
}
aux()[idx] = aux_val;
if (kind == OP_CSI || kind == OP_DCS_HOOK) {
uint32_t input_pos = start;
copy_params_to_arena(idx, s);
lengths()[idx] = input_pos;
starts()[idx] = param_starts()[idx];
} else if (kind == OP_ESC) {
uint32_t input_pos = start;
uint32_t repeat = lengths()[idx];
copy_params_to_arena(idx, s);
starts()[idx] = input_pos;
lengths()[idx] = repeat;
} else {
param_starts()[idx] = 0;
param_counts()[idx] = 0;
}
header()->op_count++;
return (int)idx;
}
static int is_printable(uint32_t code) {
return (code >= 0x20 && code <= 0x7e) || code >= PARSER_NON_ASCII_PRINTABLE;
}
__attribute__((export_name("reset")))
void reset(void) {
ParserWasmState *s = state();
s->current_state = PARSER_STATE_GROUND;
s->collect = 0;
s->preceding_join_state = 0;
s->osc_start = 0;
s->dcs_start = 0;
s->apc_start = 0;
params_reset_zdm(s);
header()->op_count = 0;
header()->input_len = 0;
header()->params_arena_len = 0;
header()->scan_offset = 0;
}
__attribute__((export_name("get_input_ptr")))
uint32_t get_input_ptr(void) { return MEM_INPUT_BASE; }
__attribute__((export_name("get_kinds_ptr")))
uint32_t get_kinds_ptr(void) { return MEM_KINDS; }
__attribute__((export_name("get_starts_ptr")))
uint32_t get_starts_ptr(void) { return MEM_STARTS; }
__attribute__((export_name("get_lengths_ptr")))
uint32_t get_lengths_ptr(void) { return MEM_LENGTHS; }
__attribute__((export_name("get_aux_ptr")))
uint32_t get_aux_ptr(void) { return MEM_AUX; }
__attribute__((export_name("get_param_starts_ptr")))
uint32_t get_param_starts_ptr(void) { return MEM_PARAM_STARTS; }
__attribute__((export_name("get_param_counts_ptr")))
uint32_t get_param_counts_ptr(void) { return MEM_PARAM_COUNTS; }
__attribute__((export_name("get_params_ptr")))
uint32_t get_params_ptr(void) { return MEM_PARAMS_ARENA; }
__attribute__((export_name("get_state_ptr")))
uint32_t get_state_ptr(void) { return MEM_STATE; }
__attribute__((export_name("get_header_ptr")))
uint32_t get_header_ptr(void) { return MEM_HEADER; }
__attribute__((export_name("set_state")))
void set_state(uint32_t current_state, uint32_t collect) {
state()->current_state = current_state;
state()->collect = collect;
}
__attribute__((export_name("export_params_to")))
void export_params_to(uint32_t dst_ptr, uint32_t max_len) {
ParserWasmState *s = state();
int32_t *dst = (int32_t *)(uintptr_t)dst_ptr;
uint32_t n = s->params_len < max_len ? s->params_len : max_len;
for (uint32_t i = 0; i < n; i++) {
dst[i] = s->params[i];
}
}
__attribute__((export_name("get_params_len")))
uint32_t get_params_len(void) {
return state()->params_len;
}
__attribute__((export_name("get_subparams_len")))
uint32_t get_subparams_len(void) {
return state()->subparams_len;
}
__attribute__((export_name("get_subparams_ptr")))
uint32_t get_subparams_ptr(void) {
return (uint32_t)(uintptr_t)state()->subparams;
}
__attribute__((export_name("get_subparams_idx_ptr")))
uint32_t get_subparams_idx_ptr(void) {
return (uint32_t)(uintptr_t)state()->subparams_idx;
}
__attribute__((export_name("sync_params_from")))
void sync_params_from(uint32_t src_ptr, uint32_t len) {
ParserWasmState *s = state();
const int32_t *src = (const int32_t *)(uintptr_t)src_ptr;
s->params_len = len > PARSER_PARAMS_MAX_LEN ? PARSER_PARAMS_MAX_LEN : len;
for (uint32_t i = 0; i < s->params_len; i++) {
s->params[i] = src[i];
}
s->subparams_len = 0;
s->reject_digits = 0;
s->reject_sub_digits = 0;
s->digit_is_sub = 0;
}
__attribute__((export_name("probe_action")))
uint32_t probe_action(uint32_t state_id, uint32_t code) {
uint32_t tr = transition_lookup(state_id, code);
return tr >> PARSER_TABLE_ACTION_SHIFT;
}
static int emit_or_stop(uint8_t kind, uint32_t start, uint32_t length, uint32_t aux_val, ParserWasmState *s, uint32_t stop_at) {
if (emit_op(kind, start, length, aux_val, s) < 0) {
header()->scan_offset = stop_at;
return 0;
}
return 1;
}
__attribute__((export_name("scan")))
int32_t scan(uint32_t offset, uint32_t length) {
ParserWasmState *s = state();
ParserScanHeader *h = header();
h->op_count = 0;
h->params_arena_len = 0;
h->input_len = length;
uint32_t i = offset;
uint32_t code;
uint32_t transition;
uint32_t action;
uint32_t print_start;
while (i < length) {
code = input()[i];
if (code < 0x18 && s->current_state <= PARSER_STATE_CSI_PARAM + 2) {
if (!emit_or_stop(OP_EXECUTE, i, 0, code, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->preceding_join_state = 0;
i++;
continue;
}
if (code == 0x1b && s->current_state < PARSER_STATE_OSC_STRING && i + 2 < length && input()[i + 1] == 0x5b) {
params_reset_zdm(s);
s->collect = 0;
uint32_t k = i + 2;
uint32_t ch = input()[k];
if (ch >= 0x3c && ch <= 0x3f) {
s->collect = ch;
k++;
}
int csi_done = 0;
for (; k < length; k++) {
ch = input()[k];
if (ch >= 0x30 && ch <= 0x39) {
params_add_digit(s, ch - 48);
} else if (ch == 0x3b) {
params_add_param(s, 0);
} else if (ch == 0x3a) {
params_add_subparam(s, -1);
} else if (ch >= 0x40 && ch <= 0x7e) {
uint32_t ident = (s->collect << 8) | ch;
if (!emit_or_stop(OP_CSI, k, 0, ident, s, k)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->preceding_join_state = 0;
i = k;
s->current_state = PARSER_STATE_GROUND;
csi_done = 1;
break;
} else {
break;
}
}
if (!csi_done) {
i = k - 1;
s->current_state = PARSER_STATE_CSI_PARAM;
}
i++;
continue;
}
transition = transition_lookup(s->current_state, code);
action = transition >> PARSER_TABLE_ACTION_SHIFT;
{
uint32_t next_state = transition & PARSER_TABLE_STATE_MASK;
switch (action) {
case 1: /* ERROR - report state before transition (matches TS parser) */
if (!emit_or_stop(OP_ERROR, i, 0, (s->current_state << 16) | code, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->current_state = next_state;
break;
default:
s->current_state = next_state;
break;
}
}
switch (action) {
case PARSER_ACTION_PRINT: {
print_start = i;
uint32_t c = i;
uint32_t l4 = length > 4 ? length - 4 : 0;
while (c < l4 && is_printable(input()[c]) && is_printable(input()[c + 1]) &&
is_printable(input()[c + 2]) && is_printable(input()[c + 3])) {
c += 4;
}
while (c < length && is_printable(input()[c])) c++;
if (!emit_or_stop(OP_PRINT, print_start, c - print_start, 0, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
i = c - 1;
break;
}
case PARSER_ACTION_EXECUTE:
if (!emit_or_stop(OP_EXECUTE, i, 0, code, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->preceding_join_state = 0;
break;
case PARSER_ACTION_IGNORE:
break;
case 1: /* ERROR - handled above */
break;
case PARSER_ACTION_CSI_DISPATCH: {
uint32_t ident = (s->collect << 8) | code;
if (!emit_or_stop(OP_CSI, i, 0, ident, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->preceding_join_state = 0;
break;
}
case 8: /* PARAM */
do {
switch (code) {
case 0x3b: params_add_param(s, 0); break;
case 0x3a: params_add_subparam(s, -1); break;
default: params_add_digit(s, code - 48); break;
}
} while (++i < length && (code = input()[i]) > 0x2f && code < 0x3c);
i--;
break;
case 9: /* COLLECT */
s->collect <<= 8;
s->collect |= code;
break;
case PARSER_ACTION_ESC_DISPATCH: {
uint32_t ident = (s->collect << 8) | code;
if (!emit_or_stop(OP_ESC, i, 0, ident, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->preceding_join_state = 0;
break;
}
case 11: /* CLEAR */
params_reset_zdm(s);
s->collect = 0;
break;
case 4: /* OSC_START */
if (!emit_or_stop(OP_OSC_START, i, 0, 0, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->osc_start = i + 1;
break;
case 5: /* OSC_PUT */
for (uint32_t j = i + 1; ; j++) {
if (j >= length || (code = input()[j]) < 0x20 ||
(code > 0x7f && code < PARSER_NON_ASCII_PRINTABLE)) {
if (j > i && !emit_or_stop(OP_OSC_PUT, i, j - i, 0, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
i = j - 1;
break;
}
}
break;
case PARSER_ACTION_OSC_END: {
if (!emit_or_stop(OP_OSC_END, i, 0, code, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->osc_term = code;
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
params_reset_zdm(s);
s->collect = 0;
s->preceding_join_state = 0;
break;
}
case 12: /* DCS_HOOK */
s->dcs_hook_ident = (s->collect << 8) | code;
if (!emit_or_stop(OP_DCS_HOOK, i, 0, s->dcs_hook_ident, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->dcs_start = i + 1;
break;
case 13: /* DCS_PUT */
for (uint32_t j = i + 1; ; j++) {
if (j >= length || (code = input()[j]) == 0x18 || code == 0x1a || code == 0x1b ||
(code > 0x7f && code < PARSER_NON_ASCII_PRINTABLE)) {
if (j > i && !emit_or_stop(OP_DCS_PUT, i, j - i, 0, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
i = j - 1;
break;
}
}
break;
case PARSER_ACTION_DCS_UNHOOK: {
if (!emit_or_stop(OP_DCS_UNHOOK, i, 0, code, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
params_reset_zdm(s);
s->collect = 0;
s->preceding_join_state = 0;
break;
}
case 15: /* APC_START */
s->apc_hook_ident = (s->collect << 8) | code;
if (!emit_or_stop(OP_APC_START, i, 0, s->apc_hook_ident, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
s->apc_start = i + 1;
break;
case 16: /* APC_PUT */
for (uint32_t j = i + 1; ; j++) {
if (j < length && (
(input()[j] >= 0x20 && input()[j] < 0x7f) ||
(input()[j] >= 0x08 && input()[j] < 0x0e) ||
input()[j] >= PARSER_NON_ASCII_PRINTABLE
)) continue;
if (j > i && !emit_or_stop(OP_APC_PUT, i, j - i, 0, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
i = j - 1;
break;
}
break;
case PARSER_ACTION_APC_END: {
if (!emit_or_stop(OP_APC_END, i, 0, code, s, i)) {
return h->op_count > 0 ? (int32_t)h->op_count : -1;
}
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
params_reset_zdm(s);
s->collect = 0;
s->preceding_join_state = 0;
break;
}
default:
break;
}
i++;
}
h->scan_offset = length;
return (int32_t)h->op_count;
}
+80
View File
@@ -0,0 +1,80 @@
/* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
#ifndef PARSER_WASM_H
#define PARSER_WASM_H
#include <stdint.h>
#define PARSER_NON_ASCII_PRINTABLE 0xA0
#define PARSER_TABLE_ACTION_SHIFT 8
#define PARSER_TABLE_STATE_MASK 255
#define PARSER_TABLE_INDEX_STATE_SHIFT 8
#define OP_PRINT 0
#define OP_EXECUTE 1
#define OP_ESC 2
#define OP_CSI 3
#define OP_OSC 4
#define OP_DCS 5
#define OP_APC 6
#define OP_DCS_HOOK 7
#define OP_DCS_PUT 8
#define OP_DCS_UNHOOK 9
#define OP_OSC_START 10
#define OP_OSC_PUT 11
#define OP_OSC_END 12
#define OP_APC_START 13
#define OP_APC_PUT 14
#define OP_APC_END 15
#define OP_ERROR 16
#define PARSER_STATE_GROUND 0
#define PARSER_STATE_ESCAPE 1
#define PARSER_STATE_OSC_STRING 8
#define PARSER_STATE_DCS_PASSTHROUGH 13
#define PARSER_STATE_APC_PASSTHROUGH 16
#define PARSER_STATE_CSI_PARAM 4
#define PARSER_ACTION_IGNORE 0
#define PARSER_ACTION_PRINT 2
#define PARSER_ACTION_EXECUTE 3
#define PARSER_ACTION_CSI_DISPATCH 7
#define PARSER_ACTION_ESC_DISPATCH 10
#define PARSER_ACTION_OSC_END 6
#define PARSER_ACTION_DCS_UNHOOK 14
#define PARSER_ACTION_APC_END 17
#define PARSER_MAX_OPS 4096
#define PARSER_MAX_PARAMS 4096
#define PARSER_PARAMS_MAX_LEN 32
#define PARSER_SUBPARAMS_MAX_LEN 256
typedef struct {
uint32_t current_state;
uint32_t collect;
uint32_t preceding_join_state;
int32_t params[PARSER_PARAMS_MAX_LEN];
uint32_t params_len;
int32_t subparams[PARSER_SUBPARAMS_MAX_LEN];
uint32_t subparams_len;
uint16_t subparams_idx[PARSER_PARAMS_MAX_LEN];
uint32_t reject_digits;
uint32_t reject_sub_digits;
uint32_t digit_is_sub;
uint32_t osc_start;
uint32_t dcs_start;
uint32_t apc_start;
uint32_t dcs_hook_ident;
uint32_t apc_hook_ident;
uint32_t osc_term;
} ParserWasmState;
typedef struct {
uint32_t op_count;
uint32_t input_len;
uint32_t params_arena_len;
uint32_t scan_offset;
} ParserScanHeader;
#endif
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
+279
View File
@@ -0,0 +1,279 @@
/* Auto-generated by bin/gen_parser_transition_table.mjs - do not edit */
#ifndef PARSER_TRANSITION_TABLE_H
#define PARSER_TRANSITION_TABLE_H
#include <stdint.h>
#define PARSER_TABLE_SIZE 4257
static const uint16_t PARSER_TRANSITION_TABLE[PARSER_TABLE_SIZE] = {
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0xb01, 0x300, 0x300, 0x300, 0x300,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x100,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x200, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301,
0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x300, 0x301, 0x300, 0xb01, 0x301, 0x301, 0x301, 0x301,
0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xb09, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0x7, 0xa00, 0xa00, 0xb03, 0xa00, 0x408, 0x7, 0xb0e,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0x1,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302,
0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x300, 0x302, 0x300, 0xb01, 0x302, 0x302, 0x302, 0x302,
0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0x2,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303,
0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x300, 0x303, 0x300, 0xb01, 0x303, 0x303, 0x303, 0x303,
0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905,
0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x904, 0x904, 0x904, 0x904,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x3,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304,
0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x300, 0x304, 0x300, 0xb01, 0x304, 0x304, 0x304, 0x304,
0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905,
0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x6, 0x6, 0x6, 0x6,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x4,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305,
0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x300, 0x305, 0x300, 0xb01, 0x305, 0x305, 0x305, 0x305,
0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905,
0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x5,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306,
0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x300, 0x306, 0x300, 0xb01, 0x306, 0x306, 0x306, 0x306,
0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x6, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x300, 0x7, 0x300, 0xb01, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x600, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x600, 0x8, 0x600, 0x600, 0x8, 0x8, 0x8, 0x8,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x600, 0x408, 0x7, 0xb0e,
0x508, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9,
0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x300, 0x9, 0x300, 0xb01, 0x9, 0x9, 0x9, 0x9,
0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c,
0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x90a, 0x90a, 0x90a, 0x90a,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0x9,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa,
0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0x300, 0xa, 0x300, 0xb01, 0xa, 0xa, 0xa, 0xa,
0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c,
0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0xb, 0xb, 0xb, 0xb,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xa,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0x300, 0xb, 0x300, 0xb01, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0xb, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc,
0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0x300, 0xc, 0x300, 0xb01, 0xc, 0xc, 0xc, 0xc,
0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xe00, 0xd0d, 0xe00, 0xe00, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0xe00, 0x408, 0x7, 0xb0e,
0xd0d, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe,
0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0x300, 0xe, 0x300, 0xb01, 0xe, 0xe, 0xe, 0xe,
0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xe,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x300, 0xf, 0x300, 0xb01, 0xf, 0xf, 0xf, 0xf,
0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1100, 0x10, 0x1100, 0x1100, 0x10, 0x10, 0x10, 0x10,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x10,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x1100, 0x408, 0x7, 0xb0e,
0x1010
};
#endif
+1 -1
View File
@@ -4,7 +4,7 @@
*/
import { CellData } from '../buffer/CellData';
import { IBufferLine, ICellData } from '../buffer/Types';
import { IBufferLine, ICellData } from '../Types';
import { IBufferCell as IBufferCellApi, IBufferLine as IBufferLineApi } from '@xterm/xterm';
export class BufferLineApiView implements IBufferLineApi {
+1 -1
View File
@@ -5,7 +5,7 @@
import { IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi } from '@xterm/xterm';
import { BufferApiView } from './BufferApiView';
import { ICoreTerminal } from '../CoreTerminal';
import { ICoreTerminal } from '../Types';
import { Disposable } from '../Lifecycle';
import { Emitter } from '../Event';
+1 -1
View File
@@ -5,7 +5,7 @@
import { IParams } from '../parser/Types';
import { IDisposable, IFunctionIdentifier, IParser } from '@xterm/xterm';
import { ICoreTerminal } from '../CoreTerminal';
import { ICoreTerminal } from '../Types';
export class ParserApi implements IParser {
constructor(private _core: ICoreTerminal) { }
+1 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { ICoreTerminal } from '../CoreTerminal';
import { ICoreTerminal } from '../Types';
import { IUnicodeHandling, IUnicodeVersionProvider } from '@xterm/xterm';
export class UnicodeApi implements IUnicodeHandling {
+2 -1
View File
@@ -4,8 +4,9 @@
*/
import { Disposable } from '../Lifecycle';
import { IAttributeData, IBuffer, IBufferLine, IBufferSet } from '../buffer/Types';
import { IAttributeData, IBufferLine } from '../Types';
import { BufferSet } from '../buffer/BufferSet';
import { IBuffer, IBufferSet } from '../buffer/Types';
import { IBufferService, ILogService, IOptionsService, type IBufferResizeEvent } from './Services';
import { Emitter } from '../Event';
@@ -5,7 +5,7 @@
import { assert } from 'chai';
import { DecorationLineCache, DecorationService } from './DecorationService';
import { IMarker } from '../buffer/Types';
import { IMarker } from '../Types';
import { Disposable } from '../Lifecycle';
import { Emitter } from '../Event';
import { MockLogService, MockBufferService, MockOptionsService } from '../TestUtils.test';
+2 -2
View File
@@ -3,13 +3,13 @@
* @license MIT
*/
import type { ICircularList, IDeleteEvent, IInsertEvent } from '../CircularList';
import type { IDeleteEvent, IInsertEvent } from '../CircularList';
import { MicrotaskTimer } from '../Async';
import { css } from '../Color';
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../Lifecycle';
import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from './Services';
import { SortedList } from '../SortedList';
import { IColor } from '../Types';
import { IColor, ICircularList } from '../Types';
import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm';
import { Emitter } from '../Event';
+2 -2
View File
@@ -9,8 +9,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInstantiationService } from './Services';
import { IServiceIdentifier, getServiceDependencies } from './ServiceRegistry';
import { IInstantiationService, IServiceIdentifier } from './Services';
import { getServiceDependencies } from './ServiceRegistry';
export class ServiceCollection {
+1 -1
View File
@@ -126,7 +126,7 @@ const DEFAULT_ENCODINGS: { [key: string]: CoreMouseEncoding } = {
*/
DEFAULT: (e: ICoreMouseEvent) => {
const params = [eventCode(e, false) + 32, e.col + 32, e.row + 32];
// suppress mouse report if we exceed addressable range
// supress mouse report if we exceed addressible range
// Note this is handled differently by emulators
// - xterm: sends 0;0 coords instead
// - vte, konsole: no report
+1 -2
View File
@@ -3,8 +3,7 @@
* @license MIT
*/
import { IBufferService, IOscLinkService } from './Services';
import { IOscLinkData } from '../Types';
import { IMarker } from '../buffer/Types';
import { IMarker, IOscLinkData } from '../Types';
export class OscLinkService implements IOscLinkService {
public serviceBrand: any;
+1 -5
View File
@@ -9,11 +9,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IServiceIdentifier<T> {
(...args: any[]): void;
type: T;
_id: string;
}
import { IServiceIdentifier } from './Services';
const enum Constants {
DI_TARGET = 'di$target',
+12 -6
View File
@@ -4,9 +4,9 @@
*/
import type { IDecoration, IDecorationOptions, ILinkHandler, ILogger, IWindowsPty, IOverviewRulerOptions } from '@xterm/xterm';
import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IKittyKeyboardState, IModes, IOscLinkData, IWindowOptions } from '../Types';
import { IAttributeData, IBuffer, IBufferSet } from '../buffer/Types';
import { createDecorator, IServiceIdentifier } from './ServiceRegistry';
import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, IAttributeData, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IKittyKeyboardState, IModes, IOscLinkData, IWindowOptions } from '../Types';
import { IBuffer, IBufferSet } from '../buffer/Types';
import { createDecorator } from './ServiceRegistry';
import type { Emitter, IEvent } from '../Event';
export const IBufferService = createDecorator<IBufferService>('BufferService');
@@ -119,6 +119,12 @@ export interface ICharsetService {
setgCharset(g: number, charset: ICharset | undefined): void;
}
export interface IServiceIdentifier<T> {
(...args: any[]): void;
type: T;
_id: string;
}
export interface IBrandedService {
serviceBrand: undefined;
}
@@ -324,7 +330,7 @@ export interface IOscLinkService {
* bit 0: shouldJoin - should combine with preceding character.
* bit 1..2: wcwidth - see UnicodeCharWidth.
* bit 3..31: class of character (currently only 4 bits are used).
* This is used to determine grapheme clustering - i.e. which codepoints
* This is used to determined grapheme clustering - i.e. which codepoints
* are to be combined into a single compound character.
*
* Use the UnicodeService static function createPropertyValue to create a
@@ -344,13 +350,13 @@ export type UnicodeCharWidth = 0 | 1 | 2;
export const IUnicodeService = createDecorator<IUnicodeService>('UnicodeService');
export interface IUnicodeService {
serviceBrand: undefined;
/** Register a Unicode version provider. */
/** Register an Unicode version provider. */
register(provider: IUnicodeVersionProvider): void;
/** Registered Unicode versions. */
readonly versions: string[];
/** Currently active version. */
activeVersion: string;
/** Event triggered when the active version changes. */
/** Event triggered, when activate version changed. */
readonly onChange: IEvent<string>;
/**
@@ -4,7 +4,6 @@
*/
import { assert } from 'chai';
import { UnicodeV6 } from '../input/UnicodeV6';
import { UnicodeService } from './UnicodeService';
import { IUnicodeVersionProvider } from './Services';
@@ -22,7 +21,6 @@ describe('unicode provider', () => {
let us: UnicodeService;
beforeEach(() => {
us = new UnicodeService();
us.register(new UnicodeV6());
});
it('default to V6', () => {
assert.equal(us.activeVersion, '6');
+9 -4
View File
@@ -3,6 +3,7 @@
* @license MIT
*/
import { UnicodeV6 } from '../input/UnicodeV6';
import { IUnicodeService, IUnicodeVersionProvider, UnicodeCharProperties, UnicodeCharWidth } from './Services';
import { Emitter } from '../Event';
@@ -11,7 +12,7 @@ export class UnicodeService implements IUnicodeService {
private _providers: {[key: string]: IUnicodeVersionProvider} = Object.create(null);
private _active: string = '';
private _activeProvider!: IUnicodeVersionProvider;
private _activeProvider: IUnicodeVersionProvider;
private readonly _onChange = new Emitter<string>();
public readonly onChange = this._onChange.event;
@@ -29,6 +30,13 @@ export class UnicodeService implements IUnicodeService {
return ((state & 0xffffff) << 3) | ((width & 3) << 1) | (shouldJoin?1:0);
}
constructor() {
const defaultProvider = new UnicodeV6();
this.register(defaultProvider);
this._active = defaultProvider.version;
this._activeProvider = defaultProvider;
}
public dispose(): void {
this._onChange.dispose();
}
@@ -52,9 +60,6 @@ export class UnicodeService implements IUnicodeService {
public register(provider: IUnicodeVersionProvider): void {
this._providers[provider.version] = provider;
if (!this._active) {
this.activeVersion = provider.version;
}
}
/**
+2 -2
View File
@@ -22,9 +22,9 @@
*/
import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine';
import { IBuffer, IMarker } from '../common/buffer/Types';
import { IBuffer } from '../common/buffer/Types';
import { CoreTerminal } from '../common/CoreTerminal';
import { ITerminalOptions } from '../common/Types';
import { IMarker, ITerminalOptions } from '../common/Types';
import { Emitter, EventUtils } from '../common/Event';
export class Terminal extends CoreTerminal {
@@ -4,8 +4,8 @@
*/
import { perfContext, before, RuntimeCase } from 'xterm-benchmark';
import { DecorationService } from '../../src/common/services/DecorationService';
import { MockLogService, MockBufferService, MockOptionsService } from '../../src/common/TestUtils.test';
import { DecorationService } from '../../out/common/services/DecorationService';
import { MockLogService, MockBufferService, MockOptionsService } from '../../out/common/TestUtils.test';
const enum Constants {
COLS = 80,
ROWS = 30,
@@ -4,12 +4,12 @@
*/
import { perfContext, before, beforeEach, ThroughputRuntimeCase } from 'xterm-benchmark';
import { EscapeSequenceParser } from '../../src/common/parser/EscapeSequenceParser';
import { C0, C1 } from '../../src/common/data/EscapeSequences';
import { IDcsHandler, IOscHandler, IApcHandler, IParams } from '../../src/common/parser/Types';
import { OscHandler } from '../../src/common/parser/OscParser';
import { DcsHandler } from '../../src/common/parser/DcsParser';
import { ApcHandler } from '../../src/common/parser/ApcParser';
import { EscapeSequenceParser } from '../../out/common/parser/EscapeSequenceParser';
import { C0, C1 } from '../../out/common/data/EscapeSequences';
import { IDcsHandler, IOscHandler, IApcHandler, IParams } from '../../out/common/parser/Types';
import { OscHandler } from '../../out/common/parser/OscParser';
import { DcsHandler } from '../../out/common/parser/DcsParser';
import { ApcHandler } from '../../out/common/parser/ApcParser';
const SIZE = 5000000;
+1 -1
View File
@@ -4,7 +4,7 @@
*/
import { perfContext, before, RuntimeCase } from 'xterm-benchmark';
import { Emitter } from '../../src/common/Event';
import { Emitter } from '../../out/common/Event';
const ITERATIONS = 1_000_000;
+2 -2
View File
@@ -6,8 +6,8 @@
import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark';
import { spawn } from 'node-pty';
import { Utf8ToUtf32, stringFromCodePoint } from '../../src/common/input/TextDecoder';
import { CoreBrowserTerminal } from '../../src/browser/CoreBrowserTerminal';
import { Utf8ToUtf32, stringFromCodePoint } from '../../out/common/input/TextDecoder';
import { CoreBrowserTerminal } from '../../out/browser/CoreBrowserTerminal';
perfContext('Terminal: ls -lR /usr/lib', () => {
let content = '';
+1 -1
View File
@@ -980,7 +980,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
});
test.describe('selectionInactiveBackground', async () => {
test('should render the inactive selection when not focused', async () => {
test('should render the the inactive selection when not focused', async () => {
const theme: ITheme = {
selectionBackground: '#FF000080',
selectionInactiveBackground: '#0000FF80'
+1 -3
View File
@@ -7,9 +7,7 @@ import { Browser, JSHandle, Page } from '@playwright/test';
import { deepStrictEqual, strictEqual } from 'assert';
import type { IRenderDimensions as IRenderDimensionsInternal } from '../../src/browser/renderer/shared/Types';
import type { IRenderService } from '../../src/browser/services/Services';
import type { IDisposable } from '../../src/common/Types';
import type { IMarker } from '../../src/common/buffer/Types';
import type { ICoreTerminal } from '../../src/common/CoreTerminal';
import type { ICoreTerminal, IDisposable, IMarker } from '../../src/common/Types';
import * as playwright from '@playwright/test';
import { IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, IRenderDimensions, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm';