From 9c9aa68c23b228bce802ef6e5bdde7998276ea3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 15 Nov 2019 01:07:14 +0100 Subject: [PATCH 01/30] wcwidth rules unicode 10 --- src/InputHandler.ts | 3 +- src/common/CharWidth.ts | 249 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 1 deletion(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 3994d532..28d17b3e 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -7,7 +7,7 @@ import { IInputHandler, IInputHandlingTerminal } from './Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; -import { wcwidth } from 'common/CharWidth'; +import { wcwidth, wcwidth10 } from 'common/CharWidth'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; import { Disposable } from 'common/Lifecycle'; import { concat } from 'common/TypedArrayUtils'; @@ -394,6 +394,7 @@ export class InputHandler extends Disposable implements IInputHandler { // calculate print space // expensive call, therefore we save width in line buffer chWidth = wcwidth(code); + chWidth = wcwidth10(code); // get charset replacement character // charset is only defined for ASCII, therefore we only diff --git a/src/common/CharWidth.ts b/src/common/CharWidth.ts index 24f53450..ae826c89 100644 --- a/src/common/CharWidth.ts +++ b/src/common/CharWidth.ts @@ -169,3 +169,252 @@ export function getStringCellWidth(s: string): number { } return result; } + + + +const COMBINING_BMP = [ + [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], + [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], + [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], + [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], + [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], + [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], + [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], + [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], + [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], + [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], + [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], + [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], + [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], + [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], + [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], + [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], + [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], + [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], + [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], + [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], + [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], + [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], + [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], + [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], + [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], + [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], + [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], + [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], + [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], + [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], + [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], + [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], + [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], + [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], + [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], + [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], + [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], + [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], + [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], + [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], + [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], + [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], + [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], + [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], + [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], + [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], + [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], + [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], + [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], + [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], + [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], + [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], + [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], + [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], + [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], + [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], + [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], + [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], + [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], + [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], + [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], + [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], + [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], + [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], + [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], + [0xFFF9, 0xFFFB] +]; + +const COMBINING_HIGH = [ + [0x101FD, 0x101FD], [0x102E0, 0x102E0], + [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], + [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], + [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], + [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], + [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], + [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], + [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], + [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], + [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], + [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], + [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], + [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], + [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], + [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], + [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], + [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], + [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], + [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], + [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], + [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], + [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], + [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], + [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], + [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], + [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], + [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], + [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], + [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], + [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], + [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], + [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], + [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] +]; + +const WIDE_BMP = [ + [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], + [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], + [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], + [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], + [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], + [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], + [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], + [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], + [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], + [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], + [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], + [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], + [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], + [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], + [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], + [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], + [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], + [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], + [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], + [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], + [0xFFE0, 0xFFE6] +]; + +const WIDE_HIGH = [ + [0x16FE0, 0x16FE3], [0x17000, 0x187F7], + [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], + [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], + [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], + [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], + [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], + [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], + [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], + [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], + [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], + [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], + [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], + [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], + [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], + [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], + [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], + [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], + [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] +]; + +function bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { + return false; + } + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + return false; +} + +function wcwidthHigh(ucs: number): 0 | 1 | 2 { + if (bisearch(ucs, COMBINING_HIGH)) { + return 0; + } + if (bisearch(ucs, WIDE_HIGH)) { + return 2; + } + return 1; +} + + +const TABLE = new Uint8Array(65536); +fill(TABLE, 1); +fill(TABLE, 0, 1, 32); +fill(TABLE, 0, 0x7f, 0xa0); +for (let r = 0; r < COMBINING_BMP.length; ++r) { + fill(TABLE, 0, COMBINING_BMP[r][0], COMBINING_BMP[r][1] + 1); +} +for (let r = 0; r < WIDE_BMP.length; ++r) { + fill(TABLE, 2, WIDE_BMP[r][0], WIDE_BMP[r][1] + 1); +} + +export function wcwidth10(num: number): number { + if (num < 32) { + return 0; + } + if (num < 127) { + return 1; + } + if (num < 65536) { + return TABLE[num]; + } + return wcwidthHigh(num); +}; + +export function getStringCellWidth10(s: string): number { + let result = 0; + const length = s.length; + for (let i = 0; i < length; ++i) { + let code = s.charCodeAt(i); + // surrogate pair first + if (0xD800 <= code && code <= 0xDBFF) { + if (++i >= length) { + // this should not happen with strings retrieved from + // Buffer.translateToString as it converts from UTF-32 + // and therefore always should contain the second part + // for any other string we still have to handle it somehow: + // simply treat the lonely surrogate first as a single char (UCS-2 behavior) + return result + wcwidth10(code); + } + const second = s.charCodeAt(i); + // convert surrogate pair to high codepoint only for valid second part (UTF-16) + // otherwise treat them independently (UCS-2 behavior) + if (0xDC00 <= second && second <= 0xDFFF) { + code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } else { + result += wcwidth10(second); + } + } + result += wcwidth10(code); + } + return result; +} From c7ad4aa460710aa2062b2bcf06b015525a9aa3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 15 Nov 2019 13:33:28 +0100 Subject: [PATCH 02/30] terminal option unicodeVersion --- demo/client.ts | 3 ++- src/InputHandler.ts | 12 +++++++++--- src/Terminal.test.ts | 4 ++-- src/browser/Linkifier.ts | 4 ++-- src/common/CharWidth.test.ts | 4 ++-- src/common/CharWidth.ts | 20 ++++++++++---------- src/common/services/OptionsService.ts | 3 ++- src/common/services/Services.ts | 2 ++ 8 files changed, 31 insertions(+), 21 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 5b76700e..51c16d50 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -256,7 +256,8 @@ function initOptions(term: TerminalType): void { fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], logLevel: ['debug', 'info', 'warn', 'error', 'off'], rendererType: ['dom', 'canvas'], - wordSeparator: null + wordSeparator: null, + unicodeVersion: ['6', '10'] }; const options = Object.keys((term)._core.options); const booleanOptions = []; diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 28d17b3e..d2ad6cd3 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -7,7 +7,7 @@ import { IInputHandler, IInputHandlingTerminal } from './Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; -import { wcwidth, wcwidth10 } from 'common/CharWidth'; +import { wcwidthV6, wcwidthV10 } from 'common/CharWidth'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; import { Disposable } from 'common/Lifecycle'; import { concat } from 'common/TypedArrayUtils'; @@ -127,6 +127,7 @@ export class InputHandler extends Disposable implements IInputHandler { private _stringDecoder: StringToUtf32 = new StringToUtf32(); private _utf8Decoder: Utf8ToUtf32 = new Utf8ToUtf32(); private _workCell: CellData = new CellData(); + private _wcwidth: (codepoint: number) => number; private _onCursorMove = new EventEmitter(); public get onCursorMove(): IEvent { return this._onCursorMove.event; } @@ -146,6 +147,12 @@ export class InputHandler extends Disposable implements IInputHandler { private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { super(); + this._wcwidth = this._optionsService.options.unicodeVersion === '10' ? wcwidthV10 : wcwidthV6; + this._optionsService.onOptionChange(option => { + if (option === 'unicodeVersion') { + this._wcwidth = this._optionsService.options.unicodeVersion === '10' ? wcwidthV10 : wcwidthV6; + } + }); this.register(this._parser); @@ -393,8 +400,7 @@ export class InputHandler extends Disposable implements IInputHandler { // calculate print space // expensive call, therefore we save width in line buffer - chWidth = wcwidth(code); - chWidth = wcwidth10(code); + chWidth = this._wcwidth(code); // get charset replacement character // charset is only defined for ASCII, therefore we only diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index b55a516b..2335f0f6 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -7,7 +7,7 @@ import { assert, expect } from 'chai'; import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from './TestUtils.test'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { wcwidth } from 'common/CharWidth'; +import { wcwidthV6 } from 'common/CharWidth'; import { IBufferService } from 'common/services/Services'; import { Linkifier } from 'browser/Linkifier'; import { MockLogService } from 'common/TestUtils.test'; @@ -762,7 +762,7 @@ describe('Terminal', () => { for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.buffer.x = term.cols - 1; term.wraparoundMode = false; - const width = wcwidth((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000); + const width = wcwidthV6((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000); if (width !== 1) { continue; } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 58aca70e..f51ea1ca 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -5,7 +5,7 @@ import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types'; import { IBufferStringIteratorResult } from 'common/buffer/Types'; -import { getStringCellWidth } from 'common/CharWidth'; +import { getStringCellWidthV6 } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { ILogService, IBufferService } from 'common/services/Services'; @@ -278,7 +278,7 @@ export class Linkifier implements ILinkifier { return; } - const width = getStringCellWidth(uri); + const width = getStringCellWidthV6(uri); // FIXME: apply options.unicodeVersion const x1 = x % this._bufferService.cols; const y1 = y + Math.floor(x / this._bufferService.cols); let x2 = (x1 + width) % this._bufferService.cols; diff --git a/src/common/CharWidth.test.ts b/src/common/CharWidth.test.ts index 6b2493ee..8a857424 100644 --- a/src/common/CharWidth.test.ts +++ b/src/common/CharWidth.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { wcwidth } from 'common/CharWidth'; +import { wcwidthV6 } from 'common/CharWidth'; it('wcwidth should match all values from the old implementation', function(): void { // old implementation @@ -177,6 +177,6 @@ it('wcwidth should match all values from the old implementation', function(): vo // test full BMP range old vs new implmenetation for (let i = 0; i < 65536; ++i) { - assert.equal(wcwidth(i), wcwidthOld(i), `mismatch for i: ${i}`); + assert.equal(wcwidthV6(i), wcwidthOld(i), `mismatch for i: ${i}`); } }); diff --git a/src/common/CharWidth.ts b/src/common/CharWidth.ts index ae826c89..b03f74ad 100644 --- a/src/common/CharWidth.ts +++ b/src/common/CharWidth.ts @@ -5,7 +5,7 @@ import { fill } from 'common/TypedArrayUtils'; -export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number { +export const wcwidthV6 = (function(opts: {nul: number, control: number}): (ucs: number) => number { // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c // combining characters const COMBINING_BMP = [ @@ -141,7 +141,7 @@ export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: nu /** * Get the terminal cell width for a string. */ -export function getStringCellWidth(s: string): number { +export function getStringCellWidthV6(s: string): number { let result = 0; const length = s.length; for (let i = 0; i < length; ++i) { @@ -154,7 +154,7 @@ export function getStringCellWidth(s: string): number { // and therefore always should contain the second part // for any other string we still have to handle it somehow: // simply treat the lonely surrogate first as a single char (UCS-2 behavior) - return result + wcwidth(code); + return result + wcwidthV6(code); } const second = s.charCodeAt(i); // convert surrogate pair to high codepoint only for valid second part (UTF-16) @@ -162,10 +162,10 @@ export function getStringCellWidth(s: string): number { if (0xDC00 <= second && second <= 0xDFFF) { code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } else { - result += wcwidth(second); + result += wcwidthV6(second); } } - result += wcwidth(code); + result += wcwidthV6(code); } return result; } @@ -377,7 +377,7 @@ for (let r = 0; r < WIDE_BMP.length; ++r) { fill(TABLE, 2, WIDE_BMP[r][0], WIDE_BMP[r][1] + 1); } -export function wcwidth10(num: number): number { +export function wcwidthV10(num: number): number { if (num < 32) { return 0; } @@ -390,7 +390,7 @@ export function wcwidth10(num: number): number { return wcwidthHigh(num); }; -export function getStringCellWidth10(s: string): number { +export function getStringCellWidthV10(s: string): number { let result = 0; const length = s.length; for (let i = 0; i < length; ++i) { @@ -403,7 +403,7 @@ export function getStringCellWidth10(s: string): number { // and therefore always should contain the second part // for any other string we still have to handle it somehow: // simply treat the lonely surrogate first as a single char (UCS-2 behavior) - return result + wcwidth10(code); + return result + wcwidthV10(code); } const second = s.charCodeAt(i); // convert surrogate pair to high codepoint only for valid second part (UTF-16) @@ -411,10 +411,10 @@ export function getStringCellWidth10(s: string): number { if (0xDC00 <= second && second <= 0xDFFF) { code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } else { - result += wcwidth10(second); + result += wcwidthV10(second); } } - result += wcwidth10(code); + result += wcwidthV10(code); } return result; } diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index d9ea60d5..ccd38d47 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -50,7 +50,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ screenKeys: false, cancelEvents: false, useFlowControl: false, - wordSeparator: ' ()[]{}\',:;"' + wordSeparator: ' ()[]{}\',:;"', + unicodeVersion: '6' }); /** diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 45d39a6a..380bc6c4 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -212,6 +212,7 @@ export interface IPartialTerminalOptions { scrollSensitivity?: number; tabStopWidth?: number; theme?: ITheme; + unicodeVersion?: '6' | '10'; windowsMode?: boolean; wordSeparator?: string; } @@ -244,6 +245,7 @@ export interface ITerminalOptions { scrollSensitivity: number; tabStopWidth: number; theme: ITheme; + unicodeVersion: '6' | '10'; windowsMode: boolean; wordSeparator: string; From 25bbde2be26cf6dc29bc11411b641efa9a1ebe31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 15 Nov 2019 14:47:51 +0100 Subject: [PATCH 03/30] cleanup CharWidth.ts --- src/common/CharWidth.ts | 364 ++++++++++++++++------------------------ 1 file changed, 146 insertions(+), 218 deletions(-) diff --git a/src/common/CharWidth.ts b/src/common/CharWidth.ts index b03f74ad..852b99af 100644 --- a/src/common/CharWidth.ts +++ b/src/common/CharWidth.ts @@ -5,143 +5,28 @@ import { fill } from 'common/TypedArrayUtils'; -export const wcwidthV6 = (function(opts: {nul: number, control: number}): (ucs: number) => number { - // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c - // combining characters - const COMBINING_BMP = [ - [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], - [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], - [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], - [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], - [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], - [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], - [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], - [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], - [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], - [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], - [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], - [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], - [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], - [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], - [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], - [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], - [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], - [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], - [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], - [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], - [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], - [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], - [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], - [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], - [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], - [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], - [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], - [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], - [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], - [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], - [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], - [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], - [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], - [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], - [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], - [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], - [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] - ]; - const COMBINING_HIGH = [ - [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], - [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] - ]; - // binary search - function bisearch(ucs: number, data: number[][]): boolean { - let min = 0; - let max = data.length - 1; - let mid; - if (ucs < data[0][0] || ucs > data[max][1]) { - return false; - } - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) { - min = mid + 1; - } else if (ucs < data[mid][0]) { - max = mid - 1; - } else { - return true; - } - } +// helper: binary search +function bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { return false; } - function wcwidthHigh(ucs: number): 0 | 1 | 2 { - if (bisearch(ucs, COMBINING_HIGH)) { - return 0; + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; } - if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) { - return 2; - } - return 1; } - const control = opts.control | 0; + return false; +} - // create lookup table for BMP plane - const table = new Uint8Array(65536); - fill(table, 1); - table[0] = opts.nul; - // control chars - fill(table, opts.control, 1, 32); - fill(table, opts.control, 0x7f, 0xa0); - - // apply wide char rules first - // wide chars - fill(table, 2, 0x1100, 0x1160); - table[0x2329] = 2; - table[0x232a] = 2; - fill(table, 2, 0x2e80, 0xa4d0); - table[0x303f] = 1; // wrongly in last line - - fill(table, 2, 0xac00, 0xd7a4); - fill(table, 2, 0xf900, 0xfb00); - fill(table, 2, 0xfe10, 0xfe1a); - fill(table, 2, 0xfe30, 0xfe70); - fill(table, 2, 0xff00, 0xff61); - fill(table, 2, 0xffe0, 0xffe7); - - // apply combining last to ensure we overwrite - // wrongly wide set chars: - // the original algo evals combining first and falls - // through to wide check so we simply do here the opposite - // combining 0 - for (let r = 0; r < COMBINING_BMP.length; ++r) { - fill(table, 0, COMBINING_BMP[r][0], COMBINING_BMP[r][1] + 1); - } - - return function (num: number): number { - if (num < 32) { - return control | 0; - } - if (num < 127) { - return 1; - } - if (num < 65536) { - return table[num]; - } - // do a full search for high codepoints - return wcwidthHigh(num); - }; -})({nul: 0, control: 0}); // configurable options - -/** - * Get the terminal cell width for a string. - */ -export function getStringCellWidthV6(s: string): number { +function getStringCellWidthPrivate(wcwidthImpl: (cp: number) => number, s: string): number { let result = 0; const length = s.length; for (let i = 0; i < length; ++i) { @@ -154,7 +39,7 @@ export function getStringCellWidthV6(s: string): number { // and therefore always should contain the second part // for any other string we still have to handle it somehow: // simply treat the lonely surrogate first as a single char (UCS-2 behavior) - return result + wcwidthV6(code); + return result + wcwidthImpl(code); } const second = s.charCodeAt(i); // convert surrogate pair to high codepoint only for valid second part (UTF-16) @@ -162,17 +47,119 @@ export function getStringCellWidthV6(s: string): number { if (0xDC00 <= second && second <= 0xDFFF) { code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } else { - result += wcwidthV6(second); + result += wcwidthImpl(second); } } - result += wcwidthV6(code); + result += wcwidthImpl(code); } return result; } +/** + * Unicode version 6 + */ +const BMP_COMBINING_V6 = [ + [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], + [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], + [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], + [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], + [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], + [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], + [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], + [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], + [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], + [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], + [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], + [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], + [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], + [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], + [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], + [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], + [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], + [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], + [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], + [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], + [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], + [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], + [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], + [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], + [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], + [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], + [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], + [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], + [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], + [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], + [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], + [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], + [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], + [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], + [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], + [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], + [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] +]; +const HIGH_COMBINING_V6 = [ + [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], + [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] +]; +// create lookup table for BMP plane +const BMP_TABLE_V6 = new Uint8Array(65536); +fill(BMP_TABLE_V6, 1); +BMP_TABLE_V6[0] = 0; +// control chars +fill(BMP_TABLE_V6, 0, 1, 32); +fill(BMP_TABLE_V6, 0, 0x7f, 0xa0); -const COMBINING_BMP = [ +// apply wide char rules first +// wide chars +fill(BMP_TABLE_V6, 2, 0x1100, 0x1160); +BMP_TABLE_V6[0x2329] = 2; +BMP_TABLE_V6[0x232a] = 2; +fill(BMP_TABLE_V6, 2, 0x2e80, 0xa4d0); +BMP_TABLE_V6[0x303f] = 1; // wrongly in last line + +fill(BMP_TABLE_V6, 2, 0xac00, 0xd7a4); +fill(BMP_TABLE_V6, 2, 0xf900, 0xfb00); +fill(BMP_TABLE_V6, 2, 0xfe10, 0xfe1a); +fill(BMP_TABLE_V6, 2, 0xfe30, 0xfe70); +fill(BMP_TABLE_V6, 2, 0xff00, 0xff61); +fill(BMP_TABLE_V6, 2, 0xffe0, 0xffe7); + +// apply combining last to ensure we overwrite +// wrongly wide set chars: +// the original algo evals combining first and falls +// through to wide check so we simply do here the opposite +// combining 0 +for (let r = 0; r < BMP_COMBINING_V6.length; ++r) { + fill(BMP_TABLE_V6, 0, BMP_COMBINING_V6[r][0], BMP_COMBINING_V6[r][1] + 1); +} + +export function wcwidthV6(num: number): number { + if (num < 32) return 0; + if (num < 127) return 1; + if (num < 65536) return BMP_TABLE_V6[num]; + if (bisearch(num, HIGH_COMBINING_V6)) return 0; + if ((num >= 0x20000 && num <= 0x2fffd) || (num >= 0x30000 && num <= 0x3fffd)) return 2; + return 1; +} + +export function getStringCellWidthV6(s: string): number { + return getStringCellWidthPrivate(wcwidthV6, s); +} + +/** + * Unicode version 10 + */ +const BMP_COMBINING_V10 = [ [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], @@ -244,10 +231,10 @@ const COMBINING_BMP = [ [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], - [0xFFF9, 0xFFFB] + [0xFFF9, 0xFFFB] ]; - -const COMBINING_HIGH = [ + +const HIGH_COMBINING_V10 = [ [0x101FD, 0x101FD], [0x102E0, 0x102E0], [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], @@ -290,7 +277,7 @@ const COMBINING_HIGH = [ [0xE0100, 0xE01EF] ]; -const WIDE_BMP = [ +const BMP_WIDE_V10 = [ [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], @@ -313,8 +300,8 @@ const WIDE_BMP = [ [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], [0xFFE0, 0xFFE6] ]; - -const WIDE_HIGH = [ + +const HIGH_WIDE_V10 = [ [0x16FE0, 0x16FE3], [0x17000, 0x187F7], [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], @@ -335,86 +322,27 @@ const WIDE_HIGH = [ [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] ]; -function bisearch(ucs: number, data: number[][]): boolean { - let min = 0; - let max = data.length - 1; - let mid; - if (ucs < data[0][0] || ucs > data[max][1]) { - return false; - } - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) { - min = mid + 1; - } else if (ucs < data[mid][0]) { - max = mid - 1; - } else { - return true; - } - } - return false; +const BMP_TABLE_V10 = new Uint8Array(65536); +fill(BMP_TABLE_V10, 1); +BMP_TABLE_V10[0] = 0; +fill(BMP_TABLE_V10, 0, 1, 32); +fill(BMP_TABLE_V10, 0, 0x7f, 0xa0); +for (let r = 0; r < BMP_COMBINING_V10.length; ++r) { + fill(BMP_TABLE_V10, 0, BMP_COMBINING_V10[r][0], BMP_COMBINING_V10[r][1] + 1); } - -function wcwidthHigh(ucs: number): 0 | 1 | 2 { - if (bisearch(ucs, COMBINING_HIGH)) { - return 0; - } - if (bisearch(ucs, WIDE_HIGH)) { - return 2; - } - return 1; -} - - -const TABLE = new Uint8Array(65536); -fill(TABLE, 1); -fill(TABLE, 0, 1, 32); -fill(TABLE, 0, 0x7f, 0xa0); -for (let r = 0; r < COMBINING_BMP.length; ++r) { - fill(TABLE, 0, COMBINING_BMP[r][0], COMBINING_BMP[r][1] + 1); -} -for (let r = 0; r < WIDE_BMP.length; ++r) { - fill(TABLE, 2, WIDE_BMP[r][0], WIDE_BMP[r][1] + 1); +for (let r = 0; r < BMP_WIDE_V10.length; ++r) { + fill(BMP_TABLE_V10, 2, BMP_WIDE_V10[r][0], BMP_WIDE_V10[r][1] + 1); } export function wcwidthV10(num: number): number { - if (num < 32) { - return 0; - } - if (num < 127) { - return 1; - } - if (num < 65536) { - return TABLE[num]; - } - return wcwidthHigh(num); -}; + if (num < 32) return 0; + if (num < 127) return 1; + if (num < 65536) return BMP_TABLE_V10[num]; + if (bisearch(num, HIGH_COMBINING_V10)) return 0; + if (bisearch(num, HIGH_WIDE_V10)) return 2; + return 1; +} export function getStringCellWidthV10(s: string): number { - let result = 0; - const length = s.length; - for (let i = 0; i < length; ++i) { - let code = s.charCodeAt(i); - // surrogate pair first - if (0xD800 <= code && code <= 0xDBFF) { - if (++i >= length) { - // this should not happen with strings retrieved from - // Buffer.translateToString as it converts from UTF-32 - // and therefore always should contain the second part - // for any other string we still have to handle it somehow: - // simply treat the lonely surrogate first as a single char (UCS-2 behavior) - return result + wcwidthV10(code); - } - const second = s.charCodeAt(i); - // convert surrogate pair to high codepoint only for valid second part (UTF-16) - // otherwise treat them independently (UCS-2 behavior) - if (0xDC00 <= second && second <= 0xDFFF) { - code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; - } else { - result += wcwidthV10(second); - } - } - result += wcwidthV10(code); - } - return result; + return getStringCellWidthPrivate(wcwidthV10, s); } From aca6d24e964419a6b292a0fc36ddeaba6c04af4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 15 Nov 2019 18:08:17 +0100 Subject: [PATCH 04/30] change to v11, expose unicodeVersion, fix linkifier --- demo/client.ts | 2 +- src/InputHandler.ts | 6 +++--- src/Terminal.test.ts | 4 ++-- src/Terminal.ts | 2 +- src/browser/Linkifier.test.ts | 4 ++-- src/browser/Linkifier.ts | 10 ++++----- src/common/CharWidth.test.ts | 9 +++++++- src/common/CharWidth.ts | 38 ++++++++++++++++----------------- src/common/services/Services.ts | 4 ++-- typings/xterm.d.ts | 5 +++++ 10 files changed, 48 insertions(+), 36 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 51c16d50..347a9d65 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -257,7 +257,7 @@ function initOptions(term: TerminalType): void { logLevel: ['debug', 'info', 'warn', 'error', 'off'], rendererType: ['dom', 'canvas'], wordSeparator: null, - unicodeVersion: ['6', '10'] + unicodeVersion: ['6', '11'] }; const options = Object.keys((term)._core.options); const booleanOptions = []; diff --git a/src/InputHandler.ts b/src/InputHandler.ts index d2ad6cd3..e439a7d9 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -7,7 +7,7 @@ import { IInputHandler, IInputHandlingTerminal } from './Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; -import { wcwidthV6, wcwidthV10 } from 'common/CharWidth'; +import { wcwidthV6, wcwidthV11 } from 'common/CharWidth'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; import { Disposable } from 'common/Lifecycle'; import { concat } from 'common/TypedArrayUtils'; @@ -147,10 +147,10 @@ export class InputHandler extends Disposable implements IInputHandler { private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { super(); - this._wcwidth = this._optionsService.options.unicodeVersion === '10' ? wcwidthV10 : wcwidthV6; + this._wcwidth = this._optionsService.options.unicodeVersion === '11' ? wcwidthV11 : wcwidthV6; this._optionsService.onOptionChange(option => { if (option === 'unicodeVersion') { - this._wcwidth = this._optionsService.options.unicodeVersion === '10' ? wcwidthV10 : wcwidthV6; + this._wcwidth = this._optionsService.options.unicodeVersion === '11' ? wcwidthV11 : wcwidthV6; } }); diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 2335f0f6..5f7f0721 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -10,7 +10,7 @@ import { CellData } from 'common/buffer/CellData'; import { wcwidthV6 } from 'common/CharWidth'; import { IBufferService } from 'common/services/Services'; import { Linkifier } from 'browser/Linkifier'; -import { MockLogService } from 'common/TestUtils.test'; +import { MockLogService, MockOptionsService } from 'common/TestUtils.test'; import { IRegisteredLinkMatcher, IMouseZoneManager, IMouseZone } from 'browser/Types'; const INIT_COLS = 80; @@ -1371,7 +1371,7 @@ describe('Terminal', () => { class TestLinkifier extends Linkifier { constructor(bufferService: IBufferService) { - super(bufferService, new MockLogService()); + super(bufferService, new MockLogService(), new MockOptionsService()); Linkifier._timeBeforeLatency = 0; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 6a039a0e..b4c0e4e6 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -281,7 +281,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); - this.linkifier = this.linkifier || new Linkifier(this._bufferService, this._logService); + this.linkifier = this.linkifier || new Linkifier(this._bufferService, this._logService, this.optionsService); if (this.options.windowsMode) { this._windowsMode = applyWindowsMode(this); diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index 128f786d..a4b4d2f5 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -9,12 +9,12 @@ import { IBufferLine } from 'common/Types'; import { Linkifier } from 'browser/Linkifier'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { MockLogService, MockBufferService } from 'common/TestUtils.test'; +import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; class TestLinkifier extends Linkifier { constructor(bufferService: IBufferService) { - super(bufferService, new MockLogService()); + super(bufferService, new MockLogService(), new MockOptionsService()); Linkifier._timeBeforeLatency = 0; } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index f51ea1ca..072f7972 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -5,9 +5,9 @@ import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types'; import { IBufferStringIteratorResult } from 'common/buffer/Types'; -import { getStringCellWidthV6 } from 'common/CharWidth'; +import { getStringCellWidthV6, getStringCellWidthV11 } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { ILogService, IBufferService } from 'common/services/Services'; +import { ILogService, IBufferService, IOptionsService } from 'common/services/Services'; /** * Limit of the unwrapping line expansion (overscan) at the top and bottom @@ -45,7 +45,8 @@ export class Linkifier implements ILinkifier { constructor( protected readonly _bufferService: IBufferService, - private readonly _logService: ILogService + private readonly _logService: ILogService, + private readonly _optionsService: IOptionsService ) { this._rowsToLinkify = { start: undefined, @@ -277,8 +278,7 @@ export class Linkifier implements ILinkifier { if (!this._mouseZoneManager || !this._element) { return; } - - const width = getStringCellWidthV6(uri); // FIXME: apply options.unicodeVersion + const width = this._optionsService.options.unicodeVersion === '11' ? getStringCellWidthV11(uri) : getStringCellWidthV6(uri); const x1 = x % this._bufferService.cols; const y1 = y + Math.floor(x / this._bufferService.cols); let x2 = (x1 + width) % this._bufferService.cols; diff --git a/src/common/CharWidth.test.ts b/src/common/CharWidth.test.ts index 8a857424..ff6eb3a6 100644 --- a/src/common/CharWidth.test.ts +++ b/src/common/CharWidth.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { wcwidthV6 } from 'common/CharWidth'; +import { wcwidthV6, getStringCellWidthV6, getStringCellWidthV11 } from 'common/CharWidth'; it('wcwidth should match all values from the old implementation', function(): void { // old implementation @@ -180,3 +180,10 @@ it('wcwidth should match all values from the old implementation', function(): vo assert.equal(wcwidthV6(i), wcwidthOld(i), `mismatch for i: ${i}`); } }); + +it('wcwidth V6 vs. V11', () => { + const widthV6 = getStringCellWidthV6('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'); + assert.equal(widthV6, 10); + const widthV10 = getStringCellWidthV11('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'); + assert.equal(widthV10, 20); +}); diff --git a/src/common/CharWidth.ts b/src/common/CharWidth.ts index 852b99af..ca7cd2d1 100644 --- a/src/common/CharWidth.ts +++ b/src/common/CharWidth.ts @@ -159,7 +159,7 @@ export function getStringCellWidthV6(s: string): number { /** * Unicode version 10 */ -const BMP_COMBINING_V10 = [ +const BMP_COMBINING_V11 = [ [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], @@ -234,7 +234,7 @@ const BMP_COMBINING_V10 = [ [0xFFF9, 0xFFFB] ]; -const HIGH_COMBINING_V10 = [ +const HIGH_COMBINING_V11 = [ [0x101FD, 0x101FD], [0x102E0, 0x102E0], [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], @@ -277,7 +277,7 @@ const HIGH_COMBINING_V10 = [ [0xE0100, 0xE01EF] ]; -const BMP_WIDE_V10 = [ +const BMP_WIDE_V11 = [ [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], @@ -301,7 +301,7 @@ const BMP_WIDE_V10 = [ [0xFFE0, 0xFFE6] ]; -const HIGH_WIDE_V10 = [ +const HIGH_WIDE_V11 = [ [0x16FE0, 0x16FE3], [0x17000, 0x187F7], [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], @@ -322,27 +322,27 @@ const HIGH_WIDE_V10 = [ [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] ]; -const BMP_TABLE_V10 = new Uint8Array(65536); -fill(BMP_TABLE_V10, 1); -BMP_TABLE_V10[0] = 0; -fill(BMP_TABLE_V10, 0, 1, 32); -fill(BMP_TABLE_V10, 0, 0x7f, 0xa0); -for (let r = 0; r < BMP_COMBINING_V10.length; ++r) { - fill(BMP_TABLE_V10, 0, BMP_COMBINING_V10[r][0], BMP_COMBINING_V10[r][1] + 1); +const BMP_TABLE_V11 = new Uint8Array(65536); +fill(BMP_TABLE_V11, 1); +BMP_TABLE_V11[0] = 0; +fill(BMP_TABLE_V11, 0, 1, 32); +fill(BMP_TABLE_V11, 0, 0x7f, 0xa0); +for (let r = 0; r < BMP_COMBINING_V11.length; ++r) { + fill(BMP_TABLE_V11, 0, BMP_COMBINING_V11[r][0], BMP_COMBINING_V11[r][1] + 1); } -for (let r = 0; r < BMP_WIDE_V10.length; ++r) { - fill(BMP_TABLE_V10, 2, BMP_WIDE_V10[r][0], BMP_WIDE_V10[r][1] + 1); +for (let r = 0; r < BMP_WIDE_V11.length; ++r) { + fill(BMP_TABLE_V11, 2, BMP_WIDE_V11[r][0], BMP_WIDE_V11[r][1] + 1); } -export function wcwidthV10(num: number): number { +export function wcwidthV11(num: number): number { if (num < 32) return 0; if (num < 127) return 1; - if (num < 65536) return BMP_TABLE_V10[num]; - if (bisearch(num, HIGH_COMBINING_V10)) return 0; - if (bisearch(num, HIGH_WIDE_V10)) return 2; + if (num < 65536) return BMP_TABLE_V11[num]; + if (bisearch(num, HIGH_COMBINING_V11)) return 0; + if (bisearch(num, HIGH_WIDE_V11)) return 2; return 1; } -export function getStringCellWidthV10(s: string): number { - return getStringCellWidthPrivate(wcwidthV10, s); +export function getStringCellWidthV11(s: string): number { + return getStringCellWidthPrivate(wcwidthV11, s); } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 380bc6c4..b86c635e 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -212,7 +212,7 @@ export interface IPartialTerminalOptions { scrollSensitivity?: number; tabStopWidth?: number; theme?: ITheme; - unicodeVersion?: '6' | '10'; + unicodeVersion?: '6' | '11'; windowsMode?: boolean; wordSeparator?: string; } @@ -245,7 +245,7 @@ export interface ITerminalOptions { scrollSensitivity: number; tabStopWidth: number; theme: ITheme; - unicodeVersion: '6' | '10'; + unicodeVersion: '6' | '11'; windowsMode: boolean; wordSeparator: string; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index a086d030..4cd23544 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -198,6 +198,11 @@ declare module 'xterm' { */ theme?: ITheme; + /** + * Unicode version to be used for wcwidth. Default is version 6. + */ + unicodeVersion?: '6' | '11'; + /** * Whether "Windows mode" is enabled. Because Windows backends winpty and * conpty operate by doing line wrapping on their side, xterm.js does not From 435d480cf17b7addcd271c8f3a3c79ae6d6acbcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Tue, 26 Nov 2019 01:59:10 +0100 Subject: [PATCH 05/30] unicode service stub --- src/common/services/Services.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index b86c635e..28c5c370 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -280,3 +280,28 @@ export interface ITheme { brightCyan?: string; brightWhite?: string; } + +export const IUnicodeService = createDecorator('UnicodeService'); +export interface IUnicodeService { + /** Register an Unicode version provider. */ + registerVersion(provider: IUnicodeVersionProvider): void; + /** Registered Unicode versions. */ + readonly versions: string[]; + /** Currently active version. */ + readonly activeVersion: string; + /** Event triggered, when activate version changed. */ + readonly onChange: IEvent; + /** Event triggered, when a new version provider was registered. */ + readonly onRegister: IEvent; + + /** + * Unicode version dependent + */ + wcwidth(codepoint: number): number; + getStringCellWidth(s: string): number; +} + +export interface IUnicodeVersionProvider { + version: string; + wcwidth(ucs: number): number; +} From 3395ccb94330002837957fa4880cea847a0696da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 5 Dec 2019 15:42:55 +0100 Subject: [PATCH 06/30] UnicodeService & provider --- demo/client.ts | 2 +- src/InputHandler.test.ts | 20 +- src/InputHandler.ts | 14 +- src/Terminal.test.ts | 8 +- src/Terminal.ts | 17 +- src/browser/Linkifier.ts | 4 +- src/common/TestUtils.test.ts | 18 +- src/common/input/UnicodeV11.ts | 216 +++++++++++++++++++++ src/common/input/UnicodeV6.test.ts | 185 ++++++++++++++++++ src/common/input/UnicodeV6.ts | 129 ++++++++++++ src/common/services/Services.ts | 4 +- src/common/services/UnicodeService.test.ts | 50 +++++ src/common/services/UnicodeService.ts | 23 ++- 13 files changed, 648 insertions(+), 42 deletions(-) create mode 100644 src/common/input/UnicodeV11.ts create mode 100644 src/common/input/UnicodeV6.test.ts create mode 100644 src/common/input/UnicodeV6.ts create mode 100644 src/common/services/UnicodeService.test.ts diff --git a/demo/client.ts b/demo/client.ts index 347a9d65..9f1c6f59 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -257,7 +257,7 @@ function initOptions(term: TerminalType): void { logLevel: ['debug', 'info', 'warn', 'error', 'off'], rendererType: ['dom', 'canvas'], wordSeparator: null, - unicodeVersion: ['6', '11'] + unicodeVersion: ['6'] }; const options = Object.keys((term)._core.options); const booleanOptions = []; diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index d8defa1c..1aaeb739 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -13,7 +13,7 @@ import { CellData } from 'common/buffer/CellData'; import { Attributes } from 'common/buffer/Constants'; import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; -import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService, MockCoreMouseService } from 'common/TestUtils.test'; +import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService, MockCoreMouseService, MockUnicodeService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { clone } from 'common/Clone'; @@ -41,7 +41,7 @@ describe('InputHandler', () => { bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // Save cursor position inputHandler.saveCursor(); assert.equal(bufferService.buffer.x, 1); @@ -60,7 +60,7 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const optionsService = new MockOptionsService(); - const inputHandler = new InputHandler(new MockInputHandlingTerminal(), new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService()); + const inputHandler = new InputHandler(new MockInputHandlingTerminal(), new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService()); inputHandler.setCursorStyle(Params.fromArray([0])); assert.equal(optionsService.options['cursorStyle'], 'block'); @@ -101,7 +101,7 @@ describe('InputHandler', () => { it('should toggle Terminal.bracketedPasteMode', () => { const terminal = new MockInputHandlingTerminal(); terminal.bracketedPasteMode = false; - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // Set bracketed paste mode inputHandler.setModePrivate(Params.fromArray([2004])); assert.equal(terminal.bracketedPasteMode, true); @@ -120,7 +120,7 @@ describe('InputHandler', () => { it('insertChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -158,7 +158,7 @@ describe('InputHandler', () => { it('deleteChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -199,7 +199,7 @@ describe('InputHandler', () => { it('eraseInLine', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -228,7 +228,7 @@ describe('InputHandler', () => { it('eraseInDisplay', function(): void { const term = new Terminal({cols: 80, rows: 7}); const bufferService = new MockBufferService(80, 7); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -363,7 +363,7 @@ describe('InputHandler', () => { describe('print', () => { it('should not cause an infinite loop (regression test)', () => { const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -378,7 +378,7 @@ describe('InputHandler', () => { beforeEach(() => { term = new Terminal(); bufferService = new MockBufferService(80, 30); - handler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService()); + handler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index e439a7d9..db395313 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -7,7 +7,6 @@ import { IInputHandler, IInputHandlingTerminal } from './Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; -import { wcwidthV6, wcwidthV11 } from 'common/CharWidth'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; import { Disposable } from 'common/Lifecycle'; import { concat } from 'common/TypedArrayUtils'; @@ -19,7 +18,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; import { IAttributeData, IDisposable } from 'common/Types'; -import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService } from 'common/services/Services'; +import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, IUnicodeService } from 'common/services/Services'; import { OscHandler } from 'common/parser/OscParser'; import { DcsHandler } from 'common/parser/DcsParser'; @@ -127,7 +126,6 @@ export class InputHandler extends Disposable implements IInputHandler { private _stringDecoder: StringToUtf32 = new StringToUtf32(); private _utf8Decoder: Utf8ToUtf32 = new Utf8ToUtf32(); private _workCell: CellData = new CellData(); - private _wcwidth: (codepoint: number) => number; private _onCursorMove = new EventEmitter(); public get onCursorMove(): IEvent { return this._onCursorMove.event; } @@ -144,16 +142,10 @@ export class InputHandler extends Disposable implements IInputHandler { private readonly _logService: ILogService, private readonly _optionsService: IOptionsService, private readonly _coreMouseService: ICoreMouseService, + private readonly _unicodeService: IUnicodeService, private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { super(); - this._wcwidth = this._optionsService.options.unicodeVersion === '11' ? wcwidthV11 : wcwidthV6; - this._optionsService.onOptionChange(option => { - if (option === 'unicodeVersion') { - this._wcwidth = this._optionsService.options.unicodeVersion === '11' ? wcwidthV11 : wcwidthV6; - } - }); - this.register(this._parser); /** @@ -400,7 +392,7 @@ export class InputHandler extends Disposable implements IInputHandler { // calculate print space // expensive call, therefore we save width in line buffer - chWidth = this._wcwidth(code); + chWidth = this._unicodeService.wcwidth(code); // get charset replacement character // charset is only defined for ASCII, therefore we only diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 5f7f0721..266769ed 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -7,15 +7,17 @@ import { assert, expect } from 'chai'; import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from './TestUtils.test'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { wcwidthV6 } from 'common/CharWidth'; import { IBufferService } from 'common/services/Services'; import { Linkifier } from 'browser/Linkifier'; -import { MockLogService, MockOptionsService } from 'common/TestUtils.test'; +import { MockLogService, MockOptionsService, MockUnicodeService } from 'common/TestUtils.test'; import { IRegisteredLinkMatcher, IMouseZoneManager, IMouseZone } from 'browser/Types'; const INIT_COLS = 80; const INIT_ROWS = 24; +// grab wcwidth from mock unicode service (hardcoded to V6) +const wcwidth = (new MockUnicodeService()).wcwidth; + describe('Terminal', () => { let term: TestTerminal; const termOptions = { @@ -762,7 +764,7 @@ describe('Terminal', () => { for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.buffer.x = term.cols - 1; term.wraparoundMode = false; - const width = wcwidthV6((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000); + const width = wcwidth((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000); if (width !== 1) { continue; } diff --git a/src/Terminal.ts b/src/Terminal.ts index cbc01cd9..b2586df9 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -46,7 +46,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { handleWindowsModeLineFeed } from 'common/WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { IOptionsService, IBufferService, ICoreMouseService, ICoreService, ILogService, IDirtyRowService, IInstantiationService } from 'common/services/Services'; +import { IOptionsService, IBufferService, ICoreMouseService, ICoreService, ILogService, IDirtyRowService, IInstantiationService, IUnicodeService } from 'common/services/Services'; import { OptionsService } from 'common/services/OptionsService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; @@ -64,6 +64,7 @@ import { InstantiationService } from 'common/services/InstantiationService'; import { CoreMouseService } from 'common/services/CoreMouseService'; import { WriteBuffer } from 'common/input/WriteBuffer'; import { CoreBrowserService } from 'browser/services/CoreBrowserService'; +import { UnicodeService } from 'common/services/UnicodeService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -101,6 +102,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _instantiationService: IInstantiationService; private _logService: ILogService; public optionsService: IOptionsService; + private _unicodeService: IUnicodeService; // browser services private _charSizeService: ICharSizeService; @@ -228,6 +230,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._instantiationService.setService(ICoreMouseService, this._coreMouseService); this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); + this._unicodeService = this._instantiationService.createInstance(UnicodeService); + this._instantiationService.setService(IUnicodeService, this._unicodeService); this._setupOptionsListeners(); this._setup(); @@ -276,7 +280,16 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._userScrolling = false; // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService); + this._inputHandler = new InputHandler( + this, + this._bufferService, + this._coreService, + this._dirtyRowService, + this._logService, + this.optionsService, + this._coreMouseService, + this._unicodeService + ); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 072f7972..3dc048c7 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -5,7 +5,7 @@ import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types'; import { IBufferStringIteratorResult } from 'common/buffer/Types'; -import { getStringCellWidthV6, getStringCellWidthV11 } from 'common/CharWidth'; +import { getStringCellWidthV6 } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { ILogService, IBufferService, IOptionsService } from 'common/services/Services'; @@ -278,7 +278,7 @@ export class Linkifier implements ILinkifier { if (!this._mouseZoneManager || !this._element) { return; } - const width = this._optionsService.options.unicodeVersion === '11' ? getStringCellWidthV11(uri) : getStringCellWidthV6(uri); + const width = getStringCellWidthV6(uri); // FIXME: get cell length from buffer to avoid mismatch after version change const x1 = x % this._bufferService.cols; const y1 = y + Math.floor(x / this._bufferService.cols); let x2 = (x1 + width) % this._bufferService.cols; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 7e47d4b0..1d996a82 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -3,13 +3,14 @@ * @license MIT */ -import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions, IDirtyRowService, ICoreMouseService } from 'common/services/Services'; +import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions, IDirtyRowService, ICoreMouseService, IUnicodeService, IUnicodeVersionProvider } from 'common/services/Services'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { BufferSet } from 'common/buffer/BufferSet'; import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType } from 'common/Types'; +import { UnicodeV6 } from 'common/input/UnicodeV6'; export class MockBufferService implements IBufferService { serviceBrand: any; @@ -90,3 +91,18 @@ export class MockOptionsService implements IOptionsService { throw new Error('Method not implemented.'); } } + +// defaults to V6 always to keep tests passing +export class MockUnicodeService implements IUnicodeService { + private _provider = new UnicodeV6(); + register(provider: IUnicodeVersionProvider): void { + throw new Error('Method not implemented.'); + } + versions: string[] = []; + activeVersion: string = ''; + onChange: IEvent = new EventEmitter().event; + wcwidth = (codepoint: number) => this._provider.wcwidth(codepoint); + getStringCellWidth(s: string): number { + throw new Error('Method not implemented.'); + } +} diff --git a/src/common/input/UnicodeV11.ts b/src/common/input/UnicodeV11.ts new file mode 100644 index 00000000..6393e3f5 --- /dev/null +++ b/src/common/input/UnicodeV11.ts @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ +import { IUnicodeVersionProvider } from 'common/services/Services'; +import { fill } from 'common/TypedArrayUtils'; + +export class UnicodeV11 implements IUnicodeVersionProvider { + static bmpCombining = [ + [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], + [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], + [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], + [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], + [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], + [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], + [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], + [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], + [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], + [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], + [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], + [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], + [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], + [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], + [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], + [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], + [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], + [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], + [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], + [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], + [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], + [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], + [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], + [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], + [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], + [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], + [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], + [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], + [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], + [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], + [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], + [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], + [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], + [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], + [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], + [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], + [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], + [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], + [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], + [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], + [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], + [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], + [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], + [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], + [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], + [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], + [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], + [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], + [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], + [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], + [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], + [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], + [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], + [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], + [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], + [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], + [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], + [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], + [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], + [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], + [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], + [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], + [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], + [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], + [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], + [0xFFF9, 0xFFFB] + ]; + static highCombining = [ + [0x101FD, 0x101FD], [0x102E0, 0x102E0], + [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], + [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], + [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], + [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], + [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], + [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], + [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], + [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], + [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], + [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], + [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], + [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], + [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], + [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], + [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], + [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], + [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], + [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], + [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], + [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], + [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], + [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], + [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], + [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], + [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], + [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], + [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], + [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], + [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], + [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], + [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], + [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] + ]; + static bmpWide = [ + [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], + [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], + [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], + [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], + [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], + [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], + [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], + [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], + [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], + [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], + [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], + [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], + [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], + [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], + [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], + [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], + [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], + [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], + [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], + [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], + [0xFFE0, 0xFFE6] + ]; + static highWide = [ + [0x16FE0, 0x16FE3], [0x17000, 0x187F7], + [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], + [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], + [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], + [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], + [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], + [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], + [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], + [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], + [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], + [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], + [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], + [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], + [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], + [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], + [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], + [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], + [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] + ]; + static bmpTable: Uint8Array; + + static bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { + return false; + } + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + return false; + } + + public version = '11'; + + constructor() { + if (!UnicodeV11.bmpTable) { + UnicodeV11.bmpTable = new Uint8Array(65536); + fill(UnicodeV11.bmpTable, 1); + UnicodeV11.bmpTable[0] = 0; + fill(UnicodeV11.bmpTable, 0, 1, 32); + fill(UnicodeV11.bmpTable, 0, 0x7f, 0xa0); + for (let r = 0; r < UnicodeV11.bmpCombining.length; ++r) { + fill(UnicodeV11.bmpTable, 0, UnicodeV11.bmpCombining[r][0], UnicodeV11.bmpCombining[r][1] + 1); + } + for (let r = 0; r < UnicodeV11.bmpWide.length; ++r) { + fill(UnicodeV11.bmpTable, 2, UnicodeV11.bmpWide[r][0], UnicodeV11.bmpWide[r][1] + 1); + } + } + } + + public wcwidth(num: number): number { + if (num < 32) return 0; + if (num < 127) return 1; + if (num < 65536) return UnicodeV11.bmpTable[num]; + if (UnicodeV11.bisearch(num, UnicodeV11.highCombining)) return 0; + if (UnicodeV11.bisearch(num, UnicodeV11.highWide)) return 2; + return 1; + } +} diff --git a/src/common/input/UnicodeV6.test.ts b/src/common/input/UnicodeV6.test.ts new file mode 100644 index 00000000..4a44f15b --- /dev/null +++ b/src/common/input/UnicodeV6.test.ts @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { UnicodeV6 } from 'common/input/UnicodeV6'; + +it('wcwidth should match all values from the old implementation', function(): void { + // new implementation + const wcwidthNew = (new UnicodeV6()).wcwidth; + + // old implementation + const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number { + // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c + // combining characters + const COMBINING_BMP = [ + [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], + [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], + [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], + [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], + [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], + [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], + [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], + [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], + [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], + [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], + [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], + [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], + [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], + [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], + [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], + [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], + [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], + [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], + [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], + [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], + [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], + [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], + [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], + [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], + [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], + [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], + [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], + [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], + [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], + [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], + [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], + [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], + [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], + [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], + [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], + [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], + [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] + ]; + const COMBINING_HIGH = [ + [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], + [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] + ]; + // binary search + function bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { + return false; + } + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + return false; + } + function wcwidthBMP(ucs: number): number { + // test for 8-bit control characters + if (ucs === 0) { + return opts.nul; + } + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) { + return opts.control; + } + // binary search in table of non-spacing characters + if (bisearch(ucs, COMBINING_BMP)) { + return 0; + } + // if we arrive here, ucs is not a combining or C0/C1 control character + if (isWideBMP(ucs)) { + return 2; + } + return 1; + } + function isWideBMP(ucs: number): boolean { + return ( + ucs >= 0x1100 && ( + ucs <= 0x115f || // Hangul Jamo init. consonants + ucs === 0x2329 || + ucs === 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi + (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables + (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs + (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms + (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms + (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms + (ucs >= 0xffe0 && ucs <= 0xffe6))); + } + function wcwidthHigh(ucs: number): 0 | 1 | 2 { + if (bisearch(ucs, COMBINING_HIGH)) { + return 0; + } + if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) { + return 2; + } + return 1; + } + const control = opts.control | 0; + let table: number[] | Uint32Array | undefined; + function initTable(): number[] | Uint32Array { + // lookup table for BMP + const CODEPOINTS = 65536; // BMP holds 65536 codepoints + const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2 + const ITEMSIZE = 32; // using uint32_t + const CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE; + const CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH; + table = (typeof Uint32Array === 'undefined') + ? new Array(CONTAINERSIZE) + : new Uint32Array(CONTAINERSIZE); + for (let i = 0; i < CONTAINERSIZE; ++i) { + let num = 0; + let pos = CODEPOINTS_PER_ITEM; + while (pos--) { + num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos); + } + table[i] = num; + } + return table; + } + // get width from lookup table + // position in container : num / CODEPOINTS_PER_ITEM + // ==> n = table[Math.floor(num / 16)] + // ==> n = table[num >> 4] + // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 + // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH + // ==> m = (n % 16) * 2 + // ==> m = (num & 15) << 1 + // right shift to position m + // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 + // we are only interested in 2 LSBs, cut off higher bits + // ==> n = n & 3 e.g. 000000000000000000000000000000XX + return function (num: number): number { + num = num | 0; // get asm.js like optimization under V8 + if (num < 32) { + return control | 0; + } + if (num < 127) { + return 1; + } + const t = table || initTable(); + if (num < 65536) { + return t[num >> 4] >> ((num & 15) << 1) & 3; + } + // do a full search for high codepoints + return wcwidthHigh(num); + }; + })({nul: 0, control: 0}); // configurable options + + // test full BMP range old vs new implmenetation + for (let i = 0; i < 65536; ++i) { + assert.equal(wcwidthNew(i), wcwidthOld(i), `mismatch for i: ${i}`); + } +}); diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts new file mode 100644 index 00000000..56ee71fd --- /dev/null +++ b/src/common/input/UnicodeV6.ts @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ +import { IUnicodeVersionProvider } from 'common/services/Services'; +import { fill } from 'common/TypedArrayUtils'; + +export class UnicodeV6 implements IUnicodeVersionProvider { + static bmpCombining = [ + [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], + [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], + [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], + [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], + [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], + [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], + [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], + [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], + [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], + [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], + [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], + [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], + [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], + [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], + [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], + [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], + [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], + [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], + [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], + [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], + [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], + [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], + [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], + [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], + [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], + [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], + [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], + [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], + [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], + [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], + [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], + [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], + [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], + [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], + [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], + [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], + [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] + ]; + static highCombining = [ + [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], + [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] + ]; + static bmpTable: Uint8Array; + + static bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { + return false; + } + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + return false; + } + + public version = '6'; + + constructor() { + // init lookup table once + if (!UnicodeV6.bmpTable) { + UnicodeV6.bmpTable = new Uint8Array(65536); + fill(UnicodeV6.bmpTable, 1); + UnicodeV6.bmpTable[0] = 0; + // control chars + fill(UnicodeV6.bmpTable, 0, 1, 32); + fill(UnicodeV6.bmpTable, 0, 0x7f, 0xa0); + + // apply wide char rules first + // wide chars + fill(UnicodeV6.bmpTable, 2, 0x1100, 0x1160); + UnicodeV6.bmpTable[0x2329] = 2; + UnicodeV6.bmpTable[0x232a] = 2; + fill(UnicodeV6.bmpTable, 2, 0x2e80, 0xa4d0); + UnicodeV6.bmpTable[0x303f] = 1; // wrongly in last line + + fill(UnicodeV6.bmpTable, 2, 0xac00, 0xd7a4); + fill(UnicodeV6.bmpTable, 2, 0xf900, 0xfb00); + fill(UnicodeV6.bmpTable, 2, 0xfe10, 0xfe1a); + fill(UnicodeV6.bmpTable, 2, 0xfe30, 0xfe70); + fill(UnicodeV6.bmpTable, 2, 0xff00, 0xff61); + fill(UnicodeV6.bmpTable, 2, 0xffe0, 0xffe7); + + // apply combining last to ensure we overwrite + // wrongly wide set chars: + // the original algo evals combining first and falls + // through to wide check so we simply do here the opposite + // combining 0 + for (let r = 0; r < UnicodeV6.bmpCombining.length; ++r) { + fill(UnicodeV6.bmpTable, 0, UnicodeV6.bmpCombining[r][0], UnicodeV6.bmpCombining[r][1] + 1); + } + } + } + + public wcwidth(num: number): number { + if (num < 32) return 0; + if (num < 127) return 1; + if (num < 65536) return UnicodeV6.bmpTable[num]; + if (UnicodeV6.bisearch(num, UnicodeV6.highCombining)) return 0; + if ((num >= 0x20000 && num <= 0x2fffd) || (num >= 0x30000 && num <= 0x3fffd)) return 2; + return 1; + } +} diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 10df5924..554e5e96 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -285,15 +285,13 @@ export interface ITheme { export const IUnicodeService = createDecorator('UnicodeService'); export interface IUnicodeService { /** Register an Unicode version provider. */ - registerVersion(provider: IUnicodeVersionProvider): void; + register(provider: IUnicodeVersionProvider): void; /** Registered Unicode versions. */ readonly versions: string[]; /** Currently active version. */ readonly activeVersion: string; /** Event triggered, when activate version changed. */ readonly onChange: IEvent; - /** Event triggered, when a new version provider was registered. */ - readonly onRegister: IEvent; /** * Unicode version dependent diff --git a/src/common/services/UnicodeService.test.ts b/src/common/services/UnicodeService.test.ts new file mode 100644 index 00000000..7df72605 --- /dev/null +++ b/src/common/services/UnicodeService.test.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { UnicodeService } from 'common/services/UnicodeService'; +import { IUnicodeVersionProvider } from 'common/services/Services'; + +class DummyProvider implements IUnicodeVersionProvider { + version = '123'; + wcwidth(n: number): number { + return 123; + } +} + +describe('unicode provider', () => { + let us: UnicodeService; + beforeEach(() => { + us = new UnicodeService(); + }); + it('default to V6', () => { + assert.equal(us.activeVersion, '6'); + assert.deepEqual(us.versions, ['6']); + assert.doesNotThrow(() => { us.activeVersion = '6'; }, `unknown Unicode version "6"`); + assert.equal(us.getStringCellWidth('hello'), 5); + }); + it('activate should throw for unknown version', () => { + assert.throws(() => { us.activeVersion = '55'; }, 'unknown Unicode version "55"'); + }); + it('should notify about version change', () => { + const notes: string[] = []; + us.onChange(version => notes.push(version)); + const dummyProvider = new DummyProvider(); + us.register(dummyProvider); + us.activeVersion = dummyProvider.version; + assert.deepEqual(notes, [dummyProvider.version]); + }); + it('correctly changes provider impl', () => { + assert.equal(us.getStringCellWidth('hello'), 5); + const dummyProvider = new DummyProvider(); + us.register(dummyProvider); + us.activeVersion = dummyProvider.version; + assert.equal(us.getStringCellWidth('hello'), 123 * 5); + }); + it('wcwidth V6 emoji test', () => { + const widthV6 = us.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'); + assert.equal(widthV6, 10); + }); +}); diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index 35e256d9..14b9979f 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -4,16 +4,23 @@ */ import { IUnicodeService, IUnicodeVersionProvider } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { UnicodeV6 } from 'common/input/UnicodeV6'; export class UnicodeService implements IUnicodeService { private _providers: {[key: string]: IUnicodeVersionProvider} = Object.create(null); private _active: string = ''; - private _onRegister = new EventEmitter(); - public get onRegister(): IEvent { return this._onRegister.event; } + private _activeProvider: IUnicodeVersionProvider; private _onChange = new EventEmitter(); public get onChange(): IEvent { return this._onChange.event; } + constructor() { + const defaultProvider = new UnicodeV6(); + this.register(defaultProvider); + this._active = defaultProvider.version; + this._activeProvider = defaultProvider; + } + public get versions(): string[] { return Object.keys(this._providers); } @@ -27,22 +34,20 @@ export class UnicodeService implements IUnicodeService { throw new Error(`unknown Unicode version "${version}"`); } this._active = version; - this.wcwidth = (num: number) => this._providers[version].wcwidth(num); // perf an issue here? + this._activeProvider = this._providers[version]; this._onChange.fire(version); } - public registerVersion(provider: IUnicodeVersionProvider): void { + public register(provider: IUnicodeVersionProvider): void { this._providers[provider.version] = provider; - this._onRegister.fire(provider.version); - if (!this.activeVersion) { - this.activeVersion = provider.version; - } } /** * Unicode version dependent interface. */ - public wcwidth: (num: number) => number = () => { throw new Error('no Unicode version registered and activated'); } + public wcwidth(num: number): number { + return this._activeProvider.wcwidth(num); + } public getStringCellWidth(s: string): number { let result = 0; From 43303ba4c1c10ccb62fe0e0fdfa85807f7903325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 5 Dec 2019 21:30:59 +0100 Subject: [PATCH 07/30] add interfaces and option handling --- src/Terminal.ts | 5 ++++ src/common/input/UnicodeV11.ts | 4 +-- src/common/input/UnicodeV6.ts | 4 +-- src/common/services/Services.ts | 4 +-- src/common/services/UnicodeService.test.ts | 6 ++--- src/public/Terminal.ts | 23 +++++++++++++++- typings/xterm.d.ts | 31 ++++++++++++++++++++++ 7 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index b2586df9..5a655da7 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -391,6 +391,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._windowsMode = undefined; } break; + case 'unicodeVersion': + if (this._unicodeService.activeVersion !== this.optionsService.options.unicodeVersion) { + this._unicodeService.activeVersion = this.optionsService.options.unicodeVersion; + } + break; } }); } diff --git a/src/common/input/UnicodeV11.ts b/src/common/input/UnicodeV11.ts index 6393e3f5..b3ee56d3 100644 --- a/src/common/input/UnicodeV11.ts +++ b/src/common/input/UnicodeV11.ts @@ -205,10 +205,10 @@ export class UnicodeV11 implements IUnicodeVersionProvider { } } - public wcwidth(num: number): number { + public wcwidth(num: number): 0 | 1 | 2 { if (num < 32) return 0; if (num < 127) return 1; - if (num < 65536) return UnicodeV11.bmpTable[num]; + if (num < 65536) return UnicodeV11.bmpTable[num] as 0 | 1 | 2; if (UnicodeV11.bisearch(num, UnicodeV11.highCombining)) return 0; if (UnicodeV11.bisearch(num, UnicodeV11.highWide)) return 2; return 1; diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index 56ee71fd..af5089b2 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -118,10 +118,10 @@ export class UnicodeV6 implements IUnicodeVersionProvider { } } - public wcwidth(num: number): number { + public wcwidth(num: number): 0 | 1 | 2 { if (num < 32) return 0; if (num < 127) return 1; - if (num < 65536) return UnicodeV6.bmpTable[num]; + if (num < 65536) return UnicodeV6.bmpTable[num] as 0 | 1 | 2; if (UnicodeV6.bisearch(num, UnicodeV6.highCombining)) return 0; if ((num >= 0x20000 && num <= 0x2fffd) || (num >= 0x30000 && num <= 0x3fffd)) return 2; return 1; diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 554e5e96..9ee348fb 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -289,7 +289,7 @@ export interface IUnicodeService { /** Registered Unicode versions. */ readonly versions: string[]; /** Currently active version. */ - readonly activeVersion: string; + activeVersion: string; /** Event triggered, when activate version changed. */ readonly onChange: IEvent; @@ -302,5 +302,5 @@ export interface IUnicodeService { export interface IUnicodeVersionProvider { version: string; - wcwidth(ucs: number): number; + wcwidth(ucs: number): 0 | 1 | 2; } diff --git a/src/common/services/UnicodeService.test.ts b/src/common/services/UnicodeService.test.ts index 7df72605..cb03b09b 100644 --- a/src/common/services/UnicodeService.test.ts +++ b/src/common/services/UnicodeService.test.ts @@ -9,8 +9,8 @@ import { IUnicodeVersionProvider } from 'common/services/Services'; class DummyProvider implements IUnicodeVersionProvider { version = '123'; - wcwidth(n: number): number { - return 123; + wcwidth(n: number): 0 | 1 | 2 { + return 2; } } @@ -41,7 +41,7 @@ describe('unicode provider', () => { const dummyProvider = new DummyProvider(); us.register(dummyProvider); us.activeVersion = dummyProvider.version; - assert.equal(us.getStringCellWidth('hello'), 123 * 5); + assert.equal(us.getStringCellWidth('hello'), 2 * 5); }); it('wcwidth V6 emoji test', () => { const widthV6 = us.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'); diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 397898c7..75c35e84 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier } from 'xterm'; +import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier, IUnicodeHandling, IUnicodeVersionProvider } from 'xterm'; import { ITerminal } from '../Types'; import { IBufferLine } from 'common/Types'; import { IBuffer } from 'common/buffer/Types'; @@ -41,6 +41,9 @@ export class Terminal implements ITerminalApi { } return this._parser; } + public get unicode(): IUnicodeHandling { + return new UnicodeApi(this._core); + } public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; } public get rows(): number { return this._core.rows; } public get cols(): number { return this._core.cols; } @@ -240,3 +243,21 @@ class ParserApi implements IParser { return this._core.addOscHandler(ident, callback); } } + +class UnicodeApi implements IUnicodeHandling { + constructor(private _core: ITerminal) {} + + public register(provider: IUnicodeVersionProvider): void {} + + public get versions(): string[] { + return (this._core as any)._unicodeService.versions; + } + + public get activeVersion(): string { + return (this._core as any)._unicodeService.activeVersion; + } + + public set activeVersion(version: string) { + (this._core as any)._unicodeService.activeVersion = version; + } +} diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index a00bdcca..a1ecbbff 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -426,6 +426,12 @@ declare module 'xterm' { */ readonly parser: IParser; + /** + * (EXPERIMENTAL) Get the Unicode handling interface + * to register and switch Unicode version. + */ + readonly unicode: IUnicodeHandling; + /** * Natural language strings that can be localized. */ @@ -1130,4 +1136,29 @@ declare module 'xterm' { */ addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; } + + /** + * (EXPERIMENTAL) Unicode version provider. + * Used to register custom Unicode versions with `Terminal.unicode.register`. + */ + export interface IUnicodeVersionProvider { + /** String indicating the Unicode version provided. */ + version: string; + /** Unicode version dependent wcwidth implementation. */ + wcwidth(ucs: number): 0 | 1 | 2; + } + + /** + * (EXPERIMENTAL) Unicode handling interface. + */ + export interface IUnicodeHandling { + /** + * Register a custom Unicode version provider. + */ + register(provider: IUnicodeVersionProvider): void; + /** + * Registered Unicode versions. + */ + versions: string[]; + } } From 303abf8a9133d0a3754c61cdf60c839940cdd8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 6 Dec 2019 16:28:16 +0100 Subject: [PATCH 08/30] unicode11 addon --- addons/xterm-addon-unicode11/.gitignore | 2 + addons/xterm-addon-unicode11/.npmignore | 5 + addons/xterm-addon-unicode11/LICENSE | 19 ++ addons/xterm-addon-unicode11/README.md | 20 ++ addons/xterm-addon-unicode11/package.json | 21 ++ .../src/Unicode11Addon.api.ts | 61 ++++ .../src/Unicode11Addon.ts | 267 ++++++++++++++++++ .../xterm-addon-unicode11/src/tsconfig.json | 19 ++ .../typings/xterm-addon-unicode11.d.ts | 14 + .../xterm-addon-unicode11/webpack.config.js | 31 ++ demo/client.ts | 15 +- src/public/Terminal.ts | 4 +- tsconfig.all.json | 3 +- 13 files changed, 476 insertions(+), 5 deletions(-) create mode 100644 addons/xterm-addon-unicode11/.gitignore create mode 100644 addons/xterm-addon-unicode11/.npmignore create mode 100644 addons/xterm-addon-unicode11/LICENSE create mode 100644 addons/xterm-addon-unicode11/README.md create mode 100644 addons/xterm-addon-unicode11/package.json create mode 100644 addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts create mode 100644 addons/xterm-addon-unicode11/src/Unicode11Addon.ts create mode 100644 addons/xterm-addon-unicode11/src/tsconfig.json create mode 100644 addons/xterm-addon-unicode11/typings/xterm-addon-unicode11.d.ts create mode 100644 addons/xterm-addon-unicode11/webpack.config.js diff --git a/addons/xterm-addon-unicode11/.gitignore b/addons/xterm-addon-unicode11/.gitignore new file mode 100644 index 00000000..3063f07d --- /dev/null +++ b/addons/xterm-addon-unicode11/.gitignore @@ -0,0 +1,2 @@ +lib +node_modules diff --git a/addons/xterm-addon-unicode11/.npmignore b/addons/xterm-addon-unicode11/.npmignore new file mode 100644 index 00000000..1c794445 --- /dev/null +++ b/addons/xterm-addon-unicode11/.npmignore @@ -0,0 +1,5 @@ +**/*.api.js +**/*.api.ts +tsconfig.json +.yarnrc +webpack.config.js diff --git a/addons/xterm-addon-unicode11/LICENSE b/addons/xterm-addon-unicode11/LICENSE new file mode 100644 index 00000000..8f178925 --- /dev/null +++ b/addons/xterm-addon-unicode11/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/addons/xterm-addon-unicode11/README.md b/addons/xterm-addon-unicode11/README.md new file mode 100644 index 00000000..eff46b39 --- /dev/null +++ b/addons/xterm-addon-unicode11/README.md @@ -0,0 +1,20 @@ +## xterm-addon-unicode11 + +An addon providing Unicode version 11 rules for xterm.js. + +### Install + +```bash +npm install --save xterm-addon-unicode11 +``` + +### Usage + +```ts +import { Terminal } from 'xterm'; +import { Unicode11Addon } from 'xterm-addon-unicode11'; + +const terminal = new Terminal(); +const unicode11Addon = new Unicode11Addon(); +terminal.loadAddon(unicode11Addon); +``` diff --git a/addons/xterm-addon-unicode11/package.json b/addons/xterm-addon-unicode11/package.json new file mode 100644 index 00000000..0aaa4a1d --- /dev/null +++ b/addons/xterm-addon-unicode11/package.json @@ -0,0 +1,21 @@ +{ + "name": "xterm-addon-unicode11", + "version": "0.1.0", + "author": { + "name": "The xterm.js authors", + "url": "https://xtermjs.org/" + }, + "main": "lib/xterm-addon-unicode11.js", + "types": "typings/xterm-addon-unicode11.d.ts", + "repository": "https://github.com/xtermjs/xterm.js", + "license": "MIT", + "scripts": { + "build": "../../node_modules/.bin/tsc -p src", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package" + }, + "peerDependencies": { + "xterm": "^4.0.0" + } +} diff --git a/addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts b/addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts new file mode 100644 index 00000000..a731e7bb --- /dev/null +++ b/addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import * as puppeteer from 'puppeteer'; +import { ITerminalOptions } from 'xterm'; +import { assert } from 'chai'; + +const APP = 'http://127.0.0.1:3000/test'; + +let browser: puppeteer.Browser; +let page: puppeteer.Page; +const width = 800; +const height = 600; + +describe('Unicode11Addon', () => { + before(async function(): Promise { + this.timeout(20000); + browser = await puppeteer.launch({ + headless: process.argv.indexOf('--headless') !== -1, + args: [`--window-size=${width},${height}`, `--no-sandbox`] + }); + page = (await browser.pages())[0]; + await page.setViewport({ width, height }); + }); + + after(async () => { + await browser.close(); + }); + + beforeEach(async function(): Promise { + this.timeout(20000); + await page.goto(APP); + await openTerminal(); + }); + + it('wcwidth V11 emoji test', async () => { + await page.evaluate(` + window.unicode11 = new Unicode11Addon(); + window.term.loadAddon(window.unicode11); + `); + // should have loaded '11' + assert.deepEqual(await page.evaluate(`window.term.unicode.versions`), ['6', '11']); + // switch should not throw + await page.evaluate(`window.term.unicode.activeVersion = '11';`); + assert.deepEqual(await page.evaluate(`window.term.unicode.activeVersion`), '11'); + // v6: 10, V11: 20 + assert.deepEqual(await page.evaluate(`window.term._core._unicodeService.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣')`), 20); + }); +}); + +async function openTerminal(options: ITerminalOptions = {}): Promise { + await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`); + await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); + if (options.rendererType === 'dom') { + await page.waitForSelector('.xterm-rows'); + } else { + await page.waitForSelector('.xterm-text-layer'); + } +} diff --git a/addons/xterm-addon-unicode11/src/Unicode11Addon.ts b/addons/xterm-addon-unicode11/src/Unicode11Addon.ts new file mode 100644 index 00000000..926b5ad9 --- /dev/null +++ b/addons/xterm-addon-unicode11/src/Unicode11Addon.ts @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + * + * UnicodeVersionProvider for V11. + */ + +import { Terminal, ITerminalAddon, IUnicodeVersionProvider } from 'xterm'; + + +type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray + | Int8Array | Int16Array | Int32Array + | Float32Array | Float64Array; + + +/** + * polyfill for TypedArray.fill + * This is needed to support .fill in all safari versions and IE 11. + */ +function fill(array: T, value: number, start?: number, end?: number): T { + // all modern engines that support .fill + if (array.fill) { + return array.fill(value, start, end) as T; + } + return fillFallback(array, value, start, end); +} + +function fillFallback(array: T, value: number, start: number = 0, end: number = array.length): T { + // safari and IE 11 + // since IE 11 does not support Array.prototype.fill either + // we cannot use the suggested polyfill from MDN + // instead we simply fall back to looping + if (start >= array.length) { + return array; + } + start = (array.length + start) % array.length; + if (end >= array.length) { + end = array.length; + } else { + end = (array.length + end) % array.length; + } + for (let i = start; i < end; ++i) { + array[i] = value; + } + return array; +} + +class UnicodeV11 implements IUnicodeVersionProvider { + static bmpCombining = [ + [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], + [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], + [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], + [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], + [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], + [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], + [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], + [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], + [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], + [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], + [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], + [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], + [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], + [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], + [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], + [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], + [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], + [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], + [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], + [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], + [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], + [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], + [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], + [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], + [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], + [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], + [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], + [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], + [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], + [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], + [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], + [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], + [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], + [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], + [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], + [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], + [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], + [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], + [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], + [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], + [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], + [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], + [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], + [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], + [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], + [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], + [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], + [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], + [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], + [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], + [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], + [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], + [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], + [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], + [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], + [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], + [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], + [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], + [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], + [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], + [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], + [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], + [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], + [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], + [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], + [0xFFF9, 0xFFFB] + ]; + static highCombining = [ + [0x101FD, 0x101FD], [0x102E0, 0x102E0], + [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], + [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], + [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], + [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], + [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], + [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], + [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], + [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], + [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], + [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], + [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], + [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], + [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], + [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], + [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], + [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], + [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], + [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], + [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], + [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], + [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], + [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], + [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], + [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], + [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], + [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], + [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], + [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], + [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], + [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], + [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], + [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] + ]; + static bmpWide = [ + [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], + [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], + [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], + [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], + [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], + [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], + [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], + [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], + [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], + [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], + [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], + [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], + [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], + [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], + [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], + [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], + [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], + [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], + [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], + [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], + [0xFFE0, 0xFFE6] + ]; + static highWide = [ + [0x16FE0, 0x16FE3], [0x17000, 0x187F7], + [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], + [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], + [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], + [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], + [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], + [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], + [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], + [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], + [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], + [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], + [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], + [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], + [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], + [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], + [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], + [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], + [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] + ]; + static bmpTable: Uint8Array; + + static bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { + return false; + } + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + return false; + } + + public version = '11'; + + constructor() { + if (!UnicodeV11.bmpTable) { + UnicodeV11.bmpTable = new Uint8Array(65536); + fill(UnicodeV11.bmpTable, 1); + UnicodeV11.bmpTable[0] = 0; + fill(UnicodeV11.bmpTable, 0, 1, 32); + fill(UnicodeV11.bmpTable, 0, 0x7f, 0xa0); + for (let r = 0; r < UnicodeV11.bmpCombining.length; ++r) { + fill(UnicodeV11.bmpTable, 0, UnicodeV11.bmpCombining[r][0], UnicodeV11.bmpCombining[r][1] + 1); + } + for (let r = 0; r < UnicodeV11.bmpWide.length; ++r) { + fill(UnicodeV11.bmpTable, 2, UnicodeV11.bmpWide[r][0], UnicodeV11.bmpWide[r][1] + 1); + } + } + } + + public wcwidth(num: number): 0 | 1 | 2 { + if (num < 32) return 0; + if (num < 127) return 1; + if (num < 65536) return UnicodeV11.bmpTable[num] as 0 | 1 | 2; + if (UnicodeV11.bisearch(num, UnicodeV11.highCombining)) return 0; + if (UnicodeV11.bisearch(num, UnicodeV11.highWide)) return 2; + return 1; + } +} + + +export class Unicode11Addon implements ITerminalAddon { + public activate(terminal: Terminal): void { + console.log('got we loaded?'); + (window as any)._v11 = new UnicodeV11(); + terminal.unicode.register(new UnicodeV11()); + } + + public dispose(): void { } +} diff --git a/addons/xterm-addon-unicode11/src/tsconfig.json b/addons/xterm-addon-unicode11/src/tsconfig.json new file mode 100644 index 00000000..d875aa53 --- /dev/null +++ b/addons/xterm-addon-unicode11/src/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2015", + "lib": [ + "dom", + "es2015" + ], + "rootDir": ".", + "outDir": "../out", + "sourceMap": true, + "removeComments": true, + "strict": true + }, + "include": [ + "./**/*", + "../../../typings/xterm.d.ts" + ] +} diff --git a/addons/xterm-addon-unicode11/typings/xterm-addon-unicode11.d.ts b/addons/xterm-addon-unicode11/typings/xterm-addon-unicode11.d.ts new file mode 100644 index 00000000..1d0dce1b --- /dev/null +++ b/addons/xterm-addon-unicode11/typings/xterm-addon-unicode11.d.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { Terminal, ITerminalAddon } from 'xterm'; + +declare module 'xterm-addon-unicode11' { + export class Unicode11Addon implements ITerminalAddon { + constructor(); + public activate(terminal: Terminal): void; + public dispose(): void; + } +} diff --git a/addons/xterm-addon-unicode11/webpack.config.js b/addons/xterm-addon-unicode11/webpack.config.js new file mode 100644 index 00000000..07e9de29 --- /dev/null +++ b/addons/xterm-addon-unicode11/webpack.config.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const path = require('path'); + +const addonName = 'Unicode11Addon'; +const mainFile = 'xterm-addon-unicode11.js'; + +module.exports = { + entry: `./out/${addonName}.js`, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre", + exclude: /node_modules/ + } + ] + }, + output: { + filename: mainFile, + path: path.resolve('./lib'), + library: addonName, + libraryTarget: 'umd' + }, + mode: 'production' +}; diff --git a/demo/client.ts b/demo/client.ts index 9f1c6f59..47828d0f 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -14,6 +14,7 @@ import { FitAddon } from '../addons/xterm-addon-fit/out/FitAddon'; import { SearchAddon, ISearchOptions } from '../addons/xterm-addon-search/out/SearchAddon'; import { WebLinksAddon } from '../addons/xterm-addon-web-links/out/WebLinksAddon'; import { WebglAddon } from '../addons/xterm-addon-webgl/out/WebglAddon'; +import { Unicode11Addon } from '../addons/xterm-addon-unicode11/out/Unicode11Addon'; // Use webpacked version (yarn package) // import { Terminal } from '../lib/xterm'; @@ -22,6 +23,7 @@ import { WebglAddon } from '../addons/xterm-addon-webgl/out/WebglAddon'; // import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; // import { WebLinksAddon } from 'xterm-addon-web-links'; // import { WebglAddon } from 'xterm-addon-webgl'; +// import { Unicode11Addon } from 'xterm-addon-unicode11'; // Pulling in the module's types relies on the above, it's looks a // little weird here as we're importing "this" module @@ -35,6 +37,7 @@ export interface IWindowWithTerminal extends Window { SearchAddon?: typeof SearchAddon; WebLinksAddon?: typeof WebLinksAddon; WebglAddon?: typeof WebglAddon; + Unicode11Addon?: typeof Unicode11Addon; } declare let window: IWindowWithTerminal; @@ -44,7 +47,7 @@ let socketURL; let socket; let pid; -type AddonType = 'attach' | 'fit' | 'search' | 'web-links' | 'webgl'; +type AddonType = 'attach' | 'fit' | 'search' | 'web-links' | 'webgl' | 'unicode11'; interface IDemoAddon { name: T; @@ -54,6 +57,7 @@ interface IDemoAddon { T extends 'fit' ? typeof FitAddon : T extends 'search' ? typeof SearchAddon : T extends 'web-links' ? typeof WebLinksAddon : + T extends 'unicode11' ? typeof Unicode11Addon : typeof WebglAddon; instance?: T extends 'attach' ? AttachAddon : @@ -61,6 +65,7 @@ interface IDemoAddon { T extends 'search' ? SearchAddon : T extends 'web-links' ? WebLinksAddon : T extends 'webgl' ? WebglAddon : + T extends 'unicode11' ? typeof Unicode11Addon : never; } @@ -69,7 +74,8 @@ const addons: { [T in AddonType]: IDemoAddon} = { fit: { name: 'fit', ctor: FitAddon, canChange: false }, search: { name: 'search', ctor: SearchAddon, canChange: true }, 'web-links': { name: 'web-links', ctor: WebLinksAddon, canChange: true }, - webgl: { name: 'webgl', ctor: WebglAddon, canChange: true } + webgl: { name: 'webgl', ctor: WebglAddon, canChange: true }, + unicode11: { name: 'unicode11', ctor: Unicode11Addon, canChange: false } }; const terminalContainer = document.getElementById('terminal-container'); @@ -115,6 +121,7 @@ if (document.location.pathname === '/test') { window.SearchAddon = SearchAddon; window.WebLinksAddon = WebLinksAddon; window.WebglAddon = WebglAddon; + window.Unicode11Addon = Unicode11Addon; } else { createTerminal(); document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler); @@ -136,9 +143,11 @@ function createTerminal(): void { addons['web-links'].instance = new WebLinksAddon(); addons.search.instance = new SearchAddon(); addons.fit.instance = new FitAddon(); + addons.unicode11.instance = new Unicode11Addon(); typedTerm.loadAddon(addons['web-links'].instance); typedTerm.loadAddon(addons.search.instance); typedTerm.loadAddon(addons.fit.instance); + typedTerm.loadAddon(addons.unicode11.instance); window.term = term; // Expose `term` to window for debugging purposes term.onResize((size: { cols: number, rows: number }) => { @@ -257,7 +266,7 @@ function initOptions(term: TerminalType): void { logLevel: ['debug', 'info', 'warn', 'error', 'off'], rendererType: ['dom', 'canvas'], wordSeparator: null, - unicodeVersion: ['6'] + unicodeVersion: ['6', '11'] }; const options = Object.keys((term)._core.options); const booleanOptions = []; diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 75c35e84..74ec2fcd 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -247,7 +247,9 @@ class ParserApi implements IParser { class UnicodeApi implements IUnicodeHandling { constructor(private _core: ITerminal) {} - public register(provider: IUnicodeVersionProvider): void {} + public register(provider: IUnicodeVersionProvider): void { + (this._core as any)._unicodeService.register(provider); + } public get versions(): string[] { return (this._core as any)._unicodeService.versions; diff --git a/tsconfig.all.json b/tsconfig.all.json index 7ddf33d5..efd968f3 100644 --- a/tsconfig.all.json +++ b/tsconfig.all.json @@ -9,6 +9,7 @@ { "path": "./addons/xterm-addon-fit/src" }, { "path": "./addons/xterm-addon-search/src" }, { "path": "./addons/xterm-addon-web-links/src" }, - { "path": "./addons/xterm-addon-webgl/src" } + { "path": "./addons/xterm-addon-webgl/src" }, + { "path": "./addons/xterm-addon-unicode11/src" } ] } From 794573969c52c734f73c7ed93df3c05a6b1da839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 6 Dec 2019 16:44:20 +0100 Subject: [PATCH 09/30] remove CharWidth.ts --- src/Terminal.test.ts | 2 +- src/Terminal.ts | 2 +- src/browser/Linkifier.test.ts | 4 +- src/browser/Linkifier.ts | 9 +- src/common/CharWidth.test.ts | 189 ------------------ src/common/CharWidth.ts | 348 ---------------------------------- 6 files changed, 9 insertions(+), 545 deletions(-) delete mode 100644 src/common/CharWidth.test.ts delete mode 100644 src/common/CharWidth.ts diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 266769ed..422394e4 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -1373,7 +1373,7 @@ describe('Terminal', () => { class TestLinkifier extends Linkifier { constructor(bufferService: IBufferService) { - super(bufferService, new MockLogService(), new MockOptionsService()); + super(bufferService, new MockLogService(), new MockOptionsService(), new MockUnicodeService()); Linkifier._timeBeforeLatency = 0; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 5a655da7..ca075fa8 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -294,7 +294,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); - this.linkifier = this.linkifier || new Linkifier(this._bufferService, this._logService, this.optionsService); + this.linkifier = this.linkifier || new Linkifier(this._bufferService, this._logService, this.optionsService, this._unicodeService); if (this.options.windowsMode) { this._enableWindowsMode(); diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index a4b4d2f5..9f155d52 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -9,12 +9,12 @@ import { IBufferLine } from 'common/Types'; import { Linkifier } from 'browser/Linkifier'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; +import { MockLogService, MockBufferService, MockOptionsService, MockUnicodeService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; class TestLinkifier extends Linkifier { constructor(bufferService: IBufferService) { - super(bufferService, new MockLogService(), new MockOptionsService()); + super(bufferService, new MockLogService(), new MockOptionsService(), new MockUnicodeService()); Linkifier._timeBeforeLatency = 0; } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index 3dc048c7..30c76e8a 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -5,9 +5,8 @@ import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types'; import { IBufferStringIteratorResult } from 'common/buffer/Types'; -import { getStringCellWidthV6 } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { ILogService, IBufferService, IOptionsService } from 'common/services/Services'; +import { ILogService, IBufferService, IOptionsService, IUnicodeService } from 'common/services/Services'; /** * Limit of the unwrapping line expansion (overscan) at the top and bottom @@ -46,7 +45,8 @@ export class Linkifier implements ILinkifier { constructor( protected readonly _bufferService: IBufferService, private readonly _logService: ILogService, - private readonly _optionsService: IOptionsService + private readonly _optionsService: IOptionsService, + private readonly _unicodeService: IUnicodeService ) { this._rowsToLinkify = { start: undefined, @@ -278,7 +278,8 @@ export class Linkifier implements ILinkifier { if (!this._mouseZoneManager || !this._element) { return; } - const width = getStringCellWidthV6(uri); // FIXME: get cell length from buffer to avoid mismatch after version change + // FIXME: get cell length from buffer to avoid mismatch after Unicode version change + const width = this._unicodeService.getStringCellWidth(uri); const x1 = x % this._bufferService.cols; const y1 = y + Math.floor(x / this._bufferService.cols); let x2 = (x1 + width) % this._bufferService.cols; diff --git a/src/common/CharWidth.test.ts b/src/common/CharWidth.test.ts deleted file mode 100644 index ff6eb3a6..00000000 --- a/src/common/CharWidth.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { assert } from 'chai'; -import { wcwidthV6, getStringCellWidthV6, getStringCellWidthV11 } from 'common/CharWidth'; - -it('wcwidth should match all values from the old implementation', function(): void { - // old implementation - const wcwidthOld = (function(opts: {nul: number, control: number}): (ucs: number) => number { - // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c - // combining characters - const COMBINING_BMP = [ - [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], - [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], - [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], - [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], - [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], - [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], - [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], - [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], - [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], - [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], - [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], - [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], - [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], - [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], - [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], - [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], - [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], - [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], - [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], - [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], - [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], - [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], - [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], - [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], - [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], - [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], - [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], - [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], - [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], - [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], - [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], - [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], - [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], - [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], - [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], - [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], - [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] - ]; - const COMBINING_HIGH = [ - [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], - [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] - ]; - // binary search - function bisearch(ucs: number, data: number[][]): boolean { - let min = 0; - let max = data.length - 1; - let mid; - if (ucs < data[0][0] || ucs > data[max][1]) { - return false; - } - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) { - min = mid + 1; - } else if (ucs < data[mid][0]) { - max = mid - 1; - } else { - return true; - } - } - return false; - } - function wcwidthBMP(ucs: number): number { - // test for 8-bit control characters - if (ucs === 0) { - return opts.nul; - } - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) { - return opts.control; - } - // binary search in table of non-spacing characters - if (bisearch(ucs, COMBINING_BMP)) { - return 0; - } - // if we arrive here, ucs is not a combining or C0/C1 control character - if (isWideBMP(ucs)) { - return 2; - } - return 1; - } - function isWideBMP(ucs: number): boolean { - return ( - ucs >= 0x1100 && ( - ucs <= 0x115f || // Hangul Jamo init. consonants - ucs === 0x2329 || - ucs === 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi - (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables - (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs - (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms - (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms - (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms - (ucs >= 0xffe0 && ucs <= 0xffe6))); - } - function wcwidthHigh(ucs: number): 0 | 1 | 2 { - if (bisearch(ucs, COMBINING_HIGH)) { - return 0; - } - if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) { - return 2; - } - return 1; - } - const control = opts.control | 0; - let table: number[] | Uint32Array | undefined; - function initTable(): number[] | Uint32Array { - // lookup table for BMP - const CODEPOINTS = 65536; // BMP holds 65536 codepoints - const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2 - const ITEMSIZE = 32; // using uint32_t - const CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE; - const CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH; - table = (typeof Uint32Array === 'undefined') - ? new Array(CONTAINERSIZE) - : new Uint32Array(CONTAINERSIZE); - for (let i = 0; i < CONTAINERSIZE; ++i) { - let num = 0; - let pos = CODEPOINTS_PER_ITEM; - while (pos--) { - num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos); - } - table[i] = num; - } - return table; - } - // get width from lookup table - // position in container : num / CODEPOINTS_PER_ITEM - // ==> n = table[Math.floor(num / 16)] - // ==> n = table[num >> 4] - // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 - // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH - // ==> m = (n % 16) * 2 - // ==> m = (num & 15) << 1 - // right shift to position m - // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 - // we are only interested in 2 LSBs, cut off higher bits - // ==> n = n & 3 e.g. 000000000000000000000000000000XX - return function (num: number): number { - num = num | 0; // get asm.js like optimization under V8 - if (num < 32) { - return control | 0; - } - if (num < 127) { - return 1; - } - const t = table || initTable(); - if (num < 65536) { - return t[num >> 4] >> ((num & 15) << 1) & 3; - } - // do a full search for high codepoints - return wcwidthHigh(num); - }; - })({nul: 0, control: 0}); // configurable options - - // test full BMP range old vs new implmenetation - for (let i = 0; i < 65536; ++i) { - assert.equal(wcwidthV6(i), wcwidthOld(i), `mismatch for i: ${i}`); - } -}); - -it('wcwidth V6 vs. V11', () => { - const widthV6 = getStringCellWidthV6('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'); - assert.equal(widthV6, 10); - const widthV10 = getStringCellWidthV11('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'); - assert.equal(widthV10, 20); -}); diff --git a/src/common/CharWidth.ts b/src/common/CharWidth.ts deleted file mode 100644 index ca7cd2d1..00000000 --- a/src/common/CharWidth.ts +++ /dev/null @@ -1,348 +0,0 @@ -/** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { fill } from 'common/TypedArrayUtils'; - -// helper: binary search -function bisearch(ucs: number, data: number[][]): boolean { - let min = 0; - let max = data.length - 1; - let mid; - if (ucs < data[0][0] || ucs > data[max][1]) { - return false; - } - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) { - min = mid + 1; - } else if (ucs < data[mid][0]) { - max = mid - 1; - } else { - return true; - } - } - return false; -} - -function getStringCellWidthPrivate(wcwidthImpl: (cp: number) => number, s: string): number { - let result = 0; - const length = s.length; - for (let i = 0; i < length; ++i) { - let code = s.charCodeAt(i); - // surrogate pair first - if (0xD800 <= code && code <= 0xDBFF) { - if (++i >= length) { - // this should not happen with strings retrieved from - // Buffer.translateToString as it converts from UTF-32 - // and therefore always should contain the second part - // for any other string we still have to handle it somehow: - // simply treat the lonely surrogate first as a single char (UCS-2 behavior) - return result + wcwidthImpl(code); - } - const second = s.charCodeAt(i); - // convert surrogate pair to high codepoint only for valid second part (UTF-16) - // otherwise treat them independently (UCS-2 behavior) - if (0xDC00 <= second && second <= 0xDFFF) { - code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; - } else { - result += wcwidthImpl(second); - } - } - result += wcwidthImpl(code); - } - return result; -} - -/** - * Unicode version 6 - */ -const BMP_COMBINING_V6 = [ - [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], - [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], - [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], - [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], - [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], - [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], - [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], - [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], - [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], - [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], - [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], - [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], - [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], - [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], - [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], - [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], - [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], - [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], - [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], - [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], - [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], - [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], - [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], - [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], - [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], - [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], - [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], - [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], - [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], - [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], - [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], - [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], - [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], - [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], - [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], - [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], - [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] -]; -const HIGH_COMBINING_V6 = [ - [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], - [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] -]; - -// create lookup table for BMP plane -const BMP_TABLE_V6 = new Uint8Array(65536); -fill(BMP_TABLE_V6, 1); -BMP_TABLE_V6[0] = 0; -// control chars -fill(BMP_TABLE_V6, 0, 1, 32); -fill(BMP_TABLE_V6, 0, 0x7f, 0xa0); - -// apply wide char rules first -// wide chars -fill(BMP_TABLE_V6, 2, 0x1100, 0x1160); -BMP_TABLE_V6[0x2329] = 2; -BMP_TABLE_V6[0x232a] = 2; -fill(BMP_TABLE_V6, 2, 0x2e80, 0xa4d0); -BMP_TABLE_V6[0x303f] = 1; // wrongly in last line - -fill(BMP_TABLE_V6, 2, 0xac00, 0xd7a4); -fill(BMP_TABLE_V6, 2, 0xf900, 0xfb00); -fill(BMP_TABLE_V6, 2, 0xfe10, 0xfe1a); -fill(BMP_TABLE_V6, 2, 0xfe30, 0xfe70); -fill(BMP_TABLE_V6, 2, 0xff00, 0xff61); -fill(BMP_TABLE_V6, 2, 0xffe0, 0xffe7); - -// apply combining last to ensure we overwrite -// wrongly wide set chars: -// the original algo evals combining first and falls -// through to wide check so we simply do here the opposite -// combining 0 -for (let r = 0; r < BMP_COMBINING_V6.length; ++r) { - fill(BMP_TABLE_V6, 0, BMP_COMBINING_V6[r][0], BMP_COMBINING_V6[r][1] + 1); -} - -export function wcwidthV6(num: number): number { - if (num < 32) return 0; - if (num < 127) return 1; - if (num < 65536) return BMP_TABLE_V6[num]; - if (bisearch(num, HIGH_COMBINING_V6)) return 0; - if ((num >= 0x20000 && num <= 0x2fffd) || (num >= 0x30000 && num <= 0x3fffd)) return 2; - return 1; -} - -export function getStringCellWidthV6(s: string): number { - return getStringCellWidthPrivate(wcwidthV6, s); -} - -/** - * Unicode version 10 - */ -const BMP_COMBINING_V11 = [ - [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], - [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], - [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], - [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], - [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], - [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], - [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], - [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], - [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], - [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], - [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], - [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], - [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], - [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], - [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], - [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], - [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], - [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], - [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], - [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], - [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], - [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], - [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], - [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], - [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], - [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], - [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], - [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], - [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], - [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], - [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], - [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], - [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], - [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], - [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], - [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], - [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], - [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], - [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], - [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], - [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], - [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], - [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], - [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], - [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], - [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], - [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], - [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], - [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], - [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], - [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], - [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], - [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], - [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], - [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], - [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], - [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], - [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], - [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], - [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], - [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], - [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], - [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], - [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], - [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], - [0xFFF9, 0xFFFB] -]; - -const HIGH_COMBINING_V11 = [ - [0x101FD, 0x101FD], [0x102E0, 0x102E0], - [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], - [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], - [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], - [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], - [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], - [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], - [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], - [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], - [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], - [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], - [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], - [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], - [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], - [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], - [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], - [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], - [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], - [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], - [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], - [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], - [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], - [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], - [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], - [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], - [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], - [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], - [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], - [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], - [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], - [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], - [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], - [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], - [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], - [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], - [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], - [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] -]; - -const BMP_WIDE_V11 = [ - [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], - [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], - [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], - [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], - [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], - [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], - [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], - [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], - [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], - [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], - [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], - [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], - [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], - [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], - [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], - [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], - [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], - [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], - [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], - [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], - [0xFFE0, 0xFFE6] -]; - -const HIGH_WIDE_V11 = [ - [0x16FE0, 0x16FE3], [0x17000, 0x187F7], - [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], - [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], - [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], - [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], - [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], - [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], - [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], - [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], - [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], - [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], - [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], - [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], - [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], - [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], - [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], - [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], - [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] -]; - -const BMP_TABLE_V11 = new Uint8Array(65536); -fill(BMP_TABLE_V11, 1); -BMP_TABLE_V11[0] = 0; -fill(BMP_TABLE_V11, 0, 1, 32); -fill(BMP_TABLE_V11, 0, 0x7f, 0xa0); -for (let r = 0; r < BMP_COMBINING_V11.length; ++r) { - fill(BMP_TABLE_V11, 0, BMP_COMBINING_V11[r][0], BMP_COMBINING_V11[r][1] + 1); -} -for (let r = 0; r < BMP_WIDE_V11.length; ++r) { - fill(BMP_TABLE_V11, 2, BMP_WIDE_V11[r][0], BMP_WIDE_V11[r][1] + 1); -} - -export function wcwidthV11(num: number): number { - if (num < 32) return 0; - if (num < 127) return 1; - if (num < 65536) return BMP_TABLE_V11[num]; - if (bisearch(num, HIGH_COMBINING_V11)) return 0; - if (bisearch(num, HIGH_WIDE_V11)) return 2; - return 1; -} - -export function getStringCellWidthV11(s: string): number { - return getStringCellWidthPrivate(wcwidthV11, s); -} From 0f83d645b602cad13ce27327f364c551612c99f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 6 Dec 2019 16:46:50 +0100 Subject: [PATCH 10/30] remove V11 from base repo --- src/common/input/UnicodeV11.ts | 216 --------------------------------- 1 file changed, 216 deletions(-) delete mode 100644 src/common/input/UnicodeV11.ts diff --git a/src/common/input/UnicodeV11.ts b/src/common/input/UnicodeV11.ts deleted file mode 100644 index b3ee56d3..00000000 --- a/src/common/input/UnicodeV11.ts +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ -import { IUnicodeVersionProvider } from 'common/services/Services'; -import { fill } from 'common/TypedArrayUtils'; - -export class UnicodeV11 implements IUnicodeVersionProvider { - static bmpCombining = [ - [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], - [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], - [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], - [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], - [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], - [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], - [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], - [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], - [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], - [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], - [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], - [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], - [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], - [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], - [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], - [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], - [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], - [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], - [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], - [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], - [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], - [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], - [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], - [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], - [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], - [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], - [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], - [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], - [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], - [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], - [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], - [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], - [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], - [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], - [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], - [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], - [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], - [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], - [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], - [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], - [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], - [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], - [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], - [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], - [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], - [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], - [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], - [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], - [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], - [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], - [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], - [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], - [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], - [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], - [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], - [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], - [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], - [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], - [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], - [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], - [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], - [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], - [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], - [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], - [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], - [0xFFF9, 0xFFFB] - ]; - static highCombining = [ - [0x101FD, 0x101FD], [0x102E0, 0x102E0], - [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], - [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], - [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], - [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], - [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], - [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], - [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], - [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], - [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], - [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], - [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], - [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], - [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], - [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], - [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], - [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], - [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], - [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], - [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], - [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], - [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], - [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], - [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], - [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], - [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], - [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], - [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], - [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], - [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], - [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], - [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], - [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], - [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], - [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], - [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], - [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] - ]; - static bmpWide = [ - [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], - [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], - [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], - [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], - [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], - [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], - [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], - [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], - [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], - [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], - [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], - [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], - [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], - [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], - [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], - [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], - [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], - [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], - [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], - [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], - [0xFFE0, 0xFFE6] - ]; - static highWide = [ - [0x16FE0, 0x16FE3], [0x17000, 0x187F7], - [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], - [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], - [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], - [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], - [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], - [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], - [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], - [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], - [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], - [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], - [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], - [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], - [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], - [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], - [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], - [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], - [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] - ]; - static bmpTable: Uint8Array; - - static bisearch(ucs: number, data: number[][]): boolean { - let min = 0; - let max = data.length - 1; - let mid; - if (ucs < data[0][0] || ucs > data[max][1]) { - return false; - } - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) { - min = mid + 1; - } else if (ucs < data[mid][0]) { - max = mid - 1; - } else { - return true; - } - } - return false; - } - - public version = '11'; - - constructor() { - if (!UnicodeV11.bmpTable) { - UnicodeV11.bmpTable = new Uint8Array(65536); - fill(UnicodeV11.bmpTable, 1); - UnicodeV11.bmpTable[0] = 0; - fill(UnicodeV11.bmpTable, 0, 1, 32); - fill(UnicodeV11.bmpTable, 0, 0x7f, 0xa0); - for (let r = 0; r < UnicodeV11.bmpCombining.length; ++r) { - fill(UnicodeV11.bmpTable, 0, UnicodeV11.bmpCombining[r][0], UnicodeV11.bmpCombining[r][1] + 1); - } - for (let r = 0; r < UnicodeV11.bmpWide.length; ++r) { - fill(UnicodeV11.bmpTable, 2, UnicodeV11.bmpWide[r][0], UnicodeV11.bmpWide[r][1] + 1); - } - } - } - - public wcwidth(num: number): 0 | 1 | 2 { - if (num < 32) return 0; - if (num < 127) return 1; - if (num < 65536) return UnicodeV11.bmpTable[num] as 0 | 1 | 2; - if (UnicodeV11.bisearch(num, UnicodeV11.highCombining)) return 0; - if (UnicodeV11.bisearch(num, UnicodeV11.highWide)) return 2; - return 1; - } -} From 70dabf11db2b1945aaa0b6eb5dd5cabaa7f60116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 6 Dec 2019 17:00:46 +0100 Subject: [PATCH 11/30] fix failing tests --- src/Terminal.test.ts | 8 ++++---- src/browser/Linkifier.test.ts | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 422394e4..c9cce991 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -7,7 +7,7 @@ import { assert, expect } from 'chai'; import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from './TestUtils.test'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { IBufferService } from 'common/services/Services'; +import { IBufferService, IUnicodeService } from 'common/services/Services'; import { Linkifier } from 'browser/Linkifier'; import { MockLogService, MockOptionsService, MockUnicodeService } from 'common/TestUtils.test'; import { IRegisteredLinkMatcher, IMouseZoneManager, IMouseZone } from 'browser/Types'; @@ -1047,7 +1047,7 @@ describe('Terminal', () => { // to get the special handling of fullwidth, surrogate and combining chars in the input handler beforeEach(() => { terminal = new TestTerminal({ cols: 10, rows: 5 }); - linkifier = new TestLinkifier((terminal as any)._bufferService); + linkifier = new TestLinkifier((terminal as any)._bufferService, (terminal as any)._unicodeService); mouseZoneManager = new TestMouseZoneManager(); linkifier.attachToDom({} as any, mouseZoneManager); }); @@ -1372,8 +1372,8 @@ describe('Terminal', () => { }); class TestLinkifier extends Linkifier { - constructor(bufferService: IBufferService) { - super(bufferService, new MockLogService(), new MockOptionsService(), new MockUnicodeService()); + constructor(bufferService: IBufferService, unicodeService: IUnicodeService) { + super(bufferService, new MockLogService(), new MockOptionsService(), unicodeService); Linkifier._timeBeforeLatency = 0; } diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index 9f155d52..f107d646 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -9,12 +9,13 @@ import { IBufferLine } from 'common/Types'; import { Linkifier } from 'browser/Linkifier'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { MockLogService, MockBufferService, MockOptionsService, MockUnicodeService } from 'common/TestUtils.test'; +import { MockLogService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; +import { UnicodeService } from 'common/services/UnicodeService'; class TestLinkifier extends Linkifier { constructor(bufferService: IBufferService) { - super(bufferService, new MockLogService(), new MockOptionsService(), new MockUnicodeService()); + super(bufferService, new MockLogService(), new MockOptionsService(), new UnicodeService()); Linkifier._timeBeforeLatency = 0; } From 92ac99e87887b01cd68e1d6e8469663a03f6730a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 6 Dec 2019 20:41:24 +0100 Subject: [PATCH 12/30] cleanup --- addons/xterm-addon-unicode11/src/Unicode11Addon.ts | 2 -- src/common/services/Services.ts | 4 ++-- typings/xterm.d.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/addons/xterm-addon-unicode11/src/Unicode11Addon.ts b/addons/xterm-addon-unicode11/src/Unicode11Addon.ts index 926b5ad9..37c5eb7d 100644 --- a/addons/xterm-addon-unicode11/src/Unicode11Addon.ts +++ b/addons/xterm-addon-unicode11/src/Unicode11Addon.ts @@ -258,8 +258,6 @@ class UnicodeV11 implements IUnicodeVersionProvider { export class Unicode11Addon implements ITerminalAddon { public activate(terminal: Terminal): void { - console.log('got we loaded?'); - (window as any)._v11 = new UnicodeV11(); terminal.unicode.register(new UnicodeV11()); } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 9ee348fb..5e52a206 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -212,7 +212,7 @@ export interface IPartialTerminalOptions { scrollSensitivity?: number; tabStopWidth?: number; theme?: ITheme; - unicodeVersion?: '6' | '11'; + unicodeVersion?: string; windowsMode?: boolean; wordSeparator?: string; } @@ -246,7 +246,7 @@ export interface ITerminalOptions { scrollSensitivity: number; tabStopWidth: number; theme: ITheme; - unicodeVersion: '6' | '11'; + unicodeVersion: string; windowsMode: boolean; wordSeparator: string; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index a1ecbbff..472e01b8 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -213,7 +213,7 @@ declare module 'xterm' { /** * Unicode version to be used for wcwidth. Default is version 6. */ - unicodeVersion?: '6' | '11'; + unicodeVersion?: string; /** * Whether "Windows mode" is enabled. Because Windows backends winpty and From 0631323bf57277d6b48ef6c2d2c20027c92498d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 21 Dec 2019 16:27:12 +0100 Subject: [PATCH 13/30] cleanup --- .../src/Unicode11Addon.ts | 253 +----------------- .../xterm-addon-unicode11/src/UnicodeV11.ts | 222 +++++++++++++++ .../xterm-addon-unicode11/src/tsconfig.json | 9 +- .../xterm-addon-unicode11/webpack.config.js | 7 + demo/client.ts | 2 +- src/common/input/UnicodeV6.ts | 190 ++++++------- tsconfig.all.json | 4 +- typings/xterm.d.ts | 2 +- 8 files changed, 340 insertions(+), 349 deletions(-) create mode 100644 addons/xterm-addon-unicode11/src/UnicodeV11.ts diff --git a/addons/xterm-addon-unicode11/src/Unicode11Addon.ts b/addons/xterm-addon-unicode11/src/Unicode11Addon.ts index 37c5eb7d..cb8c9c56 100644 --- a/addons/xterm-addon-unicode11/src/Unicode11Addon.ts +++ b/addons/xterm-addon-unicode11/src/Unicode11Addon.ts @@ -5,261 +5,12 @@ * UnicodeVersionProvider for V11. */ -import { Terminal, ITerminalAddon, IUnicodeVersionProvider } from 'xterm'; - - -type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray - | Int8Array | Int16Array | Int32Array - | Float32Array | Float64Array; - - -/** - * polyfill for TypedArray.fill - * This is needed to support .fill in all safari versions and IE 11. - */ -function fill(array: T, value: number, start?: number, end?: number): T { - // all modern engines that support .fill - if (array.fill) { - return array.fill(value, start, end) as T; - } - return fillFallback(array, value, start, end); -} - -function fillFallback(array: T, value: number, start: number = 0, end: number = array.length): T { - // safari and IE 11 - // since IE 11 does not support Array.prototype.fill either - // we cannot use the suggested polyfill from MDN - // instead we simply fall back to looping - if (start >= array.length) { - return array; - } - start = (array.length + start) % array.length; - if (end >= array.length) { - end = array.length; - } else { - end = (array.length + end) % array.length; - } - for (let i = start; i < end; ++i) { - array[i] = value; - } - return array; -} - -class UnicodeV11 implements IUnicodeVersionProvider { - static bmpCombining = [ - [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], - [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], - [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], - [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], - [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], - [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], - [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], - [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], - [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], - [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], - [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], - [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], - [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], - [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], - [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], - [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], - [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], - [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], - [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], - [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], - [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], - [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], - [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], - [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], - [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], - [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], - [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], - [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], - [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], - [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], - [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], - [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], - [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], - [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], - [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], - [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], - [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], - [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], - [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], - [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], - [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], - [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], - [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], - [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], - [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], - [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], - [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], - [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], - [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], - [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], - [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], - [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], - [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], - [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], - [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], - [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], - [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], - [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], - [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], - [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], - [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], - [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], - [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], - [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], - [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], - [0xFFF9, 0xFFFB] - ]; - static highCombining = [ - [0x101FD, 0x101FD], [0x102E0, 0x102E0], - [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], - [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], - [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], - [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], - [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], - [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], - [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], - [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], - [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], - [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], - [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], - [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], - [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], - [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], - [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], - [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], - [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], - [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], - [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], - [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], - [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], - [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], - [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], - [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], - [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], - [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], - [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], - [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], - [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], - [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], - [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], - [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], - [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], - [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], - [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], - [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] - ]; - static bmpWide = [ - [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], - [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], - [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], - [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], - [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], - [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], - [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], - [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], - [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], - [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], - [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], - [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], - [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], - [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], - [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], - [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], - [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], - [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], - [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], - [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], - [0xFFE0, 0xFFE6] - ]; - static highWide = [ - [0x16FE0, 0x16FE3], [0x17000, 0x187F7], - [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], - [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], - [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], - [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], - [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], - [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], - [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], - [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], - [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], - [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], - [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], - [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], - [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], - [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], - [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], - [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], - [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] - ]; - static bmpTable: Uint8Array; - - static bisearch(ucs: number, data: number[][]): boolean { - let min = 0; - let max = data.length - 1; - let mid; - if (ucs < data[0][0] || ucs > data[max][1]) { - return false; - } - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) { - min = mid + 1; - } else if (ucs < data[mid][0]) { - max = mid - 1; - } else { - return true; - } - } - return false; - } - - public version = '11'; - - constructor() { - if (!UnicodeV11.bmpTable) { - UnicodeV11.bmpTable = new Uint8Array(65536); - fill(UnicodeV11.bmpTable, 1); - UnicodeV11.bmpTable[0] = 0; - fill(UnicodeV11.bmpTable, 0, 1, 32); - fill(UnicodeV11.bmpTable, 0, 0x7f, 0xa0); - for (let r = 0; r < UnicodeV11.bmpCombining.length; ++r) { - fill(UnicodeV11.bmpTable, 0, UnicodeV11.bmpCombining[r][0], UnicodeV11.bmpCombining[r][1] + 1); - } - for (let r = 0; r < UnicodeV11.bmpWide.length; ++r) { - fill(UnicodeV11.bmpTable, 2, UnicodeV11.bmpWide[r][0], UnicodeV11.bmpWide[r][1] + 1); - } - } - } - - public wcwidth(num: number): 0 | 1 | 2 { - if (num < 32) return 0; - if (num < 127) return 1; - if (num < 65536) return UnicodeV11.bmpTable[num] as 0 | 1 | 2; - if (UnicodeV11.bisearch(num, UnicodeV11.highCombining)) return 0; - if (UnicodeV11.bisearch(num, UnicodeV11.highWide)) return 2; - return 1; - } -} - +import { Terminal, ITerminalAddon } from 'xterm'; +import { UnicodeV11 } from './UnicodeV11'; export class Unicode11Addon implements ITerminalAddon { public activate(terminal: Terminal): void { terminal.unicode.register(new UnicodeV11()); } - public dispose(): void { } } diff --git a/addons/xterm-addon-unicode11/src/UnicodeV11.ts b/addons/xterm-addon-unicode11/src/UnicodeV11.ts new file mode 100644 index 00000000..bd778b40 --- /dev/null +++ b/addons/xterm-addon-unicode11/src/UnicodeV11.ts @@ -0,0 +1,222 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IUnicodeVersionProvider } from 'xterm'; +import { fill } from 'common/TypedArrayUtils'; + +type CharWidth = 0 | 1 | 2; + +const BMP_COMBINING = [ + [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], + [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], + [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], + [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], + [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], + [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], + [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], + [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], + [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], + [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], + [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], + [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], + [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], + [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], + [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], + [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], + [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], + [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], + [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], + [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], + [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], + [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], + [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], + [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], + [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], + [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], + [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], + [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], + [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], + [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], + [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], + [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], + [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], + [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], + [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], + [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], + [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], + [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], + [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], + [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], + [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], + [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], + [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], + [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], + [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], + [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], + [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], + [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], + [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], + [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], + [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], + [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], + [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], + [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], + [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], + [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], + [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], + [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], + [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], + [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], + [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], + [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], + [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], + [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], + [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], + [0xFFF9, 0xFFFB] +]; +const HIGH_COMBINING = [ + [0x101FD, 0x101FD], [0x102E0, 0x102E0], + [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], + [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], + [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], + [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], + [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], + [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], + [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], + [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], + [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], + [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], + [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], + [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], + [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], + [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], + [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], + [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], + [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], + [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], + [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], + [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], + [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], + [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], + [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], + [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], + [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], + [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], + [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], + [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], + [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], + [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], + [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], + [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] +]; +const BMP_WIDE = [ + [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], + [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], + [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], + [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], + [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], + [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], + [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], + [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], + [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], + [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], + [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], + [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], + [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], + [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], + [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], + [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], + [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], + [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], + [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], + [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], + [0xFFE0, 0xFFE6] +]; +const HIGH_WIDE = [ + [0x16FE0, 0x16FE3], [0x17000, 0x187F7], + [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], + [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], + [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], + [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], + [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], + [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], + [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], + [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], + [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], + [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], + [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], + [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], + [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], + [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], + [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], + [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], + [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] +]; + +// BMP lookup table, lazy initialized during first addon loading +let TABLE: Uint8Array; + +function bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { + return false; + } + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + return false; +} + + +export class UnicodeV11 implements IUnicodeVersionProvider { + public version = '11'; + + constructor() { + if (!TABLE) { + TABLE = new Uint8Array(65536); + fill(TABLE, 1); + TABLE[0] = 0; + fill(TABLE, 0, 1, 32); + fill(TABLE, 0, 0x7f, 0xa0); + for (let r = 0; r < BMP_COMBINING.length; ++r) { + fill(TABLE, 0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); + } + for (let r = 0; r < BMP_WIDE.length; ++r) { + fill(TABLE, 2, BMP_WIDE[r][0], BMP_WIDE[r][1] + 1); + } + } + } + + public wcwidth(num: number): CharWidth { + if (num < 32) return 0; + if (num < 127) return 1; + if (num < 65536) return TABLE[num] as CharWidth; + if (bisearch(num, HIGH_COMBINING)) return 0; + if (bisearch(num, HIGH_WIDE)) return 2; + return 1; + } +} diff --git a/addons/xterm-addon-unicode11/src/tsconfig.json b/addons/xterm-addon-unicode11/src/tsconfig.json index d875aa53..1681e668 100644 --- a/addons/xterm-addon-unicode11/src/tsconfig.json +++ b/addons/xterm-addon-unicode11/src/tsconfig.json @@ -10,10 +10,17 @@ "outDir": "../out", "sourceMap": true, "removeComments": true, - "strict": true + "strict": true, + "baseUrl": ".", + "paths": { + "common/*": [ "../../../src/common/*" ] + }, }, "include": [ "./**/*", "../../../typings/xterm.d.ts" + ], + "references": [ + { "path": "../../../src/common" } ] } diff --git a/addons/xterm-addon-unicode11/webpack.config.js b/addons/xterm-addon-unicode11/webpack.config.js index 07e9de29..66a83a2f 100644 --- a/addons/xterm-addon-unicode11/webpack.config.js +++ b/addons/xterm-addon-unicode11/webpack.config.js @@ -21,6 +21,13 @@ module.exports = { } ] }, + resolve: { + modules: ['./node_modules'], + extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common') + } + }, output: { filename: mainFile, path: path.resolve('./lib'), diff --git a/demo/client.ts b/demo/client.ts index 9598ffe0..3a015710 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -75,7 +75,7 @@ const addons: { [T in AddonType]: IDemoAddon} = { search: { name: 'search', ctor: SearchAddon, canChange: true }, 'web-links': { name: 'web-links', ctor: WebLinksAddon, canChange: true }, webgl: { name: 'webgl', ctor: WebglAddon, canChange: true }, - unicode11: { name: 'unicode11', ctor: Unicode11Addon, canChange: false } + unicode11: { name: 'unicode11', ctor: Unicode11Addon, canChange: true } }; const terminalContainer = document.getElementById('terminal-container'); diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index af5089b2..288e5692 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -5,124 +5,128 @@ import { IUnicodeVersionProvider } from 'common/services/Services'; import { fill } from 'common/TypedArrayUtils'; -export class UnicodeV6 implements IUnicodeVersionProvider { - static bmpCombining = [ - [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], - [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], - [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], - [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], - [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], - [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], - [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], - [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], - [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], - [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], - [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], - [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], - [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], - [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], - [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], - [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], - [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], - [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], - [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], - [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], - [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], - [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], - [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], - [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], - [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], - [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], - [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], - [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], - [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], - [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], - [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], - [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], - [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], - [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], - [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], - [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], - [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] - ]; - static highCombining = [ - [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], - [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] - ]; - static bmpTable: Uint8Array; +type CharWidth = 0 | 1 | 2; - static bisearch(ucs: number, data: number[][]): boolean { - let min = 0; - let max = data.length - 1; - let mid; - if (ucs < data[0][0] || ucs > data[max][1]) { - return false; - } - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) { - min = mid + 1; - } else if (ucs < data[mid][0]) { - max = mid - 1; - } else { - return true; - } - } +const BMP_COMBINING = [ + [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], + [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], + [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], + [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], + [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], + [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], + [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], + [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], + [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], + [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], + [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], + [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], + [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], + [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], + [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], + [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], + [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], + [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], + [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], + [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], + [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], + [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], + [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], + [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], + [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], + [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], + [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], + [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], + [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], + [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], + [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], + [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], + [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], + [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], + [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], + [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], + [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], + [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], + [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], + [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], + [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], + [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], + [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] +]; +const HIGH_COMBINING = [ + [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], + [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], + [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], + [0xE0100, 0xE01EF] +]; + +// BMP lookup table, lazy initialized during first addon loading +let TABLE: Uint8Array; + +function bisearch(ucs: number, data: number[][]): boolean { + let min = 0; + let max = data.length - 1; + let mid; + if (ucs < data[0][0] || ucs > data[max][1]) { return false; } + while (max >= min) { + mid = (min + max) >> 1; + if (ucs > data[mid][1]) { + min = mid + 1; + } else if (ucs < data[mid][0]) { + max = mid - 1; + } else { + return true; + } + } + return false; +} +export class UnicodeV6 implements IUnicodeVersionProvider { public version = '6'; constructor() { // init lookup table once - if (!UnicodeV6.bmpTable) { - UnicodeV6.bmpTable = new Uint8Array(65536); - fill(UnicodeV6.bmpTable, 1); - UnicodeV6.bmpTable[0] = 0; + if (!TABLE) { + TABLE = new Uint8Array(65536); + fill(TABLE, 1); + TABLE[0] = 0; // control chars - fill(UnicodeV6.bmpTable, 0, 1, 32); - fill(UnicodeV6.bmpTable, 0, 0x7f, 0xa0); + fill(TABLE, 0, 1, 32); + fill(TABLE, 0, 0x7f, 0xa0); // apply wide char rules first // wide chars - fill(UnicodeV6.bmpTable, 2, 0x1100, 0x1160); - UnicodeV6.bmpTable[0x2329] = 2; - UnicodeV6.bmpTable[0x232a] = 2; - fill(UnicodeV6.bmpTable, 2, 0x2e80, 0xa4d0); - UnicodeV6.bmpTable[0x303f] = 1; // wrongly in last line + fill(TABLE, 2, 0x1100, 0x1160); + TABLE[0x2329] = 2; + TABLE[0x232a] = 2; + fill(TABLE, 2, 0x2e80, 0xa4d0); + TABLE[0x303f] = 1; // wrongly in last line - fill(UnicodeV6.bmpTable, 2, 0xac00, 0xd7a4); - fill(UnicodeV6.bmpTable, 2, 0xf900, 0xfb00); - fill(UnicodeV6.bmpTable, 2, 0xfe10, 0xfe1a); - fill(UnicodeV6.bmpTable, 2, 0xfe30, 0xfe70); - fill(UnicodeV6.bmpTable, 2, 0xff00, 0xff61); - fill(UnicodeV6.bmpTable, 2, 0xffe0, 0xffe7); + fill(TABLE, 2, 0xac00, 0xd7a4); + fill(TABLE, 2, 0xf900, 0xfb00); + fill(TABLE, 2, 0xfe10, 0xfe1a); + fill(TABLE, 2, 0xfe30, 0xfe70); + fill(TABLE, 2, 0xff00, 0xff61); + fill(TABLE, 2, 0xffe0, 0xffe7); // apply combining last to ensure we overwrite // wrongly wide set chars: // the original algo evals combining first and falls // through to wide check so we simply do here the opposite // combining 0 - for (let r = 0; r < UnicodeV6.bmpCombining.length; ++r) { - fill(UnicodeV6.bmpTable, 0, UnicodeV6.bmpCombining[r][0], UnicodeV6.bmpCombining[r][1] + 1); + for (let r = 0; r < BMP_COMBINING.length; ++r) { + fill(TABLE, 0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } } } - public wcwidth(num: number): 0 | 1 | 2 { + public wcwidth(num: number): CharWidth { if (num < 32) return 0; if (num < 127) return 1; - if (num < 65536) return UnicodeV6.bmpTable[num] as 0 | 1 | 2; - if (UnicodeV6.bisearch(num, UnicodeV6.highCombining)) return 0; + if (num < 65536) return TABLE[num] as CharWidth; + if (bisearch(num, HIGH_COMBINING)) return 0; if ((num >= 0x20000 && num <= 0x2fffd) || (num >= 0x30000 && num <= 0x3fffd)) return 2; return 1; } diff --git a/tsconfig.all.json b/tsconfig.all.json index efd968f3..8a26c302 100644 --- a/tsconfig.all.json +++ b/tsconfig.all.json @@ -8,8 +8,8 @@ { "path": "./addons/xterm-addon-attach/src" }, { "path": "./addons/xterm-addon-fit/src" }, { "path": "./addons/xterm-addon-search/src" }, + { "path": "./addons/xterm-addon-unicode11/src" }, { "path": "./addons/xterm-addon-web-links/src" }, - { "path": "./addons/xterm-addon-webgl/src" }, - { "path": "./addons/xterm-addon-unicode11/src" } + { "path": "./addons/xterm-addon-webgl/src" } ] } diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 2efa39f7..b1fd2e06 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -1150,7 +1150,7 @@ declare module 'xterm' { /** String indicating the Unicode version provided. */ version: string; /** Unicode version dependent wcwidth implementation. */ - wcwidth(ucs: number): 0 | 1 | 2; + wcwidth(codepoint: number): 0 | 1 | 2; } /** From 0f8a034e8bfe980695fc47db8392d3b21dcbf719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 21 Dec 2019 16:29:46 +0100 Subject: [PATCH 14/30] make linter happy --- .../xterm-addon-unicode11/src/UnicodeV11.ts | 20 +++++----- src/common/input/UnicodeV6.ts | 40 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/addons/xterm-addon-unicode11/src/UnicodeV11.ts b/addons/xterm-addon-unicode11/src/UnicodeV11.ts index bd778b40..d1525d25 100644 --- a/addons/xterm-addon-unicode11/src/UnicodeV11.ts +++ b/addons/xterm-addon-unicode11/src/UnicodeV11.ts @@ -169,7 +169,7 @@ const HIGH_WIDE = [ ]; // BMP lookup table, lazy initialized during first addon loading -let TABLE: Uint8Array; +let table: Uint8Array; function bisearch(ucs: number, data: number[][]): boolean { let min = 0; @@ -196,17 +196,17 @@ export class UnicodeV11 implements IUnicodeVersionProvider { public version = '11'; constructor() { - if (!TABLE) { - TABLE = new Uint8Array(65536); - fill(TABLE, 1); - TABLE[0] = 0; - fill(TABLE, 0, 1, 32); - fill(TABLE, 0, 0x7f, 0xa0); + if (!table) { + table = new Uint8Array(65536); + fill(table, 1); + table[0] = 0; + fill(table, 0, 1, 32); + fill(table, 0, 0x7f, 0xa0); for (let r = 0; r < BMP_COMBINING.length; ++r) { - fill(TABLE, 0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); + fill(table, 0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } for (let r = 0; r < BMP_WIDE.length; ++r) { - fill(TABLE, 2, BMP_WIDE[r][0], BMP_WIDE[r][1] + 1); + fill(table, 2, BMP_WIDE[r][0], BMP_WIDE[r][1] + 1); } } } @@ -214,7 +214,7 @@ export class UnicodeV11 implements IUnicodeVersionProvider { public wcwidth(num: number): CharWidth { if (num < 32) return 0; if (num < 127) return 1; - if (num < 65536) return TABLE[num] as CharWidth; + if (num < 65536) return table[num] as CharWidth; if (bisearch(num, HIGH_COMBINING)) return 0; if (bisearch(num, HIGH_WIDE)) return 2; return 1; diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index 288e5692..d6bb7458 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -61,7 +61,7 @@ const HIGH_COMBINING = [ ]; // BMP lookup table, lazy initialized during first addon loading -let TABLE: Uint8Array; +let table: Uint8Array; function bisearch(ucs: number, data: number[][]): boolean { let min = 0; @@ -88,28 +88,28 @@ export class UnicodeV6 implements IUnicodeVersionProvider { constructor() { // init lookup table once - if (!TABLE) { - TABLE = new Uint8Array(65536); - fill(TABLE, 1); - TABLE[0] = 0; + if (!table) { + table = new Uint8Array(65536); + fill(table, 1); + table[0] = 0; // control chars - fill(TABLE, 0, 1, 32); - fill(TABLE, 0, 0x7f, 0xa0); + fill(table, 0, 1, 32); + fill(table, 0, 0x7f, 0xa0); // apply wide char rules first // wide chars - fill(TABLE, 2, 0x1100, 0x1160); - TABLE[0x2329] = 2; - TABLE[0x232a] = 2; - fill(TABLE, 2, 0x2e80, 0xa4d0); - TABLE[0x303f] = 1; // wrongly in last line + fill(table, 2, 0x1100, 0x1160); + table[0x2329] = 2; + table[0x232a] = 2; + fill(table, 2, 0x2e80, 0xa4d0); + table[0x303f] = 1; // wrongly in last line - fill(TABLE, 2, 0xac00, 0xd7a4); - fill(TABLE, 2, 0xf900, 0xfb00); - fill(TABLE, 2, 0xfe10, 0xfe1a); - fill(TABLE, 2, 0xfe30, 0xfe70); - fill(TABLE, 2, 0xff00, 0xff61); - fill(TABLE, 2, 0xffe0, 0xffe7); + fill(table, 2, 0xac00, 0xd7a4); + fill(table, 2, 0xf900, 0xfb00); + fill(table, 2, 0xfe10, 0xfe1a); + fill(table, 2, 0xfe30, 0xfe70); + fill(table, 2, 0xff00, 0xff61); + fill(table, 2, 0xffe0, 0xffe7); // apply combining last to ensure we overwrite // wrongly wide set chars: @@ -117,7 +117,7 @@ export class UnicodeV6 implements IUnicodeVersionProvider { // through to wide check so we simply do here the opposite // combining 0 for (let r = 0; r < BMP_COMBINING.length; ++r) { - fill(TABLE, 0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); + fill(table, 0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } } } @@ -125,7 +125,7 @@ export class UnicodeV6 implements IUnicodeVersionProvider { public wcwidth(num: number): CharWidth { if (num < 32) return 0; if (num < 127) return 1; - if (num < 65536) return TABLE[num] as CharWidth; + if (num < 65536) return table[num] as CharWidth; if (bisearch(num, HIGH_COMBINING)) return 0; if ((num >= 0x20000 && num <= 0x2fffd) || (num >= 0x30000 && num <= 0x3fffd)) return 2; return 1; From df4b07f9f2841bbb9ca099422b070ac4aedd6c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 4 Jan 2020 14:47:14 +0100 Subject: [PATCH 15/30] type fixes & docs --- addons/xterm-addon-unicode11/README.md | 3 +++ addons/xterm-addon-unicode11/src/UnicodeV11.ts | 2 +- src/Terminal.test.ts | 2 +- src/Terminal.ts | 14 +++++++------- src/TestUtils.test.ts | 3 ++- src/Types.d.ts | 3 ++- src/common/input/UnicodeV6.ts | 2 +- src/common/services/Services.ts | 2 +- src/public/Terminal.ts | 8 ++++---- typings/xterm.d.ts | 10 ++++++++-- 10 files changed, 30 insertions(+), 19 deletions(-) diff --git a/addons/xterm-addon-unicode11/README.md b/addons/xterm-addon-unicode11/README.md index eff46b39..ec01203e 100644 --- a/addons/xterm-addon-unicode11/README.md +++ b/addons/xterm-addon-unicode11/README.md @@ -17,4 +17,7 @@ import { Unicode11Addon } from 'xterm-addon-unicode11'; const terminal = new Terminal(); const unicode11Addon = new Unicode11Addon(); terminal.loadAddon(unicode11Addon); + +// activate the new version +terminal.unicode.activeVersion = '11'; ``` diff --git a/addons/xterm-addon-unicode11/src/UnicodeV11.ts b/addons/xterm-addon-unicode11/src/UnicodeV11.ts index d1525d25..d9d548d6 100644 --- a/addons/xterm-addon-unicode11/src/UnicodeV11.ts +++ b/addons/xterm-addon-unicode11/src/UnicodeV11.ts @@ -193,7 +193,7 @@ function bisearch(ucs: number, data: number[][]): boolean { export class UnicodeV11 implements IUnicodeVersionProvider { - public version = '11'; + public readonly version = '11'; constructor() { if (!table) { diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 25a22176..d65c2478 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -1039,7 +1039,7 @@ describe('Terminal', () => { // to get the special handling of fullwidth, surrogate and combining chars in the input handler beforeEach(() => { terminal = new TestTerminal({ cols: 10, rows: 5 }); - linkifier = new TestLinkifier((terminal as any)._bufferService, (terminal as any)._unicodeService); + linkifier = new TestLinkifier((terminal as any)._bufferService, terminal.unicodeService); mouseZoneManager = new TestMouseZoneManager(); linkifier.attachToDom({} as any, mouseZoneManager); }); diff --git a/src/Terminal.ts b/src/Terminal.ts index 232f03ea..8ae36768 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -99,7 +99,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _instantiationService: IInstantiationService; private _logService: ILogService; public optionsService: IOptionsService; - private _unicodeService: IUnicodeService; + public unicodeService: IUnicodeService; // browser services private _charSizeService: ICharSizeService; @@ -211,8 +211,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._instantiationService.setService(ICoreMouseService, this._coreMouseService); this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); - this._unicodeService = this._instantiationService.createInstance(UnicodeService); - this._instantiationService.setService(IUnicodeService, this._unicodeService); + this.unicodeService = this._instantiationService.createInstance(UnicodeService); + this._instantiationService.setService(IUnicodeService, this.unicodeService); this._charsetService = this._instantiationService.createInstance(CharsetService); this._instantiationService.setService(ICharsetService, this._charsetService); @@ -248,7 +248,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._inputHandler.reset(); } else { // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this._unicodeService); + this._inputHandler = new InputHandler(this, this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService); this._inputHandler.onRequestBell(() => this.bell()); this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)); this._inputHandler.onRequestReset(() => this.reset()); @@ -258,7 +258,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } if (!this.linkifier) { - this.linkifier = new Linkifier(this._bufferService, this._logService, this.optionsService, this._unicodeService); + this.linkifier = new Linkifier(this._bufferService, this._logService, this.optionsService, this.unicodeService); } if (this.options.windowsMode) { @@ -348,8 +348,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } break; case 'unicodeVersion': - if (this._unicodeService.activeVersion !== this.optionsService.options.unicodeVersion) { - this._unicodeService.activeVersion = this.optionsService.options.unicodeVersion; + if (this.unicodeService.activeVersion !== this.optionsService.options.unicodeVersion) { + this.unicodeService.activeVersion = this.optionsService.options.unicodeVersion; } break; } diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 57a9fe78..c6e193a7 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -13,7 +13,7 @@ import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm'; import { Terminal } from './Terminal'; import { AttributeData } from 'common/buffer/AttributeData'; import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport } from 'browser/Types'; -import { IOptionsService } from 'common/services/Services'; +import { IOptionsService, IUnicodeService } from 'common/services/Services'; import { EventEmitter } from 'common/EventEmitter'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; import { ISelectionService } from 'browser/services/Services'; @@ -41,6 +41,7 @@ export class MockTerminal implements ITerminal { onResize: IEvent<{ cols: number; rows: number; }>; markers: IMarker[]; optionsService: IOptionsService; + unicodeService: IUnicodeService; addMarker(cursorYOffset: number): IMarker { throw new Error('Method not implemented.'); } diff --git a/src/Types.d.ts b/src/Types.d.ts index ebc5d338..0b875029 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -7,7 +7,7 @@ import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISele import { ICharset, IAttributeData, CharData, CoreMouseEventType } from 'common/Types'; import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport } from 'browser/Types'; -import { IOptionsService } from 'common/services/Services'; +import { IOptionsService, IUnicodeService } from 'common/services/Services'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; @@ -140,6 +140,7 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc optionsService: IOptionsService; // TODO: We should remove options once components adopt optionsService options: ITerminalOptions; + unicodeService: IUnicodeService; onBlur: IEvent; onFocus: IEvent; diff --git a/src/common/input/UnicodeV6.ts b/src/common/input/UnicodeV6.ts index d6bb7458..b3082030 100644 --- a/src/common/input/UnicodeV6.ts +++ b/src/common/input/UnicodeV6.ts @@ -84,7 +84,7 @@ function bisearch(ucs: number, data: number[][]): boolean { } export class UnicodeV6 implements IUnicodeVersionProvider { - public version = '6'; + public readonly version = '6'; constructor() { // init lookup table once diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index f6bd47a8..ce24e5c1 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -328,6 +328,6 @@ export interface IUnicodeService { } export interface IUnicodeVersionProvider { - version: string; + readonly version: string; wcwidth(ucs: number): 0 | 1 | 2; } diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index ce432ce1..4f70e347 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -263,18 +263,18 @@ class UnicodeApi implements IUnicodeHandling { constructor(private _core: ITerminal) {} public register(provider: IUnicodeVersionProvider): void { - (this._core as any)._unicodeService.register(provider); + this._core.unicodeService.register(provider); } public get versions(): string[] { - return (this._core as any)._unicodeService.versions; + return this._core.unicodeService.versions; } public get activeVersion(): string { - return (this._core as any)._unicodeService.activeVersion; + return this._core.unicodeService.activeVersion; } public set activeVersion(version: string) { - (this._core as any)._unicodeService.activeVersion = version; + this._core.unicodeService.activeVersion = version; } } diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 3229b158..8a7464a6 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -217,6 +217,7 @@ declare module 'xterm' { /** * Unicode version to be used for wcwidth. Default is version 6. + * New unicode versions can be registered through the `Terminal.unicode.register` API. */ unicodeVersion?: string; @@ -1173,7 +1174,7 @@ declare module 'xterm' { */ export interface IUnicodeVersionProvider { /** String indicating the Unicode version provided. */ - version: string; + readonly version: string; /** Unicode version dependent wcwidth implementation. */ wcwidth(codepoint: number): 0 | 1 | 2; } @@ -1189,6 +1190,11 @@ declare module 'xterm' { /** * Registered Unicode versions. */ - versions: string[]; + readonly versions: ReadonlyArray; + /** + * Getter/setter for activate Unicode version. + * FIXME: Should we go with this instead of get/SetOption? + */ + activeVersion: string; } } From 3e1dbd4c39eb2bbd95eecad7ee913b0d7046defa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 4 Jan 2020 15:39:00 +0100 Subject: [PATCH 16/30] fix api test --- addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts b/addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts index a731e7bb..fb721b47 100644 --- a/addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts +++ b/addons/xterm-addon-unicode11/src/Unicode11Addon.api.ts @@ -46,7 +46,7 @@ describe('Unicode11Addon', () => { await page.evaluate(`window.term.unicode.activeVersion = '11';`); assert.deepEqual(await page.evaluate(`window.term.unicode.activeVersion`), '11'); // v6: 10, V11: 20 - assert.deepEqual(await page.evaluate(`window.term._core._unicodeService.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣')`), 20); + assert.deepEqual(await page.evaluate(`window.term._core.unicodeService.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣')`), 20); }); }); From 7d8c0268ea0d70bf765d8864d319dde0f0aedb16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Tue, 7 Jan 2020 00:29:48 +0100 Subject: [PATCH 17/30] remove unicodeVersion from options --- demo/client.ts | 3 +-- src/Terminal.ts | 5 ----- src/common/services/OptionsService.ts | 1 - src/common/services/Services.ts | 2 -- typings/xterm.d.ts | 20 ++++++++++---------- 5 files changed, 11 insertions(+), 20 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 3a015710..2a0339eb 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -265,8 +265,7 @@ function initOptions(term: TerminalType): void { fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], logLevel: ['debug', 'info', 'warn', 'error', 'off'], rendererType: ['dom', 'canvas'], - wordSeparator: null, - unicodeVersion: ['6', '11'] + wordSeparator: null }; const options = Object.keys((term)._core.options); const booleanOptions = []; diff --git a/src/Terminal.ts b/src/Terminal.ts index 8ae36768..6a75979b 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -347,11 +347,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._windowsMode = undefined; } break; - case 'unicodeVersion': - if (this.unicodeService.activeVersion !== this.optionsService.options.unicodeVersion) { - this.unicodeService.activeVersion = this.optionsService.options.unicodeVersion; - } - break; } }); } diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 505d16e1..7cd111ac 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -52,7 +52,6 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ screenKeys: false, cancelEvents: false, useFlowControl: false, - unicodeVersion: '6', wordSeparator: ' ()[]{}\',:;"`' }); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index ce24e5c1..ce7b33f7 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -238,7 +238,6 @@ export interface IPartialTerminalOptions { scrollSensitivity?: number; tabStopWidth?: number; theme?: ITheme; - unicodeVersion?: string; windowsMode?: boolean; wordSeparator?: string; } @@ -273,7 +272,6 @@ export interface ITerminalOptions { scrollSensitivity: number; tabStopWidth: number; theme: ITheme; - unicodeVersion: string; windowsMode: boolean; wordSeparator: string; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 8a7464a6..d0479897 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -215,12 +215,6 @@ declare module 'xterm' { */ theme?: ITheme; - /** - * Unicode version to be used for wcwidth. Default is version 6. - * New unicode versions can be registered through the `Terminal.unicode.register` API. - */ - unicodeVersion?: string; - /** * Whether "Windows mode" is enabled. Because Windows backends winpty and * conpty operate by doing line wrapping on their side, xterm.js does not @@ -1173,9 +1167,14 @@ declare module 'xterm' { * Used to register custom Unicode versions with `Terminal.unicode.register`. */ export interface IUnicodeVersionProvider { - /** String indicating the Unicode version provided. */ + /** + * String indicating the Unicode version provided. + */ readonly version: string; - /** Unicode version dependent wcwidth implementation. */ + + /** + * Unicode version dependent wcwidth implementation. + */ wcwidth(codepoint: number): 0 | 1 | 2; } @@ -1187,13 +1186,14 @@ declare module 'xterm' { * Register a custom Unicode version provider. */ register(provider: IUnicodeVersionProvider): void; + /** * Registered Unicode versions. */ readonly versions: ReadonlyArray; + /** - * Getter/setter for activate Unicode version. - * FIXME: Should we go with this instead of get/SetOption? + * Getter/setter for active Unicode version. */ activeVersion: string; } From f5c3b00c42d7fbc0fcae5eaf1bafd90c02f40351 Mon Sep 17 00:00:00 2001 From: kumaran-14 Date: Fri, 10 Jan 2020 00:02:18 +0530 Subject: [PATCH 18/30] Fixes Alt + click behaviour when rows are wrapped --- src/browser/input/MoveToCell.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index 406ec807..72a24280 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -30,7 +30,30 @@ export function moveToCellSequence(targetX: number, targetY: number, bufferServi } // Only move horizontally for the normal buffer - return moveHorizontallyOnly(startX, startY, targetX, targetY, bufferService, applicationCursor); + let direction; + if(startY == targetY) { + direction = startX > targetX ? Direction.LEFT : Direction.RIGHT; + return repeat(Math.abs(startX - targetX), sequence(direction, applicationCursor)); + } else { + direction = startY > targetY ? Direction.LEFT : Direction.RIGHT; + return repeat(colsFromRowEnd(startY > targetY ? targetX : startX, bufferService), sequence(direction, applicationCursor)) + + repeat((Math.abs(wrappedRowsForRow(bufferService, startY) - wrappedRowsForRow(bufferService, targetY))-1)*bufferService.cols, sequence(direction, applicationCursor)) + + repeat(colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService), sequence(direction, applicationCursor)); + } +} + +/** + * Find the number of cols from a row beginning to a col. + */ +function colsFromRowBeginning(currX: number, bufferService: IBufferService): number { + return Math.abs(currX); +} + +/** + * Find the number of cols from a col to row end. + */ +function colsFromRowEnd(currX: number, bufferService: IBufferService): number { + return Math.abs(bufferService.cols - currX); } /** From 76d4de225a541d19be340e2d0bba7e423211f300 Mon Sep 17 00:00:00 2001 From: kumaran-14 Date: Fri, 10 Jan 2020 00:40:49 +0530 Subject: [PATCH 19/30] Run eslint and fix errors --- src/browser/input/MoveToCell.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index 72a24280..9b493288 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -31,15 +31,14 @@ export function moveToCellSequence(targetX: number, targetY: number, bufferServi // Only move horizontally for the normal buffer let direction; - if(startY == targetY) { + if (startY === targetY) { direction = startX > targetX ? Direction.LEFT : Direction.RIGHT; return repeat(Math.abs(startX - targetX), sequence(direction, applicationCursor)); - } else { + } direction = startY > targetY ? Direction.LEFT : Direction.RIGHT; return repeat(colsFromRowEnd(startY > targetY ? targetX : startX, bufferService), sequence(direction, applicationCursor)) - + repeat((Math.abs(wrappedRowsForRow(bufferService, startY) - wrappedRowsForRow(bufferService, targetY))-1)*bufferService.cols, sequence(direction, applicationCursor)) + + repeat((Math.abs(wrappedRowsForRow(bufferService, startY) - wrappedRowsForRow(bufferService, targetY)) - 1 ) * bufferService.cols, sequence(direction, applicationCursor)) + repeat(colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService), sequence(direction, applicationCursor)); - } } /** From af67b724923b04a47f3a506e7d0d388886915676 Mon Sep 17 00:00:00 2001 From: kumaran-14 Date: Wed, 15 Jan 2020 21:32:04 +0530 Subject: [PATCH 20/30] Fix unit tests for MoveToCellSequence --- src/browser/input/MoveToCell.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/browser/input/MoveToCell.test.ts b/src/browser/input/MoveToCell.test.ts index bc4012c0..b78748ba 100644 --- a/src/browser/input/MoveToCell.test.ts +++ b/src/browser/input/MoveToCell.test.ts @@ -23,15 +23,15 @@ describe('MoveToCell', () => { assert.equal(moveToCellSequence(4, 3, bufferService, false), '\x1b[C'); }); it('should ignore the Y value', () => { - assert.equal(moveToCellSequence(1, 1, bufferService, false), '\x1b[D\x1b[D'); - assert.equal(moveToCellSequence(1, 2, bufferService, false), '\x1b[D\x1b[D'); - assert.equal(moveToCellSequence(1, 3, bufferService, false), '\x1b[D\x1b[D'); - assert.equal(moveToCellSequence(1, 4, bufferService, false), '\x1b[D\x1b[D'); - assert.equal(moveToCellSequence(1, 5, bufferService, false), '\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(1, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); + assert.equal(moveToCellSequence(1, 2, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); + assert.equal(moveToCellSequence(1, 3, bufferService, false), '\u001b[D\u001b[D'); + assert.equal(moveToCellSequence(1, 4, bufferService, false), '\u001b[C\u001b[C\u001b[C'); + assert.equal(moveToCellSequence(1, 5, bufferService, false), '\u001b[C\u001b[C\u001b[C'); }); it('should use the correct character for application cursor', () => { - assert.equal(moveToCellSequence(2, 1, bufferService, false), '\x1b[D'); - assert.equal(moveToCellSequence(2, 1, bufferService, true), '\x1bOD'); + assert.equal(moveToCellSequence(2, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); + assert.equal(moveToCellSequence(2, 1, bufferService, true), '\u001bOD\u001bOD\u001bOD\u001bOD\u001bOD\u001bOD'); }); }); From 6240a5d7a4d2a01cbc4746024810873c76a54cb9 Mon Sep 17 00:00:00 2001 From: Jeff Baier Date: Wed, 22 Jan 2020 14:16:39 -0600 Subject: [PATCH 21/30] Add _terminalClass identifier to cursor blink animation css so that multiple DOM rendered instances no longer lose the cursor theme color Fixes #2674 --- src/browser/renderer/dom/DomRenderer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 5fbcfdc9..e8679026 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -173,13 +173,13 @@ export class DomRenderer extends Disposable implements IRenderer { `}`; // Blink animation styles += - `@keyframes blink_box_shadow {` + + `@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` + ` 50% {` + ` box-shadow: none;` + ` }` + `}`; styles += - `@keyframes blink_block {` + + `@keyframes blink_block` + `_` + this._terminalClass + ` {` + ` 0% {` + ` background-color: ${this._colors.cursor.css};` + ` color: ${this._colors.cursorAccent.css};` + @@ -196,10 +196,10 @@ export class DomRenderer extends Disposable implements IRenderer { ` outline-offset: -1px;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` + - ` animation: blink_box_shadow 1s step-end infinite;` + + ` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + - ` animation: blink_block 1s step-end infinite;` + + ` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${this._colors.cursor.css};` + From 49bc27e24f7f13e45d387247bec0b7a2c84dfb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 2 Feb 2020 13:07:11 +0100 Subject: [PATCH 22/30] fixing visual repr chars --- src/common/data/Charsets.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/data/Charsets.ts b/src/common/data/Charsets.ts index 56ca6799..56513b8a 100644 --- a/src/common/data/Charsets.ts +++ b/src/common/data/Charsets.ts @@ -30,14 +30,14 @@ export const DEFAULT_CHARSET: ICharset | null = CHARSETS['B']; CHARSETS['0'] = { '`': '\u25c6', // '◆' 'a': '\u2592', // '▒' - 'b': '\u0009', // '\t' - 'c': '\u000c', // '\f' - 'd': '\u000d', // '\r' - 'e': '\u000a', // '\n' + 'b': '\u2409', // '␉' (HT) + 'c': '\u240c', // '␌' (FF) + 'd': '\u240d', // '␍' (CR) + 'e': '\u240a', // '␊' (LF) 'f': '\u00b0', // '°' 'g': '\u00b1', // '±' - 'h': '\u2424', // '\u2424' (NL) - 'i': '\u000b', // '\v' + 'h': '\u2424', // '␤' (NL) + 'i': '\u240b', // '␋' (VT) 'j': '\u2518', // '┘' 'k': '\u2510', // '┐' 'l': '\u250c', // '┌' From d5bf8966d9e6dad268346116d7878da58f9243b2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 2 Feb 2020 05:10:57 -0800 Subject: [PATCH 23/30] Remove ; and : from default wordSeparator Fixes #2665 --- src/common/services/OptionsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 7cd111ac..b855974a 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -52,7 +52,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ screenKeys: false, cancelEvents: false, useFlowControl: false, - wordSeparator: ' ()[]{}\',:;"`' + wordSeparator: ' ()[]{}\',"`' }); /** From b591ccc40dcf9226da43ae63b30db7ec7b6cf370 Mon Sep 17 00:00:00 2001 From: kumaran-14 Date: Sun, 2 Feb 2020 23:28:24 +0530 Subject: [PATCH 24/30] Fix moveToCell when target is beyond buffer --- src/browser/input/MoveToCell.test.ts | 4 ++-- src/browser/input/MoveToCell.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/input/MoveToCell.test.ts b/src/browser/input/MoveToCell.test.ts index b78748ba..735697d9 100644 --- a/src/browser/input/MoveToCell.test.ts +++ b/src/browser/input/MoveToCell.test.ts @@ -23,14 +23,14 @@ describe('MoveToCell', () => { assert.equal(moveToCellSequence(4, 3, bufferService, false), '\x1b[C'); }); it('should ignore the Y value', () => { - assert.equal(moveToCellSequence(1, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); + assert.equal(moveToCellSequence(1, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); assert.equal(moveToCellSequence(1, 2, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); assert.equal(moveToCellSequence(1, 3, bufferService, false), '\u001b[D\u001b[D'); assert.equal(moveToCellSequence(1, 4, bufferService, false), '\u001b[C\u001b[C\u001b[C'); assert.equal(moveToCellSequence(1, 5, bufferService, false), '\u001b[C\u001b[C\u001b[C'); }); it('should use the correct character for application cursor', () => { - assert.equal(moveToCellSequence(2, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); + assert.equal(moveToCellSequence(2, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); assert.equal(moveToCellSequence(2, 1, bufferService, true), '\u001bOD\u001bOD\u001bOD\u001bOD\u001bOD\u001bOD'); }); }); diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index 9b493288..e3b2555a 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -37,7 +37,7 @@ export function moveToCellSequence(targetX: number, targetY: number, bufferServi } direction = startY > targetY ? Direction.LEFT : Direction.RIGHT; return repeat(colsFromRowEnd(startY > targetY ? targetX : startX, bufferService), sequence(direction, applicationCursor)) - + repeat((Math.abs(wrappedRowsForRow(bufferService, startY) - wrappedRowsForRow(bufferService, targetY)) - 1 ) * bufferService.cols, sequence(direction, applicationCursor)) + + repeat((Math.abs(startY - targetY) - 1 ) * bufferService.cols, sequence(direction, applicationCursor)) + repeat(colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService), sequence(direction, applicationCursor)); } From 7d84083f557e1cfd6ac04cc9c8c510afb3fdf036 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 3 Feb 2020 06:43:20 -0800 Subject: [PATCH 25/30] Fix algorithm to move the right amount of cells --- src/browser/input/MoveToCell.test.ts | 24 ++++++++++++++++-------- src/browser/input/MoveToCell.ts | 14 ++++++++------ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/browser/input/MoveToCell.test.ts b/src/browser/input/MoveToCell.test.ts index 735697d9..5753ea46 100644 --- a/src/browser/input/MoveToCell.test.ts +++ b/src/browser/input/MoveToCell.test.ts @@ -15,6 +15,9 @@ describe('MoveToCell', () => { bufferService = new MockBufferService(5, 5); bufferService.buffer.x = 3; bufferService.buffer.y = 3; + // Lines 2-4 are wrapped + bufferService.buffer.lines.get(3)!.isWrapped = true; + bufferService.buffer.lines.get(4)!.isWrapped = true; }); describe('normal buffer', () => { @@ -22,16 +25,21 @@ describe('MoveToCell', () => { assert.equal(moveToCellSequence(2, 3, bufferService, false), '\x1b[D'); assert.equal(moveToCellSequence(4, 3, bufferService, false), '\x1b[C'); }); - it('should ignore the Y value', () => { - assert.equal(moveToCellSequence(1, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); - assert.equal(moveToCellSequence(1, 2, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); - assert.equal(moveToCellSequence(1, 3, bufferService, false), '\u001b[D\u001b[D'); - assert.equal(moveToCellSequence(1, 4, bufferService, false), '\u001b[C\u001b[C\u001b[C'); - assert.equal(moveToCellSequence(1, 5, bufferService, false), '\u001b[C\u001b[C\u001b[C'); + it('should wrap around entire row instead of doing up and down when the Y value differs', () => { + assert.equal(moveToCellSequence(2, 2, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(4, 4, bufferService, false), '\x1b[C\x1b[C\x1b[C'); + }); + it('should wrap across 2+ rows', () => { + bufferService.buffer.y = 2; + assert.equal(moveToCellSequence(3, 4, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D', '(3,2) to (3,4) = 10 cells'); + }); + it('should not move the cursor beyond the current wrapped row', () => { + assert.equal(moveToCellSequence(1, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(1, 5, bufferService, false), '\x1b[C\x1b[C\x1b[C'); }); it('should use the correct character for application cursor', () => { - assert.equal(moveToCellSequence(2, 1, bufferService, false), '\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D'); - assert.equal(moveToCellSequence(2, 1, bufferService, true), '\u001bOD\u001bOD\u001bOD\u001bOD\u001bOD\u001bOD'); + assert.equal(moveToCellSequence(2, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(2, 1, bufferService, true), '\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD'); }); }); diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index e3b2555a..1113f957 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -35,24 +35,26 @@ export function moveToCellSequence(targetX: number, targetY: number, bufferServi direction = startX > targetX ? Direction.LEFT : Direction.RIGHT; return repeat(Math.abs(startX - targetX), sequence(direction, applicationCursor)); } - direction = startY > targetY ? Direction.LEFT : Direction.RIGHT; - return repeat(colsFromRowEnd(startY > targetY ? targetX : startX, bufferService), sequence(direction, applicationCursor)) - + repeat((Math.abs(startY - targetY) - 1 ) * bufferService.cols, sequence(direction, applicationCursor)) - + repeat(colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService), sequence(direction, applicationCursor)); + direction = startY > targetY ? Direction.LEFT : Direction.RIGHT; + const rowDifference = Math.abs(startY - targetY); + const cellsToMove = colsFromRowEnd(startY > targetY ? targetX : startX, bufferService) + + (rowDifference - 1) * bufferService.cols + 1/*wrap around 1 row*/ + + colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService); + return repeat(cellsToMove, sequence(direction, applicationCursor)); } /** * Find the number of cols from a row beginning to a col. */ function colsFromRowBeginning(currX: number, bufferService: IBufferService): number { - return Math.abs(currX); + return currX - 1; } /** * Find the number of cols from a col to row end. */ function colsFromRowEnd(currX: number, bufferService: IBufferService): number { - return Math.abs(bufferService.cols - currX); + return bufferService.cols - currX; } /** From bb944f190e5f8e388128b655ece302bcbb69fc0b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 3 Feb 2020 06:54:35 -0800 Subject: [PATCH 26/30] Fix and add more MoveToCell tests --- src/browser/input/MoveToCell.test.ts | 41 ++++++++++++++++++---------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/browser/input/MoveToCell.test.ts b/src/browser/input/MoveToCell.test.ts index 5753ea46..61e5f90b 100644 --- a/src/browser/input/MoveToCell.test.ts +++ b/src/browser/input/MoveToCell.test.ts @@ -15,31 +15,44 @@ describe('MoveToCell', () => { bufferService = new MockBufferService(5, 5); bufferService.buffer.x = 3; bufferService.buffer.y = 3; - // Lines 2-4 are wrapped - bufferService.buffer.lines.get(3)!.isWrapped = true; - bufferService.buffer.lines.get(4)!.isWrapped = true; }); describe('normal buffer', () => { it('should use the right directional escape sequences', () => { + assert.equal(moveToCellSequence(1, 3, bufferService, false), '\x1b[D\x1b[D'); assert.equal(moveToCellSequence(2, 3, bufferService, false), '\x1b[D'); assert.equal(moveToCellSequence(4, 3, bufferService, false), '\x1b[C'); + assert.equal(moveToCellSequence(5, 3, bufferService, false), '\x1b[C\x1b[C'); }); it('should wrap around entire row instead of doing up and down when the Y value differs', () => { - assert.equal(moveToCellSequence(2, 2, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); - assert.equal(moveToCellSequence(4, 4, bufferService, false), '\x1b[C\x1b[C\x1b[C'); - }); - it('should wrap across 2+ rows', () => { - bufferService.buffer.y = 2; - assert.equal(moveToCellSequence(3, 4, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D', '(3,2) to (3,4) = 10 cells'); - }); - it('should not move the cursor beyond the current wrapped row', () => { assert.equal(moveToCellSequence(1, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); - assert.equal(moveToCellSequence(1, 5, bufferService, false), '\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(2, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(3, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(4, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(5, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(1, 2, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(2, 2, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(3, 2, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(4, 2, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(5, 2, bufferService, false), '\x1b[D\x1b[D\x1b[D'); + assert.equal(moveToCellSequence(1, 4, bufferService, false), '\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(2, 4, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(3, 4, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(4, 4, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(5, 4, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(1, 5, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(2, 5, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(3, 5, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(4, 5, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); + assert.equal(moveToCellSequence(5, 5, bufferService, false), '\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C\x1b[C'); }); it('should use the correct character for application cursor', () => { - assert.equal(moveToCellSequence(2, 1, bufferService, false), '\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D'); - assert.equal(moveToCellSequence(2, 1, bufferService, true), '\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD'); + assert.equal(moveToCellSequence(3, 1, bufferService, true), '\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD'); + assert.equal(moveToCellSequence(3, 2, bufferService, true), '\x1bOD\x1bOD\x1bOD\x1bOD\x1bOD'); + assert.equal(moveToCellSequence(2, 3, bufferService, true), '\x1bOD'); + assert.equal(moveToCellSequence(4, 3, bufferService, true), '\x1bOC'); + assert.equal(moveToCellSequence(3, 4, bufferService, true), '\x1bOC\x1bOC\x1bOC\x1bOC\x1bOC'); + assert.equal(moveToCellSequence(3, 5, bufferService, true), '\x1bOC\x1bOC\x1bOC\x1bOC\x1bOC\x1bOC\x1bOC\x1bOC\x1bOC\x1bOC'); }); }); From 3d53847f30be8feb3b3ea5a8c504ec2e7181ae4a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 3 Feb 2020 07:00:06 -0800 Subject: [PATCH 27/30] Return exit code in Linux unit tests Fixes #2681 --- azure-pipelines.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f6f892c7..e641e716 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -27,7 +27,9 @@ jobs: displayName: 'Install dependencies and build' - script: | yarn test-unit-coverage --forbid-only + EXIT_CODE=$? ./node_modules/.bin/nyc report --reporter=cobertura + exit $EXIT_CODE displayName: 'Unit tests' - script: yarn lint displayName: 'Lint' From 8093a5f4191cc58475dc13ad547fae9d02bf30f9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 3 Feb 2020 07:02:14 -0800 Subject: [PATCH 28/30] Fix lint --- src/browser/input/MoveToCell.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/input/MoveToCell.ts b/src/browser/input/MoveToCell.ts index 1113f957..4a7b55aa 100644 --- a/src/browser/input/MoveToCell.ts +++ b/src/browser/input/MoveToCell.ts @@ -38,7 +38,7 @@ export function moveToCellSequence(targetX: number, targetY: number, bufferServi direction = startY > targetY ? Direction.LEFT : Direction.RIGHT; const rowDifference = Math.abs(startY - targetY); const cellsToMove = colsFromRowEnd(startY > targetY ? targetX : startX, bufferService) + - (rowDifference - 1) * bufferService.cols + 1/*wrap around 1 row*/ + + (rowDifference - 1) * bufferService.cols + 1 /*wrap around 1 row*/ + colsFromRowBeginning(startY > targetY ? startX : targetX, bufferService); return repeat(cellsToMove, sequence(direction, applicationCursor)); } From 412d6fe42cac9843cc988f8110ed487df1fc8f58 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 3 Feb 2020 10:13:13 -0800 Subject: [PATCH 29/30] Refresh cursor row when cursor settings updated Fixes #2683 --- src/Terminal.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Terminal.ts b/src/Terminal.ts index 7d0def9b..f6c9351b 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -308,6 +308,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._renderService?.clear(); this._charSizeService?.measure(); break; + case 'cursorBlink': + case 'cursorStyle': + // The DOM renderer needs a row refresh to update the cursor styles + this.refresh(this.buffer.y, this.buffer.y); + break; case 'drawBoldTextInBrightColors': case 'letterSpacing': case 'lineHeight': From 4a567c9f5ae080b401c9e1cf0d1fa9077df6f8ef Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 3 Feb 2020 10:56:46 -0800 Subject: [PATCH 30/30] Remove old unused options from codebase Fixes #2675 --- demo/client.ts | 3 --- src/Types.d.ts | 3 --- src/common/services/OptionsService.ts | 2 -- src/common/services/Services.ts | 2 -- src/public/Terminal.ts | 8 ++------ typings/xterm.d.ts | 28 +++------------------------ 6 files changed, 5 insertions(+), 41 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 2a0339eb..c07d0081 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -248,10 +248,7 @@ function initOptions(term: TerminalType): void { // Internal only options 'cancelEvents', 'convertEol', - 'handler', - 'screenKeys', 'termName', - 'useFlowControl', // Complex option 'theme' ]; diff --git a/src/Types.d.ts b/src/Types.d.ts index 0b875029..0f3a7751 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -220,10 +220,7 @@ export interface ITerminalOptions extends IPublicTerminalOptions { [key: string]: any; cancelEvents?: boolean; convertEol?: boolean; - handler?: (data: string) => void; - screenKeys?: boolean; termName?: string; - useFlowControl?: boolean; } export interface IBrowser { diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index b855974a..4e0b5e8b 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -49,9 +49,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ convertEol: false, termName: 'xterm', - screenKeys: false, cancelEvents: false, - useFlowControl: false, wordSeparator: ' ()[]{}\',"`' }); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index ce7b33f7..fd11b927 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -278,9 +278,7 @@ export interface ITerminalOptions { [key: string]: any; cancelEvents: boolean; convertEol: boolean; - screenKeys: boolean; termName: string; - useFlowControl: boolean; } export interface ITheme { diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 4f70e347..c96af698 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -146,10 +146,8 @@ export class Terminal implements ITerminalApi { this._core.paste(data); } public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string; - public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; - public getOption(key: 'colors'): string[]; + public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell'): boolean; public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number; - public getOption(key: 'handler'): (data: string) => void; public getOption(key: string): any; public getOption(key: any): any { return this._core.optionsService.getOption(key); @@ -159,10 +157,8 @@ export class Terminal implements ITerminalApi { public setOption(key: 'logLevel', value: 'debug' | 'info' | 'warn' | 'error' | 'off'): void; public setOption(key: 'bellStyle', value: 'none' | 'visual' | 'sound' | 'both'): void; public setOption(key: 'cursorStyle', value: 'block' | 'underline' | 'bar'): void; - public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; - public setOption(key: 'colors', value: string[]): void; + public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell', value: boolean): void; public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void; - public setOption(key: 'handler', value: (data: string) => void): void; public setOption(key: 'theme', value: ITheme): void; public setOption(key: 'cols' | 'rows', value: number): void; public setOption(key: string, value: any): void; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index d0479897..99c1440d 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -748,22 +748,12 @@ declare module 'xterm' { * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: 'colors'): string[]; + getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell' | 'windowsMode'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. */ getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: 'handler'): (data: string) => void; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -805,25 +795,13 @@ declare module 'xterm' { * @param key The option key. * @param value The option value. */ - setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'colors', value: string[]): void; + setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'visualBell' | 'windowsMode', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'handler', value: (data: string) => void): void; /** * Sets an option on the terminal. * @param key The option key. @@ -1171,7 +1149,7 @@ declare module 'xterm' { * String indicating the Unicode version provided. */ readonly version: string; - + /** * Unicode version dependent wcwidth implementation. */