]> git.proxmox.com Git - mirror_xterm.js.git/blob - src/Buffer.ts
Clean up buffer clean up/fill logic
[mirror_xterm.js.git] / src / Buffer.ts
1 /**
2 * @license MIT
3 */
4
5 import { ITerminal, IBuffer } from './Interfaces';
6 import { CircularList } from './utils/CircularList';
7
8 /**
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
14 */
15 export class Buffer implements IBuffer {
16 private _lines: CircularList<[number, string, number][]>;
17
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;
25 public savedY: number;
26 public savedX: number;
27
28 /**
29 * Create a new Buffer.
30 * @param {Terminal} _terminal - The terminal the Buffer will belong to
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)
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(
37 private _terminal: ITerminal
38 ) {
39 this.clear();
40 }
41
42 public get lines(): CircularList<[number, string, number][]> {
43 return this._lines;
44 }
45
46 public fillViewportRows(): void {
47 if (this._lines.length === 0) {
48 let i = this._terminal.rows;
49 while (i--) {
50 this.lines.push(this._terminal.blankLine());
51 }
52 }
53 }
54
55 public clear(): void {
56 this.ydisp = 0;
57 this.ybase = 0;
58 this.y = 0;
59 this.x = 0;
60 this.scrollBottom = 0;
61 this.scrollTop = 0;
62 this.tabs = {};
63 this._lines = new CircularList<[number, string, number][]>(this._terminal.scrollback);
64 this.scrollBottom = this._terminal.rows - 1;
65 }
66
67 public resize(newCols: number, newRows: number): void {
68 // Don't resize the buffer if it's empty and hasn't been used yet.
69 if (this._lines.length === 0) {
70 return;
71 }
72
73 // Deal with columns increasing (we don't do anything when columns reduce)
74 if (this._terminal.cols < newCols) {
75 const ch: [number, string, number] = [this._terminal.defAttr, ' ', 1]; // does xterm use the default attr?
76 for (let i = 0; i < this._lines.length; i++) {
77 if (this._lines.get(i) === undefined) {
78 this._lines.set(i, this._terminal.blankLine());
79 }
80 while (this._lines.get(i).length < newCols) {
81 this._lines.get(i).push(ch);
82 }
83 }
84 }
85
86 // Resize rows in both directions as needed
87 let addToY = 0;
88 if (this._terminal.rows < newRows) {
89 for (let y = this._terminal.rows; y < newRows; y++) {
90 if (this._lines.length < newRows + this.ybase) {
91 if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) {
92 // There is room above the buffer and there are no empty elements below the line,
93 // scroll up
94 this.ybase--;
95 addToY++;
96 if (this.ydisp > 0) {
97 // Viewport is at the top of the buffer, must increase downwards
98 this.ydisp--;
99 }
100 } else {
101 // Add a blank line if there is no buffer left at the top to scroll to, or if there
102 // are blank lines after the cursor
103 this._lines.push(this._terminal.blankLine());
104 }
105 }
106 }
107 } else { // (this._terminal.rows >= newRows)
108 for (let y = this._terminal.rows; y > newRows; y--) {
109 if (this._lines.length > newRows + this.ybase) {
110 if (this._lines.length > this.ybase + this.y + 1) {
111 // The line is a blank line below the cursor, remove it
112 this._lines.pop();
113 } else {
114 // The line is the cursor, scroll down
115 this.ybase++;
116 this.ydisp++;
117 }
118 }
119 }
120 }
121
122 // Make sure that the cursor stays on screen
123 if (this.y >= newRows) {
124 this.y = newRows - 1;
125 }
126 if (addToY) {
127 this.y += addToY;
128 }
129
130 if (this.x >= newCols) {
131 this.x = newCols - 1;
132 }
133
134 this.scrollTop = 0;
135 this.scrollBottom = newRows - 1;
136 }
137 }