This extension adds Julia support to Zed, powered by JETLS language server.
Quick links
- Installation
- Julia executable
- Language server
- Built-in tasks
- Running code in the REPL
- Plot side pane
- Using Zed from the Julia REPL
- Customizing syntax highlighting
- Contributing
Installation
- Install the latest version of Zed for your platform.
- Start Zed.
- Inside Zed, go to the extensions view by executing the
zed: extensionscommand (click Zed->Extensions). - In the extensions view, simply search for the term
juliain the search box, then select the extension namedJuliaand click the install button.
Julia executable
By default, JETLS and the built-in Julia tasks
resolve julia from the worktree environment's PATH. No configuration is
required if the desired Julia executable is already available there.
To select Julia per project, use an approved direnv file:
.envrc(project root)
JULIA_HOME="/path/to/julia" PATH_add "$JULIA_HOME/bin"
Run direnv allow after creating or modifying the file. This changes the Julia
executable used by both JETLS and the built-in tasks.
Language server
Important
Version 0.2 is a breaking migration from LanguageServer.jl to JETLS. See Migrating to version 0.2 for the new requirements and configuration changes.
Warning
JETLS is a new language server and still experimental. Notably, it currently has a known memory leak issue where memory usage grows with each re-analysis (see the announcement for details and a workaround). If the language server causes problems, you can disable it and keep using the rest of the extension.
Julia for JETLS
JETLS requires Julia 1.12.2 through 1.13.
To override only the Julia executable used by managed JETLS without changing the
worktree PATH, set the standard Pkg app runtime override:
~/.config/zed/settings.json(global) or.zed/settings.json(per-project)
The selected Julia executable is used to install and update JETLS, verify the installation, and launch the server.
Each executable and Julia minor version (e.g. 1.12 vs 1.13) combination uses a separate managed depot, so worktrees can select different Julia installations without repeatedly reinstalling JETLS, and Julia patch releases reuse the existing depot.
Automatic installation and updates
Using the selected Julia executable, the extension automatically installs a pinned JETLS release as a Julia Pkg app the first time it is needed. Network access is required when JETLS is installed or updated.
JETLS is installed into a depot private to the extension, so this does not
create or modify a user-global ~/.julia/bin/jetls. Each zed-julia release
pins an exact JETLS release. When that pin changes, Zed's normal extension
auto-update causes the managed JETLS installation to update as well. Once the
pinned release is installed, verifying and launching the managed JETLS
installation does not require network access. JETLS may still access the network
to instantiate a workspace package environment unless
full_analysis.auto_instantiate is disabled.
The initial installation may take several minutes while Julia installs and precompiles JETLS.
Tip
The managed depots live in the jetls-depots directory inside Zed's work
directory for this extension (on macOS, for example,
~/Library/Application Support/Zed/extensions/work/julia/jetls-depots).
After an update, the extension garbage-collects previous JETLS versions from
the depot. It is always safe to delete jetls-depots or individual depots
inside it to reclaim disk space: the pinned JETLS release is reinstalled
automatically the next time the language server starts.
Launch configuration
JETLS runs with --threads=auto by default. You can customize how it is
launched in the lsp.JETLS section of Zed settings.
Managed JETLS arguments
When binary.path is omitted, the extension uses its managed JETLS installation.
Custom binary.arguments must include exactly one
serve subcommand of
JETLS. To specify a different --threads option:
~/.config/zed/settings.json(global) or.zed/settings.json(per-project)
binary.arguments can be combined with
binary.env.JULIA_APPS_JULIA_CMD to use custom arguments
with a specific Julia executable:
~/.config/zed/settings.json(global) or.zed/settings.json(per-project)
Custom JETLS command
To launch a local JETLS checkout, a wrapper script, or another custom command,
set binary.path to a command that starts a compatible language server over
standard input and output.
Zed accepts absolute paths, ~-relative paths, worktree-relative executables
such as tools/start-jetls, and bare commands such as julia.
Worktree-relative paths are resolved against the worktree root; bare commands
that are not worktree entries are resolved through the worktree PATH.
Scripts must be directly executable; otherwise, set binary.path to their
interpreter and include the script path in binary.arguments.
Note
Setting binary.path bypasses the extension's managed installation and
version preflight. Install custom commands yourself, and complete any required
compilation before starting the language server to avoid the LSP
initialization timeout.
For example, to launch a local JETLS checkout with Julia from the worktree
PATH:
~/.config/zed/settings.json(global) or.zed/settings.json(per-project)
Server configuration
JETLS has dynamic configuration, which can change throughout the server's lifetime,
and static initialization options, which are set once at server startup.
Both can be set in a project-local .JETLSConfig.toml or in Zed settings.
Dynamic configuration changes are applied to the running server automatically
with either method, while changes to initialization options require restarting
the language server.
Method 1: Project-specific configuration file
This method uses JETLS's native
.JETLSConfig.toml configuration file
and is therefore editor-agnostic: the same file configures JETLS in any editor,
as well as its CLI (e.g. jetls check). Create a configuration file, e.g.:
.JETLSConfig.toml(project root)
# Use JuliaFormatter instead of Runic formatter = "JuliaFormatter" # Prevent JETLS from automatically instantiating the package environment [full_analysis] auto_instantiate = false # Suppress unused argument warnings [[diagnostic.patterns]] pattern = "lowering/unused-argument" match_by = "code" match_type = "literal" severity = "off" # Reuse Julia's native inference cache for faster full analysis [initialization_options] reuse_native_inference = true
Method 2: Zed settings
This method uses JETLS's
LSP-based configuration
mechanism, which Zed supports natively: when you change initialization_options
and save settings.json, Zed automatically restarts the server to apply them.
Configure initialization options and server settings under the lsp.JETLS section:
~/.config/zed/settings.json(global) or.zed/settings.json(per-project)
Note
.JETLSConfig.toml takes precedence over editor settings when both are present.
For complete configuration details, see the JETLS documentation for configuration and initialization options.
Formatting
JETLS delegates formatting to an external formatter executable. Install the
formatter you want to use as a Julia Pkg app and ensure that its executable is
available on PATH. Julia Pkg apps are normally installed into ~/.julia/bin.
Runic
Runic is the default formatter:
julia -e 'using Pkg; Pkg.Apps.add("Runic")'JETLS invokes the runic executable for document and range formatting.
JuliaFormatter
To use JuliaFormatter instead,
install its jlfmt executable:
julia -e 'using Pkg; Pkg.Apps.add("JuliaFormatter")'Then set formatter = "JuliaFormatter" using either configuration method
above. Range formatting requires JuliaFormatter v2.7.0 or later.
For custom formatter executables and further details, see the JETLS formatter documentation.
Disabling the language server
If JETLS causes problems, you can disable it while keeping the rest of the extension working: syntax highlighting, built-in tasks, REPL integration, and so on. Add the following setting:
~/.config/zed/settings.json(global) or.zed/settings.json(per-project)
Remove the setting to re-enable JETLS.
Migrating to version 0.2
Zed Julia extension v0.2 replaces the LanguageServer.jl backend with JETLS.jl. This is a breaking migration for existing users:
- JETLS requires Julia 1.12.2 through 1.13. See Julia for JETLS for runtime selection.
- The language-server identifier has changed from
juliatoJETLS. Existing LanguageServer.jl settings underlsp.juliaare not migrated or forwarded to JETLS; configure the new server underlsp.JETLS. - The extension now installs and updates a pinned JETLS Julia Pkg app in an extension-private depot. The LanguageServer.jl environment previously used by the extension is no longer used.
- JETLS uses Runic as its default formatter. See Formatting for installation instructions and the JuliaFormatter alternative.
If JETLS is not suitable for your current setup, you can disable the language server while continuing to use syntax highlighting, built-in tasks, and the other extension features.
Built-in tasks
The extension provides the following built-in tasks:
| Task | Operation |
|---|---|
Julia: Pkg.jl instantiate |
Pkg.instantiate() |
Julia: Pkg.jl precompile |
Pkg.precompile() |
Julia: Pkg.jl update |
Pkg.update() |
Julia: Pkg.jl resolve |
Pkg.resolve() |
Julia: Pkg.jl test |
Pkg.test() |
Open the command palette, run task: spawn
(Cmd+Shift+R on macOS or Alt+Shift+T on Linux and Windows),
and select a task. Each task uses the julia command from the worktree environment
and activates the project at ZED_WORKTREE_ROOT.
Note that the Pkg.jl operations may modify the project environment.
The task terminal is hidden automatically when the command succeeds.
Running code in the REPL
This section describes how to select Julia code in the editor and run it in Zed's integrated terminal. This is more of a workaround than a full integration. Currently, there is no inline code execution as in VSCode. On the other hand, the Language Server is not required to make this work.
-
Open a .jl file in the editor.
-
From the Command Palette, run
open in terminal. This opens a new terminal in the worktree root (where the Project.toml lives). You can also right-click in the editor and use the context menu or pressctrl-shift-`as defined in the json example below. -
In the terminal, start the REPL with
julia --project. -
Now it's time to select some code in the editor, copy it to the clipboard, paste it into the terminal, execute it, and go back to the editor. To make that less tedious, add one or more of the following key bindings. Change the
ctrl-shift-f10/11/12combinations to your liking.Note: interacting with the terminal requires to send keystrokes. In the examples,
cmd-vis used to paste code. Please adjust this binding for your operating system.~/.config/zed/keymap.json(can be opened viazed: open keymap file)
Plot side pane
For plot support in Zed, we recommend using ZedPlotPane.jl.
To install it, run the following in your Julia REPL:
using Pkg Pkg.add("ZedPlotPane")
To enable the plot pane, load ZedPlotPane (using ZedPlotPane) in your Julia REPL before loading Plots or any other plotting package.
The first plot will create and open ~/.cache/zed-julia/current-plot.png. Drag that tab into a side pane; subsequent plots will update there automatically.
If you close the plot pane, you can re-open it by running ZedPlotPane._open_viewer() in the REPL.
For more information on how to use it, please refer to the ZedPlotPane.jl documentation.
Using Zed from the Julia REPL
Zed is currently not on the list of Julia's predefined editors. You can register it in your Julia startup file:
~/.julia/config/startup.jl
atreplinit() do repl InteractiveUtils.define_editor("zed") do cmd, path, line, column `$cmd $path:$line:$column` end end
Set the environment variable EDITOR (or VISUAL or JULIA_EDITOR, whatever
you use) to zed --wait. Then, using InteractiveUtils.edit etc. will open
the document in Zed.
Customizing syntax highlighting
You can change the foreground color and text attributes of syntax tokens, for instance:
~/.config/zed/settings.json
{
"theme_overrides": {
"One Dark": {
"syntax": {
"comment.doc": {
"font_style": "italic",
},
"function.definition": {
"color": "#0000AA",
"font_weight": 700,
},
},
},
},
}See Syntax Highlighting and Themes and Tree-sitter Queries for further details.
Syntax tokens are called captures in tree-sitter jargon. The following table lists all captures provided by zed-julia. Some captures have default values (defined in Zed's color themes) and the other captures fall back to one of the defaults. Depending on your color theme, some captures may be set to the editor's foreground color or to a very similar one. In this case, try to assign a different color to improve the contrast.
| Capture | Is there a default value? | Note/Example |
|---|---|---|
| boolean | yes | |
| comment | yes | line or block comment |
| comment.doc | yes | docstring |
| constant | yes | |
| constant.builtin | no, falls back to constant | core julia built-in |
| function.builtin | no, falls back to function | core julia built-in |
| function.call | no, falls back to function | name of the called function |
| function.definition | no, falls back to function | name of the defined function |
| function.macro | no, falls back to function | name of the macro |
| keyword | yes | |
| keyword.conditional | no, falls back to keyword | if, else |
| keyword.conditional.ternary | no, falls back to keyword | ? : |
| keyword.exception | no, falls back to keyword | try, catch |
| keyword.function | no, falls back to keyword | function, do, short function definition: = |
| keyword.import | no, falls back to keyword | im/export, using, module definition |
| keyword.operator | no, falls back to keyword | in, isa, where |
| keyword.repeat | no, falls back to keyword | for, while |
| keyword.return | no, falls back to keyword | return |
| keyword.type | no, falls back to keyword | struct or type definition |
| label | yes | label name for @label, @goto |
| number | yes | |
| number.float | no, falls back to number | |
| operator | yes | |
| punctuation.bracket | yes | (), [], {} |
| punctuation.delimiter | yes | ,, ;, :: |
| punctuation.special | yes | ., ..., string interpolation $ |
| string | yes | |
| string.escape | yes | escape sequence |
| string.special | yes | command literal |
| string.special.symbol | yes | quote expression |
| type | yes | |
| type.builtin | no, falls back to type | core julia built-in |
| type.definition | no, falls back to type | |
| variable | yes | |
| variable.builtin | no, falls back to variable | core julia built-in: begin and end in indices |
| variable.member | no, falls back to variable | example: in foo.bar, the member is bar |
