]>
Commit | Line | Data |
---|---|---|
f7d6ab5f DI |
1 | /** |
2 | * @license MIT | |
3 | */ | |
4 | ||
5 | import { ITerminal } from './Interfaces'; | |
6 | ||
7 | export class SelectionModel { | |
8 | /** | |
9 | * Whether select all is currently active. | |
10 | */ | |
11 | public isSelectAllActive: boolean; | |
12 | ||
13 | /** | |
14 | * The [x, y] position the selection starts at. | |
15 | */ | |
16 | public selectionStart: [number, number]; | |
17 | ||
18 | /** | |
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. | |
22 | */ | |
23 | public selectionStartLength: number; | |
24 | ||
25 | /** | |
26 | * The [x, y] position the selection ends at. | |
27 | */ | |
28 | public selectionEnd: [number, number]; | |
29 | ||
30 | constructor( | |
31 | private _terminal: ITerminal | |
32 | ) { | |
33 | } | |
34 | ||
35 | /** | |
36 | * The final selection start, taking into consideration select all. | |
37 | */ | |
38 | public get finalSelectionStart(): [number, number] { | |
39 | if (this.isSelectAllActive) { | |
40 | return [0, 0]; | |
41 | } | |
42 | ||
43 | if (!this.selectionEnd) { | |
44 | return this.selectionStart; | |
45 | } | |
46 | ||
47 | return this._areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart; | |
48 | } | |
49 | ||
50 | /** | |
51 | * The final selection end, taking into consideration select all, double click | |
52 | * word selection and triple click line selection. | |
53 | */ | |
54 | public get finalSelectionEnd(): [number, number] { | |
55 | if (this.isSelectAllActive) { | |
56 | return [this._terminal.cols - 1, this._terminal.ydisp + this._terminal.rows - 1]; | |
57 | } | |
58 | ||
59 | // Use the selection start if the end doesn't exist or they're reversed | |
60 | if (!this.selectionEnd || this._areSelectionValuesReversed()) { | |
61 | return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]]; | |
62 | } | |
63 | ||
64 | // Ensure the the word/line is selected after a double/triple click | |
65 | if (this.selectionStartLength) { | |
66 | // Select the larger of the two when start and end are on the same line | |
67 | if (this.selectionEnd[1] === this.selectionStart[1]) { | |
68 | return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]]; | |
69 | } | |
70 | } | |
71 | return this.selectionEnd; | |
72 | } | |
73 | ||
74 | /** | |
75 | * Returns whether the selection start and end are reversed. | |
76 | */ | |
77 | private _areSelectionValuesReversed(): boolean { | |
78 | const start = this.selectionStart; | |
79 | const end = this.selectionEnd; | |
80 | return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]); | |
81 | } | |
82 | ||
83 | /** | |
84 | * Handle the buffer being trimmed, adjust the selection position. | |
85 | * @param amount The amount the buffer is being trimmed. | |
86 | * @return Whether a refresh is necessary. | |
87 | */ | |
88 | public onTrim(amount: number): boolean { | |
89 | // Adjust the selection position based on the trimmed amount. | |
90 | if (this.selectionStart) { | |
91 | this.selectionStart[0] -= amount; | |
92 | } | |
93 | if (this.selectionEnd) { | |
94 | this.selectionEnd[0] -= amount; | |
95 | } | |
96 | ||
97 | // The selection has moved off the buffer, clear it. | |
98 | if (this.selectionEnd && this.selectionEnd[0] < 0) { | |
99 | this.selectionStart = null; | |
100 | this.selectionEnd = null; | |
101 | return true; | |
102 | } | |
103 | ||
104 | // If the selection start is trimmed, ensure the start column is 0. | |
105 | if (this.selectionStart && this.selectionStart[0] < 0) { | |
106 | this.selectionStart[1] = 0; | |
107 | } | |
108 | return false; | |
109 | } | |
110 | } |