5 import { ITerminal, IBuffer } from './Interfaces';
6 import { CircularList } from './utils/CircularList';
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
15 export class Buffer implements IBuffer {
16 private _lines: CircularList<[number, string, number][]>;
22 public scrollBottom: number;
23 public scrollTop: number;
25 public savedY: number;
26 public savedX: number;
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
37 private _terminal: ITerminal
42 public get lines(): CircularList<[number, string, number][]> {
47 * Fills the buffer's viewport with blank lines.
49 public fillViewportRows(): void {
50 if (this._lines.length === 0) {
51 let i = this._terminal.rows;
53 this.lines.push(this._terminal.blankLine());
59 * Clears the buffer to it's initial state, discarding all previous data.
61 public clear(): void {
66 this.scrollBottom = 0;
69 this._lines = new CircularList<[number, string, number][]>(this._terminal.scrollback);
70 this.scrollBottom = this._terminal.rows - 1;
74 * Resizes the buffer, adjusting its data accordingly.
75 * @param newCols The new number of columns.
76 * @param newRows The new number of rows.
78 public resize(newCols: number, newRows: number): void {
79 // Don't resize the buffer if it's empty and hasn't been used yet.
80 if (this._lines.length === 0) {
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?
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());
91 while (this._lines.get(i).length < newCols) {
92 this._lines.get(i).push(ch);
97 // Resize rows in both directions as needed
99 if (this._terminal.rows < newRows) {
100 for (let y = this._terminal.rows; y < newRows; y++) {
101 if (this._lines.length < newRows + this.ybase) {
102 if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) {
103 // There is room above the buffer and there are no empty elements below the line,
107 if (this.ydisp > 0) {
108 // Viewport is at the top of the buffer, must increase downwards
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
114 this._lines.push(this._terminal.blankLine());
118 } else { // (this._terminal.rows >= newRows)
119 for (let y = this._terminal.rows; y > newRows; y--) {
120 if (this._lines.length > newRows + this.ybase) {
121 if (this._lines.length > this.ybase + this.y + 1) {
122 // The line is a blank line below the cursor, remove it
125 // The line is the cursor, scroll down
133 // Make sure that the cursor stays on screen
134 if (this.y >= newRows) {
135 this.y = newRows - 1;
141 if (this.x >= newCols) {
142 this.x = newCols - 1;
146 this.scrollBottom = newRows - 1;