2
2
const stringWidth = require ( 'string-width' ) ;
3
3
const stripAnsi = require ( 'strip-ansi' ) ;
4
4
5
- const ESCAPES = [
5
+ const ESCAPES = new Set ( [
6
6
'\u001B' ,
7
7
'\u009B'
8
- ] ;
8
+ ] ) ;
9
9
10
10
const END_CODE = 39 ;
11
11
@@ -37,26 +37,35 @@ const ESCAPE_CODES = new Map([
37
37
[ 47 , 49 ]
38
38
] ) ;
39
39
40
- const wrapAnsi = code => `${ ESCAPES [ 0 ] } [${ code } m` ;
40
+ const wrapAnsi = code => `${ ESCAPES . values ( ) . next ( ) . value } [${ code } m` ;
41
41
42
42
// Calculate the length of words split on ' ', ignoring
43
43
// the extra characters added by ansi escape codes
44
44
const wordLengths = str => str . split ( ' ' ) . map ( s => stringWidth ( s ) ) ;
45
45
46
46
// Wrap a long word across multiple rows
47
47
// Ansi escape codes do not count towards length
48
- function wrapWord ( rows , word , cols ) {
48
+ const wrapWord = ( rows , word , cols ) => {
49
+ const arr = Array . from ( word ) ;
50
+
49
51
let insideEscape = false ;
50
- let visible = stripAnsi ( rows [ rows . length - 1 ] ) . length ;
52
+ let visible = stringWidth ( stripAnsi ( rows [ rows . length - 1 ] ) ) ;
51
53
52
- for ( let i = 0 ; i < word . length ; i ++ ) {
53
- const x = word [ i ] ;
54
+ for ( const item of arr . entries ( ) ) {
55
+ const i = item [ 0 ] ;
56
+ const char = item [ 1 ] ;
57
+ const charLength = stringWidth ( char ) ;
54
58
55
- rows [ rows . length - 1 ] += x ;
59
+ if ( visible + charLength <= cols ) {
60
+ rows [ rows . length - 1 ] += char ;
61
+ } else {
62
+ rows . push ( char ) ;
63
+ visible = 0 ;
64
+ }
56
65
57
- if ( ESCAPES . indexOf ( x ) !== - 1 ) {
66
+ if ( ESCAPES . has ( char ) ) {
58
67
insideEscape = true ;
59
- } else if ( insideEscape && x === 'm' ) {
68
+ } else if ( insideEscape && char === 'm' ) {
60
69
insideEscape = false ;
61
70
continue ;
62
71
}
@@ -65,9 +74,9 @@ function wrapWord(rows, word, cols) {
65
74
continue ;
66
75
}
67
76
68
- visible ++ ;
77
+ visible += charLength ;
69
78
70
- if ( visible >= cols && i < word . length - 1 ) {
79
+ if ( visible === cols && i < arr . length - 1 ) {
71
80
rows . push ( '' ) ;
72
81
visible = 0 ;
73
82
}
@@ -78,7 +87,7 @@ function wrapWord(rows, word, cols) {
78
87
if ( ! visible && rows [ rows . length - 1 ] . length > 0 && rows . length > 1 ) {
79
88
rows [ rows . length - 2 ] += rows . pop ( ) ;
80
89
}
81
- }
90
+ } ;
82
91
83
92
// The wrap-ansi module can be invoked
84
93
// in either 'hard' or 'soft' wrap mode
@@ -87,7 +96,7 @@ function wrapWord(rows, word, cols) {
87
96
// than cols characters
88
97
//
89
98
// 'soft' allows long words to expand past the column length
90
- function exec ( str , cols , opts ) {
99
+ const exec = ( str , cols , opts ) => {
91
100
const options = opts || { } ;
92
101
93
102
let pre = '' ;
@@ -98,7 +107,10 @@ function exec(str, cols, opts) {
98
107
const words = str . split ( ' ' ) ;
99
108
const rows = [ '' ] ;
100
109
101
- for ( let i = 0 , word ; ( word = words [ i ] ) !== undefined ; i ++ ) {
110
+ for ( const item of Array . from ( words ) . entries ( ) ) {
111
+ const i = item [ 0 ] ;
112
+ const word = item [ 1 ] ;
113
+
102
114
let rowLength = stringWidth ( rows [ rows . length - 1 ] ) ;
103
115
104
116
if ( rowLength ) {
@@ -135,33 +147,35 @@ function exec(str, cols, opts) {
135
147
136
148
pre = rows . map ( x => x . trim ( ) ) . join ( '\n' ) ;
137
149
138
- for ( let j = 0 ; j < pre . length ; j ++ ) {
139
- const y = pre [ j ] ;
150
+ for ( const item of Array . from ( pre ) . entries ( ) ) {
151
+ const i = item [ 0 ] ;
152
+ const char = item [ 1 ] ;
140
153
141
- ret += y ;
154
+ ret += char ;
142
155
143
- if ( ESCAPES . indexOf ( y ) !== - 1 ) {
144
- const code = parseFloat ( / \d [ ^ m ] * / . exec ( pre . slice ( j , j + 4 ) ) ) ;
156
+ if ( ESCAPES . has ( char ) ) {
157
+ const code = parseFloat ( / \d [ ^ m ] * / . exec ( pre . slice ( i , i + 4 ) ) ) ;
145
158
escapeCode = code === END_CODE ? null : code ;
146
159
}
147
160
148
- const code = ESCAPE_CODES . get ( parseInt ( escapeCode , 10 ) ) ;
161
+ const code = ESCAPE_CODES . get ( Number ( escapeCode ) ) ;
149
162
150
163
if ( escapeCode && code ) {
151
- if ( pre [ j + 1 ] === '\n' ) {
164
+ if ( pre [ i + 1 ] === '\n' ) {
152
165
ret += wrapAnsi ( code ) ;
153
- } else if ( y === '\n' ) {
166
+ } else if ( char === '\n' ) {
154
167
ret += wrapAnsi ( escapeCode ) ;
155
168
}
156
169
}
157
170
}
158
171
159
172
return ret ;
160
- }
173
+ } ;
161
174
162
175
// For each newline, invoke the method separately
163
176
module . exports = ( str , cols , opts ) => {
164
177
return String ( str )
178
+ . normalize ( )
165
179
. split ( '\n' )
166
180
. map ( line => exec ( line , cols , opts ) )
167
181
. join ( '\n' ) ;
0 commit comments