]>
Commit | Line | Data |
---|---|---|
95cb6f30 PK |
1 | /** |
2 | * @license MIT | |
3 | */ | |
4 | ||
f03d00a4 | 5 | import { ITerminal, IBuffer } from './Interfaces'; |
95cb6f30 PK |
6 | import { CircularList } from './utils/CircularList'; |
7 | ||
bbafdd3d | 8 | /** |
58b9f712 PK |
9 | * This class represents a terminal buffer (an internal state of the terminal), where the |
10 | * following information is stored (in high-level): | |
11 | * - text content of this particular buffer | |
12 | * - cursor position | |
13 | * - scroll position | |
bbafdd3d | 14 | */ |
f03d00a4 DI |
15 | export class Buffer implements IBuffer { |
16 | private _lines: CircularList<[number, string, number][]>; | |
95cb6f30 | 17 | |
f03d00a4 DI |
18 | public ydisp: number; |
19 | public ybase: number; | |
20 | public y: number; | |
21 | public x: number; | |
22 | public scrollBottom: number; | |
23 | public scrollTop: number; | |
24 | public tabs: any; | |
604959a3 DI |
25 | public savedY: number; |
26 | public savedX: number; | |
27 | ||
bbafdd3d PK |
28 | /** |
29 | * Create a new Buffer. | |
3d20c2f2 | 30 | * @param {Terminal} _terminal - The terminal the Buffer will belong to |
58b9f712 PK |
31 | * @param {number} ydisp - The scroll position of the Buffer in the viewport |
32 | * @param {number} ybase - The scroll position of the y cursor (ybase + y = the y position within the Buffer) | |
bbafdd3d PK |
33 | * @param {number} y - The cursor's y position after ybase |
34 | * @param {number} x - The cursor's x position after ybase | |
35 | */ | |
36 | constructor( | |
f03d00a4 | 37 | private _terminal: ITerminal |
bbafdd3d | 38 | ) { |
f03d00a4 DI |
39 | this.clear(); |
40 | } | |
41 | ||
42 | public get lines(): CircularList<[number, string, number][]> { | |
43 | return this._lines; | |
44 | } | |
45 | ||
0731f286 DI |
46 | /** |
47 | * Fills the buffer's viewport with blank lines. | |
48 | */ | |
f03d00a4 DI |
49 | public fillViewportRows(): void { |
50 | if (this._lines.length === 0) { | |
51 | let i = this._terminal.rows; | |
52 | while (i--) { | |
53 | this.lines.push(this._terminal.blankLine()); | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
0731f286 DI |
58 | /** |
59 | * Clears the buffer to it's initial state, discarding all previous data. | |
60 | */ | |
f03d00a4 DI |
61 | public clear(): void { |
62 | this.ydisp = 0; | |
63 | this.ybase = 0; | |
64 | this.y = 0; | |
65 | this.x = 0; | |
66 | this.scrollBottom = 0; | |
67 | this.scrollTop = 0; | |
68 | this.tabs = {}; | |
69 | this._lines = new CircularList<[number, string, number][]>(this._terminal.scrollback); | |
3d20c2f2 DI |
70 | this.scrollBottom = this._terminal.rows - 1; |
71 | } | |
72 | ||
0731f286 DI |
73 | /** |
74 | * Resizes the buffer, adjusting its data accordingly. | |
75 | * @param newCols The new number of columns. | |
76 | * @param newRows The new number of rows. | |
77 | */ | |
3d20c2f2 DI |
78 | public resize(newCols: number, newRows: number): void { |
79 | // Don't resize the buffer if it's empty and hasn't been used yet. | |
f03d00a4 | 80 | if (this._lines.length === 0) { |
3d20c2f2 DI |
81 | return; |
82 | } | |
83 | ||
84 | // Deal with columns increasing (we don't do anything when columns reduce) | |
85 | if (this._terminal.cols < newCols) { | |
86 | const ch: [number, string, number] = [this._terminal.defAttr, ' ', 1]; // does xterm use the default attr? | |
f03d00a4 DI |
87 | for (let i = 0; i < this._lines.length; i++) { |
88 | if (this._lines.get(i) === undefined) { | |
89 | this._lines.set(i, this._terminal.blankLine()); | |
3d20c2f2 | 90 | } |
f03d00a4 DI |
91 | while (this._lines.get(i).length < newCols) { |
92 | this._lines.get(i).push(ch); | |
3d20c2f2 DI |
93 | } |
94 | } | |
95 | } | |
96 | ||
97 | // Resize rows in both directions as needed | |
98 | let addToY = 0; | |
99 | if (this._terminal.rows < newRows) { | |
100 | for (let y = this._terminal.rows; y < newRows; y++) { | |
f03d00a4 DI |
101 | if (this._lines.length < newRows + this.ybase) { |
102 | if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) { | |
3d20c2f2 DI |
103 | // There is room above the buffer and there are no empty elements below the line, |
104 | // scroll up | |
105 | this.ybase--; | |
106 | addToY++; | |
107 | if (this.ydisp > 0) { | |
108 | // Viewport is at the top of the buffer, must increase downwards | |
109 | this.ydisp--; | |
110 | } | |
111 | } else { | |
112 | // Add a blank line if there is no buffer left at the top to scroll to, or if there | |
113 | // are blank lines after the cursor | |
f03d00a4 | 114 | this._lines.push(this._terminal.blankLine()); |
3d20c2f2 DI |
115 | } |
116 | } | |
117 | } | |
118 | } else { // (this._terminal.rows >= newRows) | |
119 | for (let y = this._terminal.rows; y > newRows; y--) { | |
f03d00a4 DI |
120 | if (this._lines.length > newRows + this.ybase) { |
121 | if (this._lines.length > this.ybase + this.y + 1) { | |
3d20c2f2 | 122 | // The line is a blank line below the cursor, remove it |
f03d00a4 | 123 | this._lines.pop(); |
3d20c2f2 DI |
124 | } else { |
125 | // The line is the cursor, scroll down | |
126 | this.ybase++; | |
127 | this.ydisp++; | |
128 | } | |
129 | } | |
130 | } | |
131 | } | |
132 | ||
133 | // Make sure that the cursor stays on screen | |
134 | if (this.y >= newRows) { | |
135 | this.y = newRows - 1; | |
136 | } | |
137 | if (addToY) { | |
138 | this.y += addToY; | |
139 | } | |
140 | ||
141 | if (this.x >= newCols) { | |
142 | this.x = newCols - 1; | |
143 | } | |
144 | ||
145 | this.scrollTop = 0; | |
146 | this.scrollBottom = newRows - 1; | |
95cb6f30 PK |
147 | } |
148 | } |