Mostly emoji selection

Selection is not aware of characters that have a size greater than 1.

When the emoji is at the start of the selection it still doesn't work.

Fixes #1015
This commit is contained in:
Daniel Imms
2017-09-30 06:13:58 -04:00
parent 313d1e6fec
commit feb2b958a4
2 changed files with 72 additions and 13 deletions
+30 -8
View File
@@ -208,9 +208,19 @@ export class Buffer implements IBuffer {
public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
// Get full line
let lineString = '';
let widthAdjustedStartCol = startCol;
let widthAdjustedEndCol = endCol;
const line = this.lines.get(lineIndex);
if (!line) {
return '';
}
// Initialize column and index values. Column values represent the actual
// cell column, indexes represent the index in the string. Indexes are
// needed here because some chars are 0 characters long (eg. after wide
// chars) and some chars are longer than 1 characters long (eg. emojis).
let startIndex = startCol;
endCol = endCol || line.length;
let endIndex = endCol;
for (let i = 0; i < line.length; i++) {
const char = line[i];
lineString += char[CHAR_DATA_CHAR_INDEX];
@@ -218,29 +228,41 @@ export class Buffer implements IBuffer {
// column indexes
if (char[CHAR_DATA_WIDTH_INDEX] === 0) {
if (startCol >= i) {
widthAdjustedStartCol--;
startIndex -= char[CHAR_DATA_CHAR_INDEX].length;
}
if (endCol >= i) {
widthAdjustedEndCol--;
endIndex -= char[CHAR_DATA_CHAR_INDEX].length;
}
} else {
// Adjust the columns to take glyphs that are represented by multiple
// code points into account.
if (char[CHAR_DATA_CHAR_INDEX].length > 1) {
if (startCol >= i) {
startIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;
}
if (endCol >= i) {
endIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;
}
}
}
// TODO: startCol needs to be emoji-aware, currently each emoji code point is
// consuming addition space in the selection text
}
// Calculate the final end col by trimming whitespace on the right of the
// line if needed.
let finalEndCol = widthAdjustedEndCol || line.length;
if (trimRight) {
const rightWhitespaceIndex = lineString.search(/\s+$/);
if (rightWhitespaceIndex !== -1) {
finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex);
endIndex = Math.min(endIndex, rightWhitespaceIndex);
}
// Return the empty string if only trimmed whitespace is selected
if (finalEndCol <= widthAdjustedStartCol) {
if (endIndex <= startIndex) {
return '';
}
}
return lineString.substring(widthAdjustedStartCol, finalEndCol);
return lineString.substring(startIndex, endIndex);
}
/**
+42 -5
View File
@@ -11,7 +11,7 @@ import { EventEmitter } from './EventEmitter';
import { ITerminal, ICircularList, ISelectionManager, IBuffer } from './Interfaces';
import { SelectionModel } from './SelectionModel';
import { LineData } from './Types';
import { CHAR_DATA_WIDTH_INDEX } from './Buffer';
import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer';
/**
* The number of pixels the mouse needs to be above or below the viewport in
@@ -281,6 +281,9 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
// Convert to 0-based
coords[0]--;
coords[1]--;
console.log('coords', coords);
// Convert viewport coords to buffer coords
coords[1] += this._terminal.buffer.ydisp;
return coords;
@@ -545,9 +548,16 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
for (let i = 0; coords[0] >= i; i++) {
const char = bufferLine[i];
if (char[CHAR_DATA_WIDTH_INDEX] === 0) {
// Wide characters aren't included in the line string so decrement the index
charIndex--;
// }
} else if (char[CHAR_DATA_CHAR_INDEX].length > 1) {
console.log('char length > 1', char[CHAR_DATA_CHAR_INDEX], char[CHAR_DATA_CHAR_INDEX].length);
// Emojis take up multiple characters, so adjust accordingly
charIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;
}
}
console.log('character index: ', charIndex);
return charIndex;
}
@@ -606,20 +616,47 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
endCol++;
}
// Expand the string in both directions until a space is hit
while (startIndex > 0 && !this._isCharWordSeparator(line.charAt(startIndex - 1))) {
if (bufferLine[startCol - 1][CHAR_DATA_WIDTH_INDEX] === 0) {
let nextCharData = startIndex > 0 ? bufferLine[startCol - 1] : null;
// TODO: Need to make sure that characters whose strings are longer than 1 get compensated for
// Double click words should expand to the spaces.
// while (startIndex > 0 && !this._isCharWordSeparator(line.charAt(startIndex - 1))) {
console.log('start char: ' + bufferLine[startCol]);
console.log('scan backwards');
console.log(' startIndex:',startIndex);
while (startIndex > 0 && !this._isCharWordSeparator(bufferLine[startCol - 1][CHAR_DATA_CHAR_INDEX])) {
const char = bufferLine[startCol - 1];
console.log(' char: ' + char);
if (char[CHAR_DATA_WIDTH_INDEX] === 0) {
// If the next character is a wide char, record it and skip the column
leftWideCharCount++;
startCol--;
} else if (char[CHAR_DATA_CHAR_INDEX].length > 1) {
startIndex -= char[CHAR_DATA_CHAR_INDEX].length - 1;
console.log('x', char[CHAR_DATA_CHAR_INDEX], char[CHAR_DATA_CHAR_INDEX].length);
}
startIndex--;
startCol--;
}
while (endIndex + 1 < line.length && !this._isCharWordSeparator(line.charAt(endIndex + 1))) {
if (bufferLine[endCol + 1][CHAR_DATA_WIDTH_INDEX] === 2) {
console.log('scan forwards');
// while (endIndex + 1 < line.length && !this._isCharWordSeparator(line.charAt(endIndex + 1))) {
console.log(' first checking: ',bufferLine[endCol + 1]);
console.log(' endIndex:',endIndex);
console.log(' line:',line);
console.log(' line.length:',line.length);
while (endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine[endCol + 1][CHAR_DATA_CHAR_INDEX])) {
const char = bufferLine[endCol + 1];
console.log(' char: ' + char);
if (char[CHAR_DATA_WIDTH_INDEX] === 2) {
// If the next character is a wide char, record it and skip the column
rightWideCharCount++;
endCol++;
} else if (char[CHAR_DATA_CHAR_INDEX].length > 1) {
startIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;
}
endIndex++;
endCol++;