diff --git a/addons/xterm-addon-unicode-graphemes/test/UnicodeGraphemesAddon.api.ts b/addons/xterm-addon-unicode-graphemes/test/UnicodeGraphemesAddon.api.ts index f00e023d..33dc7284 100644 --- a/addons/xterm-addon-unicode-graphemes/test/UnicodeGraphemesAddon.api.ts +++ b/addons/xterm-addon-unicode-graphemes/test/UnicodeGraphemesAddon.api.ts @@ -29,6 +29,9 @@ describe('UnicodeGraphemesAddon', () => { await page.goto(APP); await openTerminal(page); }); + async function evalWidth(str: string): Promise { + return page.evaluate(`window.term._core.unicodeService.getStringCellWidth('${str}')`); + } const ourVersion = '15-graphemes'; it('wcwidth V15 emoji test', async () => { await page.evaluate(` @@ -41,6 +44,23 @@ describe('UnicodeGraphemesAddon', () => { await page.evaluate(`window.term.unicode.activeVersion = '${ourVersion}';`); assert.deepEqual(await page.evaluate(`window.term.unicode.activeVersion`), ourVersion); // v6: 10, V15: 20 - assert.deepEqual(await page.evaluate(`window.term._core.unicodeService.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣')`), 20); + assert.deepEqual(await evalWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'), 20); + // baby with emoji modifier fitzpatrick type-6; baby + assert.deepEqual(await evalWidth('\u{1F476}\u{1F3FF}\u{1F476}'), 4); + // woman+zwj+woman+zwj+boy + assert.deepEqual(await evalWidth('\u{1F469}\u200d\u{1f469}\u200d\u{1f466}'), 2); + // REGIONAL INDICATOR SYMBOL LETTER N and RI O + assert.deepEqual(await evalWidth('\u{1f1f3}\u{1f1f4}_'), 3); + assert.deepEqual(await evalWidth('\u{1f1f3}_\u{1f1f4}'), 3); + // letter a with acute accent + assert.deepEqual(await evalWidth('\u0061\u0301'), 1); + // Korean Jamo + assert.deepEqual(await evalWidth('{\u1100\u1161\u11a8}'), 4); + // coffin with text_presentation + assert.deepEqual(await evalWidth('(\u26b0\ufe0e)'), 3); + // coffin with Emoji_presentation + assert.deepEqual(await evalWidth('(\u26b0\ufe0f)'), 4); + // Égalité (using separate acute) emoij_presentation + assert.deepEqual(await evalWidth(''), 16); }); }); diff --git a/demo/client.ts b/demo/client.ts index 68cdec65..417d0806 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -18,6 +18,7 @@ import { SerializeAddon } from '../addons/xterm-addon-serialize/out/SerializeAdd 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'; +import { UnicodeGraphemesAddon } from '../addons/xterm-addon-unicode-graphemes/out/UnicodeGraphemesAddon'; import { LigaturesAddon } from '../addons/xterm-addon-ligatures/out/LigaturesAddon'; // Use webpacked version (yarn package) @@ -29,6 +30,7 @@ import { LigaturesAddon } from '../addons/xterm-addon-ligatures/out/LigaturesAdd // import { WebLinksAddon } from 'xterm-addon-web-links'; // import { WebglAddon } from 'xterm-addon-webgl'; // import { Unicode11Addon } from 'xterm-addon-unicode11'; +// import { UnicodeGraphemesAddon } from 'xterm-addon-unicode-graphemes'; // import { LigaturesAddon } from 'xterm-addon-ligatures'; // Pulling in the module's types relies on the above, it's looks a @@ -45,6 +47,7 @@ export interface IWindowWithTerminal extends Window { WebLinksAddon?: typeof WebLinksAddon; // eslint-disable-line @typescript-eslint/naming-convention WebglAddon?: typeof WebglAddon; // eslint-disable-line @typescript-eslint/naming-convention Unicode11Addon?: typeof Unicode11Addon; // eslint-disable-line @typescript-eslint/naming-convention + UnicodeGraphemesAddon?: typeof UnicodeGraphemesAddon; // eslint-disable-line @typescript-eslint/naming-convention LigaturesAddon?: typeof LigaturesAddon; // eslint-disable-line @typescript-eslint/naming-convention } declare let window: IWindowWithTerminal; @@ -55,7 +58,7 @@ let socketURL; let socket; let pid; -type AddonType = 'attach' | 'canvas' | 'fit' | 'search' | 'serialize' | 'unicode11' | 'web-links' | 'webgl' | 'ligatures'; +type AddonType = 'attach' | 'canvas' | 'fit' | 'search' | 'serialize' | 'unicode11' | 'unicode-graphemes' | 'web-links' | 'webgl' | 'ligatures'; interface IDemoAddon { name: T; @@ -68,8 +71,9 @@ interface IDemoAddon { T extends 'serialize' ? typeof SerializeAddon : T extends 'web-links' ? typeof WebLinksAddon : T extends 'unicode11' ? typeof Unicode11Addon : - T extends 'ligatures' ? typeof LigaturesAddon : - typeof WebglAddon + T extends 'unicode-graphemes' ? typeof UnicodeGraphemesAddon : + T extends 'ligatures' ? typeof LigaturesAddon : + typeof WebglAddon ); instance?: ( T extends 'attach' ? AttachAddon : @@ -80,8 +84,9 @@ interface IDemoAddon { T extends 'web-links' ? WebLinksAddon : T extends 'webgl' ? WebglAddon : T extends 'unicode11' ? typeof Unicode11Addon : - T extends 'ligatures' ? typeof LigaturesAddon : - never + T extends 'unicode-graphemes' ? typeof UnicodeGraphemesAddon : + T extends 'ligatures' ? typeof LigaturesAddon : + never ); } @@ -94,6 +99,7 @@ const addons: { [T in AddonType]: IDemoAddon } = { 'web-links': { name: 'web-links', ctor: WebLinksAddon, canChange: true }, webgl: { name: 'webgl', ctor: WebglAddon, canChange: true }, unicode11: { name: 'unicode11', ctor: Unicode11Addon, canChange: true }, + 'unicode-graphemes': { name: 'unicode-graphemes', ctor: UnicodeGraphemesAddon, canChange: true }, ligatures: { name: 'ligatures', ctor: LigaturesAddon, canChange: true } }; @@ -162,6 +168,7 @@ const disposeRecreateButtonHandler: () => void = () => { addons.search.instance = undefined; addons.serialize.instance = undefined; addons.unicode11.instance = undefined; + addons['unicode-graphemes'].instance = undefined; addons.ligatures.instance = undefined; addons['web-links'].instance = undefined; addons.webgl.instance = undefined; @@ -208,6 +215,7 @@ if (document.location.pathname === '/test') { window.SearchAddon = SearchAddon; window.SerializeAddon = SerializeAddon; window.Unicode11Addon = Unicode11Addon; + window.UnicodeGraphemesAddon = UnicodeGraphemesAddon; window.LigaturesAddon = LigaturesAddon; window.WebLinksAddon = WebLinksAddon; window.WebglAddon = WebglAddon; @@ -252,6 +260,7 @@ function createTerminal(): void { addons.serialize.instance = new SerializeAddon(); addons.fit.instance = new FitAddon(); addons.unicode11.instance = new Unicode11Addon(); + addons['unicode-graphemes'].instance = new UnicodeGraphemesAddon(); try { // try to start with webgl renderer (might throw on older safari/webkit) addons.webgl.instance = new WebglAddon(); } catch (e) { @@ -262,6 +271,7 @@ function createTerminal(): void { typedTerm.loadAddon(addons.search.instance); typedTerm.loadAddon(addons.serialize.instance); typedTerm.loadAddon(addons.unicode11.instance); + typedTerm.loadAddon(addons['unicode-graphemes'].instance); typedTerm.loadAddon(addons['web-links'].instance); window.term = term; // Expose `term` to window for debugging purposes @@ -554,6 +564,9 @@ function initAddons(term: TerminalType): void { if (name === 'unicode11' && checkbox.checked) { term.unicode.activeVersion = '11'; } + if (name === 'unicode-graphemes' && checkbox.checked) { + term.unicode.activeVersion = '15-graphemes'; + } if (name === 'search' && checkbox.checked) { addon.instance.onDidChangeResults(e => updateFindResults(e)); } @@ -576,6 +589,8 @@ function initAddons(term: TerminalType): void { }, 0); } else if (name === 'unicode11') { term.unicode.activeVersion = '11'; + } else if (name === 'unicode-graphemes') { + term.unicode.activeVersion = '15-graphemes'; } else if (name === 'search') { addon.instance.onDidChangeResults(e => updateFindResults(e)); } @@ -590,7 +605,7 @@ function initAddons(term: TerminalType): void { addons.webgl.instance.textureAtlas.remove(); } else if (name === 'canvas') { addons.canvas.instance.textureAtlas.remove(); - } else if (name === 'unicode11') { + } else if (name === 'unicode11' || name === 'unicode-graphemes') { term.unicode.activeVersion = '6'; } addon.instance!.dispose();