Revert "Support wide characters and combined characters"

This reverts commit f961f90bc8.
This commit is contained in:
ntchjb
2019-01-03 15:32:25 +07:00
parent f961f90bc8
commit efe22d8cf3
+50 -164
View File
@@ -7,8 +7,6 @@ import { ISearchHelper, ISearchAddonTerminal, ISearchOptions, ISearchResult } fr
const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\;:"\',./<>?';
const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
const CHAR_DATA_CHAR_INDEX = 1;
const CHAR_DATA_WIDTH_INDEX = 2;
/**
* A class that knows how to search the terminal and how to display the results.
@@ -57,25 +55,28 @@ export class SearchHelper implements ISearchHelper {
this._initLinesCache();
// The row that has isWrapped = false
let findingRow = startRow;
// index of beginning column that _findInLine need to scan.
let cumulativeCols = startCol;
// If startRow is wrapped row, scan for unwrapped row above.
// So we can start matching on wrapped line from long unwrapped line.
while (this._terminal._core.buffer.lines.get(findingRow).isWrapped) {
findingRow--;
cumulativeCols += this._terminal.cols;
}
// Search startRow
result = this._findInLine(term, startRow, startCol, searchOptions);
// Search unwarpped row
result = this._findInLine(term, findingRow, cumulativeCols, searchOptions);
// Search from startRow + 1 to end, if the row is still wrapped line, increase cumulativeCols,
// otherwise, reset it and set the new unwrapped line index.
// Search from startRow + 1 to end
if (!result) {
// A row that has isWrapped = false
let findingRow = startRow;
// index of beginning column that _findInLine need to scan.
let cumulativeCols = startCol;
// If startRow is wrapped row, scan for unwrapped row above.
// So we can start matching on wrapped line from long unwrapped line.
while (this._terminal._core.buffer.lines.get(findingRow).isWrapped) {
findingRow--;
cumulativeCols += this._terminal.cols;
}
for (let y = startRow + 1; y < this._terminal._core.buffer.ybase + this._terminal.rows; y++) {
// Run _findInLine at unwrapped row, scan for cumulativeCols columns
result = this._findInLine(term, findingRow, cumulativeCols, searchOptions);
if (result) {
break;
}
// If the current line is wrapped line, increase index of column to ignore the previous scan
// Otherwise, reset beginning column index to zero with set new unwrapped line index
if (this._terminal._core.buffer.lines.get(y).isWrapped) {
@@ -84,11 +85,6 @@ export class SearchHelper implements ISearchHelper {
cumulativeCols = 0;
findingRow = y;
}
// Run _findInLine at unwrapped row, start scan at cumulativeCols column index
result = this._findInLine(term, findingRow, cumulativeCols, searchOptions);
if (result) {
break;
}
}
}
@@ -230,129 +226,11 @@ export class SearchHelper implements ISearchHelper {
(((searchIndex + term.length) === line.length) || (NON_WORD_CHARACTERS.indexOf(line[searchIndex + term.length]) !== -1)));
}
/**
* Translates a string index back to a BufferIndex.
* To get the correct buffer position the string must start at `startCol` 0
* (default in translateBufferLineToString).
* This method is similar to stringIndexToBufferIndex in Buffer.ts
* but this method added some modification that, if it found an empty cell,
* the method will see it as a whitespace and count it as a character.
* The modification is added because the given string index may include
* empty cells inside the string, which is needed to be counted.
* The return value of this method is the same as BufferIndex
* @param lineIndex line index the string was retrieved from
* @param stringIndex index within the string
* @param startCol column offset the string was retrieved from
*/
private _stringIndexToBufferIndex(lineIndex: number, stringIndex: number): [number, number] {
while (stringIndex) {
const line = this._terminal._core.buffer.lines.get(lineIndex);
if (!line) {
return [-1, -1];
}
for (let i = 0; i < this._terminal.cols; ++i) {
const charData = line.get(i);
const char = charData[CHAR_DATA_CHAR_INDEX];
// If found empty cell with width equals to 1, see it as whitespace
if (charData[CHAR_DATA_CHAR_INDEX] === '' && charData[CHAR_DATA_WIDTH_INDEX] > 0) {
stringIndex--;
}
stringIndex -= char.length;
if (stringIndex < 0) {
return [lineIndex, i];
}
}
lineIndex++;
}
return [lineIndex, 0];
}
/**
* Convert buffer index of unwrapped row to string index.
* @param lineIndex index of terminal row that is unwrapped
* @param bufferIndex index of terminal column on unwrapped row
*/
private _bufferIndexToStringIndex(lineIndex: number, bufferIndex: number): number {
let stringIndex = -1;
const buffer = this._terminal._core.buffer;
while (bufferIndex >= 0) {
const line = buffer.lines.get(lineIndex);
// Exceed index of bottom row, returned
if (!line) {
break;
}
let lineLength = this._terminal.cols;
// At the last line, lineLength will be trimmed to remove trailing empty cells
if (bufferIndex < lineLength) {
// Add 1 to getTrimmedLength because if providing bufferIndex is larger than
// converted string length, the result should be `string length`, not `string length - 1`
// to make sure that searching range includes the last character in the string
lineLength = line.getTrimmedLength() + 1;
}
for (let i = 0; i < lineLength; i++) {
const cell = line.get(i);
// Count number of characters from current buffer column in each cell.
stringIndex += cell[CHAR_DATA_CHAR_INDEX].length;
bufferIndex--;
// If found empty cell, act like found whitespace
if (cell[CHAR_DATA_CHAR_INDEX] === '' && cell[CHAR_DATA_WIDTH_INDEX] > 0) {
stringIndex++;
}
if (bufferIndex < 0) {
return stringIndex;
}
}
lineIndex++;
}
return stringIndex;
}
/**
* Get buffer length (number of cells) from provided string
* @param result The search result object including term, row, and col
*/
private _getCellLengthFromString(result: ISearchResult): number {
const length = result.term.length;
let strCount = 0;
let cellCount = 0;
let { col, row } = result;
let rowContent = this._terminal._core.buffer.lines.get(row);
// Count cells along with characters until the number of characters
// exceeds string length of search result.
while (strCount <= length) {
strCount += rowContent.get(col)[CHAR_DATA_CHAR_INDEX].length;
cellCount += rowContent.get(col)[CHAR_DATA_WIDTH_INDEX];
if (strCount >= length) {
break;
}
col++;
// In case that current cell exceed total number of cells in a row
// Begin col at 0 on the next line
if (col >= this._terminal.cols) {
col = 0;
rowContent = this._terminal._core.buffer.lines.get(++row);
}
}
return cellCount;
}
/**
* Searches a line for a search term. Takes the provided terminal line and searches the text line, which may contain
* subsequent terminal lines if the text is wrapped. If the provided line number is part of a wrapped text line that
* started on an earlier line then it is skipped since it will be properly searched when the terminal line that the
* text starts on is searched.
*
* The concept of searching is that:
* Get unwarpped line as string => convert rowand col to string index => begin searching to get search result as
* string index => convert back to buffer index (col, row) => return the result.
* @param term The search term.
* @param row The line to start the search from.
* @param col The column to start the search from.
@@ -365,8 +243,6 @@ export class SearchHelper implements ISearchHelper {
if (this._terminal._core.buffer.lines.get(row).isWrapped) {
return;
}
// Get unwrapped string from buffer lines
let stringLine = this._linesCache ? this._linesCache[row] : void 0;
if (stringLine === void 0) {
stringLine = this.translateBufferLineToStringWithWrap(row, true);
@@ -375,54 +251,67 @@ export class SearchHelper implements ISearchHelper {
}
}
// Check for case sensitive option
const searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase();
const searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase();
let resultIndex = -1;
// Convert from buffer index (col, row) to string index before begin searching
const stringIndex = this._bufferIndexToStringIndex(row, col);
if (searchOptions.regex) {
const searchRegex = RegExp(searchTerm, 'g');
let foundTerm: RegExpExecArray;
if (isReverseSearch) {
// This loop will get the resultIndex of the _last_ regex match in the range 0..col
while (foundTerm = searchRegex.exec(searchStringLine.slice(0, stringIndex))) {
while (foundTerm = searchRegex.exec(searchStringLine.slice(0, col))) {
resultIndex = searchRegex.lastIndex - foundTerm[0].length;
term = foundTerm[0];
searchRegex.lastIndex -= (term.length - 1);
}
} else {
foundTerm = searchRegex.exec(searchStringLine.slice(stringIndex));
foundTerm = searchRegex.exec(searchStringLine.slice(col));
if (foundTerm && foundTerm[0].length > 0) {
resultIndex = stringIndex + (searchRegex.lastIndex - foundTerm[0].length);
resultIndex = col + (searchRegex.lastIndex - foundTerm[0].length);
term = foundTerm[0];
}
}
} else {
if (isReverseSearch) {
if (stringIndex - searchTerm.length >= 0) {
resultIndex = searchStringLine.lastIndexOf(searchTerm, stringIndex - searchTerm.length);
if (col - searchTerm.length >= 0) {
resultIndex = searchStringLine.lastIndexOf(searchTerm, col - searchTerm.length);
}
} else {
resultIndex = searchStringLine.indexOf(searchTerm, stringIndex);
resultIndex = searchStringLine.indexOf(searchTerm, col);
}
}
if (resultIndex >= 0) {
// After getting the result as string index, convert it to buffer index.
const resultBufferIndex = this._stringIndexToBufferIndex(row, resultIndex);
// Check for wholeword option
// Adjust the row number and search index if needed since a "line" of text can span multiple rows
if (resultIndex >= this._terminal.cols) {
row += Math.floor(resultIndex / this._terminal.cols);
resultIndex = resultIndex % this._terminal.cols;
}
if (searchOptions.wholeWord && !this._isWholeWord(resultIndex, searchStringLine, term)) {
return;
}
const line = this._terminal._core.buffer.lines.get(row);
for (let i = 0; i < resultIndex; i++) {
const charData = line.get(i);
// Adjust the searchIndex to normalize emoji into single chars
const char = charData[1/*CHAR_DATA_CHAR_INDEX*/];
if (char.length > 1) {
resultIndex -= char.length - 1;
}
// Adjust the searchIndex for empty characters following wide unicode
// chars (eg. CJK)
const charWidth = charData[2/*CHAR_DATA_WIDTH_INDEX*/];
if (charWidth === 0) {
resultIndex++;
}
}
return {
term,
col: resultBufferIndex[1],
row: resultBufferIndex[0]
col: resultIndex,
row
};
}
}
@@ -432,7 +321,7 @@ export class SearchHelper implements ISearchHelper {
* function is useful for getting the actual text underneath the raw selection
* position.
* @param line The line being translated.
* @param trimRight Whether to trim -space to the right.
* @param trimRight Whether to trim whitespace to the right.
*/
public translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean): string {
let lineString = '';
@@ -441,8 +330,7 @@ export class SearchHelper implements ISearchHelper {
do {
const nextLine = this._terminal._core.buffer.lines.get(lineIndex + 1);
lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
// string should be cut with string index, not buffer index to support wide characters
lineString += this._terminal._core.buffer.translateBufferLineToString(lineIndex, !lineWrapsToNext && trimRight).substring(0, this._bufferIndexToStringIndex(lineIndex, this._terminal.cols));
lineString += this._terminal._core.buffer.translateBufferLineToString(lineIndex, !lineWrapsToNext && trimRight).substring(0, this._terminal.cols);
lineIndex++;
} while (lineWrapsToNext);
@@ -459,9 +347,7 @@ export class SearchHelper implements ISearchHelper {
this._terminal.clearSelection();
return false;
}
// The selection length should be number of cell needed to be selected, not string length.
// To support wide character
this._terminal._core.selectionManager.setSelection(result.col, result.row, this._getCellLengthFromString(result));
this._terminal._core.selectionManager.setSelection(result.col, result.row, result.term.length);
this._terminal.scrollLines(result.row - this._terminal._core.buffer.ydisp);
return true;
}