From 285911cd98d13d9a3226f9429567dac0e2b42ce5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 2 Jun 2018 17:53:35 -0700 Subject: [PATCH 1/3] Implement IBuffer.getWrappedRangeForLine Part of #1456 --- src/Buffer.test.ts | 47 +++++++++++++++++++++++++++++++++++++ src/Buffer.ts | 14 +++++++++++ src/Types.ts | 1 + src/utils/TestUtils.test.ts | 3 +++ 4 files changed, 65 insertions(+) diff --git a/src/Buffer.test.ts b/src/Buffer.test.ts index 44687f0e..09d22c5d 100644 --- a/src/Buffer.test.ts +++ b/src/Buffer.test.ts @@ -48,6 +48,53 @@ describe('Buffer', () => { }); }); + describe('getWrappedRangeForLine', () => { + describe('non-wrapped', () => { + it('should return a single row for the first row', () => { + buffer.fillViewportRows(); + assert.deepEqual(buffer.getWrappedRangeForLine(0), { first: 0, last: 0 }); + }); + it('should return a single row for a middle row', () => { + buffer.fillViewportRows(); + assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 12, last: 12 }); + }); + it('should return a single row for the last row', () => { + buffer.fillViewportRows(); + assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 1), { first: 23, last: 23 }); + }); + }); + describe('wrapped', () => { + it('should return a range for the first row', () => { + buffer.fillViewportRows(); + ( buffer.lines.get(1)).isWrapped = true; + assert.deepEqual(buffer.getWrappedRangeForLine(0), { first: 0, last: 1 }); + }); + it('should return a range for a middle row wrapping upwards', () => { + buffer.fillViewportRows(); + ( buffer.lines.get(12)).isWrapped = true; + assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 11, last: 12 }); + }); + it('should return a range for a middle row wrapping downwards', () => { + buffer.fillViewportRows(); + ( buffer.lines.get(13)).isWrapped = true; + assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 12, last: 13 }); + }); + it('should return a range for a middle row wrapping both ways', () => { + buffer.fillViewportRows(); + ( buffer.lines.get(11)).isWrapped = true; + ( buffer.lines.get(12)).isWrapped = true; + ( buffer.lines.get(13)).isWrapped = true; + ( buffer.lines.get(14)).isWrapped = true; + assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 10, last: 14 }); + }); + it('should return a range for the last row', () => { + buffer.fillViewportRows(); + ( buffer.lines.get(23)).isWrapped = true; + assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 1), { first: 22, last: 23 }); + }); + }); + }); + describe('resize', () => { describe('column size is reduced', () => { it('should not trim the data in the buffer', () => { diff --git a/src/Buffer.ts b/src/Buffer.ts index 1ea303b2..235411c3 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -259,6 +259,20 @@ export class Buffer implements IBuffer { return lineString.substring(startIndex, endIndex); } + public getWrappedRangeForLine(y: number): { first: number, last: number } { + let first = y; + let last = y; + // Scan upwards for wrapped lines + while (first > 0 && (this.lines.get(first)).isWrapped) { + first--; + } + // Scan downwards for wrapped lines + while (last + 1 < this.lines.length - 1 && (this.lines.get(last + 1)).isWrapped) { + last++; + } + return { first, last }; + } + /** * Setup the tab stops. * @param i The index to start setting up tab stops from. diff --git a/src/Types.ts b/src/Types.ts index 5075daa9..45f1039b 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -284,6 +284,7 @@ export interface IBuffer { savedX: number; isCursorInViewport: boolean; translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; + getWrappedRangeForLine(y: number): { first: number, last: number }; nextStop(x?: number): number; prevStop(x?: number): number; } diff --git a/src/utils/TestUtils.test.ts b/src/utils/TestUtils.test.ts index e3ae89d3..6cb8d42e 100644 --- a/src/utils/TestUtils.test.ts +++ b/src/utils/TestUtils.test.ts @@ -302,6 +302,9 @@ export class MockBuffer implements IBuffer { translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string { return Buffer.prototype.translateBufferLineToString.apply(this, arguments); } + getWrappedRangeForLine(y: number): { first: number; last: number; } { + throw new Error('Method not implemented.'); + } nextStop(x?: number): number { throw new Error('Method not implemented.'); } From cd3418517e091f9a7dc06335dabe760a1f17f508 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 2 Jun 2018 17:56:44 -0700 Subject: [PATCH 2/3] Select the whole wrapped line when triple clicking Fixes #1456 --- src/SelectionManager.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index c150554f..1b056e83 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -803,7 +803,9 @@ export class SelectionManager extends EventEmitter implements ISelectionManager * @param line The line index. */ protected _selectLineAt(line: number): void { - this._model.selectionStart = [0, line]; - this._model.selectionStartLength = this._terminal.cols; + const wrappedRange = this._buffer.getWrappedRangeForLine(line); + this._model.selectionStart = [0, wrappedRange.first]; + this._model.selectionEnd = [this._terminal.cols, wrappedRange.last]; + this._model.selectionStartLength = 0; } } From 956b7734218429b9883835b74465e2eebe00ea4e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 2 Jun 2018 18:07:49 -0700 Subject: [PATCH 3/3] Add more tests, fix boundary bug --- src/Buffer.test.ts | 12 +++++++++++- src/Buffer.ts | 2 +- src/SelectionManager.test.ts | 10 ++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Buffer.test.ts b/src/Buffer.test.ts index 09d22c5d..82aa0224 100644 --- a/src/Buffer.test.ts +++ b/src/Buffer.test.ts @@ -89,9 +89,19 @@ describe('Buffer', () => { }); it('should return a range for the last row', () => { buffer.fillViewportRows(); - ( buffer.lines.get(23)).isWrapped = true; + ( buffer.lines.get(23)).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 1), { first: 22, last: 23 }); }); + it('should return a range for a row that wraps upward to first row', () => { + buffer.fillViewportRows(); + ( buffer.lines.get(1)).isWrapped = true; + assert.deepEqual(buffer.getWrappedRangeForLine(1), { first: 0, last: 1 }); + }); + it('should return a range for a row that wraps downward to last row', () => { + buffer.fillViewportRows(); + ( buffer.lines.get(buffer.lines.length - 1)).isWrapped = true; + assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 2), { first: 22, last: 23 }); + }); }); }); diff --git a/src/Buffer.ts b/src/Buffer.ts index 235411c3..379214ae 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -267,7 +267,7 @@ export class Buffer implements IBuffer { first--; } // Scan downwards for wrapped lines - while (last + 1 < this.lines.length - 1 && (this.lines.get(last + 1)).isWrapped) { + while (last + 1 < this.lines.length && (this.lines.get(last + 1)).isWrapped) { last++; } return { first, last }; diff --git a/src/SelectionManager.test.ts b/src/SelectionManager.test.ts index 06cea766..8e89ea3f 100644 --- a/src/SelectionManager.test.ts +++ b/src/SelectionManager.test.ts @@ -298,6 +298,16 @@ describe('SelectionManager', () => { assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]); assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 0], 'The actual selection spans the entire column'); }); + it('should select the entire wrapped line', () => { + buffer.lines.set(0, stringToRow('foo')); + const line2 = stringToRow('bar'); + (line2).isWrapped = true; + buffer.lines.set(1, line2); + selectionManager.selectLineAt(0); + assert.equal(selectionManager.selectionText, 'foobar', 'The selected text is correct'); + assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]); + assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1], 'The actual selection spans the entire column'); + }); }); describe('selectAll', () => {