Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions lib/node_modules/@stdlib/string/base/left-pad/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@
// MODULES //

var repeat = require( '@stdlib/string/base/repeat' );
var numGraphemeClusters = require( '@stdlib/string/num-grapheme-clusters' );
var ceil = require( '@stdlib/math/base/special/ceil' );


// MAIN //

/**
* Left pads a string such that the padded string has a length of at least `len`.
* Left pads a string such that the padded string has a length of at least `len` grapheme clusters (i.e., user-perceived characters).
*
* @param {string} str - string to pad
* @param {NonNegativeInteger} len - minimum string length
* @param {NonNegativeInteger} len - minimum string length (in grapheme clusters)
* @param {string} pad - string used to pad
* @returns {string} padded string
*
Expand All @@ -45,9 +46,14 @@ var ceil = require( '@stdlib/math/base/special/ceil' );
* @example
* var str = lpad( 'boop', 12, 'beep' );
* // returns 'beepbeepboop'
*
* @example
* // Padding counts grapheme clusters, not UTF-16 code units, so a surrogate pair counts as one character:
* var str = lpad( 'πŸ˜€', 3, 'x' );
* // returns 'xxπŸ˜€'
*/
function lpad( str, len, pad ) {
var n = ( len - str.length ) / pad.length;
var n = ( len - numGraphemeClusters( str ) ) / numGraphemeClusters( pad );
if ( n <= 0 ) {
return str;
}
Expand Down
18 changes: 18 additions & 0 deletions lib/node_modules/@stdlib/string/base/left-pad/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,21 @@ tape( 'if the specified string length is less than or equal to the input string
t.strictEqual( lpad( 'boop', 4, 'beep' ), 'boop', 'returns input string (=)' );
t.end();
});

tape( 'the function measures string length in grapheme clusters, not UTF-16 code units, so a string containing a surrogate pair is treated as one character', function test( t ) {
var str = lpad( 'πŸ˜€', 3, 'x' );
t.strictEqual( str, 'xxπŸ˜€', 'pads based on grapheme cluster count' );
t.end();
});

tape( 'the function supports padding with a multi-code-unit pad string without splitting a surrogate pair', function test( t ) {
var str = lpad( 'a', 3, 'πŸ˜€' );
t.strictEqual( str, 'πŸ˜€πŸ˜€a', 'repeats the whole pad character' );
t.end();
});

tape( 'KNOWN LIMITATION: if the pad string is a bare Unicode combining mark, repeating it does not increase the grapheme cluster count (per UAX #29 GB9, consecutive Extend characters merge into one cluster), so the output may fall short of the requested length', function test( t ) {
var str = lpad( 'a', 5, '̈' ); // U+0308 COMBINING DIAERESIS
t.strictEqual( str, '̈̈̈̈a', 'pinned current behavior; result has only 2 grapheme clusters, not 5' );
t.end();
});
12 changes: 9 additions & 3 deletions lib/node_modules/@stdlib/string/base/right-pad/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@
// MODULES //

var repeat = require( '@stdlib/string/base/repeat' );
var numGraphemeClusters = require( '@stdlib/string/num-grapheme-clusters' );
var ceil = require( '@stdlib/math/base/special/ceil' );


// MAIN //

/**
* Right pads a string such that the padded string has a length of at least `len`.
* Right pads a string such that the padded string has a length of at least `len` grapheme clusters (i.e., user-perceived characters).
*
* @param {string} str - string to pad
* @param {NonNegativeInteger} len - minimum string length
* @param {NonNegativeInteger} len - minimum string length (in grapheme clusters)
* @param {string} pad - string used to pad
* @returns {string} padded string
*
Expand All @@ -45,9 +46,14 @@ var ceil = require( '@stdlib/math/base/special/ceil' );
* @example
* var str = rpad( 'boop', 12, 'beep' );
* // returns 'boopbeepbeep'
*
* @example
* // Padding counts grapheme clusters, not UTF-16 code units, so a surrogate pair counts as one character:
* var str = rpad( 'πŸ˜€', 3, 'x' );
* // returns 'πŸ˜€xx'
*/
function rpad( str, len, pad ) {
var n = ( len - str.length ) / pad.length;
var n = ( len - numGraphemeClusters( str ) ) / numGraphemeClusters( pad );
if ( n <= 0 ) {
return str;
}
Expand Down
18 changes: 18 additions & 0 deletions lib/node_modules/@stdlib/string/base/right-pad/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,21 @@ tape( 'if the specified string length is less than or equal to the input string
t.strictEqual( rpad( 'boop', 4, 'beep' ), 'boop', 'returns input string (=)' );
t.end();
});

tape( 'the function measures string length in grapheme clusters, not UTF-16 code units, so a string containing a surrogate pair is treated as one character', function test( t ) {
var str = rpad( 'πŸ˜€', 3, 'x' );
t.strictEqual( str, 'πŸ˜€xx', 'pads based on grapheme cluster count' );
t.end();
});

tape( 'the function supports padding with a multi-code-unit pad string without splitting a surrogate pair', function test( t ) {
var str = rpad( 'a', 3, 'πŸ˜€' );
t.strictEqual( str, 'aπŸ˜€πŸ˜€', 'repeats the whole pad character' );
t.end();
});

tape( 'KNOWN LIMITATION: if the pad string is a bare Unicode combining mark, repeating it does not increase the grapheme cluster count (per UAX #29 GB9, consecutive Extend characters merge into the preceding cluster), so the output may fall short of the requested length', function test( t ) {
var str = rpad( 'a', 5, '̈' ); // U+0308 COMBINING DIAERESIS
t.strictEqual( str, 'ä̈̈̈', 'pinned current behavior; result has only 1 grapheme cluster, not 5' );
t.end();
});
12 changes: 11 additions & 1 deletion lib/node_modules/@stdlib/string/left-pad/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ var lpad = require( '@stdlib/string/left-pad' );

#### lpad( str, len\[, pad] )

Left pads a string such that the padded string has a length of **at least** `len`.
Left pads a string such that the padded string has a length of **at least** `len` grapheme clusters (i.e., user-perceived characters, not UTF-16 code units).

```javascript
var str = lpad( 'a', 5 );
// returns ' a'
```

```javascript
// A surrogate pair (e.g., an emoji) counts as a single character:
var str = lpad( 'πŸ˜€', 3, 'x' );
// returns 'xxπŸ˜€'
```

By default, an input string is padded with a Unicode "space" character (U+0020). To pad with a different character or sequence of characters, provide a `pad` string.

```javascript
Expand Down Expand Up @@ -67,6 +73,8 @@ str = lpad( 'boop', 12, 'beep' );
// returns 'epbeepboop'
```

- Length is measured in grapheme clusters. If `pad` is a string whose grapheme cluster count does not grow when repeated β€” a bare Unicode combining mark is the notable case, per [UAX #29][unicode-uax-29] a run of `Extend` characters with no preceding base character merges into a single cluster no matter how many times it repeats β€” the returned string may have fewer than `len` grapheme clusters.

- This function differs from [`String.prototype.padStart`][mdn-string-padstart] in the following ways:

- The function is **not** guaranteed to return a string having a length exactly equal to `len` (as explained above).
Expand Down Expand Up @@ -220,6 +228,8 @@ $ echo -n 'boop\tbeep' | lpad --len 8 --split '\t'

[mdn-string-padstart]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart

[unicode-uax-29]: https://unicode.org/reports/tr29/

<!-- <related-links> -->

[@stdlib/string/pad]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/string/pad
Expand Down
6 changes: 6 additions & 0 deletions lib/node_modules/@stdlib/string/left-pad/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,9 @@ tape( 'if the specified string length is less than or equal to the input string
t.strictEqual( lpad( 'boop', 4, 'beep' ), 'boop', 'returns input string (=)' );
t.end();
});

tape( 'the function measures string length in grapheme clusters, not UTF-16 code units, so a string containing a surrogate pair is treated as one character', function test( t ) {
var str = lpad( 'πŸ˜€', 3, 'x' );
t.strictEqual( str, 'xxπŸ˜€', 'pads based on grapheme cluster count' );
t.end();
});
10 changes: 9 additions & 1 deletion lib/node_modules/@stdlib/string/pad/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,21 @@ var pad = require( '@stdlib/string/pad' );

#### pad( str, len\[, options] )

Pads a `string` such that the padded `string` has a `length` of `len`.
Pads a `string` such that the padded `string` has a `length` of `len` grapheme clusters (i.e., user-perceived characters, not UTF-16 code units).

```javascript
var str = pad( 'a', 5 );
// returns 'a '
```

```javascript
// A surrogate pair (e.g., an emoji) counts as a single character and is never split apart:
var str = pad( 'πŸ˜€', 3, {
'rpad': 'x'
});
// returns 'πŸ˜€xx'
```

The function accepts the following `options`:

- **lpad**: `string` used to left pad. Default: `''`.
Expand Down
31 changes: 19 additions & 12 deletions lib/node_modules/@stdlib/string/pad/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ var ceil = require( '@stdlib/math/base/special/ceil' );
var lpad = require( '@stdlib/string/left-pad' );
var rpad = require( '@stdlib/string/right-pad' );
var abs = require( '@stdlib/math/base/special/abs' );
var numGraphemeClusters = require( '@stdlib/string/num-grapheme-clusters' );
var sliceGraphemeClusters = require( '@stdlib/string/base/slice-grapheme-clusters' );
var FLOAT64_MAX_SAFE_INTEGER = require( '@stdlib/constants/float64/max-safe-integer' );
var validate = require( './validate.js' );

Expand Down Expand Up @@ -90,6 +92,8 @@ function pad( str, len, options ) {
var isodd;
var right;
var left;
var slen;
var tlen;
var opts;
var err;
var tmp;
Expand All @@ -110,8 +114,10 @@ function pad( str, len, options ) {
throw err;
}
}
// Note: `len` counts grapheme clusters (i.e., user-perceived characters), not UTF-16 code units, so that multi-code-unit characters (e.g., astral-plane code points, combining marks) are never split apart.
slen = numGraphemeClusters( str );
if ( opts.lpad && opts.rpad ) {
n = ( len-str.length ) / 2;
n = ( len-slen ) / 2;
if ( n === 0 ) {
return str;
}
Expand All @@ -122,25 +128,25 @@ function pad( str, len, options ) {
if ( n < 0 ) {
n = floor( abs( n ) );
nleft = n;
nright = str.length - n;
nright = slen - n;

// If |len-str.length| is an odd number, take away an additional character from one side...
// If |len-slen| is an odd number, take away an additional character from one side...
if ( isodd ) {
if ( opts.centerRight ) {
nright -= 1;
} else {
nleft += 1;
}
}
return str.substring( nleft, nright );
return sliceGraphemeClusters( str, nleft, nright );
}
nleft = ceil( n / opts.lpad.length );
nleft = ceil( n / numGraphemeClusters( opts.lpad ) );
left = repeat( opts.lpad, nleft );

nright = ceil( n / opts.rpad.length );
nright = ceil( n / numGraphemeClusters( opts.rpad ) );
right = repeat( opts.rpad, nright );

// If (len-str.length) is an odd number, give one side one extra character...
// If (len-slen) is an odd number, give one side one extra character...
n = tmp;
nleft = n;
nright = n;
Expand All @@ -151,19 +157,20 @@ function pad( str, len, options ) {
nright += 1;
}
}
left = left.substring( 0, nleft );
right = right.substring( 0, nright );
left = sliceGraphemeClusters( left, 0, nleft );
right = sliceGraphemeClusters( right, 0, nright );
return left + str + right;
}
if ( opts.lpad ) {
tmp = lpad( str, len, opts.lpad );
return tmp.substring( tmp.length-len );
tlen = numGraphemeClusters( tmp );
return sliceGraphemeClusters( tmp, tlen-len, tlen );
}
if ( opts.rpad ) {
return ( rpad( str, len, opts.rpad ) ).substring( 0, len );
return sliceGraphemeClusters( rpad( str, len, opts.rpad ), 0, len );
}
if ( opts.rpad === void 0 ) {
return ( rpad( str, len, ' ' ) ).substring( 0, len );
return sliceGraphemeClusters( rpad( str, len, ' ' ), 0, len );
}
throw new RangeError( format( 'invalid argument. At least one padding option must have a length greater than 0. Left padding: `%s`. Right padding: `%s`.', opts.lpad, opts.rpad ) );
}
Expand Down
60 changes: 60 additions & 0 deletions lib/node_modules/@stdlib/string/pad/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,63 @@ tape( 'if the specified string length is less than or equal to the input string

t.end();
});

tape( 'the function measures string length in grapheme clusters, not UTF-16 code units, so a string containing a surrogate pair is treated as one character', function test( t ) {
var str;

str = pad( 'πŸ˜€', 3 );
t.strictEqual( str, 'πŸ˜€ ', 'right padded, default pad string' );

str = pad( 'πŸ˜€', 3, {
'lpad': 'x'
});
t.strictEqual( str, 'xxπŸ˜€', 'left padded' );

str = pad( 'πŸ˜€', 3, {
'rpad': 'x'
});
t.strictEqual( str, 'πŸ˜€xx', 'right padded' );

t.end();
});

tape( 'the function never splits a surrogate pair when truncating a string that already exceeds the specified length', function test( t ) {
var str;

// Two astral characters (4 code units, 2 grapheme clusters) trimmed to 1 grapheme cluster:
str = pad( 'πŸ˜€πŸ˜', 1, {
'rpad': 'x'
});
t.strictEqual( str, 'πŸ˜€', 'right padded (<); no lone surrogate' );

str = pad( 'πŸ˜€πŸ˜', 1, {
'lpad': 'x'
});
t.strictEqual( str, '😁', 'left padded (<); no lone surrogate' );

str = pad( 'πŸ˜€πŸ˜πŸ˜‚', 2, {
'lpad': '@',
'rpad': '!'
});
t.strictEqual( str, 'πŸ˜πŸ˜‚', 'centered (<); no lone surrogate' );

t.end();
});

tape( 'the function supports padding with a multi-code-unit pad string without splitting a surrogate pair', function test( t ) {
var str;

str = pad( 'a', 4, {
'lpad': 'πŸ˜€',
'rpad': '!'
});
t.strictEqual( str, 'πŸ˜€a!!', 'left/right padded with an astral lpad character' );

str = pad( 'a', 4, {
'lpad': '!',
'rpad': 'πŸ˜€'
});
t.strictEqual( str, '!aπŸ˜€πŸ˜€', 'left/right padded with an astral rpad character' );

t.end();
});
12 changes: 11 additions & 1 deletion lib/node_modules/@stdlib/string/right-pad/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ var rpad = require( '@stdlib/string/right-pad' );

#### rpad( str, len\[, pad] )

Right pads a `string` such that the padded `string` has a `length` of **at least** `len`.
Right pads a `string` such that the padded `string` has a `length` of **at least** `len` grapheme clusters (i.e., user-perceived characters, not UTF-16 code units).

```javascript
var str = rpad( 'a', 5 );
// returns 'a '
```

```javascript
// A surrogate pair (e.g., an emoji) counts as a single character:
var str = rpad( 'πŸ˜€', 3, 'x' );
// returns 'πŸ˜€xx'
```

By default, an input `string` is padded with `spaces`. To pad with a different character or sequence of characters, provide a `pad` string.

```javascript
Expand Down Expand Up @@ -67,6 +73,8 @@ str = rpad( 'beep', 12, 'boop' );
// returns 'beepboopbo' => length 10
```

- Length is measured in grapheme clusters. If `pad` is a string whose grapheme cluster count does not grow when repeated β€” a bare Unicode combining mark is the notable case, per [UAX #29][unicode-uax-29] a run of `Extend` characters merges into a single cluster no matter how many times it repeats β€” the returned string may have fewer than `len` grapheme clusters.

</section>

<!-- /.notes -->
Expand Down Expand Up @@ -199,6 +207,8 @@ beep%%%%

[mdn-regexp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions

[unicode-uax-29]: https://unicode.org/reports/tr29/

<!-- <related-links> -->

[@stdlib/string/left-pad]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/string/left-pad
Expand Down
6 changes: 6 additions & 0 deletions lib/node_modules/@stdlib/string/right-pad/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,9 @@ tape( 'if the specified string length is less than or equal to the input string
t.strictEqual( rpad( 'beep', 4, 'boop' ), 'beep', 'returns input string (=)' );
t.end();
});

tape( 'the function measures string length in grapheme clusters, not UTF-16 code units, so a string containing a surrogate pair is treated as one character', function test( t ) {
var str = rpad( 'πŸ˜€', 3, 'x' );
t.strictEqual( str, 'πŸ˜€xx', 'pads based on grapheme cluster count' );
t.end();
});
Loading