🎨 代码语法高亮

粘贴代码,选择语言,一键生成带语法着色和行号的代码,支持复制高亮 HTML

输出预览 浅色主题
点击「高亮渲染」按钮,或「加载示例」快速体验
🎨 工具介绍

代码语法高亮工具是一款纯前端实现的代码着色工具,内置简易词法分析引擎,无需引入 highlight.js、Prism 等第三方库。支持 JavaScript、HTML、CSS、Python、JSON、SQL、Markdown 七种语言,可区分关键字、字符串、注释、数字、函数名、运算符等语法元素并着色显示。

🔥 功能特点

  • 支持 7 种编程/标记语言
  • 内置词法分析引擎,零外部依赖
  • 浅色/深色代码主题切换
  • 显示行号,方便定位
  • 一键复制带行号的高亮 HTML
  • 等宽字体,代码清晰易读

📋 使用场景

  • 技术博客/文章中插入代码片段
  • 技术文档中展示示例代码
  • 代码分享时生成高亮预览
  • 快速预览代码的语法结构

常见问题

复制的高亮 HTML 如何使用?
点击「复制高亮 HTML」后,粘贴到富文本编辑器(如 WordPress、Notion、飞书文档)中即可保留着色效果。也可以粘贴到 HTML 文件中使用。
高亮引擎是否支持自定义颜色?
当前版本使用预设的浅色和深色两套配色方案,暂不支持自定义颜色。后续版本可能加入此功能。
`, css: `/* CSS Grid Layout Example */ :root { --primary: #FF6B35; --secondary: #7C3AED; --bg: #FFFAF7; --radius: 16px; } .container { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; max-width: 1200px; margin: 0 auto; padding: 32px 16px; } .card { background: white; border-radius: var(--radius); padding: 24px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); transition: transform 0.3s ease, box-shadow 0.3s ease; } .card:hover { transform: translateY(-4px); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12); } .card h3 { color: var(--primary); margin-bottom: 12px; font-size: 20px; font-weight: 700; } .card p { color: #6b7280; line-height: 1.7; font-size: 14px; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @media (max-width: 768px) { .container { grid-template-columns: 1fr; padding: 16px; } }`, python: `# Python 示例 - 数据处理与统计分析 import json from collections import Counter, defaultdict def analyze_data(filepath): \"\"\"读取 JSON 数据并分析统计信息\"\"\" with open(filepath, "r", encoding="utf-8") as f: data = json.load(f) if not data: print("Warning: Empty dataset") return None # 基本统计 total = len(data) scores = [item["score"] for item in data if "score" in item] avg_score = sum(scores) / len(scores) if scores else 0 max_score = max(scores) if scores else 0 min_score = min(scores) if scores else 0 # 分类统计 categories = Counter(item["category"] for item in data) print(f"Total records: {total}") print(f"Average score: {avg_score:.2f}") print(f"Score range: {min_score} ~ {max_score}") print(f"Categories: {len(categories)}") return { "total": total, "average": round(avg_score, 2), "categories": dict(categories) } # 使用示例 if __name__ == "__main__": result = analyze_data("data/users.json") print(result)`, json: `{ "name": "ToolBox", "version": "2.0.0", "description": "1010+ Free Online Tools", "author": { "name": "ChenGuangWu", "email": "dev@example.com", "url": "https://chenguangwu.github.io" }, "features": [ "Code Formatting", "Text Processing", "Encryption & Hashing", "Data Conversion", "Developer Utilities" ], "stats": { "totalTools": 1010, "industries": 12, "categories": 11 }, "deployment": { "platform": "GitHub Pages", "type": "Static Site", "ssl": true, "regions": ["China", "Global"] }, "dependencies": { "tailwindcss": "^4.0.0", "lucide": "latest" } }`, sql: `-- SQL 示例 - 电商数据分析查询 -- 创建用户表 CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(100) NOT NULL, age INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, status VARCHAR(20) DEFAULT 'active' ); -- 创建订单表 CREATE TABLE orders ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), total_amount DECIMAL(10, 2) NOT NULL, status VARCHAR(20) DEFAULT 'pending', order_date DATE DEFAULT CURRENT_DATE ); -- 查询:月度销售统计 SELECT DATE_TRUNC('month', order_date) AS month, COUNT(*) AS total_orders, COUNT(DISTINCT user_id) AS unique_users, SUM(total_amount) AS revenue, AVG(total_amount) AS avg_order_value FROM orders WHERE status NOT IN ('cancelled', 'refunded') AND order_date >= DATE_TRUNC('year', CURRENT_DATE) GROUP BY DATE_TRUNC('month', order_date) ORDER BY month DESC LIMIT 12; -- 查询:Top 10 消费用户 SELECT u.username, u.email, COUNT(o.id) AS order_count, SUM(o.total_amount) AS total_spent, RANK() OVER (ORDER BY SUM(o.total_amount) DESC) AS rank FROM users u INNER JOIN orders o ON u.id = o.user_id WHERE o.status = 'completed' GROUP BY u.id, u.username, u.email HAVING SUM(o.total_amount) > 100 ORDER BY total_spent DESC LIMIT 10;`, markdown: `# Markdown 语法高亮演示 ## 基本语法 这是一段普通文本,支持 **加粗**、*斜体* 和 ~~删除线~~。 ### 列表 - 无序列表项 1 - 无序列表项 2 - 嵌套列表项 1. 有序列表项 A 2. 有序列表项 B 3. 有序列表项 C ### 代码 行内代码:\`const x = 42;\` 代码块示例(非代码高亮,仅展示 Markdown 原文): \`\`\`python def hello(): print("Hello, World!") \`\`\` ### 引用 > 这是一段引用文本, > 可以跨越多行。 ### 链接与图片 [访问 ToolBox](https://chenguangwu.github.io) ### 表格 | 功能 | 语言 | 状态 | |------|------|------| | 高亮 | JS | 已完成 | | 格式化 | CSS | 已完成 | | 解析 | JSON | 已完成 |` }; // ========== HTML 转义 ========== function esc(s) { return s .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } // ========== 词法分析:按 token 切分 ========== function tokenize(code, lang) { const tokens = []; let i = 0; const len = code.length; while (i < len) { // --- JavaScript / Python / JSON --- if (lang === 'javascript' || lang === 'python' || lang === 'json') { // 单行注释 if ((lang === 'javascript' && code[i] === '/' && code[i + 1] === '/') || (lang === 'python' && code[i] === '#')) { let end = code.indexOf('\n', i); if (end === -1) end = len; tokens.push({ type: 'comment', value: code.slice(i, end) }); i = end; continue; } // 多行注释 if (lang === 'javascript' && code[i] === '/' && code[i + 1] === '*') { let end = code.indexOf('*/', i + 2); end = end === -1 ? len : end + 2; tokens.push({ type: 'comment', value: code.slice(i, end) }); i = end; continue; } // 三引号字符串 (Python) if (lang === 'python' && (code.slice(i, i + 3) === '"""' || code.slice(i, i + 3) === "'''")) { const quote = code.slice(i, i + 3); let end = code.indexOf(quote, i + 3); end = end === -1 ? len : end + 3; tokens.push({ type: 'string', value: code.slice(i, end) }); i = end; continue; } // 字符串 if (code[i] === '"' || code[i] === "'" || code[i] === '`') { const quote = code[i]; let j = i + 1; while (j < len) { if (code[j] === '\\') { j += 2; continue; } if (code[j] === quote) { j++; break; } j++; } tokens.push({ type: 'string', value: code.slice(i, j) }); i = j; continue; } // 数字 if (/[0-9]/.test(code[i]) && (i === 0 || !/\w/.test(code[i - 1]) || code[i - 1] === '.')) { let j = i; if (code[j] === '0' && (code[j + 1] === 'x' || code[j + 1] === 'X')) { j += 2; while (j < len && /[0-9a-fA-F]/.test(code[j])) j++; } else { while (j < len && /[0-9]/.test(code[j])) j++; if (code[j] === '.' && /[0-9]/.test(code[j + 1])) { j++; while (j < len && /[0-9]/.test(code[j])) j++; } if (code[j] === 'e' || code[j] === 'E') { j++; if (code[j] === '+' || code[j] === '-') j++; while (j < len && /[0-9]/.test(code[j])) j++; } } tokens.push({ type: 'number', value: code.slice(i, j) }); i = j; continue; } // 标识符 / 关键字 if (/[a-zA-Z_$]/.test(code[i])) { let j = i; while (j < len && /[a-zA-Z0-9_$]/.test(code[j])) j++; const word = code.slice(i, j); const kws = KEYWORDS[lang]; // 检查是否为函数调用 if (j < len && code[j] === '(') { tokens.push({ type: 'function', value: word }); } else if (kws && kws.has(word)) { tokens.push({ type: 'keyword', value: word }); } else if (lang === 'python' && BUILTINS.python.has(word)) { tokens.push({ type: 'function', value: word }); } else { tokens.push({ type: 'plain', value: word }); } i = j; continue; } // 运算符 if (/[+\-*/%=<>!&|^~?:]/.test(code[i])) { let j = i + 1; while (j < len && /[+\-*/%=<>!&|^~?:]/.test(code[j]) && j - i < 3) j++; tokens.push({ type: 'operator', value: code.slice(i, j) }); i = j; continue; } // 括号等标点 if (/[{}()\[\];,.]/.test(code[i])) { tokens.push({ type: 'punctuation', value: code[i] }); i++; continue; } // 其他(空白等) tokens.push({ type: 'plain', value: code[i] }); i++; continue; } // --- SQL --- if (lang === 'sql') { // 单行注释 if (code[i] === '-' && code[i + 1] === '-') { let end = code.indexOf('\n', i); if (end === -1) end = len; tokens.push({ type: 'comment', value: code.slice(i, end) }); i = end; continue; } // 字符串 if (code[i] === "'") { let j = i + 1; while (j < len) { if (code[j] === "'" && code[j + 1] === "'") { j += 2; continue; } if (code[j] === "'") { j++; break; } j++; } tokens.push({ type: 'string', value: code.slice(i, j) }); i = j; continue; } // 数字 if (/[0-9]/.test(code[i]) && (i === 0 || !/\w/.test(code[i - 1]))) { let j = i; while (j < len && /[0-9.]/.test(code[j])) j++; tokens.push({ type: 'number', value: code.slice(i, j) }); i = j; continue; } // 标识符 / 关键字 if (/[a-zA-Z_]/.test(code[i])) { let j = i; while (j < len && /[a-zA-Z0-9_]/.test(code[j])) j++; const word = code.slice(i, j); const upper = word.toUpperCase(); if (KEYWORDS.sql.has(upper)) { tokens.push({ type: 'keyword', value: word }); } else if (j < len && code[j] === '(') { tokens.push({ type: 'function', value: word }); } else { tokens.push({ type: 'plain', value: word }); } i = j; continue; } // 运算符 if (/[+\-*/%=<>!&|^~]/.test(code[i])) { let j = i + 1; while (j < len && /[+\-*/%=<>!&|^~]/.test(code[j]) && j - i < 3) j++; tokens.push({ type: 'operator', value: code.slice(i, j) }); i = j; continue; } if (/[{}()\[\];,.]/.test(code[i])) { tokens.push({ type: 'punctuation', value: code[i] }); i++; continue; } tokens.push({ type: 'plain', value: code[i] }); i++; continue; } // --- HTML --- if (lang === 'html') { // 注释 if (code.slice(i, i + 4) === '', i + 4); end = end === -1 ? len : end + 3; tokens.push({ type: 'comment', value: code.slice(i, end) }); i = end; continue; } // 标签 if (code[i] === '<' && /[a-zA-Z\/!]/.test(code[i + 1] || '')) { let j = i + 1; // 闭合斜杠 if (code[j] === '/') j++; // 标签名 while (j < len && /[a-zA-Z0-9\-]/.test(code[j])) j++; const tagName = code.slice(i + (code[i + 1] === '/' ? 2 : 1), j); tokens.push({ type: 'tag', value: '<' + code.slice(i + 1, j) }); // 属性区 while (j < len && code[j] !== '>') { // 属性名 if (/[a-zA-Z\-@]/.test(code[j])) { let ak = j; while (j < len && /[a-zA-Z0-9\-@:.]/.test(code[j])) j++; tokens.push({ type: 'attr', value: code.slice(ak, j) }); // = 号 if (code[j] === '=') { tokens.push({ type: 'operator', value: '=' }); j++; // 属性值 if (code[j] === '"' || code[j] === "'") { const q = code[j]; let vk = j + 1; while (vk < len && code[vk] !== q) vk++; vk++; tokens.push({ type: 'attr-value', value: code.slice(j, vk) }); j = vk; } else { let vk = j; while (vk < len && /[a-zA-Z0-9\-_.]/.test(code[vk])) vk++; tokens.push({ type: 'attr-value', value: code.slice(j, vk) }); j = vk; } } } else if (code[j] === ' ') { tokens.push({ type: 'plain', value: ' ' }); j++; } else if (code[j] === '/' && code[j + 1] === '>') { tokens.push({ type: 'plain', value: '/>' }); j += 2; break; } else { tokens.push({ type: 'plain', value: code[j] }); j++; } } if (j < len && code[j] === '>') { tokens.push({ type: 'plain', value: '>' }); j++; } i = j; continue; } tokens.push({ type: 'plain', value: code[i] }); i++; continue; } // --- CSS --- if (lang === 'css') { // 注释 if (code[i] === '/' && code[i + 1] === '*') { let end = code.indexOf('*/', i + 2); end = end === -1 ? len : end + 2; tokens.push({ type: 'comment', value: code.slice(i, end) }); i = end; continue; } // 字符串 if (code[i] === '"' || code[i] === "'") { const q = code[i]; let j = i + 1; while (j < len && code[j] !== q) { if (code[j] === '\\') j++; j++; } j++; tokens.push({ type: 'string', value: code.slice(i, j) }); i = j; continue; } // 数字(含单位) if (/[0-9]/.test(code[i])) { let j = i; while (j < len && /[0-9.]/.test(code[j])) j++; while (j < len && /[a-zA-Z%]/.test(code[j])) j++; tokens.push({ type: 'number', value: code.slice(i, j) }); i = j; continue; } // 标识符 if (/[a-zA-Z_@#.\-]/.test(code[i])) { let j = i; while (j < len && /[a-zA-Z0-9_\-#.@]/.test(code[j])) j++; const word = code.slice(i, j); if (KEYWORDS.css.has(word)) { tokens.push({ type: 'keyword', value: word }); } else { tokens.push({ type: 'plain', value: word }); } i = j; continue; } // 颜色值 #xxx if (code[i] === '#' && /[0-9a-fA-F]/.test(code[i + 1] || '')) { let j = i + 1; while (j < len && /[0-9a-fA-F]/.test(code[j]) && j - i <= 8) j++; tokens.push({ type: 'number', value: code.slice(i, j) }); i = j; continue; } // 运算符 if (/[{}():;>,+~*]/.test(code[i])) { tokens.push({ type: 'punctuation', value: code[i] }); i++; continue; } tokens.push({ type: 'plain', value: code[i] }); i++; continue; } // --- Markdown --- if (lang === 'markdown') { // 标题 # if (code[i] === '#' && (i === 0 || code[i - 1] === '\n')) { let j = i; while (j < len && code[j] === '#') j++; tokens.push({ type: 'heading', value: code.slice(i, j) }); i = j; continue; } // 粗体 ** if (code[i] === '*' && code[i + 1] === '*') { let end = code.indexOf('**', i + 2); if (end !== -1) { tokens.push({ type: 'bold', value: '**' }); i += 2; continue; } } // 粗体 __ if (code[i] === '_' && code[i + 1] === '_') { let end = code.indexOf('__', i + 2); if (end !== -1) { tokens.push({ type: 'bold', value: '__' }); i += 2; continue; } } // 删除线 if (code[i] === '~' && code[i + 1] === '~') { let end = code.indexOf('~~', i + 2); if (end !== -1) { tokens.push({ type: 'operator', value: '~~' }); i += 2; continue; } } // 行内代码 if (code[i] === '`') { let j = i + 1; while (j < len && code[j] !== '`') j++; j++; tokens.push({ type: 'code-inline', value: code.slice(i, j) }); i = j; continue; } // 引用 if (code[i] === '>' && (i === 0 || code[i - 1] === '\n')) { tokens.push({ type: 'blockquote', value: '>' }); i++; continue; } // 无序列表 if ((code[i] === '-' || code[i] === '*') && code[i + 1] === ' ' && (i === 0 || code[i - 1] === '\n')) { tokens.push({ type: 'list', value: code[i] }); i++; continue; } // 有序列表 if (/[0-9]/.test(code[i]) && code[i + 1] === '.' && code[i + 2] === ' ' && (i === 0 || code[i - 1] === '\n')) { let j = i; while (j < len && /[0-9]/.test(code[j])) j++; tokens.push({ type: 'list', value: code.slice(i, j + 1) }); i = j + 1; continue; } // 链接 [text](url) if (code[i] === '[') { let textEnd = code.indexOf('](', i); if (textEnd !== -1) { let urlEnd = code.indexOf(')', textEnd + 2); if (urlEnd !== -1) { tokens.push({ type: 'link', value: code.slice(i, urlEnd + 1) }); i = urlEnd + 1; continue; } } } // 表格分隔 if (code[i] === '|' && (i === 0 || code[i - 1] === '\n' || /[|\-\s]/.test(code[i - 1]))) { let j = i; while (j < len && /[|\-\s:]/.test(code[j])) j++; if (j > i + 1) { tokens.push({ type: 'punctuation', value: code.slice(i, j) }); i = j; continue; } } // 斜体 * if (code[i] === '*') { tokens.push({ type: 'bold', value: '*' }); i++; continue; } tokens.push({ type: 'plain', value: code[i] }); i++; continue; } // fallback tokens.push({ type: 'plain', value: code[i] }); i++; } return tokens; } // ========== token 渲染 ========== function renderToken(token) { const v = esc(token.value); const cls = 'hl-' + token.type; return '' + v + ''; } // ========== 逐行渲染 ========== function renderHighlighted(code, lang) { const lines = code.split('\n'); const lineHtmls = []; // 逐行 tokenize,确保行号与高亮内容对齐 for (let l = 0; l < lines.length; l++) { const line = lines[l]; const lineTokens = tokenize(line, lang); let lineH = ''; for (let t = 0; t < lineTokens.length; t++) { lineH += renderToken(lineTokens[t]); } // 确保空行也有内容(保持行高) if (lineH === '') lineH = ' '; lineHtmls.push(lineH); } return lineHtmls; } // ========== 主高亮函数 ========== function highlight() { const code = document.getElementById('codeInput').value; const lang = document.getElementById('langSelect').value; const outputBody = document.getElementById('codeOutputBody'); const placeholder = document.getElementById('codePlaceholder'); const statsEl = document.getElementById('codeStats'); if (!code.trim()) { outputBody.innerHTML = '
请输入或粘贴代码后再点击高亮
'; statsEl.innerHTML = ''; return; } const lineHtmls = renderHighlighted(code, lang); // 构建 HTML let tableHtml = ''; for (let i = 0; i < lineHtmls.length; i++) { tableHtml += '' + '' + '' + ''; } tableHtml += '
' + (i + 1) + '' + lineHtmls[i] + '
'; outputBody.innerHTML = tableHtml; // 更新标签 const langNames = { javascript: 'JavaScript', html: 'HTML', css: 'CSS', python: 'Python', json: 'JSON', sql: 'SQL', markdown: 'Markdown' }; document.getElementById('outputLangLabel').textContent = langNames[lang] || lang; // 统计 const lines = code.split('\n').length; const chars = code.length; const nonEmpty = code.split('\n').filter(l => l.trim()).length; statsEl.innerHTML = '行数: ' + lines + '' + '非空行: ' + nonEmpty + '' + '字符数: ' + chars + '' + '语言: ' + langNames[lang] + ''; applyEditorTheme(); } // ========== 编辑器主题切换 ========== function applyEditorTheme() { const wrap = document.getElementById('codeOutputWrap'); const theme = document.getElementById('editorTheme').value; const label = document.getElementById('outputThemeLabel'); if (theme === 'dark') { wrap.classList.add('dark-editor'); label.textContent = '深色主题'; } else { wrap.classList.remove('dark-editor'); label.textContent = '浅色主题'; } } // ========== 复制高亮 HTML ========== function copyHighlightedHTML() { const outputBody = document.getElementById('codeOutputBody'); const table = outputBody.querySelector('.code-table'); if (!table) { ToolBox.showToast('请先执行高亮渲染', 'warning'); return; } // 构建独立的带样式 HTML const theme = document.getElementById('editorTheme').value; const isDark = theme === 'dark'; const bg = isDark ? '#1e1e2e' : '#fafafa'; const headerBg = isDark ? '#181825' : 'rgba(0,0,0,0.03)'; const numColor = isDark ? '#585b70' : '#9ca3af'; const numBg = isDark ? '#181825' : 'rgba(0,0,0,0.02)'; const borderColor = isDark ? '#313244' : '#e5e7eb'; const codeColor = isDark ? '#cdd6f4' : '#1f2937'; const html = '
' + '' + table.innerHTML .replace(/ class="code-line-num"/g, ' style="width:56px;text-align:right;padding:0 12px 0 8px;color:' + numColor + ';background:' + numBg + ';border-right:1px solid ' + borderColor + ';user-select:none;font-size:12px;"') .replace(/ class="code-line-code"/g, ' style="padding:0 16px;white-space:pre;color:' + codeColor + ';"') + '
'; ToolBox.copyText(html, '高亮 HTML 已复制到剪贴板'); } // ========== 清空 ========== function clearAll() { document.getElementById('codeInput').value = ''; document.getElementById('codeOutputBody').innerHTML = '
点击「高亮渲染」按钮,或「加载示例」快速体验
'; document.getElementById('codeStats').innerHTML = ''; } // ========== 加载示例 ========== function loadExample() { const lang = document.getElementById('langSelect').value; document.getElementById('codeInput').value = EXAMPLES[lang] || ''; highlight(); ToolBox.showToast('已加载 ' + lang.charAt(0).toUpperCase() + lang.slice(1) + ' 示例代码', 'info'); } // ========== 工具介绍折叠 ========== function toggleIntro() { document.getElementById('toolIntro').classList.toggle('open'); } // ========== Tab 键支持 ========== document.addEventListener('DOMContentLoaded', function() { const ta = document.getElementById('codeInput'); ta.addEventListener('keydown', function(e) { if (e.key === 'Tab') { e.preventDefault(); const start = this.selectionStart; const end = this.selectionEnd; const val = this.value; this.value = val.substring(0, start) + ' ' + val.substring(end); this.selectionStart = this.selectionEnd = start + 2; } }); });