/** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { IUnicodeService, IUnicodeVersionProvider } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { UnicodeV6 } from 'common/input/UnicodeV6'; export class UnicodeService implements IUnicodeService { public serviceBrand: any; private _providers: {[key: string]: IUnicodeVersionProvider} = Object.create(null); private _active: string = ''; 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); } public get activeVersion(): string { return this._active; } public set activeVersion(version: string) { if (!this._providers[version]) { throw new Error(`unknown Unicode version "${version}"`); } this._active = version; this._activeProvider = this._providers[version]; this._onChange.fire(version); } public register(provider: IUnicodeVersionProvider): void { this._providers[provider.version] = provider; } /** * Unicode version dependent interface. */ public wcwidth(num: number): number { return this._activeProvider.wcwidth(num); } public getStringCellWidth(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 + this.wcwidth(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 += this.wcwidth(second); } } result += this.wcwidth(code); } return result; } }