Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Bridgewater <ruben@bridgewater.de>2019-12-31 17:07:37 +0300
committerRuben Bridgewater <ruben@bridgewater.de>2020-01-10 11:11:51 +0300
commit539df7387dfc0b35840817e0a710a31db49d5a5a (patch)
tree88372f731184e9cdfbe9ac38fba06951d6f89203 /lib/internal/readline
parentb52bf605187d65be272a0ab8fb7f5623d8934f18 (diff)
readline: improve getStringWidth()
1. Simplify the getStringWidth function used by Intl builds by removing dead code (the options were unused) and by refactoring the logic. 2. Improve the getStringWidth unicode handling used by non-Intl builds. The getStringWidth function returned the wrong width for multiple inputs. It's now improved by supporting various zero width characters and more full width characters. PR-URL: https://github.com/nodejs/node/pull/31112 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib/internal/readline')
-rw-r--r--lib/internal/readline/utils.js74
1 files changed, 28 insertions, 46 deletions
diff --git a/lib/internal/readline/utils.js b/lib/internal/readline/utils.js
index 6a2d1553655..e4d718d94f4 100644
--- a/lib/internal/readline/utils.js
+++ b/lib/internal/readline/utils.js
@@ -1,8 +1,6 @@
'use strict';
const {
- Boolean,
- NumberIsInteger,
RegExp,
Symbol,
} = primordials;
@@ -21,7 +19,6 @@ const kEscape = '\x1b';
const kSubstringSearch = Symbol('kSubstringSearch');
let getStringWidth;
-let isFullWidthCodePoint;
function CSI(strings, ...args) {
let ret = `${kEscape}[`;
@@ -41,63 +38,37 @@ CSI.kClearScreenDown = CSI`0J`;
if (internalBinding('config').hasIntl) {
const icu = internalBinding('icu');
- getStringWidth = function getStringWidth(str, options) {
- options = options || {};
- if (NumberIsInteger(str)) {
- // Provide information about the character with code point 'str'.
- return icu.getStringWidth(
- str,
- Boolean(options.ambiguousAsFullWidth),
- false
- );
- }
- str = stripVTControlCharacters(String(str));
+ // icu.getStringWidth(string, ambiguousAsFullWidth, expandEmojiSequence)
+ // Defaults: ambiguousAsFullWidth = false; expandEmojiSequence = true;
+ getStringWidth = function getStringWidth(str) {
let width = 0;
+ str = stripVTControlCharacters(str);
for (let i = 0; i < str.length; i++) {
// Try to avoid calling into C++ by first handling the ASCII portion of
// the string. If it is fully ASCII, we skip the C++ part.
const code = str.charCodeAt(i);
- if (code < 127) {
- width += code >= 32;
- continue;
+ if (code >= 127) {
+ width += icu.getStringWidth(str.slice(i));
+ break;
}
- width += icu.getStringWidth(
- str.slice(i),
- Boolean(options.ambiguousAsFullWidth),
- Boolean(options.expandEmojiSequence)
- );
- break;
+ width += code >= 32 ? 1 : 0;
}
return width;
};
- isFullWidthCodePoint =
- function isFullWidthCodePoint(code, options) {
- if (typeof code !== 'number')
- return false;
- return icu.getStringWidth(code, options) === 2;
- };
} else {
/**
* Returns the number of columns required to display the given string.
*/
getStringWidth = function getStringWidth(str) {
- if (NumberIsInteger(str))
- return isFullWidthCodePoint(str) ? 2 : 1;
-
let width = 0;
- str = stripVTControlCharacters(String(str));
-
- for (let i = 0; i < str.length; i++) {
- const code = str.codePointAt(i);
-
- if (code >= kUTF16SurrogateThreshold) { // Surrogates.
- i++;
- }
+ str = stripVTControlCharacters(str);
+ for (const char of str) {
+ const code = char.codePointAt(0);
if (isFullWidthCodePoint(code)) {
width += 2;
- } else {
+ } else if (!isZeroWidthCodePoint(code)) {
width++;
}
}
@@ -109,10 +80,10 @@ if (internalBinding('config').hasIntl) {
* Returns true if the character represented by a given
* Unicode code point is full-width. Otherwise returns false.
*/
- isFullWidthCodePoint = function isFullWidthCodePoint(code) {
- // Code points are derived from:
+ const isFullWidthCodePoint = (code) => {
+ // Code points are partially derived from:
// http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
- return NumberIsInteger(code) && code >= 0x1100 && (
+ return code >= 0x1100 && (
code <= 0x115f || // Hangul Jamo
code === 0x2329 || // LEFT-POINTING ANGLE BRACKET
code === 0x232a || // RIGHT-POINTING ANGLE BRACKET
@@ -139,10 +110,23 @@ if (internalBinding('config').hasIntl) {
(code >= 0x1b000 && code <= 0x1b001) ||
// Enclosed Ideographic Supplement
(code >= 0x1f200 && code <= 0x1f251) ||
+ // Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff
+ // Emoticons 0x1f600 - 0x1f64f
+ (code >= 0x1f300 && code <= 0x1f64f) ||
// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
(code >= 0x20000 && code <= 0x3fffd)
);
};
+
+ const isZeroWidthCodePoint = (code) => {
+ return code <= 0x1F || // C0 control codes
+ (code > 0x7F && code <= 0x9F) || // C1 control codes
+ (code >= 0x0300 && code <= 0x036F) || // Combining Diacritical Marks
+ (code >= 0x200B && code <= 0x200F) || // Modifying Invisible Characters
+ (code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors
+ (code >= 0xFE20 && code <= 0xFE2F) || // Combining Half Marks
+ (code >= 0xE0100 && code <= 0xE01EF); // Variation Selectors
+ };
}
/**
@@ -471,9 +455,7 @@ module.exports = {
commonPrefix,
emitKeys,
getStringWidth,
- isFullWidthCodePoint,
kSubstringSearch,
- kUTF16SurrogateThreshold,
stripVTControlCharacters,
CSI
};