import { checkSupportTerminal } from "../lib/checkSupportTerminal" import { toPrintArgs_Terminal } from "../lib/toPrintArgs_Terminal" import { toPrintArgs_Web } from "../lib/toPrintArgs_Web" /** * 格式化日志输出 * * `[Hero]` 主标题 带背景的标题 * `` 标题 带高亮色的标题 * `<Title|title2|title3>` 标题 多级高亮标题 * `!<Title|title2|title3>` 警告标题 多级高亮标题 * `_subtext_` 备注文本 浅色文本 * `*bold*` 加粗文本 浅色文本 * ![Warn] !Warn! 警告文本 红色警告文本 * `text~~3` paddingEnd 文本末尾填充 * `text3~~` paddingEnd 文本开始填充 * `text~13~` paddingMid 文本中间填充 */ export function fmt(input: any): Segment[] export function fmt(...inputs: any[]): Segment[][] export function fmt(...inputs: any[]): any { let segmentsList = parsingFmtToSegments(inputs) let args: any[] if (checkSupportTerminal()) { args = toPrintArgs_Terminal(segmentsList) } else { args = toPrintArgs_Web(segmentsList) } // console.log("texts:", inputs) // console.log("segmentsList:", segmentsList) // console.log("args:", args) return args } function parsingFmtToSegments(inputs: any[]) { let segmentsList: Segment[][] = [] for (let text of inputs) { let segments: Segment[] = [] if (typeof text === "string") { let SegmentPadding!: SegmentOptions // 解析 padding ~~N 结尾 const paddingEndRegex = /~~(\d+)?$/ let paddingEndMatch = text.match(paddingEndRegex) if (paddingEndMatch) { let paddingLength = parseInt(paddingEndMatch[1] || "0", 10) SegmentPadding = { padLength: paddingLength, padType: "end" } text = text.slice(0, -paddingEndMatch[0].length) } // 解析 padding N~~ 开头 const paddingStartRegex = /^(\d+)?~~/ let paddingStartMatch = text.match(paddingStartRegex) if (paddingStartMatch) { let paddingLength = parseInt(paddingStartMatch[1] || "0", 10) SegmentPadding = { padLength: paddingLength, padType: "start" } text = text.slice(paddingStartMatch[0].length) } // 解析 padding ~N~ 中间 const paddingMidRegex = /~(\d+)~/ let paddingMidMatch = text.match(paddingMidRegex) if (paddingMidMatch) { let paddingLength = parseInt(paddingMidMatch[1], 10) SegmentPadding = { padLength: paddingLength, padType: "mid" } text = text.slice(paddingMidMatch[0].length) } let sub_segments = parsingSegment(text) segments = segments.concat(sub_segments) if (SegmentPadding) { if (SegmentPadding.padType === "end") { segments[segments.length - 1][2] = SegmentPadding } else if (SegmentPadding.padType === "start") { segments[0][2] = SegmentPadding } else if (SegmentPadding.padType === "mid") { segments[0][2] = SegmentPadding } } } else { segments.push([text, SegmentType.Raw]) } segmentsList.push(segments) } return segmentsList } /** * 解析文本段落 */ function parsingSegment(text: any): Segment[] { if (typeof text !== "string") return [[text, SegmentType.Text]] // 空字符串也应该是一个有效的可打印段落。 // 否则在终端分支 toPrintArgs_Terminal 中会因为 segments 为空而返回 undefined。 if (text.length === 0) return [["", SegmentType.Text]] const segments: Segment[] = [] let rest = text // 定义所有格式的正则和类型 const patterns: [RegExp, (match: RegExpExecArray) => Segment][] = [ // 主标题(警告) ![Warn] [/!\[([^\]]+)\]/g, (m) => [m[1], SegmentType.HeroWarn]], // 主标题 [Hero] [/\[([^\]]+)\]/g, (m) => [m[1], SegmentType.Hero]], // 警告多级标题 !<Title|title2|title3> [ /!<([^>]+)>/g, (m: any) => { let titles = m[1].split("|") titles = titles.join(" › ") return [titles, SegmentType.TitleWarn] }, ], // 多级标题 <Title|title2|title3> [ /<([^>]+)>/g, (m: any) => { let titles = m[1].split("|") titles = titles.join(" › ") return [titles, SegmentType.Title] }, ], // 备注文本 _subtext_ [/_(.+?)_/g, (m) => [m[1], SegmentType.Subtext]], // 临时调试 >>>debug [/^>>>/g, () => ["", SegmentType.Debug]], // 加粗文本 *bold* [/\*([^\*]+)\*/g, (m) => [m[1], SegmentType.Bold]], // 警告文本 !bold! [/\!([^\!]+)\!/g, (m) => [m[1], SegmentType.Warn]], // 当前时间 %now% [/%now%/g, () => [new Date().toISOString(), SegmentType.Now]], ] while (rest.length > 0) { let matched = false for (const [regex, handler] of patterns) { regex.lastIndex = 0 // 重置正则状态 const match = regex.exec(rest) if (match && match.index === 0) { segments.push(handler(match)) rest = rest.slice(match[0].length) matched = true break } } if (!matched) { // 找到下一个格式化片段的起始位置 let nextIndex = rest.length for (const [regex] of patterns) { regex.lastIndex = 0 const m = regex.exec(rest) if (m && m.index < nextIndex) nextIndex = m.index } // 普通文本片段 if (nextIndex > 0) { segments.push([rest.slice(0, nextIndex), SegmentType.Text]) rest = rest.slice(nextIndex) } else { // 没有剩余可处理内容 break } } } return segments } export enum SegmentType { Hero = "Hero", HeroWarn = "HeroWarn", Title = "Title", TitleWarn = "TitleWarn", Subtext = "Subtext", Bold = "Bold", Warn = "Warn", Now = "Now", Debug = "Debug", Text = "", Raw = "Raw", } type SegmentOptions = { padLength: number padType: "end" | "start" | "mid" } export type Segment = [any, SegmentType, SegmentOptions?] // fmt(`[Name] <12/32> _files:210_ namespace`); // fmt(`[Name] <12/32> _files:210_ namespace`); // fmt(`<Request|name|name>`, " sdfasdfsd ");