Use jsdoc/multiline-blocks instead of multiline-comment-style
Replace multiline-comment-style (starred-block), which also forces multiline // comments into block comments, with jsdoc/multiline-blocks. That rule only enforces starred JSDoc layout for /** blocks (no text on the opening line) and leaves multiline // comments unchanged for internal documentation. Fix multiline JSDoc in Types.ts. Fixes #5899 Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
This commit is contained in:
co-authored by
Daniel Imms
parent
4ec85e67ba
commit
df3422fb0b
@@ -21,10 +21,8 @@ const enum BoundaryPosition {
|
||||
BOTTOM
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn this on to unhide the accessibility tree and display it under
|
||||
* (instead of overlapping with) the terminal.
|
||||
*/
|
||||
// Turn this on to unhide the accessibility tree and display it under
|
||||
// (instead of overlapping with) the terminal.
|
||||
const DEBUG = false;
|
||||
|
||||
export class AccessibilityManager extends Disposable {
|
||||
@@ -225,10 +223,8 @@ export class AccessibilityManager extends Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't scroll when the last focused item was not the second row (focus is going the other
|
||||
* direction)
|
||||
*/
|
||||
// Don't scroll when the last focused item was not the second row (focus is going the other
|
||||
// direction)
|
||||
if (e.relatedTarget !== beforeBoundaryElement) {
|
||||
return;
|
||||
}
|
||||
@@ -287,11 +283,9 @@ export class AccessibilityManager extends Disposable {
|
||||
}
|
||||
|
||||
if (selection.isCollapsed) {
|
||||
/*
|
||||
* Only do something when the anchorNode is inside the row container. This
|
||||
* behavior mirrors what we do with mouse --- if the mouse clicks
|
||||
* somewhere outside of the terminal, we don't clear the selection.
|
||||
*/
|
||||
// Only do something when the anchorNode is inside the row container. This
|
||||
// behavior mirrors what we do with mouse --- if the mouse clicks
|
||||
// somewhere outside of the terminal, we don't clear the selection.
|
||||
if (this._rowContainer.contains(selection.anchorNode)) {
|
||||
this._terminal.clearSelection();
|
||||
}
|
||||
|
||||
@@ -22,10 +22,8 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean):
|
||||
if (!bracketedPasteMode) {
|
||||
return text;
|
||||
}
|
||||
/*
|
||||
* Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste)
|
||||
* by replacing ESC (\x1b) with its visible representation U+241B (␛).
|
||||
*/
|
||||
// Sanitize pasted text to prevent injected escape sequences (e.g. exiting bracketed paste)
|
||||
// by replacing ESC (\x1b) with its visible representation U+241B (␛).
|
||||
const sanitizedText = text.replace(/\x1b/g, '\u241b');
|
||||
return `\x1b[200~${sanitizedText}\x1b[201~`;
|
||||
}
|
||||
|
||||
@@ -323,10 +323,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
* Binds the desired blur behavior on a given terminal object.
|
||||
*/
|
||||
private _handleTextAreaBlur(): void {
|
||||
/*
|
||||
* Text can safely be removed on blur. Doing it earlier could interfere with
|
||||
* screen readers reading it out.
|
||||
*/
|
||||
// Text can safely be removed on blur. Doing it earlier could interfere with
|
||||
// screen readers reading it out.
|
||||
this.textarea!.value = '';
|
||||
this.refresh(this.buffer.y, this.buffer.y);
|
||||
if (this.coreService.decPrivateModes.sendFocus) {
|
||||
@@ -352,10 +350,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
const cursorTop = this.buffer.y * this._renderService.dimensions.css.cell.height;
|
||||
const cursorLeft = cursorX * this._renderService.dimensions.css.cell.width;
|
||||
|
||||
/*
|
||||
* Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||
* text is.
|
||||
*/
|
||||
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||
// text is.
|
||||
this.textarea.style.left = cursorLeft + 'px';
|
||||
this.textarea.style.top = cursorTop + 'px';
|
||||
this.textarea.style.width = cellWidth + 'px';
|
||||
@@ -372,10 +368,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
// Bind clipboard functionality
|
||||
this._register(addDisposableListener(this.element!, 'copy', (event: ClipboardEvent) => {
|
||||
/*
|
||||
* If mouse events are active it means the selection manager is disabled and
|
||||
* copy should be handled by the host program.
|
||||
*/
|
||||
// If mouse events are active it means the selection manager is disabled and
|
||||
// copy should be handled by the host program.
|
||||
if (!this.hasSelection()) {
|
||||
return;
|
||||
}
|
||||
@@ -399,16 +393,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the textarea under the cursor when middle clicking on Linux to ensure
|
||||
* middle click to paste selection works. This only appears to work in Chrome
|
||||
* at the time is writing.
|
||||
*/
|
||||
// Move the textarea under the cursor when middle clicking on Linux to ensure
|
||||
// middle click to paste selection works. This only appears to work in Chrome
|
||||
// at the time is writing.
|
||||
if (Browser.isLinux) {
|
||||
/*
|
||||
* Use auxclick event over mousedown the latter doesn't seem to work. Note
|
||||
* that the regular click event doesn't fire for the middle mouse button.
|
||||
*/
|
||||
// Use auxclick event over mousedown the latter doesn't seem to work. Note
|
||||
// that the regular click event doesn't fire for the middle mouse button.
|
||||
this._register(addDisposableListener(this.element!, 'auxclick', (event: MouseEvent) => {
|
||||
if (event.button === 1) {
|
||||
moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!);
|
||||
@@ -425,13 +415,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this._register(addDisposableListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));
|
||||
this._register(addDisposableListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true));
|
||||
this._register(addDisposableListener(this.textarea!, 'compositionstart', () => {
|
||||
/*
|
||||
* Ensure the textarea is synced to the latest cursor location before composition begins. This
|
||||
* is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively
|
||||
* would cause the IME to appear in the wrong position. The theory is that when the IME is
|
||||
* triggered during a partial render the textarea position becomes locked and will not move
|
||||
* until it is hidden and a custom move occurs.
|
||||
*/
|
||||
// Ensure the textarea is synced to the latest cursor location before composition begins. This
|
||||
// is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively
|
||||
// would cause the IME to appear in the wrong position. The theory is that when the IME is
|
||||
// triggered during a partial render the textarea position becomes locked and will not move
|
||||
// until it is hidden and a custom move occurs.
|
||||
this._syncTextArea();
|
||||
this._compositionHelper!.compositionstart();
|
||||
this._compositionHelper!.updateCompositionElements();
|
||||
@@ -479,10 +467,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this._register(this.optionsService.onSpecificOptionChange('allowTransparency', value => this.element!.classList.toggle('allow-transparency', value)));
|
||||
parent.appendChild(this.element);
|
||||
|
||||
/*
|
||||
* Performance: Use a document fragment to build the terminal
|
||||
* viewport and helper elements detached from the DOM
|
||||
*/
|
||||
// Performance: Use a document fragment to build the terminal
|
||||
// viewport and helper elements detached from the DOM
|
||||
const fragment = this._document.createDocumentFragment();
|
||||
this._viewportElement = this._document.createElement('div');
|
||||
this._viewportElement.classList.add('xterm-viewport');
|
||||
@@ -491,10 +477,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this.screenElement = this._document.createElement('div');
|
||||
this.screenElement.classList.add('xterm-screen');
|
||||
this._register(addDisposableListener(this.screenElement, 'mousemove', (ev: MouseEvent) => this.updateCursorStyle(ev)));
|
||||
/*
|
||||
* Create the container that will hold helpers like the textarea for
|
||||
* capturing DOM Events. Then produce the helpers.
|
||||
*/
|
||||
// Create the container that will hold helpers like the textarea for
|
||||
// capturing DOM Events. Then produce the helpers.
|
||||
this._helperContainer = this._document.createElement('div');
|
||||
this._helperContainer.classList.add('xterm-helpers');
|
||||
this.screenElement.appendChild(this._helperContainer);
|
||||
@@ -504,10 +488,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this.textarea.classList.add('xterm-helper-textarea');
|
||||
this.textarea.setAttribute('aria-label', Strings.promptLabel.get());
|
||||
if (!Browser.isChromeOS) {
|
||||
/*
|
||||
* ChromeVox on ChromeOS does not like this. See
|
||||
* https://issuetracker.google.com/issues/260170397
|
||||
*/
|
||||
// ChromeVox on ChromeOS does not like this. See
|
||||
// https://issuetracker.google.com/issues/260170397
|
||||
this.textarea.setAttribute('aria-multiline', 'false');
|
||||
}
|
||||
this.textarea.setAttribute('autocorrect', 'off');
|
||||
@@ -517,10 +499,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin));
|
||||
this.textarea.readOnly = this.optionsService.rawOptions.disableStdin;
|
||||
|
||||
/*
|
||||
* Register the core browser service before the generic textarea handlers are registered so it
|
||||
* handles them first. Otherwise the renderers may use the wrong focus state.
|
||||
*/
|
||||
// Register the core browser service before the generic textarea handlers are registered so it
|
||||
// handles them first. Otherwise the renderers may use the wrong focus state.
|
||||
this._coreBrowserService = this._register(this._instantiationService.createInstance(CoreBrowserService,
|
||||
this.textarea,
|
||||
parent.ownerDocument.defaultView ?? window,
|
||||
@@ -618,11 +598,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this._register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
|
||||
this._register(this._selectionService.onRequestRedraw(e => this._renderService!.handleSelectionChanged(e.start, e.end, e.columnSelectMode)));
|
||||
this._register(this._selectionService.onLinuxMouseSelection(text => {
|
||||
/*
|
||||
* If there's a new selection, put it into the textarea, focus and select it
|
||||
* in order to register it as a selection on the OS. This event is fired
|
||||
* only on Linux to enable middle click to paste selection.
|
||||
*/
|
||||
// If there's a new selection, put it into the textarea, focus and select it
|
||||
// in order to register it as a selection on the OS. This event is fired
|
||||
// only on Linux to enable middle click to paste selection.
|
||||
this.textarea!.value = text;
|
||||
this.textarea!.focus();
|
||||
this.textarea!.select();
|
||||
@@ -647,10 +625,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
|
||||
if (this.options.screenReaderMode) {
|
||||
/*
|
||||
* Note that this must be done *after* the renderer is created in order to
|
||||
* ensure the correct order of the dprchange event
|
||||
*/
|
||||
// Note that this must be done *after* the renderer is created in order to
|
||||
// ensure the correct order of the dprchange event
|
||||
this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this);
|
||||
}
|
||||
this._register(this.optionsService.onSpecificOptionChange('screenReaderMode', e => this._handleScreenReaderModeOptionChange(e)));
|
||||
@@ -675,10 +651,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
// Initialize global actions that need to be taken on the document.
|
||||
this._initGlobal();
|
||||
|
||||
/*
|
||||
* Listen for mouse events and translate
|
||||
* them into terminal mouse protocols.
|
||||
*/
|
||||
// Listen for mouse events and translate
|
||||
// them into terminal mouse protocols.
|
||||
this._mouseService.bindMouse({
|
||||
element: this.element!,
|
||||
screenElement: this.screenElement!,
|
||||
@@ -918,11 +892,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case
|
||||
* letters cannot be input while caps lock is on. Skip this hack when using kitty protocol
|
||||
* or Win32 input mode as they need to send proper sequences for all key events.
|
||||
*/
|
||||
// HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case
|
||||
// letters cannot be input while caps lock is on. Skip this hack when using kitty protocol
|
||||
// or Win32 input mode as they need to send proper sequences for all key events.
|
||||
if (!this._keyboardService.useKitty && !this._keyboardService.useWin32InputMode && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) {
|
||||
if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) {
|
||||
return true;
|
||||
@@ -934,11 +906,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers
|
||||
* will announce deleted characters. This will not work 100% of the time but it should cover
|
||||
* most scenarios.
|
||||
*/
|
||||
// If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers
|
||||
// will announce deleted characters. This will not work 100% of the time but it should cover
|
||||
// most scenarios.
|
||||
if (result.key === C0.ETX || result.key === C0.CR) {
|
||||
this.textarea!.value = '';
|
||||
}
|
||||
@@ -948,12 +918,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this._showCursor();
|
||||
this.coreService.triggerDataEvent(result.key, !wasModifierOnly);
|
||||
|
||||
/*
|
||||
* Cancel events when not in screen reader mode so events don't get bubbled up and handled by
|
||||
* other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt
|
||||
* is also depressed) so that the cursor textarea can be updated, which triggers the screen
|
||||
* reader to read it.
|
||||
*/
|
||||
// Cancel events when not in screen reader mode so events don't get bubbled up and handled by
|
||||
// other listeners. When screen reader mode is enabled, we don't cancel them (unless ctrl or alt
|
||||
// is also depressed) so that the cursor textarea can be updated, which triggers the screen
|
||||
// reader to read it.
|
||||
if (!this.optionsService.rawOptions.screenReaderMode || event.altKey || event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@@ -1042,10 +1010,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
this._keyPressHandled = true;
|
||||
|
||||
/*
|
||||
* The key was handled so clear the dead key state, otherwise certain keystrokes like arrow
|
||||
* keys could be ignored
|
||||
*/
|
||||
// The key was handled so clear the dead key state, otherwise certain keystrokes like arrow
|
||||
// keys could be ignored
|
||||
this._unprocessedDeadKey = false;
|
||||
|
||||
return true;
|
||||
@@ -1058,20 +1024,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
* @param ev The input event to be handled.
|
||||
*/
|
||||
protected _inputEvent(ev: InputEvent): boolean {
|
||||
/*
|
||||
* 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
|
||||
* Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679
|
||||
*/
|
||||
// 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
|
||||
// Based on these event traces: https://github.com/xtermjs/xterm.js/issues/3679
|
||||
if (ev.data && ev.inputType === 'insertText' && (!ev.composed || !this._keyDownSeen) && !this.optionsService.rawOptions.screenReaderMode) {
|
||||
if (this._keyPressHandled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* The key was handled so clear the dead key state, otherwise certain keystrokes like arrow
|
||||
* keys could be ignored
|
||||
*/
|
||||
// The key was handled so clear the dead key state, otherwise certain keystrokes like arrow
|
||||
// keys could be ignored
|
||||
this._unprocessedDeadKey = false;
|
||||
|
||||
const text = ev.data;
|
||||
@@ -1121,10 +1083,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
for (let i = 1; i < this.rows; i++) {
|
||||
this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
/*
|
||||
* IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear
|
||||
* scroll event and that the viewport's state will be valid for immediate writes.
|
||||
*/
|
||||
// IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear
|
||||
// scroll event and that the viewport's state will be valid for immediate writes.
|
||||
this._onScroll.fire({ position: this.buffer.ydisp });
|
||||
this.refresh(0, this.rows - 1);
|
||||
}
|
||||
|
||||
+14
-26
@@ -87,11 +87,9 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
||||
}
|
||||
|
||||
private _handleHover(position: IBufferCellPosition): void {
|
||||
/*
|
||||
* TODO: This currently does not cache link provider results across wrapped lines, activeLine
|
||||
* should be something like `activeRange: {startY, endY}`
|
||||
* Check if we need to clear the link
|
||||
*/
|
||||
// TODO: This currently does not cache link provider results across wrapped lines, activeLine
|
||||
// should be something like `activeRange: {startY, endY}`
|
||||
// Check if we need to clear the link
|
||||
if (this._activeLine !== position.y || this._wasResized) {
|
||||
this._clearCurrentLink();
|
||||
this._askForLink(position, false);
|
||||
@@ -127,11 +125,9 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
||||
const existingReply = this._activeProviderReplies?.get(i);
|
||||
// If there isn't a reply, the provider hasn't responded yet.
|
||||
|
||||
/*
|
||||
* TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring
|
||||
* provideLinks isn't triggered again saves ILink.hover firing twice though. This probably
|
||||
* needs promises to get fixed
|
||||
*/
|
||||
// TODO: If there isn't a reply yet it means that the provider is still resolving. Ensuring
|
||||
// provideLinks isn't triggered again saves ILink.hover firing twice though. This probably
|
||||
// needs promises to get fixed
|
||||
if (existingReply) {
|
||||
linkProvided = this._checkLinkProviderResult(i, position, linkProvided);
|
||||
}
|
||||
@@ -144,10 +140,8 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
||||
this._activeProviderReplies?.set(i, linksWithState);
|
||||
linkProvided = this._checkLinkProviderResult(i, position, linkProvided);
|
||||
|
||||
/*
|
||||
* If all providers have responded, remove lower priority links that intersect ranges of
|
||||
* higher priority links
|
||||
*/
|
||||
// If all providers have responded, remove lower priority links that intersect ranges of
|
||||
// higher priority links
|
||||
if (this._activeProviderReplies?.size === this._linkProviderService.linkProviders.length) {
|
||||
this._removeIntersectingLinks(position.y, this._activeProviderReplies);
|
||||
}
|
||||
@@ -193,10 +187,8 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If all providers with higher priority came back undefined, then this provider's link for
|
||||
* the position should be used
|
||||
*/
|
||||
// If all providers with higher priority came back undefined, then this provider's link for
|
||||
// the position should be used
|
||||
if (!hasLinkBefore && links) {
|
||||
const linkAtPosition = links.find(link => this._linkAtPosition(link.link, position));
|
||||
if (linkAtPosition) {
|
||||
@@ -304,19 +296,15 @@ export class Linkifier extends Disposable implements ILinkifier2 {
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Listen to viewport changes to re-render the link under the cursor (only when the line the
|
||||
* link is on changes)
|
||||
*/
|
||||
// Listen to viewport changes to re-render the link under the cursor (only when the line the
|
||||
// link is on changes)
|
||||
this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange(e => {
|
||||
// Sanity check, this shouldn't happen in practice as this listener would be disposed
|
||||
if (!this._currentLink) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* When start is 0 a scroll most likely occurred, make sure links above the fold also get
|
||||
* cleared.
|
||||
*/
|
||||
// When start is 0 a scroll most likely occurred, make sure links above the fold also get
|
||||
// cleared.
|
||||
const start = e.start === 0 ? 0 : e.start + 1 + this._bufferService.buffer.ydisp;
|
||||
const end = this._bufferService.buffer.ydisp + 1 + e.end;
|
||||
// Only clear the link if the viewport change happened on this line
|
||||
|
||||
@@ -33,10 +33,8 @@ export class OscLinkProvider implements ILinkProvider {
|
||||
let currentStart = -1;
|
||||
let finishLink = false;
|
||||
for (let x = 0; x < lineLength; x++) {
|
||||
/*
|
||||
* Minor optimization, only check for content if there isn't a link in case the link ends with
|
||||
* a null cell
|
||||
*/
|
||||
// Minor optimization, only check for content if there isn't a link in case the link ends with
|
||||
// a null cell
|
||||
if (currentStart === -1 && !line.hasContent(x)) {
|
||||
continue;
|
||||
}
|
||||
@@ -109,10 +107,8 @@ export class OscLinkProvider implements ILinkProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Handle fetching and returning other link ranges to underline other links with the same
|
||||
* id
|
||||
*/
|
||||
// TODO: Handle fetching and returning other link ranges to underline other links with the same
|
||||
// id
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,16 +576,14 @@ describe('Terminal', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
term.clearSelection = () => { };
|
||||
/*
|
||||
* term.compositionHelper = {
|
||||
* isComposing: false,
|
||||
* keydown: {
|
||||
* bind: () => {
|
||||
* return () => { return true; };
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
// term.compositionHelper = {
|
||||
// isComposing: false,
|
||||
// keydown: {
|
||||
// bind: () => {
|
||||
// return () => { return true; };
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
evKeyDown = {
|
||||
preventDefault: () => { },
|
||||
stopPropagation: () => { },
|
||||
@@ -1150,10 +1148,8 @@ describe('Terminal', () => {
|
||||
|
||||
// FIXME: move to common/CoreTerminal.test once the trimming is moved over
|
||||
describe('marker lifecycle', () => {
|
||||
/*
|
||||
* create a 10x5 terminal with markers on every line
|
||||
* to test marker lifecycle under various terminal actions
|
||||
*/
|
||||
// create a 10x5 terminal with markers on every line
|
||||
// to test marker lifecycle under various terminal actions
|
||||
let markers: IMarker[];
|
||||
let disposeStack: IMarker[];
|
||||
let term: TestTerminal;
|
||||
|
||||
@@ -43,10 +43,8 @@ export class TimeBasedDebouncer implements IRenderDebouncer {
|
||||
this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart;
|
||||
this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd;
|
||||
|
||||
/*
|
||||
* Only refresh if the time since last refresh is above a threshold, otherwise wait for
|
||||
* enough time to pass before refreshing again.
|
||||
*/
|
||||
// Only refresh if the time since last refresh is above a threshold, otherwise wait for
|
||||
// enough time to pass before refreshing again.
|
||||
const refreshRequestTime: number = performance.now();
|
||||
if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) {
|
||||
// Enough time has lapsed since the last refresh; refresh immediately
|
||||
|
||||
@@ -201,10 +201,8 @@ export const DEFAULT_ANSI_COLORS = Object.freeze((() => {
|
||||
css.toColor('#eeeeec')
|
||||
];
|
||||
|
||||
/*
|
||||
* Fill in the remaining 240 ANSI colors.
|
||||
* Generate colors (16-231)
|
||||
*/
|
||||
// Fill in the remaining 240 ANSI colors.
|
||||
// Generate colors (16-231)
|
||||
const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];
|
||||
for (let i = 0; i < 216; i++) {
|
||||
const r = v[(i / 36) % 6 | 0];
|
||||
|
||||
+11
-21
@@ -99,20 +99,16 @@ export class Viewport extends Disposable {
|
||||
|
||||
this._register(this._bufferService.onResize(() => this.queueSync()));
|
||||
this._register(this._bufferService.buffers.onBufferActivate(() => {
|
||||
/*
|
||||
* Reset _latestYDisp when switching buffers to prevent stale scroll position
|
||||
* from alt buffer contaminating normal buffer scroll position
|
||||
*/
|
||||
// Reset _latestYDisp when switching buffers to prevent stale scroll position
|
||||
// from alt buffer contaminating normal buffer scroll position
|
||||
this._latestYDisp = undefined;
|
||||
this.queueSync();
|
||||
}));
|
||||
this._register(this._bufferService.onScroll(() => this._sync()));
|
||||
|
||||
/*
|
||||
* Flush deferred viewport sync after a render completes (e.g. after ESU ends
|
||||
* synchronized output mode). This ensures DOM scroll position updates atomically
|
||||
* with the canvas render.
|
||||
*/
|
||||
// Flush deferred viewport sync after a render completes (e.g. after ESU ends
|
||||
// synchronized output mode). This ensures DOM scroll position updates atomically
|
||||
// with the canvas render.
|
||||
this._register(this._renderService.onRender(() => {
|
||||
if (this._needsSyncOnRender) {
|
||||
this._needsSyncOnRender = false;
|
||||
@@ -177,20 +173,16 @@ export class Viewport extends Disposable {
|
||||
if (!this._renderService || this._isSyncing) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Defer DOM scroll updates during synchronized output to prevent visible
|
||||
* scroll position flickering while the canvas content is frozen.
|
||||
*/
|
||||
// Defer DOM scroll updates during synchronized output to prevent visible
|
||||
// scroll position flickering while the canvas content is frozen.
|
||||
if (this._coreService.decPrivateModes.synchronizedOutput) {
|
||||
this._needsSyncOnRender = true;
|
||||
return;
|
||||
}
|
||||
this._isSyncing = true;
|
||||
|
||||
/*
|
||||
* Ignore any onScroll event that happens as a result of dimensions changing as this should
|
||||
* never cause a scrollLines call, only setScrollPosition can do that.
|
||||
*/
|
||||
// Ignore any onScroll event that happens as a result of dimensions changing as this should
|
||||
// never cause a scrollLines call, only setScrollPosition can do that.
|
||||
this._suppressOnScrollHandler = true;
|
||||
this._scrollableElement.setScrollDimensions({
|
||||
height: this._renderService.dimensions.css.canvas.height,
|
||||
@@ -198,10 +190,8 @@ export class Viewport extends Disposable {
|
||||
});
|
||||
this._suppressOnScrollHandler = false;
|
||||
|
||||
/*
|
||||
* If ydisp has been changed by some other component (input/buffer), then stop animating smooth
|
||||
* scroll and scroll there immediately.
|
||||
*/
|
||||
// If ydisp has been changed by some other component (input/buffer), then stop animating smooth
|
||||
// scroll and scroll there immediately.
|
||||
if (ydisp !== this._latestYDisp) {
|
||||
this._scrollableElement.setScrollPosition({
|
||||
scrollTop: ydisp * this._renderService.dimensions.css.cell.height
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInternalDecoration } from 'common/services/Services';
|
||||
|
||||
@@ -34,11 +32,9 @@ interface IMinimalDecorationForColorZone {
|
||||
export class ColorZoneStore implements IColorZoneStore {
|
||||
private _zones: IColorZone[] = [];
|
||||
|
||||
/*
|
||||
* The zone pool is used to keep zone objects from being freed between clearing the color zone
|
||||
* store and fetching the zones. This helps reduce GC pressure since the color zones are
|
||||
* accumulated on potentially every scroll event.
|
||||
*/
|
||||
// The zone pool is used to keep zone objects from being freed between clearing the color zone
|
||||
// store and fetching the zones. This helps reduce GC pressure since the color zones are
|
||||
// accumulated on potentially every scroll event.
|
||||
private _zonePool: IColorZone[] = [];
|
||||
private _zonePoolIndex = 0;
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
|
||||
import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
|
||||
@@ -14,10 +12,8 @@ const enum Constants {
|
||||
OVERVIEW_RULER_BORDER_WIDTH = 1
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper objects to avoid excessive calculation and garbage collection during rendering. These are
|
||||
* static values for each render and can be accessed using the decoration position as the key.
|
||||
*/
|
||||
// Helper objects to avoid excessive calculation and garbage collection during rendering. These are
|
||||
// static values for each render and can be accessed using the decoration position as the key.
|
||||
const drawHeight = {
|
||||
full: 0,
|
||||
left: 0,
|
||||
|
||||
@@ -236,11 +236,9 @@ describe('CompositionHelper', () => {
|
||||
|
||||
it('Should insert middle composition and subsequent input without appending existing trailing text', (done) => {
|
||||
textarea.value = '一二';
|
||||
/*
|
||||
* screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen
|
||||
* readers), so the caret can be moved within the textarea (eg. via arrow keys) before
|
||||
* starting composition.
|
||||
*/
|
||||
// screenReaderMode keeps textarea content/selection for assistive technologies (eg. screen
|
||||
// readers), so the caret can be moved within the textarea (eg. via arrow keys) before
|
||||
// starting composition.
|
||||
textarea.selectionStart = 1;
|
||||
textarea.selectionEnd = 1;
|
||||
|
||||
|
||||
@@ -72,10 +72,8 @@ export class CompositionHelper {
|
||||
*/
|
||||
public compositionstart(): void {
|
||||
this._isComposing = true;
|
||||
/*
|
||||
* It's important to use the selection here instead of textarea length to avoid conflicts with
|
||||
* screen reader mode
|
||||
*/
|
||||
// It's important to use the selection here instead of textarea length to avoid conflicts with
|
||||
// screen reader mode
|
||||
const start = this._textarea.selectionStart ?? this._textarea.value.length;
|
||||
const end = this._textarea.selectionEnd ?? start;
|
||||
this._compositionPosition.start = Math.min(start, end);
|
||||
@@ -91,10 +89,8 @@ export class CompositionHelper {
|
||||
* @param ev The event.
|
||||
*/
|
||||
public compositionupdate(ev: Pick<CompositionEvent, 'data'>): void {
|
||||
/*
|
||||
* Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long
|
||||
* compositions
|
||||
*/
|
||||
// Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long
|
||||
// compositions
|
||||
this._compositionView.textContent = `\u200E${ev.data}\u200E`;
|
||||
this.updateCompositionElements();
|
||||
setTimeout(() => {
|
||||
@@ -119,28 +115,22 @@ export class CompositionHelper {
|
||||
public keydown(ev: KeyboardEvent): boolean {
|
||||
if (this._isComposing || this._isSendingComposition) {
|
||||
if (ev.keyCode === 20 || ev.keyCode === 229) {
|
||||
/*
|
||||
* 20 is CapsLock, 229 is Enter
|
||||
* Continue composing if the keyCode is the "composition character"
|
||||
*/
|
||||
// 20 is CapsLock, 229 is Enter
|
||||
// Continue composing if the keyCode is the "composition character"
|
||||
return false;
|
||||
}
|
||||
if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
|
||||
// Continue composing if the keyCode is a modifier key
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Finish composition immediately. This is mainly here for the case where enter is
|
||||
* pressed and the handler needs to be triggered before the command is executed.
|
||||
*/
|
||||
// Finish composition immediately. This is mainly here for the case where enter is
|
||||
// pressed and the handler needs to be triggered before the command is executed.
|
||||
this._finalizeComposition(false);
|
||||
}
|
||||
|
||||
if (ev.keyCode === 229) {
|
||||
/*
|
||||
* If the "composition character" is used but gets to this point it means a non-composition
|
||||
* character (eg. numbers and punctuation) was pressed when the IME was active.
|
||||
*/
|
||||
// If the "composition character" is used but gets to this point it means a non-composition
|
||||
// character (eg. numbers and punctuation) was pressed when the IME was active.
|
||||
this._handleAnyTextareaChanges();
|
||||
return false;
|
||||
}
|
||||
@@ -166,49 +156,39 @@ export class CompositionHelper {
|
||||
const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end);
|
||||
this._coreService.triggerDataEvent(input, true);
|
||||
} else {
|
||||
/*
|
||||
* Make a deep copy of the composition position here as a new compositionstart event may
|
||||
* fire before the setTimeout executes.
|
||||
*/
|
||||
// Make a deep copy of the composition position here as a new compositionstart event may
|
||||
// fire before the setTimeout executes.
|
||||
const currentCompositionPosition = {
|
||||
start: this._compositionPosition.start,
|
||||
end: this._compositionPosition.end
|
||||
};
|
||||
const currentCompositionSuffix = this._compositionSuffix;
|
||||
|
||||
/*
|
||||
* Since composition* events happen before the changes take place in the textarea on most
|
||||
* browsers, use a setTimeout with 0ms time to allow the native compositionend event to
|
||||
* complete. This ensures the correct character is retrieved.
|
||||
* This solution was used because:
|
||||
* - The compositionend event's data property is unreliable, at least on Chromium
|
||||
* - The last compositionupdate event's data property does not always accurately describe
|
||||
* the character, a counter example being Korean where an ending consonsant can move to
|
||||
* the following character if the following input is a vowel.
|
||||
*/
|
||||
// Since composition* events happen before the changes take place in the textarea on most
|
||||
// browsers, use a setTimeout with 0ms time to allow the native compositionend event to
|
||||
// complete. This ensures the correct character is retrieved.
|
||||
// This solution was used because:
|
||||
// - The compositionend event's data property is unreliable, at least on Chromium
|
||||
// - The last compositionupdate event's data property does not always accurately describe
|
||||
// the character, a counter example being Korean where an ending consonsant can move to
|
||||
// the following character if the following input is a vowel.
|
||||
this._isSendingComposition = true;
|
||||
setTimeout(() => {
|
||||
// Ensure that the input has not already been sent
|
||||
if (this._isSendingComposition) {
|
||||
this._isSendingComposition = false;
|
||||
let input;
|
||||
/*
|
||||
* Add length of data already sent due to keydown event,
|
||||
* otherwise input characters can be duplicated. (Issue #3191)
|
||||
*/
|
||||
// Add length of data already sent due to keydown event,
|
||||
// otherwise input characters can be duplicated. (Issue #3191)
|
||||
currentCompositionPosition.start += this._dataAlreadySent.length;
|
||||
if (this._isComposing) {
|
||||
/*
|
||||
* Use the start position of the new composition to get the string
|
||||
* if a new composition has started.
|
||||
*/
|
||||
// Use the start position of the new composition to get the string
|
||||
// if a new composition has started.
|
||||
input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start);
|
||||
} else {
|
||||
/*
|
||||
* Keep support for non-composition characters typed immediately after composition end
|
||||
* while avoiding re-sending the trailing text that was already present
|
||||
* before composition started.
|
||||
*/
|
||||
// Keep support for non-composition characters typed immediately after composition end
|
||||
// while avoiding re-sending the trailing text that was already present
|
||||
// before composition started.
|
||||
const value = this._textarea.value;
|
||||
const valueEnd = currentCompositionSuffix.length > 0 && value.endsWith(currentCompositionSuffix)
|
||||
? value.length - currentCompositionSuffix.length
|
||||
@@ -280,18 +260,14 @@ export class CompositionHelper {
|
||||
this._compositionView.style.lineHeight = cellHeight + 'px';
|
||||
this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily;
|
||||
this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px';
|
||||
/*
|
||||
* Limit the composition view width to the space between the cursor and
|
||||
* the terminal's right edge, preventing it from overflowing the terminal.
|
||||
*/
|
||||
// Limit the composition view width to the space between the cursor and
|
||||
// the terminal's right edge, preventing it from overflowing the terminal.
|
||||
const maxWidth = this._bufferService.cols * this._renderService.dimensions.css.cell.width - cursorLeft;
|
||||
this._compositionView.style.maxWidth = maxWidth + 'px';
|
||||
this._compositionView.style.overflow = 'hidden';
|
||||
this._compositionView.style.direction = 'rtl';
|
||||
/*
|
||||
* Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||
* text is.
|
||||
*/
|
||||
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||
// text is.
|
||||
const compositionViewBounds = this._compositionView.getBoundingClientRect();
|
||||
this._textarea.style.left = cursorLeft + 'px';
|
||||
this._textarea.style.top = cursorTop + 'px';
|
||||
|
||||
@@ -44,11 +44,9 @@ export function getCoords(window: Pick<Window, 'getComputedStyle'>, event: Pick<
|
||||
coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth);
|
||||
coords[1] = Math.ceil(coords[1] / cssCellHeight);
|
||||
|
||||
/*
|
||||
* Ensure coordinates are within the terminal viewport. Note that selections
|
||||
* need an addition point of precision to cover the end point (as characters
|
||||
* cover half of one char and half of the next).
|
||||
*/
|
||||
// Ensure coordinates are within the terminal viewport. Note that selections
|
||||
// need an addition point of precision to cover the end point (as characters
|
||||
// cover half of one char and half of the next).
|
||||
coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0));
|
||||
coords[1] = Math.min(Math.max(coords[1], 1), rowCount);
|
||||
|
||||
|
||||
@@ -54,11 +54,9 @@ export class Terminal extends Disposable implements ITerminalApi {
|
||||
}
|
||||
|
||||
private _checkReadonlyOptions(propName: string): void {
|
||||
/*
|
||||
* Throw an error if any constructor only option is modified
|
||||
* from terminal.options
|
||||
* Modifications from anywhere else are allowed
|
||||
*/
|
||||
// Throw an error if any constructor only option is modified
|
||||
// from terminal.options
|
||||
// Modifications from anywhere else are allowed
|
||||
if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) {
|
||||
throw new Error(`Option "${propName}" can only be set in the constructor`);
|
||||
}
|
||||
|
||||
@@ -114,10 +114,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
this._register(toDisposable(() => {
|
||||
this._element.classList.remove(Constants.TERMINAL_CLASS_PREFIX + this._terminalClass);
|
||||
|
||||
/*
|
||||
* Outside influences such as React unmounts may manipulate the DOM before our disposal.
|
||||
* https://github.com/xtermjs/xterm.js/issues/2960
|
||||
*/
|
||||
// Outside influences such as React unmounts may manipulate the DOM before our disposal.
|
||||
// https://github.com/xtermjs/xterm.js/issues/2960
|
||||
this._rowContainer.remove();
|
||||
this._selectionContainer.remove();
|
||||
this._widthCache.dispose();
|
||||
@@ -186,11 +184,9 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
// Base CSS
|
||||
let styles =
|
||||
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} {` +
|
||||
/*
|
||||
* Disabling pointer events circumvents a browser behavior that prevents `click` events from
|
||||
* being delivered if the target element is replaced during the click. This happened due to
|
||||
* refresh() being called during the mousedown handler to start a selection.
|
||||
*/
|
||||
// Disabling pointer events circumvents a browser behavior that prevents `click` events from
|
||||
// being delivered if the target element is replaced during the click. This happened due to
|
||||
// refresh() being called during the mousedown handler to start a selection.
|
||||
` pointer-events: none;` +
|
||||
` color: ${colors.foreground.css};` +
|
||||
`}`;
|
||||
@@ -261,11 +257,9 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS}.${Constants.CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
|
||||
` animation: none !important;` +
|
||||
`}` +
|
||||
/*
|
||||
* !important helps fix an issue where the cursor will not render on top of the selection,
|
||||
* however it's very hard to fix this issue and retain the blink animation without the use of
|
||||
* !important. So this edge case fails when cursor blink is on.
|
||||
*/
|
||||
// !important helps fix an issue where the cursor will not render on top of the selection,
|
||||
// however it's very hard to fix this issue and retain the blink animation without the use of
|
||||
// !important. So this edge case fails when cursor blink is on.
|
||||
`${this._terminalSelector} .${Constants.ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` background-color: ${colors.cursor.css};` +
|
||||
` color: ${colors.cursorAccent.css};` +
|
||||
|
||||
@@ -359,10 +359,8 @@ describe('DomRendererRowFactory', () => {
|
||||
});
|
||||
|
||||
describe('createRow with merged spans', () => {
|
||||
/*
|
||||
* for test purpose assume all in codepoints 0..255 are merging
|
||||
* const ALL_MERGING = new Uint8Array(FontMetrics.MAX);
|
||||
*/
|
||||
// for test purpose assume all in codepoints 0..255 are merging
|
||||
// const ALL_MERGING = new Uint8Array(FontMetrics.MAX);
|
||||
|
||||
beforeEach(() => {
|
||||
lineData = createEmptyLineData(10);
|
||||
|
||||
@@ -115,26 +115,20 @@ export class DomRendererRowFactory {
|
||||
// If true, indicates that the current character(s) to draw were joined.
|
||||
let isJoined = false;
|
||||
|
||||
/*
|
||||
* Indicates whether this cell is part of a joined range that should be ignored as it cannot
|
||||
* be rendered entirely, like the selection state differs across the range.
|
||||
*/
|
||||
// Indicates whether this cell is part of a joined range that should be ignored as it cannot
|
||||
// be rendered entirely, like the selection state differs across the range.
|
||||
let isValidJoinRange = (x >= skipJoinedCheckUntilX);
|
||||
|
||||
let lastCharX = x;
|
||||
|
||||
/*
|
||||
* Process any joined character ranges as needed. Because of how the
|
||||
* ranges are produced, we know that they are valid for the characters
|
||||
* and attributes of our input.
|
||||
*/
|
||||
// Process any joined character ranges as needed. Because of how the
|
||||
// ranges are produced, we know that they are valid for the characters
|
||||
// and attributes of our input.
|
||||
let cell: ICellData = this._workCell;
|
||||
if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) {
|
||||
const range = joinedRanges.shift()!;
|
||||
/*
|
||||
* If the ligature's selection state is not consistent, don't join it. This helps the
|
||||
* selection render correctly regardless whether they should be joined.
|
||||
*/
|
||||
// If the ligature's selection state is not consistent, don't join it. This helps the
|
||||
// selection render correctly regardless whether they should be joined.
|
||||
const firstSelectionState = this._isCellInSelection(range[0], row);
|
||||
for (i = range[0] + 1; i < range[1]; i++) {
|
||||
isValidJoinRange &&= (firstSelectionState === this._isCellInSelection(i, row));
|
||||
@@ -146,10 +140,8 @@ export class DomRendererRowFactory {
|
||||
} else {
|
||||
isJoined = true;
|
||||
|
||||
/*
|
||||
* We already know the exact start and end column of the joined range,
|
||||
* so we get the string and width representing it directly
|
||||
*/
|
||||
// We already know the exact start and end column of the joined range,
|
||||
// so we get the string and width representing it directly
|
||||
cell = new JoinedCellData(
|
||||
this._workCell,
|
||||
lineData.translateToString(true, range[0], range[1]),
|
||||
@@ -251,11 +243,9 @@ export class DomRendererRowFactory {
|
||||
oldIsInSelection = isInSelection;
|
||||
|
||||
if (isJoined) {
|
||||
/*
|
||||
* The DOM renderer colors the background of the cursor but for ligatures all cells are
|
||||
* joined. The workaround here is to show a cursor around the whole ligature so it shows up,
|
||||
* the cursor looks the same when on any character of the ligature though
|
||||
*/
|
||||
// The DOM renderer colors the background of the cursor but for ligatures all cells are
|
||||
// joined. The workaround here is to show a cursor around the whole ligature so it shows up,
|
||||
// the cursor looks the same when on any character of the ligature though
|
||||
if (cursorX >= x && cursorX <= lastCharX) {
|
||||
cursorX = x;
|
||||
}
|
||||
@@ -343,10 +333,8 @@ export class DomRendererRowFactory {
|
||||
classes.push(RowCss.STRIKETHROUGH_CLASS);
|
||||
}
|
||||
|
||||
/*
|
||||
* apply link hover underline late, effectively overrides any previous text-decoration
|
||||
* settings
|
||||
*/
|
||||
// apply link hover underline late, effectively overrides any previous text-decoration
|
||||
// settings
|
||||
if (isLinkHover) {
|
||||
charElement.style.textDecoration = 'underline';
|
||||
}
|
||||
@@ -365,10 +353,8 @@ export class DomRendererRowFactory {
|
||||
bgColorMode = temp2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply any decoration foreground/background overrides, this must happen after inverse has
|
||||
* been applied
|
||||
*/
|
||||
// Apply any decoration foreground/background overrides, this must happen after inverse has
|
||||
// been applied
|
||||
let bgOverride: IColor | undefined;
|
||||
let fgOverride: IColor | undefined;
|
||||
let isTop = false;
|
||||
@@ -391,19 +377,15 @@ export class DomRendererRowFactory {
|
||||
|
||||
// Apply selection
|
||||
if (!isTop && isInSelection) {
|
||||
/*
|
||||
* If in the selection, force the element to be above the selection to improve contrast and
|
||||
* support opaque selections. The applies background is not actually needed here as
|
||||
* selection is drawn in a seperate container, the main purpose of this to ensuring minimum
|
||||
* contrast ratio
|
||||
*/
|
||||
// If in the selection, force the element to be above the selection to improve contrast and
|
||||
// support opaque selections. The applies background is not actually needed here as
|
||||
// selection is drawn in a seperate container, the main purpose of this to ensuring minimum
|
||||
// contrast ratio
|
||||
bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque;
|
||||
bg = bgOverride.rgba >> 8 & 0xFFFFFF;
|
||||
bgColorMode = Attributes.CM_RGB;
|
||||
/*
|
||||
* Since an opaque selection is being rendered, the selection pretends to be a decoration to
|
||||
* ensure text is drawn above the selection.
|
||||
*/
|
||||
// Since an opaque selection is being rendered, the selection pretends to be a decoration to
|
||||
// ensure text is drawn above the selection.
|
||||
isTop = true;
|
||||
// Apply selection foreground if applicable
|
||||
if (colors.selectionForeground) {
|
||||
@@ -477,11 +459,9 @@ export class DomRendererRowFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* apply CSS classes
|
||||
* slightly faster than using classList by omitting
|
||||
* checks for doubled entries (code above should not have doublets)
|
||||
*/
|
||||
// apply CSS classes
|
||||
// slightly faster than using classList by omitting
|
||||
// checks for doubled entries (code above should not have doublets)
|
||||
if (classes.length) {
|
||||
charElement.className = classes.join(' ');
|
||||
classes.length = 0;
|
||||
@@ -524,10 +504,8 @@ export class DomRendererRowFactory {
|
||||
|
||||
// Calculate and store in cache
|
||||
if (adjustedColor === undefined) {
|
||||
/*
|
||||
* Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from
|
||||
* non-dim cells
|
||||
*/
|
||||
// Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from
|
||||
// non-dim cells
|
||||
const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1);
|
||||
adjustedColor = color.ensureContrastRatio(bgOverride ?? bg, fgOverride ?? fg, ratio);
|
||||
cache.setColor((bgOverride ?? bg).rgba, (fgOverride ?? fg).rgba, adjustedColor ?? null);
|
||||
|
||||
@@ -31,20 +31,16 @@ export interface IWidthCacheFontVariantCanvas {
|
||||
}
|
||||
|
||||
export class WidthCache implements IDisposable {
|
||||
/*
|
||||
* flat cache for regular variant up to CacheSettings.FLAT_SIZE
|
||||
* NOTE: ~4x faster access than holey (serving >>80% of terminal content)
|
||||
* It has a small memory footprint (only 1MB for full BMP caching),
|
||||
* still the sweet spot is not reached before touching 32k different codepoints,
|
||||
* thus we store the remaining <<20% of terminal data in a holey structure.
|
||||
*/
|
||||
// flat cache for regular variant up to CacheSettings.FLAT_SIZE
|
||||
// NOTE: ~4x faster access than holey (serving >>80% of terminal content)
|
||||
// It has a small memory footprint (only 1MB for full BMP caching),
|
||||
// still the sweet spot is not reached before touching 32k different codepoints,
|
||||
// thus we store the remaining <<20% of terminal data in a holey structure.
|
||||
protected _flat = new Float32Array(WidthCacheSettings.FLAT_SIZE);
|
||||
|
||||
/*
|
||||
* holey cache for bold, italic and bold&italic for any string
|
||||
* FIXME: can grow really big over time (~8.5 MB for full BMP caching),
|
||||
* so a shared API across terminals is needed
|
||||
*/
|
||||
// holey cache for bold, italic and bold&italic for any string
|
||||
// FIXME: can grow really big over time (~8.5 MB for full BMP caching),
|
||||
// so a shared API across terminals is needed
|
||||
protected _holey: Map<string, number> | undefined;
|
||||
|
||||
private _font = '';
|
||||
|
||||
@@ -13,10 +13,8 @@ describe('RendererUtils', () => {
|
||||
let line = 1;
|
||||
let variantOffset = 0;
|
||||
|
||||
/*
|
||||
* should line 1
|
||||
* =,_,=_,=_,
|
||||
*/
|
||||
// should line 1
|
||||
// =,_,=_,=_,
|
||||
let cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth];
|
||||
let result = [1, 0, 0, 0];
|
||||
for (let index = 0; index < cells.length; index++) {
|
||||
@@ -25,10 +23,8 @@ describe('RendererUtils', () => {
|
||||
assert.equal(variantOffset, result[index]);
|
||||
}
|
||||
|
||||
/*
|
||||
* should line 2
|
||||
* ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==,
|
||||
*/
|
||||
// should line 2
|
||||
// ==__==__==_,_==__==__==,__==__==__==__==__==__,==__==__==__==__==__==,
|
||||
line = 2;
|
||||
variantOffset = 0;
|
||||
cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth];
|
||||
@@ -39,10 +35,8 @@ describe('RendererUtils', () => {
|
||||
assert.equal(variantOffset, result[index]);
|
||||
}
|
||||
|
||||
/*
|
||||
* should line 3
|
||||
* ===___===__,_===___===_,__===___===___===___==,=___===___===___===___,
|
||||
*/
|
||||
// should line 3
|
||||
// ===___===__,_===___===_,__===___===___===___==,=___===___===___===___,
|
||||
line = 3;
|
||||
variantOffset = 0;
|
||||
cells = [cellWidth, cellWidth, doubleCellWidth, doubleCellWidth];
|
||||
|
||||
@@ -13,11 +13,9 @@ export function throwIfFalsy<T>(value: T | undefined | null): T {
|
||||
}
|
||||
|
||||
export function isPowerlineGlyph(codepoint: number): boolean {
|
||||
/*
|
||||
* Only return true for Powerline symbols which require
|
||||
* different padding and should be excluded from minimum contrast
|
||||
* ratio standards
|
||||
*/
|
||||
// Only return true for Powerline symbols which require
|
||||
// different padding and should be excluded from minimum contrast
|
||||
// ratio standards
|
||||
return 0xE0A4 <= codepoint && codepoint <= 0xE0D6;
|
||||
}
|
||||
|
||||
@@ -50,10 +48,8 @@ export function allowRescaling(codepoint: number | undefined, width: number, gly
|
||||
return (
|
||||
// Is single cell width
|
||||
width === 1 &&
|
||||
/*
|
||||
* Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that
|
||||
* barely overlap
|
||||
*/
|
||||
// Glyph exceeds cell bounds, add 50% to avoid hurting readability by rescaling glyphs that
|
||||
// barely overlap
|
||||
glyphSizeX > Math.ceil(deviceCellWidth * 1.5) &&
|
||||
// Never rescale ascii
|
||||
codepoint !== undefined && codepoint > 0xFF &&
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from '../Dom';
|
||||
import { FastDomNode } from './fastDomNode';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export class FastDomNode<T extends HTMLElement> {
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from '../Dom';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar';
|
||||
import { IScrollableElementResolvedOptions } from './scrollableElementOptions';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as platform from 'common/Platform';
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, IEvent } from 'common/Event';
|
||||
import { Disposable, IDisposable } from 'common/Lifecycle';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from '../Dom';
|
||||
import { FastDomNode } from './fastDomNode';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ScrollbarVisibility } from './scrollable';
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor';
|
||||
import { Widget } from './widget';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* The minimal size of the slider (such that it can still be clickable).
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FastDomNode } from './fastDomNode';
|
||||
import { TimeoutTimer } from 'common/Async';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DomUtils from '../Dom';
|
||||
import { Disposable, IDisposable, toDisposable } from 'common/Lifecycle';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AbstractScrollbar, ISimplifiedPointerEvent, IScrollbarHost } from './abstractScrollbar';
|
||||
import { IScrollableElementResolvedOptions } from './scrollableElementOptions';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from '../Dom';
|
||||
import { IMouseEvent, StandardMouseEvent } from './mouseEvent';
|
||||
|
||||
@@ -61,10 +61,8 @@ abstract class BaseMeasureStategy extends Disposable implements IMeasureStrategy
|
||||
protected _result: IMeasureResult = { width: 0, height: 0 };
|
||||
|
||||
protected _validateAndSet(width: number | undefined, height: number | undefined): void {
|
||||
/*
|
||||
* If values are 0 then the element is likely currently display:none, in which case we should
|
||||
* retain the previous value.
|
||||
*/
|
||||
// If values are 0 then the element is likely currently display:none, in which case we should
|
||||
// retain the previous value.
|
||||
if (width !== undefined && width > 0 && height !== undefined && height > 0) {
|
||||
this._result.width = width;
|
||||
this._result.height = height;
|
||||
|
||||
@@ -13,10 +13,8 @@ import { ICharacterJoinerService } from 'browser/services/Services';
|
||||
|
||||
export class JoinedCellData extends AttributeData implements ICellData {
|
||||
private _width: number;
|
||||
/*
|
||||
* .content carries no meaning for joined CellData, simply nullify it
|
||||
* thus we have to overload all other .content accessors
|
||||
*/
|
||||
// .content carries no meaning for joined CellData, simply nullify it
|
||||
// thus we have to overload all other .content accessors
|
||||
public content: number = 0;
|
||||
public fg: number;
|
||||
public bg: number;
|
||||
@@ -44,10 +42,8 @@ export class JoinedCellData extends AttributeData implements ICellData {
|
||||
}
|
||||
|
||||
public getCode(): number {
|
||||
/*
|
||||
* code always gets the highest possible fake codepoint (read as -1)
|
||||
* this is needed as code is used by caches as identifier
|
||||
*/
|
||||
// code always gets the highest possible fake codepoint (read as -1)
|
||||
// this is needed as code is used by caches as identifier
|
||||
return 0x1FFFFF;
|
||||
}
|
||||
|
||||
@@ -105,12 +101,10 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
const ranges: [number, number][] = [];
|
||||
const lineStr = line.translateToString(true);
|
||||
|
||||
/*
|
||||
* Because some cells can be represented by multiple javascript characters,
|
||||
* we track the cell and the string indexes separately. This allows us to
|
||||
* translate the string ranges we get from the joiners back into cell ranges
|
||||
* for use when rendering
|
||||
*/
|
||||
// Because some cells can be represented by multiple javascript characters,
|
||||
// we track the cell and the string indexes separately. This allows us to
|
||||
// translate the string ranges we get from the joiners back into cell ranges
|
||||
// for use when rendering
|
||||
let rangeStartColumn = 0;
|
||||
let currentStringIndex = 0;
|
||||
let rangeStartStringIndex = 0;
|
||||
@@ -127,10 +121,8 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
|
||||
// End of range
|
||||
if (this._workCell.fg !== rangeAttrFG || this._workCell.bg !== rangeAttrBG) {
|
||||
/*
|
||||
* If we ended up with a sequence of more than one character,
|
||||
* look for ranges to join.
|
||||
*/
|
||||
// If we ended up with a sequence of more than one character,
|
||||
// look for ranges to join.
|
||||
if (x - rangeStartColumn > 1) {
|
||||
const joinedRanges = this._getJoinedRanges(
|
||||
lineStr,
|
||||
@@ -181,11 +173,9 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
*/
|
||||
private _getJoinedRanges(line: string, startIndex: number, endIndex: number, lineData: IBufferLine, startCol: number): [number, number][] {
|
||||
const text = line.substring(startIndex, endIndex);
|
||||
/*
|
||||
* At this point we already know that there is at least one joiner so
|
||||
* we can just pull its value and assign it directly rather than
|
||||
* merging it into an empty array, which incurs unnecessary writes.
|
||||
*/
|
||||
// At this point we already know that there is at least one joiner so
|
||||
// we can just pull its value and assign it directly rather than
|
||||
// merging it into an empty array, which incurs unnecessary writes.
|
||||
let allJoinedRanges: [number, number][] = [];
|
||||
try {
|
||||
allJoinedRanges = this._characterJoiners[0].handler(text);
|
||||
@@ -230,10 +220,8 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
const width = line.getWidth(x);
|
||||
const length = line.getString(x).length || WHITESPACE_CELL_CHAR.length;
|
||||
|
||||
/*
|
||||
* We skip zero-width characters when creating the string to join the text
|
||||
* so we do the same here
|
||||
*/
|
||||
// We skip zero-width characters when creating the string to join the text
|
||||
// so we do the same here
|
||||
if (width === 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -256,12 +244,10 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ranges can be on adjacent characters. Because the end index of the
|
||||
* ranges are exclusive, this means that the index for the start of a
|
||||
* range can be the same as the end index of the previous range. To
|
||||
* account for the start of the next range, we check here just in case.
|
||||
*/
|
||||
// Ranges can be on adjacent characters. Because the end index of the
|
||||
// ranges are exclusive, this means that the index for the start of a
|
||||
// range can be the same as the end index of the previous range. To
|
||||
// account for the start of the next range, we check here just in case.
|
||||
if (currentRange[0] <= currentStringIndex) {
|
||||
currentRange[0] = x;
|
||||
currentRangeStarted = true;
|
||||
@@ -270,17 +256,13 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the string index based on the character length to line up with
|
||||
* the column adjustment
|
||||
*/
|
||||
// Adjust the string index based on the character length to line up with
|
||||
// the column adjustment
|
||||
currentStringIndex += length;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is still a range left at the end, it must extend all the way to
|
||||
* the end of the line.
|
||||
*/
|
||||
// If there is still a range left at the end, it must extend all the way to
|
||||
// the end of the line.
|
||||
if (currentRange) {
|
||||
currentRange[1] = this._bufferService.cols;
|
||||
}
|
||||
@@ -306,19 +288,15 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
}
|
||||
|
||||
if (newRange[1] <= range[1]) {
|
||||
/*
|
||||
* Case 2: New range is either wholly contained within the
|
||||
* search range or overlaps with the front of it
|
||||
*/
|
||||
// Case 2: New range is either wholly contained within the
|
||||
// search range or overlaps with the front of it
|
||||
range[0] = Math.min(newRange[0], range[0]);
|
||||
return ranges;
|
||||
}
|
||||
|
||||
if (newRange[0] < range[1]) {
|
||||
/*
|
||||
* Case 3: New range either wholly contains the search range
|
||||
* or overlaps with the end of it
|
||||
*/
|
||||
// Case 3: New range either wholly contains the search range
|
||||
// or overlaps with the end of it
|
||||
range[0] = Math.min(newRange[0], range[0]);
|
||||
inRange = true;
|
||||
}
|
||||
@@ -327,28 +305,22 @@ export class CharacterJoinerService implements ICharacterJoinerService {
|
||||
continue;
|
||||
} else {
|
||||
if (newRange[1] <= range[0]) {
|
||||
/*
|
||||
* Case 5: New range extends from previous range but doesn't
|
||||
* reach the current one
|
||||
*/
|
||||
// Case 5: New range extends from previous range but doesn't
|
||||
// reach the current one
|
||||
ranges[i - 1][1] = newRange[1];
|
||||
return ranges;
|
||||
}
|
||||
|
||||
if (newRange[1] <= range[1]) {
|
||||
/*
|
||||
* Case 6: New range extends from prvious range into the
|
||||
* current range
|
||||
*/
|
||||
// Case 6: New range extends from prvious range into the
|
||||
// current range
|
||||
ranges[i - 1][1] = Math.max(newRange[1], range[1]);
|
||||
ranges.splice(i, 1);
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/*
|
||||
* Case 7: New range extends from previous range past the
|
||||
* end of the current range
|
||||
*/
|
||||
// Case 7: New range extends from previous range past the
|
||||
// end of the current range
|
||||
ranges.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
|
||||
@@ -127,10 +127,8 @@ describe('MouseService _triggerMouseEvent', () => {
|
||||
assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.RIGHT, action: CoreMouseAction.DOWN }), true);
|
||||
assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.UP }), true);
|
||||
assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.MOVE }), true);
|
||||
/*
|
||||
* should not report in any case
|
||||
* invalid button + action combinations
|
||||
*/
|
||||
// should not report in any case
|
||||
// invalid button + action combinations
|
||||
assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.WHEEL, action: CoreMouseAction.MOVE }), false);
|
||||
assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.DOWN }), false);
|
||||
assert.equal(trigger({ col: 0, row: 0, x: 0, y: 0, button: CoreMouseButton.NONE, action: CoreMouseAction.UP }), false);
|
||||
|
||||
@@ -147,10 +147,8 @@ export class MouseService implements IMouseService {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* exit if we cannot determine valid button/action values
|
||||
* do nothing for higher buttons than wheel
|
||||
*/
|
||||
// exit if we cannot determine valid button/action values
|
||||
// do nothing for higher buttons than wheel
|
||||
if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) {
|
||||
return false;
|
||||
}
|
||||
@@ -206,23 +204,19 @@ export class MouseService implements IMouseService {
|
||||
ev.preventDefault();
|
||||
ctx.focus();
|
||||
|
||||
/*
|
||||
* Don't send the mouse button to the pty if mouse events are disabled or
|
||||
* if the selection manager is having selection forced (ie. a modifier is
|
||||
* held).
|
||||
*/
|
||||
// Don't send the mouse button to the pty if mouse events are disabled or
|
||||
// if the selection manager is having selection forced (ie. a modifier is
|
||||
// held).
|
||||
if (!this._mouseStateService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendEvent(ctx, ev);
|
||||
|
||||
/*
|
||||
* Register additional global handlers which should keep reporting outside
|
||||
* of the terminal element.
|
||||
* Note: Other emulators also do this for 'mousedown' while a button
|
||||
* is held, we currently limit 'mousedown' to the terminal only.
|
||||
*/
|
||||
// Register additional global handlers which should keep reporting outside
|
||||
// of the terminal element.
|
||||
// Note: Other emulators also do this for 'mousedown' while a button
|
||||
// is held, we currently limit 'mousedown' to the terminal only.
|
||||
if (ctx.requestedEvents.mouseup) {
|
||||
ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup);
|
||||
}
|
||||
@@ -242,14 +236,12 @@ export class MouseService implements IMouseService {
|
||||
}
|
||||
|
||||
if (!this._bufferService.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 even when mouse
|
||||
* events are not enabled.
|
||||
* This used implementation used get the actual lines/partial lines scrolled from the
|
||||
* viewport but since moving to the new viewport implementation has been simplified to
|
||||
* simply send a single up or down sequence.
|
||||
*/
|
||||
// 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 even when mouse
|
||||
// events are not enabled.
|
||||
// This used implementation used get the actual lines/partial lines scrolled from the
|
||||
// viewport but since moving to the new viewport implementation has been simplified to
|
||||
// simply send a single up or down sequence.
|
||||
|
||||
// Do nothing if there's no vertical scroll
|
||||
const deltaY = ev.deltaY;
|
||||
|
||||
@@ -88,11 +88,9 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
this._register(this._optionsService.onOptionChange(() => this._handleOptionsChanged()));
|
||||
this._register(this._charSizeService.onCharSizeChange(() => this.handleCharSizeChanged()));
|
||||
|
||||
/*
|
||||
* Do a full refresh whenever any decoration is added or removed. This may not actually result
|
||||
* in changes but since decorations should be used sparingly or added/removed all in the same
|
||||
* frame this should have minimal performance impact.
|
||||
*/
|
||||
// Do a full refresh whenever any decoration is added or removed. This may not actually result
|
||||
// in changes but since decorations should be used sparingly or added/removed all in the same
|
||||
// frame this should have minimal performance impact.
|
||||
this._register(decorationService.onDecorationRegistered(() => this._fullRefresh()));
|
||||
this._register(decorationService.onDecorationRemoved(() => this._fullRefresh()));
|
||||
|
||||
@@ -126,10 +124,8 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
private _registerIntersectionObserver(w: Window & typeof globalThis, screenElement: HTMLElement): void {
|
||||
/*
|
||||
* Detect whether IntersectionObserver is detected and enable renderer pause
|
||||
* and resume based on terminal visibility if so
|
||||
*/
|
||||
// Detect whether IntersectionObserver is detected and enable renderer pause
|
||||
// and resume based on terminal visibility if so
|
||||
if ('IntersectionObserver' in w) {
|
||||
const observer = new w.IntersectionObserver(e => this._handleIntersectionChange(e[e.length - 1]), { threshold: 0 });
|
||||
this._observerDisposable.value = toDisposable(() => {
|
||||
@@ -190,20 +186,16 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip rendering if synchronized output mode is enabled. This check must happen here
|
||||
* (in addition to refreshRows) to handle renders that were queued before the mode was enabled.
|
||||
*/
|
||||
// Skip rendering if synchronized output mode is enabled. This check must happen here
|
||||
// (in addition to refreshRows) to handle renders that were queued before the mode was enabled.
|
||||
if (this._coreService.decPrivateModes.synchronizedOutput) {
|
||||
this._syncOutputHandler.bufferRows(start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since this is debounced, a resize event could have happened between the time a refresh was
|
||||
* requested and when this triggers. Clamp the values of start and end to ensure they're valid
|
||||
* given the current viewport state.
|
||||
*/
|
||||
// Since this is debounced, a resize event could have happened between the time a refresh was
|
||||
// requested and when this triggers. Clamp the values of start and end to ensure they're valid
|
||||
// given the current viewport state.
|
||||
start = Math.min(start, this._rowCount - 1);
|
||||
end = Math.min(end, this._rowCount - 1);
|
||||
|
||||
@@ -285,10 +277,8 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
public handleDevicePixelRatioChange(): void {
|
||||
/*
|
||||
* Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable
|
||||
* when devicePixelRatio changes
|
||||
*/
|
||||
// Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable
|
||||
// when devicePixelRatio changes
|
||||
this._charSizeService.measure();
|
||||
|
||||
if (!this._renderer.value) {
|
||||
|
||||
@@ -257,10 +257,8 @@ describe('SelectionService', () => {
|
||||
])); // The a is here to prevent the space being trimmed in selectionText
|
||||
selectionService.selectWordAt([0, 0]);
|
||||
assert.equal(selectionService.selectionText, ' ');
|
||||
/*
|
||||
* ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells
|
||||
* The selection text should retain ZWJ chars despite not combining on the terminal
|
||||
*/
|
||||
// ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells
|
||||
// The selection text should retain ZWJ chars despite not combining on the terminal
|
||||
selectionService.selectWordAt([1, 0]);
|
||||
assert.equal(selectionService.selectionText, '👨👩👧👦');
|
||||
selectionService.selectWordAt([2, 0]);
|
||||
@@ -475,10 +473,8 @@ describe('SelectionService', () => {
|
||||
});
|
||||
|
||||
it('should select a column of text with double character emojis', () => {
|
||||
/*
|
||||
* TODO the case this is testing works for me in the demo webapp,
|
||||
* but doing it programmatically fails.
|
||||
*/
|
||||
// TODO the case this is testing works for me in the demo webapp,
|
||||
// but doing it programmatically fails.
|
||||
buffer.lines.length = 3;
|
||||
buffer.lines.set(0, stringToRow('a '));
|
||||
buffer.lines.set(1, stringArrayToRow(['😁', ' ']));
|
||||
|
||||
@@ -152,10 +152,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this._removeMouseDownListeners();
|
||||
}));
|
||||
|
||||
/*
|
||||
* Clear selection when resizing vertically. This experience could be improved, this is the
|
||||
* simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300
|
||||
*/
|
||||
// Clear selection when resizing vertically. This experience could be improved, this is the
|
||||
// simple option to fix the buggy behavior. https://github.com/xtermjs/xterm.js/issues/5300
|
||||
this._register(this._bufferService.onResize(e => {
|
||||
if (e.rowsChanged) {
|
||||
this.clearSelection();
|
||||
@@ -217,10 +215,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
return '';
|
||||
}
|
||||
|
||||
/*
|
||||
* For column selection it's not enough to rely on final selection's swapping of reversed
|
||||
* values, it also needs the x coordinates to swap independently of the y coordinate is needed
|
||||
*/
|
||||
// For column selection it's not enough to rely on final selection's swapping of reversed
|
||||
// values, it also needs the x coordinates to swap independently of the y coordinate is needed
|
||||
const startCol = start[0] < end[0] ? start[0] : end[0];
|
||||
const endCol = start[0] < end[0] ? end[0] : start[0];
|
||||
for (let i = start[1]; i <= end[1]; i++) {
|
||||
@@ -255,10 +251,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Format string by replacing non-breaking space chars with regular spaces
|
||||
* and joining the array into a multi-line string.
|
||||
*/
|
||||
// Format string by replacing non-breaking space chars with regular spaces
|
||||
// and joining the array into a multi-line string.
|
||||
const formattedResult = result.map(line => {
|
||||
return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' ');
|
||||
}).join(Browser.isWindows ? '\r\n' : '\n');
|
||||
@@ -287,10 +281,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._refresh());
|
||||
}
|
||||
|
||||
/*
|
||||
* If the platform is Linux and the refresh call comes from a mouse event,
|
||||
* we need to update the selection for middle click to paste selection.
|
||||
*/
|
||||
// If the platform is Linux and the refresh call comes from a mouse event,
|
||||
// we need to update the selection for middle click to paste selection.
|
||||
if (Browser.isLinux && isLinuxMouseSelection) {
|
||||
const selectionText = this.selectionText;
|
||||
if (selectionText.length) {
|
||||
@@ -455,10 +447,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
*/
|
||||
public handleMouseDown(event: MouseEvent): void {
|
||||
this._mouseDownTimeStamp = event.timeStamp;
|
||||
/*
|
||||
* If we have selection, we want the context menu on right click even if the
|
||||
* terminal is in mouse mode.
|
||||
*/
|
||||
// If we have selection, we want the context menu on right click even if the
|
||||
// terminal is in mouse mode.
|
||||
if (event.button === 2 && this.hasSelection) {
|
||||
return;
|
||||
}
|
||||
@@ -571,10 +561,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the mouse is over the second half of a wide character, adjust the
|
||||
* selection to cover the whole character
|
||||
*/
|
||||
// If the mouse is over the second half of a wide character, adjust the
|
||||
// selection to cover the whole character
|
||||
if (line.hasWidth(this._model.selectionStart[0]) === 0) {
|
||||
this._model.selectionStart[0]++;
|
||||
}
|
||||
@@ -617,25 +605,19 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
* @param event The mousemove event.
|
||||
*/
|
||||
private _handleMouseMove(event: MouseEvent): void {
|
||||
/*
|
||||
* If the mousemove listener is active it means that a selection is
|
||||
* currently being made, we should stop propagation to prevent mouse events
|
||||
* to be sent to the pty.
|
||||
*/
|
||||
// If the mousemove listener is active it means that a selection is
|
||||
// currently being made, we should stop propagation to prevent mouse events
|
||||
// to be sent to the pty.
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
/*
|
||||
* Do nothing if there is no selection start, this can happen if the first
|
||||
* click in the terminal is an incremental click
|
||||
*/
|
||||
// Do nothing if there is no selection start, this can happen if the first
|
||||
// click in the terminal is an incremental click
|
||||
if (!this._model.selectionStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record the previous position so we know whether to redraw the selection
|
||||
* at the end.
|
||||
*/
|
||||
// Record the previous position so we know whether to redraw the selection
|
||||
// at the end.
|
||||
const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;
|
||||
|
||||
// Set the initial selection end based on the mouse coordinates
|
||||
@@ -659,11 +641,9 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
// Determine the amount of scrolling that will happen.
|
||||
this._dragScrollAmount = this._getMouseEventScrollAmount(event);
|
||||
|
||||
/*
|
||||
* If the cursor was above or below the viewport, make sure it's at the
|
||||
* start or end of the viewport respectively. This should only happen when
|
||||
* NOT in column select mode.
|
||||
*/
|
||||
// If the cursor was above or below the viewport, make sure it's at the
|
||||
// start or end of the viewport respectively. This should only happen when
|
||||
// NOT in column select mode.
|
||||
if (this._activeSelectionMode !== SelectionMode.COLUMN) {
|
||||
if (this._dragScrollAmount > 0) {
|
||||
this._model.selectionEnd[0] = this._bufferService.cols;
|
||||
@@ -672,11 +652,9 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the character is a wide character include the cell to the right in the
|
||||
* selection. Note that selections at the very end of the line will never
|
||||
* have a character.
|
||||
*/
|
||||
// If the character is a wide character include the cell to the right in the
|
||||
// selection. Note that selections at the very end of the line will never
|
||||
// have a character.
|
||||
const buffer = this._bufferService.buffer;
|
||||
if (this._model.selectionEnd[1] < buffer.lines.length) {
|
||||
const line = buffer.lines.get(this._model.selectionEnd[1]);
|
||||
@@ -705,12 +683,10 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
}
|
||||
if (this._dragScrollAmount) {
|
||||
this._onRequestScrollLines.fire({ amount: this._dragScrollAmount, suppressScrollEvent: false });
|
||||
/*
|
||||
* Re-evaluate selection
|
||||
* If the cursor was above or below the viewport, make sure it's at the
|
||||
* start or end of the viewport respectively. This should only happen when
|
||||
* NOT in column select mode.
|
||||
*/
|
||||
// Re-evaluate selection
|
||||
// If the cursor was above or below the viewport, make sure it's at the
|
||||
// start or end of the viewport respectively. This should only happen when
|
||||
// NOT in column select mode.
|
||||
const buffer = this._bufferService.buffer;
|
||||
if (this._dragScrollAmount > 0) {
|
||||
if (this._activeSelectionMode !== SelectionMode.COLUMN) {
|
||||
@@ -789,12 +765,10 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
|
||||
private _handleBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void {
|
||||
this.clearSelection();
|
||||
/*
|
||||
* Only adjust the selection on trim, shiftElements is rarely used (only in
|
||||
* reverseIndex) and delete in a splice is only ever used when the same
|
||||
* number of elements was just added. Given this is could actually be
|
||||
* beneficial to leave the selection as is for these cases.
|
||||
*/
|
||||
// Only adjust the selection on trim, shiftElements is rarely used (only in
|
||||
// reverseIndex) and delete in a splice is only ever used when the same
|
||||
// number of elements was just added. Given this is could actually be
|
||||
// beneficial to leave the selection as is for these cases.
|
||||
this._trimListener.dispose();
|
||||
this._trimListener = e.activeBuffer.lines.onTrim(amount => this._handleTrim(amount));
|
||||
}
|
||||
@@ -810,17 +784,13 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
for (let i = 0; x >= i; i++) {
|
||||
const length = bufferLine.loadCell(i, this._workCell).getChars().length;
|
||||
if (this._workCell.getWidth() === 0) {
|
||||
/*
|
||||
* Wide characters aren't included in the line string so decrement the
|
||||
* index so the index is back on the wide character.
|
||||
*/
|
||||
// Wide characters aren't included in the line string so decrement the
|
||||
// index so the index is back on the wide character.
|
||||
charIndex--;
|
||||
} else if (length > 1 && x !== i) {
|
||||
/*
|
||||
* Emojis take up multiple characters, so adjust accordingly. For these
|
||||
* we don't want ot include the character at the column as we're
|
||||
* returning the start index in the string, not the end index.
|
||||
*/
|
||||
// Emojis take up multiple characters, so adjust accordingly. For these
|
||||
// we don't want ot include the character at the column as we're
|
||||
// returning the start index in the string, not the end index.
|
||||
charIndex += length - 1;
|
||||
}
|
||||
}
|
||||
@@ -883,19 +853,15 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
endIndex++;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Expand until whitespace is hit. This algorithm works by scanning left
|
||||
* and right from the starting position, keeping both the index format
|
||||
* (line) and the column format (bufferLine) in sync. When a wide
|
||||
* character is hit, it is recorded and the column index is adjusted.
|
||||
*/
|
||||
// Expand until whitespace is hit. This algorithm works by scanning left
|
||||
// and right from the starting position, keeping both the index format
|
||||
// (line) and the column format (bufferLine) in sync. When a wide
|
||||
// character is hit, it is recorded and the column index is adjusted.
|
||||
let startCol = coords[0];
|
||||
let endCol = coords[0];
|
||||
|
||||
/*
|
||||
* Consider the initial position, skip it and increment the wide char
|
||||
* variable
|
||||
*/
|
||||
// Consider the initial position, skip it and increment the wide char
|
||||
// variable
|
||||
if (bufferLine.getWidth(startCol) === 0) {
|
||||
leftWideCharCount++;
|
||||
startCol--;
|
||||
@@ -921,10 +887,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
leftWideCharCount++;
|
||||
startCol--;
|
||||
} else if (length > 1) {
|
||||
/*
|
||||
* If the next character's string is longer than 1 char (eg. emoji),
|
||||
* adjust the index
|
||||
*/
|
||||
// If the next character's string is longer than 1 char (eg. emoji),
|
||||
// adjust the index
|
||||
leftLongCharOffset += length - 1;
|
||||
startIndex -= length - 1;
|
||||
}
|
||||
@@ -939,10 +903,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
rightWideCharCount++;
|
||||
endCol++;
|
||||
} else if (length > 1) {
|
||||
/*
|
||||
* If the next character's string is longer than 1 char (eg. emoji),
|
||||
* adjust the index
|
||||
*/
|
||||
// If the next character's string is longer than 1 char (eg. emoji),
|
||||
// adjust the index
|
||||
rightLongCharOffset += length - 1;
|
||||
endIndex += length - 1;
|
||||
}
|
||||
@@ -954,20 +916,16 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
// Incremenet the end index so it is at the start of the next character
|
||||
endIndex++;
|
||||
|
||||
/*
|
||||
* Calculate the start _column_, converting the the string indexes back to
|
||||
* column coordinates.
|
||||
*/
|
||||
// Calculate the start _column_, converting the the string indexes back to
|
||||
// column coordinates.
|
||||
let start =
|
||||
startIndex // The index of the selection's start char in the line string
|
||||
+ charOffset // The difference between the initial char's column and index
|
||||
- leftWideCharCount // The number of wide chars left of the initial char
|
||||
+ leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)
|
||||
|
||||
/*
|
||||
* Calculate the length in _columns_, converting the the string indexes back
|
||||
* to column coordinates.
|
||||
*/
|
||||
// Calculate the length in _columns_, converting the the string indexes back
|
||||
// to column coordinates.
|
||||
let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols
|
||||
endIndex // The index of the selection's end char in the line string
|
||||
- startIndex // The index of the selection's start char in the line string
|
||||
@@ -1044,10 +1002,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
endRow--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust wrapped length value, this only needs to happen when values are reversed as in that
|
||||
* case we're interested in the start of the word, not the end
|
||||
*/
|
||||
// Adjust wrapped length value, this only needs to happen when values are reversed as in that
|
||||
// case we're interested in the start of the word, not the end
|
||||
if (!this._model.areSelectionValuesReversed()) {
|
||||
while (wordPosition.start + wordPosition.length > this._bufferService.cols) {
|
||||
wordPosition.length -= this._bufferService.cols;
|
||||
@@ -1065,10 +1021,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
* @param cell The cell to check.
|
||||
*/
|
||||
private _isCharWordSeparator(cell: ICellData): boolean {
|
||||
/*
|
||||
* Zero width characters are never separators as they are always to the
|
||||
* right of wide characters
|
||||
*/
|
||||
// Zero width characters are never separators as they are always to the
|
||||
// right of wide characters
|
||||
if (cell.getWidth() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -52,10 +52,8 @@ export class CircularList<T> extends Disposable implements ICircularList<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconstruct array, starting at index 0. Only transfer values from the
|
||||
* indexes 0 to length.
|
||||
*/
|
||||
// Reconstruct array, starting at index 0. Only transfer values from the
|
||||
// indexes 0 to length.
|
||||
const newArray = new Array<T | undefined>(newMaxLength);
|
||||
for (let i = 0; i < Math.min(newMaxLength, this.length); i++) {
|
||||
newArray[i] = this._array[this._getCyclicIndex(i)];
|
||||
|
||||
+12
-22
@@ -27,11 +27,9 @@ export namespace channels {
|
||||
}
|
||||
|
||||
export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number {
|
||||
/*
|
||||
* Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32
|
||||
* on LE systems, before it can be used for direct 32-bit buffer writes.
|
||||
* >>> 0 forces an unsigned int
|
||||
*/
|
||||
// Note: The aggregated number is RGBA32 (BE), thus needs to be converted to ABGR32
|
||||
// on LE systems, before it can be used for direct 32-bit buffer writes.
|
||||
// >>> 0 forces an unsigned int
|
||||
return (r << 24 | g << 16 | b << 8 | a) >>> 0;
|
||||
}
|
||||
|
||||
@@ -199,10 +197,8 @@ export namespace css {
|
||||
throw new Error('css.toColor: Unsupported css format');
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate the color using canvas fillStyle
|
||||
* See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles
|
||||
*/
|
||||
// Validate the color using canvas fillStyle
|
||||
// See https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles
|
||||
$ctx.fillStyle = $litmusColor;
|
||||
$ctx.fillStyle = css;
|
||||
if (typeof $ctx.fillStyle !== 'string') {
|
||||
@@ -217,11 +213,9 @@ export namespace css {
|
||||
throw new Error('css.toColor: Unsupported css format');
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the color from the canvas' fillStyle property which exposes the color value in rgba()
|
||||
* format
|
||||
* See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color
|
||||
*/
|
||||
// Extract the color from the canvas' fillStyle property which exposes the color value in rgba()
|
||||
// format
|
||||
// See https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color
|
||||
return {
|
||||
rgba: channels.toRgba($r, $g, $b, $a),
|
||||
css
|
||||
@@ -327,10 +321,8 @@ export namespace rgba {
|
||||
}
|
||||
|
||||
export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number {
|
||||
/*
|
||||
* This is a naive but fast approach to reducing luminance as converting to
|
||||
* HSL and back is expensive
|
||||
*/
|
||||
// This is a naive but fast approach to reducing luminance as converting to
|
||||
// HSL and back is expensive
|
||||
const bgR = (bgRgba >> 24) & 0xFF;
|
||||
const bgG = (bgRgba >> 16) & 0xFF;
|
||||
const bgB = (bgRgba >> 8) & 0xFF;
|
||||
@@ -349,10 +341,8 @@ export namespace rgba {
|
||||
}
|
||||
|
||||
export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number {
|
||||
/*
|
||||
* This is a naive but fast approach to increasing luminance as converting to
|
||||
* HSL and back is expensive
|
||||
*/
|
||||
// This is a naive but fast approach to increasing luminance as converting to
|
||||
// HSL and back is expensive
|
||||
const bgR = (bgRgba >> 24) & 0xFF;
|
||||
const bgG = (bgRgba >> 16) & 0xFF;
|
||||
const bgB = (bgRgba >> 8) & 0xFF;
|
||||
|
||||
@@ -176,10 +176,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
x = Math.max(x, BufferServiceConstants.MINIMUM_COLS);
|
||||
y = Math.max(y, BufferServiceConstants.MINIMUM_ROWS);
|
||||
|
||||
/*
|
||||
* Flush pending writes before resize to avoid race conditions where async
|
||||
* writes are processed with incorrect dimensions
|
||||
*/
|
||||
// Flush pending writes before resize to avoid race conditions where async
|
||||
// writes are processed with incorrect dimensions
|
||||
this._writeBuffer.flushSync();
|
||||
|
||||
this._bufferService.resize(x, y);
|
||||
|
||||
@@ -613,10 +613,8 @@ describe('InputHandler', () => {
|
||||
await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2
|
||||
for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols));
|
||||
|
||||
/*
|
||||
* params[1] left and above with wrap
|
||||
* confirm precondition that line 2 is wrapped
|
||||
*/
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true);
|
||||
bufferService.buffer.y = 2;
|
||||
bufferService.buffer.x = 40;
|
||||
@@ -630,10 +628,8 @@ describe('InputHandler', () => {
|
||||
await inputHandler.parseP('a'.repeat(bufferService.cols + 9)); // line 1 and 2
|
||||
for (let i = 3; i < bufferService.rows; ++i) await inputHandler.parseP('a'.repeat(bufferService.cols));
|
||||
|
||||
/*
|
||||
* params[1] left and above with wrap
|
||||
* confirm precondition that line 2 is wrapped
|
||||
*/
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true);
|
||||
bufferService.buffer.y = 1;
|
||||
bufferService.buffer.x = 90; // Cursor is beyond last column
|
||||
|
||||
+121
-203
@@ -33,12 +33,10 @@ const GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-':
|
||||
/**
|
||||
* Document xterm VT features here that are currently unsupported
|
||||
*/
|
||||
/*
|
||||
* @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys."
|
||||
* @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String."
|
||||
* @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data."
|
||||
* @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name."
|
||||
*/
|
||||
// @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys."
|
||||
// @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String."
|
||||
// @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data."
|
||||
// @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name."
|
||||
|
||||
/**
|
||||
* Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
|
||||
@@ -290,17 +288,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._parser.registerOscHandler(1, new OscHandler(data => this.setIconName(data)));
|
||||
// 2 - title
|
||||
this._parser.registerOscHandler(2, new OscHandler(data => this.setTitle(data)));
|
||||
/*
|
||||
* 3 - set property X in the form "prop=value"
|
||||
* 4 - Change Color Number
|
||||
*/
|
||||
// 3 - set property X in the form "prop=value"
|
||||
// 4 - Change Color Number
|
||||
this._parser.registerOscHandler(4, new OscHandler(data => this.setOrReportIndexedColor(data)));
|
||||
/*
|
||||
* 5 - Change Special Color Number
|
||||
* 6 - Enable/disable Special Color Number c
|
||||
* 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939)
|
||||
* 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda)
|
||||
*/
|
||||
// 5 - Change Special Color Number
|
||||
// 6 - Enable/disable Special Color Number c
|
||||
// 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939)
|
||||
// 8 - create hyperlink (not in xterm spec, see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda)
|
||||
this._parser.registerOscHandler(8, new OscHandler(data => this.setHyperlink(data)));
|
||||
// 10 - Change VT100 text foreground color to Pt.
|
||||
this._parser.registerOscHandler(10, new OscHandler(data => this.setOrReportFgColor(data)));
|
||||
@@ -308,40 +302,34 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._parser.registerOscHandler(11, new OscHandler(data => this.setOrReportBgColor(data)));
|
||||
// 12 - Change text cursor color to Pt.
|
||||
this._parser.registerOscHandler(12, new OscHandler(data => this.setOrReportCursorColor(data)));
|
||||
/*
|
||||
* 13 - Change mouse foreground color to Pt.
|
||||
* 14 - Change mouse background color to Pt.
|
||||
* 15 - Change Tektronix foreground color to Pt.
|
||||
* 16 - Change Tektronix background color to Pt.
|
||||
* 17 - Change highlight background color to Pt.
|
||||
* 18 - Change Tektronix cursor color to Pt.
|
||||
* 19 - Change highlight foreground color to Pt.
|
||||
* 46 - Change Log File to Pt.
|
||||
* 50 - Set Font to Pt.
|
||||
* 51 - reserved for Emacs shell.
|
||||
* 52 - Manipulate Selection Data.
|
||||
* 104 ; c - Reset Color Number c.
|
||||
*/
|
||||
// 13 - Change mouse foreground color to Pt.
|
||||
// 14 - Change mouse background color to Pt.
|
||||
// 15 - Change Tektronix foreground color to Pt.
|
||||
// 16 - Change Tektronix background color to Pt.
|
||||
// 17 - Change highlight background color to Pt.
|
||||
// 18 - Change Tektronix cursor color to Pt.
|
||||
// 19 - Change highlight foreground color to Pt.
|
||||
// 46 - Change Log File to Pt.
|
||||
// 50 - Set Font to Pt.
|
||||
// 51 - reserved for Emacs shell.
|
||||
// 52 - Manipulate Selection Data.
|
||||
// 104 ; c - Reset Color Number c.
|
||||
this._parser.registerOscHandler(104, new OscHandler(data => this.restoreIndexedColor(data)));
|
||||
/*
|
||||
* 105 ; c - Reset Special Color Number c.
|
||||
* 106 ; c; f - Enable/disable Special Color Number c.
|
||||
* 110 - Reset VT100 text foreground color.
|
||||
*/
|
||||
// 105 ; c - Reset Special Color Number c.
|
||||
// 106 ; c; f - Enable/disable Special Color Number c.
|
||||
// 110 - Reset VT100 text foreground color.
|
||||
this._parser.registerOscHandler(110, new OscHandler(data => this.restoreFgColor(data)));
|
||||
// 111 - Reset VT100 text background color.
|
||||
this._parser.registerOscHandler(111, new OscHandler(data => this.restoreBgColor(data)));
|
||||
// 112 - Reset text cursor color.
|
||||
this._parser.registerOscHandler(112, new OscHandler(data => this.restoreCursorColor(data)));
|
||||
/*
|
||||
* 113 - Reset mouse foreground color.
|
||||
* 114 - Reset mouse background color.
|
||||
* 115 - Reset Tektronix foreground color.
|
||||
* 116 - Reset Tektronix background color.
|
||||
* 117 - Reset highlight color.
|
||||
* 118 - Reset Tektronix cursor color.
|
||||
* 119 - Reset highlight foreground color.
|
||||
*/
|
||||
// 113 - Reset mouse foreground color.
|
||||
// 114 - Reset mouse background color.
|
||||
// 115 - Reset Tektronix foreground color.
|
||||
// 116 - Reset Tektronix background color.
|
||||
// 117 - Reset highlight color.
|
||||
// 118 - Reset Tektronix cursor color.
|
||||
// 119 - Reset highlight foreground color.
|
||||
|
||||
/**
|
||||
* ESC handlers
|
||||
@@ -478,10 +466,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the dirty row service so we know which lines changed as a result of parsing
|
||||
* Important: do not clear between async calls, otherwise we lost pending update information.
|
||||
*/
|
||||
// Clear the dirty row service so we know which lines changed as a result of parsing
|
||||
// Important: do not clear between async calls, otherwise we lost pending update information.
|
||||
if (!wasPaused) {
|
||||
this._dirtyRowTracker.clearRange();
|
||||
}
|
||||
@@ -516,10 +502,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._onCursorMove.fire();
|
||||
}
|
||||
|
||||
/*
|
||||
* Refresh any dirty rows accumulated as part of parsing, fire only for rows within the
|
||||
* _viewport_ which is relative to ydisp, not relative to ybase.
|
||||
*/
|
||||
// Refresh any dirty rows accumulated as part of parsing, fire only for rows within the
|
||||
// _viewport_ which is relative to ydisp, not relative to ybase.
|
||||
const viewportEnd = this._dirtyRowTracker.end + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp);
|
||||
const viewportStart = this._dirtyRowTracker.start + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp);
|
||||
if (viewportStart < this._bufferService.rows) {
|
||||
@@ -541,10 +525,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const curAttr = this._curAttrData;
|
||||
let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
|
||||
|
||||
/*
|
||||
* Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async
|
||||
* scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597
|
||||
*/
|
||||
// Defensive check: bufferRow can be undefined if a resize occurred mid-write due to async
|
||||
// scheduling gaps in WriteBuffer. See https://github.com/xtermjs/xterm.js/issues/5597
|
||||
if (!bufferRow) {
|
||||
return;
|
||||
}
|
||||
@@ -560,19 +542,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
for (let pos = start; pos < end; ++pos) {
|
||||
code = data[pos];
|
||||
|
||||
/*
|
||||
* Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat
|
||||
* it as a zero-width hint to text layout engines and simply ignore it.
|
||||
*/
|
||||
// Soft hyphen's (U+00AD) behavior is ambiguous and differs across terminals. We opt to treat
|
||||
// it as a zero-width hint to text layout engines and simply ignore it.
|
||||
if (code === 0xAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* get charset replacement character
|
||||
* charset is only defined for ASCII, therefore we only
|
||||
* search for an replacement char if code < 127
|
||||
*/
|
||||
// get charset replacement character
|
||||
// charset is only defined for ASCII, therefore we only
|
||||
// search for an replacement char if code < 127
|
||||
if (code < 127 && charset) {
|
||||
const ch = charset[String.fromCharCode(code)];
|
||||
if (ch) {
|
||||
@@ -593,16 +571,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._oscLinkService.addLineToLink(this._getCurrentLinkId(), this._activeBuffer.ybase + this._activeBuffer.y);
|
||||
}
|
||||
|
||||
/*
|
||||
* goto next line if ch would overflow
|
||||
* NOTE: To avoid costly width checks here,
|
||||
* the terminal does not allow a cols < 2.
|
||||
*/
|
||||
// goto next line if ch would overflow
|
||||
// NOTE: To avoid costly width checks here,
|
||||
// the terminal does not allow a cols < 2.
|
||||
if (this._activeBuffer.x + chWidth - oldWidth > cols) {
|
||||
/*
|
||||
* autowrap - DECAWM
|
||||
* automatically wraps to the beginning of the next line
|
||||
*/
|
||||
// autowrap - DECAWM
|
||||
// automatically wraps to the beginning of the next line
|
||||
if (wraparoundMode) {
|
||||
const oldRow = bufferRow;
|
||||
let oldCol = this._activeBuffer.x - oldWidth;
|
||||
@@ -615,10 +589,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
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
|
||||
*/
|
||||
// The line already exists (eg. the initial viewport), mark it as a
|
||||
// wrapped line
|
||||
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true;
|
||||
}
|
||||
// row changed, get it again
|
||||
@@ -627,10 +599,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
return;
|
||||
}
|
||||
if (oldWidth > 0 && bufferRow instanceof BufferLine) {
|
||||
/*
|
||||
* Combining character widens 1 column to 2.
|
||||
* Move old character to next line.
|
||||
*/
|
||||
// Combining character widens 1 column to 2.
|
||||
// Move old character to next line.
|
||||
bufferRow.copyCellsFrom(oldRow as BufferLine,
|
||||
oldCol, 0, oldWidth, false);
|
||||
}
|
||||
@@ -641,28 +611,22 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
} else {
|
||||
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
|
||||
*/
|
||||
// FIXME: check for xterm behavior
|
||||
// What to do here? We got a wide char that does not fit into last cell
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* insert combining char at last cursor position
|
||||
* this._activeBuffer.x should never be 0 for a combining char
|
||||
* since they always follow a cell consuming char
|
||||
* therefore we can test for this._activeBuffer.x to avoid overflow left
|
||||
*/
|
||||
// insert combining char at last cursor position
|
||||
// this._activeBuffer.x should never be 0 for a combining char
|
||||
// since they always follow a cell consuming char
|
||||
// therefore we can test for this._activeBuffer.x to avoid overflow left
|
||||
if (shouldJoin && this._activeBuffer.x) {
|
||||
const offset = bufferRow.getWidth(this._activeBuffer.x - 1) ? 1 : 2;
|
||||
/*
|
||||
* if 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
|
||||
*/
|
||||
// if 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(this._activeBuffer.x - offset,
|
||||
code, chWidth);
|
||||
for (let delta = chWidth - oldWidth; --delta >= 0;) {
|
||||
@@ -675,11 +639,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (insertMode) {
|
||||
// right shift cells according to the width
|
||||
bufferRow.insertCells(this._activeBuffer.x, chWidth - oldWidth, this._activeBuffer.getNullCell(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
|
||||
*/
|
||||
// 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
|
||||
if (bufferRow.getWidth(cols - 1) === 2) {
|
||||
bufferRow.setCellFromCodepoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr);
|
||||
}
|
||||
@@ -688,11 +650,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// write current char to buffer and advance cursor
|
||||
bufferRow.setCellFromCodepoint(this._activeBuffer.x++, code, chWidth, curAttr);
|
||||
|
||||
/*
|
||||
* 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 this._activeBuffer.x + chWidth will not overflow right
|
||||
*/
|
||||
// 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 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
|
||||
@@ -790,13 +750,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
} else if (this._activeBuffer.y >= this._bufferService.rows) {
|
||||
this._activeBuffer.y = this._bufferService.rows - 1;
|
||||
} else {
|
||||
/*
|
||||
* There was an explicit line feed (not just a carriage return), so clear the wrapped state of
|
||||
* the line. This is particularly important on conpty/Windows where revisiting lines to
|
||||
* reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics
|
||||
* can mess with this so windowsMode should be disabled, which is recommended on Windows build
|
||||
* 21376 and above.
|
||||
*/
|
||||
// There was an explicit line feed (not just a carriage return), so clear the wrapped state of
|
||||
// the line. This is particularly important on conpty/Windows where revisiting lines to
|
||||
// reprint is common, especially on resize. Note that the windowsMode wrapped line heuristics
|
||||
// can mess with this so windowsMode should be disabled, which is recommended on Windows build
|
||||
// 21376 and above.
|
||||
this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false;
|
||||
}
|
||||
// If the end of the line is hit, prevent this action from wrapping around to the next line.
|
||||
@@ -841,11 +799,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* reverse wrap-around is enabled
|
||||
* other than for normal operation mode, reverse wrap-around allows the cursor
|
||||
* to be at x=cols to be able to address the last cell of a row by BS
|
||||
*/
|
||||
// reverse wrap-around is enabled
|
||||
// other than for normal operation mode, reverse wrap-around allows the cursor
|
||||
// to be at x=cols to be able to address the last cell of a row by BS
|
||||
this._restrictCursor(this._bufferService.cols);
|
||||
|
||||
if (this._activeBuffer.x > 0) {
|
||||
@@ -866,21 +822,17 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
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
|
||||
*/
|
||||
// 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 = 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.
|
||||
*/
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -961,10 +913,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Set relative cursor position.
|
||||
*/
|
||||
private _moveCursor(x: number, y: number): void {
|
||||
/*
|
||||
* for relative changes we have to make sure we are within 0 .. cols/rows - 1
|
||||
* before calculating the new position
|
||||
*/
|
||||
// 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._activeBuffer.x + x, this._activeBuffer.y + y);
|
||||
}
|
||||
@@ -1404,10 +1354,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
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
|
||||
*/
|
||||
// test: echo -e '\e[44m\e[1L\e[0m'
|
||||
// blankLine(true) - xterm/linux behavior
|
||||
this._activeBuffer.lines.splice(scrollBottomAbsolute - 1, 1);
|
||||
this._activeBuffer.lines.splice(row, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
|
||||
}
|
||||
@@ -1440,10 +1388,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
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
|
||||
*/
|
||||
// test: echo -e '\e[44m\e[1M\e[0m'
|
||||
// blankLine(true) - xterm/linux behavior
|
||||
this._activeBuffer.lines.splice(row, 1);
|
||||
this._activeBuffer.lines.splice(j, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
|
||||
}
|
||||
@@ -1791,20 +1737,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (params.params[0] > 0) {
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* xterm and urxvt
|
||||
* seem to spit this
|
||||
* out around ~370 times (?).
|
||||
*/
|
||||
// xterm and urxvt
|
||||
// seem to spit this
|
||||
// out around ~370 times (?).
|
||||
if (this._is('xterm')) {
|
||||
this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c');
|
||||
} else if (this._is('rxvt-unicode')) {
|
||||
this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c');
|
||||
} else if (this._is('linux')) {
|
||||
/*
|
||||
* not supported by linux console.
|
||||
* linux console echoes parameters.
|
||||
*/
|
||||
// not supported by linux console.
|
||||
// linux console echoes parameters.
|
||||
this._coreService.triggerDataEvent(params.params[0] + 'c');
|
||||
} else if (this._is('screen')) {
|
||||
this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c');
|
||||
@@ -2037,17 +1979,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._mouseStateService.activeProtocol = 'DRAG';
|
||||
break;
|
||||
case 1003: // any event mouse
|
||||
/*
|
||||
* any event - sends motion events,
|
||||
* even if there is no button held down.
|
||||
*/
|
||||
// any event - sends motion events,
|
||||
// even if there is no button held down.
|
||||
this._mouseStateService.activeProtocol = 'ANY';
|
||||
break;
|
||||
case 1004: // send focusin/focusout events
|
||||
/*
|
||||
* focusin: ^[[I
|
||||
* focusout: ^[[O
|
||||
*/
|
||||
// focusin: ^[[I
|
||||
// focusout: ^[[O
|
||||
this._coreService.decPrivateModes.sendFocus = true;
|
||||
this._onRequestSendFocus.fire();
|
||||
break;
|
||||
@@ -2470,12 +2408,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Returns advance for params index.
|
||||
*/
|
||||
private _extractColor(params: IParams, pos: number, attr: IAttributeData): number {
|
||||
/*
|
||||
* normalize params
|
||||
* meaning: [target, CM, ign, val, val, val]
|
||||
* RGB : [ 38/48, 2, ign, r, g, b]
|
||||
* P256 : [ 38/48, 5, ign, v, ign, ign]
|
||||
*/
|
||||
// normalize params
|
||||
// meaning: [target, CM, ign, val, val, val]
|
||||
// RGB : [ 38/48, 2, ign, r, g, b]
|
||||
// P256 : [ 38/48, 5, ign, v, ign, ign]
|
||||
const accu = [0, 0, -1, 0, 0, 0];
|
||||
|
||||
// alignment placeholder for non color space sequences
|
||||
@@ -2541,10 +2477,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* 4:5 - dashed underline
|
||||
*/
|
||||
private _processUnderline(style: number, attr: IAttributeData): void {
|
||||
/*
|
||||
* treat extended attrs as immutable, thus always clone from old one
|
||||
* this is needed since the buffer only holds references to it
|
||||
*/
|
||||
// treat extended attrs as immutable, thus always clone from old one
|
||||
// this is needed since the buffer only holds references to it
|
||||
attr.extended = attr.extended.clone();
|
||||
|
||||
// default to 1 == single underline
|
||||
@@ -2567,10 +2501,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
attr.fg = DEFAULT_ATTR_DATA.fg;
|
||||
attr.bg = DEFAULT_ATTR_DATA.bg;
|
||||
attr.extended = attr.extended.clone();
|
||||
/*
|
||||
* Reset underline style and color. Note that we don't want to reset other
|
||||
* fields such as the url id.
|
||||
*/
|
||||
// Reset underline style and color. Note that we don't want to reset other
|
||||
// fields such as the url id.
|
||||
attr.extended.underlineStyle = UnderlineStyle.NONE;
|
||||
attr.extended.underlineColor &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||
attr.updateExtended();
|
||||
@@ -2709,10 +2641,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// blink
|
||||
attr.fg |= FgFlags.BLINK;
|
||||
} else if (p === 7) {
|
||||
/*
|
||||
* inverse and positive
|
||||
* test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
|
||||
*/
|
||||
// inverse and positive
|
||||
// test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
|
||||
attr.fg |= FgFlags.INVERSE;
|
||||
} else if (p === 8) {
|
||||
// invisible
|
||||
@@ -2826,10 +2756,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
// @vt: #P[Only CPR is supported.] CSI DECDSR "DEC Device Status Report" "CSI ? Ps n" "Only CPR is supported (same as DSR)."
|
||||
public deviceStatusPrivate(params: IParams): boolean {
|
||||
/*
|
||||
* modern xterm doesnt seem to
|
||||
* respond to any of these except ?6, 6, and 5
|
||||
*/
|
||||
// modern xterm doesnt seem to
|
||||
// respond to any of these except ?6, 6, and 5
|
||||
switch (params.params[0]) {
|
||||
case 6:
|
||||
// cursor position
|
||||
@@ -2838,28 +2766,20 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`);
|
||||
break;
|
||||
case 15:
|
||||
/*
|
||||
* no printer
|
||||
* this.handler(C0.ESC + '[?11n');
|
||||
*/
|
||||
// no printer
|
||||
// this.handler(C0.ESC + '[?11n');
|
||||
break;
|
||||
case 25:
|
||||
/*
|
||||
* dont support user defined keys
|
||||
* this.handler(C0.ESC + '[?21n');
|
||||
*/
|
||||
// dont support user defined keys
|
||||
// this.handler(C0.ESC + '[?21n');
|
||||
break;
|
||||
case 26:
|
||||
/*
|
||||
* north american keyboard
|
||||
* this.handler(C0.ESC + '[?27;1;0;0n');
|
||||
*/
|
||||
// north american keyboard
|
||||
// this.handler(C0.ESC + '[?27;1;0;0n');
|
||||
break;
|
||||
case 53:
|
||||
/*
|
||||
* no dec locator/mouse
|
||||
* this.handler(C0.ESC + '[?50n');
|
||||
*/
|
||||
// no dec locator/mouse
|
||||
// this.handler(C0.ESC + '[?50n');
|
||||
break;
|
||||
case 996:
|
||||
// color scheme query (https://contour-terminal.org/vt-extensions/color-palette-update-notifications/)
|
||||
@@ -3481,11 +3401,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public reverseIndex(): boolean {
|
||||
this._restrictCursor();
|
||||
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
|
||||
*/
|
||||
// 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 = 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()));
|
||||
|
||||
+8
-14
@@ -9,18 +9,14 @@ interface INavigator {
|
||||
platform: string;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're declaring a navigator global here as we expect it in all runtimes (node and browser), but
|
||||
* we want this module to live in common.
|
||||
*/
|
||||
// We're declaring a navigator global here as we expect it in all runtimes (node and browser), but
|
||||
// we want this module to live in common.
|
||||
declare const navigator: INavigator;
|
||||
declare const process: unknown;
|
||||
|
||||
/*
|
||||
* navigator.userAgent is also checked here because bundling with the process module can cause
|
||||
* issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is
|
||||
* "Node.js/<version>".
|
||||
*/
|
||||
// navigator.userAgent is also checked here because bundling with the process module can cause
|
||||
// issues otherwise. Note that navigator exists in Node.js 21+ but the userAgent is
|
||||
// "Node.js/<version>".
|
||||
export const isNode = (typeof process !== 'undefined' && 'title' in (process as any) && (typeof navigator === 'undefined' || navigator.userAgent.startsWith('Node.js/'))) ? true : false;
|
||||
const userAgent = (isNode) ? 'node' : navigator.userAgent;
|
||||
const platform = (isNode) ? 'node' : navigator.platform;
|
||||
@@ -48,11 +44,9 @@ export function getSafariVersion(): number {
|
||||
return parseInt(majorVersion[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the users platform. We use this to interpret the meta key
|
||||
* and ISO third level shifts.
|
||||
* http://stackoverflow.com/q/19877924/577598
|
||||
*/
|
||||
// Find the users platform. We use this to interpret the meta key
|
||||
// and ISO third level shifts.
|
||||
// http://stackoverflow.com/q/19877924/577598
|
||||
export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform);
|
||||
export const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(platform);
|
||||
export const isLinux = platform.indexOf('Linux') >= 0;
|
||||
|
||||
@@ -191,10 +191,8 @@ export class SortedList<T> {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* key not in list
|
||||
* still return closest min (also used as insert position)
|
||||
*/
|
||||
// key not in list
|
||||
// still return closest min (also used as insert position)
|
||||
return min;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-13
@@ -83,23 +83,17 @@ abstract class TaskQueue implements ITaskQueue {
|
||||
if (!this._tasks[this._i]()) {
|
||||
this._i++;
|
||||
}
|
||||
/*
|
||||
* other than performance.now, performance.now might not be stable (changes on wall clock
|
||||
* changes), this is not an issue here as a clock change during a short running task is very
|
||||
* unlikely in case it still happened and leads to negative duration, simply assume 1 msec
|
||||
*/
|
||||
// other than performance.now, performance.now might not be stable (changes on wall clock
|
||||
// changes), this is not an issue here as a clock change during a short running task is very
|
||||
// unlikely in case it still happened and leads to negative duration, simply assume 1 msec
|
||||
taskDuration = Math.max(1, performance.now() - taskDuration);
|
||||
longestTask = Math.max(taskDuration, longestTask);
|
||||
/*
|
||||
* Guess the following task will take a similar time to the longest task in this batch, allow
|
||||
* additional room to try avoid exceeding the deadline
|
||||
*/
|
||||
// Guess the following task will take a similar time to the longest task in this batch, allow
|
||||
// additional room to try avoid exceeding the deadline
|
||||
deadlineRemaining = deadline.timeRemaining();
|
||||
if (longestTask * 1.5 > deadlineRemaining) {
|
||||
/*
|
||||
* Warn when the time exceeding the deadline is over 20ms, if this happens in practice the
|
||||
* task should be split into sub-tasks to ensure the UI remains responsive.
|
||||
*/
|
||||
// Warn when the time exceeding the deadline is over 20ms, if this happens in practice the
|
||||
// task should be split into sub-tasks to ensure the UI remains responsive.
|
||||
if (lastDeadlineRemaining - taskDuration < -20) {
|
||||
this._logService.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(lastDeadlineRemaining - taskDuration))}ms`);
|
||||
}
|
||||
|
||||
+14
-16
@@ -310,10 +310,8 @@ export const enum CoreMouseButton {
|
||||
RIGHT = 2,
|
||||
NONE = 3,
|
||||
WHEEL = 4,
|
||||
/*
|
||||
* additional buttons 1..8
|
||||
* untested!
|
||||
*/
|
||||
// additional buttons 1..8
|
||||
// untested!
|
||||
AUX1 = 8,
|
||||
AUX2 = 9,
|
||||
AUX3 = 10,
|
||||
@@ -532,7 +530,7 @@ export interface IInputHandler {
|
||||
|
||||
/**
|
||||
* OSC 0
|
||||
* OSC 2
|
||||
* OSC 2
|
||||
*/ setTitle(data: string): boolean;
|
||||
/** OSC 4 */ setOrReportIndexedColor(data: string): boolean;
|
||||
/** OSC 10 */ setOrReportFgColor(data: string): boolean;
|
||||
@@ -548,16 +546,16 @@ export interface IInputHandler {
|
||||
/** ESC > */ keypadNumericMode(): boolean;
|
||||
/**
|
||||
* ESC % G
|
||||
* ESC % @
|
||||
* ESC % @
|
||||
*/ selectDefaultCharset(): boolean;
|
||||
/**
|
||||
* ESC ( C
|
||||
* ESC ) C
|
||||
* ESC * C
|
||||
* ESC + C
|
||||
* ESC - C
|
||||
* ESC . C
|
||||
* ESC / C
|
||||
* ESC ) C
|
||||
* ESC * C
|
||||
* ESC + C
|
||||
* ESC - C
|
||||
* ESC . C
|
||||
* ESC / C
|
||||
*/ selectCharset(collectAndFlag: string): boolean;
|
||||
/** ESC D */ index(): boolean;
|
||||
/** ESC H */ tabSet(): boolean;
|
||||
@@ -565,10 +563,10 @@ export interface IInputHandler {
|
||||
/** ESC c */ fullReset(): boolean;
|
||||
/**
|
||||
* ESC n
|
||||
* ESC o
|
||||
* ESC |
|
||||
* ESC }
|
||||
* ESC ~
|
||||
* ESC o
|
||||
* ESC |
|
||||
* ESC }
|
||||
* ESC ~
|
||||
*/ setgLevel(level: number): boolean;
|
||||
/** ESC # 8 */ screenAlignmentPattern(): boolean;
|
||||
}
|
||||
|
||||
+10
-12
@@ -7,18 +7,16 @@ import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'comm
|
||||
import { IBufferService } from 'common/services/Services';
|
||||
|
||||
export function updateWindowsModeWrappedState(bufferService: IBufferService): void {
|
||||
/*
|
||||
* Winpty does not support wraparound mode which means that lines will never
|
||||
* be marked as wrapped. This causes issues for things like copying a line
|
||||
* retaining the wrapped new line characters or if consumers are listening
|
||||
* in on the data stream.
|
||||
*
|
||||
* The workaround for this is to listen to every incoming line feed and mark
|
||||
* the line as wrapped if the last character in the previous line is not a
|
||||
* space. This is certainly not without its problems, but generally on
|
||||
* Windows when text reaches the end of the terminal it's likely going to be
|
||||
* wrapped.
|
||||
*/
|
||||
// Winpty does not support wraparound mode which means that lines will never
|
||||
// be marked as wrapped. This causes issues for things like copying a line
|
||||
// retaining the wrapped new line characters or if consumers are listening
|
||||
// in on the data stream.
|
||||
//
|
||||
// The workaround for this is to listen to every incoming line feed and mark
|
||||
// the line as wrapped if the last character in the previous line is not a
|
||||
// space. This is certainly not without its problems, but generally on
|
||||
// Windows when text reaches the end of the terminal it's likely going to be
|
||||
// wrapped.
|
||||
const line = bufferService.buffer.lines.get(bufferService.buffer.ybase + bufferService.buffer.y - 1);
|
||||
const lastChar = line?.get(bufferService.cols - 1);
|
||||
|
||||
|
||||
@@ -348,10 +348,8 @@ describe('Buffer', () => {
|
||||
assert.equal(buffer.lines.get(4)!.translateToString(), ' ');
|
||||
});
|
||||
it('should remove the correct amount of rows when reflowing larger', () => {
|
||||
/*
|
||||
* This is a regression test to ensure that successive wrapped lines that are getting
|
||||
* 3+ lines removed on a reflow actually remove the right lines
|
||||
*/
|
||||
// This is a regression test to ensure that successive wrapped lines that are getting
|
||||
// 3+ lines removed on a reflow actually remove the right lines
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(10, 10);
|
||||
buffer.y = 2;
|
||||
@@ -431,12 +429,10 @@ describe('Buffer', () => {
|
||||
buffer.lines.get(2)!.set(i, [0, char, 1, code]);
|
||||
}
|
||||
buffer.y = 3;
|
||||
/*
|
||||
* Buffer:
|
||||
* abcdefghij
|
||||
* 0123456789
|
||||
* abcdefghij
|
||||
*/
|
||||
// Buffer:
|
||||
// abcdefghij
|
||||
// 0123456789
|
||||
// abcdefghij
|
||||
const firstMarker = buffer.addMarker(0);
|
||||
const secondMarker = buffer.addMarker(1);
|
||||
const thirdMarker = buffer.addMarker(2);
|
||||
@@ -496,12 +492,10 @@ describe('Buffer', () => {
|
||||
buffer.lines.get(2)!.set(i, [0, char, 1, code]);
|
||||
}
|
||||
buffer.y = 10;
|
||||
/*
|
||||
* Buffer:
|
||||
* abcdefghij
|
||||
* 0123456789
|
||||
* abcdefghij
|
||||
*/
|
||||
// Buffer:
|
||||
// abcdefghij
|
||||
// 0123456789
|
||||
// abcdefghij
|
||||
const firstMarker = buffer.addMarker(0);
|
||||
const secondMarker = buffer.addMarker(1);
|
||||
const thirdMarker = buffer.addMarker(2);
|
||||
@@ -545,11 +539,9 @@ describe('Buffer', () => {
|
||||
buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]);
|
||||
buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]);
|
||||
buffer.lines.get(1)!.isWrapped = true;
|
||||
/*
|
||||
* Buffer:
|
||||
* "ab " (wrapped)
|
||||
* "cd"
|
||||
*/
|
||||
// Buffer:
|
||||
// "ab " (wrapped)
|
||||
// "cd"
|
||||
buffer.resize(5, 10);
|
||||
assert.equal(buffer.ybase, 0);
|
||||
assert.equal(buffer.lines.length, 10);
|
||||
@@ -578,11 +570,9 @@ describe('Buffer', () => {
|
||||
buffer.lines.get(1)!.set(i, [0, '', 0, 0]);
|
||||
}
|
||||
buffer.lines.get(1)!.isWrapped = true;
|
||||
/*
|
||||
* Buffer:
|
||||
* 汉语汉语汉语 (wrapped)
|
||||
* 汉语汉语汉语
|
||||
*/
|
||||
// Buffer:
|
||||
// 汉语汉语汉语 (wrapped)
|
||||
// 汉语汉语汉语
|
||||
assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语');
|
||||
assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语');
|
||||
buffer.resize(13, 10);
|
||||
@@ -607,11 +597,9 @@ describe('Buffer', () => {
|
||||
buffer.lines.get(1)!.set(0, [0, 'c', 1, 'c'.charCodeAt(0)]);
|
||||
buffer.lines.get(1)!.set(1, [0, 'd', 1, 'd'.charCodeAt(0)]);
|
||||
buffer.lines.get(1)!.isWrapped = true;
|
||||
/*
|
||||
* Buffer:
|
||||
* "ab " (wrapped)
|
||||
* "cd"
|
||||
*/
|
||||
// Buffer:
|
||||
// "ab " (wrapped)
|
||||
// "cd"
|
||||
buffer.resize(3, 10);
|
||||
assert.equal(buffer.y, 2);
|
||||
assert.equal(buffer.ybase, 0);
|
||||
@@ -643,11 +631,9 @@ describe('Buffer', () => {
|
||||
buffer.lines.get(1)!.set(i, [0, '', 0, 0]);
|
||||
}
|
||||
buffer.lines.get(1)!.isWrapped = true;
|
||||
/*
|
||||
* Buffer:
|
||||
* 汉语汉语汉语 (wrapped)
|
||||
* 汉语汉语汉语
|
||||
*/
|
||||
// Buffer:
|
||||
// 汉语汉语汉语 (wrapped)
|
||||
// 汉语汉语汉语
|
||||
assert.equal(buffer.lines.get(0)!.translateToString(true), '汉语汉语汉语');
|
||||
assert.equal(buffer.lines.get(1)!.translateToString(true), '汉语汉语汉语');
|
||||
buffer.resize(11, 10);
|
||||
@@ -682,19 +668,17 @@ describe('Buffer', () => {
|
||||
|
||||
describe('reflowLarger cases', () => {
|
||||
beforeEach(() => {
|
||||
/*
|
||||
* Setup buffer state:
|
||||
* 'ab'
|
||||
* 'cd' (wrapped)
|
||||
* 'ef'
|
||||
* 'gh' (wrapped)
|
||||
* 'ij'
|
||||
* 'kl' (wrapped)
|
||||
* ' '
|
||||
* ' '
|
||||
* ' '
|
||||
* ' '
|
||||
*/
|
||||
// Setup buffer state:
|
||||
// 'ab'
|
||||
// 'cd' (wrapped)
|
||||
// 'ef'
|
||||
// 'gh' (wrapped)
|
||||
// 'ij'
|
||||
// 'kl' (wrapped)
|
||||
// ' '
|
||||
// ' '
|
||||
// ' '
|
||||
// ' '
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(2, 10);
|
||||
buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
@@ -875,19 +859,17 @@ describe('Buffer', () => {
|
||||
});
|
||||
describe('reflowSmaller cases', () => {
|
||||
beforeEach(() => {
|
||||
/*
|
||||
* Setup buffer state:
|
||||
* 'abcd'
|
||||
* 'efgh' (wrapped)
|
||||
* 'ijkl'
|
||||
* ' '
|
||||
* ' '
|
||||
* ' '
|
||||
* ' '
|
||||
* ' '
|
||||
* ' '
|
||||
* ' '
|
||||
*/
|
||||
// Setup buffer state:
|
||||
// 'abcd'
|
||||
// 'efgh' (wrapped)
|
||||
// 'ijkl'
|
||||
// ' '
|
||||
// ' '
|
||||
// ' '
|
||||
// ' '
|
||||
// ' '
|
||||
// ' '
|
||||
// ' '
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(4, 10);
|
||||
buffer.lines.get(0)!.set(0, [0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
@@ -1319,10 +1301,8 @@ describe('Buffer', () => {
|
||||
assert.equal(line.length, INIT_COLS / 2 - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for a bit to give IdleTaskQueue a chance to kick in
|
||||
* and finish memory cleaning
|
||||
*/
|
||||
// wait for a bit to give IdleTaskQueue a chance to kick in
|
||||
// and finish memory cleaning
|
||||
await new Promise(r => setTimeout(r, 30));
|
||||
|
||||
// cleanup should have realigned memory with exact bytelength
|
||||
|
||||
+35
-67
@@ -169,25 +169,19 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
// count bufferlines with overly big memory to be cleaned afterwards
|
||||
let dirtyMemoryLines = 0;
|
||||
|
||||
/*
|
||||
* Increase max length if needed before adjustments to allow space to fill
|
||||
* as required.
|
||||
*/
|
||||
// Increase max length if needed before adjustments to allow space to fill
|
||||
// as required.
|
||||
const newMaxLength = this._getCorrectBufferLength(newRows);
|
||||
if (newMaxLength > this.lines.maxLength) {
|
||||
this.lines.maxLength = newMaxLength;
|
||||
}
|
||||
|
||||
/*
|
||||
* if (this._cols > newCols) {
|
||||
* console.log('increase!');
|
||||
* }
|
||||
*/
|
||||
// if (this._cols > newCols) {
|
||||
// console.log('increase!');
|
||||
// }
|
||||
|
||||
/*
|
||||
* The following adjustments should only happen if the buffer has been
|
||||
* initialized/filled.
|
||||
*/
|
||||
// The following adjustments should only happen if the buffer has been
|
||||
// initialized/filled.
|
||||
if (this.lines.length > 0) {
|
||||
// Deal with columns increasing (reducing needs to happen after reflow)
|
||||
if (this._cols < newCols) {
|
||||
@@ -203,17 +197,13 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
for (let y = this._rows; y < newRows; y++) {
|
||||
if (this.lines.length < newRows + this.ybase) {
|
||||
if (this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) {
|
||||
/*
|
||||
* Just add the new missing rows on Windows as conpty reprints the screen with it's
|
||||
* view of the world. Once a line enters scrollback for conpty it remains there
|
||||
*/
|
||||
// Just add the new missing rows on Windows as conpty reprints the screen with it's
|
||||
// view of the world. Once a line enters scrollback for conpty it remains there
|
||||
this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false));
|
||||
} else {
|
||||
if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
|
||||
/*
|
||||
* There is room above the buffer and there are no empty elements below the line,
|
||||
* scroll up
|
||||
*/
|
||||
// There is room above the buffer and there are no empty elements below the line,
|
||||
// scroll up
|
||||
this.ybase--;
|
||||
addToY++;
|
||||
if (this.ydisp > 0) {
|
||||
@@ -221,10 +211,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
this.ydisp--;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Add a blank line if there is no buffer left at the top to scroll to, or if there
|
||||
* are blank lines after the cursor
|
||||
*/
|
||||
// Add a blank line if there is no buffer left at the top to scroll to, or if there
|
||||
// are blank lines after the cursor
|
||||
this.lines.push(new BufferLine(this._stringCache, newCols, nullCell, false));
|
||||
}
|
||||
}
|
||||
@@ -245,10 +233,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reduce max length if needed after adjustments, this is done after as it
|
||||
* would otherwise cut data from the bottom of the buffer.
|
||||
*/
|
||||
// Reduce max length if needed after adjustments, this is done after as it
|
||||
// would otherwise cut data from the bottom of the buffer.
|
||||
if (newMaxLength < this.lines.maxLength) {
|
||||
// Trim from the top of the buffer and adjust ybase and ydisp.
|
||||
const amountToTrim = this.lines.length - newMaxLength;
|
||||
@@ -289,10 +275,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
this._cols = newCols;
|
||||
this._rows = newRows;
|
||||
|
||||
/*
|
||||
* Ensure the cursor position invariant: ybase + y must be within buffer bounds
|
||||
* This can be violated during reflow or when shrinking rows
|
||||
*/
|
||||
// Ensure the cursor position invariant: ybase + y must be within buffer bounds
|
||||
// This can be violated during reflow or when shrinking rows
|
||||
if (this.lines.length > 0) {
|
||||
const maxY = Math.max(0, this.lines.length - this.ybase - 1);
|
||||
this.y = Math.min(this.y, maxY);
|
||||
@@ -309,10 +293,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
private _batchedMemoryCleanup(): boolean {
|
||||
let normalRun = true;
|
||||
if (this._memoryCleanupPosition >= this.lines.length) {
|
||||
/*
|
||||
* cleanup made it once through all lines, thus rescan in loop below to also catch shifted
|
||||
* lines, which should finish rather quick if there are no more cleanups pending
|
||||
*/
|
||||
// cleanup made it once through all lines, thus rescan in loop below to also catch shifted
|
||||
// lines, which should finish rather quick if there are no more cleanups pending
|
||||
this._memoryCleanupPosition = 0;
|
||||
normalRun = false;
|
||||
}
|
||||
@@ -324,11 +306,9 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* normal runs always need another rescan afterwards
|
||||
* if we made it here with normalRun=false, we are in a final run
|
||||
* and can end the cleanup task for sure
|
||||
*/
|
||||
// normal runs always need another rescan afterwards
|
||||
// if we made it here with normalRun=false, we are in a final run
|
||||
// and can end the cleanup task for sure
|
||||
return normalRun;
|
||||
}
|
||||
|
||||
@@ -389,10 +369,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
private _reflowSmaller(newCols: number, newRows: number): void {
|
||||
const reflowCursorLine = this._optionsService.rawOptions.reflowCursorLine;
|
||||
const nullCell = this.getNullCell(DEFAULT_ATTR_DATA);
|
||||
/*
|
||||
* Gather all BufferLines that need to be inserted into the Buffer here so that they can be
|
||||
* batched up and only committed once
|
||||
*/
|
||||
// Gather all BufferLines that need to be inserted into the Buffer here so that they can be
|
||||
// batched up and only committed once
|
||||
const toInsert = [];
|
||||
let countToInsert = 0;
|
||||
// Go backwards as many lines may be trimmed and this will avoid considering them
|
||||
@@ -411,10 +389,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
}
|
||||
|
||||
if (!reflowCursorLine) {
|
||||
/*
|
||||
* If these lines contain the cursor don't touch them, the program will handle fixing up
|
||||
* wrapped lines with the cursor
|
||||
*/
|
||||
// If these lines contain the cursor don't touch them, the program will handle fixing up
|
||||
// wrapped lines with the cursor
|
||||
const absoluteY = this.ybase + this.y;
|
||||
if (absoluteY >= y && absoluteY < y + wrappedLines.length) {
|
||||
continue;
|
||||
@@ -440,10 +416,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
}
|
||||
if (newLines.length > 0) {
|
||||
toInsert.push({
|
||||
/*
|
||||
* countToInsert here gets the actual index, taking into account other inserted items.
|
||||
* using this we can iterate through the list forwards
|
||||
*/
|
||||
// countToInsert here gets the actual index, taking into account other inserted items.
|
||||
// using this we can iterate through the list forwards
|
||||
start: y + wrappedLines.length + countToInsert,
|
||||
newLines
|
||||
});
|
||||
@@ -463,10 +437,8 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
while (srcLineIndex >= 0) {
|
||||
const cellsToCopy = Math.min(srcCol, destCol);
|
||||
if (wrappedLines[destLineIndex] === undefined) {
|
||||
/*
|
||||
* Sanity check that the line exists, this has been known to fail for an unknown reason
|
||||
* which would stop the reflow from happening if an exception would throw.
|
||||
*/
|
||||
// Sanity check that the line exists, this has been known to fail for an unknown reason
|
||||
// which would stop the reflow from happening if an exception would throw.
|
||||
break;
|
||||
}
|
||||
wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true);
|
||||
@@ -514,16 +486,12 @@ export class Buffer extends Disposable implements IBuffer {
|
||||
this.savedY = Math.min(this.savedY + linesToAdd, this.ybase + newRows - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rearrange lines in the buffer if there are any insertions, this is done at the end rather
|
||||
* than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many
|
||||
* costly calls to CircularList.splice.
|
||||
*/
|
||||
// Rearrange lines in the buffer if there are any insertions, this is done at the end rather
|
||||
// than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many
|
||||
// costly calls to CircularList.splice.
|
||||
if (toInsert.length > 0) {
|
||||
/*
|
||||
* Record buffer insert events and then play them back backwards so that the indexes are
|
||||
* correct
|
||||
*/
|
||||
// Record buffer insert events and then play them back backwards so that the indexes are
|
||||
// correct
|
||||
const insertEvents: IInsertEvent[] = [];
|
||||
|
||||
// Record original lines so they don't get overridden when we rearrange the list
|
||||
|
||||
@@ -288,11 +288,9 @@ describe('BufferLine', function(): void {
|
||||
assert.equal(line2.isWrapped, line.isWrapped);
|
||||
});
|
||||
it('should support combining chars', function(): void {
|
||||
/*
|
||||
* CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print
|
||||
* --> set code to the last charCodeAt value of the string
|
||||
* Note: needs to be fixed once the string pointer is in place
|
||||
*/
|
||||
// CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print
|
||||
// --> set code to the last charCodeAt value of the string
|
||||
// Note: needs to be fixed once the string pointer is in place
|
||||
const line = new TestBufferLine(2, createCellData(1, 'e\u0301', 1));
|
||||
assert.deepEqual(line.toArray(), [[1, 'e\u0301', 1, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]]);
|
||||
const line2 = new TestBufferLine(5, createCellData(1, 'a', 1), true);
|
||||
@@ -480,11 +478,9 @@ describe('BufferLine', function(): void {
|
||||
});
|
||||
it('should always return some sane value', function(): void {
|
||||
const columns: number[] = [];
|
||||
/*
|
||||
* sanity check - broken line with invalid out of bound null width cells
|
||||
* this can atm happen with deleting/inserting chars in inputhandler by "breaking"
|
||||
* fullwidth pairs --> needs to be fixed after settling BufferLine impl
|
||||
*/
|
||||
// sanity check - broken line with invalid out of bound null width cells
|
||||
// this can atm happen with deleting/inserting chars in inputhandler by "breaking"
|
||||
// fullwidth pairs --> needs to be fixed after settling BufferLine impl
|
||||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||||
assert.equal(line.translateToString(false, undefined, undefined, columns), ' ');
|
||||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
@@ -504,10 +500,8 @@ describe('BufferLine', function(): void {
|
||||
const line = new TestBufferLine(3, NULL_CELL_DATA, false);
|
||||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0);
|
||||
const cell = line.loadCell(0, new CellData());
|
||||
/*
|
||||
* chars contains single combining char
|
||||
* width is set to 1
|
||||
*/
|
||||
// chars contains single combining char
|
||||
// width is set to 1
|
||||
assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
assert.equal(cell.isCombined(), 0);
|
||||
@@ -519,10 +513,8 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(0, cell);
|
||||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0);
|
||||
line.loadCell(0, cell);
|
||||
/*
|
||||
* chars contains 3 chars
|
||||
* width is set to 1
|
||||
*/
|
||||
// chars contains 3 chars
|
||||
// width is set to 1
|
||||
assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
@@ -534,10 +526,8 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(0, cell);
|
||||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0);
|
||||
line.loadCell(0, cell);
|
||||
/*
|
||||
* chars contains 2 chars
|
||||
* width is set to 1
|
||||
*/
|
||||
// chars contains 2 chars
|
||||
// width is set to 1
|
||||
assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
|
||||
@@ -9,13 +9,11 @@ import { CellData } from 'common/buffer/CellData';
|
||||
import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
|
||||
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
||||
|
||||
/*
|
||||
* Buffer memory layout:
|
||||
*
|
||||
* [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21)
|
||||
* [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8)
|
||||
* [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8)
|
||||
*/
|
||||
// Buffer memory layout:
|
||||
//
|
||||
// [0]: content `uint32_t` - wcwidth(2) comb(1) codepoint(21)
|
||||
// [1]: fg `uint32_t` - flags(8) r(8) g(8) b(8)
|
||||
// [2]: bg `uint32_t` - flags(8) r(8) g(8) b(8)
|
||||
|
||||
const enum Constants {
|
||||
/** The number of 32 bit array indices taken by one cell. */
|
||||
@@ -257,19 +255,15 @@ export class BufferLine implements IBufferLine {
|
||||
this._combined[index] += stringFromCodePoint(codePoint);
|
||||
} else {
|
||||
if (content & Content.CODEPOINT_MASK) {
|
||||
/*
|
||||
* normal case for combining chars:
|
||||
* - move current leading char + new one into combined string
|
||||
* - set combined flag
|
||||
*/
|
||||
// normal case for combining chars:
|
||||
// - move current leading char + new one into combined string
|
||||
// - set combined flag
|
||||
this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint);
|
||||
content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0
|
||||
content |= Content.IS_COMBINED_MASK;
|
||||
} else {
|
||||
/*
|
||||
* should not happen - we actually have no data in the cell yet
|
||||
* simply set the data in the cell buffer with a width of 1
|
||||
*/
|
||||
// should not happen - we actually have no data in the cell yet
|
||||
// simply set the data in the cell buffer with a width of 1
|
||||
content = codePoint | (1 << Content.WIDTH_SHIFT);
|
||||
}
|
||||
}
|
||||
@@ -324,11 +318,9 @@ export class BufferLine implements IBufferLine {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle fullwidth at pos:
|
||||
* - reset pos-1 if wide char
|
||||
* - reset pos if width==0 (previous second cell of a wide char)
|
||||
*/
|
||||
// handle fullwidth at pos:
|
||||
// - reset pos-1 if wide char
|
||||
// - reset pos if width==0 (previous second cell of a wide char)
|
||||
if (pos && this.getWidth(pos - 1) === 2) {
|
||||
this.setCellFromCodepoint(pos - 1, 0, 1, fillCellData);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,8 @@ export interface INewLayoutResult {
|
||||
* @param reflowCursorLine Whether to reflow the line containing the cursor.
|
||||
*/
|
||||
export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, oldCols: number, newCols: number, bufferAbsoluteY: number, nullCell: ICellData, reflowCursorLine: boolean): number[] {
|
||||
/*
|
||||
* Gather all BufferLines that need to be removed from the Buffer here so that they can be
|
||||
* batched up and only committed once
|
||||
*/
|
||||
// Gather all BufferLines that need to be removed from the Buffer here so that they can be
|
||||
// batched up and only committed once
|
||||
const toRemove: number[] = [];
|
||||
|
||||
for (let y = 0; y < lines.length - 1; y++) {
|
||||
@@ -45,10 +43,8 @@ export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, o
|
||||
}
|
||||
|
||||
if (!reflowCursorLine) {
|
||||
/*
|
||||
* If these lines contain the cursor don't touch them, the program will handle fixing up
|
||||
* wrapped lines with the cursor
|
||||
*/
|
||||
// If these lines contain the cursor don't touch them, the program will handle fixing up
|
||||
// wrapped lines with the cursor
|
||||
if (bufferAbsoluteY >= y && bufferAbsoluteY < i) {
|
||||
y += wrappedLines.length - 1;
|
||||
continue;
|
||||
@@ -184,10 +180,8 @@ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCo
|
||||
const newLineLengths: number[] = [];
|
||||
const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c);
|
||||
|
||||
/*
|
||||
* Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and
|
||||
* linesNeeded
|
||||
*/
|
||||
// Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and
|
||||
// linesNeeded
|
||||
let srcCol = 0;
|
||||
let srcLine = 0;
|
||||
let cellsAvailable = 0;
|
||||
@@ -220,11 +214,9 @@ export function getWrappedLineTrimmedLength(lines: BufferLine[], i: number, cols
|
||||
if (i === lines.length - 1) {
|
||||
return lines[i].getTrimmedLength();
|
||||
}
|
||||
/*
|
||||
* Detect whether the following line starts with a wide character and the end of the current line
|
||||
* is null, if so then we can be pretty sure the null character should be excluded from the line
|
||||
* length]
|
||||
*/
|
||||
// Detect whether the following line starts with a wide character and the end of the current line
|
||||
// is null, if so then we can be pretty sure the null character should be excluded from the line
|
||||
// length]
|
||||
const endsInNull = !(lines[i].hasContent(cols - 1)) && lines[i].getWidth(cols - 1) === 1;
|
||||
const followingLineStartsWithWide = lines[i + 1].getWidth(0) === 2;
|
||||
if (endsInNull && followingLineStartsWithWide) {
|
||||
|
||||
@@ -43,10 +43,8 @@ export class BufferSet extends Disposable implements IBufferSet {
|
||||
this._normalBuffer.value = this._normal;
|
||||
this._normal.fillViewportRows();
|
||||
|
||||
/*
|
||||
* The alt buffer should never have scrollback.
|
||||
* See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
*/
|
||||
// The alt buffer should never have scrollback.
|
||||
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
this._alt = new Buffer(false, this._optionsService, this._bufferService, this._logService);
|
||||
this._altBuffer.value = this._alt;
|
||||
this._activeBuffer = this._normal;
|
||||
@@ -88,11 +86,9 @@ export class BufferSet extends Disposable implements IBufferSet {
|
||||
}
|
||||
this._normal.x = this._alt.x;
|
||||
this._normal.y = this._alt.y;
|
||||
/*
|
||||
* The alt buffer should always be cleared when we switch to the normal
|
||||
* buffer. This frees up memory since the alt buffer should always be new
|
||||
* when activated.
|
||||
*/
|
||||
// The alt buffer should always be cleared when we switch to the normal
|
||||
// buffer. This frees up memory since the alt buffer should always be new
|
||||
// when activated.
|
||||
this._alt.clearAllMarkers();
|
||||
this._alt.clear();
|
||||
this._activeBuffer = this._normal;
|
||||
@@ -109,10 +105,8 @@ export class BufferSet extends Disposable implements IBufferSet {
|
||||
if (this._activeBuffer === this._alt) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Since the alt buffer is always cleared when the normal buffer is
|
||||
* activated, we want to fill it when switching to it.
|
||||
*/
|
||||
// Since the alt buffer is always cleared when the normal buffer is
|
||||
// activated, we want to fill it when switching to it.
|
||||
this._alt.fillViewportRows(fillAttr);
|
||||
this._alt.x = this._normal.x;
|
||||
this._alt.y = this._normal.y;
|
||||
|
||||
@@ -65,10 +65,8 @@ export class CellData extends AttributeData implements ICellData {
|
||||
}
|
||||
else if (value[CHAR_DATA_CHAR_INDEX].length === 2) {
|
||||
const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0);
|
||||
/*
|
||||
* if the 2-char string is a surrogate create single codepoint
|
||||
* everything else is combined
|
||||
*/
|
||||
// if the 2-char string is a surrogate create single codepoint
|
||||
// everything else is combined
|
||||
if (0xD800 <= code && code <= 0xDBFF) {
|
||||
const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1);
|
||||
if (0xDC00 <= second && second <= 0xDFFF) {
|
||||
|
||||
@@ -123,10 +123,8 @@ describe('Keyboard', () => {
|
||||
});
|
||||
|
||||
describe('On non-macOS platforms', () => {
|
||||
/*
|
||||
* Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100
|
||||
* http://unix.stackexchange.com/a/108106
|
||||
*/
|
||||
// Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100
|
||||
// http://unix.stackexchange.com/a/108106
|
||||
it('should return \\x1b[1;3D for alt+left', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;3D'); // CSI 1;3 D
|
||||
});
|
||||
|
||||
@@ -43,10 +43,8 @@ export function evaluateKeyboardEvent(
|
||||
): IKeyboardResult {
|
||||
const result: IKeyboardResult = {
|
||||
type: KeyboardResultType.SEND_KEY,
|
||||
/*
|
||||
* Whether to cancel event propagation (NOTE: this may not be needed since the event is
|
||||
* canceled at the end of keyDown
|
||||
*/
|
||||
// Whether to cancel event propagation (NOTE: this may not be needed since the event is
|
||||
// canceled at the end of keyDown
|
||||
cancel: false,
|
||||
// The new key even to emit
|
||||
key: undefined
|
||||
@@ -102,10 +100,8 @@ export function evaluateKeyboardEvent(
|
||||
case 13:
|
||||
// return/enter
|
||||
if (ev.key === 'c' && ev.ctrlKey) {
|
||||
/*
|
||||
* HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware
|
||||
* keyboard
|
||||
*/
|
||||
// HACK: Safari on iPad, iOS, AppleVisionPro sends key 13 when typing ctrl-c on hardware
|
||||
// keyboard
|
||||
result.key = C0.ETX;
|
||||
} else {
|
||||
result.key = ev.altKey ? C0.ESC + C0.CR : C0.CR;
|
||||
@@ -175,10 +171,8 @@ export function evaluateKeyboardEvent(
|
||||
case 45:
|
||||
// insert
|
||||
if (!ev.shiftKey && !ev.ctrlKey) {
|
||||
/*
|
||||
* <Ctrl> or <Shift> + <Insert> are used to
|
||||
* copy-paste on some systems.
|
||||
*/
|
||||
// <Ctrl> or <Shift> + <Insert> are used to
|
||||
// copy-paste on some systems.
|
||||
result.key = C0.ESC + '[2~';
|
||||
}
|
||||
break;
|
||||
@@ -352,13 +346,11 @@ export function evaluateKeyboardEvent(
|
||||
} else if (ev.keyCode === 32) {
|
||||
result.key = C0.ESC + (ev.ctrlKey ? C0.NUL : ' ');
|
||||
} else if (ev.key === 'Dead' && ev.code.startsWith('Key')) {
|
||||
/*
|
||||
* Reference: https://github.com/xtermjs/xterm.js/issues/3725
|
||||
* Alt will produce a "dead key" (initate composition) with some
|
||||
* of the letters in US layout (e.g. N/E/U).
|
||||
* It's safe to match against Key* since no other `code` values begin with "Key".
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac
|
||||
*/
|
||||
// Reference: https://github.com/xtermjs/xterm.js/issues/3725
|
||||
// Alt will produce a "dead key" (initate composition) with some
|
||||
// of the letters in US layout (e.g. N/E/U).
|
||||
// It's safe to match against Key* since no other `code` values begin with "Key".
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values#code_values_on_mac
|
||||
let keyString = ev.code.slice(3, 4);
|
||||
if (!ev.shiftKey) {
|
||||
keyString = keyString.toLowerCase();
|
||||
@@ -371,10 +363,8 @@ export function evaluateKeyboardEvent(
|
||||
result.type = KeyboardResultType.SELECT_ALL;
|
||||
}
|
||||
} else if (ev.key && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.keyCode >= 48 && ev.key.length === 1) {
|
||||
/*
|
||||
* Include only keys that that result in a _single_ character; don't include num lock,
|
||||
* volume up, etc.
|
||||
*/
|
||||
// Include only keys that that result in a _single_ character; don't include num lock,
|
||||
// volume up, etc.
|
||||
result.key = ev.key;
|
||||
} else if (ev.key && ev.ctrlKey && ev.shiftKey) {
|
||||
switch (ev.code) {
|
||||
|
||||
@@ -40,10 +40,8 @@ describe('KittyKeyboard', () => {
|
||||
const flags = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES;
|
||||
|
||||
it('shift+letter sends plain character in DISAMBIGUATE mode', () => {
|
||||
/*
|
||||
* Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding
|
||||
* Shift+a → "A" is not ambiguous, so send plain "A"
|
||||
*/
|
||||
// Kitty spec: DISAMBIGUATE only encodes keys ambiguous in legacy encoding
|
||||
// Shift+a → "A" is not ambiguous, so send plain "A"
|
||||
const result = kitty.evaluate(createEvent({ key: 'A', shiftKey: true }), flags);
|
||||
assert.strictEqual(result.key, 'A');
|
||||
});
|
||||
@@ -556,10 +554,8 @@ describe('KittyKeyboard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and
|
||||
* isn't specified in the spec, but press and repeat events shouldn't get swallowed.
|
||||
*/
|
||||
// Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense and
|
||||
// isn't specified in the spec, but press and repeat events shouldn't get swallowed.
|
||||
describe('REPORT_EVENT_TYPES flag without DISAMBIGUATE_ESCAPE_CODES', () => {
|
||||
const flags = KittyKeyboardFlags.REPORT_EVENT_TYPES;
|
||||
|
||||
|
||||
@@ -437,12 +437,10 @@ export class KittyKeyboard {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Spec § "Report all keys as escape codes": "Additionally, with this mode,
|
||||
* events for pressing modifier keys are reported." — i.e. *without* this
|
||||
* mode, modifier-key press events are suppressed. Kitty's is_modifier_key()
|
||||
* treats CapsLock/NumLock/ScrollLock as modifier keys for this rule.
|
||||
*/
|
||||
// Spec § "Report all keys as escape codes": "Additionally, with this mode,
|
||||
// events for pressing modifier keys are reported." — i.e. *without* this
|
||||
// mode, modifier-key press events are suppressed. Kitty's is_modifier_key()
|
||||
// treats CapsLock/NumLock/ScrollLock as modifier keys for this rule.
|
||||
if (this._isLockKey(ev) && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) {
|
||||
return result;
|
||||
}
|
||||
@@ -476,10 +474,8 @@ export class KittyKeyboard {
|
||||
// Special handling for Enter/Tab/Backspace.
|
||||
const specialKey = keyCode === 13 || keyCode === 9 || keyCode === 127;
|
||||
|
||||
/*
|
||||
* Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape
|
||||
* codes" is also set.
|
||||
*/
|
||||
// Per spec, Enter/Tab/Backspace will not have release events unless "Report all keys as escape
|
||||
// codes" is also set.
|
||||
if (specialKey && eventType === KittyKeyboardEventType.RELEASE && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES)) {
|
||||
return result;
|
||||
}
|
||||
@@ -489,18 +485,14 @@ export class KittyKeyboard {
|
||||
const useCsiU = !!(
|
||||
flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES ||
|
||||
(reportEventTypes && eventType === KittyKeyboardEventType.RELEASE) ||
|
||||
/*
|
||||
* Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so
|
||||
* just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity.
|
||||
* See: https://github.com/kovidgoyal/kitty/issues/9999
|
||||
*/
|
||||
// Enabling REPORT_EVENT_TYPES without DISAMBIGUATE_ESCAPE_CODES doesn't really make sense, so
|
||||
// just make REPORT_EVENT_TYPES imply DISAMBIGUATE_ESCAPE_CODES here for simplicity.
|
||||
// See: https://github.com/kovidgoyal/kitty/issues/9999
|
||||
((flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES || reportEventTypes) &&
|
||||
(
|
||||
/*
|
||||
* Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and
|
||||
* consider space to be a text-generating key, so these skip the isFunc fast-path and only
|
||||
* get CSI u when modifiers are present (handled below).
|
||||
*/
|
||||
// Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy mode" and
|
||||
// consider space to be a text-generating key, so these skip the isFunc fast-path and only
|
||||
// get CSI u when modifiers are present (handled below).
|
||||
(isFunc && !specialKey) ||
|
||||
(
|
||||
(modifiers > 0 && ev.key.length !== 1) ||
|
||||
|
||||
@@ -28,13 +28,11 @@ export function utf32ToString(data: Uint32Array, start: number = 0, end: number
|
||||
for (let i = start; i < end; ++i) {
|
||||
let codepoint = data[i];
|
||||
if (codepoint > 0xFFFF) {
|
||||
/*
|
||||
* JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate
|
||||
* pair conversion rules:
|
||||
* - subtract 0x10000 from code point, leaving a 20 bit number
|
||||
* - add high 10 bits to 0xD800 --> first surrogate
|
||||
* - add low 10 bits to 0xDC00 --> second surrogate
|
||||
*/
|
||||
// JS strings are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate
|
||||
// pair conversion rules:
|
||||
// - subtract 0x10000 from code point, leaving a 20 bit number
|
||||
// - add high 10 bits to 0xD800 --> first surrogate
|
||||
// - add low 10 bits to 0xDC00 --> second surrogate
|
||||
codepoint -= 0x10000;
|
||||
result += String.fromCharCode((codepoint >> 10) + 0xD800) + String.fromCharCode((codepoint % 0x400) + 0xDC00);
|
||||
} else {
|
||||
|
||||
@@ -12,10 +12,8 @@ it('wcwidth should match all values from the old implementation', function(): vo
|
||||
|
||||
// old implementation
|
||||
const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number {
|
||||
/*
|
||||
* extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
* combining characters
|
||||
*/
|
||||
// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
|
||||
// combining characters
|
||||
const COMBINING_BMP = [
|
||||
[0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],
|
||||
[0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],
|
||||
@@ -151,20 +149,18 @@ it('wcwidth should match all values from the old implementation', function(): vo
|
||||
}
|
||||
return table;
|
||||
}
|
||||
/*
|
||||
* get width from lookup table
|
||||
* position in container : num / CODEPOINTS_PER_ITEM
|
||||
* ==> n = table[Math.floor(num / 16)]
|
||||
* ==> n = table[num >> 4]
|
||||
* 16 codepoints per number: FFEEDDCCBBAA99887766554433221100
|
||||
* position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH
|
||||
* ==> m = (n % 16) * 2
|
||||
* ==> m = (num & 15) << 1
|
||||
* right shift to position m
|
||||
* ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766
|
||||
* we are only interested in 2 LSBs, cut off higher bits
|
||||
* ==> n = n & 3 e.g. 000000000000000000000000000000XX
|
||||
*/
|
||||
// get width from lookup table
|
||||
// position in container : num / CODEPOINTS_PER_ITEM
|
||||
// ==> n = table[Math.floor(num / 16)]
|
||||
// ==> n = table[num >> 4]
|
||||
// 16 codepoints per number: FFEEDDCCBBAA99887766554433221100
|
||||
// position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH
|
||||
// ==> m = (n % 16) * 2
|
||||
// ==> m = (num & 15) << 1
|
||||
// right shift to position m
|
||||
// ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766
|
||||
// we are only interested in 2 LSBs, cut off higher bits
|
||||
// ==> n = n & 3 e.g. 000000000000000000000000000000XX
|
||||
return (num: number): number => {
|
||||
num |= 0; // get asm.js like optimization under V8
|
||||
if (num < 32) {
|
||||
|
||||
@@ -94,10 +94,8 @@ export class UnicodeV6 implements IUnicodeVersionProvider {
|
||||
table.fill(0, 1, 32);
|
||||
table.fill(0, 0x7f, 0xa0);
|
||||
|
||||
/*
|
||||
* apply wide char rules first
|
||||
* wide chars
|
||||
*/
|
||||
// apply wide char rules first
|
||||
// wide chars
|
||||
table.fill(2, 0x1100, 0x1160);
|
||||
table[0x2329] = 2;
|
||||
table[0x232a] = 2;
|
||||
@@ -111,13 +109,11 @@ export class UnicodeV6 implements IUnicodeVersionProvider {
|
||||
table.fill(2, 0xff00, 0xff61);
|
||||
table.fill(2, 0xffe0, 0xffe7);
|
||||
|
||||
/*
|
||||
* apply combining last to ensure we overwrite
|
||||
* wrongly wide set chars:
|
||||
* the original algo evals combining first and falls
|
||||
* through to wide check so we simply do here the opposite
|
||||
* combining 0
|
||||
*/
|
||||
// apply combining last to ensure we overwrite
|
||||
// wrongly wide set chars:
|
||||
// the original algo evals combining first and falls
|
||||
// through to wide check so we simply do here the opposite
|
||||
// combining 0
|
||||
for (let r = 0; r < BMP_COMBINING.length; ++r) {
|
||||
table.fill(0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1);
|
||||
}
|
||||
|
||||
@@ -198,10 +198,8 @@ export class Win32InputMode {
|
||||
* Returns 0 for non-character keys.
|
||||
*/
|
||||
private _getUnicodeChar(ev: IKeyboardEvent): number {
|
||||
/*
|
||||
* Handle special keys that produce control characters
|
||||
* Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL
|
||||
*/
|
||||
// Handle special keys that produce control characters
|
||||
// Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL
|
||||
if (ev.ctrlKey && !ev.altKey && !ev.metaKey) {
|
||||
if (ev.key === 'Enter') {
|
||||
return 0x0A; // Line feed (Ctrl+Enter)
|
||||
@@ -247,11 +245,9 @@ export class Win32InputMode {
|
||||
state |= Win32ControlKeyState.SHIFT_PRESSED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: We can't distinguish left/right for ctrl/alt in standard browser events,
|
||||
* so we use the generic pressed flags. The right-side flags are used when
|
||||
* we can detect them (e.g., via code property).
|
||||
*/
|
||||
// Note: We can't distinguish left/right for ctrl/alt in standard browser events,
|
||||
// so we use the generic pressed flags. The right-side flags are used when
|
||||
// we can detect them (e.g., via code property).
|
||||
if (ev.ctrlKey) {
|
||||
if (ev.code === 'ControlRight') {
|
||||
state |= Win32ControlKeyState.RIGHT_CTRL_PRESSED;
|
||||
|
||||
@@ -90,16 +90,12 @@ export class WriteBuffer extends Disposable {
|
||||
* @deprecated Unreliable, to be removed soon.
|
||||
*/
|
||||
public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void {
|
||||
/*
|
||||
* stop writeSync recursions with maxSubsequentCalls argument
|
||||
* This is dangerous to use as it will lose the current data chunk
|
||||
* and return immediately.
|
||||
*/
|
||||
// stop writeSync recursions with maxSubsequentCalls argument
|
||||
// This is dangerous to use as it will lose the current data chunk
|
||||
// and return immediately.
|
||||
if (maxSubsequentCalls !== undefined && this._syncCalls > maxSubsequentCalls) {
|
||||
/*
|
||||
* comment next line if a whole loop block should only contain x `writeSync` calls
|
||||
* (total flat vs. deep nested limit)
|
||||
*/
|
||||
// comment next line if a whole loop block should only contain x `writeSync` calls
|
||||
// (total flat vs. deep nested limit)
|
||||
this._syncCalls = 0;
|
||||
return;
|
||||
}
|
||||
@@ -116,22 +112,18 @@ export class WriteBuffer extends Disposable {
|
||||
}
|
||||
this._isSyncWriting = true;
|
||||
|
||||
/*
|
||||
* force sync processing on pending data chunks to avoid in-band data scrambling
|
||||
* does the same as innerWrite but without event loop
|
||||
* we have to do it here as single loop steps to not corrupt loop subject
|
||||
* by another writeSync call triggered from _action
|
||||
*/
|
||||
// force sync processing on pending data chunks to avoid in-band data scrambling
|
||||
// does the same as innerWrite but without event loop
|
||||
// we have to do it here as single loop steps to not corrupt loop subject
|
||||
// by another writeSync call triggered from _action
|
||||
let chunk: string | Uint8Array | undefined;
|
||||
while (chunk = this._writeBuffer.shift()) {
|
||||
this._action(chunk);
|
||||
const cb = this._callbacks.shift();
|
||||
if (cb) cb();
|
||||
}
|
||||
/*
|
||||
* reset to avoid reprocessing of chunks with scheduled innerWrite call
|
||||
* stopping scheduled innerWrite by offset > length condition
|
||||
*/
|
||||
// reset to avoid reprocessing of chunks with scheduled innerWrite call
|
||||
// stopping scheduled innerWrite by offset > length condition
|
||||
this._pendingData = 0;
|
||||
this._bufferOffset = 0x7FFFFFFF;
|
||||
|
||||
@@ -149,11 +141,9 @@ export class WriteBuffer extends Disposable {
|
||||
if (!this._writeBuffer.length) {
|
||||
this._bufferOffset = 0;
|
||||
|
||||
/*
|
||||
* If this is the first write call after the user has done some input,
|
||||
* parse it immediately to minimize input latency,
|
||||
* otherwise schedule for the next event
|
||||
*/
|
||||
// If this is the first write call after the user has done some input,
|
||||
// parse it immediately to minimize input latency,
|
||||
// otherwise schedule for the next event
|
||||
if (this._didUserInput) {
|
||||
this._didUserInput = false;
|
||||
this._pendingData += data.length;
|
||||
@@ -244,19 +234,15 @@ export class WriteBuffer extends Disposable {
|
||||
* throughput by eval'ing `startTime` upfront pulling at least one more chunk into the
|
||||
* current microtask queue (executed before setTimeout).
|
||||
*/
|
||||
/*
|
||||
* const continuation: (r: boolean) => void = performance.now() - startTime >=
|
||||
* Constants.WRITE_TIMEOUT_MS
|
||||
* ? r => setTimeout(() => this._innerWrite(0, r))
|
||||
* : r => this._innerWrite(startTime, r);
|
||||
*/
|
||||
// const continuation: (r: boolean) => void = performance.now() - startTime >=
|
||||
// Constants.WRITE_TIMEOUT_MS
|
||||
// ? r => setTimeout(() => this._innerWrite(0, r))
|
||||
// : r => this._innerWrite(startTime, r);
|
||||
|
||||
/*
|
||||
* Handle exceptions synchronously to current band position, idea:
|
||||
* 1. spawn a single microtask which we allow to throw hard
|
||||
* 2. spawn a promise immediately resolving to `true`
|
||||
* (executed on the same queue, thus properly aligned before continuation happens)
|
||||
*/
|
||||
// Handle exceptions synchronously to current band position, idea:
|
||||
// 1. spawn a single microtask which we allow to throw hard
|
||||
// 2. spawn a promise immediately resolving to `true`
|
||||
// (executed on the same queue, thus properly aligned before continuation happens)
|
||||
result.catch(err => {
|
||||
queueMicrotask(() => {throw err;});
|
||||
return Promise.resolve(false);
|
||||
@@ -274,10 +260,8 @@ export class WriteBuffer extends Disposable {
|
||||
}
|
||||
}
|
||||
if (this._writeBuffer.length > this._bufferOffset) {
|
||||
/*
|
||||
* Allow renderer to catch up before processing the next batch
|
||||
* trim already processed chunks if we are above threshold
|
||||
*/
|
||||
// Allow renderer to catch up before processing the next batch
|
||||
// trim already processed chunks if we are above threshold
|
||||
if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) {
|
||||
this._writeBuffer = this._writeBuffer.slice(this._bufferOffset);
|
||||
this._callbacks = this._callbacks.slice(this._bufferOffset);
|
||||
|
||||
@@ -50,11 +50,9 @@ export function parseColor(data: string): [number, number, number] | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Named colors are currently not supported due to the large addition to the xterm.js bundle size
|
||||
* they would add. In order to support named colors, we would need some way of optionally loading
|
||||
* additional payloads so startup/download time is not bloated (see #3530).
|
||||
*/
|
||||
// Named colors are currently not supported due to the large addition to the xterm.js bundle size
|
||||
// they would add. In order to support named colors, we would need some way of optionally loading
|
||||
// additional payloads so startup/download time is not bloated (see #3530).
|
||||
}
|
||||
|
||||
// pad hex output to requested bit width
|
||||
|
||||
@@ -181,10 +181,8 @@ export class ApcHandler implements IApcHandler {
|
||||
} else if (success) {
|
||||
ret = this._handler(this._data);
|
||||
if (ret instanceof Promise) {
|
||||
/*
|
||||
* need to hold data until `ret` got resolved
|
||||
* dont care for errors, data will be freed anyway on next start
|
||||
*/
|
||||
// need to hold data until `ret` got resolved
|
||||
// dont care for errors, data will be freed anyway on next start
|
||||
return ret.then(res => {
|
||||
this._data = '';
|
||||
this._hitLimit = false;
|
||||
|
||||
@@ -147,12 +147,10 @@ export class DcsHandler implements IDcsHandler {
|
||||
constructor(private _handler: (data: string, params: IParams) => boolean | Promise<boolean>) { }
|
||||
|
||||
public hook(params: IParams): void {
|
||||
/*
|
||||
* since we need to preserve params until `unhook`, we have to clone it
|
||||
* (only borrowed from parser and spans multiple parser states)
|
||||
* perf optimization:
|
||||
* clone only, if we have non empty params, otherwise stick with default
|
||||
*/
|
||||
// since we need to preserve params until `unhook`, we have to clone it
|
||||
// (only borrowed from parser and spans multiple parser states)
|
||||
// perf optimization:
|
||||
// clone only, if we have non empty params, otherwise stick with default
|
||||
this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS;
|
||||
this._data = '';
|
||||
this._hitLimit = false;
|
||||
@@ -176,10 +174,8 @@ export class DcsHandler implements IDcsHandler {
|
||||
} else if (success) {
|
||||
ret = this._handler(this._data, this._params);
|
||||
if (ret instanceof Promise) {
|
||||
/*
|
||||
* need to hold data and params until `ret` got resolved
|
||||
* dont care for errors, data will be freed anyway on next start
|
||||
*/
|
||||
// need to hold data and params until `ret` got resolved
|
||||
// dont care for errors, data will be freed anyway on next start
|
||||
return ret.then(res => {
|
||||
this._params = EMPTY_PARAMS;
|
||||
this._data = '';
|
||||
|
||||
@@ -2065,10 +2065,8 @@ async function throwsAsync(fn: () => Promise<any>, message?: string | undefined)
|
||||
}
|
||||
|
||||
describe('EscapeSequenceParser - async', () => {
|
||||
/*
|
||||
* sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN
|
||||
* needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X
|
||||
*/
|
||||
// sequences: SGR 1;31 | hello SP | ESC %G | wor | ESC E | ld! | SGR 0 | EXE \r\n | $> | DCS 1;2 a [xyz] ST | OSC 1;foo=bar ST | APC X abc ST | FIN
|
||||
// needed handlers: CSI m, PRINT, ESC %G, ESC E, EXE \r, EXE \n, OSC 1, APC X
|
||||
const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1bP1;2axyz\x1b\\\x1b]1;foo=bar\x1b\\\x1b_Xabc\x1b\\FIN';
|
||||
let RESULT: any[];
|
||||
let parser: TestEscapeSequenceParser;
|
||||
@@ -2157,10 +2155,8 @@ describe('EscapeSequenceParser - async', () => {
|
||||
it('sync parse call does not work anymore', () => {
|
||||
assert.notEqual(!parseSync(parser, INPUT), true);
|
||||
assert.notDeepEqual(callstack, RESULT);
|
||||
/*
|
||||
* due to sync calling we should save exactly one saved stack
|
||||
* proper continuation is not possible anymore, as we lost the promise resolve value
|
||||
*/
|
||||
// due to sync calling we should save exactly one saved stack
|
||||
// proper continuation is not possible anymore, as we lost the promise resolve value
|
||||
assert.equal(parser.trackedStack.length, 1);
|
||||
});
|
||||
it('improper continuation should throw', async () => {
|
||||
@@ -2226,10 +2222,8 @@ describe('EscapeSequenceParser - async', () => {
|
||||
|
||||
// split to single char input
|
||||
for (let i = 0; i < INPUT.length; ++i) {
|
||||
/*
|
||||
* Note: a single fully awaited parse call always ends in sync mode,
|
||||
* which re-enables faster sync processing in the higher up callstack
|
||||
*/
|
||||
// Note: a single fully awaited parse call always ends in sync mode,
|
||||
// which re-enables faster sync processing in the higher up callstack
|
||||
await parseP(parser, INPUT[i]);
|
||||
}
|
||||
assert.deepEqual(callstack, RESULT);
|
||||
|
||||
@@ -15,22 +15,20 @@ import { ApcParser } from 'common/parser/ApcParser';
|
||||
/**
|
||||
* VT commands done by the parser
|
||||
*/
|
||||
/*
|
||||
* @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence."
|
||||
* @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence."
|
||||
* @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence."
|
||||
* @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences."
|
||||
* @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message."
|
||||
* @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence."
|
||||
* @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence."
|
||||
* @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence."
|
||||
* @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence."
|
||||
* @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences."
|
||||
* @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message."
|
||||
* @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence."
|
||||
* @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored."
|
||||
* @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence."
|
||||
*/
|
||||
// @vt: #Y ESC CSI "Control Sequence Introducer" "ESC [" "Start of a CSI sequence."
|
||||
// @vt: #Y ESC OSC "Operating System Command" "ESC ]" "Start of an OSC sequence."
|
||||
// @vt: #Y ESC DCS "Device Control String" "ESC P" "Start of a DCS sequence."
|
||||
// @vt: #Y ESC ST "String Terminator" "ESC \" "Terminator used for string type sequences."
|
||||
// @vt: #Y ESC PM "Privacy Message" "ESC ^" "Start of a privacy message."
|
||||
// @vt: #Y ESC APC "Application Program Command" "ESC _" "Start of an APC sequence."
|
||||
// @vt: #Y C1 CSI "Control Sequence Introducer" "\x9B" "Start of a CSI sequence."
|
||||
// @vt: #Y C1 OSC "Operating System Command" "\x9D" "Start of an OSC sequence."
|
||||
// @vt: #Y C1 DCS "Device Control String" "\x90" "Start of a DCS sequence."
|
||||
// @vt: #Y C1 ST "String Terminator" "\x9C" "Terminator used for string type sequences."
|
||||
// @vt: #Y C1 PM "Privacy Message" "\x9E" "Start of a privacy message."
|
||||
// @vt: #Y C1 APC "Application Program Command" "\x9F" "Start of an APC sequence."
|
||||
// @vt: #Y C0 NUL "Null" "\0, \x00" "NUL is ignored."
|
||||
// @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence."
|
||||
|
||||
/**
|
||||
* Table values are generated like this:
|
||||
@@ -97,10 +95,8 @@ const NON_ASCII_PRINTABLE = 0xA0;
|
||||
* Taken from https://vt100.net/emu/dec_ansi_parser.
|
||||
*/
|
||||
export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||
/*
|
||||
* table size:
|
||||
* (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1
|
||||
*/
|
||||
// table size:
|
||||
// (ParserState.STATE_LENGTH - 1) << TableAccess.INDEX_STATE_SHIFT | NON_ASCII_PRINTABLE + 1
|
||||
const table: TransitionTable = new TransitionTable(4257);
|
||||
|
||||
// range macro for byte
|
||||
@@ -504,11 +500,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._params.resetZdm();
|
||||
this._collect = 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
|
||||
*/
|
||||
// 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
|
||||
if (this._parseStack.state !== ParserStackType.NONE) {
|
||||
this._parseStack.state = ParserStackType.RESET;
|
||||
this._parseStack.handlers = []; // also release handlers ref
|
||||
@@ -585,10 +579,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
|
||||
// resume from async handler
|
||||
if (this._parseStack.state) {
|
||||
/*
|
||||
* allow sync parser reset even in continuation mode
|
||||
* Note: can be used to recover parser from improper continuation error below
|
||||
*/
|
||||
// allow sync parser reset even in continuation mode
|
||||
// Note: can be used to recover parser from improper continuation error below
|
||||
if (this._parseStack.state === ParserStackType.RESET) {
|
||||
this._parseStack.state = ParserStackType.NONE;
|
||||
start = this._parseStack.chunkPos + 1; // continue with next codepoint in GROUND
|
||||
@@ -613,11 +605,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
throw new Error('improper continuation due to previous async handler, giving up parsing');
|
||||
}
|
||||
|
||||
/*
|
||||
* we have to resume the old handler loop if:
|
||||
* - return value of the promise was `false`
|
||||
* - handlers are not exhausted yet
|
||||
*/
|
||||
// we have to resume the old handler loop if:
|
||||
// - return value of the promise was `false`
|
||||
// - handlers are not exhausted yet
|
||||
const handlers = this._parseStack.handlers;
|
||||
let handlerPos = this._parseStack.handlerPos - 1;
|
||||
switch (this._parseStack.state) {
|
||||
@@ -804,10 +794,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
const handlers = this._csiHandlers[this._collect << 8 | code];
|
||||
let j = handlers ? handlers.length - 1 : -1;
|
||||
for (; j >= 0; j--) {
|
||||
/*
|
||||
* true means success and to stop bubbling
|
||||
* a promise indicates an async handler that needs to finish before progressing
|
||||
*/
|
||||
// true means success and to stop bubbling
|
||||
// a promise indicates an async handler that needs to finish before progressing
|
||||
handlerResult = handlers[j](this._params);
|
||||
if (handlerResult === true) {
|
||||
break;
|
||||
@@ -845,10 +833,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
const handlersEsc = this._escHandlers[this._collect << 8 | code];
|
||||
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
|
||||
for (; jj >= 0; jj--) {
|
||||
/*
|
||||
* true means success and to stop bubbling
|
||||
* a promise indicates an async handler that needs to finish before progressing
|
||||
*/
|
||||
// true means success and to stop bubbling
|
||||
// a promise indicates an async handler that needs to finish before progressing
|
||||
handlerResult = handlersEsc[jj]();
|
||||
if (handlerResult === true) {
|
||||
break;
|
||||
@@ -870,10 +856,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._dcsParser.hook(this._collect << 8 | code, this._params);
|
||||
break;
|
||||
case ParserAction.DCS_PUT:
|
||||
/*
|
||||
* inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f
|
||||
* unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort)
|
||||
*/
|
||||
// inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f
|
||||
// unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort)
|
||||
for (let j = i + 1; ; ++j) {
|
||||
if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
|
||||
this._dcsParser.put(data, i, j);
|
||||
@@ -921,10 +905,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._apcParser.start(this._collect << 8 | code);
|
||||
break;
|
||||
case ParserAction.APC_PUT:
|
||||
/*
|
||||
* inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c
|
||||
* allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE
|
||||
*/
|
||||
// inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c
|
||||
// allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE
|
||||
for (let j = i + 1; ; ++j) {
|
||||
if (j < length && (
|
||||
(data[j] >= 0x20 && data[j] < 0x7f) || (data[j] >= 0x08 && data[j] < 0x0e) || data[j] >= NON_ASCII_PRINTABLE
|
||||
|
||||
@@ -133,11 +133,9 @@ export class OscParser implements IOscParser {
|
||||
}
|
||||
// do nothing if command was faulty
|
||||
if (this._state !== OscState.ABORT) {
|
||||
/*
|
||||
* if we are still in ID state and get an early end
|
||||
* means that the command has no payload thus we still have
|
||||
* to announce START and send END right after
|
||||
*/
|
||||
// if we are still in ID state and get an early end
|
||||
// means that the command has no payload thus we still have
|
||||
// to announce START and send END right after
|
||||
if (this._state === OscState.ID) {
|
||||
this._start();
|
||||
}
|
||||
@@ -168,11 +166,9 @@ export class OscParser implements IOscParser {
|
||||
}
|
||||
j--;
|
||||
}
|
||||
/*
|
||||
* cleanup left over handlers
|
||||
* we always have to call .end for proper cleanup,
|
||||
* here we use `success` to indicate whether a handler should execute
|
||||
*/
|
||||
// cleanup left over handlers
|
||||
// we always have to call .end for proper cleanup,
|
||||
// here we use `success` to indicate whether a handler should execute
|
||||
for (; j >= 0; j--) {
|
||||
handlerResult = this._active[j].end(false);
|
||||
if (handlerResult instanceof Promise) {
|
||||
@@ -226,10 +222,8 @@ export class OscHandler implements IOscHandler {
|
||||
} else if (success) {
|
||||
ret = this._handler(this._data);
|
||||
if (ret instanceof Promise) {
|
||||
/*
|
||||
* need to hold data until `ret` got resolved
|
||||
* dont care for errors, data will be freed anyway on next start
|
||||
*/
|
||||
// need to hold data until `ret` got resolved
|
||||
// dont care for errors, data will be freed anyway on next start
|
||||
return ret.then(res => {
|
||||
this._data = '';
|
||||
this._hitLimit = false;
|
||||
|
||||
@@ -103,28 +103,22 @@ export class BufferService extends Disposable implements IBufferService {
|
||||
buffer.ydisp++;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* When the buffer is full and the user has scrolled up, keep the text
|
||||
* stable unless ydisp is right at the top
|
||||
*/
|
||||
// When the buffer is full and the user has scrolled up, keep the text
|
||||
// stable unless ydisp is right at the top
|
||||
if (this.isUserScrolling) {
|
||||
buffer.ydisp = Math.max(buffer.ydisp - 1, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* scrollTop is non-zero which means no line will be going to the
|
||||
* scrollback, instead we can just shift them in-place.
|
||||
*/
|
||||
// scrollTop is non-zero which means no line will be going to the
|
||||
// scrollback, instead we can just shift them in-place.
|
||||
const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */;
|
||||
buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);
|
||||
buffer.lines.set(bottomRow, newLine.clone());
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the viewport to the bottom of the buffer unless the user is
|
||||
* scrolling.
|
||||
*/
|
||||
// Move the viewport to the bottom of the buffer unless the user is
|
||||
// scrolling.
|
||||
if (!this.isUserScrolling) {
|
||||
buffer.ydisp = buffer.ybase;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
*
|
||||
* This was heavily inspired from microsoft/vscode's dependency injection system (MIT).
|
||||
*/
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInstantiationService, IServiceIdentifier } from 'common/services/Services';
|
||||
import { getServiceDependencies } from 'common/services/ServiceRegistry';
|
||||
|
||||
@@ -16,10 +16,8 @@ interface IConsole {
|
||||
warn: LogType;
|
||||
}
|
||||
|
||||
/*
|
||||
* console is available on both node.js and browser contexts but the common
|
||||
* module doesn't depend on them so we need to explicitly declare it.
|
||||
*/
|
||||
// console is available on both node.js and browser contexts but the common
|
||||
// module doesn't depend on them so we need to explicitly declare it.
|
||||
declare const console: IConsole;
|
||||
|
||||
const optionsKeyToLogLevel: { [key: string]: LogLevelEnum } = {
|
||||
|
||||
@@ -105,10 +105,8 @@ function eventCode(e: ICoreMouseEvent, isSGR: boolean): number {
|
||||
if (e.action === CoreMouseAction.MOVE) {
|
||||
code |= CoreMouseAction.MOVE;
|
||||
} else if (e.action === CoreMouseAction.UP && !isSGR) {
|
||||
/*
|
||||
* special case - only SGR can report button on release
|
||||
* all others have to go with NONE
|
||||
*/
|
||||
// special case - only SGR can report button on release
|
||||
// all others have to go with NONE
|
||||
code |= CoreMouseButton.NONE;
|
||||
}
|
||||
}
|
||||
@@ -128,12 +126,10 @@ const DEFAULT_ENCODINGS: { [key: string]: CoreMouseEncoding } = {
|
||||
*/
|
||||
DEFAULT: (e: ICoreMouseEvent) => {
|
||||
const params = [eventCode(e, false) + 32, e.col + 32, e.row + 32];
|
||||
/*
|
||||
* supress mouse report if we exceed addressible range
|
||||
* Note this is handled differently by emulators
|
||||
* - xterm: sends 0;0 coords instead
|
||||
* - vte, konsole: no report
|
||||
*/
|
||||
// supress mouse report if we exceed addressible range
|
||||
// Note this is handled differently by emulators
|
||||
// - xterm: sends 0;0 coords instead
|
||||
// - vte, konsole: no report
|
||||
if (params[0] > 255 || params[1] > 255 || params[2] > 255) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -90,10 +90,8 @@ export class OptionsService extends Disposable implements IOptionsService {
|
||||
this.options = { ... defaultOptions };
|
||||
this._setupOptions();
|
||||
|
||||
/*
|
||||
* Clear out options that could link outside xterm.js as they could easily cause an embedder
|
||||
* memory leak
|
||||
*/
|
||||
// Clear out options that could link outside xterm.js as they could easily cause an embedder
|
||||
// memory leak
|
||||
this._register(toDisposable(() => {
|
||||
this.rawOptions.linkHandler = null;
|
||||
this.rawOptions.documentOverride = null;
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
*
|
||||
* This was heavily inspired from microsoft/vscode's dependency injection system (MIT).
|
||||
*/
|
||||
/*
|
||||
*---------------------------------------------------------------------------------------------
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IServiceIdentifier } from 'common/services/Services';
|
||||
|
||||
|
||||
@@ -78,20 +78,16 @@ export class UnicodeService implements IUnicodeService {
|
||||
// surrogate pair first
|
||||
if (0xD800 <= code && code <= 0xDBFF) {
|
||||
if (++i >= length) {
|
||||
/*
|
||||
* this should not happen with strings retrieved from
|
||||
* Buffer.translateToString as it converts from UTF-32
|
||||
* and therefore always should contain the second part
|
||||
* for any other string we still have to handle it somehow:
|
||||
* simply treat the lonely surrogate first as a single char (UCS-2 behavior)
|
||||
*/
|
||||
// this should not happen with strings retrieved from
|
||||
// Buffer.translateToString as it converts from UTF-32
|
||||
// and therefore always should contain the second part
|
||||
// for any other string we still have to handle it somehow:
|
||||
// simply treat the lonely surrogate first as a single char (UCS-2 behavior)
|
||||
return result + this.wcwidth(code);
|
||||
}
|
||||
const second = s.charCodeAt(i);
|
||||
/*
|
||||
* convert surrogate pair to high codepoint only for valid second part (UTF-16)
|
||||
* otherwise treat them independently (UCS-2 behavior)
|
||||
*/
|
||||
// convert surrogate pair to high codepoint only for valid second part (UTF-16)
|
||||
// otherwise treat them independently (UCS-2 behavior)
|
||||
if (0xDC00 <= second && second <= 0xDFFF) {
|
||||
code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
||||
} else {
|
||||
|
||||
@@ -58,11 +58,9 @@ export class Terminal extends Disposable implements ITerminalApi {
|
||||
}
|
||||
|
||||
private _checkReadonlyOptions(propName: string): void {
|
||||
/*
|
||||
* Throw an error if any constructor only option is modified
|
||||
* from terminal.options
|
||||
* Modifications from anywhere else are allowed
|
||||
*/
|
||||
// Throw an error if any constructor only option is modified
|
||||
// from terminal.options
|
||||
// Modifications from anywhere else are allowed
|
||||
if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) {
|
||||
throw new Error(`Option "${propName}" can only be set in the constructor`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user