Skip to content

Commit baf4461

Browse files
ammarbinfaisalsindresorhus
andcommittedJun 13, 2019
Add preferTruncationOnSpace option (#11)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
1 parent 21f9d9b commit baf4461

File tree

5 files changed

+94
-1
lines changed

5 files changed

+94
-1
lines changed
 

‎index.d.ts

+19
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ declare namespace cliTruncate {
2828
```
2929
*/
3030
readonly space?: boolean;
31+
32+
/**
33+
Truncate the string from a whitespace if it is within 3 characters from the actual breaking point.
34+
35+
@default false
36+
37+
@example
38+
```
39+
cliTruncate('unicorns rainbow dragons', 20, {position: 'start', preferTruncationOnSpace: true});
40+
//=> '…rainbow dragons'
41+
42+
cliTruncate('unicorns rainbow dragons', 20, {position: 'middle', preferTruncationOnSpace: true});
43+
//=> 'unicorns…dragons'
44+
45+
cliTruncate('unicorns rainbow dragons', 6, {position: 'end', preferTruncationOnSpace: true});
46+
//=> 'unico…'
47+
````
48+
*/
49+
readonly preferTruncationOnSpace?: boolean;
3150
}
3251
}
3352

‎index.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ const stringWidth = require('string-width');
55
module.exports = (text, columns, options) => {
66
options = {
77
position: 'end',
8+
preferTruncationOnSpace: false,
89
...options
910
};
1011

11-
const {position, space} = options;
12+
const {position, space, preferTruncationOnSpace} = options;
1213
let ellipsis = '…';
1314
let ellipsisWidth = 1;
1415

@@ -35,6 +36,11 @@ module.exports = (text, columns, options) => {
3536
}
3637

3738
if (position === 'start') {
39+
if (preferTruncationOnSpace) {
40+
const nearestSpace = getIndexOfNearestSpace(text, length - columns + 1, true);
41+
return ellipsis + sliceAnsi(text, nearestSpace, length).trim();
42+
}
43+
3844
if (space === true) {
3945
ellipsis += ' ';
4046
ellipsisWidth = 2;
@@ -50,6 +56,13 @@ module.exports = (text, columns, options) => {
5056
}
5157

5258
const half = Math.floor(columns / 2);
59+
60+
if (preferTruncationOnSpace) {
61+
const spaceNearFirstBreakPoint = getIndexOfNearestSpace(text, half);
62+
const spaceNearSecondBreakPoint = getIndexOfNearestSpace(text, length - (columns - half) + 1, true);
63+
return sliceAnsi(text, 0, spaceNearFirstBreakPoint) + ellipsis + sliceAnsi(text, spaceNearSecondBreakPoint, length).trim();
64+
}
65+
5366
return (
5467
sliceAnsi(text, 0, half) +
5568
ellipsis +
@@ -58,6 +71,11 @@ module.exports = (text, columns, options) => {
5871
}
5972

6073
if (position === 'end') {
74+
if (preferTruncationOnSpace) {
75+
const nearestSpace = getIndexOfNearestSpace(text, columns - 1);
76+
return sliceAnsi(text, 0, nearestSpace) + ellipsis;
77+
}
78+
6179
if (space === true) {
6280
ellipsis = ' ' + ellipsis;
6381
ellipsisWidth = 2;
@@ -68,3 +86,21 @@ module.exports = (text, columns, options) => {
6886

6987
throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${position}`);
7088
};
89+
90+
function getIndexOfNearestSpace(string, index, shouldSearchRight) {
91+
if (string.charAt(index) === ' ') {
92+
return index;
93+
}
94+
95+
for (let i = 1; i <= 3; i++) {
96+
if (shouldSearchRight) {
97+
if (string.charAt(index + i) === ' ') {
98+
return index + i;
99+
}
100+
} else if (string.charAt(index - i) === ' ') {
101+
return index - i;
102+
}
103+
}
104+
105+
return index;
106+
}

‎index.test-d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ expectType<string>(cliTruncate('unicorn', 4));
55
expectType<string>(cliTruncate('unicorn', 4, {position: 'start'}));
66
expectType<string>(cliTruncate('unicorn', 4, {position: 'middle'}));
77
expectType<string>(cliTruncate('unicorn', 4, {position: 'end'}));
8+
expectType<string>(cliTruncate('unicorn', 4, {position: 'end', preferTruncationOnSpace: true}));

‎readme.md

+28
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ cliTruncate('unicorn', 4, {position: 'start'});
2727
cliTruncate('unicorn', 4, {position: 'middle'});
2828
//=> 'un…n'
2929

30+
cliTruncate('unicorns rainbow dragons', 6, {position: 'end'})
31+
//=> 'unico…'
32+
3033
cliTruncate('\u001B[31municorn\u001B[39m', 4);
3134
//=> '\u001B[31muni\u001B[39m…'
3235

@@ -94,6 +97,31 @@ cliTruncate('unicorns', 7, {position: 'middle', space: true});
9497
//=> 'uni … s'
9598
```
9699

100+
##### preferTruncationOnSpace
101+
102+
Type: `boolean`<br>
103+
Default: `false`
104+
105+
Truncate the string from a whitespace if it is within 3 characters from the actual breaking point.
106+
107+
```js
108+
cliTruncate('unicorns rainbow dragons', 20, {position: 'start', preferTruncationOnSpace: true})
109+
//=> '…rainbow dragons'
110+
111+
// without preferTruncationOnSpace
112+
cliTruncate('unicorns rainbow dragons', 20, {position: 'start'})
113+
//=> '…rns rainbow dragons'
114+
115+
cliTruncate('unicorns rainbow dragons', 20, {position: 'middle', preferTruncationOnSpace: true})
116+
//=> 'unicorns…dragons'
117+
118+
cliTruncate('unicorns rainbow dragons', 6, {position: 'end', preferTruncationOnSpace: true})
119+
//=> 'unico…'
120+
121+
// preferTruncationOnSpace would have no effect if space isn't found within next 3 indexes
122+
cliTruncate('unicorns rainbow dragons', 6, {position: 'middle', preferTruncationOnSpace: true})
123+
//=> 'uni…ns'
124+
```
97125

98126
## Related
99127

‎test.js

+9
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,12 @@ test('space option', t => {
3434
t.is(cliTruncate('Plant a tree every day.', 14, {position: 'middle', space: true}), 'Plant a … day.');
3535
t.is(cliTruncate('안녕하세요', 4, {position: 'start', space: true}), '… 요', 'wide char');
3636
});
37+
38+
test('preferTruncationOnSpace option', t => {
39+
t.is(cliTruncate('unicorns are awesome', 15, {position: 'start', preferTruncationOnSpace: true}), '…are awesome');
40+
t.is(cliTruncate('dragons are awesome', 15, {position: 'end', preferTruncationOnSpace: true}), 'dragons are…');
41+
t.is(cliTruncate('unicorns rainbow dragons', 6, {position: 'start', preferTruncationOnSpace: true}), '…agons');
42+
t.is(cliTruncate('unicorns rainbow dragons', 6, {position: 'end', preferTruncationOnSpace: true}), 'unico…');
43+
t.is(cliTruncate('unicorns rainbow dragons', 6, {position: 'middle', preferTruncationOnSpace: true}), 'uni…ns');
44+
t.is(cliTruncate('unicorns partying with dragons', 20, {position: 'middle', preferTruncationOnSpace: true}), 'unicorns…dragons');
45+
});

0 commit comments

Comments
 (0)
Please sign in to comment.