mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into recreate-terminal
This commit is contained in:
@@ -269,4 +269,81 @@ describe('Buffer', () => {
|
||||
assert.equal(buffer.markers.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe ('translateBufferLineToString', () => {
|
||||
it('should handle selecting a section of ascii text', () => {
|
||||
buffer.lines.set(0, [
|
||||
[ null, 'a', 1, 'a'.charCodeAt(0)],
|
||||
[ null, 'b', 1, 'b'.charCodeAt(0)],
|
||||
[ null, 'c', 1, 'c'.charCodeAt(0)],
|
||||
[ null, 'd', 1, 'd'.charCodeAt(0)]
|
||||
]);
|
||||
|
||||
const str = buffer.translateBufferLineToString(0, true, 0, 2);
|
||||
assert.equal(str, 'ab');
|
||||
});
|
||||
|
||||
it('should handle a cut-off double width character by including it', () => {
|
||||
buffer.lines.set(0, [
|
||||
[ null, '語', 2, 35486 ],
|
||||
[ null, '', 0, null],
|
||||
[ null, 'a', 1, 'a'.charCodeAt(0)]
|
||||
]);
|
||||
|
||||
const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
assert.equal(str1, '語');
|
||||
});
|
||||
|
||||
it('should handle a zero width character in the middle of the string by not including it', () => {
|
||||
buffer.lines.set(0, [
|
||||
[ null, '語', 2, '語'.charCodeAt(0) ],
|
||||
[ null, '', 0, null],
|
||||
[ null, 'a', 1, 'a'.charCodeAt(0)]
|
||||
]);
|
||||
|
||||
const str0 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
assert.equal(str0, '語');
|
||||
|
||||
const str1 = buffer.translateBufferLineToString(0, true, 0, 2);
|
||||
assert.equal(str1, '語');
|
||||
|
||||
const str2 = buffer.translateBufferLineToString(0, true, 0, 3);
|
||||
assert.equal(str2, '語a');
|
||||
});
|
||||
|
||||
it('should handle single width emojis', () => {
|
||||
buffer.lines.set(0, [
|
||||
[ null, '😁', 1, '😁'.charCodeAt(0) ],
|
||||
[ null, 'a', 1, 'a'.charCodeAt(0)]
|
||||
]);
|
||||
|
||||
const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
assert.equal(str1, '😁');
|
||||
|
||||
const str2 = buffer.translateBufferLineToString(0, true, 0, 2);
|
||||
assert.equal(str2, '😁a');
|
||||
});
|
||||
|
||||
it('should handle double width emojis', () => {
|
||||
buffer.lines.set(0, [
|
||||
[ null, '😁', 2, '😁'.charCodeAt(0) ],
|
||||
[ null, '', 0, null]
|
||||
]);
|
||||
|
||||
const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
assert.equal(str1, '😁');
|
||||
|
||||
const str2 = buffer.translateBufferLineToString(0, true, 0, 2);
|
||||
assert.equal(str2, '😁');
|
||||
|
||||
buffer.lines.set(0, [
|
||||
[ null, '😁', 2, '😁'.charCodeAt(0) ],
|
||||
[ null, '', 0, null],
|
||||
[ null, 'a', 1, 'a'.charCodeAt(0)]
|
||||
]);
|
||||
|
||||
const str3 = buffer.translateBufferLineToString(0, true, 0, 3);
|
||||
assert.equal(str3, '😁a');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -227,7 +227,7 @@ export class Buffer implements IBuffer {
|
||||
if (startCol >= i) {
|
||||
startIndex--;
|
||||
}
|
||||
if (endCol >= i) {
|
||||
if (endCol > i) {
|
||||
endIndex--;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { CharMeasure } from './ui/CharMeasure';
|
||||
import { SelectionManager } from './SelectionManager';
|
||||
import { SelectionManager, SelectionMode } from './SelectionManager';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import { BufferSet } from './BufferSet';
|
||||
import { LineData, CharData, ITerminal, IBuffer } from './Types';
|
||||
@@ -25,6 +25,8 @@ class TestSelectionManager extends SelectionManager {
|
||||
|
||||
public get model(): SelectionModel { return this._model; }
|
||||
|
||||
public set selectionMode(mode: SelectionMode) { this._activeSelectionMode = mode; }
|
||||
|
||||
public selectLineAt(line: number): void { this._selectLineAt(line); }
|
||||
public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords, true); }
|
||||
|
||||
@@ -378,4 +380,61 @@ describe('SelectionManager', () => {
|
||||
assert.equal(selectionManager.hasSelection, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('column selection', () => {
|
||||
it('should select a column of text', () => {
|
||||
buffer.lines.length = 3;
|
||||
buffer.lines.set(0, stringToRow('abcdefghij'));
|
||||
buffer.lines.set(1, stringToRow('klmnopqrst'));
|
||||
buffer.lines.set(2, stringToRow('uvwxyz'));
|
||||
|
||||
selectionManager.selectionMode = SelectionMode.COLUMN;
|
||||
selectionManager.model.selectionStart = [2, 0];
|
||||
selectionManager.model.selectionEnd = [4, 2];
|
||||
|
||||
assert.equal(selectionManager.selectionText, 'cd\nmn\nwx');
|
||||
});
|
||||
|
||||
it('should select a column of text without chopping up double width characters', () => {
|
||||
buffer.lines.length = 3;
|
||||
buffer.lines.set(0, stringToRow('a'));
|
||||
buffer.lines.set(1, stringToRow('語'));
|
||||
buffer.lines.set(2, stringToRow('b'));
|
||||
|
||||
selectionManager.selectionMode = SelectionMode.COLUMN;
|
||||
selectionManager.model.selectionStart = [0, 0];
|
||||
selectionManager.model.selectionEnd = [1, 2];
|
||||
|
||||
assert.equal(selectionManager.selectionText, 'a\n語\nb');
|
||||
});
|
||||
|
||||
it('should select a column of text with single character emojis', () => {
|
||||
buffer.lines.length = 3;
|
||||
buffer.lines.set(0, stringToRow('a'));
|
||||
buffer.lines.set(1, stringToRow('☃'));
|
||||
buffer.lines.set(2, stringToRow('c'));
|
||||
|
||||
selectionManager.selectionMode = SelectionMode.COLUMN;
|
||||
selectionManager.model.selectionStart = [0, 0];
|
||||
selectionManager.model.selectionEnd = [1, 2];
|
||||
|
||||
assert.equal(selectionManager.selectionText, 'a\n☃\nc');
|
||||
});
|
||||
|
||||
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.
|
||||
buffer.lines.length = 3;
|
||||
buffer.lines.set(0, stringToRow('a '));
|
||||
buffer.lines.set(1, stringArrayToRow(['😁', ' ']));
|
||||
buffer.lines.set(2, stringToRow('c '));
|
||||
|
||||
selectionManager.selectionMode = SelectionMode.COLUMN;
|
||||
selectionManager.model.selectionStart = [0, 0];
|
||||
selectionManager.model.selectionEnd = [1, 2];
|
||||
|
||||
assert.equal(selectionManager.selectionText, 'a\n😁\nc');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+55
-25
@@ -54,10 +54,11 @@ interface IWordPosition {
|
||||
/**
|
||||
* A selection mode, this drives how the selection behaves on mouse move.
|
||||
*/
|
||||
const enum SelectionMode {
|
||||
export const enum SelectionMode {
|
||||
NORMAL,
|
||||
WORD,
|
||||
LINE
|
||||
LINE,
|
||||
COLUMN
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +81,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
/**
|
||||
* The current selection mode.
|
||||
*/
|
||||
private _activeSelectionMode: SelectionMode;
|
||||
protected _activeSelectionMode: SelectionMode;
|
||||
|
||||
/**
|
||||
* A setInterval timer that is active while the mouse is down whose callback
|
||||
@@ -182,30 +183,43 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get first row
|
||||
const startRowEndCol = start[1] === end[1] ? end[0] : null;
|
||||
const result: string[] = [];
|
||||
result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));
|
||||
|
||||
// Get middle rows
|
||||
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
|
||||
const bufferLine = this._buffer.lines.get(i);
|
||||
const lineText = this._buffer.translateBufferLineToString(i, true);
|
||||
if ((<any>bufferLine).isWrapped) {
|
||||
result[result.length - 1] += lineText;
|
||||
} else {
|
||||
if (this._activeSelectionMode === SelectionMode.COLUMN) {
|
||||
// Ignore zero width selections
|
||||
if (start[0] === end[0]) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for (let i = start[1]; i <= end[1]; i++) {
|
||||
const lineText = this._buffer.translateBufferLineToString(i, true, start[0], end[0]);
|
||||
result.push(lineText);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Get first row
|
||||
const startRowEndCol = start[1] === end[1] ? end[0] : null;
|
||||
result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));
|
||||
|
||||
// Get final row
|
||||
if (start[1] !== end[1]) {
|
||||
const bufferLine = this._buffer.lines.get(end[1]);
|
||||
const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]);
|
||||
if ((<any>bufferLine).isWrapped) {
|
||||
result[result.length - 1] += lineText;
|
||||
} else {
|
||||
result.push(lineText);
|
||||
// Get middle rows
|
||||
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
|
||||
const bufferLine = this._buffer.lines.get(i);
|
||||
const lineText = this._buffer.translateBufferLineToString(i, true);
|
||||
if ((<any>bufferLine).isWrapped) {
|
||||
result[result.length - 1] += lineText;
|
||||
} else {
|
||||
result.push(lineText);
|
||||
}
|
||||
}
|
||||
|
||||
// Get final row
|
||||
if (start[1] !== end[1]) {
|
||||
const bufferLine = this._buffer.lines.get(end[1]);
|
||||
const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]);
|
||||
if ((<any>bufferLine).isWrapped) {
|
||||
result[result.length - 1] += lineText;
|
||||
} else {
|
||||
result.push(lineText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +268,11 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
*/
|
||||
private _refresh(): void {
|
||||
this._refreshAnimationFrame = null;
|
||||
this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd });
|
||||
this.emit('refresh', {
|
||||
start: this._model.finalSelectionStart,
|
||||
end: this._model.finalSelectionEnd,
|
||||
columnSelectMode: this._activeSelectionMode === SelectionMode.COLUMN
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,7 +381,11 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
public shouldForceSelection(event: MouseEvent): boolean {
|
||||
return Browser.isMac ? event.altKey : event.shiftKey;
|
||||
if (Browser.isMac) {
|
||||
return event.altKey && this._terminal.options.macOptionClickForcesSelection;
|
||||
}
|
||||
|
||||
return event.shiftKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,7 +476,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
private _onSingleClick(event: MouseEvent): void {
|
||||
this._model.selectionStartLength = 0;
|
||||
this._model.isSelectAllActive = false;
|
||||
this._activeSelectionMode = SelectionMode.NORMAL;
|
||||
this._activeSelectionMode = this.shouldColumnSelect(event) ? SelectionMode.COLUMN : SelectionMode.NORMAL;
|
||||
|
||||
// Initialize the new selection
|
||||
this._model.selectionStart = this._getMouseBufferCoords(event);
|
||||
@@ -507,6 +529,14 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the selection manager should operate in column select mode
|
||||
* @param event the mouse or keyboard event
|
||||
*/
|
||||
public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean {
|
||||
return event.altKey && !(Browser.isMac && this._terminal.options.macOptionClickForcesSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the mousemove event when the mouse button is down, recording the
|
||||
* end of the selection and refreshing the selection.
|
||||
|
||||
+21
-1
@@ -98,6 +98,7 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
|
||||
screenReaderMode: false,
|
||||
debug: false,
|
||||
macOptionIsMeta: false,
|
||||
macOptionClickForcesSelection: false,
|
||||
cancelEvents: false,
|
||||
disableStdin: false,
|
||||
useFlowControl: false,
|
||||
@@ -586,6 +587,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
if (!wasMondifierKeyOnlyEvent(ev)) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
self._keyUp(ev);
|
||||
}, true));
|
||||
|
||||
this.register(addDisposableDomListener(this.textarea, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));
|
||||
@@ -696,7 +699,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
this.selectionManager = new SelectionManager(this, this.charMeasure);
|
||||
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this.selectionManager.onMouseDown(e)));
|
||||
this.register(this.selectionManager.addDisposableListener('refresh', data => this.renderer.onSelectionChanged(data.start, data.end)));
|
||||
this.register(this.selectionManager.addDisposableListener('refresh', data => this.renderer.onSelectionChanged(data.start, data.end, data.columnSelectMode)));
|
||||
this.register(this.selectionManager.addDisposableListener('newselection', 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
|
||||
@@ -1105,6 +1108,17 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the cursor style for different selection modes
|
||||
*/
|
||||
public updateCursorStyle(ev: KeyboardEvent): void {
|
||||
if (this.selectionManager && this.selectionManager.shouldColumnSelect(ev)) {
|
||||
this.element.classList.add('xterm-cursor-crosshair');
|
||||
} else {
|
||||
this.element.classList.remove('xterm-cursor-crosshair');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the cursor element
|
||||
*/
|
||||
@@ -1422,6 +1436,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
const result = evaluateKeyboardEvent(event, this.applicationCursor, this.browser.isMac, this.options.macOptionIsMeta);
|
||||
|
||||
this.updateCursorStyle(event);
|
||||
|
||||
// if (result.key === C0.DC3) { // XOFF
|
||||
// this._writeStopped = true;
|
||||
// } else if (result.key === C0.DC1) { // XON
|
||||
@@ -1493,6 +1509,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
}
|
||||
}
|
||||
|
||||
protected _keyUp(ev: KeyboardEvent): void {
|
||||
this.updateCursorStyle(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a keypress event.
|
||||
* Key Resources:
|
||||
|
||||
@@ -49,7 +49,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
public onFocus(terminal: ITerminal): void {}
|
||||
public onCursorMove(terminal: ITerminal): void {}
|
||||
public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {}
|
||||
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {}
|
||||
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {}
|
||||
|
||||
public onThemeChanged(terminal: ITerminal, colorSet: IColorSet): void {
|
||||
this._refreshCharAtlas(terminal, colorSet);
|
||||
|
||||
@@ -143,8 +143,8 @@ export class Renderer extends EventEmitter implements IRenderer {
|
||||
this._runOperation(l => l.onFocus(this._terminal));
|
||||
}
|
||||
|
||||
public onSelectionChanged(start: [number, number], end: [number, number]): void {
|
||||
this._runOperation(l => l.onSelectionChanged(this._terminal, start, end));
|
||||
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {
|
||||
this._runOperation(l => l.onSelectionChanged(this._terminal, start, end, columnSelectMode));
|
||||
}
|
||||
|
||||
public onCursorMove(): void {
|
||||
|
||||
@@ -37,7 +37,7 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
}
|
||||
|
||||
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {
|
||||
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void {
|
||||
// Selection has not changed
|
||||
if (this._state.start === start || this._state.end === end) {
|
||||
return;
|
||||
@@ -62,21 +62,30 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw first row
|
||||
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
||||
const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
||||
this._ctx.fillStyle = this._colors.selection.css;
|
||||
this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
|
||||
|
||||
// Draw middle rows
|
||||
const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
|
||||
this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount);
|
||||
if (columnSelectMode) {
|
||||
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
||||
const width = end[0] - startCol;
|
||||
const height = viewportCappedEndRow - viewportCappedStartRow + 1;
|
||||
this.fillCells(startCol, viewportCappedStartRow, width, height);
|
||||
|
||||
// Draw final row
|
||||
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
||||
// Only draw viewportEndRow if it's not the same as viewportStartRow
|
||||
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
||||
this.fillCells(0, viewportCappedEndRow, endCol, 1);
|
||||
} else {
|
||||
// Draw first row
|
||||
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
||||
const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
||||
this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
|
||||
|
||||
// Draw middle rows
|
||||
const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
|
||||
this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount);
|
||||
|
||||
// Draw final row
|
||||
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
||||
// Only draw viewportEndRow if it's not the same as viewportStartRow
|
||||
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
||||
this.fillCells(0, viewportCappedEndRow, endCol, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Save state for next render
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface IRenderer extends IEventEmitter, IDisposable {
|
||||
onCharSizeChanged(): void;
|
||||
onBlur(): void;
|
||||
onFocus(): void;
|
||||
onSelectionChanged(start: [number, number], end: [number, number]): void;
|
||||
onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void;
|
||||
onCursorMove(): void;
|
||||
onOptionsChanged(): void;
|
||||
clear(): void;
|
||||
@@ -99,7 +99,7 @@ export interface IRenderLayer {
|
||||
/**
|
||||
* Calls when the selection changes.
|
||||
*/
|
||||
onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void;
|
||||
onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void;
|
||||
|
||||
/**
|
||||
* Resize the render layer.
|
||||
|
||||
@@ -217,7 +217,7 @@ export class DomRenderer extends EventEmitter implements IRenderer {
|
||||
this._rowContainer.classList.add(FOCUS_CLASS);
|
||||
}
|
||||
|
||||
public onSelectionChanged(start: [number, number], end: [number, number]): void {
|
||||
public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void {
|
||||
// Remove all selections
|
||||
while (this._selectionContainer.children.length) {
|
||||
this._selectionContainer.removeChild(this._selectionContainer.children[0]);
|
||||
@@ -241,18 +241,25 @@ export class DomRenderer extends EventEmitter implements IRenderer {
|
||||
|
||||
// Create the selections
|
||||
const documentFragment = document.createDocumentFragment();
|
||||
// Draw first row
|
||||
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
||||
const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
|
||||
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));
|
||||
// Draw middle rows
|
||||
const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1;
|
||||
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._terminal.cols, middleRowsCount));
|
||||
// Draw final row
|
||||
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
||||
// Only draw viewportEndRow if it's not the same as viewporttartRow
|
||||
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
|
||||
documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol));
|
||||
|
||||
if (columnSelectMode) {
|
||||
documentFragment.appendChild(
|
||||
this._createSelectionElement(viewportCappedStartRow, startCol, end[0], viewportCappedEndRow - viewportStartRow + 1)
|
||||
);
|
||||
} else {
|
||||
// Draw first row
|
||||
const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
|
||||
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));
|
||||
// Draw middle rows
|
||||
const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1;
|
||||
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._terminal.cols, middleRowsCount));
|
||||
// Draw final row
|
||||
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
||||
// Only draw viewportEndRow if it's not the same as viewporttartRow
|
||||
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
|
||||
documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol));
|
||||
}
|
||||
}
|
||||
this._selectionContainer.appendChild(documentFragment);
|
||||
}
|
||||
|
||||
@@ -139,6 +139,11 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.xterm.xterm-cursor-crosshair {
|
||||
/* Column selection mode */
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.xterm .xterm-accessibility,
|
||||
.xterm .xterm-message {
|
||||
position: absolute;
|
||||
|
||||
Vendored
+9
@@ -124,6 +124,15 @@ declare module 'xterm' {
|
||||
*/
|
||||
macOptionIsMeta?: boolean;
|
||||
|
||||
/**
|
||||
* Whether holding a modifier key will force normal selection behavior,
|
||||
* regardless of whether the terminal is in mouse events mode. This will
|
||||
* also prevent mouse events from being emitted by the terminal. For example,
|
||||
* this allows you to use xterm.js' regular selection inside tmux with
|
||||
* mouse mode enabled.
|
||||
*/
|
||||
macOptionClickForcesSelection?: boolean;
|
||||
|
||||
/**
|
||||
* (EXPERIMENTAL) The type of renderer to use, this allows using the
|
||||
* fallback DOM renderer when canvas is too slow for the environment. The
|
||||
|
||||
Reference in New Issue
Block a user