CodeForge:配置驱动插件化语言系统,让多语言代码草稿本添加语言更轻松!
起因找不到顺手的「代码草稿本」自己动手做事情起始很简单。我常需快速验证小段代码像 Python 正则、Go 并发写法、Rust 所有权实验等。但为运行这几行临时代码要么新建项目、配置环境要么开浏览器找在线 playground可在线的通常只支持一两种语言还常连不上。我想要的很简单打开就能写、写完一键能跑、还能看懂跑出的 JSON。最好是桌面应用本地、快速、不依赖网络。找了一圈没合适的于是我做了 CodeForge。目前它支持 30 多种语言运行涵盖 Python、Node、Go、Rust、Swift、Haskell 甚至 AppleScript。本文重点聊支撑它的「插件化的语言系统」以及其工程落地方式。第一个决定为何选 Tauri 2 而非 Electron做桌面应用首先要选壳。Electron 是省心之选生态成熟、文档全、遇问题易搜到答案。我起初也这么想但越想越觉得它有两个难以接受的代价包体积和内存占用。一个定位「随手打开的代码草稿本」若安装后一百多兆、空载就占几百兆内存便背离了我做它的初衷——那种「轻、快、即开即用」的手感是产品的核心体验。Tauri 2 用系统自带的 WebView 渲染前端后端是 Rust最终产物体积小常驻内存低。对主打轻量的工具而言这不是「优化项」而是「能否成立」的问题。当然也有代价WebView 在不同平台上行为不完全一致macOS 的 WKWebView、Windows 的 WebView2、Linux 的 WebKitGTK偶尔需写平台分支Rust 的学习曲线比 Node 陡前期开发明显更慢遇到问题时能搜到的现成答案比 Electron 少很多不少坑得自己啃文档和 issue。但对这个项目来说这些代价换来的轻量和性能是值得的。最终技术栈为前端 Vue 3 TypeScript CodeMirror 6后端 Rust Tauri 2执行历史、AI 对话、代码片段、应用配置统一存入 SQLite。核心难题30 多种语言避免写成 30 个 if - else支持一种语言运行很简单找到解释器/编译器拼命令起子进程抓取输出。但语言数量增加时这种简单写法会变成巨大的分支判断如「如果是 Python 就这样、如果是 Go 就那样、如果是 Rust 还要先编译再运行……」。每添加一种语言都要修改核心逻辑每种语言的特殊性都会污染主流程这是随规模膨胀易失控的设计。所以从一开始我就将其设计为「插件化」。强调的是其灵魂在于「配置驱动」。核心引擎不认识具体语言只认识描述「这门语言该怎么跑」的配置绝大多数通用逻辑拼命令、起进程、抓输出、清理都写在统一处每个语言插件本身很薄。一个语言插件能有多薄在 Rust 里所有语言实现同一个 traitpub trait LanguagePlugin: Send Sync { fn get_language_name(self) - static str; // 显示名如 Python 3 fn get_language_key(self) - static str; // 唯一键如 python3 fn get_file_extension(self) - String; // 扩展名 fn get_version_args(self) - Vecstatic str; // 版本探测参数 fn get_path_command(self) - String; // 探测工具链是否可用 fn get_default_config(self) - PluginConfig; // 默认配置核心 fn get_default_command(self) - String; // …还有一组带默认实现的钩子下面会讲}真正承载「这门语言怎么跑」的是 PluginConfigpub struct PluginConfig { pub enabled: bool, pub extension: String, pub language: String, pub run_command: OptionString, // 命令模板如 python3 $filename pub before_compile: OptionString, // 运行前的准备命令 pub after_compile: OptionString, // 运行后的清理命令 pub template: OptionString, // 新建文件时的初始模板 pub timeout: Optionu64, // 超时默认 30s pub execute_home: OptionString, // 自定义工具链路径 // …}解释型语言的插件几乎就是「填一张表」。如 Python 3 插件的核心无执行逻辑仅声明「我叫什么、扩展名是啥、用 python3 $filename 跑」fn get_default_config(self) - PluginConfig { PluginConfig { enabled: true, language: python3.into(), extension: py.into(), run_command: Some(python3 $filename.into()), // $filename 运行时被替换 before_compile: None, after_compile: None, template: Some(# 在这里输入 Python 3 代码.into()), timeout: Some(30), // … }}核心引擎拿到文件后从配置中取出 run_command将 $filename 替换为真实路径其余起进程、流式抓输出、算耗时等都是「与语言无关」的通用逻辑。添加一门解释型语言本质就是再填一张表。编译型语言如何融入同一套抽象解释型语言一步到位而 Rust、C/C、Java 等「先编译、再运行」的两步语言较麻烦。我没为它们单独设计机制而是用了两个简单有效的方法。第一招用 shell 的 把两步串成一条命令Rust 插件如此操作其运行命令展开为 rustc /path/to/main.rs -o /tmp/main /tmp/main。编译和运行通过 连成整体交给 shell。编译失败 短路编译器报错成为运行输出这正是「验证代码」场景中用户想看到的无需单独展示错误。运行后after_compile 配置 rm -f /tmp/main 清理产物。Windows 上则用 cmd /c rustc ... -o main.exe main.exe平台差异用 #[cfg(target_os)] 分支处理。第二招before_compile/after_compile 两个钩子Go 插件的 before_compile 是 go mod init temp 2/dev/null || true临时运行 Go 片段需先有 module此钩子为用户补上|| true 保证已初始化时不报错。运行命令用 go run $filename一步完成编译和运行。Java 直接用 java $filenameJava 11 起支持单文件源码模式一条命令可编译运行还配置 after_compile: rm -f *.class 清理残留字节码。可见编译型语言的「特殊性」最终收敛为配置里的几个字符串字段未渗入核心引擎。这套设计的最大红利新增语言常无需写代码因为「怎么跑一门语言」已完整编码进 PluginConfig命令模板 编译前后钩子 扩展名 超时所以新增语言很多时候无需写 Rust填一份配置即可。我将此能力开放给用户除内置插件外还有 CustomPlugin启动时从用户配置加载只要语言键不与内置冲突用户就能通过填配置为 CodeForge 添加新语言。配置驱动设计在此体现了最大价值。运行时的几个真实工程细节运行代码细节很关键。以下几点值得一提工具链探测用户机器不一定安装对应工具链如不会为运行 Haskell 特意安装 GHC。每个插件需提供探测方式get_version_args 提供版本参数Python 是 --version、Java 是 -version、Go 是 version 等get_path_command 给出定位工具的命令如 Python 用 import sys; print(sys.executable) 反查解释器路径。探测不到会明确提示「未检测到 xxx」而非让用户看到 command not found。在配置里设环境变量before_compile 不仅能运行准备命令还能写 export KEYvalueUnix或 set KEYvalueWindows插件会解析并跨平台设置环境变量还会展开 $PATH / %PATH%。这样需要特定环境变量的语言在配置里写一行即可解决无需改代码。多标签并发与流式输出每次运行请求带 task_id用于事件路由。Rust 端读到子进程输出后带 task_id 发事件前端按 task_id 将输出投递到对应标签页。多个标签可同时运行输出实时流式刷新互不干扰而非等进程结束一次性输出。「成功但没输出」也是一种结果运行成功但 stdout/stderr 为空时后执行钩子会填入 END - NO - OUTPUT 哨兵值。避免用户点击运行后界面空白分不清是「跑成功但无输出」还是「卡住了」哨兵值明确区分这两种状态。每语言可配的超时默认 30 秒可在配置中按语言调整避免死循环拖垮应用。顺手做的一件事让结构化数据「一目了然」运行代码常输出 JSON而压缩的 JSON 难读。所以我为 JSON / XML / YAML 做了两种可视化可折叠的「层级树」和字段关系卡片 连线的「关系图」。Markdown 会实时渲染预览内嵌 HTML 用 DOMPurify 净化防 XSS。还有个我喜欢的小功能识别到文件是 GitHub Actions 的 workflow 时自动渲染成「Jobs 依赖的 DAG 图」触发事件、各个 Job、依赖关系和 Job 的 Steps 一目了然比看 YAML 缩进方便。写在最后目前CodeForge 是我「最想天天用」的项目它解决了我日常遇到的问题。配置驱动的插件化前期看似「过度设计」但当语言数量从 5 种增至 30 多种时其价值凸显核心简洁添加语言变得轻松甚至可由用户自己完成。若你对实现感兴趣或想要本地多语言代码草稿本欢迎查看、点 star、提 issue[ **https://github.com/devlive-community/codeforge**](https://github.com/devlive-community/codeforge)。技术栈Vue 3 TypeScript CodeMirror 6前端、Rust Tauri 2后端、SQLite存储、配置驱动的插件化语言系统架构。你最希望它支持哪种语言、或想要什么功能评论区告诉我下一个版本可能会安排。