]>
Commit | Line | Data |
---|---|---|
f7d6ab5f DI |
1 | /** |
2 | * @license MIT | |
3 | */ | |
4 | ||
5 | import { ITerminal } from './Interfaces'; | |
6 | ||
6075498c DI |
7 | /** |
8 | * Represents a selection within the buffer. This model only cares about column | |
9 | * and row coordinates, not wide characters. | |
10 | */ | |
f7d6ab5f DI |
11 | export class SelectionModel { |
12 | /** | |
13 | * Whether select all is currently active. | |
14 | */ | |
15 | public isSelectAllActive: boolean; | |
16 | ||
17 | /** | |
18 | * The [x, y] position the selection starts at. | |
19 | */ | |
20 | public selectionStart: [number, number]; | |
21 | ||
22 | /** | |
23 | * The minimal length of the selection from the start position. When double | |
24 | * clicking on a word, the word will be selected which makes the selection | |
25 | * start at the start of the word and makes this variable the length. | |
26 | */ | |
27 | public selectionStartLength: number; | |
28 | ||
29 | /** | |
30 | * The [x, y] position the selection ends at. | |
31 | */ | |
32 | public selectionEnd: [number, number]; | |
33 | ||
34 | constructor( | |
35 | private _terminal: ITerminal | |
36 | ) { | |
a047359f | 37 | this.clearSelection(); |
f7d6ab5f DI |
38 | } |
39 | ||
4405f5e1 DI |
40 | /** |
41 | * Clears the current selection. | |
42 | */ | |
43 | public clearSelection(): void { | |
44 | this.selectionStart = null; | |
45 | this.selectionEnd = null; | |
46 | this.isSelectAllActive = false; | |
a047359f | 47 | this.selectionStartLength = 0; |
4405f5e1 DI |
48 | } |
49 | ||
f7d6ab5f DI |
50 | /** |
51 | * The final selection start, taking into consideration select all. | |
52 | */ | |
53 | public get finalSelectionStart(): [number, number] { | |
54 | if (this.isSelectAllActive) { | |
55 | return [0, 0]; | |
56 | } | |
57 | ||
a047359f | 58 | if (!this.selectionEnd || !this.selectionStart) { |
f7d6ab5f DI |
59 | return this.selectionStart; |
60 | } | |
61 | ||
4b170ca4 | 62 | return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart; |
f7d6ab5f DI |
63 | } |
64 | ||
65 | /** | |
66 | * The final selection end, taking into consideration select all, double click | |
67 | * word selection and triple click line selection. | |
68 | */ | |
69 | public get finalSelectionEnd(): [number, number] { | |
2b243182 | 70 | if (this.isSelectAllActive) { |
28ed7772 | 71 | return [this._terminal.cols, this._terminal.ybase + this._terminal.rows - 1]; |
24bed01f DI |
72 | } |
73 | ||
2b243182 DI |
74 | if (!this.selectionStart) { |
75 | return null; | |
f7d6ab5f DI |
76 | } |
77 | ||
78 | // Use the selection start if the end doesn't exist or they're reversed | |
4b170ca4 | 79 | if (!this.selectionEnd || this.areSelectionValuesReversed()) { |
f7d6ab5f DI |
80 | return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]]; |
81 | } | |
82 | ||
83 | // Ensure the the word/line is selected after a double/triple click | |
84 | if (this.selectionStartLength) { | |
85 | // Select the larger of the two when start and end are on the same line | |
86 | if (this.selectionEnd[1] === this.selectionStart[1]) { | |
87 | return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]]; | |
88 | } | |
89 | } | |
90 | return this.selectionEnd; | |
91 | } | |
92 | ||
93 | /** | |
94 | * Returns whether the selection start and end are reversed. | |
95 | */ | |
4b170ca4 | 96 | public areSelectionValuesReversed(): boolean { |
f7d6ab5f DI |
97 | const start = this.selectionStart; |
98 | const end = this.selectionEnd; | |
99 | return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]); | |
100 | } | |
101 | ||
102 | /** | |
103 | * Handle the buffer being trimmed, adjust the selection position. | |
104 | * @param amount The amount the buffer is being trimmed. | |
105 | * @return Whether a refresh is necessary. | |
106 | */ | |
107 | public onTrim(amount: number): boolean { | |
108 | // Adjust the selection position based on the trimmed amount. | |
109 | if (this.selectionStart) { | |
b81c165b | 110 | this.selectionStart[1] -= amount; |
f7d6ab5f DI |
111 | } |
112 | if (this.selectionEnd) { | |
b81c165b | 113 | this.selectionEnd[1] -= amount; |
f7d6ab5f DI |
114 | } |
115 | ||
116 | // The selection has moved off the buffer, clear it. | |
b81c165b DI |
117 | if (this.selectionEnd && this.selectionEnd[1] < 0) { |
118 | this.clearSelection(); | |
f7d6ab5f DI |
119 | return true; |
120 | } | |
121 | ||
122 | // If the selection start is trimmed, ensure the start column is 0. | |
b81c165b | 123 | if (this.selectionStart && this.selectionStart[1] < 0) { |
f7d6ab5f DI |
124 | this.selectionStart[1] = 0; |
125 | } | |
126 | return false; | |
127 | } | |
128 | } |