// 本文件提到的 sokonanoda — learn theorem proving in a Lean-4-style languageZero setup. Install the extension, open a Part of the sokonanoda-lang suite: a self-contained teaching compiler + language server + agent skills. What you getProof checking that doesn't lie
Guided exercises
A real editing experience
Typing notation (
|
| you type | you get | aliases | you type | you get | aliases | |
|---|---|---|---|---|---|---|
\and |
∧ |
\an \wedge |
\powerset |
𝒫 |
\McP |
|
\or |
∨ |
\vee |
\compl |
ᶜ |
\complement |
|
\iff |
↔ |
\leftrightarrow \lr |
\preim |
⁻¹' |
\preimage |
|
\not |
¬ |
\neg \lnot |
\xs |
×ˢ |
— | |
\to |
→ |
\imp \rightarrow |
\approx |
≈ |
\thickapprox |
|
\forall |
∀ |
\all |
\comp |
∘ |
\circ |
|
\exists |
∃ |
\ex |
\inv |
⁻¹ |
\sy |
|
\ne |
≠ |
\neq \eqn |
\smul |
• |
\bub \bu |
|
\in |
∈ |
\member \mem |
\oplus |
⊕ |
— | |
\sub |
⊆ |
\ss \subseteqq \subseteq \subset |
\sUnion |
⋃₀ |
— | |
\cup |
∪ |
\union \un |
\sInter |
⋂₀ |
— | |
\cap |
∩ |
\inter \intersection |
\langle |
⟨ |
\< |
|
\setminus |
\ |
— | \rangle |
⟩ |
\> |
|
\empty |
∅ |
\emptyset \varnothing |
Greek letters — the spelled-out name, plus the short keys Lean 4 has (letter rows read lower case · upper case):
| lower | upper | lower | upper | |||
|---|---|---|---|---|---|---|
| alpha | \alpha \a |
\Alpha |
nu | \nu |
\Nu |
|
| beta | \beta \b \be |
\Beta |
xi | \xi |
\Xi |
|
| gamma | \gamma \g \ga |
\Gamma \G |
omicron | \omicron |
\Omicron |
|
| delta | \delta \de |
\Delta \D |
pi | \pi |
\Pi \p \P |
|
| epsilon | \epsilon \e \ep \eps |
\Epsilon |
rho | \rho \rh |
\Rho |
|
| zeta | \zeta \ze |
\Zeta |
sigma | \sigma \si |
\Sigma \S |
|
| eta | \eta \et |
\Eta |
tau | \tau \ta |
\Tau |
|
| theta | \theta \th |
\Theta |
upsilon | \upsilon |
\Upsilon |
|
| iota | \iota \io |
\Iota |
phi | \phi \ph \straightphi |
\Phi |
|
| kappa | \kappa \ka |
\Kappa |
chi | \chi \c \ch |
\Chi |
|
| lambda | \lambda \la \lamda \lam \fun |
\Lambda \L \Lamda |
psi | \psi \ps |
\Psi |
|
| mu | \mu \m |
\Mu \GM |
omega | \omega \om |
\Omega |
The keys are copied key-for-key from Lean 4, so the muscle memory transfers —
with one documented exception: Lean 4 has no \Mu (its capital mu is \GM only), so
this table accepts both \GM (Lean's key) and \Mu (the spelled-out name the
other 23 capitals use);
hovering a symbol shows the same information (∈ → "输入:\in(别名 \mem)"),
and so does hovering a Greek-letter variable (α → "输入:\alpha(别名 \a)").
Ordinary typing is never touched: a lone \ (the set-difference symbol) is never
rewritten, indentation and suggestion acceptance keep working, and a word that is
still a prefix of a longer abbreviation waits instead of guessing.
A short key like \a is a prefix of \alpha / \approx / \and, so it
waits while you keep typing and lands once the word is closed — that is Lean's
own rule, and it is why the short keys are unambiguous. Lean's single-letter keys
that point at logic symbols (\v → ∨, \i → ∩, \o → ∘, \r → →) are
deliberately not taken: \i giving ∩ while \in gives ∈ would be a trap.
.sokonanoda files do not auto-close brackets (editor.autoClosingBrackets: "never" for this language): the automatic ) collides with the automatic notation
replacement — VS Code forgets which ) it inserted once the extension edits the
line, so the ) you type is no longer skipped over and the line ends up with an
extra bracket (visible in a whole proof: exact h))). Type the closing bracket
yourself, or set "[sokonanoda]": { "editor.autoClosingBrackets": "languageDefined" }
if you prefer the auto-close and never edit inside a pair.
Two more behaviours worth knowing:
- How a word lands (Lean 4's rule): a separator — Space, punctuation —
closes the word and finishes it (
\in→∈,\sub→⊆,\a→α), and a word that is no longer a prefix of any longer key lands immediately (\alpha→α). While you keep typing letters a prefix waits (\anwaits for\and,\inwaits for\inter,\awaits for\alpha). Turningsokonanoda.input.eageroff disables the automatic replacement (thenTab, which the extension also binds while a\-word is being typed, is the only way to force one). - Pasting never rewrites — a replacement only fires for a single character
typed at the caret, so text you paste (even a full
\alpha) stays exactly as pasted. Press Space orTabafter it if you want it turned into a symbol. - Undo — a replacement is a single edit, so one undo takes it back in one step; with several cursors, each abbreviation is rewritten in that same single edit.
Vim users (VSCodeVim)
\ is also VSCodeVim's leader (vim.leader also defaults to \), and vim
takes the key first in NORMAL mode — so a file that opens in NORMAL mode swallows
\alpha as a vim command before the extension ever sees it. Change both
settings together (that is the same escape hatch Lean 4 offers as
lean4.input.leader):
{ "sokonanoda.input.leader": ",", "vim.leader": "," }
Then ,alpha + Space → α — the leader is honoured everywhere (the word before
the cursor, the Tab keybinding's when clause, and the "pending word" highlight).
Also press i before typing: NORMAL mode never inserts text, whatever the leader
is. An empty sokonanoda.input.leader falls back to \.
Two vim-specific traps are handled for you:
- The caret stays right after the symbol. VSCodeVim re-applies its own caret
after an extension edit; that used to push the caret past an auto-closed
), so every following keystroke landed one character too far right ((\alpha+ Space +xcame out as(α) x). The extension now writes the caret itself after a replacement — the same workaround Lean 4's client uses for vim (updateUnchangedSelections). - Vim's own edits are not "typing". Only a single character inserted at the
caret can trigger a replacement, so normal-mode
o/p/P/.no longer rewrite a pending abbreviation on a neighbouring line.
Install & use
- Install this extension from the Marketplace — VS Code picks the package with the server bundled for your platform.
- Open any
.sokonanodafile (or theplayground.sokonanodathat ships in the repo — 12 exercises, zero to theorem). - That's it. The server binary lives in
bin/<platform>/inside the extension; the client and its bundled kernel are built from the same release, so there is no version drift.
The server is discovered bundled-first by default:
- the bundled
bin/<target>/sokonanoda-lsp[.exe](repairs a lost executable bit automatically) — the client and its kernel are built from the same release, so there is no version drift; - the version-pinned download cache — only used by the fallback package
for platforms with no bundled build (e.g. Linux armhf), and always
pinned to this extension's own release tag, never
latest.
sokonanoda.serverPath / SOKONANODA_LSP_BIN and workspace
target/debug|release builds are ignored unless you opt in with the
sokonanoda.serverOverride setting (default false). This prevents a stale
local build (a common cause of "the server is still 0.26.0" surprises) from
silently overriding the bundled server. Use Sokonanoda: Doctor (诊断服务器与版本) any time
to see which server is in use, its source (bundled / override / cache), the
running vs extension version, and other version-skew issues.
No Lean toolchain. The kernel ships inside the server; on platforms with a bundled package everything works fully offline.
Teaching with AI agents
The sokonanoda-lang repo ships three agent skills that any skill-aware agent (Claude Code, opencode, …) can load:
| Skill | Turns an agent into… |
|---|---|
sokonanoda-teacher |
Your proof teacher: assigns exercises from the canvas, grades via kernel --json events, walks the hint ladder with you |
sokonanoda-dev |
A careful contributor to the compiler stack (frozen kernel, TDD discipline) |
sokonanoda-ci |
A disciplined releaser (CI verification, release pipeline) |
opencode's startup plugin (.opencode/plugins/sokonanoda.ts) already wires the
language server for .sokonanoda files, so opening this repo in opencode gives
you kernel-checked diagnostics in the agent's own editor loop. The same
--json event stream (sokonanoda --json file, sokonanoda watch)
is the integration surface for any tooling you want to build.
The same grading verdict is also available as one JSON object:
sokonanoda query <op> (check/state/goals/holes/hints/reduce)
answers a single question for agents and scripts — query state answers
"what is still missing at this position" without scanning the whole event
stream. scripts/soko query … is the harness-neutral form of the same
command.
Build and rebuild the compile cache
Sokonanoda: Build (编译项目,预热缓存)(alt+b)在大项目上是分钟级:它编整个项目, 每个文件各编一遍自己那一份 import 闭包(文件之间不共享)。实测courses/set-theory(35 个文件)约 2.5 分钟(debug CLI、冷热都一样—— 冷热差异取决于项目缓存有没有命中,见docs/PERF.md)。只想快速看一个文件时 直接打开它即可,不必先build(要只编一个文件用 CLI:sokonanoda build <file.sokonanoda>)。
The compiler keeps a persistent compile cache (.sokonanoda → compiled
report), so the second run of a file — and the first keystroke in a project —
are hits instead of full recompiles. Two commands drive it from the editor:
Sokonanoda: Build (编译项目,预热缓存)(alt+b) — compile the project: the module root the language server reports for the active document (soko/project→root), or the first workspace folder while the server has not answered yet. It is never "just the file you have open" — the CLI compiles exactly what you hand it, so passing the file made a 35-file course report1 个文件. The result line reportsfiles · compiled · hit · failed. It shows progress while it runs (0.73.0): the CLI's per-file event stream drives the status bar ($(sync~spin) Sokonanoda: 3/13 文件 · lib/Set.sokonanoda), the Infoview's three-line progress block and the overview ruler, and a VS Code progress notification carries a cancel button — cancelling kills the CLI subprocess.Sokonanoda: Rebuild (清空编译缓存后重编译)(alt+shift+b) — the same, but first runssokonanoda rebuild <module root>(0.85.0: the CLI's ownrebuildsubcommand, one process — it equalsclean+build, whereclean≡build --clean) to drop the cache, i.e. "recompile everything from scratch" (it clears both the global cache and the module root's.sokonanoda/). Project artifacts live in<module root>/.sokonanoda/— a self-ignoring.gitignore(one line,*) keeps them out of your repository, so there is nothing to configure; add.sokonanoda/to your own.gitignoreonly if you want it visible there too (committing the artifacts needsgit add -f) — and the language server reads that directory first, sobuildwarms exactly what the editor then opens. Single-file entries still live in the global cache.Sokonanoda: Clean Cache (清除编译缓存)— clear the cache and stop: no compile follows. It runsbuild --clean <module root>, which clears both the global cache and the module root's.sokonanoda/compiled/, and reports the three numbers from the CLI'sbuild.cleanevent (清掉 N 条缓存(全局 X · 项目 Y)). Use it when you want the next compile to start from scratch but do not want to pay for a rebuild right now.SOKONANODA_NO_PROJECT_ARTIFACTS=1opts back into the global-only behaviour.
Both write the CLI's JSON Lines events (build.file / build.clean /
build.summary) to the sokonanoda build output channel, refresh the
exercise/project/course views afterwards (a warm cache changes what they show),
and warn — never fail silently — when a file does not compile.
build/rebuild time out after sokonanoda.build.timeoutMs (default 300000 ms;
0 = no limit) — a cold build of a whole course can exceed the default, and
the timeout message names the setting so you know where to change it.
Sokonanoda: Install Command Line (安装命令行)— copy the CLI that ships inside this extension to~/.local/share/sokonanoda/bin/, so you get asokonanodacommand without downloading anything (no network, no mirror, nocargo). Because it installs the bundled binary, its version is the extension's version by construction — and the command verifies that by running--versionand comparing the answer, so a stale build is reported instead of silently installed. On an extension upgrade, a previously installed standalone copy is upgraded automatically from the new VSIX (without one ever having been installed, nothing is installed for you); the PATH line is recognized whether the extension wrote it or you added it yourself.
Warming on open — set sokonanoda.warmCacheOnOpen to true and the
extension runs one build over the workspace root in the background when
the window activates, so the first unit you open is already a cache hit.
Off by default: it costs CPU/IO, and "opening the editor" itself gets
slower — the other side of the same complaint. It never steals focus (progress
goes to the sokonanoda build channel) and never errors (a failed warm-up just
means nothing was pre-built; Sokonanoda: Doctor (诊断服务器与版本) tells you why).
The course map
The 「课程」tree shells out to the sokonanoda CLI (it aggregates all course
units — the language server itself tracks several documents but answers one
project closure at a time). The CLI ships in the same platform package as the
server, so it works out of the box; in a development host without a staged
bin/, a workspace target/{debug,release} build is used instead.
Requirements
- VS Code 1.106+ (the Infoview uses an extension-contributed secondary-side-bar container)
- No network needed on platforms with a bundled package; the fallback
package for other platforms downloads once (or use
sokonanoda.serverPathto point at a local build)
Links
- Repository & docs: github.com/ColorlessBoy/sokonanoda-lang
- Release notes: CHANGELOG
License
Apache-2.0