Added support for searching accross wrapped lines

This commit is contained in:
Alex Ross
2018-09-10 10:20:31 -07:00
parent 12e38404a2
commit 09028d4d86
3 changed files with 80 additions and 41 deletions
+27
View File
@@ -265,6 +265,33 @@ export class Buffer implements IBuffer {
return lineString.substring(startIndex, endIndex);
}
/**
* Translates a buffer line to a string, with optional start and end columns.
* Wide characters will count as two columns in the resulting string. This
* function is useful for getting the actual text underneath the raw selection
* position.
* @param line The line being translated.
* @param trimRight Whether to trim whitespace to the right.
*/
public translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean): string {
// Get full line
let lineString = '';
let lineWrapsToNext = true;
if (this.lines.get(lineIndex).isWrapped) {
// This terminal line is a continuation of the previous line.
return '';
}
do {
lineString += this.translateBufferLineToString(lineIndex, true);
lineIndex++;
const nextLine = this.lines.get(lineIndex);
lineWrapsToNext = nextLine ? this.lines.get(lineIndex).isWrapped : false;
} while (lineWrapsToNext);
return lineString;
}
public getWrappedRangeForLine(y: number): { first: number, last: number } {
let first = y;
let last = y;
+1 -1
View File
@@ -107,7 +107,7 @@ export class SearchHelper implements ISearchHelper {
* @return The search result if it was found.
*/
protected _findInLine(term: string, y: number, searchOptions: ISearchOptions = {}): ISearchResult {
const lowerStringLine = this._terminal._core.buffer.translateBufferLineToString(y, true).toLowerCase();
const lowerStringLine = this._terminal._core.buffer.translateBufferLineToStringWithWrap(y, true).toLowerCase();
const lowerTerm = term.toLowerCase();
let searchIndex = -1;
if (searchOptions.regex) {
+52 -40
View File
@@ -40,45 +40,57 @@ describe('search addon', function(): void {
assert.equal(typeof (<any>MockTerminalPlain).prototype.findPrevious, 'function');
});
});
it('Searchhelper - should find correct position', function(): void {
search.apply(<any>MockTerminal);
const term = new MockTerminal({cols: 20, rows: 3});
term.core.write('Hello World\r\ntest\n123....hello');
term.pushWriteData();
const hello0 = term.searchHelper.findInLine('Hello', 0);
const hello1 = term.searchHelper.findInLine('Hello', 1);
const hello2 = term.searchHelper.findInLine('Hello', 2);
expect(hello0).eql({col: 0, row: 0, term: 'Hello'});
expect(hello1).eql(undefined);
expect(hello2).eql({col: 11, row: 2, term: 'Hello'});
});
it('should respect search regex', function(): void {
search.apply(<any>MockTerminal);
const term = new MockTerminal({cols: 10, rows: 4});
term.core.write('abcdefghijklmnopqrstuvwxyz\r\n~/dev ');
/*
abcdefghij
klmnopqrst
uvwxyz
~/dev
*/
term.pushWriteData();
const searchOptions = {
regex: true,
wholeWord: false,
caseSensitive: false
};
const hello0 = term.searchHelper.findInLine('dee*', 0, searchOptions);
term.searchHelper.findInLine('jkk*', 0, searchOptions);
term.searchHelper.findInLine('mnn*', 1, searchOptions);
const tilda0 = term.searchHelper.findInLine('^~', 3, searchOptions);
const tilda1 = term.searchHelper.findInLine('^[~]', 3, searchOptions);
const tilda2 = term.searchHelper.findInLine('^\\~', 3, searchOptions);
expect(hello0).eql({col: 3, row: 0, term: 'de'});
// TODO: uncomment this test when line wrap search is checked in expect(hello1).eql({col: 9, row: 0, term: 'jk'});
// TODO: uncomment this test when line wrap search is checked in expect(hello2).eql(undefined);
expect(tilda0).eql({col: 0, row: 3, term: '~'});
expect(tilda1).eql({col: 0, row: 3, term: '~'});
expect(tilda2).eql({col: 0, row: 3, term: '~'});
describe('find', () => {
it('Searchhelper - should find correct position', function(): void {
search.apply(<any>MockTerminal);
const term = new MockTerminal({cols: 20, rows: 3});
term.core.write('Hello World\r\ntest\n123....hello');
term.pushWriteData();
const hello0 = term.searchHelper.findInLine('Hello', 0);
const hello1 = term.searchHelper.findInLine('Hello', 1);
const hello2 = term.searchHelper.findInLine('Hello', 2);
expect(hello0).eql({col: 0, row: 0, term: 'Hello'});
expect(hello1).eql(undefined);
expect(hello2).eql({col: 11, row: 2, term: 'Hello'});
});
it('should find search term accross line wrap', function(): void {
search.apply(<any>MockTerminal);
const term = new MockTerminal({cols: 10, rows: 2});
term.core.write('texttextHellotext');
term.pushWriteData();
const hello0 = (term.searchHelper as any)._findInLine('Hello', 0);
const hello1 = (term.searchHelper as any)._findInLine('Hello', 1);
expect(hello0).eql({col: 8, row: 0, term: 'Hello'});
expect(hello1).eql(undefined);
});
it('should respect search regex', function(): void {
search.apply(<any>MockTerminal);
const term = new MockTerminal({cols: 10, rows: 4});
term.core.write('abcdefghijklmnopqrstuvwxyz\r\n~/dev ');
/*
abcdefghij
klmnopqrst
uvwxyz
~/dev
*/
term.pushWriteData();
const searchOptions = {
regex: true,
wholeWord: false,
caseSensitive: false
};
const hello0 = term.searchHelper.findInLine('dee*', 0, searchOptions);
const hello1 = term.searchHelper.findInLine('jkk*', 0, searchOptions);
const hello2 = term.searchHelper.findInLine('mnn*', 1, searchOptions);
const tilda0 = term.searchHelper.findInLine('^~', 3, searchOptions);
const tilda1 = term.searchHelper.findInLine('^[~]', 3, searchOptions);
const tilda2 = term.searchHelper.findInLine('^\\~', 3, searchOptions);
expect(hello0).eql({col: 3, row: 0, term: 'de'});
expect(hello1).eql({col: 9, row: 0, term: 'jk'});
expect(hello2).eql(undefined);
expect(tilda0).eql({col: 0, row: 3, term: '~'});
expect(tilda1).eql({col: 0, row: 3, term: '~'});
expect(tilda2).eql({col: 0, row: 3, term: '~'});
});
});
});