Sora Editor 上做代码高亮和纠错:Tree-sitter、LSP 与终端包的边界
PyStudio 编辑器这条线最容易混淆三个东西:APK 里给 Sora Editor 用的 Tree-sitter native library、终端里用户能运行的 tree-sitter CLI,以及真正做诊断、补全、Quick Fix 的 LSP server。
它们都和“代码智能”有关,但不是一层能力。终端里 tree-sitter --version 能跑,不代表编辑器高亮生效;Tree-sitter 接好了,也不代表会有语义纠错。把这几条边界画清楚,后面才知道该修 APK native、运行时包,还是 editor-lsp。
这张图先把三层拆开:结构解析、终端工具、语义服务分别解决不同问题。
Tree-sitter 解决的是结构,不是语义
Tree-sitter 很适合做增量解析和结构化高亮。对编辑器来说,它能回答:
- 这是关键字还是字符串;
- 当前光标在函数、参数、代码块还是注释里;
- 改了一行后如何增量更新语法树;
- 是否能基于语法节点做缩进和简单补全。
但它通常不能单独回答:
- 这个变量有没有定义;
- 这个函数参数类型是否正确;
- 这个 import 是否缺包;
- 如何生成 Quick Fix;
- 跨文件符号引用是什么。
所以在 PyStudio 里,Tree-sitter 的目标应该是:
1 | fast syntax tree |
而不是完整语义分析。
终端 CLI 和编辑器 native 库要分开
PyStudio 里有两套 tree-sitter:
1 | APK 内: |
终端里的 tree-sitter CLI 是给用户和调试用的,它能证明包安装到了 prefix:
1 | tree-sitter --version |
但编辑器高亮不应该依赖终端 CLI。编辑器运行在 Android App 进程里,要加载 APK 自带的 native library,尤其包名、ABI、System.loadLibrary 路径都要匹配。
这也是我踩过的坑之一:早期 Android native 库不是适配当前包名的版本,终端 CLI 即使可用,编辑器里也不会自动生效。后来路线改成:编辑器 Tree-sitter 使用 APK-bundled native libraries,终端 tree-sitter 只是 optional package。
自动缩进也不是 LSP 的锅
用户很容易用一个例子判断“代码智能没生效”:
1 | if ready: |
按回车后应该自动缩进到代码块内部。如果没有缩进,第一反应可能是“LSP 没接上”。但这类行为大多数时候属于编辑器语言配置:
- 冒号后换行增加缩进;
return、break、pass后可能减少缩进;- 括号、字符串、注释里的换行规则;
- Tab/空格宽度;
- 粘贴代码时是否保持缩进。
LSP 可以提供 formatting,但“按回车时缩进”最好由编辑器语言模块直接处理,这样离线、未安装 LSP、LSP 启动慢时也能稳定工作。
对 Python 来说,最低可用规则是:
1 | 上一行去掉尾部空白后以 ":" 结尾 -> 下一行增加一个 indent |
Tree-sitter 可以让这些规则更准,但不应该等 LSP 才能工作。
真正的纠错和 Quick Fix 要靠 LSP
安装
pylsp或pyright-langserver只是准备工作;编辑器必须完成启动、握手、同步和结果映射。
如果目标是 VS Code 那种体验,最终还是要引入 LSP:
1 | Sora Editor |
PyStudio 的运行时清单里已经可以放 Python LSP 和 debug 工具包。安装后,App 侧需要做的是:
- 检查命令是否存在,比如
pylsp、pyright-langserver、debugpy; - 通过终端 prefix 启动 LSP server;
- 用 stdio 或 socket bridge 接入 editor-lsp;
- 将 diagnostics 映射到 Sora Editor 的错误标记;
- 将 completion items 映射到补全框;
- 将 code actions 映射成 Quick Fix 菜单;
- 在设置页提供开关和日志。
这里最需要避免的是“装了包就以为编辑器有语义能力”。安装只是第一步,编辑器还必须真的启动 server、握手、打开 document、同步修改、处理返回。
editor-lsp 为什么应该独立模块
我更倾向于把 editor-lsp 做成独立模块,而不是塞进编辑器页面:
1 | app |
好处是很明显的:
- 可以单独调试 LSP 握手;
- Python、C/C++、JavaScript 后续可以复用桥接层;
- 终端包管理和编辑器 UI 不互相污染;
- Sora Editor 升级时,只需要适配 editor surface,不要重写整个 LSP。
移动端做 LSP 不一定要追求一开始就“全 VS Code 体验”。先把 diagnostics、completion、hover 做稳,再做 Quick Fix 和 formatting,会更现实。
C/C++ 的扩展路线
Python 跑通后,C/C++ 的路线也类似:
1 | runtime package: |
这里要注意 Android prefix 的 include path、sysroot、动态链接库路径。clangd 的诊断质量很依赖 compile_commands.json,所以项目型 C/C++ 支持最好引导用户生成编译数据库,而不是只对单文件硬猜。
怎么验收编辑器能力
Tree-sitter 验收:
1 | 打开 .py 文件 |
自动缩进验收:
1 | if ready: |
在冒号后回车,应自动进入缩进层级。
LSP 验收:
1 | import not_exists |
应该看到 import/变量诊断;输入 str. 时应该有语义补全;Quick Fix 至少能展示来自 server 的 code actions。
调试验收:
1 | print("hello") |
点击运行后保存文件、切到终端、新会话运行脚本、显示退出码和耗时。
这条线最大的经验
Tree-sitter、CLI、LSP 是三层,最好从设计上就分开。编辑器高亮应该使用 APK native library,不要依赖终端里有没有 tree-sitter 命令;自动缩进也是编辑器语言体验的基础能力,不应该等 LSP 启动后才可用。
真正纠错、语义补全和 Quick Fix 要接 LSP server,并且要有可观察日志。运行时包管理只负责把 pylsp、pyright-langserver、debugpy、clangd 这些命令安装好;编辑器还要负责启动、握手、同步文档和渲染结果。
做移动端代码编辑器,最怕把“包已经安装”当成“功能已经集成”。真正的体验是在编辑器进程、终端 prefix、native library、LSP server 之间,把每一条边都验证清楚。