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.
103 lines
2.5 KiB
103 lines
2.5 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 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.includes(';')) { |
|
ansiCode = ansiCode.split(';')[0][0] + '0'; |
|
} |
|
|
|
const item = ansiStyles.codes.get(Number.parseInt(ansiCode, 10)); |
|
if (item) { |
|
const indexEscape = ansiCodes.indexOf(item.toString()); |
|
if (indexEscape === -1) { |
|
output.push(wrapAnsi(isEscapes ? item : ansiCodeOrigin)); |
|
} else { |
|
ansiCodes.splice(indexEscape, 1); |
|
} |
|
} 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(Number.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]; |
|
const ansiCodes = []; |
|
|
|
let stringEnd = 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 < stringEnd) { |
|
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 (typeof end !== 'number') { |
|
stringEnd++; |
|
} |
|
} |
|
|
|
if (visible > begin && visible <= stringEnd) { |
|
output += character; |
|
} else if (visible === begin && !isInsideEscape && ansiCode !== undefined) { |
|
output = checkAnsi(ansiCodes); |
|
} else if (visible >= stringEnd) { |
|
output += checkAnsi(ansiCodes, true, ansiCode); |
|
break; |
|
} |
|
} |
|
|
|
return output; |
|
};
|
|
|