Merge branch 'master' into options1
This commit is contained in:
@@ -53,6 +53,7 @@ export class AccessibilityManager extends Disposable {
|
||||
) {
|
||||
super();
|
||||
this._accessibilityTreeRoot = document.createElement('div');
|
||||
this._accessibilityTreeRoot.setAttribute('role', 'document');
|
||||
this._accessibilityTreeRoot.classList.add('xterm-accessibility');
|
||||
|
||||
this._rowContainer = document.createElement('div');
|
||||
|
||||
@@ -732,80 +732,78 @@ describe('Terminal', () => {
|
||||
});
|
||||
|
||||
describe('unicode - surrogates', () => {
|
||||
it('2 characters per cell', async function (): Promise<void> {
|
||||
this.timeout(10000); // This is needed because istanbul patches code and slows it down
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
await term.writeP(high + String.fromCharCode(i));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
it('2 characters at last cell', async () => {
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
term.buffer.x = term.cols - 1;
|
||||
await term.writeP(high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
it('2 characters per cell over line end with autowrap', async function (): Promise<void> {
|
||||
this.timeout(10000);
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
term.buffer.x = term.cols - 1;
|
||||
|
||||
await term.writeP('a' + high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
it('2 characters per cell over line end without autowrap', async function (): Promise<void> {
|
||||
this.timeout(10000);
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
term.buffer.x = term.cols - 1;
|
||||
await term.writeP('\x1b[?7l'); // Disable wraparound mode
|
||||
const width = wcwidth((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000);
|
||||
if (width !== 1) {
|
||||
continue;
|
||||
for (let i = 0xDC00; i <= 0xDCF0; i += 0x10) {
|
||||
const range = `0x${i.toString(16).toUpperCase()}-0x${(i + 0xF).toString(16).toUpperCase()}`;
|
||||
it(`${range}: 2 characters per cell`, async function (): Promise<void> {
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let j = i; j <= i + 0xF; j++) {
|
||||
await term.writeP(high + String.fromCharCode(j));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(j));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
await term.writeP('a' + high + String.fromCharCode(i));
|
||||
// auto wraparound mode should cut off the rest of the line
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
it('splitted surrogates', async function (): Promise<void> {
|
||||
this.timeout(10000);
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
await term.writeP(high + String.fromCharCode(i));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
});
|
||||
it(`${range}: 2 characters at last cell`, async () => {
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
term.buffer.x = term.cols - 1;
|
||||
for (let j = i; j <= i + 0xF; j++) {
|
||||
await term.writeP(high + String.fromCharCode(j));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars(), high + String.fromCharCode(j));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
it(`${range}: 2 characters per cell over line end with autowrap`, async function (): Promise<void> {
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let j = i; j <= i + 0xF; j++) {
|
||||
term.buffer.x = term.cols - 1;
|
||||
await term.writeP('a' + high + String.fromCharCode(j));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), high + String.fromCharCode(j));
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
it(`${range}: 2 characters per cell over line end without autowrap`, async function (): Promise<void> {
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let j = i; j <= i + 0xF; j++) {
|
||||
term.buffer.x = term.cols - 1;
|
||||
await term.writeP('\x1b[?7l'); // Disable wraparound mode
|
||||
const width = wcwidth((0xD800 - 0xD800) * 0x400 + j - 0xDC00 + 0x10000);
|
||||
if (width !== 1) {
|
||||
continue;
|
||||
}
|
||||
await term.writeP('a' + high + String.fromCharCode(j));
|
||||
// auto wraparound mode should cut off the rest of the line
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), high + String.fromCharCode(j));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
it(`${range}: splitted surrogates`, async function (): Promise<void> {
|
||||
const high = String.fromCharCode(0xD800);
|
||||
const cell = new CellData();
|
||||
for (let j = i; j <= i + 0xF; j++) {
|
||||
await term.writeP(high + String.fromCharCode(j));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(j));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('unicode - combining characters', () => {
|
||||
|
||||
+37
-25
@@ -164,6 +164,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
// Setup InputHandler listeners
|
||||
this.register(this._inputHandler.onRequestBell(() => this.bell()));
|
||||
this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)));
|
||||
this.register(this._inputHandler.onRequestSendFocus(() => this._reportFocus()));
|
||||
this.register(this._inputHandler.onRequestReset(() => this.reset()));
|
||||
this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type)));
|
||||
this.register(this._inputHandler.onAnsiColorChange((event) => this._changeAnsiColor(event)));
|
||||
@@ -698,8 +699,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
},
|
||||
wheel: (ev: WheelEvent) => {
|
||||
sendEvent(ev);
|
||||
ev.preventDefault();
|
||||
return this.cancel(ev);
|
||||
return this.cancel(ev, true);
|
||||
},
|
||||
mousedrag: (ev: MouseEvent) => {
|
||||
// deal only with move while a button is held
|
||||
@@ -794,33 +794,31 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
}));
|
||||
|
||||
this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => {
|
||||
if (!requestedEvents.wheel) {
|
||||
// do nothing, if app side handles wheel itself
|
||||
if (requestedEvents.wheel) return;
|
||||
|
||||
if (!this.buffer.hasScrollback) {
|
||||
// Convert wheel events into up/down events when the buffer does not have scrollback, this
|
||||
// enables scrolling in apps hosted in the alt buffer such as vim or tmux.
|
||||
if (!this.buffer.hasScrollback) {
|
||||
const amount = this.viewport!.getLinesScrolled(ev);
|
||||
const amount = this.viewport!.getLinesScrolled(ev);
|
||||
|
||||
// Do nothing if there's no vertical scroll
|
||||
if (amount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct and send sequences
|
||||
const sequence = C0.ESC + (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
|
||||
let data = '';
|
||||
for (let i = 0; i < Math.abs(amount); i++) {
|
||||
data += sequence;
|
||||
}
|
||||
this.coreService.triggerDataEvent(data, true);
|
||||
// Do nothing if there's no vertical scroll
|
||||
if (amount === 0) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, { passive: true }));
|
||||
|
||||
// allow wheel scrolling in
|
||||
// the shell for example
|
||||
this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => {
|
||||
if (requestedEvents.wheel) return;
|
||||
// Construct and send sequences
|
||||
const sequence = C0.ESC + (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
|
||||
let data = '';
|
||||
for (let i = 0; i < Math.abs(amount); i++) {
|
||||
data += sequence;
|
||||
}
|
||||
this.coreService.triggerDataEvent(data, true);
|
||||
return this.cancel(ev, true);
|
||||
}
|
||||
|
||||
// normal viewport scrolling
|
||||
// conditionally stop event, if the viewport still had rows to scroll within
|
||||
if (!this.viewport!.onWheel(ev)) {
|
||||
return this.cancel(ev);
|
||||
}
|
||||
@@ -1180,7 +1178,9 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
* @param ev The input event to be handled.
|
||||
*/
|
||||
protected _inputEvent(ev: InputEvent): boolean {
|
||||
if (ev.data && ev.inputType === 'insertText') {
|
||||
// Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to
|
||||
// support reading out character input which can doubling up input characters
|
||||
if (ev.data && ev.inputType === 'insertText' && !this.optionsService.options.screenReaderMode) {
|
||||
if (this._keyPressHandled) {
|
||||
return false;
|
||||
}
|
||||
@@ -1290,6 +1290,18 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.viewport?.syncScrollArea();
|
||||
}
|
||||
|
||||
public clearTextureAtlas(): void {
|
||||
this._renderService?.clearTextureAtlas();
|
||||
}
|
||||
|
||||
private _reportFocus(): void {
|
||||
if (this.element?.classList.contains('focus')) {
|
||||
this.coreService.triggerDataEvent(C0.ESC + '[I');
|
||||
} else {
|
||||
this.coreService.triggerDataEvent(C0.ESC + '[O');
|
||||
}
|
||||
}
|
||||
|
||||
private _reportWindowsOptions(type: WindowsOptionsReportType): void {
|
||||
if (!this._renderService) {
|
||||
return;
|
||||
|
||||
@@ -195,6 +195,9 @@ export class MockTerminal implements ITerminal {
|
||||
public reset(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public clearTextureAtlas(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public refresh(start: number, end: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
@@ -374,6 +377,9 @@ export class MockRenderService implements IRenderService {
|
||||
public refreshRows(start: number, end: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public clearTextureAtlas(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public resize(cols: number, rows: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -79,6 +79,7 @@ export interface IPublicTerminal extends IDisposable {
|
||||
write(data: string | Uint8Array, callback?: () => void): void;
|
||||
paste(data: string): void;
|
||||
refresh(start: number, end: number): void;
|
||||
clearTextureAtlas(): void;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
|
||||
+14
-9
@@ -8,6 +8,8 @@ import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||
import { IColorSet, IViewport } from 'browser/Types';
|
||||
import { ICharSizeService, IRenderService } from 'browser/services/Services';
|
||||
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
|
||||
const FALLBACK_SCROLL_BAR_WIDTH = 15;
|
||||
|
||||
@@ -18,12 +20,15 @@ const FALLBACK_SCROLL_BAR_WIDTH = 15;
|
||||
export class Viewport extends Disposable implements IViewport {
|
||||
public scrollBarWidth: number = 0;
|
||||
private _currentRowHeight: number = 0;
|
||||
private _currentScaledCellHeight: number = 0;
|
||||
private _lastRecordedBufferLength: number = 0;
|
||||
private _lastRecordedViewportHeight: number = 0;
|
||||
private _lastRecordedBufferHeight: number = 0;
|
||||
private _lastTouchY: number = 0;
|
||||
private _lastScrollTop: number = 0;
|
||||
private _lastHadScrollBar: boolean = false;
|
||||
private _activeBuffer: IBuffer;
|
||||
private _renderDimensions: IRenderDimensions;
|
||||
|
||||
// Stores a partial line amount when scrolling, this is used to keep track of how much of a line
|
||||
// is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a
|
||||
@@ -51,6 +56,12 @@ export class Viewport extends Disposable implements IViewport {
|
||||
this._lastHadScrollBar = true;
|
||||
this.register(addDisposableDomListener(this._viewportElement, 'scroll', this._onScroll.bind(this)));
|
||||
|
||||
// Track properties used in performance critical code manually to avoid using slow getters
|
||||
this._activeBuffer = this._bufferService.buffer;
|
||||
this.register(this._bufferService.buffers.onBufferActivate(e => this._activeBuffer = e.activeBuffer));
|
||||
this._renderDimensions = this._renderService.dimensions;
|
||||
this.register(this._renderService.onDimensionsChange(e => this._renderDimensions = e));
|
||||
|
||||
// Perform this async to ensure the ICharSizeService is ready.
|
||||
setTimeout(() => this.syncScrollArea(), 0);
|
||||
}
|
||||
@@ -79,6 +90,7 @@ export class Viewport extends Disposable implements IViewport {
|
||||
private _innerRefresh(): void {
|
||||
if (this._charSizeService.height > 0) {
|
||||
this._currentRowHeight = this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio;
|
||||
this._currentScaledCellHeight = this._renderService.dimensions.scaledCellHeight;
|
||||
this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;
|
||||
const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderService.dimensions.canvasHeight);
|
||||
if (this._lastRecordedBufferHeight !== newBufferHeight) {
|
||||
@@ -126,20 +138,13 @@ export class Viewport extends Disposable implements IViewport {
|
||||
}
|
||||
|
||||
// If the buffer position doesn't match last scroll top
|
||||
const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;
|
||||
if (this._lastScrollTop !== newScrollTop) {
|
||||
this._refresh(immediate);
|
||||
return;
|
||||
}
|
||||
|
||||
// If element's scroll top changed, this can happen when hiding the element
|
||||
if (this._lastScrollTop !== this._viewportElement.scrollTop) {
|
||||
if (this._lastScrollTop !== this._activeBuffer.ydisp * this._currentRowHeight) {
|
||||
this._refresh(immediate);
|
||||
return;
|
||||
}
|
||||
|
||||
// If row height changed
|
||||
if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
|
||||
if (this._renderDimensions.scaledCellHeight !== this._currentScaledCellHeight) {
|
||||
this._refresh(immediate);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -225,6 +225,9 @@ export class Terminal implements ITerminalApi {
|
||||
public reset(): void {
|
||||
this._core.reset();
|
||||
}
|
||||
public clearTextureAtlas(): void {
|
||||
this._core.clearTextureAtlas();
|
||||
}
|
||||
public loadAddon(addon: ITerminalAddon): void {
|
||||
return this._addonManager.loadAddon(this, addon);
|
||||
}
|
||||
|
||||
@@ -138,6 +138,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
|
||||
public abstract reset(): void;
|
||||
|
||||
public clearTextureAtlas(): void {
|
||||
this._charAtlas?.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills 1+ cells completely. This uses the existing fillStyle on the context.
|
||||
* @param x The column to start at.
|
||||
|
||||
@@ -55,7 +55,6 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
'block': this._renderBlockCursor.bind(this),
|
||||
'underline': this._renderUnderlineCursor.bind(this)
|
||||
};
|
||||
// TODO: Consider initial options? Maybe onOptionsChanged should be called at the end of open?
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -80,26 +79,18 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
|
||||
public reset(): void {
|
||||
this._clearCursor();
|
||||
if (this._cursorBlinkStateManager) {
|
||||
this._cursorBlinkStateManager.dispose();
|
||||
this._cursorBlinkStateManager = undefined;
|
||||
this.onOptionsChanged();
|
||||
}
|
||||
this._cursorBlinkStateManager?.restartBlinkAnimation();
|
||||
this.onOptionsChanged();
|
||||
}
|
||||
|
||||
public onBlur(): void {
|
||||
if (this._cursorBlinkStateManager) {
|
||||
this._cursorBlinkStateManager.pause();
|
||||
}
|
||||
this._cursorBlinkStateManager?.pause();
|
||||
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
|
||||
}
|
||||
|
||||
public onFocus(): void {
|
||||
if (this._cursorBlinkStateManager) {
|
||||
this._cursorBlinkStateManager.resume();
|
||||
} else {
|
||||
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
|
||||
}
|
||||
this._cursorBlinkStateManager?.resume();
|
||||
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
|
||||
}
|
||||
|
||||
public onOptionsChanged(): void {
|
||||
@@ -119,9 +110,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
|
||||
public onCursorMove(): void {
|
||||
if (this._cursorBlinkStateManager) {
|
||||
this._cursorBlinkStateManager.restartBlinkAnimation();
|
||||
}
|
||||
this._cursorBlinkStateManager?.restartBlinkAnimation();
|
||||
}
|
||||
|
||||
public onGridChanged(startRow: number, endRow: number): void {
|
||||
@@ -313,6 +302,7 @@ class CursorBlinkStateManager {
|
||||
// Clear any existing interval
|
||||
if (this._blinkInterval) {
|
||||
window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
|
||||
// Setup the initial timeout which will hide the cursor, this is done before
|
||||
|
||||
@@ -149,6 +149,12 @@ export class Renderer extends Disposable implements IRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
public clearTextureAtlas(): void {
|
||||
for (const layer of this._renderLayers) {
|
||||
layer.clearTextureAtlas();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the character and canvas dimensions.
|
||||
*/
|
||||
|
||||
Vendored
+6
@@ -52,6 +52,7 @@ export interface IRenderer extends IDisposable {
|
||||
onOptionsChanged(): void;
|
||||
clear(): void;
|
||||
renderRows(start: number, end: number): void;
|
||||
clearTextureAtlas?(): void;
|
||||
}
|
||||
|
||||
export interface IRenderLayer extends IDisposable {
|
||||
@@ -100,4 +101,9 @@ export interface IRenderLayer extends IDisposable {
|
||||
* Clear the state of the render layer.
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* Clears the texture atlas.
|
||||
*/
|
||||
clearTextureAtlas(): void;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ export abstract class BaseCharAtlas implements IDisposable {
|
||||
*/
|
||||
private _doWarmUp(): void { }
|
||||
|
||||
public clear(): void { }
|
||||
|
||||
/**
|
||||
* Called when we start drawing a new frame.
|
||||
*
|
||||
|
||||
@@ -119,6 +119,16 @@ export class DynamicCharAtlas extends BaseCharAtlas {
|
||||
this._drawToCacheCount = 0;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
if (this._cacheMap.size > 0) {
|
||||
const capacity = this._width * this._height;
|
||||
this._cacheMap = new LRUMap(capacity);
|
||||
this._cacheMap.prealloc(capacity);
|
||||
}
|
||||
this._cacheCtx.clearRect(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
this._tmpCtx.clearRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight);
|
||||
}
|
||||
|
||||
public draw(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
glyph: IGlyphIdentifier,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags,
|
||||
import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { IBufferLine } from 'common/Types';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { MockOptionsService } from 'common/TestUtils.test';
|
||||
import { MockCoreService, MockOptionsService } from 'common/TestUtils.test';
|
||||
import { css } from 'browser/Color';
|
||||
import { MockCharacterJoinerService } from 'browser/TestUtils.test';
|
||||
|
||||
@@ -21,30 +21,36 @@ describe('DomRendererRowFactory', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
dom = new jsdom.JSDOM('');
|
||||
rowFactory = new DomRendererRowFactory(dom.window.document, {
|
||||
background: css.toColor('#010101'),
|
||||
foreground: css.toColor('#020202'),
|
||||
ansi: [
|
||||
// dark:
|
||||
css.toColor('#2e3436'),
|
||||
css.toColor('#cc0000'),
|
||||
css.toColor('#4e9a06'),
|
||||
css.toColor('#c4a000'),
|
||||
css.toColor('#3465a4'),
|
||||
css.toColor('#75507b'),
|
||||
css.toColor('#06989a'),
|
||||
css.toColor('#d3d7cf'),
|
||||
// bright:
|
||||
css.toColor('#555753'),
|
||||
css.toColor('#ef2929'),
|
||||
css.toColor('#8ae234'),
|
||||
css.toColor('#fce94f'),
|
||||
css.toColor('#729fcf'),
|
||||
css.toColor('#ad7fa8'),
|
||||
css.toColor('#34e2e2'),
|
||||
css.toColor('#eeeeec')
|
||||
]
|
||||
} as any, new MockCharacterJoinerService(), new MockOptionsService({ drawBoldTextInBrightColors: true }));
|
||||
rowFactory = new DomRendererRowFactory(
|
||||
dom.window.document,
|
||||
{
|
||||
background: css.toColor('#010101'),
|
||||
foreground: css.toColor('#020202'),
|
||||
ansi: [
|
||||
// dark:
|
||||
css.toColor('#2e3436'),
|
||||
css.toColor('#cc0000'),
|
||||
css.toColor('#4e9a06'),
|
||||
css.toColor('#c4a000'),
|
||||
css.toColor('#3465a4'),
|
||||
css.toColor('#75507b'),
|
||||
css.toColor('#06989a'),
|
||||
css.toColor('#d3d7cf'),
|
||||
// bright:
|
||||
css.toColor('#555753'),
|
||||
css.toColor('#ef2929'),
|
||||
css.toColor('#8ae234'),
|
||||
css.toColor('#fce94f'),
|
||||
css.toColor('#729fcf'),
|
||||
css.toColor('#ad7fa8'),
|
||||
css.toColor('#34e2e2'),
|
||||
css.toColor('#eeeeec')
|
||||
]
|
||||
} as any,
|
||||
new MockCharacterJoinerService(),
|
||||
new MockOptionsService({ drawBoldTextInBrightColors: true }),
|
||||
new MockCoreService()
|
||||
);
|
||||
lineData = createEmptyLineData(2);
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IBufferLine } from 'common/Types';
|
||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||
import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { IOptionsService } from 'common/services/Services';
|
||||
import { ICoreService, IOptionsService } from 'common/services/Services';
|
||||
import { color, rgba } from 'browser/Color';
|
||||
import { IColorSet, IColor } from 'browser/Types';
|
||||
import { ICharacterJoinerService } from 'browser/services/Services';
|
||||
@@ -31,7 +31,8 @@ export class DomRendererRowFactory {
|
||||
private readonly _document: Document,
|
||||
private _colors: IColorSet,
|
||||
@ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@ICoreService private readonly _coreService: ICoreService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ export class DomRendererRowFactory {
|
||||
}
|
||||
}
|
||||
|
||||
if (isCursorRow && x === cursorX) {
|
||||
if (!this._coreService.isCursorHidden && isCursorRow && x === cursorX) {
|
||||
charElement.classList.add(CURSOR_CLASS);
|
||||
|
||||
if (cursorBlink) {
|
||||
|
||||
@@ -168,6 +168,11 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
}
|
||||
|
||||
public clearTextureAtlas(): void {
|
||||
this._renderer?.clearTextureAtlas?.();
|
||||
this._fullRefresh();
|
||||
}
|
||||
|
||||
public setColors(colors: IColorSet): void {
|
||||
this._renderer.setColors(colors);
|
||||
this._fullRefresh();
|
||||
|
||||
@@ -53,6 +53,7 @@ export interface IRenderService extends IDisposable {
|
||||
dimensions: IRenderDimensions;
|
||||
|
||||
refreshRows(start: number, end: number): void;
|
||||
clearTextureAtlas(): void;
|
||||
resize(cols: number, rows: number): void;
|
||||
changeOptions(): void;
|
||||
setRenderer(renderer: IRenderer): void;
|
||||
|
||||
@@ -77,54 +77,54 @@ describe('InputHandler', () => {
|
||||
optionsService.options.scrollback = 1;
|
||||
bufferService.reset();
|
||||
});
|
||||
it('SL (scrollLeft)', () => {
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[ @');
|
||||
it('SL (scrollLeft)', async () => {
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[ @');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '2345', '2345', '2345', '2345', '2345']);
|
||||
inputHandler.parseP('\x1b[0 @');
|
||||
await inputHandler.parseP('\x1b[0 @');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '345', '345', '345', '345', '345']);
|
||||
inputHandler.parseP('\x1b[2 @');
|
||||
await inputHandler.parseP('\x1b[2 @');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '5', '5', '5', '5', '5']);
|
||||
});
|
||||
it('SR (scrollRight)', () => {
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[ A');
|
||||
it('SR (scrollRight)', async () => {
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[ A');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
|
||||
inputHandler.parseP('\x1b[0 A');
|
||||
await inputHandler.parseP('\x1b[0 A');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']);
|
||||
inputHandler.parseP('\x1b[2 A');
|
||||
await inputHandler.parseP('\x1b[2 A');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
|
||||
});
|
||||
it('insertColumns (DECIC)', () => {
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[3;3H');
|
||||
inputHandler.parseP('\x1b[\'}');
|
||||
it('insertColumns (DECIC)', async () => {
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[3;3H');
|
||||
await inputHandler.parseP('\x1b[\'}');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
|
||||
bufferService.reset();
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[3;3H');
|
||||
inputHandler.parseP('\x1b[1\'}');
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[3;3H');
|
||||
await inputHandler.parseP('\x1b[1\'}');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
|
||||
bufferService.reset();
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[3;3H');
|
||||
inputHandler.parseP('\x1b[2\'}');
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[3;3H');
|
||||
await inputHandler.parseP('\x1b[2\'}');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
|
||||
});
|
||||
it('deleteColumns (DECDC)', () => {
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[3;3H');
|
||||
inputHandler.parseP('\x1b[\'~');
|
||||
it('deleteColumns (DECDC)', async () => {
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[3;3H');
|
||||
await inputHandler.parseP('\x1b[\'~');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '1245', '1245', '1245', '1245', '1245']);
|
||||
bufferService.reset();
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[3;3H');
|
||||
inputHandler.parseP('\x1b[1\'~');
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[3;3H');
|
||||
await inputHandler.parseP('\x1b[1\'~');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '1245', '1245', '1245', '1245', '1245']);
|
||||
bufferService.reset();
|
||||
inputHandler.parseP('12345'.repeat(6));
|
||||
inputHandler.parseP('\x1b[3;3H');
|
||||
inputHandler.parseP('\x1b[2\'~');
|
||||
await inputHandler.parseP('12345'.repeat(6));
|
||||
await inputHandler.parseP('\x1b[3;3H');
|
||||
await inputHandler.parseP('\x1b[2\'~');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '125', '125', '125', '125', '125']);
|
||||
});
|
||||
});
|
||||
@@ -137,41 +137,41 @@ describe('InputHandler', () => {
|
||||
bufferService.reset();
|
||||
});
|
||||
describe('reverseWraparound set', () => {
|
||||
it('should not reverse outside of scroll margins', () => {
|
||||
it('should not reverse outside of scroll margins', async () => {
|
||||
// prepare buffer content
|
||||
inputHandler.parseP('#####abcdefghijklmnopqrstuvwxy');
|
||||
await inputHandler.parseP('#####abcdefghijklmnopqrstuvwxy');
|
||||
assert.deepEqual(getLines(bufferService, 6), ['#####', 'abcde', 'fghij', 'klmno', 'pqrst', 'uvwxy']);
|
||||
assert.equal(bufferService.buffers.active.ydisp, 1);
|
||||
assert.equal(bufferService.buffers.active.x, 5);
|
||||
assert.equal(bufferService.buffers.active.y, 4);
|
||||
inputHandler.parseP(ttyBS.repeat(100));
|
||||
await inputHandler.parseP(ttyBS.repeat(100));
|
||||
assert.deepEqual(getLines(bufferService, 6), ['#####', 'abcde', 'fghij', 'klmno', 'pqrst', ' y']);
|
||||
|
||||
inputHandler.parseP('\x1b[?45h');
|
||||
inputHandler.parseP('uvwxy');
|
||||
await inputHandler.parseP('\x1b[?45h');
|
||||
await inputHandler.parseP('uvwxy');
|
||||
|
||||
// set top/bottom to 1/3 (0-based)
|
||||
inputHandler.parseP('\x1b[2;4r');
|
||||
await inputHandler.parseP('\x1b[2;4r');
|
||||
// place cursor below scroll bottom
|
||||
bufferService.buffers.active.x = 5;
|
||||
bufferService.buffers.active.y = 4;
|
||||
inputHandler.parseP(ttyBS.repeat(100));
|
||||
await inputHandler.parseP(ttyBS.repeat(100));
|
||||
assert.deepEqual(getLines(bufferService, 6), ['#####', 'abcde', 'fghij', 'klmno', 'pqrst', ' ']);
|
||||
|
||||
inputHandler.parseP('uvwxy');
|
||||
await inputHandler.parseP('uvwxy');
|
||||
// place cursor within scroll margins
|
||||
bufferService.buffers.active.x = 5;
|
||||
bufferService.buffers.active.y = 3;
|
||||
inputHandler.parseP(ttyBS.repeat(100));
|
||||
await inputHandler.parseP(ttyBS.repeat(100));
|
||||
assert.deepEqual(getLines(bufferService, 6), ['#####', 'abcde', ' ', ' ', ' ', 'uvwxy']);
|
||||
assert.equal(bufferService.buffers.active.x, 0);
|
||||
assert.equal(bufferService.buffers.active.y, bufferService.buffers.active.scrollTop); // stops at 0, scrollTop
|
||||
|
||||
inputHandler.parseP('fghijklmnopqrst');
|
||||
await inputHandler.parseP('fghijklmnopqrst');
|
||||
// place cursor above scroll top
|
||||
bufferService.buffers.active.x = 5;
|
||||
bufferService.buffers.active.y = 0;
|
||||
inputHandler.parseP(ttyBS.repeat(100));
|
||||
await inputHandler.parseP(ttyBS.repeat(100));
|
||||
assert.deepEqual(getLines(bufferService, 6), ['#####', ' ', 'fghij', 'klmno', 'pqrst', 'uvwxy']);
|
||||
});
|
||||
});
|
||||
|
||||
+209
-229
@@ -20,6 +20,7 @@ import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService, LogLevelEnum } from 'common/services/Services';
|
||||
import { OscHandler } from 'common/parser/OscParser';
|
||||
import { DcsHandler } from 'common/parser/DcsParser';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
|
||||
/**
|
||||
* Map collect to glevel. Used in `selectCharset`.
|
||||
@@ -234,12 +235,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();
|
||||
private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();
|
||||
|
||||
private _activeBuffer: IBuffer;
|
||||
|
||||
private _onRequestBell = new EventEmitter<void>();
|
||||
public get onRequestBell(): IEvent<void> { return this._onRequestBell.event; }
|
||||
private _onRequestRefreshRows = new EventEmitter<number, number>();
|
||||
public get onRequestRefreshRows(): IEvent<number, number> { return this._onRequestRefreshRows.event; }
|
||||
private _onRequestReset = new EventEmitter<void>();
|
||||
public get onRequestReset(): IEvent<void> { return this._onRequestReset.event; }
|
||||
private _onRequestSendFocus = new EventEmitter<void>();
|
||||
public get onRequestSendFocus(): IEvent<void> { return this._onRequestSendFocus.event; }
|
||||
private _onRequestSyncScrollBar = new EventEmitter<void>();
|
||||
public get onRequestSyncScrollBar(): IEvent<void> { return this._onRequestSyncScrollBar.event; }
|
||||
private _onRequestWindowsOptionsReport = new EventEmitter<WindowsOptionsReportType>();
|
||||
@@ -282,6 +287,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
super();
|
||||
this.register(this._parser);
|
||||
|
||||
// Track properties used in performance critical code manually to avoid using slow getters
|
||||
this._activeBuffer = this._bufferService.buffer;
|
||||
this.register(this._bufferService.buffers.onBufferActivate(e => this._activeBuffer = e.activeBuffer));
|
||||
|
||||
/**
|
||||
* custom fallback handlers
|
||||
*/
|
||||
@@ -508,9 +517,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public parse(data: string | Uint8Array, promiseResult?: boolean): void | Promise<boolean> {
|
||||
let result: void | Promise<boolean>;
|
||||
let buffer = this._bufferService.buffer;
|
||||
let cursorStartX = buffer.x;
|
||||
let cursorStartY = buffer.y;
|
||||
let cursorStartX = this._activeBuffer.x;
|
||||
let cursorStartY = this._activeBuffer.y;
|
||||
let start = 0;
|
||||
const wasPaused = this._parseStack.paused;
|
||||
|
||||
@@ -569,8 +577,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
buffer = this._bufferService.buffer;
|
||||
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
||||
if (this._activeBuffer.x !== cursorStartX || this._activeBuffer.y !== cursorStartY) {
|
||||
this._onCursorMove.fire();
|
||||
}
|
||||
|
||||
@@ -581,20 +588,19 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public print(data: Uint32Array, start: number, end: number): void {
|
||||
let code: number;
|
||||
let chWidth: number;
|
||||
const buffer = this._bufferService.buffer;
|
||||
const charset = this._charsetService.charset;
|
||||
const screenReaderMode = this._optionsService.options.screenReaderMode;
|
||||
const cols = this._bufferService.cols;
|
||||
const wraparoundMode = this._coreService.decPrivateModes.wraparound;
|
||||
const insertMode = this._coreService.modes.insertMode;
|
||||
const curAttr = this._curAttrData;
|
||||
let bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
|
||||
let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
|
||||
|
||||
this._dirtyRowService.markDirty(buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
|
||||
// handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char
|
||||
if (buffer.x && end - start > 0 && bufferRow.getWidth(buffer.x - 1) === 2) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
if (this._activeBuffer.x && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x - 1) === 2) {
|
||||
bufferRow.setCellFromCodePoint(this._activeBuffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
|
||||
for (let pos = start; pos < end; ++pos) {
|
||||
@@ -619,17 +625,17 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
// insert combining char at last cursor position
|
||||
// buffer.x should never be 0 for a combining char
|
||||
// this._activeBuffer.x should never be 0 for a combining char
|
||||
// since they always follow a cell consuming char
|
||||
// therefore we can test for buffer.x to avoid overflow left
|
||||
if (!chWidth && buffer.x) {
|
||||
if (!bufferRow.getWidth(buffer.x - 1)) {
|
||||
// therefore we can test for this._activeBuffer.x to avoid overflow left
|
||||
if (!chWidth && this._activeBuffer.x) {
|
||||
if (!bufferRow.getWidth(this._activeBuffer.x - 1)) {
|
||||
// found empty cell after fullwidth, need to go 2 cells back
|
||||
// it is save to step 2 cells back here
|
||||
// since an empty cell is only set by fullwidth chars
|
||||
bufferRow.addCodepointToCell(buffer.x - 2, code);
|
||||
bufferRow.addCodepointToCell(this._activeBuffer.x - 2, code);
|
||||
} else {
|
||||
bufferRow.addCodepointToCell(buffer.x - 1, code);
|
||||
bufferRow.addCodepointToCell(this._activeBuffer.x - 1, code);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -637,31 +643,31 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// goto next line if ch would overflow
|
||||
// NOTE: To avoid costly width checks here,
|
||||
// the terminal does not allow a cols < 2.
|
||||
if (buffer.x + chWidth - 1 >= cols) {
|
||||
if (this._activeBuffer.x + chWidth - 1 >= cols) {
|
||||
// autowrap - DECAWM
|
||||
// automatically wraps to the beginning of the next line
|
||||
if (wraparoundMode) {
|
||||
// clear left over cells to the right
|
||||
while (buffer.x < cols) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
while (this._activeBuffer.x < cols) {
|
||||
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
buffer.x = 0;
|
||||
buffer.y++;
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._activeBuffer.x = 0;
|
||||
this._activeBuffer.y++;
|
||||
if (this._activeBuffer.y === this._activeBuffer.scrollBottom + 1) {
|
||||
this._activeBuffer.y--;
|
||||
this._bufferService.scroll(this._eraseAttrData(), true);
|
||||
} else {
|
||||
if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
if (this._activeBuffer.y >= this._bufferService.rows) {
|
||||
this._activeBuffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
// The line already exists (eg. the initial viewport), mark it as a
|
||||
// wrapped line
|
||||
buffer.lines.get(buffer.ybase + buffer.y)!.isWrapped = true;
|
||||
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true;
|
||||
}
|
||||
// row changed, get it again
|
||||
bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
|
||||
bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
|
||||
} else {
|
||||
buffer.x = cols - 1;
|
||||
this._activeBuffer.x = cols - 1;
|
||||
if (chWidth === 2) {
|
||||
// FIXME: check for xterm behavior
|
||||
// What to do here? We got a wide char that does not fit into last cell
|
||||
@@ -673,7 +679,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// insert mode: move characters to right
|
||||
if (insertMode) {
|
||||
// right shift cells according to the width
|
||||
bufferRow.insertCells(buffer.x, chWidth, buffer.getNullCell(curAttr), curAttr);
|
||||
bufferRow.insertCells(this._activeBuffer.x, chWidth, this._activeBuffer.getNullCell(curAttr), curAttr);
|
||||
// test last cell - since the last cell has only room for
|
||||
// a halfwidth char any fullwidth shifted there is lost
|
||||
// and will be set to empty cell
|
||||
@@ -683,15 +689,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
// write current char to buffer and advance cursor
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, code, chWidth, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
|
||||
// fullwidth char - also set next cell to placeholder stub and advance cursor
|
||||
// for graphemes bigger than fullwidth we can simply loop to zero
|
||||
// we already made sure above, that buffer.x + chWidth will not overflow right
|
||||
// we already made sure above, that this._activeBuffer.x + chWidth will not overflow right
|
||||
if (chWidth > 0) {
|
||||
while (--chWidth) {
|
||||
// other than a regular empty cell a cell following a wide char has no width
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 0, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -700,7 +706,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// - fullwidth + surrogates: reset
|
||||
// - combining: only base char gets carried on (bug in xterm?)
|
||||
if (end - start > 0) {
|
||||
bufferRow.loadCell(buffer.x - 1, this._workCell);
|
||||
bufferRow.loadCell(this._activeBuffer.x - 1, this._workCell);
|
||||
if (this._workCell.getWidth() === 2 || this._workCell.getCode() > 0xFFFF) {
|
||||
this._parser.precedingCodepoint = 0;
|
||||
} else if (this._workCell.isCombined()) {
|
||||
@@ -711,11 +717,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
// handle wide chars: reset cell to the right if it is second cell of a wide char
|
||||
if (buffer.x < cols && end - start > 0 && bufferRow.getWidth(buffer.x) === 0 && !bufferRow.hasContent(buffer.x)) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
if (this._activeBuffer.x < cols && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x) === 0 && !bufferRow.hasContent(this._activeBuffer.x)) {
|
||||
bufferRow.setCellFromCodePoint(this._activeBuffer.x, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
|
||||
this._dirtyRowService.markDirty(buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -779,25 +785,22 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y C0 FF "Form Feed" "\f, \x0C" "Treated as LF."
|
||||
*/
|
||||
public lineFeed(): boolean {
|
||||
// make buffer local for faster access
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
this._dirtyRowService.markDirty(buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
if (this._optionsService.options.convertEol) {
|
||||
buffer.x = 0;
|
||||
this._activeBuffer.x = 0;
|
||||
}
|
||||
buffer.y++;
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._activeBuffer.y++;
|
||||
if (this._activeBuffer.y === this._activeBuffer.scrollBottom + 1) {
|
||||
this._activeBuffer.y--;
|
||||
this._bufferService.scroll(this._eraseAttrData());
|
||||
} else if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
} else if (this._activeBuffer.y >= this._bufferService.rows) {
|
||||
this._activeBuffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
// If the end of the line is hit, prevent this action from wrapping around to the next line.
|
||||
if (buffer.x >= this._bufferService.cols) {
|
||||
buffer.x--;
|
||||
if (this._activeBuffer.x >= this._bufferService.cols) {
|
||||
this._activeBuffer.x--;
|
||||
}
|
||||
this._dirtyRowService.markDirty(buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
|
||||
this._onLineFeed.fire();
|
||||
return true;
|
||||
@@ -810,7 +813,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y C0 CR "Carriage Return" "\r, \x0D" "Move the cursor to the beginning of the row."
|
||||
*/
|
||||
public carriageReturn(): boolean {
|
||||
this._bufferService.buffer.x = 0;
|
||||
this._activeBuffer.x = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -826,13 +829,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* with the cursor, thus at the home position (top-leftmost cell) this has no effect.
|
||||
*/
|
||||
public backspace(): boolean {
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
// reverse wrap-around is disabled
|
||||
if (!this._coreService.decPrivateModes.reverseWraparound) {
|
||||
this._restrictCursor();
|
||||
if (buffer.x > 0) {
|
||||
buffer.x--;
|
||||
if (this._activeBuffer.x > 0) {
|
||||
this._activeBuffer.x--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -842,8 +843,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// to be at x=cols to be able to address the last cell of a row by BS
|
||||
this._restrictCursor(this._bufferService.cols);
|
||||
|
||||
if (buffer.x > 0) {
|
||||
buffer.x--;
|
||||
if (this._activeBuffer.x > 0) {
|
||||
this._activeBuffer.x--;
|
||||
} else {
|
||||
/**
|
||||
* reverse wrap-around handling:
|
||||
@@ -853,21 +854,21 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* - cannot peek into scrollbuffer
|
||||
* - any cursor movement sequence keeps working as expected
|
||||
*/
|
||||
if (buffer.x === 0
|
||||
&& buffer.y > buffer.scrollTop
|
||||
&& buffer.y <= buffer.scrollBottom
|
||||
&& buffer.lines.get(buffer.ybase + buffer.y)?.isWrapped)
|
||||
if (this._activeBuffer.x === 0
|
||||
&& this._activeBuffer.y > this._activeBuffer.scrollTop
|
||||
&& this._activeBuffer.y <= this._activeBuffer.scrollBottom
|
||||
&& this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)?.isWrapped)
|
||||
{
|
||||
buffer.lines.get(buffer.ybase + buffer.y)!.isWrapped = false;
|
||||
buffer.y--;
|
||||
buffer.x = this._bufferService.cols - 1;
|
||||
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false;
|
||||
this._activeBuffer.y--;
|
||||
this._activeBuffer.x = this._bufferService.cols - 1;
|
||||
// find last taken cell - last cell can have 3 different states:
|
||||
// - hasContent(true) + hasWidth(1): narrow char - we are done
|
||||
// - hasWidth(0): second part of wide char - we are done
|
||||
// - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one cell further back
|
||||
const line = buffer.lines.get(buffer.ybase + buffer.y)!;
|
||||
if (line.hasWidth(buffer.x) && !line.hasContent(buffer.x)) {
|
||||
buffer.x--;
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
|
||||
if (line.hasWidth(this._activeBuffer.x) && !line.hasContent(this._activeBuffer.x)) {
|
||||
this._activeBuffer.x--;
|
||||
// We do this only once, since width=1 + hasContent=false currently happens only once before
|
||||
// early wrapping of a wide char.
|
||||
// This needs to be fixed once we support graphemes taking more than 2 cells.
|
||||
@@ -885,13 +886,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y C0 HT "Horizontal Tabulation" "\t, \x09" "Move the cursor to the next character tab stop."
|
||||
*/
|
||||
public tab(): boolean {
|
||||
if (this._bufferService.buffer.x >= this._bufferService.cols) {
|
||||
if (this._activeBuffer.x >= this._bufferService.cols) {
|
||||
return true;
|
||||
}
|
||||
const originalX = this._bufferService.buffer.x;
|
||||
this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
|
||||
const originalX = this._activeBuffer.x;
|
||||
this._activeBuffer.x = this._activeBuffer.nextStop();
|
||||
if (this._optionsService.options.screenReaderMode) {
|
||||
this._onA11yTab.fire(this._bufferService.buffer.x - originalX);
|
||||
this._onA11yTab.fire(this._activeBuffer.x - originalX);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -924,27 +925,27 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Restrict cursor to viewport size / scroll margin (origin mode).
|
||||
*/
|
||||
private _restrictCursor(maxCol: number = this._bufferService.cols - 1): void {
|
||||
this._bufferService.buffer.x = Math.min(maxCol, Math.max(0, this._bufferService.buffer.x));
|
||||
this._bufferService.buffer.y = this._coreService.decPrivateModes.origin
|
||||
? Math.min(this._bufferService.buffer.scrollBottom, Math.max(this._bufferService.buffer.scrollTop, this._bufferService.buffer.y))
|
||||
: Math.min(this._bufferService.rows - 1, Math.max(0, this._bufferService.buffer.y));
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
this._activeBuffer.x = Math.min(maxCol, Math.max(0, this._activeBuffer.x));
|
||||
this._activeBuffer.y = this._coreService.decPrivateModes.origin
|
||||
? Math.min(this._activeBuffer.scrollBottom, Math.max(this._activeBuffer.scrollTop, this._activeBuffer.y))
|
||||
: Math.min(this._bufferService.rows - 1, Math.max(0, this._activeBuffer.y));
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set absolute cursor position.
|
||||
*/
|
||||
private _setCursor(x: number, y: number): void {
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
if (this._coreService.decPrivateModes.origin) {
|
||||
this._bufferService.buffer.x = x;
|
||||
this._bufferService.buffer.y = this._bufferService.buffer.scrollTop + y;
|
||||
this._activeBuffer.x = x;
|
||||
this._activeBuffer.y = this._activeBuffer.scrollTop + y;
|
||||
} else {
|
||||
this._bufferService.buffer.x = x;
|
||||
this._bufferService.buffer.y = y;
|
||||
this._activeBuffer.x = x;
|
||||
this._activeBuffer.y = y;
|
||||
}
|
||||
this._restrictCursor();
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -954,7 +955,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// for relative changes we have to make sure we are within 0 .. cols/rows - 1
|
||||
// before calculating the new position
|
||||
this._restrictCursor();
|
||||
this._setCursor(this._bufferService.buffer.x + x, this._bufferService.buffer.y + y);
|
||||
this._setCursor(this._activeBuffer.x + x, this._activeBuffer.y + y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -966,7 +967,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public cursorUp(params: IParams): boolean {
|
||||
// stop at scrollTop
|
||||
const diffToTop = this._bufferService.buffer.y - this._bufferService.buffer.scrollTop;
|
||||
const diffToTop = this._activeBuffer.y - this._activeBuffer.scrollTop;
|
||||
if (diffToTop >= 0) {
|
||||
this._moveCursor(0, -Math.min(diffToTop, params.params[0] || 1));
|
||||
} else {
|
||||
@@ -984,7 +985,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public cursorDown(params: IParams): boolean {
|
||||
// stop at scrollBottom
|
||||
const diffToBottom = this._bufferService.buffer.scrollBottom - this._bufferService.buffer.y;
|
||||
const diffToBottom = this._activeBuffer.scrollBottom - this._activeBuffer.y;
|
||||
if (diffToBottom >= 0) {
|
||||
this._moveCursor(0, Math.min(diffToBottom, params.params[0] || 1));
|
||||
} else {
|
||||
@@ -1025,7 +1026,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public cursorNextLine(params: IParams): boolean {
|
||||
this.cursorDown(params);
|
||||
this._bufferService.buffer.x = 0;
|
||||
this._activeBuffer.x = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1039,7 +1040,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public cursorPrecedingLine(params: IParams): boolean {
|
||||
this.cursorUp(params);
|
||||
this._bufferService.buffer.x = 0;
|
||||
this._activeBuffer.x = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1050,7 +1051,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y CSI CHA "Cursor Horizontal Absolute" "CSI Ps G" "Move cursor to `Ps`-th column of the active row (default=1)."
|
||||
*/
|
||||
public cursorCharAbsolute(params: IParams): boolean {
|
||||
this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
|
||||
this._setCursor((params.params[0] || 1) - 1, this._activeBuffer.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1081,7 +1082,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y CSI HPA "Horizontal Position Absolute" "CSI Ps ` " "Same as CHA."
|
||||
*/
|
||||
public charPosAbsolute(params: IParams): boolean {
|
||||
this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
|
||||
this._setCursor((params.params[0] || 1) - 1, this._activeBuffer.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1103,7 +1104,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y CSI VPA "Vertical Position Absolute" "CSI Ps d" "Move cursor to `Ps`-th row (default=1)."
|
||||
*/
|
||||
public linePosAbsolute(params: IParams): boolean {
|
||||
this._setCursor(this._bufferService.buffer.x, (params.params[0] || 1) - 1);
|
||||
this._setCursor(this._activeBuffer.x, (params.params[0] || 1) - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1146,9 +1147,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public tabClear(params: IParams): boolean {
|
||||
const param = params.params[0];
|
||||
if (param === 0) {
|
||||
delete this._bufferService.buffer.tabs[this._bufferService.buffer.x];
|
||||
delete this._activeBuffer.tabs[this._activeBuffer.x];
|
||||
} else if (param === 3) {
|
||||
this._bufferService.buffer.tabs = {};
|
||||
this._activeBuffer.tabs = {};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1160,12 +1161,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y CSI CHT "Cursor Horizontal Tabulation" "CSI Ps I" "Move cursor `Ps` times tabs forward (default=1)."
|
||||
*/
|
||||
public cursorForwardTab(params: IParams): boolean {
|
||||
if (this._bufferService.buffer.x >= this._bufferService.cols) {
|
||||
if (this._activeBuffer.x >= this._bufferService.cols) {
|
||||
return true;
|
||||
}
|
||||
let param = params.params[0] || 1;
|
||||
while (param--) {
|
||||
this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
|
||||
this._activeBuffer.x = this._activeBuffer.nextStop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1176,16 +1177,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y CSI CBT "Cursor Backward Tabulation" "CSI Ps Z" "Move cursor `Ps` tabs backward (default=1)."
|
||||
*/
|
||||
public cursorBackwardTab(params: IParams): boolean {
|
||||
if (this._bufferService.buffer.x >= this._bufferService.cols) {
|
||||
if (this._activeBuffer.x >= this._bufferService.cols) {
|
||||
return true;
|
||||
}
|
||||
let param = params.params[0] || 1;
|
||||
|
||||
// make buffer local for faster access
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
while (param--) {
|
||||
buffer.x = buffer.prevStop();
|
||||
this._activeBuffer.x = this._activeBuffer.prevStop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1199,11 +1197,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @param end end - 1 is last erased cell
|
||||
*/
|
||||
private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void {
|
||||
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
||||
line.replaceCells(
|
||||
start,
|
||||
end,
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._activeBuffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
if (clearWrap) {
|
||||
@@ -1217,8 +1215,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @param y row index
|
||||
*/
|
||||
private _resetBufferLine(y: number): void {
|
||||
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
|
||||
line.fill(this._bufferService.buffer.getNullCell(this._eraseAttrData()));
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
||||
line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()));
|
||||
line.isWrapped = false;
|
||||
}
|
||||
|
||||
@@ -1251,22 +1249,22 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
let j;
|
||||
switch (params.params[0]) {
|
||||
case 0:
|
||||
j = this._bufferService.buffer.y;
|
||||
j = this._activeBuffer.y;
|
||||
this._dirtyRowService.markDirty(j);
|
||||
this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._bufferService.cols, this._bufferService.buffer.x === 0);
|
||||
this._eraseInBufferLine(j++, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0);
|
||||
for (; j < this._bufferService.rows; j++) {
|
||||
this._resetBufferLine(j);
|
||||
}
|
||||
this._dirtyRowService.markDirty(j);
|
||||
break;
|
||||
case 1:
|
||||
j = this._bufferService.buffer.y;
|
||||
j = this._activeBuffer.y;
|
||||
this._dirtyRowService.markDirty(j);
|
||||
// Deleted front part of line and everything before. This line will no longer be wrapped.
|
||||
this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true);
|
||||
if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) {
|
||||
this._eraseInBufferLine(j, 0, this._activeBuffer.x + 1, true);
|
||||
if (this._activeBuffer.x + 1 >= this._bufferService.cols) {
|
||||
// Deleted entire previous line. This next line can no longer be wrapped.
|
||||
this._bufferService.buffer.lines.get(j + 1)!.isWrapped = false;
|
||||
this._activeBuffer.lines.get(j + 1)!.isWrapped = false;
|
||||
}
|
||||
while (j--) {
|
||||
this._resetBufferLine(j);
|
||||
@@ -1283,11 +1281,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
break;
|
||||
case 3:
|
||||
// Clear scrollback (everything not in viewport)
|
||||
const scrollBackSize = this._bufferService.buffer.lines.length - this._bufferService.rows;
|
||||
const scrollBackSize = this._activeBuffer.lines.length - this._bufferService.rows;
|
||||
if (scrollBackSize > 0) {
|
||||
this._bufferService.buffer.lines.trimStart(scrollBackSize);
|
||||
this._bufferService.buffer.ybase = Math.max(this._bufferService.buffer.ybase - scrollBackSize, 0);
|
||||
this._bufferService.buffer.ydisp = Math.max(this._bufferService.buffer.ydisp - scrollBackSize, 0);
|
||||
this._activeBuffer.lines.trimStart(scrollBackSize);
|
||||
this._activeBuffer.ybase = Math.max(this._activeBuffer.ybase - scrollBackSize, 0);
|
||||
this._activeBuffer.ydisp = Math.max(this._activeBuffer.ydisp - scrollBackSize, 0);
|
||||
// Force a scroll event to refresh viewport
|
||||
this._onScroll.fire(0);
|
||||
}
|
||||
@@ -1322,16 +1320,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._restrictCursor(this._bufferService.cols);
|
||||
switch (params.params[0]) {
|
||||
case 0:
|
||||
this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._bufferService.cols);
|
||||
this._eraseInBufferLine(this._activeBuffer.y, this._activeBuffer.x, this._bufferService.cols);
|
||||
break;
|
||||
case 1:
|
||||
this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.buffer.x + 1);
|
||||
this._eraseInBufferLine(this._activeBuffer.y, 0, this._activeBuffer.x + 1);
|
||||
break;
|
||||
case 2:
|
||||
this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols);
|
||||
this._eraseInBufferLine(this._activeBuffer.y, 0, this._bufferService.cols);
|
||||
break;
|
||||
}
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1348,26 +1346,23 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._restrictCursor();
|
||||
let param = params.params[0] || 1;
|
||||
|
||||
// make buffer local for faster access
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||
if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const row: number = buffer.ybase + buffer.y;
|
||||
const row: number = this._activeBuffer.ybase + this._activeBuffer.y;
|
||||
|
||||
const scrollBottomRowsOffset = this._bufferService.rows - 1 - buffer.scrollBottom;
|
||||
const scrollBottomAbsolute = this._bufferService.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1;
|
||||
const scrollBottomRowsOffset = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom;
|
||||
const scrollBottomAbsolute = this._bufferService.rows - 1 + this._activeBuffer.ybase - scrollBottomRowsOffset + 1;
|
||||
while (param--) {
|
||||
// test: echo -e '\e[44m\e[1L\e[0m'
|
||||
// blankLine(true) - xterm/linux behavior
|
||||
buffer.lines.splice(scrollBottomAbsolute - 1, 1);
|
||||
buffer.lines.splice(row, 0, buffer.getBlankLine(this._eraseAttrData()));
|
||||
this._activeBuffer.lines.splice(scrollBottomAbsolute - 1, 1);
|
||||
this._activeBuffer.lines.splice(row, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
|
||||
}
|
||||
|
||||
this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
|
||||
buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.y, this._activeBuffer.scrollBottom);
|
||||
this._activeBuffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1384,27 +1379,24 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._restrictCursor();
|
||||
let param = params.params[0] || 1;
|
||||
|
||||
// make buffer local for faster access
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||
if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const row: number = buffer.ybase + buffer.y;
|
||||
const row: number = this._activeBuffer.ybase + this._activeBuffer.y;
|
||||
|
||||
let j: number;
|
||||
j = this._bufferService.rows - 1 - buffer.scrollBottom;
|
||||
j = this._bufferService.rows - 1 + buffer.ybase - j;
|
||||
j = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom;
|
||||
j = this._bufferService.rows - 1 + this._activeBuffer.ybase - j;
|
||||
while (param--) {
|
||||
// test: echo -e '\e[44m\e[1M\e[0m'
|
||||
// blankLine(true) - xterm/linux behavior
|
||||
buffer.lines.splice(row, 1);
|
||||
buffer.lines.splice(j, 0, buffer.getBlankLine(this._eraseAttrData()));
|
||||
this._activeBuffer.lines.splice(row, 1);
|
||||
this._activeBuffer.lines.splice(j, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
|
||||
}
|
||||
|
||||
this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
|
||||
buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.y, this._activeBuffer.scrollBottom);
|
||||
this._activeBuffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1421,15 +1413,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public insertChars(params: IParams): boolean {
|
||||
this._restrictCursor();
|
||||
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
|
||||
if (line) {
|
||||
line.insertCells(
|
||||
this._bufferService.buffer.x,
|
||||
this._activeBuffer.x,
|
||||
params.params[0] || 1,
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._activeBuffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1447,15 +1439,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public deleteChars(params: IParams): boolean {
|
||||
this._restrictCursor();
|
||||
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
|
||||
if (line) {
|
||||
line.deleteCells(
|
||||
this._bufferService.buffer.x,
|
||||
this._activeBuffer.x,
|
||||
params.params[0] || 1,
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._activeBuffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1471,14 +1463,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public scrollUp(params: IParams): boolean {
|
||||
let param = params.params[0] || 1;
|
||||
|
||||
// make buffer local for faster access
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
while (param--) {
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(this._eraseAttrData()));
|
||||
this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollTop, 1);
|
||||
this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollBottom, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1490,14 +1479,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public scrollDown(params: IParams): boolean {
|
||||
let param = params.params[0] || 1;
|
||||
|
||||
// make buffer local for faster access
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
while (param--) {
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollBottom, 1);
|
||||
this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollTop, 0, this._activeBuffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1520,17 +1506,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* SL has no effect outside of the scroll margins.
|
||||
*/
|
||||
public scrollLeft(params: IParams): boolean {
|
||||
const buffer = this._bufferService.buffer;
|
||||
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||
if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
|
||||
return true;
|
||||
}
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = buffer.lines.get(buffer.ybase + y)!;
|
||||
line.deleteCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
||||
line.deleteCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1554,17 +1539,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* SL has no effect outside of the scroll margins.
|
||||
*/
|
||||
public scrollRight(params: IParams): boolean {
|
||||
const buffer = this._bufferService.buffer;
|
||||
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||
if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
|
||||
return true;
|
||||
}
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = buffer.lines.get(buffer.ybase + y)!;
|
||||
line.insertCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
||||
line.insertCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1578,17 +1562,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* DECIC has no effect outside the scrolling margins.
|
||||
*/
|
||||
public insertColumns(params: IParams): boolean {
|
||||
const buffer = this._bufferService.buffer;
|
||||
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||
if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
|
||||
return true;
|
||||
}
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = this._bufferService.buffer.lines.get(buffer.ybase + y)!;
|
||||
line.insertCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
||||
line.insertCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1602,17 +1585,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* DECDC has no effect outside the scrolling margins.
|
||||
*/
|
||||
public deleteColumns(params: IParams): boolean {
|
||||
const buffer = this._bufferService.buffer;
|
||||
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||
if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
|
||||
return true;
|
||||
}
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = buffer.lines.get(buffer.ybase + y)!;
|
||||
line.deleteCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
||||
line.deleteCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1626,15 +1608,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public eraseChars(params: IParams): boolean {
|
||||
this._restrictCursor();
|
||||
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
|
||||
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
|
||||
if (line) {
|
||||
line.replaceCells(
|
||||
this._bufferService.buffer.x,
|
||||
this._bufferService.buffer.x + (params.params[0] || 1),
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._activeBuffer.x,
|
||||
this._activeBuffer.x + (params.params[0] || 1),
|
||||
this._activeBuffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
this._dirtyRowService.markDirty(this._activeBuffer.y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1976,6 +1958,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// focusin: ^[[I
|
||||
// focusout: ^[[O
|
||||
this._coreService.decPrivateModes.sendFocus = true;
|
||||
this._onRequestSendFocus.fire();
|
||||
break;
|
||||
case 1005: // utf8 ext mode mouse - removed in #2507
|
||||
this._logService.debug('DECSET 1005 not supported (see #2507)');
|
||||
@@ -2570,8 +2553,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
break;
|
||||
case 6:
|
||||
// cursor position
|
||||
const y = this._bufferService.buffer.y + 1;
|
||||
const x = this._bufferService.buffer.x + 1;
|
||||
const y = this._activeBuffer.y + 1;
|
||||
const x = this._activeBuffer.x + 1;
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[${y};${x}R`);
|
||||
break;
|
||||
}
|
||||
@@ -2585,8 +2568,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
switch (params.params[0]) {
|
||||
case 6:
|
||||
// cursor position
|
||||
const y = this._bufferService.buffer.y + 1;
|
||||
const x = this._bufferService.buffer.x + 1;
|
||||
const y = this._activeBuffer.y + 1;
|
||||
const x = this._activeBuffer.x + 1;
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`);
|
||||
break;
|
||||
case 15:
|
||||
@@ -2631,18 +2614,18 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public softReset(params: IParams): boolean {
|
||||
this._coreService.isCursorHidden = false;
|
||||
this._onRequestSyncScrollBar.fire();
|
||||
this._bufferService.buffer.scrollTop = 0;
|
||||
this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1;
|
||||
this._activeBuffer.scrollTop = 0;
|
||||
this._activeBuffer.scrollBottom = this._bufferService.rows - 1;
|
||||
this._curAttrData = DEFAULT_ATTR_DATA.clone();
|
||||
this._coreService.reset();
|
||||
this._charsetService.reset();
|
||||
|
||||
// reset DECSC data
|
||||
this._bufferService.buffer.savedX = 0;
|
||||
this._bufferService.buffer.savedY = this._bufferService.buffer.ybase;
|
||||
this._bufferService.buffer.savedCurAttrData.fg = this._curAttrData.fg;
|
||||
this._bufferService.buffer.savedCurAttrData.bg = this._curAttrData.bg;
|
||||
this._bufferService.buffer.savedCharset = this._charsetService.charset;
|
||||
this._activeBuffer.savedX = 0;
|
||||
this._activeBuffer.savedY = this._activeBuffer.ybase;
|
||||
this._activeBuffer.savedCurAttrData.fg = this._curAttrData.fg;
|
||||
this._activeBuffer.savedCurAttrData.bg = this._curAttrData.bg;
|
||||
this._activeBuffer.savedCharset = this._charsetService.charset;
|
||||
|
||||
// reset DECOM
|
||||
this._coreService.decPrivateModes.origin = false;
|
||||
@@ -2705,8 +2688,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
if (bottom > top) {
|
||||
this._bufferService.buffer.scrollTop = top - 1;
|
||||
this._bufferService.buffer.scrollBottom = bottom - 1;
|
||||
this._activeBuffer.scrollTop = top - 1;
|
||||
this._activeBuffer.scrollBottom = bottom - 1;
|
||||
this._setCursor(0, 0);
|
||||
}
|
||||
return true;
|
||||
@@ -2801,11 +2784,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y ESC SC "Save Cursor" "ESC 7" "Save cursor position, charmap and text attributes."
|
||||
*/
|
||||
public saveCursor(params?: IParams): boolean {
|
||||
this._bufferService.buffer.savedX = this._bufferService.buffer.x;
|
||||
this._bufferService.buffer.savedY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
|
||||
this._bufferService.buffer.savedCurAttrData.fg = this._curAttrData.fg;
|
||||
this._bufferService.buffer.savedCurAttrData.bg = this._curAttrData.bg;
|
||||
this._bufferService.buffer.savedCharset = this._charsetService.charset;
|
||||
this._activeBuffer.savedX = this._activeBuffer.x;
|
||||
this._activeBuffer.savedY = this._activeBuffer.ybase + this._activeBuffer.y;
|
||||
this._activeBuffer.savedCurAttrData.fg = this._curAttrData.fg;
|
||||
this._activeBuffer.savedCurAttrData.bg = this._curAttrData.bg;
|
||||
this._activeBuffer.savedCharset = this._charsetService.charset;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2819,13 +2802,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y ESC RC "Restore Cursor" "ESC 8" "Restore cursor position, charmap and text attributes."
|
||||
*/
|
||||
public restoreCursor(params?: IParams): boolean {
|
||||
this._bufferService.buffer.x = this._bufferService.buffer.savedX || 0;
|
||||
this._bufferService.buffer.y = Math.max(this._bufferService.buffer.savedY - this._bufferService.buffer.ybase, 0);
|
||||
this._curAttrData.fg = this._bufferService.buffer.savedCurAttrData.fg;
|
||||
this._curAttrData.bg = this._bufferService.buffer.savedCurAttrData.bg;
|
||||
this._activeBuffer.x = this._activeBuffer.savedX || 0;
|
||||
this._activeBuffer.y = Math.max(this._activeBuffer.savedY - this._activeBuffer.ybase, 0);
|
||||
this._curAttrData.fg = this._activeBuffer.savedCurAttrData.fg;
|
||||
this._curAttrData.bg = this._activeBuffer.savedCurAttrData.bg;
|
||||
this._charsetService.charset = (this as any)._savedCharset;
|
||||
if (this._bufferService.buffer.savedCharset) {
|
||||
this._charsetService.charset = this._bufferService.buffer.savedCharset;
|
||||
if (this._activeBuffer.savedCharset) {
|
||||
this._charsetService.charset = this._activeBuffer.savedCharset;
|
||||
}
|
||||
this._restrictCursor();
|
||||
return true;
|
||||
@@ -2907,7 +2890,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y ESC NEL "Next Line" "ESC E" "Move the cursor to the beginning of the next row."
|
||||
*/
|
||||
public nextLine(): boolean {
|
||||
this._bufferService.buffer.x = 0;
|
||||
this._activeBuffer.x = 0;
|
||||
this.index();
|
||||
return true;
|
||||
}
|
||||
@@ -2987,13 +2970,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public index(): boolean {
|
||||
this._restrictCursor();
|
||||
const buffer = this._bufferService.buffer;
|
||||
this._bufferService.buffer.y++;
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._activeBuffer.y++;
|
||||
if (this._activeBuffer.y === this._activeBuffer.scrollBottom + 1) {
|
||||
this._activeBuffer.y--;
|
||||
this._bufferService.scroll(this._eraseAttrData());
|
||||
} else if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
} else if (this._activeBuffer.y >= this._bufferService.rows) {
|
||||
this._activeBuffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
this._restrictCursor();
|
||||
return true;
|
||||
@@ -3010,7 +2992,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y ESC HTS "Horizontal Tabulation Set" "ESC H" "Places a tab stop at the current cursor position."
|
||||
*/
|
||||
public tabSet(): boolean {
|
||||
this._bufferService.buffer.tabs[this._bufferService.buffer.x] = true;
|
||||
this._activeBuffer.tabs[this._activeBuffer.x] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3025,17 +3007,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public reverseIndex(): boolean {
|
||||
this._restrictCursor();
|
||||
const buffer = this._bufferService.buffer;
|
||||
if (buffer.y === buffer.scrollTop) {
|
||||
if (this._activeBuffer.y === this._activeBuffer.scrollTop) {
|
||||
// possibly move the code below to term.reverseScroll();
|
||||
// test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
|
||||
// blankLine(true) is xterm/linux behavior
|
||||
const scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop;
|
||||
buffer.lines.shiftElements(buffer.ybase + buffer.y, scrollRegionHeight, 1);
|
||||
buffer.lines.set(buffer.ybase + buffer.y, buffer.getBlankLine(this._eraseAttrData()));
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
const scrollRegionHeight = this._activeBuffer.scrollBottom - this._activeBuffer.scrollTop;
|
||||
this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase + this._activeBuffer.y, scrollRegionHeight, 1);
|
||||
this._activeBuffer.lines.set(this._activeBuffer.ybase + this._activeBuffer.y, this._activeBuffer.getBlankLine(this._eraseAttrData()));
|
||||
this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
||||
} else {
|
||||
buffer.y--;
|
||||
this._activeBuffer.y--;
|
||||
this._restrictCursor(); // quickfix to not run out of bounds
|
||||
}
|
||||
return true;
|
||||
@@ -3096,12 +3077,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
cell.fg = this._curAttrData.fg;
|
||||
cell.bg = this._curAttrData.bg;
|
||||
|
||||
const buffer = this._bufferService.buffer;
|
||||
|
||||
this._setCursor(0, 0);
|
||||
for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) {
|
||||
const row = buffer.ybase + buffer.y + yOffset;
|
||||
const line = buffer.lines.get(row);
|
||||
const row = this._activeBuffer.ybase + this._activeBuffer.y + yOffset;
|
||||
const line = this._activeBuffer.lines.get(row);
|
||||
if (line) {
|
||||
line.fill(cell);
|
||||
line.isWrapped = false;
|
||||
|
||||
@@ -42,6 +42,10 @@ export class BufferSet extends Disposable implements IBufferSet {
|
||||
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
this._alt = new Buffer(false, this._optionsService, this._bufferService);
|
||||
this._activeBuffer = this._normal;
|
||||
this._onBufferActivate.fire({
|
||||
activeBuffer: this._normal,
|
||||
inactiveBuffer: this._alt
|
||||
});
|
||||
|
||||
this.setupTabStops();
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ export class BufferService extends Disposable implements IBufferService {
|
||||
@IOptionsService private _optionsService: IOptionsService
|
||||
) {
|
||||
super();
|
||||
this.cols = Math.max(_optionsService.options.cols, MINIMUM_COLS);
|
||||
this.rows = Math.max(_optionsService.options.rows, MINIMUM_ROWS);
|
||||
this.cols = Math.max(_optionsService.options.cols || 0, MINIMUM_COLS);
|
||||
this.rows = Math.max(_optionsService.options.rows || 0, MINIMUM_ROWS);
|
||||
this.buffers = new BufferSet(_optionsService, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,16 @@ describe('OptionsService', () => {
|
||||
afterEach(() => {
|
||||
console.error = originalError;
|
||||
});
|
||||
it('uses default value if invalid constructor option values passed for cols/rows', () => {
|
||||
const optionsService = new OptionsService({ cols: undefined, rows: undefined });
|
||||
assert.equal(optionsService.getOption('rows'), DEFAULT_OPTIONS.rows);
|
||||
assert.equal(optionsService.getOption('cols'), DEFAULT_OPTIONS.cols);
|
||||
});
|
||||
it('uses values from constructor option values if correctly passed', () => {
|
||||
const optionsService = new OptionsService({ cols: 80, rows: 25 });
|
||||
assert.equal(optionsService.getOption('rows'), 25);
|
||||
assert.equal(optionsService.getOption('cols'), 80);
|
||||
});
|
||||
it('uses default value if invalid constructor option value passed', () => {
|
||||
assert.equal(new OptionsService({tabStopWidth: 0}).getOption('tabStopWidth'), DEFAULT_OPTIONS.tabStopWidth);
|
||||
});
|
||||
|
||||
@@ -190,6 +190,11 @@ export class OptionsService implements IOptionsService {
|
||||
if (value <= 0) {
|
||||
throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
|
||||
}
|
||||
case 'rows':
|
||||
case 'cols':
|
||||
if (!value && value !== 0) {
|
||||
throw new Error(`${key} must be numeric, value: ${value}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
|
||||
Reference in New Issue
Block a user