9、code- Frame 和代码高亮

74 阅读2分钟

如何 在代码中展示 问题

image.png

核心就是这三个问题:

  • 如何打印出标记相应位置代码的 code frame(就是上图的打印格式)
  • 如何实现语法高亮
  • 如何在控制台打印颜色

如何打印 code frame

image.png

其实就是一个拼接字符串的过程,下面是拼接字符串的细节(了解即可):

传入了源代码、标记开始和结束的行列号,那么我们就能够计算出显示标记(marker “>”)的行是哪些,以及这些行的哪些列,然后依次对每一行代码做处理,如果本行没有标记则保持原样,如果本行有标记的话,那么就在开始打印一个 marker “>”,并且在下面打印一行 marker "^",最后一个标记行还要打印错误信息。

代码参考 ``

function generateCodeFrame(code, location, options = {}) {
  const { start, end } = location;
  const {
    highlightCode = false,
    message = '',
    linesAbove = 2,
    linesBelow = 2,
  } = options;

  // 将代码分割成行数组
  const lines = code.split('\n');
  const startLine = start.line - 1; // 转为 0 索引
  const startColumn = start.column - 1;

  // 上下文行范围
  const startContext = Math.max(0, startLine - linesAbove);
  const endContext = Math.min(lines.length, startLine + linesBelow + 1);

  // 收集上下文行
  const result = [];
  for (let i = startContext; i < endContext; i++) {
    const lineNumber = i + 1; // 转回 1 索引
    const line = lines[i];

    // 标记错误行
    if (i === startLine) {
      const pointer = ' '.repeat(startColumn) + '^'; // 指示器
      result.push(`> ${lineNumber} | ${line}`);
      result.push(`  ${' '.repeat(lineNumber.toString().length)} | ${pointer}`);
      if (message) {
        result.push(`  ${' '.repeat(lineNumber.toString().length)} | ${message}`);
      }
    } else {
      result.push(`  ${lineNumber} | ${line}`);
    }
  }

  // 高亮代码(可选)
  if (highlightCode) {
    return result.join('\n'); // 模拟高亮,可以接入 chalk 库优化
  }

  return result.join('\n');
}

如何实现语法高亮

核心实现思路

  1. 代码分割和解析

    • 按行分割代码,将其分解为更小的片段(单词、标点符号、字符串等)。
    • 使用正则表达式或词法分析器对代码进行分类。
  2. 为代码添加颜色

    • 不同类型的代码片段映射到不同的颜色(如关键字为蓝色,字符串为绿色)。
    • 使用 ANSI 转义序列实现颜色渲染(或借助 chalkkleur 等库)。
  3. 重组和输出

    • 将高亮后的片段组合回完整代码。
    • 确保保留原始格式(如缩进和空行)。

如何在控制台打印颜色

代码 参考

const chalk = require('chalk');

// 定义高亮规则
const HIGHLIGHT_RULES = [
  { type: 'keyword', regex: /\b(function|return|const|let|var|if|else|for|while|switch|case|break|default)\b/g, color: chalk.blue },
  { type: 'string', regex: /(["'])(?:(?=(\\?))\2.)*?\1/g, color: chalk.green },
  { type: 'number', regex: /\b\d+(\.\d+)?\b/g, color: chalk.yellow },
  { type: 'comment', regex: /(\/\/.*$|\/\*[\s\S]*?\*\/)/gm, color: chalk.gray },
  { type: 'operator', regex: /[=+\-*/<>!&|]/g, color: chalk.red },
];

// 代码高亮函数
function highlightCode(code) {
  let highlightedCode = code;

  // 应用高亮规则
  for (const rule of HIGHLIGHT_RULES) {
    highlightedCode = highlightedCode.replace(rule.regex, match => rule.color(match));
  }

  return highlightedCode;
}

// 测试代码
const rawCode = `
function sum(a, b) {
  const result = a + b; // Add two numbers
  return result;
}

console.log(sum(1, 2)); // Output: 3
`;

console.log(highlightCode(rawCode));

image.png