(str, width, { align = 'left', ellipsis = false, pad = true } = {})
| 27 | } |
| 28 | |
| 29 | function fitAnsi(str, width, { align = 'left', ellipsis = false, pad = true } = {}) { |
| 30 | if (width <= 0) return ''; |
| 31 | let i = 0; |
| 32 | const items = []; |
| 33 | let totalVis = 0; |
| 34 | const chars = Array.from(str); |
| 35 | |
| 36 | while (i < chars.length) { |
| 37 | if (chars[i] === '\x1b') { |
| 38 | let j = i; |
| 39 | while (j < chars.length) { |
| 40 | const c = chars[j]; |
| 41 | if (j > i && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) { |
| 42 | break; |
| 43 | } |
| 44 | j++; |
| 45 | } |
| 46 | if (j < chars.length) { |
| 47 | items.push({ isAnsi: true, code: chars.slice(i, j + 1).join(''), width: 0 }); |
| 48 | i = j + 1; |
| 49 | continue; |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | const ch = chars[i]; |
| 54 | const w = visualWidth(ch); |
| 55 | items.push({ isAnsi: false, char: ch, width: w }); |
| 56 | totalVis += w; |
| 57 | i++; |
| 58 | } |
| 59 | |
| 60 | if (totalVis <= width) { |
| 61 | let body = ''; |
| 62 | for (const item of items) { |
| 63 | if (item.isAnsi) body += item.code; |
| 64 | else body += item.char; |
| 65 | } |
| 66 | if (str.includes('\x1b') && !body.endsWith('\x1b[0m')) body += '\x1b[0m'; |
| 67 | |
| 68 | if (pad) { |
| 69 | const padAmount = width - totalVis; |
| 70 | const paddedContent = padAmount > 0 ? ' '.repeat(padAmount) : ''; |
| 71 | return align === 'right' ? paddedContent + body : body + paddedContent; |
| 72 | } |
| 73 | return body; |
| 74 | } |
| 75 | |
| 76 | let targetWidth = width; |
| 77 | let addDots = false; |
| 78 | if (ellipsis && width > 3) { |
| 79 | targetWidth = width - 3; |
| 80 | addDots = true; |
| 81 | } |
| 82 | |
| 83 | let currentWidth = 0; |
| 84 | const keepItems = []; |
| 85 | for (const item of items) { |
| 86 | if (item.isAnsi) { |
no test coverage detected