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.
59 lines
1.3 KiB
59 lines
1.3 KiB
'use strict'; |
|
const isFullwidthCodePoint = require('is-fullwidth-code-point'); |
|
const astralRegex = require('astral-regex'); |
|
const ansiStyles = require('ansi-styles'); |
|
|
|
const ESCAPES = [ |
|
'\u001B', |
|
'\u009B' |
|
]; |
|
|
|
const END_CODE = 39; |
|
|
|
const wrapAnsi = code => `${ESCAPES[0]}[${code}m`; |
|
|
|
module.exports = (str, begin, end) => { |
|
const arr = [...str.normalize()]; |
|
|
|
end = typeof end === 'number' ? end : arr.length; |
|
|
|
let insideEscape = false; |
|
let escapeCode = null; |
|
let visible = 0; |
|
let output = ''; |
|
|
|
for (const [i, x] of arr.entries()) { |
|
let leftEscape = false; |
|
|
|
if (ESCAPES.includes(x)) { |
|
insideEscape = true; |
|
const code = /\d[^m]*/.exec(str.slice(i, i + 18)); |
|
escapeCode = code === END_CODE ? null : code; |
|
} else if (insideEscape && x === 'm') { |
|
insideEscape = false; |
|
leftEscape = true; |
|
} |
|
|
|
if (!insideEscape && !leftEscape) { |
|
++visible; |
|
} |
|
|
|
if (!astralRegex({exact: true}).test(x) && isFullwidthCodePoint(x.codePointAt())) { |
|
++visible; |
|
} |
|
|
|
if (visible > begin && visible <= end) { |
|
output += x; |
|
} else if (visible === begin && !insideEscape && escapeCode !== null && escapeCode !== END_CODE) { |
|
output += wrapAnsi(escapeCode); |
|
} else if (visible >= end) { |
|
if (escapeCode !== null) { |
|
output += wrapAnsi(ansiStyles.codes.get(parseInt(escapeCode, 10)) || END_CODE); |
|
} |
|
|
|
break; |
|
} |
|
} |
|
|
|
return output; |
|
};
|
|
|