|
|
|
|
|
|
|
|
class OpenCodeAI { |
|
|
constructor() { |
|
|
this.supportedLanguages = [ |
|
|
'javascript', 'python', 'java', 'cpp', 'c', 'csharp', |
|
|
'go', 'rust', 'php', 'typescript', 'html', 'css' |
|
|
]; |
|
|
} |
|
|
|
|
|
|
|
|
async analyzeCode(code, language) { |
|
|
const analysis = { |
|
|
language: this.detectLanguage(code, language), |
|
|
issues: [], |
|
|
suggestions: [], |
|
|
metrics: this.calculateMetrics(code) |
|
|
}; |
|
|
|
|
|
|
|
|
analysis.issues = this.checkSyntax(code, analysis.language); |
|
|
|
|
|
|
|
|
analysis.suggestions = this.generateSuggestions(code, analysis.language); |
|
|
|
|
|
return analysis; |
|
|
} |
|
|
|
|
|
|
|
|
detectLanguage(code, hintedLanguage) { |
|
|
if (hintedLanguage && this.supportedLanguages.includes(hintedLanguage)) { |
|
|
return hintedLanguage; |
|
|
} |
|
|
|
|
|
|
|
|
if (code.includes('def ') && code.includes(':')) return 'python'; |
|
|
if (code.includes('function ') || code.includes('const ')) return 'javascript'; |
|
|
if (code.includes('public class ')) return 'java'; |
|
|
if (code.includes('#include')) return 'cpp'; |
|
|
if (code.includes('<!DOCTYPE')) return 'html'; |
|
|
if (code.includes('{') && code.includes('}')) return 'css'; |
|
|
|
|
|
return 'javascript'; |
|
|
} |
|
|
|
|
|
|
|
|
checkSyntax(code, language) { |
|
|
const issues = []; |
|
|
|
|
|
|
|
|
const lines = code.split('\n'); |
|
|
lines.forEach((line, index) => { |
|
|
|
|
|
if (line.includes('{') || line.includes('(') || line.includes('[')) { |
|
|
const openBrackets = (line.match(/[({[]/g) || []).length; |
|
|
const closeBrackets = (line.match(/[)}\]]/g) || []).length; |
|
|
if (openBrackets !== closeBrackets) { |
|
|
issues.push({ |
|
|
line: index + 1, |
|
|
type: 'syntax', |
|
|
message: '可能存在未闭合的括号', |
|
|
severity: 'warning' |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (line.length > 120) { |
|
|
issues.push({ |
|
|
line: index + 1, |
|
|
type: 'style', |
|
|
message: '行长度过长 (>120字符)', |
|
|
severity: 'info' |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
switch (language) { |
|
|
case 'javascript': |
|
|
issues.push(...this.checkJavaScript(code)); |
|
|
break; |
|
|
case 'python': |
|
|
issues.push(...this.checkPython(code)); |
|
|
break; |
|
|
case 'java': |
|
|
issues.push(...this.checkJava(code)); |
|
|
break; |
|
|
} |
|
|
|
|
|
return issues; |
|
|
} |
|
|
|
|
|
|
|
|
checkJavaScript(code) { |
|
|
const issues = []; |
|
|
|
|
|
|
|
|
if (code.includes('var ')) { |
|
|
issues.push({ |
|
|
line: null, |
|
|
type: 'style', |
|
|
message: '建议使用 let 或 const 替代 var', |
|
|
severity: 'warning' |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const statements = code.split('\n').filter(line => |
|
|
line.trim() && !line.trim().startsWith('//') && |
|
|
!line.trim().startsWith('/*') && |
|
|
!line.includes('{') && !line.includes('}') && |
|
|
!line.endsWith(';') && !line.endsWith(',') && |
|
|
!line.includes('if ') && !line.includes('for ') && |
|
|
!line.includes('while ') && !line.includes('function ') |
|
|
); |
|
|
|
|
|
if (statements.length > 0) { |
|
|
issues.push({ |
|
|
line: null, |
|
|
type: 'style', |
|
|
message: '可能缺少分号', |
|
|
severity: 'info' |
|
|
}); |
|
|
} |
|
|
|
|
|
return issues; |
|
|
} |
|
|
|
|
|
|
|
|
checkPython(code) { |
|
|
const issues = []; |
|
|
|
|
|
|
|
|
const lines = code.split('\n'); |
|
|
lines.forEach((line, index) => { |
|
|
const trimmed = line.trim(); |
|
|
if (trimmed && !trimmed.startsWith('#')) { |
|
|
const leadingSpaces = line.length - line.trimStart().length; |
|
|
if (leadingSpaces % 4 !== 0) { |
|
|
issues.push({ |
|
|
line: index + 1, |
|
|
type: 'style', |
|
|
message: '缩进应该使用4个空格', |
|
|
severity: 'warning' |
|
|
}); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
return issues; |
|
|
} |
|
|
|
|
|
|
|
|
checkJava(code) { |
|
|
const issues = []; |
|
|
|
|
|
|
|
|
const classMatch = code.match(/class\s+(\w+)/); |
|
|
if (classMatch && !/^[A-Z]/.test(classMatch[1])) { |
|
|
issues.push({ |
|
|
line: null, |
|
|
type: 'style', |
|
|
message: '类名应该以大写字母开头', |
|
|
severity: 'warning' |
|
|
}); |
|
|
} |
|
|
|
|
|
return issues; |
|
|
} |
|
|
|
|
|
|
|
|
generateSuggestions(code, language) { |
|
|
const suggestions = []; |
|
|
|
|
|
|
|
|
if (code.includes('for (')) { |
|
|
suggestions.push({ |
|
|
type: 'performance', |
|
|
message: '考虑使用更高效的循环方式,如 forEach 或 map', |
|
|
code_example: '// 替代方案\narray.forEach(item => { /* ... */ });' |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (code.includes('TODO') || code.includes('FIXME')) { |
|
|
suggestions.push({ |
|
|
type: 'maintenance', |
|
|
message: '发现待办事项,建议及时处理' |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (code.includes('eval(')) { |
|
|
suggestions.push({ |
|
|
type: 'security', |
|
|
message: '避免使用 eval(),存在安全风险', |
|
|
severity: 'high' |
|
|
}); |
|
|
} |
|
|
|
|
|
return suggestions; |
|
|
} |
|
|
|
|
|
|
|
|
calculateMetrics(code) { |
|
|
const lines = code.split('\n'); |
|
|
const totalLines = lines.length; |
|
|
const codeLines = lines.filter(line => |
|
|
line.trim() && !line.trim().startsWith('//') && |
|
|
!line.trim().startsWith('#') && !line.trim().startsWith('/*') |
|
|
).length; |
|
|
|
|
|
return { |
|
|
total_lines: totalLines, |
|
|
code_lines: codeLines, |
|
|
comment_lines: totalLines - codeLines, |
|
|
complexity: this.calculateComplexity(code) |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
calculateComplexity(code) { |
|
|
let complexity = 1; |
|
|
|
|
|
const complexityKeywords = ['if', 'else', 'for', 'while', 'switch', 'case', 'catch', '&&', '||']; |
|
|
complexityKeywords.forEach(keyword => { |
|
|
const regex = new RegExp(`\\b${keyword}\\b`, 'g'); |
|
|
const matches = code.match(regex); |
|
|
if (matches) complexity += matches.length; |
|
|
}); |
|
|
|
|
|
return complexity; |
|
|
} |
|
|
|
|
|
|
|
|
async getCodeCompletion(partialCode, position) { |
|
|
|
|
|
const suggestions = []; |
|
|
const line = partialCode.split('\n')[position.line - 1] || ''; |
|
|
const currentWord = line.substring(0, position.character).split(' ').pop(); |
|
|
const language = this.detectLanguage(partialCode); |
|
|
|
|
|
switch (language) { |
|
|
case 'javascript': |
|
|
suggestions.push(...this.getJavaScriptCompletions(currentWord)); |
|
|
break; |
|
|
case 'python': |
|
|
suggestions.push(...this.getPythonCompletions(currentWord)); |
|
|
break; |
|
|
default: |
|
|
suggestions.push(...this.getGenericCompletions(currentWord)); |
|
|
} |
|
|
|
|
|
return suggestions; |
|
|
} |
|
|
|
|
|
getJavaScriptCompletions(currentWord) { |
|
|
const jsKeywords = [ |
|
|
'function', 'const', 'let', 'var', 'if', 'else', 'for', 'while', |
|
|
'return', 'class', 'extends', 'import', 'export', 'async', 'await', |
|
|
'map', 'filter', 'reduce', 'forEach', 'find', 'some', 'every' |
|
|
]; |
|
|
|
|
|
return jsKeywords |
|
|
.filter(keyword => keyword.startsWith(currentWord)) |
|
|
.map(keyword => ({ |
|
|
text: keyword, |
|
|
type: 'keyword', |
|
|
description: `JavaScript keyword: ${keyword}` |
|
|
})); |
|
|
} |
|
|
|
|
|
getPythonCompletions(currentWord) { |
|
|
const pythonKeywords = [ |
|
|
'def', 'class', 'if', 'elif', 'else', 'for', 'while', |
|
|
'return', 'import', 'from', 'as', 'try', 'except', 'with', |
|
|
'len', 'range', 'list', 'dict', 'set', 'tuple', 'str' |
|
|
]; |
|
|
|
|
|
return pythonKeywords |
|
|
.filter(keyword => keyword.startsWith(currentWord)) |
|
|
.map(keyword => ({ |
|
|
text: keyword, |
|
|
type: 'keyword', |
|
|
description: `Python keyword: ${keyword}` |
|
|
})); |
|
|
} |
|
|
|
|
|
getGenericCompletions(currentWord) { |
|
|
return [ |
|
|
{ text: currentWord, type: 'variable', description: 'Current variable' } |
|
|
]; |
|
|
} |
|
|
} |
|
|
|
|
|
export default OpenCodeAI; |