From 092d1b04fc948f6aea85b141412e3b8587393a5b Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 9 Aug 2025 10:38:02 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat=20(plugin):=20=E6=94=AF=E6=8C=81=20ord?= =?UTF-8?q?er=20=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/plugins/manager.rs | 7 +++++-- src-tauri/src/plugins/mod.rs | 3 +++ src-tauri/src/plugins/python2.rs | 6 +++++- src-tauri/src/plugins/python3.rs | 6 +++++- src/components/StatusBar.vue | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) 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..c3cd952 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" } @@ -30,7 +34,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{}", + "# CodeForge Python 2 Execution\n# Generated at: {}\n\n{}", chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"), code ); diff --git a/src-tauri/src/plugins/python3.rs b/src-tauri/src/plugins/python3.rs index 06723d4..02f0426 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" } @@ -30,7 +34,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{}", + "# CodeForge Python 3 Execution\n# Generated at: {}\n\n{}", chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"), code ); 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 }: 环境未安装` }}
From 33ae1ed44ddd167588276c379362b3b71eecf275 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 9 Aug 2025 11:07:51 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat=20(plugin):=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=AB=98=E4=BA=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 2 +- src/components/CodeEditor.vue | 44 +++-- src/components/OutputPanel.vue | 2 +- src/utils/highlighter/index.ts | 37 +++++ src/utils/highlighter/languages/python2.ts | 94 +++++++++++ src/utils/highlighter/languages/python3.ts | 111 +++++++++++++ src/utils/highlighter/manager.ts | 180 +++++++++++++++++++++ src/utils/highlighter/types.ts | 50 ++++++ 8 files changed, 506 insertions(+), 14 deletions(-) create mode 100644 src/utils/highlighter/index.ts create mode 100644 src/utils/highlighter/languages/python2.ts create mode 100644 src/utils/highlighter/languages/python3.ts create mode 100644 src/utils/highlighter/manager.ts create mode 100644 src/utils/highlighter/types.ts diff --git a/src/App.vue b/src/App.vue index 1897951..4e0cfe5 100644 --- a/src/App.vue +++ b/src/App.vue @@ -283,7 +283,7 @@ const clearOutput = () => { showToast('输出已清空', 'info') } -window.addEventListener("contextmenu", (e) => e.preventDefault(), false); +// window.addEventListener("contextmenu", (e) => e.preventDefault(), false); onMounted(async () => { await getSupportedLanguages() diff --git a/src/components/CodeEditor.vue b/src/components/CodeEditor.vue index 46d2188..7bf0aa9 100644 --- a/src/components/CodeEditor.vue +++ b/src/components/CodeEditor.vue @@ -1,28 +1,39 @@ \ No newline at end of file diff --git a/src/components/OutputPanel.vue b/src/components/OutputPanel.vue index fc79782..bb7911f 100644 --- a/src/components/OutputPanel.vue +++ b/src/components/OutputPanel.vue @@ -21,7 +21,7 @@
{{ output }}
-
+

没有输出

可以尝试运行一些代码

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 From 96f33ab9216fba7cb684e6d4b8a93e2c03590faa Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 9 Aug 2025 11:27:14 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix=20(core):=20=E4=BF=AE=E5=A4=8D=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8=E6=98=BE=E7=A4=BA=E6=B7=B7=E4=B9=B1=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 8 +++++--- src/components/CodeEditor.vue | 15 ++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/App.vue b/src/App.vue index 4e0cfe5..a901894 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 7bf0aa9..286ca25 100644 --- a/src/components/CodeEditor.vue +++ b/src/components/CodeEditor.vue @@ -1,19 +1,20 @@ \ No newline at end of file From fe1892b4ce9690a46eca4367475d797eb6624b27 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sat, 9 Aug 2025 12:02:19 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat=20(python):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=A2=84=E5=A4=84=E7=90=86=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++++- src-tauri/src/plugins/python2.rs | 8 +------- src-tauri/src/plugins/python3.rs | 10 ++-------- 3 files changed, 8 insertions(+), 16 deletions(-) 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/python2.rs b/src-tauri/src/plugins/python2.rs index c3cd952..6e387ea 100644 --- a/src-tauri/src/plugins/python2.rs +++ b/src-tauri/src/plugins/python2.rs @@ -32,13 +32,7 @@ impl LanguagePlugin for Python2Plugin { } fn pre_execute_hook(&self, code: &str) -> Result { - // 添加一些 Python 特定的预处理 - let processed_code = format!( - "# CodeForge Python 2 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 02f0426..55db338 100644 --- a/src-tauri/src/plugins/python3.rs +++ b/src-tauri/src/plugins/python3.rs @@ -6,7 +6,7 @@ impl LanguagePlugin for Python3Plugin { fn get_order(&self) -> i32 { 2 } - + fn get_language_name(&self) -> &'static str { "Python 3" } @@ -32,13 +32,7 @@ impl LanguagePlugin for Python3Plugin { } fn pre_execute_hook(&self, code: &str) -> Result { - // 添加一些 Python 特定的预处理 - let processed_code = format!( - "# CodeForge Python 3 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> {