Language server - missing references

Hello!
I’m trying to use the Helix editor with the Julia LanguageServer. The call to the language server looks like:

julia --startup-file=yes --history-file=no --quiet -e using LanguageServer; runserver()

Now, the language server seems to work fine for definitions within the file I’m working on, but how do I get it to include all definitions in the current project, ie also from other .jl files? The project is in Julia standard format with its own environment.

1 Like

Maybe passing --project to julia should work?

You should pass the project path to the server. Here is what neovim does: nvim-lspconfig/julials.lua at 2dd9e060f21eecd403736bef07ec83b73341d955 · neovim/nvim-lspconfig · GitHub and here is the logic for finding the project: nvim-lspconfig/julials.lua at 2dd9e060f21eecd403736bef07ec83b73341d955 · neovim/nvim-lspconfig · GitHub

1 Like

Do you have roots = ["Project.toml", "Manifest.toml"] set in your ~/.config/helix/languages.toml? The default config doesn’t have that set. If you set that, the LS should be able to find everything, as that is used to detect a workspace (and set it from helix’ side during initialization).

I tried that and sadly this does not do any difference for me. Do you have experience with this changing anything?

Thank you for taking the time to point clearly to the solution for Neovim. I have tried similar things but not this exact one. I might try to steal parts of it even though I’m not sure if it should be needed for Helix.

I’m managed to get the language server working using this languages.toml file:

Also, make sure you have the LanguageServer package installed.

[[language]]
name = "julia"
scope = "source.julia"
injection-regex = "julia"
file-types = ["jl"]
comment-token = "#"
language-server = { command = "julia", args = ["--startup-file=no", "--history-file=no", "--quiet", "--project", "-e", "using LanguageServer; runserver()"] }

It should work if you open helix at the root of your project.

It seems that you completely removed the “roots” definition. Is that correct? This looks like the default configuration…?

I do try to open it in the root of my project. If correctly configured I guess it should work to open Helix anywhere within my project folder?

To be clear, this is how my language.toml file looks right now

[[language]]
name = "julia"
scope = "source.julia"
injection-regex = "julia"
file-types = ["jl"]
roots = ["Project.toml", "Manifest.toml"]
comment-token = "#"
language-server = { command = "julia", args = [
    "--startup-file=yes",
    "--history-file=no",
    "--quiet",
    "-e",
    "using LanguageServer; runserver()",
    ] }
indent = { tab-width = 4, unit = "    " }

You’re right, i forgot about roots. Without roots it should work if you open helix at the root of your project, but i guess with roots defined you can open at any directory.

I think the only problem for you is that you need to pass the --project argument when running the language server.

I also won’t recommend using --startup-file=yes, because loading the startup.jl file may increase the time for the language server to start.

I would change your languages.toml file to this:

[[language]]
name = "julia"
scope = "source.julia"
injection-regex = "julia"
file-types = ["jl"]
roots = ["Project.toml", "Manifest.toml"]
comment-token = "#"
language-server = { command = "julia", args = [
-   "--startup-file=yes",
+   "--startup-file=no",
    "--history-file=no",
    "--quiet",
+   "--project",
    "-e",
    "using LanguageServer; runserver()",
    ] }
indent = { tab-width = 4, unit = "    " }

For some reason, adding the “–project” doesn’t seem to make any difference. I also set startup-file to no.

That’s weird. Maybe looking at the helix.log file may tell us what’s wrong.

Remove the ~/.cache/helix/helix.log file, then open helix within your Julia project, wait for the language server to start and then run the command :log-open. With the log file open, see if the language server is actually loading the dependencies of your project or if any errors occurred.

First of all, thank you so much for all the time you spend trying to help me, much appreciated!

My log file looks something like

2022-10-19T12:55:52.192 helix_lsp::transport [ERROR] err <- "[ Info: Downloading caches...\n"
2022-10-19T12:55:52.675 helix_lsp::transport [ERROR] err <- "[ Info: Cloud was unable to cache FilePathsBase in the past, we won't try to retrieve it again.\n"
2022-10-19T12:55:52.677 helix_lsp::transport [ERROR] err <- "[ Info: All cache files downloaded.\n"
2022-10-19T12:55:55.420 helix_lsp::transport [ERROR] err <- "[ Info: Loading CSV from cache...\n"
2022-10-19T12:55:57.591 helix_lsp::transport [ERROR] err <- "[ Info: Loading InlineStrings from cache... (0%)\n"
2022-10-19T12:55:57.610 helix_lsp::transport [ERROR] err <- "[ Info: Loading Parsers from cache... (0%)\n"
2022-10-19T12:55:57.634 helix_lsp::transport [ERROR] err <- "[ Info: Loading Dates from cache... (0%)\n"
etc..

It looks like the right modules are loaded. Why the [ERROR] though?

That’s just because the language server is using stderr for its debug output as well.

It’s odd that setting the roots doesn’t work for you, I was under the impression that it did for me :thinking: I can check in a bit though.

That’s because stdout is used to exchange the JSON data between the editor and the language server, so all logging is done to stderr.

So, if the server loads your project dependencies correctly, then you should get intellisense and autocompletion working for symbols defined in those packages. Do you get missing references when trying to use functions defined in other packages (that are installed in your environment)? Or do you only get missing references when trying to use a function that you defined in another file?

If I have explicitly imported the module in the file it is used in with using module_x, then it works! So the only problem seems to be if the using is in another file or if the definition itself is in another file in the project.

The language server analyzes the using/import statements to load the symbols for packages that you use, and include to load symbols defined in other files.

Can you share your project structure?

Ok! Here might lay my problem. For example one of my files looks like this:

using DataStructures
const projectdir = dirname(Base.active_project()) * "/" 
include(projectdir * "src/Core/Helpers.jl")
include(projectdir * "src/Core/Constituents.jl")
include(projectdir * "src/Core/CoreSettings.jl")
include(projectdir * "src/Core/Entry.jl")
include(projectdir * "src/Core/Entries.jl")
include(projectdir * "src/Core/Collection.jl")
include(projectdir * "src/Core/SimpleFastDB.jl")
include(projectdir * "src/Core/TSDL.jl")
include(projectdir * "src/Core/CoreLayer.jl")
include(projectdir * "src/Core/DataView.jl")
include(projectdir * "src/Core/ShowCollection.jl")
include(projectdir * "src/Core/CoreReturn.jl")
include(projectdir * "src/Core/CollectionSetup.jl")
include(projectdir * "src/Core/SetOperations.jl")
include(projectdir * "src/CollectionTools/CollectionTools.jl")
# include("CoreTest.jl")
Maybe this gives a clue to my problems?

You shouldn’t use expressions inside include, because the language server can’t execute the code inside include to figure out what file to load. You don’t actually need to use projectdir here either, just using include("src/path/to/file.jl") should work.

There we have the solution, probably! Of course I added that stuff not for fun but because it helped in some situations regarding how I use the code. But I might have to consider changing that then!

Thank you so much! I’m very impressed with the level of help I received!

1 Like

I would recomend installing LanguageServer.jl to it’s own project (e.g. ~/.julia/environments/nvim-lspconfig/ in my case). Then you can also use this Makefile to build a custom sysimage for the server to give instant startup. This might require passing the project environment explicitly though, since your server runs in another one.