You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
54 lines
1.0 KiB
54 lines
1.0 KiB
import stripAnsi from 'strip-ansi'; |
|
import eastAsianWidth from 'eastasianwidth'; |
|
import emojiRegex from 'emoji-regex'; |
|
|
|
export default function stringWidth(string, options = {}) { |
|
if (typeof string !== 'string' || string.length === 0) { |
|
return 0; |
|
} |
|
|
|
options = { |
|
ambiguousIsNarrow: true, |
|
...options |
|
}; |
|
|
|
string = stripAnsi(string); |
|
|
|
if (string.length === 0) { |
|
return 0; |
|
} |
|
|
|
string = string.replace(emojiRegex(), ' '); |
|
|
|
const ambiguousCharacterWidth = options.ambiguousIsNarrow ? 1 : 2; |
|
let width = 0; |
|
|
|
for (const character of string) { |
|
const codePoint = character.codePointAt(0); |
|
|
|
// Ignore control characters |
|
if (codePoint <= 0x1F || (codePoint >= 0x7F && codePoint <= 0x9F)) { |
|
continue; |
|
} |
|
|
|
// Ignore combining characters |
|
if (codePoint >= 0x300 && codePoint <= 0x36F) { |
|
continue; |
|
} |
|
|
|
const code = eastAsianWidth.eastAsianWidth(character); |
|
switch (code) { |
|
case 'F': |
|
case 'W': |
|
width += 2; |
|
break; |
|
case 'A': |
|
width += ambiguousCharacterWidth; |
|
break; |
|
default: |
|
width += 1; |
|
} |
|
} |
|
|
|
return width; |
|
}
|
|
|