xterm
[VSoRC/.git] / node_modules / xterm / src / browser / selection / SelectionModel.ts
1 /**
2  * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3  * @license MIT
4  */
5
6 import { IBufferService } from 'common/services/Services';
7
8 /**
9  * Represents a selection within the buffer. This model only cares about column
10  * and row coordinates, not wide characters.
11  */
12 export class SelectionModel {
13   /**
14    * Whether select all is currently active.
15    */
16   public isSelectAllActive: boolean = false;
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 = 0;
24
25   /**
26    * The [x, y] position the selection starts at.
27    */
28   public selectionStart: [number, number] | undefined;
29
30   /**
31    * The [x, y] position the selection ends at.
32    */
33   public selectionEnd: [number, number] | undefined;
34
35   constructor(
36     private _bufferService: IBufferService
37   ) {
38   }
39
40   /**
41    * Clears the current selection.
42    */
43   public clearSelection(): void {
44     this.selectionStart = undefined;
45     this.selectionEnd = undefined;
46     this.isSelectAllActive = false;
47     this.selectionStartLength = 0;
48   }
49
50   /**
51    * The final selection start, taking into consideration select all.
52    */
53   public get finalSelectionStart(): [number, number] | undefined {
54     if (this.isSelectAllActive) {
55       return [0, 0];
56     }
57
58     if (!this.selectionEnd || !this.selectionStart) {
59       return this.selectionStart;
60     }
61
62     return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart;
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] | undefined {
70     if (this.isSelectAllActive) {
71       return [this._bufferService.cols, this._bufferService.buffer.ybase + this._bufferService.rows - 1];
72     }
73
74     if (!this.selectionStart) {
75       return undefined;
76     }
77
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)];
83       }
84       return [startPlusLength, this.selectionStart[1]];
85     }
86
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]];
92       }
93     }
94     return this.selectionEnd;
95   }
96
97   /**
98    * Returns whether the selection start and end are reversed.
99    */
100   public areSelectionValuesReversed(): boolean {
101     const start = this.selectionStart;
102     const end = this.selectionEnd;
103     if (!start || !end) {
104       return false;
105     }
106     return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]);
107   }
108
109   /**
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.
113    */
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;
118     }
119     if (this.selectionEnd) {
120       this.selectionEnd[1] -= amount;
121     }
122
123     // The selection has moved off the buffer, clear it.
124     if (this.selectionEnd && this.selectionEnd[1] < 0) {
125       this.clearSelection();
126       return true;
127     }
128
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;
132     }
133     return false;
134   }
135 }