Files
xterm.js/src/SelectionManager.ts
T
2017-05-20 00:01:55 -07:00

142 lines
4.7 KiB
TypeScript

/**
* @license MIT
*/
import { CharMeasure } from './utils/CharMeasure';
import { CircularList } from './utils/CircularList';
import { EventEmitter } from './EventEmitter';
import * as Mouse from './utils/Mouse';
import { ITerminal } from './Interfaces';
export class SelectionManager extends EventEmitter {
// TODO: Create a SelectionModel
private _selectionStart: [number, number];
private _selectionEnd: [number, number];
private _mouseMoveListener: EventListener;
constructor(
private _terminal: ITerminal,
private _buffer: CircularList<any>,
private _rowContainer: HTMLElement,
private _selectionContainer: HTMLElement,
private _charMeasure: CharMeasure
) {
super();
this._attachListeners();
}
private _attachListeners() {
this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
this._buffer.on('trim', amount => this._onTrim(amount));
this._rowContainer.addEventListener('mousedown', event => this._onMouseDown(event));
this._rowContainer.addEventListener('mouseup', event => this._onMouseUp(event));
}
public get selectionText(): string {
if (!this._selectionStart || !this._selectionEnd) {
return '';
}
const flipValues = this._selectionStart[1] > this._selectionEnd[1] ||
(this._selectionStart[1] === this._selectionEnd[1] && this._selectionStart[0] > this._selectionEnd[0]);
const start = flipValues ? this._selectionEnd : this._selectionStart;
const end = flipValues ? this._selectionStart : this._selectionEnd;
const startRowEndCol = start[1] === end[1] ? end[0] : null;
let result: string[] = [];
result.push(this._translateBufferLineToString(this._buffer.get(start[1]), start[0], startRowEndCol));
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
result.push(this._translateBufferLineToString(this._buffer.get(i)));
}
if (start[1] !== end[1]) {
result.push(this._translateBufferLineToString(this._buffer.get(end[1]), 0, end[1]));
}
console.log('result: ' + result);
return result.join('\n');
}
private _translateBufferLineToString(line: any, startCol: number = 0, endCol: number = null): string {
// TODO: This function should live in a buffer or buffer line class
endCol = endCol || line.length
let result = '';
for (let i = startCol; i < endCol; i++) {
result += line[i][1];
}
// TODO: Trim line?
return result;
// TODO: Handle the double-width character case
}
/**
* Redraws the selection.
*/
public refresh(): void {
// TODO: Figure out when to refresh the selection vs when to refresh the viewport
this.emit('refresh', { start: this._selectionStart, end: this._selectionEnd });
}
/**
* Handle the buffer being trimmed, adjust the selection position.
* @param amount The amount the buffer is being trimmed.
*/
private _onTrim(amount: number) {
// TODO: Somehow map the selection coordinates with the list that is constantly being trimmed
// Maybe we need an ID in the CircularList that starts from 0 for the first entry and increments
// console.log('trimmed: ' + amount);
// Adjust the selection position based on the trimmed amount.
this._selectionStart[0] -= amount;
this._selectionEnd[0] -= amount;
// The selection has moved off the buffer, clear it.
if (this._selectionEnd[0] < 0) {
this._selectionStart = null;
this._selectionEnd = null;
this.refresh();
return;
}
// If the selection start is trimmed, ensure the start column is 0.
if (this._selectionStart[0] < 0) {
this._selectionStart[1] = 0;
}
}
// TODO: Handle splice/shiftElements in the buffer (just clear the selection?)
private _getMouseBufferCoords(event: MouseEvent) {
const coords = Mouse.getCoords(event, this._rowContainer, this._charMeasure);
// Convert to 0-based
coords[0]--;
coords[1]--;
// Convert viewport coords to buffer coords
coords[1] += this._terminal.ydisp;
return coords;
}
private _onMouseDown(event: MouseEvent) {
this._selectionStart = this._getMouseBufferCoords(event);
if (this._selectionStart) {
this._selectionEnd = null;
this._rowContainer.addEventListener('mousemove', this._mouseMoveListener);
this.refresh();
}
}
private _onMouseMove(event: MouseEvent) {
this._selectionEnd = this._getMouseBufferCoords(event);
// TODO: Only draw here if the selection changes
this.refresh();
}
private _onMouseUp(event: MouseEvent) {
// console.log('mouseup');
// console.log('start', this._selectionStart);
// console.log('end', this._selectionEnd);
if (!this._selectionStart) {
return;
}
this._rowContainer.removeEventListener('mousemove', this._mouseMoveListener);
}
}