Skip to content

Commit

Permalink
Add ambiguousIsNarrow option (#34)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
seachicken and sindresorhus committed Jan 9, 2022
1 parent fd57509 commit 8438ea2
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 9 deletions.
11 changes: 10 additions & 1 deletion index.d.ts
@@ -1,3 +1,12 @@
export interface Options {
/**
Count [ambiguous width characters](https://www.unicode.org/reports/tr11/#Ambiguous) as having narrow width (count of 1) instead of wide width (count of 2).
@default false
*/
readonly ambiguousIsNarrow: boolean;
}

/**
Get the visual width of a string - the number of columns required to display it.
Expand All @@ -17,4 +26,4 @@ stringWidth('\u001B[1m古\u001B[22m');
//=> 2
```
*/
export default function stringWidth(string: string): number;
export default function stringWidth(string: string, options?: Options): number;
21 changes: 14 additions & 7 deletions index.js
@@ -1,8 +1,8 @@
import stripAnsi from 'strip-ansi';
import isFullwidthCodePoint from 'is-fullwidth-code-point';
import eastAsianWidth from 'eastasianwidth';
import emojiRegex from 'emoji-regex';

export default function stringWidth(string) {
export default function stringWidth(string, options = {}) {
if (typeof string !== 'string' || string.length === 0) {
return 0;
}
Expand All @@ -15,6 +15,7 @@ export default function stringWidth(string) {

string = string.replace(emojiRegex(), ' ');

const ambiguousCharWidth = options.ambiguousIsNarrow ? 1 : 2;
let width = 0;

for (let index = 0; index < string.length; index++) {
Expand All @@ -30,12 +31,18 @@ export default function stringWidth(string) {
continue;
}

// Surrogates
if (codePoint > 0xFFFF) {
index++;
const code = eastAsianWidth.eastAsianWidth(string.charAt(index));
switch (code) {
case 'F':
case 'W':
width += 2;
break;
case 'A':
width += ambiguousCharWidth;
break;
default:
width += 1;
}

width += isFullwidthCodePoint(codePoint) ? 2 : 1;
}

return width;
Expand Down
2 changes: 2 additions & 0 deletions index.test-d.ts
Expand Up @@ -2,3 +2,5 @@ import {expectType} from 'tsd';
import stringWidth from './index.js';

expectType<number>(stringWidth('古'));

expectType<number>(stringWidth('★', {ambiguousIsNarrow: true}));
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -47,8 +47,8 @@
"fixed-width"
],
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
"is-fullwidth-code-point": "^4.0.0",
"strip-ansi": "^7.0.1"
},
"devDependencies": {
Expand Down
21 changes: 21 additions & 0 deletions readme.md
Expand Up @@ -27,6 +27,27 @@ stringWidth('\u001B[1m古\u001B[22m');
//=> 2
```

## API

### stringWidth(string, options?)

#### string

Type: `string`

The string to be counted.

#### options

Type: `object`

##### ambiguousIsNarrow

Type: `boolean`\
Default: `false`

Count [ambiguous width characters](https://www.unicode.org/reports/tr11/#Ambiguous) as having narrow width (count of 1) instead of wide width (count of 2).

## Related

- [string-width-cli](https://github.com/sindresorhus/string-width-cli) - CLI for this module
Expand Down
2 changes: 2 additions & 0 deletions test.js
Expand Up @@ -5,6 +5,8 @@ test('main', t => {
t.is(stringWidth('abcde'), 5);
t.is(stringWidth('古池や'), 6);
t.is(stringWidth('あいうabc'), 9);
t.is(stringWidth('あいう★'), 8);
t.is(stringWidth('あいう★', {ambiguousIsNarrow: true}), 7);
t.is(stringWidth('ノード.js'), 9);
t.is(stringWidth('你好'), 4);
t.is(stringWidth('안녕하세요'), 10);
Expand Down

0 comments on commit 8438ea2

Please sign in to comment.