2 * Copyright (c) 2017 The xterm.js authors. All rights reserved.
6 import { IBufferService } from 'common/services/Services';
9 * Represents a selection within the buffer. This model only cares about column
10 * and row coordinates, not wide characters.
12 export class SelectionModel {
14 * Whether select all is currently active.
16 public isSelectAllActive: boolean = false;
19 * The minimal length of the selection from the start position. When double
20 * clicking on a word, the word will be selected which makes the selection
21 * start at the start of the word and makes this variable the length.
23 public selectionStartLength: number = 0;
26 * The [x, y] position the selection starts at.
28 public selectionStart: [number, number] | undefined;
31 * The [x, y] position the selection ends at.
33 public selectionEnd: [number, number] | undefined;
36 private _bufferService: IBufferService
41 * Clears the current selection.
43 public clearSelection(): void {
44 this.selectionStart = undefined;
45 this.selectionEnd = undefined;
46 this.isSelectAllActive = false;
47 this.selectionStartLength = 0;
51 * The final selection start, taking into consideration select all.
53 public get finalSelectionStart(): [number, number] | undefined {
54 if (this.isSelectAllActive) {
58 if (!this.selectionEnd || !this.selectionStart) {
59 return this.selectionStart;
62 return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart;
66 * The final selection end, taking into consideration select all, double click
67 * word selection and triple click line selection.
69 public get finalSelectionEnd(): [number, number] | undefined {
70 if (this.isSelectAllActive) {
71 return [this._bufferService.cols, this._bufferService.buffer.ybase + this._bufferService.rows - 1];
74 if (!this.selectionStart) {
78 // Use the selection start + length if the end doesn't exist or they're reversed
79 if (!this.selectionEnd || this.areSelectionValuesReversed()) {
80 const startPlusLength = this.selectionStart[0] + this.selectionStartLength;
81 if (startPlusLength > this._bufferService.cols) {
82 return [startPlusLength % this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols)];
84 return [startPlusLength, this.selectionStart[1]];
87 // Ensure the the word/line is selected after a double/triple click
88 if (this.selectionStartLength) {
89 // Select the larger of the two when start and end are on the same line
90 if (this.selectionEnd[1] === this.selectionStart[1]) {
91 return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]];
94 return this.selectionEnd;
98 * Returns whether the selection start and end are reversed.
100 public areSelectionValuesReversed(): boolean {
101 const start = this.selectionStart;
102 const end = this.selectionEnd;
103 if (!start || !end) {
106 return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]);
110 * Handle the buffer being trimmed, adjust the selection position.
111 * @param amount The amount the buffer is being trimmed.
112 * @return Whether a refresh is necessary.
114 public onTrim(amount: number): boolean {
115 // Adjust the selection position based on the trimmed amount.
116 if (this.selectionStart) {
117 this.selectionStart[1] -= amount;
119 if (this.selectionEnd) {
120 this.selectionEnd[1] -= amount;
123 // The selection has moved off the buffer, clear it.
124 if (this.selectionEnd && this.selectionEnd[1] < 0) {
125 this.clearSelection();
129 // If the selection start is trimmed, ensure the start column is 0.
130 if (this.selectionStart && this.selectionStart[1] < 0) {
131 this.selectionStart[1] = 0;