feat: Enhance code highlighting
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
/* JavaScript/TypeScript 关键字 */
|
||||
.keyword {
|
||||
color: #d73a49;
|
||||
font-weight: 600;
|
||||
@@ -19,6 +20,27 @@
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
/* TypeScript 特定关键字 (interface, type, enum, etc.) */
|
||||
.tsKeyword {
|
||||
color: #af00db;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:global(.dark) .tsKeyword {
|
||||
color: #c792ea;
|
||||
}
|
||||
|
||||
/* TypeScript 内置类型 */
|
||||
.type {
|
||||
color: #267f99;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:global(.dark) .type {
|
||||
color: #4ec9b0;
|
||||
}
|
||||
|
||||
/* 字符串 */
|
||||
.string {
|
||||
color: #1d6eca;
|
||||
}
|
||||
@@ -27,6 +49,7 @@
|
||||
color: #4fc3f7;
|
||||
}
|
||||
|
||||
/* 数字 */
|
||||
.number {
|
||||
color: #00c583;
|
||||
}
|
||||
@@ -35,6 +58,17 @@
|
||||
color: #66bb6a;
|
||||
}
|
||||
|
||||
/* 布尔值和字面量 (true, false, null, undefined) */
|
||||
.literal {
|
||||
color: #0000ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:global(.dark) .literal {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
/* 注释 */
|
||||
.comment {
|
||||
color: #6a737d;
|
||||
font-style: italic;
|
||||
@@ -43,3 +77,51 @@
|
||||
:global(.dark) .comment {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
|
||||
/* 装饰器 (@decorator) */
|
||||
.decorator {
|
||||
color: #e0aa00;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:global(.dark) .decorator {
|
||||
color: #dcdcaa;
|
||||
}
|
||||
|
||||
|
||||
/* 箭头函数 (=>) */
|
||||
.arrow {
|
||||
color: #d73a49;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
:global(.dark) .arrow {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
/* 标识符(变量名、函数名等) */
|
||||
.identifier {
|
||||
color: #171717;
|
||||
}
|
||||
|
||||
:global(.dark) .identifier {
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
/* 属性访问 (.property) */
|
||||
.property {
|
||||
color: #0550ae;
|
||||
}
|
||||
|
||||
:global(.dark) .property {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
/* 运算符 */
|
||||
.operator {
|
||||
color: #5a5a5a;
|
||||
}
|
||||
|
||||
:global(.dark) .operator {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
@@ -9,58 +9,157 @@ interface HighlightSyntaxProps {
|
||||
code: string
|
||||
}
|
||||
|
||||
// JavaScript/TypeScript 关键字
|
||||
const keywords =
|
||||
'async|await|function|const|let|var|if|else|for|while|return|try|catch|finally|class|extends|from|import|export|default|undefined|throw|true|false|null|this|new|in|of|instanceof|break|continue|switch|case|default|do|while|with|yield'
|
||||
'async|await|function|const|let|var|if|else|for|while|return|try|catch|finally|class|extends|from|import|export|default|undefined|throw|break|continue|switch|case|do|with|yield|delete|typeof|void|static|get|set|super|debugger'
|
||||
|
||||
// 语法高亮函数,先整体提取字符串/注释等token再高亮
|
||||
// TypeScript 特定关键字
|
||||
const tsKeywords =
|
||||
'interface|type|enum|namespace|module|declare|abstract|implements|public|private|protected|readonly|as|satisfies|infer|keyof|is'
|
||||
|
||||
// 布尔值和空值
|
||||
const literals = 'true|false|null|undefined|NaN|Infinity'
|
||||
|
||||
// TypeScript 内置类型
|
||||
const tsTypes =
|
||||
'string|number|boolean|any|unknown|never|void|object|symbol|bigint|Array|Promise|Record|Partial|Required|Readonly|Pick|Omit|Exclude|Extract|NonNullable|ReturnType|Parameters|ConstructorParameters|InstanceType|ThisType|Uppercase|Lowercase|Capitalize|Uncapitalize'
|
||||
|
||||
// 辅助函数:转义 HTML 特殊字符
|
||||
function escapeHtml(text: string): string {
|
||||
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
}
|
||||
|
||||
// 语法高亮函数,先提取 token 再转义和高亮
|
||||
function highlightSyntax(code: string): string {
|
||||
// 先转义HTML特殊字符
|
||||
const escaped = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
|
||||
// 单行字符串,所有反斜杠双重转义,保证正则安全
|
||||
// 构建正则模式,包含更多 token 类型(在原始文本上匹配)
|
||||
const pattern = new RegExp(
|
||||
'("([^"\\\\]|\\\\.)*"|\'([^\'\\\\]|\\\\.)*\'|`([^`\\\\]|\\\\.)*`|//[^\\n]*|/\\*[\\s\\S]*?\\*/|\\b\\d+\\.?\\d*\\b|\\b(?:' +
|
||||
'(' +
|
||||
// 1. 字符串(双引号、单引号、模板字符串)
|
||||
'"([^"\\\\]|\\\\.)*"|' +
|
||||
"'([^'\\\\]|\\\\.)*'|" +
|
||||
'`([^`\\\\]|\\\\.)*`|' +
|
||||
// 2. 注释(单行和多行)
|
||||
'//[^\\n]*|' +
|
||||
'/\\*[\\s\\S]*?\\*/|' +
|
||||
// 3. 装饰器
|
||||
'@[a-zA-Z_$][\\w$]*|' +
|
||||
// 4. 数字(包括小数、十六进制、科学计数法)
|
||||
'\\b0[xX][0-9a-fA-F]+\\b|' +
|
||||
'\\b\\d+\\.?\\d*(?:[eE][+-]?\\d+)?\\b|' +
|
||||
// 5. TypeScript/JavaScript 关键字
|
||||
'\\b(?:' +
|
||||
keywords +
|
||||
')\\b)',
|
||||
'|' +
|
||||
tsKeywords +
|
||||
'|' +
|
||||
literals +
|
||||
')\\b|' +
|
||||
// 6. TypeScript 内置类型
|
||||
'\\b(?:' +
|
||||
tsTypes +
|
||||
')\\b|' +
|
||||
// 7. 箭头函数
|
||||
'=>|' +
|
||||
// 8. 函数调用(函数名后跟括号)
|
||||
'\\b[a-zA-Z_$][\\w$]*(?=\\()|' +
|
||||
// 9. 属性访问
|
||||
'\\.[a-zA-Z_$][\\w$]*|' +
|
||||
// 10. 运算符和特殊符号
|
||||
'[+\\-*/%&|^!~<>=?:]+|' +
|
||||
'[{}\\[\\]();,.]' +
|
||||
')',
|
||||
'g'
|
||||
)
|
||||
|
||||
const tokens: string[] = []
|
||||
let lastIndex = 0
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pattern.exec(escaped)) !== null) {
|
||||
while ((match = pattern.exec(code)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
tokens.push(...escaped.slice(lastIndex, match.index).split(/([ \t\n\r.])/))
|
||||
const gap = code.slice(lastIndex, match.index)
|
||||
// 将间隙按空白符分割,保留空白符
|
||||
tokens.push(...gap.split(/(\s+)/))
|
||||
}
|
||||
tokens.push(match[0])
|
||||
lastIndex = pattern.lastIndex
|
||||
}
|
||||
if (lastIndex < escaped.length) {
|
||||
tokens.push(...escaped.slice(lastIndex).split(/([ \t\n\r.])/))
|
||||
if (lastIndex < code.length) {
|
||||
tokens.push(...code.slice(lastIndex).split(/(\s+)/))
|
||||
}
|
||||
|
||||
const highlighted = tokens
|
||||
.map((token) => {
|
||||
// 空白符直接返回
|
||||
if (/^\s+$/.test(token)) {
|
||||
return token
|
||||
}
|
||||
|
||||
// 1. 注释(单行和多行)
|
||||
if (/^\/\/.*$/.test(token) || /^\/\*[\s\S]*?\*\/$/.test(token)) {
|
||||
return `<span class="${styles.comment}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
|
||||
// 2. 字符串
|
||||
if (
|
||||
/^"([^"\\]|\\.)*"$/.test(token) ||
|
||||
/^'([^'\\]|\\.)*'$/.test(token) ||
|
||||
/^`([^`\\]|\\.)*`$/.test(token)
|
||||
) {
|
||||
return `<span class="${styles.string}">${token}</span>`
|
||||
return `<span class="${styles.string}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
if (/^\b\d+\.?\d*\b$/.test(token)) {
|
||||
return `<span class="${styles.number}">${token}</span>`
|
||||
|
||||
// 3. 数字
|
||||
if (/^(0[xX][0-9a-fA-F]+|\d+\.?\d*(?:[eE][+-]?\d+)?)$/.test(token)) {
|
||||
return `<span class="${styles.number}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
if (/^\/\/.*$/.test(token)) {
|
||||
return `<span class="${styles.comment}">${token}</span>`
|
||||
|
||||
// 4. 布尔值和特殊字面量
|
||||
if (new RegExp(`^(?:${literals})$`).test(token)) {
|
||||
return `<span class="${styles.literal}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
if (/^\/\*[\s\S]*?\*\/$/.test(token)) {
|
||||
return `<span class="${styles.comment}">${token}</span>`
|
||||
|
||||
// 5. JavaScript/TypeScript 关键字
|
||||
if (new RegExp(`^(?:${keywords})$`).test(token)) {
|
||||
return `<span class="${styles.keyword}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
if (new RegExp(`\\b(?:${keywords})\\b`).test(token)) {
|
||||
return `<span class="${styles.keyword}">${token}</span>`
|
||||
|
||||
// 6. TypeScript 特定关键字
|
||||
if (new RegExp(`^(?:${tsKeywords})$`).test(token)) {
|
||||
return `<span class="${styles.tsKeyword}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
return token
|
||||
|
||||
// 7. TypeScript 内置类型
|
||||
if (new RegExp(`^(?:${tsTypes})$`).test(token)) {
|
||||
return `<span class="${styles.type}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
|
||||
// 8. 装饰器
|
||||
if (/^@[a-zA-Z_$][\w$]*$/.test(token)) {
|
||||
return `<span class="${styles.decorator}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
|
||||
// 9. 箭头函数
|
||||
if (token === '=>') {
|
||||
return `<span class="${styles.arrow}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
|
||||
// 10. 函数调用和标识符
|
||||
if (/^[a-zA-Z_$][\w$]*$/.test(token)) {
|
||||
return `<span class="${styles.identifier}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
|
||||
// 11. 属性访问
|
||||
if (/^\.[a-zA-Z_$][\w$]*$/.test(token)) {
|
||||
return `<span class="${styles.property}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
|
||||
// 12. 运算符
|
||||
if (/^[+\-*/%&|^!~<>=?:]+$/.test(token)) {
|
||||
return `<span class="${styles.operator}">${escapeHtml(token)}</span>`
|
||||
}
|
||||
|
||||
// 13. 其他符号,需要转义
|
||||
return escapeHtml(token)
|
||||
})
|
||||
.join('')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user