favicon here hometagsblogmicrobio cvtech cvgpg keys

Setting up Julia LSP for Helix

#setup #editor #helix #julia #lsp

Soc Virnyl Estela | 2023-05-19 | reading time: ~4min

The Julia programming language is a popular general programming language where the community leans more on scientific computing. And like most programming languages, Julia code can be written on most text editors such as IDEs.

Most editors today contain a set of tools such as git integration, built-in terminal, and a file picker and finder. Some editors that I tried are of the following:

Setting up Julia LSP for Helix§

Helix is a new editor I daily drive since a year ago. It follows a different style of editing than Vim/Neovim. It's model and keymaps are influenced from Kakoune which follows a select-action-execute model unlike the usual Vim action-select-execute model. I've been liking it so far!

As this post is about setting up LSP for Helix, setting up LSPs on other editors are pretty straight-forward e.g. Julia plugin on VSCode.

With helix, you have to write a simple configuration, specifically at languages.toml.

Writing the Julia LSP script for the LSP§

As helix does not have the option to pass the value of the current working directory of the file or buffer (maybe I am wrong, do correct me though!) unlike neovim's %:p:h, our script is like so:

import Pkg
project_path = let
    dirname(something(
        Base.load_path_expand((
            p = get(ENV, "JULIA_PROJECT", nothing);
            isnothing(p) ? nothing : isempty(p) ? nothing : p
        )),
        Base.current_project(pwd()),
        Pkg.Types.Context().env.project_file,
        Base.active_project(),
    ))
end

ls_install_path = joinpath(get(DEPOT_PATH, 1, joinpath(homedir(), ".julia")), "environments", "helix-lsp");
pushfirst!(LOAD_PATH, ls_install_path);
using LanguageServer;
popfirst!(LOAD_PATH);
depot_path = get(ENV, "JULIA_DEPOT_PATH", "")
symbol_server_path = joinpath(homedir(), ".cache", "helix", "julia_lsp_symbol_server")
mkpath(symbol_server_path)
server = LanguageServer.LanguageServerInstance(stdin, stdout, project_path, depot_path, nothing, symbol_server_path, true)
server.runlinter = true
run(server)

To understand what it's doing, the main focus of this script is actually the project_path variable. We first check if there is a JULIA_PROJECT environmental variable, explicitly set by the user. Base.load_path_expand achieves this. If there is none or if it is empty e.g. empty string, return nothing.

If the previous returns nothing, we use the Base.current_project(pwd()). And if it does not detect a Project.toml file from the current directory, we will fallback to Pkg.Types.Context().env.project_file and Base.active_project().

The project_path variable is very important as this will help us check which project you are in and where to run the LSP.

Julia's LSP is provided by LanguageServer.jl. But I don't like installing it on the global environment, hence, we have the ls_install_path variable. This ls_install_path variable assumes that you have installed the language server on one of the many DEPOT_PATH. Here, I assumed it has to be at ~/.julia/environments/helix-lsp. We need this variable because we need to add that path to the LOAD_PATH. You can check what LOAD_PATH is in the Julia REPL. Basically, it's just an array of paths where we load our environment for using and import statements. By default, it has a path to the global environment. This is why I have to add the popfirst! function since I don't want to use that one.

depot_path and symbol_server_path are optional but I like to make sure that JULIA_DEPOT_PATH exists.

Lastly, we then initialize the server by setting the LanguageServerInstance and setting the linter to true. We then run the run function with the server.

Adding it to language.toml§

Once we are done, we can finally either add it to a script file that can be executed within your PATH or just plain execute it like julia --project=@helix-lsp path/to/scriptfile.jl.

Here is a sample:

[[language]]
name = "julia"
scope = "source.julia"
injection-regex = "julia"
file-types = ["jl"]
roots = ["Project.toml", "Manifest.toml", "JuliaProject.toml"]
comment-token = "#"
language-server = { command = "julia", args = [
    "--project=@helix-lsp",
    "--sysimage=/home/uncomfy/.julia/environments/helix-lsp/languageserver.so",
    "--startup-file=no",
    "--history-file=no",
    "--quiet",
    "--sysimage-native-code=yes",
    "/home/uncomfy/.local/bin/julia-lsp.jl"
    ] }
indent = { tab-width = 4, unit = "    " }

It is up to you if you want to use PackageCompiler.jl to create a sysimage and make the LSP faster. Fortunately, you don't really need it since version 1.9.0 is very fast now.

There is runserver, why not use that?§

There are some gotchas with using runserver. If I can recall correctly, my issue was it cannot detect the correct paths inside helix and my script for neovim and kakoune is to detect where the file is located and if the location contains Project.toml or Manifest.toml and set it as the env_path. This can be seen with my kak-lsp julia config at line 168-170 on the default kak-lsp.toml

As you may noticed, it's not in the script we have discussed before this section. So I probably must have forgotten why I wrote that script only to end up that there is no way to check the path to the buffer or file. Therefore, I recommend to run helix within the root of your Julia project.

Let me know if you are not experiencing any issues with runserver since someone suggested that it works perfectly fine now. See https://github.com/helix-editor/helix/issues/669#issuecomment-1207489723

Articles from blogs I follow around the net

The everlasting now

Continuing the experiment. My first post in this series was manually crafted, but coding a static almost-a-site generator, without having to worry about all the interrelationships on the old site was quick, & in Rust terms at least, relatively easy. This i…

via Mike KreuzerMay 23, 2025

Web3: The new Scarlet Letter on your resume

A premise about my work experience I have been working as a Software Engineer for over 8 years. In my career I have worked in these sectors and for these amounts of time: Embedded systems on Passenger Information Systems and Access Control Systems: 4…

via Christian Visintin BlogMay 21, 2025

Making a custom porteur bag

I just finished my first fully custom sewing project: a porteur bag for bike trips. This is a bag designed to fit on a front rack, in my case a rack called Jack The Bike Rack. On multi-day trips, I want to bring a change of clothes, and need somewhere to p…

via macwright.comMay 18, 2025

Status update, May 2025

Hi! Today wlroots 0.19.0 has finally been released! Among the newly supported protocols, color-management-v1 lays the first stone of HDR support (backend and renderer bits are still being reviewed) and ext-image-copy-capture-v1 enhances the previous screen…

via emersionMay 14, 2025

The British Airways position on various border disputes

My spouse and I are on vacation in Japan, spending half our time seeing the sights and the other half working remotely and enjoying the experience of living in a different place for a while. To get here, we flew on British Airways from London to Tokyo, and…

via Drew DeVault's blogMay 05, 2025

Tech Companies Apparently Do Not Understand Why We Dislike AI

It’s becoming increasingly apparent that one of the reasons why tech companies are so enthusiastic about shoving AI into every product and service is that they fundamentally do not understand why people dislike AI. I will elaborate. I was recently made awa…

via Dhole MomentsMay 04, 2025

The Date that wasn't

A tale of lakes, dates and random results.

via Technically PersonalMay 03, 2025

Get Weird And Disappear

Pre-script: Reader and now close friend Phil Giammattei could use some help with a horrible brush with cancer in the family. You can support him here. Update: You all crushed Phil's goal, thank you so much for your generosity. Things are obviously Extremel…

via LudicityApril 29, 2025

Body::poll_progress

This describes a proposal for a cancelation problem with hyper’s request and response bodies. hyper is an HTTP library for the Rust language. Background: what is the Body trait? The Body trait used by hyper is meant to represent a potentially streaming (…

via seanmonstarApril 22, 2025

CVE-2025-32433 - State Machine Err-ly RCE in Erlang/OTP SSH Server

CVE-2025-32433 is a remote code execution vulnerability in the SSH server implementation within Erlang’s OTP libraries (affecting versions legendary CVSS score of 10.0 and became known as a vulnerability for which AI-assisted exploit development process wa…

via GreyNoise LabsApril 22, 2025

AI-powered search summaries led to less clicks to websites

Google claims that links beside AI summaries get more clicks. This goes against intuition. Ahrefs did some analysis on this recently. Who is right?

via Rob O'Leary | BlogApril 21, 2025

#Rx Writing Challenge 2025

This is a short reflection on my experience of the recent writing challenge I took part in. Over the past two weeks, I have participated in the #RxWritingChallenge 1—a daily, 30-minute writing group starting at 9 AM every morning. Surrounded by fellow doct…

via Ul-lingaApril 05, 2025

My coffee workflow

My coffee workflow by Clement Delafargue on April 1, 2025 Tagged as: coffee, espresso, flair58, v60. It is my first April cools’ and I guess I could start by talking about coffee. If you’ve seen me in person, it won’t be a surprise, I guess. This po…

via Clément Delafargue - RSS feedApril 01, 2025

LLDB's TypeSystems: An Unfinished Interface

Well, it's "done". TypeSystemRust has a (semi) working prototype for LLDB 19.x. It doesn't support expressions or MSVC targets (i.e. PDB debug info), and there are a whole host of catastrophic crashes, but it more or less proves what it needs to: Rust's de…

via Cracking the ShellMarch 28, 2025

Backup Yubikey Strategy

After a local security meetup where I presented about Webauthn, I had a really interesting chat with a member about a possible Yubikey management strategy. Normally when you purchase a yubikey it's recommended that you buy two of them - one primary and one…

via Firstyear's blog-a-logFebruary 28, 2025

The Adrian Dittmann Story

the evidence, from A to Z, and righting the wrongs

via maia blogJanuary 05, 2025

Awesome Fish functions

Some awesome fish functions that I have accumalated over the years.

via Ishan WritesJanuary 03, 2025

Physics Simulations in Bevy

Bevy is the most popular and powerful game engine in Rust. Because of its flexibility, it can be used not only for games but also for (scientific) physics simulations. In this blog post, I will share my experience using Bevy for physics simulations from sc…

via mo8it.comJuly 19, 2024

Generated by openring-rs

favicon here hometagsblogmicrobio cvtech cvgpg keys