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
2
3
4
fast syntax tree
syntax highlighting
folding / indent helper
structural completion hints

而不是完整语义分析。

终端 CLI 和编辑器 native 库要分开

PyStudio 里有两套 tree-sitter:

1
2
3
4
5
6
7
APK 内:
android-tree-sitter
tree-sitter-python native library
Sora Editor language binding

终端 prefix 内:
$PREFIX/bin/tree-sitter

终端里的 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
2
if ready:
|

按回车后应该自动缩进到代码块内部。如果没有缩进,第一反应可能是“LSP 没接上”。但这类行为大多数时候属于编辑器语言配置:

  • 冒号后换行增加缩进;
  • returnbreakpass 后可能减少缩进;
  • 括号、字符串、注释里的换行规则;
  • Tab/空格宽度;
  • 粘贴代码时是否保持缩进。

LSP 可以提供 formatting,但“按回车时缩进”最好由编辑器语言模块直接处理,这样离线、未安装 LSP、LSP 启动慢时也能稳定工作。

对 Python 来说,最低可用规则是:

1
2
3
上一行去掉尾部空白后以 ":" 结尾 -> 下一行增加一个 indent
上一行是空行或注释 -> 保持当前 indent
当前行输入 dedent 关键字 -> 调整到父级

Tree-sitter 可以让这些规则更准,但不应该等 LSP 才能工作。

真正的纠错和 Quick Fix 要靠 LSP

安装 pylsppyright-langserver 只是准备工作;编辑器必须完成启动、握手、同步和结果映射。
如果目标是 VS Code 那种体验,最终还是要引入 LSP:

1
2
3
4
5
Sora Editor
-> editor-lsp module
-> LSP client
-> python-lsp-server / basedpyright / pyright
-> diagnostics / completion / hover / code actions

PyStudio 的运行时清单里已经可以放 Python LSP 和 debug 工具包。安装后,App 侧需要做的是:

  1. 检查命令是否存在,比如 pylsppyright-langserverdebugpy
  2. 通过终端 prefix 启动 LSP server;
  3. 用 stdio 或 socket bridge 接入 editor-lsp;
  4. 将 diagnostics 映射到 Sora Editor 的错误标记;
  5. 将 completion items 映射到补全框;
  6. 将 code actions 映射成 Quick Fix 菜单;
  7. 在设置页提供开关和日志。

这里最需要避免的是“装了包就以为编辑器有语义能力”。安装只是第一步,编辑器还必须真的启动 server、握手、打开 document、同步修改、处理返回。

editor-lsp 为什么应该独立模块

我更倾向于把 editor-lsp 做成独立模块,而不是塞进编辑器页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
app
-> editor surface / settings / lifecycle

editor-lsp
-> LSP process bridge
-> JSON-RPC
-> diagnostics model
-> completion adapter
-> code action adapter

terminal-shared
-> command lookup
-> runtime prefix paths

好处是很明显的:

  • 可以单独调试 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
runtime package:
clang
clangd
lldb 或 gdb
make / cmake / ninja

editor:
tree-sitter-c
tree-sitter-cpp
clangd LSP bridge

run/debug:
save file
compile in terminal session
show exit code and elapsed time
optional debugger session

这里要注意 Android prefix 的 include path、sysroot、动态链接库路径。clangd 的诊断质量很依赖 compile_commands.json,所以项目型 C/C++ 支持最好引导用户生成编译数据库,而不是只对单文件硬猜。

怎么验收编辑器能力

Tree-sitter 验收:

1
2
3
4
5
打开 .py 文件
高亮稳定
编辑中不闪烁
代码块识别正常
不依赖终端 tree-sitter CLI

自动缩进验收:

1
2
if ready:
print("ok")

在冒号后回车,应自动进入缩进层级。

LSP 验收:

1
2
3
import not_exists

print(unknown_name)

应该看到 import/变量诊断;输入 str. 时应该有语义补全;Quick Fix 至少能展示来自 server 的 code actions。

调试验收:

1
print("hello")

点击运行后保存文件、切到终端、新会话运行脚本、显示退出码和耗时。

这条线最大的经验

Tree-sitter、CLI、LSP 是三层,最好从设计上就分开。编辑器高亮应该使用 APK native library,不要依赖终端里有没有 tree-sitter 命令;自动缩进也是编辑器语言体验的基础能力,不应该等 LSP 启动后才可用。

真正纠错、语义补全和 Quick Fix 要接 LSP server,并且要有可观察日志。运行时包管理只负责把 pylsppyright-langserverdebugpyclangd 这些命令安装好;编辑器还要负责启动、握手、同步文档和渲染结果。

做移动端代码编辑器,最怕把“包已经安装”当成“功能已经集成”。真正的体验是在编辑器进程、终端 prefix、native library、LSP server 之间,把每一条边都验证清楚。