GitHub

This extension adds Julia support to Zed, powered by JETLS language server.

Zed with JETLS completions and diagnostics

Quick links

Installation

  1. Install the latest version of Zed for your platform.
  2. Start Zed.
  3. Inside Zed, go to the extensions view by executing the zed: extensions command (click Zed->Extensions).
  4. In the extensions view, simply search for the term julia in the search box, then select the extension named Julia and 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 julia to JETLS. Existing LanguageServer.jl settings under lsp.julia are not migrated or forwarded to JETLS; configure the new server under lsp.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.

  1. Open a .jl file in the editor.

  2. 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 press ctrl-shift-` as defined in the json example below.

  3. In the terminal, start the REPL with julia --project.

  4. 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/12 combinations to your liking.

    Note: interacting with the terminal requires to send keystrokes. In the examples, cmd-v is used to paste code. Please adjust this binding for your operating system.

    ~/.config/zed/keymap.json (can be opened via zed: 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

Read the original on github.com ↗