Skip to content

Commit

Permalink
✨ feat(utils): 添加代码审查报告生成器和优化工具类
Browse files Browse the repository at this point in the history
-【新功能】
- 实现 CodeReviewReportGenerator 类,支持生成 Markdown 格式的代码审查报告
- 增强多个工具类的文档注释和类型定义
- 优化 DiffSplitter 和 DiffSimplifier 的差异处理逻辑

-【代码优化】
- 完善 LocalizationManager 的错误处理和资源加载机制
- 改进 DateUtils 的日期范围计算方法
- 增强 ProgressHandler 和 NotificationHandler 的类型安全性

-【功能增强】
- 增加 webview 相关工具函数
- 扩展差异文本处理的配置选项
- 优化本地化资源管理机制
  • Loading branch information
littleCareless committed Jan 22, 2025
1 parent 79801c4 commit d9da1b3
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 22 deletions.
113 changes: 113 additions & 0 deletions src/utils/CodeReviewReportGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { CodeReviewResult, CodeReviewIssue } from "../ai/types";

/**
* 代码审查报告生成器,将代码审查结果转换为格式化的 Markdown 文档
*/
export class CodeReviewReportGenerator {
/**
* 不同严重程度对应的 emoji 图标
* @private
*/
private static readonly severityEmoji = {
NOTE: "💡", // 提示
WARNING: "⚠️", // 警告
ERROR: "🚨", // 错误
};

/**
* 生成完整的 Markdown 格式代码审查报告
* @param {CodeReviewResult} review - 代码审查结果对象
* @returns {string} 格式化的 Markdown 文本
*/
static generateMarkdownReport(review: CodeReviewResult): string {
// 按文件分组整理问题
const sections = this.groupIssuesByFile(review.issues);

// 生成报告各部分
let markdown = this.generateHeader(review.summary);
markdown += this.generateDetailedFindings(sections);

return markdown;
}

/**
* 将问题按文件路径分组
* @private
* @param {CodeReviewIssue[]} issues - 问题列表
* @returns {Record<string, CodeReviewIssue[]>} 按文件分组的问题
*/
private static groupIssuesByFile(
issues: CodeReviewIssue[]
): Record<string, CodeReviewIssue[]> {
return issues.reduce((acc, issue) => {
if (!acc[issue.filePath]) {
acc[issue.filePath] = [];
}
acc[issue.filePath].push(issue);
return acc;
}, {} as Record<string, CodeReviewIssue[]>);
}

/**
* 生成报告头部,包含总体摘要
* @private
* @param {string} summary - 审查总结
* @returns {string} Markdown 格式的报告头部
*/
private static generateHeader(summary: string): string {
return `# Code Review Report\n\n## Summary\n\n${summary}\n\n`;
}

/**
* 生成详细问题列表
* @private
* @param {Record<string, CodeReviewIssue[]>} sections - 按文件分组的问题
* @returns {string} Markdown 格式的详细问题列表
*/
private static generateDetailedFindings(
sections: Record<string, CodeReviewIssue[]>
): string {
let markdown = `## Detailed Findings\n\n`;

// 遍历每个文件的问题
for (const [filePath, issues] of Object.entries(sections)) {
markdown += `### ${filePath}\n\n`;

for (const issue of issues) {
markdown += this.generateIssueSection(issue);
}
}

return markdown;
}

/**
* 生成单个问题的描述部分
* @private
* @param {CodeReviewIssue} issue - 单个问题对象
* @returns {string} Markdown 格式的问题描述
*/
private static generateIssueSection(issue: CodeReviewIssue): string {
// 生成问题标题,包含严重程度和行号
let section = `#### ${this.severityEmoji[issue.severity]} ${
issue.severity
}: Line ${issue.startLine}${issue.endLine ? `-${issue.endLine}` : ""}\n\n`;

// 添加问题描述和建议
section += `**Issue:** ${issue.description}\n\n`;
section += `**Suggestion:** ${issue.suggestion}\n\n`;

// 如果有代码示例,添加代码块
if (issue.code) {
section += "```typescript\n" + issue.code + "\n```\n\n";
}

// 如果有相关文档,添加链接
if (issue.documentation) {
section += `📚 [Documentation](${issue.documentation})\n\n`;
}

section += `---\n\n`;
return section;
}
}
22 changes: 17 additions & 5 deletions src/utils/DateUtils.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
/**
* 日期工具类,提供日期范围计算相关功能
*/
export class DateUtils {
static getDateRangeFromPeriod(period: string): { startDate: Date; endDate: Date } {
/**
* 根据给定的时间段计算日期范围
* @param {string} period - 时间段描述,可选值: "1 week ago" | "2 weeks ago" | "3 weeks ago"
* @returns {{startDate: Date, endDate: Date}} 计算得到的日期范围
*/
static getDateRangeFromPeriod(period: string): {
startDate: Date;
endDate: Date;
} {
const now = new Date();
let startDate: Date;

// 根据时间段计算起始日期
switch (period) {
case "1 week ago":
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); // 7天前
break;
case "2 weeks ago":
startDate = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000);
startDate = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000); // 14天前
break;
case "3 weeks ago":
startDate = new Date(now.getTime() - 21 * 24 * 60 * 60 * 1000);
startDate = new Date(now.getTime() - 21 * 24 * 60 * 60 * 1000); // 21天前
break;
default:
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); // 默认7天前
}

return { startDate, endDate: now };
Expand Down
43 changes: 36 additions & 7 deletions src/utils/DiffSimplifier.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import * as vscode from "vscode";

/**
* 用于简化和格式化差异文本的工具类
* 可配置上下文行数和最大行长度,支持差异文本的精简化处理
*/
export class DiffSimplifier {
/**
* 缓存的配置对象
* @private
* @property {boolean} enabled - 是否启用差异简化
* @property {number} contextLines - 保留的上下文行数
* @property {number} maxLineLength - 单行最大长度
*/
private static configCache: {
enabled: boolean;
contextLines: number;
maxLineLength: number;
} | null = null;

/**
* 从 VSCode 配置中获取差异简化的相关设置
* @private
* @returns {Object} 包含差异简化配置的对象
*/
private static getConfig() {
if (this.configCache) {
return this.configCache;
Expand All @@ -23,17 +39,25 @@ export class DiffSimplifier {
return this.configCache;
}

// 添加配置更新方法
/**
* 清除配置缓存,用于配置更新时刷新设置
*/
static clearConfigCache() {
this.configCache = null;
}

private static readonly CONTEXT_LINES = 3;
private static readonly MAX_LINE_LENGTH = 120;

/**
* 简化差异文本,根据配置处理上下文行数和行长度
* @param {string} diff - 原始差异文本
* @returns {string} 简化后的差异文本
*/
static simplify(diff: string): string {
const config = this.getConfig();

// 如果未启用简化,直接返回原文
if (!config.enabled) {
return diff;
}
Expand All @@ -45,7 +69,7 @@ export class DiffSimplifier {
for (let i = 0; i < lines.length; i++) {
const line = lines[i];

// 保留diff头信息
// 保留 diff 头信息(Index、===、---、+++)
if (
line.startsWith("Index:") ||
line.startsWith("===") ||
Expand All @@ -56,28 +80,33 @@ export class DiffSimplifier {
continue;
}

// 处理修改行
// 处理修改行(以 + 或 - 开头)
if (line.startsWith("+") || line.startsWith("-")) {
// 截断过长的行
simplified.push(this.truncateLine(line, config.maxLineLength));
skipCount = 0;
continue;
}

// 处理上下文行
// 处理上下文行,保留配置的行数
if (skipCount < config.contextLines) {
simplified.push(this.truncateLine(line, config.maxLineLength));
skipCount++;
} else if (skipCount === config.contextLines) {
simplified.push("...");
simplified.push("..."); // 添加省略标记
skipCount++;
}
}

return simplified.join("\n");
}

// 优化 truncateLine 方法
/**
* 截断过长的行,添加省略号
* @private
* @param {string} line - 需要处理的行
* @param {number} maxLength - 最大允许长度
* @returns {string} 处理后的行
*/
private static truncateLine(line: string, maxLength: number): string {
if (!line || line.length <= maxLength) {
return line;
Expand Down
25 changes: 24 additions & 1 deletion src/utils/DiffSplitter.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
/**
* 表示一个差异块的结构
* @interface DiffChunk
* @property {string} filename - 发生变更的文件名
* @property {string} content - 包含变更内容的差异文本
*/
export interface DiffChunk {
filename: string;
content: string;
}

/**
* 用于将 Git 和 SVN 的差异文本拆分为独立的文件差异块
* @class DiffSplitter
*/
export class DiffSplitter {
/**
* 将 Git diff 输出拆分为独立的文件差异块
* @param {string} diff - 完整的 Git diff 文本输出
* @returns {DiffChunk[]} 包含各文件差异信息的数组
*/
static splitGitDiff(diff: string): DiffChunk[] {
const chunks: DiffChunk[] = [];
// 按 Git diff 文件头部分割
const files = diff.split("diff --git");

for (const file of files) {
if (!file.trim()) {
continue;
}

// 提取文件名
// 使用正则表达式提取文件名(格式: "a/path/to/file b/path/to/file")
const fileNameMatch = file.match(/a\/(.+?) b\//);
if (!fileNameMatch) {
continue;
Expand All @@ -28,15 +44,22 @@ export class DiffSplitter {
return chunks;
}

/**
* 将 SVN diff 输出拆分为独立的文件差异块
* @param {string} diff - 完整的 SVN diff 文本输出
* @returns {DiffChunk[]} 包含各文件差异信息的数组
*/
static splitSvnDiff(diff: string): DiffChunk[] {
const chunks: DiffChunk[] = [];
// 按 SVN diff 文件索引标记分割
const files = diff.split("Index: ");

for (const file of files) {
if (!file.trim()) {
continue;
}

// SVN diff 中文件名在第一行
const lines = file.split("\n");
const filename = lines[0].trim();

Expand Down
Loading

0 comments on commit d9da1b3

Please sign in to comment.