Skip to content

Commit c2bb507

Browse files
committed
Move BufferLines.ts into Buffer.ts
Part of xtermjs#791
1 parent 091854d commit c2bb507

File tree

8 files changed

+91
-92
lines changed

8 files changed

+91
-92
lines changed

src/Buffer.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import { ITerminal, IBuffer } from './Interfaces';
66
import { CircularList } from './utils/CircularList';
77
import { LineData, CharData } from './Types';
88

9+
const CHAR_DATA_CHAR_INDEX = 1;
10+
const CHAR_DATA_WIDTH_INDEX = 2;
11+
912
/**
1013
* This class represents a terminal buffer (an internal state of the terminal), where the
1114
* following information is stored (in high-level):
@@ -146,4 +149,52 @@ export class Buffer implements IBuffer {
146149
this.scrollTop = 0;
147150
this.scrollBottom = newRows - 1;
148151
}
152+
153+
/**
154+
* Translates a buffer line to a string, with optional start and end columns.
155+
* Wide characters will count as two columns in the resulting string. This
156+
* function is useful for getting the actual text underneath the raw selection
157+
* position.
158+
* @param line The line being translated.
159+
* @param trimRight Whether to trim whitespace to the right.
160+
* @param startCol The column to start at.
161+
* @param endCol The column to end at.
162+
*/
163+
public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
164+
// Get full line
165+
let lineString = '';
166+
let widthAdjustedStartCol = startCol;
167+
let widthAdjustedEndCol = endCol;
168+
const line = this.lines.get(lineIndex);
169+
for (let i = 0; i < line.length; i++) {
170+
const char = line[i];
171+
lineString += char[CHAR_DATA_CHAR_INDEX];
172+
// Adjust start and end cols for wide characters if they affect their
173+
// column indexes
174+
if (char[CHAR_DATA_WIDTH_INDEX] === 0) {
175+
if (startCol >= i) {
176+
widthAdjustedStartCol--;
177+
}
178+
if (endCol >= i) {
179+
widthAdjustedEndCol--;
180+
}
181+
}
182+
}
183+
184+
// Calculate the final end col by trimming whitespace on the right of the
185+
// line if needed.
186+
let finalEndCol = widthAdjustedEndCol || line.length;
187+
if (trimRight) {
188+
const rightWhitespaceIndex = lineString.search(/\s+$/);
189+
if (rightWhitespaceIndex !== -1) {
190+
finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex);
191+
}
192+
// Return the empty string if only trimmed whitespace is selected
193+
if (finalEndCol <= widthAdjustedStartCol) {
194+
return '';
195+
}
196+
}
197+
198+
return lineString.substring(widthAdjustedStartCol, finalEndCol);
199+
}
149200
}

src/InputHandler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,7 @@ export class InputHandler implements IInputHandler {
945945
case 47: // alt screen buffer
946946
case 1047: // alt screen buffer
947947
this._terminal.buffers.activateAltBuffer();
948+
this._terminal.selectionManager.setBuffer(this._terminal.buffer);
948949
this._terminal.viewport.syncScrollArea();
949950
this._terminal.showCursor();
950951
break;
@@ -1113,7 +1114,7 @@ export class InputHandler implements IInputHandler {
11131114
// if (params[0] === 1049) {
11141115
// this.restoreCursor(params);
11151116
// }
1116-
this._terminal.selectionManager.setBuffer(this._terminal.buffer.lines);
1117+
this._terminal.selectionManager.setBuffer(this._terminal.buffer);
11171118
this._terminal.refresh(0, this._terminal.rows - 1);
11181119
this._terminal.viewport.syncScrollArea();
11191120
this._terminal.showCursor();

src/Interfaces.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ export interface IBuffer {
144144
scrollTop: number;
145145
savedY: number;
146146
savedX: number;
147+
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
147148
}
148149

149150
export interface IBufferSet {
@@ -166,7 +167,7 @@ export interface ISelectionManager {
166167

167168
disable(): void;
168169
enable(): void;
169-
setBuffer(buffer: ICircularList<LineData>): void;
170+
setBuffer(buffer: IBuffer): void;
170171
setSelection(row: number, col: number, length: number): void;
171172
}
172173

src/SelectionManager.test.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import jsdom = require('jsdom');
66
import { assert } from 'chai';
7-
import { ITerminal, ICircularList } from './Interfaces';
7+
import { ITerminal, ICircularList, IBuffer } from './Interfaces';
88
import { CharMeasure } from './utils/CharMeasure';
99
import { CircularList } from './utils/CircularList';
1010
import { SelectionManager } from './SelectionManager';
@@ -16,7 +16,7 @@ import { LineData } from './Types';
1616
class TestSelectionManager extends SelectionManager {
1717
constructor(
1818
terminal: ITerminal,
19-
buffer: ICircularList<LineData>,
19+
buffer: IBuffer,
2020
rowContainer: HTMLElement,
2121
charMeasure: CharMeasure
2222
) {
@@ -40,7 +40,7 @@ describe('SelectionManager', () => {
4040
let document: Document;
4141

4242
let terminal: ITerminal;
43-
let bufferLines: ICircularList<LineData>;
43+
let buffer: IBuffer;
4444
let rowContainer: HTMLElement;
4545
let selectionManager: TestSelectionManager;
4646

@@ -55,8 +55,8 @@ describe('SelectionManager', () => {
5555
terminal.options.scrollback = 100;
5656
terminal.buffers = new BufferSet(terminal);
5757
terminal.buffer = terminal.buffers.active;
58-
bufferLines = terminal.buffer.lines;
59-
selectionManager = new TestSelectionManager(terminal, bufferLines, rowContainer, null);
58+
buffer = terminal.buffer;
59+
selectionManager = new TestSelectionManager(terminal, buffer, rowContainer, null);
6060
});
6161

6262
function stringToRow(text: string): LineData {
@@ -69,7 +69,7 @@ describe('SelectionManager', () => {
6969

7070
describe('_selectWordAt', () => {
7171
it('should expand selection for normal width chars', () => {
72-
bufferLines.set(0, stringToRow('foo bar'));
72+
buffer.lines.set(0, stringToRow('foo bar'));
7373
selectionManager.selectWordAt([0, 0]);
7474
assert.equal(selectionManager.selectionText, 'foo');
7575
selectionManager.selectWordAt([1, 0]);
@@ -86,7 +86,7 @@ describe('SelectionManager', () => {
8686
assert.equal(selectionManager.selectionText, 'bar');
8787
});
8888
it('should expand selection for whitespace', () => {
89-
bufferLines.set(0, stringToRow('a b'));
89+
buffer.lines.set(0, stringToRow('a b'));
9090
selectionManager.selectWordAt([0, 0]);
9191
assert.equal(selectionManager.selectionText, 'a');
9292
selectionManager.selectWordAt([1, 0]);
@@ -100,7 +100,7 @@ describe('SelectionManager', () => {
100100
});
101101
it('should expand selection for wide characters', () => {
102102
// Wide characters use a special format
103-
bufferLines.set(0, [
103+
buffer.lines.set(0, [
104104
[null, '中', 2],
105105
[null, '', 0],
106106
[null, '文', 2],
@@ -152,7 +152,7 @@ describe('SelectionManager', () => {
152152
assert.equal(selectionManager.selectionText, 'foo');
153153
});
154154
it('should select up to non-path characters that are commonly adjacent to paths', () => {
155-
bufferLines.set(0, stringToRow('(cd)[ef]{gh}\'ij"'));
155+
buffer.lines.set(0, stringToRow('(cd)[ef]{gh}\'ij"'));
156156
selectionManager.selectWordAt([0, 0]);
157157
assert.equal(selectionManager.selectionText, '(cd');
158158
selectionManager.selectWordAt([1, 0]);
@@ -190,7 +190,7 @@ describe('SelectionManager', () => {
190190

191191
describe('_selectLineAt', () => {
192192
it('should select the entire line', () => {
193-
bufferLines.set(0, stringToRow('foo bar'));
193+
buffer.lines.set(0, stringToRow('foo bar'));
194194
selectionManager.selectLineAt(0);
195195
assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct');
196196
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
@@ -200,14 +200,14 @@ describe('SelectionManager', () => {
200200

201201
describe('selectAll', () => {
202202
it('should select the entire buffer, beyond the viewport', () => {
203-
bufferLines.length = 5;
204-
bufferLines.set(0, stringToRow('1'));
205-
bufferLines.set(1, stringToRow('2'));
206-
bufferLines.set(2, stringToRow('3'));
207-
bufferLines.set(3, stringToRow('4'));
208-
bufferLines.set(4, stringToRow('5'));
203+
buffer.lines.length = 5;
204+
buffer.lines.set(0, stringToRow('1'));
205+
buffer.lines.set(1, stringToRow('2'));
206+
buffer.lines.set(2, stringToRow('3'));
207+
buffer.lines.set(3, stringToRow('4'));
208+
buffer.lines.set(4, stringToRow('5'));
209209
selectionManager.selectAll();
210-
terminal.buffer.ybase = bufferLines.length - terminal.rows;
210+
terminal.buffer.ybase = buffer.lines.length - terminal.rows;
211211
assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5');
212212
});
213213
});

src/SelectionManager.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import * as Browser from './utils/Browser';
77
import { CharMeasure } from './utils/CharMeasure';
88
import { CircularList } from './utils/CircularList';
99
import { EventEmitter } from './EventEmitter';
10-
import { ITerminal, ICircularList, ISelectionManager } from './Interfaces';
10+
import { ITerminal, ICircularList, ISelectionManager, IBuffer } from './Interfaces';
1111
import { SelectionModel } from './SelectionModel';
12-
import { translateBufferLineToString } from './utils/BufferLine';
1312
import { LineData } from './Types';
1413

1514
/**
@@ -102,7 +101,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
102101

103102
constructor(
104103
private _terminal: ITerminal,
105-
private _buffer: ICircularList<LineData>,
104+
private _buffer: IBuffer,
106105
private _rowContainer: HTMLElement,
107106
private _charMeasure: CharMeasure
108107
) {
@@ -127,7 +126,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
127126
// reverseIndex) and delete in a splice is only ever used when the same
128127
// number of elements was just added. Given this is could actually be
129128
// beneficial to leave the selection as is for these cases.
130-
this._buffer.on('trim', (amount: number) => this._onTrim(amount));
129+
this._buffer.lines.on('trim', (amount: number) => this._onTrim(amount));
131130
}
132131

133132
/**
@@ -151,7 +150,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
151150
* switched in or out.
152151
* @param buffer The active buffer.
153152
*/
154-
public setBuffer(buffer: ICircularList<LineData>): void {
153+
public setBuffer(buffer: IBuffer): void {
155154
this._buffer = buffer;
156155
this.clearSelection();
157156
}
@@ -184,12 +183,12 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
184183
// Get first row
185184
const startRowEndCol = start[1] === end[1] ? end[0] : null;
186185
let result: string[] = [];
187-
result.push(translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol));
186+
result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));
188187

189188
// Get middle rows
190189
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
191-
const bufferLine = this._buffer.get(i);
192-
const lineText = translateBufferLineToString(bufferLine, true);
190+
const bufferLine = this._buffer.lines.get(i);
191+
const lineText = this._buffer.translateBufferLineToString(i, true);
193192
if ((<any>bufferLine).isWrapped) {
194193
result[result.length - 1] += lineText;
195194
} else {
@@ -199,8 +198,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
199198

200199
// Get final row
201200
if (start[1] !== end[1]) {
202-
const bufferLine = this._buffer.get(end[1]);
203-
const lineText = translateBufferLineToString(bufferLine, true, 0, end[0]);
201+
const bufferLine = this._buffer.lines.get(end[1]);
202+
const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]);
204203
if ((<any>bufferLine).isWrapped) {
205204
result[result.length - 1] += lineText;
206205
} else {
@@ -413,7 +412,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
413412
this._model.selectionEnd = null;
414413

415414
// Ensure the line exists
416-
const line = this._buffer.get(this._model.selectionStart[1]);
415+
const line = this._buffer.lines.get(this._model.selectionStart[1]);
417416
if (!line) {
418417
return;
419418
}
@@ -493,8 +492,8 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
493492
// If the character is a wide character include the cell to the right in the
494493
// selection. Note that selections at the very end of the line will never
495494
// have a character.
496-
if (this._model.selectionEnd[1] < this._buffer.length) {
497-
const char = this._buffer.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]];
495+
if (this._model.selectionEnd[1] < this._buffer.lines.length) {
496+
const char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]];
498497
if (char && char[2] === 0) {
499498
this._model.selectionEnd[0]++;
500499
}
@@ -562,12 +561,12 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
562561
* @param coords The coordinates to get the word at.
563562
*/
564563
private _getWordAt(coords: [number, number]): IWordPosition {
565-
const bufferLine = this._buffer.get(coords[1]);
564+
const bufferLine = this._buffer.lines.get(coords[1]);
566565
if (!bufferLine) {
567566
return null;
568567
}
569568

570-
const line = translateBufferLineToString(bufferLine, false);
569+
const line = this._buffer.translateBufferLineToString(coords[1], false);
571570

572571
// Get actual index, taking into consideration wide characters
573572
let endIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);

src/Terminal.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import * as Browser from './utils/Browser';
2828
import * as Mouse from './utils/Mouse';
2929
import { CHARSETS } from './Charsets';
3030
import { getRawByteCoords } from './utils/Mouse';
31-
import { translateBufferLineToString } from './utils/BufferLine';
3231
import { CustomKeyEventHandler, Charset, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData, Option, StringOption, BooleanOption, StringArrayOption, NumberOption, GeometryOption, HandlerOption } from './Types';
3332
import { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions } from './Interfaces';
3433

@@ -379,7 +378,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
379378

380379
// Ensure the selection manager has the correct buffer
381380
if (this.selectionManager) {
382-
this.selectionManager.setBuffer(this.buffer.lines);
381+
this.selectionManager.setBuffer(this.buffer);
383382
}
384383

385384
this.setupStops();
@@ -744,7 +743,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
744743

745744
this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);
746745
this.renderer = new Renderer(this);
747-
this.selectionManager = new SelectionManager(this, this.buffer.lines, this.rowContainer, this.charMeasure);
746+
this.selectionManager = new SelectionManager(this, this.buffer, this.rowContainer, this.charMeasure);
748747
this.selectionManager.on('refresh', data => {
749748
this.renderer.refreshSelection(data.start, data.end);
750749
});

src/utils/BufferLine.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/utils/TestUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,7 @@ export class MockBuffer implements IBuffer {
180180
scrollTop: number;
181181
savedY: number;
182182
savedX: number;
183+
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
184+
throw new Error('Method not implemented.');
185+
}
183186
}

0 commit comments

Comments
 (0)