import { checkSupportTerminal } from "../lib/checkSupportTerminal"
import { toPrintArgs_Terminal } from "../lib/toPrintArgs_Terminal"
import { toPrintArgs_Web } from "../lib/toPrintArgs_Web"
/**
* 格式化日志输出
*
* `[Hero]` 主标题 带背景的标题
* `
` 标题 带高亮色的标题
* `` 标题 多级高亮标题
* `!` 警告标题 多级高亮标题
* `_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]],
// 警告多级标题 !
[
/!<([^>]+)>/g,
(m: any) => {
let titles = m[1].split("|")
titles = titles.join(" › ")
return [titles, SegmentType.TitleWarn]
},
],
// 多级标题
[
/<([^>]+)>/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(``, " sdfasdfsd ");