Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>KhoraNew to Visual Studio Code? Get it now.
Khora

Khora

Khora Lang

|
2 installs
| (0) | Free
Language support for Khora (.kh): errors as you type, hover types, go to definition, completion, semantic highlighting, effect-row inlay hints, and format on save.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Khora for VS Code

Errors as you type, hover types, format on save, and syntax highlighting for .kh files.

Everything but the highlighting comes from the compiler. The extension starts khora lsp — a subcommand of the toolchain rather than a second program to install — and shows what it says. So a diagnostic in the editor is the same diagnostic khora check gives, from the same query, and there is no second implementation to drift.

Requires khora on PATH, which both installers arrange. Point khora.server.path at an executable to use a different one, which is what to do when working on the compiler itself.

What it does

from
Errors and warnings as you type khora_types::diagnostics and khora_lint::findings
Hover: the type of the thing under the cursor the checker's BodyTypes
Go to definition, references, rename khora_hir::resolve_path, and Body for locals
Completion the checker's types, and khora-types' signature keys
Semantic highlighting over the grammar the resolver, for what a regex cannot decide
Format on save khora_fmt, the same formatter the baseline gates on
Add a missing import, from the diagnostic that says it is missing khora_lsp::imports over what the modules export
Highlighting, comment toggling, bracket matching the TextMate grammar here

The corner of the window says which toolchain answered — Khora 0.1.0 — with the reason on hover. The pin is honoured whether or not this is shown: khora lsp hands over to the version a project's [toolchain] names, because the shim runs before argument parsing. What was missing was any way to see which one you got, which matters the moment there are two on the machine. The item turns yellow when a project pins a version that is not installed, since that is a project which will not build.

Nothing here decides which toolchain to use. khora toolchain which decides, and the extension shows its sentence — a second copy of that rule in JavaScript would be a second answer to disagree with the first.

Format on save is on by default for .kh and nothing else — package.json scopes it under [khora], so it cannot turn the setting on for a language somebody has deliberately left it off for.

Two commands, both under Khora: in the palette — Restart Language Server, for after rebuilding the compiler, and Show Language Server Output, which is where the server's own complaints go. khora.trace.server puts the protocol traffic in the same place.

Working on it

Open this folder (editors/vscode) in VS Code, run npm install once, and press F5. That launches an Extension Development Host — a second window with the extension loaded live. Reload the host window after editing src/.

Installing it

Download the .vsix from a release. The extension is released on its own tags — vscode-v0.3.0 and so on — separately from the toolchain, because it versions and changes for its own reasons.

GitHub marks those releases Pre-release, and that is not a warning about the extension. It means "this is not the repository's headline release", which is true: the headline release is the compiler. Errata 52 is what happens when it is not marked.

https://github.com/codyspate/khoralang/releases?q=vscode&expanded=true

code --install-extension khora-vscode-0.3.0.vsix

or in VS Code: Extensions, the ... menu, Install from VSIX.

Then fully quit and reopen VS Code — extensions are scanned at startup, so reloading the window is not enough. Confirm with code --list-extensions, which should list khora.khora. If code says Please restart VS Code before reinstalling, it means exactly that, and the install did not happen.

An installation from vscode-v0.3.0 or earlier is khora-lang.khora, which VS Code treats as a different extension. Remove it first, or both run: code --uninstall-extension khora-lang.khora.

It needs khora on PATH; see the toolchain releases, or set khora.server.path.

From this checkout

To install what is in the tree rather than what was released — which is what you want when working on the extension or on the compiler:

editors\vscode\install.ps1
cd editors/vscode && npm ci && npm run package
code --install-extension khora.khora.vsix --force

install.ps1 is the second of those with the mistakes taken out: it checks that the .vsix it built actually contains src/extension.js and vscode-languageclient, and it reads code's exit status instead of announcing success over a failed install.

That check is there because it used to build the zip by hand — a .vsix is an OPC package and PowerShell can write one without npm — and the hand-built one copied the manifest, the language configuration and the syntaxes, and not src/extension.js and not node_modules. It installed without complaint, lit up the keywords, and had no language server in it at all. Highlighting without diagnostics is the visible symptom; there was no error anywhere to find it by.

Do not just drop the folder into ~/.vscode/extensions. That was the first approach here and it silently did nothing: the extension never entered VS Code's extension index, so nothing loaded and no error appeared. A junction fails the same way.

Other editors

Syntax support is three separate layers, and this file is only the first:

Layer Reaches Nature
TextMate grammar (this) VS Code, Sublime, TextMate, IntelliJ, GitHub Linguist Regex; cannot be correct for Khora
Tree-sitter Neovim, Helix, Zed, Emacs 29+, GitHub code navigation Incremental parser, grammar in JS compiled to C
LSP semantic tokens Every editor with an LSP client Driven by the actual compiler

Semantic tokens over LSP is the real answer, and it is now here. It is editor-agnostic by construction and reuses the compiler instead of duplicating it, which is the only way a local can be told from an import. The grammar below is the base layer it sits on.

A tree-sitter grammar would mean maintaining a second parser alongside the rowan one. The cost of even a single duplicate is visible here: the keywords_match_the_lexer test exists to stop this grammar drifting from the lexer, and it has already caught a real break. Worth doing only if Neovim, Helix or Zed users are wanted before the language server ships.

The TextMate grammar earns its place regardless of the LSP, because GitHub Linguist uses it to highlight .kh in the repository and on the web.

What the grammar covers

  • Highlighting for keywords, types, functions, strings, numbers, row variables ('r) and operators, with |> scoped separately so the language's signature operator stands out.
  • Nested block comments, matching the lexer.
  • // and /* */ comment toggling, bracket matching, auto-closing pairs.

< and > are deliberately not auto-closing pairs. They are comparison operators as often as they are type brackets, and auto-closing them is more annoying than helpful.

Limits

This is a TextMate grammar: it pattern-matches text and knows nothing about scopes or types.

It used to be much worse. Under the old "universal dot" rule, Effect.map, report.risk and RiskLevel.Low were syntactically identical, and no regex could tell a module path from a field access from a constructor — the grammar could only approximate by capitalization. Splitting :: from . (errata 13) removed that limit: a path is now visibly a path, so modules, types, constructors and associated items color correctly, and a name after . is never mistaken for a type.

What a regex still cannot do is anything needing resolution — telling a local binding from an imported name, or a field from a method. Semantic tokens do that now, layered over this grammar the same way rust-analyzer layers over Rust's. This is the base layer: keywords, literals and punctuation, which the server deliberately does not send a second opinion about.

Keeping it in sync

The keyword list here is a copy of what the lexer accepts, which is exactly the kind of duplication that rots. crates/khora-syntax/tests/editor_grammar.rs fails the build if the two disagree, so adding a keyword to the compiler without updating this grammar is caught by cargo test.

There are two lists, and they live in separate repository rules:

  • #keywords mirrors KEYWORDS — the hard keywords, matched as bare words.
  • #contextual-keywords mirrors CONTEXTUAL_KEYWORDS — handler, in, context, test, bench, derive and extern, which are ordinary identifiers everywhere except one position each. for used to be on this list and is a hard keyword now, which is the sort of drift the sync test exists to catch. Matching them as bare words would color a parameter named handler or a variable named test, so each rule instead reproduces the position the parser recognizes: handler before for, context at the start of a declaration, test/bench before a name string. These approximations are exactly the kind of thing semantic tokens will replace.

There is a third list in the compiler, RESERVED_WORDS, and it deliberately has no rule here. Those words — where, yield, macro, unsafe, unstable — are refused as identifiers and mean nothing else; colouring one as a keyword would tell a reader there is a construct to look up, and there is not. reserved_words.rs asserts they appear in neither KEYWORDS nor CONTEXTUAL_KEYWORDS, which is what keeps this grammar honest about them.

The icon

icon.png is generated from the site's own website/public/favicon.svg:

npm install --no-save sharp && node scripts/make-icon.mjs

It is committed, because packaging should not need a native image library. Regenerate it when the site's mark changes; the script is the only place the two are connected, and the alternative is an extension quietly wearing an old logo.

The bar in the mark is lifted from #172033 to #334867 on the way. The favicon is read against the site's dark page, where a near-black upright is a quiet counterweight to the chevron; on a 128px gallery tile at that colour it disappears and the mark reads as a lone >.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft