mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Remove precedingCodepoint - use precedingJoinState instead
repeatPrecedingCharacter (for REP sequence) changed to look at the grapheme cluser to the "left" in the BufferLine, and repeat that. This is an extension of the xterm behavior, and changes the semantics of REP, so is not fully compatible. However, the new behavior is cleaner and saner. Moved precedingJoinState property to EscapeSequenceParser.
This commit is contained in:
+22
-26
@@ -126,10 +126,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
protected _windowTitleStack: string[] = [];
|
||||
protected _iconNameStack: string[] = [];
|
||||
|
||||
// Cached result of getJoinProperties(..., precedingCodepoint).
|
||||
// Only valid if precedingCodepoint !== 0
|
||||
public precedingJoinState: number = -1; // UnicodeJoinProperties
|
||||
|
||||
private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();
|
||||
public getAttrData(): IAttributeData { return this._curAttrData; }
|
||||
private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();
|
||||
@@ -521,8 +517,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
bufferRow.setCellFromCodePoint(this._activeBuffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
|
||||
let precedingJoinState = this._parser.precedingCodepoint === 0 ? 0
|
||||
: this.precedingJoinState;
|
||||
let precedingJoinState = this._parser.precedingJoinState;
|
||||
for (let pos = start; pos < end; ++pos) {
|
||||
code = data[pos];
|
||||
|
||||
@@ -636,18 +631,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
this.precedingJoinState = precedingJoinState;
|
||||
// store last char in Parser.precedingCodepoint for REP to work correctly
|
||||
// This needs to check whether:
|
||||
// - combining: only base char gets carried on (bug in xterm?)
|
||||
if (end - start > 0) {
|
||||
bufferRow.loadCell(this._activeBuffer.x - 1, this._workCell);
|
||||
if (this._workCell.isCombined()) {
|
||||
this._parser.precedingCodepoint = this._workCell.getChars().charCodeAt(0);
|
||||
} else {
|
||||
this._parser.precedingCodepoint = this._workCell.content;
|
||||
}
|
||||
}
|
||||
this._parser.precedingJoinState = precedingJoinState;
|
||||
|
||||
// handle wide chars: reset cell to the right if it is second cell of a wide char
|
||||
if (this._activeBuffer.x < cols && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x) === 0 && !bufferRow.hasContent(this._activeBuffer.x)) {
|
||||
@@ -1593,9 +1577,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* If the character preceding REP is a control function or part of a control function,
|
||||
* the effect of REP is not defined by this Standard.
|
||||
*
|
||||
* Since we propagate the terminal as xterm-256color we have to follow xterm's behavior:
|
||||
* - fullwidth + surrogate chars are ignored
|
||||
* - for combining chars only the base char gets repeated
|
||||
* We extend xterm's behavior to allow repeating entire grapheme clusters.
|
||||
* This isn't 100% xterm-compatible, but it seems saner and more useful.
|
||||
* - text attrs are applied normally
|
||||
* - wrap around is respected
|
||||
* - any valid sequence resets the carried forward char
|
||||
@@ -1609,16 +1592,29 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* (NOOP for any other sequence in between or NON ASCII characters).
|
||||
*/
|
||||
public repeatPrecedingCharacter(params: IParams): boolean {
|
||||
if (!this._parser.precedingCodepoint) {
|
||||
const joinState = this._parser.precedingJoinState;
|
||||
if (!joinState) {
|
||||
return true;
|
||||
}
|
||||
// call print to insert the chars and handle correct wrapping
|
||||
const length = params.params[0] || 1;
|
||||
const data = new Uint32Array(length);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
data[i] = this._parser.precedingCodepoint;
|
||||
const chWidth = UnicodeService.extractWidth(joinState);
|
||||
const x = this._activeBuffer.x - chWidth;
|
||||
const bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
|
||||
const text = bufferRow.getString(x);
|
||||
const data = new Uint32Array(text.length * length);
|
||||
let idata = 0;
|
||||
for (let itext = 0; itext < text.length; ) {
|
||||
const ch = text.codePointAt(itext) || 0;
|
||||
data[idata++] = ch;
|
||||
itext += ch > 0xffff ? 2 : 1;
|
||||
}
|
||||
this.print(data, 0, data.length);
|
||||
let tlength = idata;
|
||||
for (let i = 1; i < length; ++i) {
|
||||
data.copyWithin(tlength, 0, idata);
|
||||
tlength += idata;
|
||||
}
|
||||
this.print(data, 0, tlength);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||
export class EscapeSequenceParser extends Disposable implements IEscapeSequenceParser {
|
||||
public initialState: number;
|
||||
public currentState: number;
|
||||
public precedingCodepoint: number;
|
||||
public precedingJoinState: number; // UnicodeJoinProperties
|
||||
|
||||
// buffers over several parse calls
|
||||
protected _params: Params;
|
||||
@@ -271,7 +271,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._params = new Params(); // defaults to 32 storable params/subparams
|
||||
this._params.addParam(0); // ZDM
|
||||
this._collect = 0;
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
|
||||
// set default fallback handlers and handler lookup containers
|
||||
this._printHandlerFb = (data, start, end): void => { };
|
||||
@@ -448,7 +448,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._params.reset();
|
||||
this._params.addParam(0); // ZDM
|
||||
this._collect = 0;
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
// 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
|
||||
@@ -610,7 +610,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
// cleanup before continuing with the main sync loop
|
||||
this._parseStack.state = ParserStackType.NONE;
|
||||
start = this._parseStack.chunkPos + 1;
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
this.currentState = this._parseStack.transition & TableAccess.TRANSITION_STATE_MASK;
|
||||
}
|
||||
}
|
||||
@@ -653,7 +653,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
case ParserAction.EXECUTE:
|
||||
if (this._executeHandlers[code]) this._executeHandlers[code]();
|
||||
else this._executeHandlerFb(code);
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
break;
|
||||
case ParserAction.IGNORE:
|
||||
break;
|
||||
@@ -688,7 +688,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
if (j < 0) {
|
||||
this._csiHandlerFb(this._collect << 8 | code, this._params);
|
||||
}
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
break;
|
||||
case ParserAction.PARAM:
|
||||
// inner loop: digits (0x30 - 0x39) and ; (0x3b) and : (0x3a)
|
||||
@@ -727,7 +727,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
if (jj < 0) {
|
||||
this._escHandlerFb(this._collect << 8 | code);
|
||||
}
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
break;
|
||||
case ParserAction.CLEAR:
|
||||
this._params.reset();
|
||||
@@ -758,7 +758,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._params.reset();
|
||||
this._params.addParam(0); // ZDM
|
||||
this._collect = 0;
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
break;
|
||||
case ParserAction.OSC_START:
|
||||
this._oscParser.start();
|
||||
@@ -783,7 +783,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._params.reset();
|
||||
this._params.addParam(0); // ZDM
|
||||
this._collect = 0;
|
||||
this.precedingCodepoint = 0;
|
||||
this.precedingJoinState = 0;
|
||||
break;
|
||||
}
|
||||
this.currentState = transition & TableAccess.TRANSITION_STATE_MASK;
|
||||
|
||||
Vendored
+5
-4
@@ -146,11 +146,12 @@ export type PrintFallbackHandlerType = PrintHandlerType;
|
||||
*/
|
||||
export interface IEscapeSequenceParser extends IDisposable {
|
||||
/**
|
||||
* Preceding codepoint to get REP working correctly.
|
||||
* This must be set by the print handler as last action.
|
||||
* It gets reset by the parser for any valid sequence beside REP itself.
|
||||
* Preceding grapheme-join-state.
|
||||
* Used for joining graphame clusters across calls to `print`.
|
||||
* Also used by REP to check if repeat a character is allowed.
|
||||
* It gets reset by the parser for any valid sequence besides text.
|
||||
*/
|
||||
precedingCodepoint: number;
|
||||
precedingJoinState: number; // More specifically: UnicodeJoinProperties
|
||||
|
||||
/**
|
||||
* Reset the parser to its initial state (handlers are kept).
|
||||
|
||||
@@ -272,18 +272,18 @@ describe('InputHandler Integration Tests', function(): void {
|
||||
`);
|
||||
await pollFor(page, () => getLinesAsArray(4), ['##', '##', '##', '######']);
|
||||
await pollFor(page, () => getCursor(), { col: 6, row: 3 });
|
||||
// should not repeat on fullwidth chars
|
||||
// do repeat on fullwidth chars (change from xterm)
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window.term.write('¥\x1b[10b');
|
||||
`);
|
||||
await pollFor(page, () => getLinesAsArray(1), ['¥']);
|
||||
// should repeat only base char of combining
|
||||
await pollFor(page, () => getLinesAsArray(1), ['¥¥¥¥¥¥¥¥¥¥¥']);
|
||||
// change from xterm: repeat grapheme cluster
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window.term.write('e\u0301\x1b[5b');
|
||||
window.term.write('e\u0301\x1b[2b');
|
||||
`);
|
||||
await pollFor(page, () => getLinesAsArray(1), ['e\u0301eeeee']);
|
||||
await pollFor(page, () => getLinesAsArray(1), ['e\u0301e\u0301e\u0301']);
|
||||
// should wrap correctly
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
|
||||
Reference in New Issue
Block a user