|
'use strict'; |
|
const isFullwidthCodePoint = require('is-fullwidth-code-point'); |
|
const astralRegex = require('astral-regex'); |
|
const ansiStyles = require('ansi-styles'); |
|
|
|
const ESCAPES = [ |
|
'\u001B', |
|
'\u009B' |
|
]; |
|
|
|
const wrapAnsi = code => `${ESCAPES[0]}[${code}m`; |
|
|
|
const checkAnsi = (ansiCodes, isEscapes, endAnsiCode) => { |
|
let output = []; |
|
ansiCodes = [...ansiCodes]; |
|
|
|
for (let ansiCode of ansiCodes) { |
|
const ansiCodeOrigin = ansiCode; |
|
if (ansiCode.match(';')) { |
|
ansiCode = ansiCode.split(';')[0][0] + '0'; |
|
} |
|
|
|
const item = ansiStyles.codes.get(parseInt(ansiCode, 10)); |
|
if (item) { |
|
const indexEscape = ansiCodes.indexOf(item.toString()); |
|
if (indexEscape >= 0) { |
|
ansiCodes.splice(indexEscape, 1); |
|
} else { |
|
output.push(wrapAnsi(isEscapes ? item : ansiCodeOrigin)); |
|
} |
|
} else if (isEscapes) { |
|
output.push(wrapAnsi(0)); |
|
break; |
|
} else { |
|
output.push(wrapAnsi(ansiCodeOrigin)); |
|
} |
|
} |
|
|
|
if (isEscapes) { |
|
output = output.filter((element, index) => output.indexOf(element) === index); |
|
if (endAnsiCode !== undefined) { |
|
const fistEscapeCode = wrapAnsi(ansiStyles.codes.get(parseInt(endAnsiCode, 10))); |
|
output = output.reduce((current, next) => next === fistEscapeCode ? [next, ...current] : [...current, next], []); |
|
} |
|
} |
|
|
|
return output.join(''); |
|
}; |
|
|
|
module.exports = (string, begin, end) => { |
|
const characters = [...string.normalize()]; |
|
const ansiCodes = []; |
|
|
|
end = typeof end === 'number' ? end : characters.length; |
|
|
|
let isInsideEscape = false; |
|
let ansiCode; |
|
let visible = 0; |
|
let output = ''; |
|
|
|
for (const [index, character] of characters.entries()) { |
|
let leftEscape = false; |
|
|
|
if (ESCAPES.includes(character)) { |
|
const code = /\d[^m]*/.exec(string.slice(index, index + 18)); |
|
ansiCode = code && code.length > 0 ? code[0] : undefined; |
|
if (visible < end) { |
|
isInsideEscape = true; |
|
if (ansiCode !== undefined) { |
|
ansiCodes.push(ansiCode); |
|
} |
|
} |
|
} else if (isInsideEscape && character === 'm') { |
|
isInsideEscape = false; |
|
leftEscape = true; |
|
} |
|
|
|
if (!isInsideEscape && !leftEscape) { |
|
++visible; |
|
} |
|
|
|
if (!astralRegex({exact: true}).test(character) && isFullwidthCodePoint(character.codePointAt())) { |
|
++visible; |
|
} |
|
|
|
if (visible > begin && visible <= end) { |
|
output += character; |
|
} else if (visible === begin && !isInsideEscape && ansiCode !== undefined) { |
|
output = checkAnsi(ansiCodes); |
|
} else if (visible >= end) { |
|
output += checkAnsi(ansiCodes, true, ansiCode); |
|
break; |
|
} |
|
} |
|
|
|
return output; |
|
}; |
|
|