gitlab-ai-review 3.8.2 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +31 -8
- package/lib/impact-analyzer.js +42 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -457,30 +457,53 @@ export class GitLabAIReview {
|
|
|
457
457
|
impactAnalysis
|
|
458
458
|
);
|
|
459
459
|
|
|
460
|
+
// 智能合并修改块的评论
|
|
461
|
+
const mergedReviews = this.mergeModificationReviews(fileReview.reviews, meaningfulChanges);
|
|
462
|
+
|
|
460
463
|
// 根据 AI 返回的结果,只对有问题的行添加评论
|
|
461
|
-
for (const review of
|
|
464
|
+
for (const review of mergedReviews) {
|
|
462
465
|
if (review.hasIssue) {
|
|
463
466
|
try {
|
|
467
|
+
// 构建评论位置参数
|
|
468
|
+
const positionParams = {
|
|
469
|
+
filePath: fileName,
|
|
470
|
+
oldPath: change.old_path,
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// 如果是修改块(同时有 oldLine 和 newLine),同时指定两个行号
|
|
474
|
+
if (review.oldLine && review.newLine) {
|
|
475
|
+
positionParams.oldLine = review.oldLine;
|
|
476
|
+
positionParams.newLine = review.newLine;
|
|
477
|
+
} else if (review.lineNumber) {
|
|
478
|
+
// 单独的删除或新增
|
|
479
|
+
const relatedChange = meaningfulChanges.find(c => c.lineNumber === review.lineNumber);
|
|
480
|
+
const isDeletion = relatedChange && relatedChange.type === 'deletion';
|
|
481
|
+
if (isDeletion) {
|
|
482
|
+
positionParams.oldLine = review.lineNumber;
|
|
483
|
+
} else {
|
|
484
|
+
positionParams.newLine = review.lineNumber;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
464
488
|
const commentResult = await this.gitlabClient.createLineComment(
|
|
465
489
|
this.config.project.projectId,
|
|
466
490
|
this.config.project.mergeRequestIid,
|
|
467
491
|
`🤖 **AI 代码审查**\n\n${review.comment}`,
|
|
468
|
-
|
|
469
|
-
filePath: fileName,
|
|
470
|
-
oldPath: change.old_path,
|
|
471
|
-
newLine: review.lineNumber,
|
|
472
|
-
}
|
|
492
|
+
positionParams
|
|
473
493
|
);
|
|
474
494
|
|
|
475
495
|
results.push({
|
|
476
496
|
status: 'success',
|
|
477
497
|
fileName,
|
|
478
|
-
lineNumber: review.lineNumber,
|
|
498
|
+
lineNumber: review.lineNumber || review.newLine || review.oldLine,
|
|
479
499
|
comment: review.comment,
|
|
480
500
|
commentResult,
|
|
481
501
|
});
|
|
482
502
|
|
|
483
|
-
|
|
503
|
+
const lineDesc = review.oldLine && review.newLine
|
|
504
|
+
? `第 ${review.oldLine}-${review.newLine} 行`
|
|
505
|
+
: `第 ${review.lineNumber} 行`;
|
|
506
|
+
console.log(` ✓ ${lineDesc}:已添加评论`);
|
|
484
507
|
} catch (error) {
|
|
485
508
|
results.push({
|
|
486
509
|
status: 'error',
|
package/lib/impact-analyzer.js
CHANGED
|
@@ -195,6 +195,48 @@ export function extractChangedSymbols(diff, fileName) {
|
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* 检查一行代码中的符号使用是否可能有局部定义
|
|
200
|
+
* @param {string} line - 代码行
|
|
201
|
+
* @param {string} symbol - 符号名
|
|
202
|
+
* @returns {boolean} 是否可能有局部定义
|
|
203
|
+
*/
|
|
204
|
+
function hasLocalDefinition(line, symbol) {
|
|
205
|
+
const trimmedLine = line.trim();
|
|
206
|
+
|
|
207
|
+
// 检查是否是局部变量定义
|
|
208
|
+
const varPatterns = [
|
|
209
|
+
new RegExp(`(const|let|var)\\s+${symbol}\\s*[=:]`), // const symbol = ...
|
|
210
|
+
new RegExp(`(const|let|var)\\s*{[^}]*\\b${symbol}\\b[^}]*}`), // const { symbol } = ...
|
|
211
|
+
new RegExp(`function\\s+\\w+\\([^)]*\\b${symbol}\\b[^)]*\\)`), // function foo(symbol)
|
|
212
|
+
new RegExp(`\\([^)]*\\b${symbol}\\b[^)]*\\)\\s*=>`), // (symbol) => ...
|
|
213
|
+
new RegExp(`\\b${symbol}\\s*:`), // symbol: in object or parameter
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
return varPatterns.some(pattern => pattern.test(trimmedLine));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* 检查一行代码中是否有导入语句
|
|
221
|
+
* @param {string} line - 代码行
|
|
222
|
+
* @param {string} symbol - 符号名
|
|
223
|
+
* @returns {boolean} 是否有导入
|
|
224
|
+
*/
|
|
225
|
+
function hasImportStatement(line, symbol) {
|
|
226
|
+
const trimmedLine = line.trim();
|
|
227
|
+
|
|
228
|
+
// 检查各种导入模式
|
|
229
|
+
const importPatterns = [
|
|
230
|
+
new RegExp(`import\\s+${symbol}\\s+from`), // import symbol from ...
|
|
231
|
+
new RegExp(`import\\s*{[^}]*\\b${symbol}\\b[^}]*}\\s+from`), // import { symbol } from ...
|
|
232
|
+
new RegExp(`import\\s*\\*\\s+as\\s+${symbol}\\s+from`), // import * as symbol from ...
|
|
233
|
+
new RegExp(`require\\([^)]*\\)\\.${symbol}`), // require(...).symbol
|
|
234
|
+
new RegExp(`const\\s+${symbol}\\s*=\\s*require`), // const symbol = require(...)
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
return importPatterns.some(pattern => pattern.test(trimmedLine));
|
|
238
|
+
}
|
|
239
|
+
|
|
198
240
|
/**
|
|
199
241
|
* 检查文件内部是否使用了指定的符号
|
|
200
242
|
* @param {string} content - 文件内容
|