diff --git a/.gitignore b/.gitignore index 3446cb9..e7b8986 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ dist-ssr *.sln *.sw? pnpm-lock.yaml +*.csv +*.json diff --git a/README.md b/README.md index 7dcc151..aee4c81 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ -# CodeForge +
+ + +

CodeForge

CodeForge 是一款轻量级、高性能的桌面代码执行器,专为开发者、学生和编程爱好者设计。 +
## 特性 diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index 394f3b6..787641a 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -20,8 +20,11 @@ impl PluginManager { } pub fn get_supported_languages(&self) -> Vec { - self.plugins - .iter() + let mut plugins: Vec<_> = self.plugins.iter().collect(); + plugins.sort_by_key(|(_, plugin)| plugin.get_order()); + + plugins + .into_iter() .map(|(key, plugin)| { serde_json::json!({ "name": plugin.get_language_name(), diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index f830ed2..a0f7f4c 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -27,6 +27,9 @@ pub struct LanguageInfo { // 语言插件接口 pub trait LanguagePlugin: Send + Sync { + fn get_order(&self) -> i32 { + 0 + } fn get_language_name(&self) -> &'static str; fn get_file_extension(&self) -> &'static str; fn get_commands(&self) -> Vec<&'static str>; diff --git a/src-tauri/src/plugins/python2.rs b/src-tauri/src/plugins/python2.rs index b0d12a1..6e387ea 100644 --- a/src-tauri/src/plugins/python2.rs +++ b/src-tauri/src/plugins/python2.rs @@ -3,6 +3,10 @@ use super::{ExecutionResult, LanguagePlugin}; pub struct Python2Plugin; impl LanguagePlugin for Python2Plugin { + fn get_order(&self) -> i32 { + 1 + } + fn get_language_name(&self) -> &'static str { "Python 2" } @@ -28,13 +32,7 @@ impl LanguagePlugin for Python2Plugin { } fn pre_execute_hook(&self, code: &str) -> Result { - // 添加一些 Python 特定的预处理 - let processed_code = format!( - "# CodeForge Python Execution\n# Generated at: {}\n\n{}", - chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"), - code - ); - Ok(processed_code) + Ok(code.to_string()) } fn post_execute_hook(&self, result: &mut ExecutionResult) -> Result<(), String> { diff --git a/src-tauri/src/plugins/python3.rs b/src-tauri/src/plugins/python3.rs index 06723d4..55db338 100644 --- a/src-tauri/src/plugins/python3.rs +++ b/src-tauri/src/plugins/python3.rs @@ -3,6 +3,10 @@ use super::{ExecutionResult, LanguagePlugin}; pub struct Python3Plugin; impl LanguagePlugin for Python3Plugin { + fn get_order(&self) -> i32 { + 2 + } + fn get_language_name(&self) -> &'static str { "Python 3" } @@ -28,13 +32,7 @@ impl LanguagePlugin for Python3Plugin { } fn pre_execute_hook(&self, code: &str) -> Result { - // 添加一些 Python 特定的预处理 - let processed_code = format!( - "# CodeForge Python Execution\n# Generated at: {}\n\n{}", - chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"), - code - ); - Ok(processed_code) + Ok(code.to_string()) } fn post_execute_hook(&self, result: &mut ExecutionResult) -> Result<(), String> { diff --git a/src/App.vue b/src/App.vue index 1897951..f2eca06 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,14 +12,16 @@
-
-
+
+

{{ getLanguageDisplayName(currentLanguage) }} 代码编辑器

{{ code.length }} 字符, {{ code.split('\n').length }}
- +
+ +
diff --git a/src/components/CodeEditor.vue b/src/components/CodeEditor.vue index 46d2188..286ca25 100644 --- a/src/components/CodeEditor.vue +++ b/src/components/CodeEditor.vue @@ -1,28 +1,40 @@ \ No newline at end of file diff --git a/src/components/OutputPanel.vue b/src/components/OutputPanel.vue index fc79782..b40ccea 100644 --- a/src/components/OutputPanel.vue +++ b/src/components/OutputPanel.vue @@ -3,11 +3,23 @@
- 输出 + 控制台
-
- - {{ executionTime }} 毫秒 +
+ {{ isCopied ? '已复制' : '复制失败' }} + + + + +
+ + {{ executionTime }} 毫秒 +
@@ -21,7 +33,7 @@
{{ output }}
-
+

没有输出

可以尝试运行一些代码

@@ -31,12 +43,55 @@ \ No newline at end of file diff --git a/src/components/StatusBar.vue b/src/components/StatusBar.vue index 4d2c965..83e6ce6 100644 --- a/src/components/StatusBar.vue +++ b/src/components/StatusBar.vue @@ -6,7 +6,7 @@ - {{ envInfo.installed ? `${ envInfo.language }: ${ envInfo.version }` : `${ envInfo.language } 环境未安装` }} + {{ envInfo.installed ? `${ envInfo.language }: ${ envInfo.version }` : `${ envInfo.language }: 环境未安装` }}
diff --git a/src/utils/highlighter/index.ts b/src/utils/highlighter/index.ts new file mode 100644 index 0000000..de2c173 --- /dev/null +++ b/src/utils/highlighter/index.ts @@ -0,0 +1,37 @@ +export * from './types' +export * from './manager' +export { Python2Highlighter } from './languages/python2' +export { Python3Highlighter } from './languages/python3' + +// 导入并重新导出管理器实例 +import { HighlightManager } from './manager' + +// 创建单例实例 +export const highlightManager = new HighlightManager() + +/** + * 高亮代码的便捷函数 + * @param code 源代码 + * @param language 语言名称 + * @returns 高亮后的 HTML + */ +export function highlightCode(code: string, language: string): string +{ + return highlightManager.highlight(code, language) +} + +/** + * 获取支持的语言列表 + */ +export function getSupportedLanguages() +{ + return highlightManager.getSupportedLanguages() +} + +/** + * 注册自定义高亮器 + */ +export function registerHighlighter(highlighter: import('./types').LanguageHighlighter) +{ + highlightManager.register(highlighter) +} \ No newline at end of file diff --git a/src/utils/highlighter/languages/python2.ts b/src/utils/highlighter/languages/python2.ts new file mode 100644 index 0000000..a1cbf2e --- /dev/null +++ b/src/utils/highlighter/languages/python2.ts @@ -0,0 +1,94 @@ +import type { HighlightRule, LanguageHighlighter } from '../types' + +export class Python2Highlighter + implements LanguageHighlighter +{ + getLanguageName(): string + { + return 'python2' + } + + getDisplayName(): string + { + return 'Python 2' + } + + getOrder(): number + { + return 2 + } + + getRules(): HighlightRule[] + { + return [ + // 1. 注释 (最高优先级) + { + pattern: /#.*$/gm, + className: 'text-gray-500 italic', + priority: 1 + }, + + // 2. 字符串 (高优先级) + { + pattern: /"""[\s\S]*?"""/g, + className: 'text-green-600', + priority: 2 + }, + { + pattern: /'''[\s\S]*?'''/g, + className: 'text-green-600', + priority: 2 + }, + { + pattern: /"(?:[^"\\]|\\.)*"/g, + className: 'text-green-600', + priority: 2 + }, + { + pattern: /'(?:[^'\\]|\\.)*'/g, + className: 'text-green-600', + priority: 2 + }, + + // 3. Python 2 关键字 + { + pattern: /\b(def|class|if|elif|else|for|while|try|except|finally|with|as|import|from|return|yield|break|continue|pass|lambda|and|or|not|in|is|True|False|None|print|exec|raw_input)\b/g, + className: 'text-purple-600 font-semibold', + priority: 3 + }, + + // 4. 数字 + { + pattern: /\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b/g, + className: 'text-blue-600', + priority: 4 + }, + + // 5. 函数调用 + { + pattern: /\b([a-zA-Z_]\w*)(?=\s*\()/g, + className: 'text-orange-600', + priority: 5 + }, + + // 6. 类名定义 + { + pattern: /\bclass\s+([A-Z]\w*)/g, + className: 'text-yellow-600 font-semibold', + priority: 6 + } + ] + } + + preProcess(code: string): string + { + // Python 2 特定的预处理 + return code + } + + postProcess(highlighted: string): string + { + // Python 2 特定的后处理 + return highlighted + } +} \ No newline at end of file diff --git a/src/utils/highlighter/languages/python3.ts b/src/utils/highlighter/languages/python3.ts new file mode 100644 index 0000000..d6f83ed --- /dev/null +++ b/src/utils/highlighter/languages/python3.ts @@ -0,0 +1,111 @@ +import type { HighlightRule, LanguageHighlighter } from '../types' + +export class Python3Highlighter + implements LanguageHighlighter +{ + getLanguageName(): string + { + return 'python3' + } + + getDisplayName(): string + { + return 'Python 3' + } + + getOrder(): number + { + return 1 + } + + getRules(): HighlightRule[] + { + return [ + // 1. 注释 (最高优先级) + { + pattern: /#.*$/gm, + className: 'text-gray-500 italic', + priority: 1 + }, + + // 2. 字符串 (高优先级) + { + pattern: /"""[\s\S]*?"""/g, + className: 'text-green-600', + priority: 2 + }, + { + pattern: /'''[\s\S]*?'''/g, + className: 'text-green-600', + priority: 2 + }, + { + pattern: /f"(?:[^"\\]|\\.)*"/g, + className: 'text-emerald-600', + priority: 2 + }, // f-strings + { + pattern: /f'(?:[^'\\]|\\.)*'/g, + className: 'text-emerald-600', + priority: 2 + }, // f-strings + { + pattern: /"(?:[^"\\]|\\.)*"/g, + className: 'text-green-600', + priority: 2 + }, + { + pattern: /'(?:[^'\\]|\\.)*'/g, + className: 'text-green-600', + priority: 2 + }, + + // 3. Python 3 关键字 + { + pattern: /\b(def|class|if|elif|else|for|while|try|except|finally|with|as|import|from|return|yield|break|continue|pass|lambda|and|or|not|in|is|True|False|None|async|await|nonlocal)\b/g, + className: 'text-purple-600 font-semibold', + priority: 3 + }, + + // 4. 数字 + { + pattern: /\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b/g, + className: 'text-blue-600', + priority: 4 + }, + + // 5. 函数调用 + { + pattern: /\b([a-zA-Z_]\w*)(?=\s*\()/g, + className: 'text-orange-600', + priority: 5 + }, + + // 6. 装饰器 + { + pattern: /@[a-zA-Z_]\w*/g, + className: 'text-yellow-600', + priority: 6 + }, + + // 7. 类名定义 + { + pattern: /\bclass\s+([A-Z]\w*)/g, + className: 'text-yellow-600 font-semibold', + priority: 7 + } + ] + } + + preProcess(code: string): string + { + // Python 3 特定的预处理 + return code + } + + postProcess(highlighted: string): string + { + // Python 3 特定的后处理 + return highlighted + } +} \ No newline at end of file diff --git a/src/utils/highlighter/manager.ts b/src/utils/highlighter/manager.ts new file mode 100644 index 0000000..9cc4105 --- /dev/null +++ b/src/utils/highlighter/manager.ts @@ -0,0 +1,180 @@ +import type { HighlightMatch, LanguageHighlighter } from './types' +import { Python2Highlighter } from './languages/python2' +import { Python3Highlighter } from './languages/python3' + +export class HighlightManager +{ + private highlighters = new Map() + + constructor() + { + this.registerDefaultHighlighters() + } + + /** + * 注册默认高亮器 + */ + private registerDefaultHighlighters(): void + { + this.register(new Python2Highlighter()) + this.register(new Python3Highlighter()) + } + + /** + * 注册高亮器 + */ + register(highlighter: LanguageHighlighter): void + { + this.highlighters.set(highlighter.getLanguageName(), highlighter) + } + + /** + * 注销高亮器 + */ + unregister(languageName: string): boolean + { + return this.highlighters.delete(languageName) + } + + /** + * 获取高亮器 + */ + getHighlighter(languageName: string): LanguageHighlighter | undefined + { + return this.highlighters.get(languageName) + } + + /** + * 获取支持的语言列表 + */ + getSupportedLanguages(): Array<{ name: string; displayName: string; order: number }> + { + const languages = Array.from(this.highlighters.values()) + .map(highlighter => ({ + name: highlighter.getLanguageName(), + displayName: highlighter.getDisplayName(), + order: highlighter.getOrder() + })) + + // 按 order 排序 + return languages.sort((a, b) => a.order - b.order) + } + + /** + * 检查是否支持某种语言 + */ + isSupported(languageName: string): boolean + { + return this.highlighters.has(languageName) + } + + /** + * 对代码进行语法高亮 + */ + highlight(code: string, languageName: string): string + { + if (!code) { + return '' + } + + const highlighter = this.getHighlighter(languageName) + if (!highlighter) { + // 如果不支持该语言,返回转义后的原始代码 + return this.escapeHtml(code) + } + + // 预处理 + let processedCode = highlighter.preProcess ? highlighter.preProcess(code) : code + + // HTML 转义 + processedCode = this.escapeHtml(processedCode) + + // 应用高亮规则 + const highlighted = this.applyHighlightRules(processedCode, highlighter.getRules()) + + // 后处理 + return highlighter.postProcess ? highlighter.postProcess(highlighted) : highlighted + } + + /** + * 应用高亮规则 + */ + private applyHighlightRules(code: string, rules: Array<{ pattern: RegExp; className: string; priority?: number }>): string + { + // 收集所有匹配 + const matches: HighlightMatch[] = [] + + for (const rule of rules) { + const regex = new RegExp(rule.pattern.source, rule.pattern.flags) + let match + + while ((match = regex.exec(code)) !== null) { + matches.push({ + start: match.index, + end: match.index + match[0].length, + className: rule.className, + priority: rule.priority || 999 + }) + + // 防止无限循环 + if (!rule.pattern.global) { + break + } + } + } + + // 按优先级排序,优先级高的(数字小的)在前 + matches.sort((a, b) => a.priority - b.priority || a.start - b.start) + + // 移除重叠的匹配(保留高优先级的) + const filteredMatches = this.removeOverlappingMatches(matches) + + // 按开始位置倒序排序,从后往前插入标签 + filteredMatches.sort((a, b) => b.start - a.start) + + // 插入高亮标签 + let highlighted = code + for (const match of filteredMatches) { + const before = highlighted.substring(0, match.start) + const content = highlighted.substring(match.start, match.end) + const after = highlighted.substring(match.end) + + highlighted = before + `${ content }` + after + } + + return highlighted + } + + /** + * 移除重叠的匹配 + */ + private removeOverlappingMatches(matches: HighlightMatch[]): HighlightMatch[] + { + const filteredMatches: HighlightMatch[] = [] + + for (const match of matches) { + const hasOverlap = filteredMatches.some(existing => + (match.start < existing.end && match.end > existing.start) + ) + + if (!hasOverlap) { + filteredMatches.push(match) + } + } + + return filteredMatches + } + + /** + * HTML 转义 + */ + private escapeHtml(text: string): string + { + return text + // .replace(/&/g, '&') + .replace(//g, '>') + // .replace(/"/g, '"') + // .replace(/'/g, ''') + } +} diff --git a/src/utils/highlighter/types.ts b/src/utils/highlighter/types.ts new file mode 100644 index 0000000..41fe98d --- /dev/null +++ b/src/utils/highlighter/types.ts @@ -0,0 +1,50 @@ +// 语法高亮规则接口 +export interface HighlightRule +{ + pattern: RegExp + className: string + priority?: number +} + +// 匹配结果接口 +export interface HighlightMatch +{ + start: number + end: number + className: string + priority: number +} + +// 语言高亮插件接口 +export interface LanguageHighlighter +{ + /** + * 获取语言名称 + */ + getLanguageName(): string + + /** + * 获取显示名称 + */ + getDisplayName(): string + + /** + * 获取高亮规则 + */ + getRules(): HighlightRule[] + + /** + * 获取排序权重(数字越小越靠前) + */ + getOrder(): number + + /** + * 预处理代码(可选) + */ + preProcess?(code: string): string + + /** + * 后处理高亮结果(可选) + */ + postProcess?(highlighted: string): string +} \ No newline at end of file