Emacs-based workflow

Thanks for the guidanace @non-Jedi @ffevotte I can now see eglot is useful now. But I have a silly question , I’m starting eglot-jl-init everytime I open a project is that necessary. I thought this command does the same as lsp finds and caches a workspace. Correct me if I’m wrong.

eglot-jl-init should only need to be run once per emacs session. All it does is inform eglot of how to start a language server for a Julia project, and after that, invoking eglot should just work for Julia files/projects.

1 Like

updated to latest eglot-jl now things are wokring fine without any workarounds.

1 Like

If anyone has a setup script for Doom Emacs, I would highly appreciate it. I only know to add (package! julia-mode) in my packages.el but if I do M-x install eglot-jl stuff, it can break Doom.

I don’t use doom, but is something missing from the doom documentation for julia and doom documentation for lsp/eglot? It describes installation and setup of LanguageServer.jl, and installing eglot-jl should be a simple matter of adding the +lsp flag for julia and the +eglot flag for lsp.

Nice to see you here, @miguelraz

Now I’m going to bother you here and on Twitter! :joy:

My Doom setup is working flawlessly on Julia-mode after doing the following.

1 Like

Anyone else had eglot breaking after updating Julia to 1.7?
I always get an “eglot server died code -1” (paraphrasing) message a few seconds after startup and opening a .jl file.

I get three instances of this block in the eglot buffer, and it doesn’t give me any additional information.

[stderr] nil
[stderr]   No Changes to `~/.emacs.d/elpa/eglot-jl-20210415.1207/Project.toml`
[stderr]   No Changes to `~/.emacs.d/elpa/eglot-jl-20210415.1207/Manifest.toml`
[stderr] [ Info: Environment successfully resolved
[internal] Mon Dec  6 17:18:13 2021:
(:message "Connection state changed" :change "exited abnormally with code 1\n")

----------b---y---e---b---y---e----------

Although this abnormal exit code message appears before, I also find some error messages in the buffer, related to SymbolServer.jl:

ERROR: LoadError: type DataType has no field isconcretetype
ERROR: LoadError: Failed to precompile SymbolServer [cf896787-08d5-524d-9de7-132aaa0cb996] to /home/tomas/.julia/compiled/v1.7/SymbolServer/jl_e0YSWz.

EDIT: Seems like the error stack trace is quite long, it seems to me it starts to complain in LanguageServer.jl, when reading something from eglot-jl. This is the footer on the whole stacktrace from eglot:

[stderr] ERROR: LoadError: Failed to precompile LanguageServer [2b0e0bc5-e4fd-59b4-8912-456d1b03d8d7] to /home/tomas/.julia/compiled/v1.7/LanguageServer/jl_9eVqXt.
[stderr] Stacktrace:
[stderr] nil
[stderr] nil
[stderr] nil
[stderr]  [1] error(s::String)
[stderr]  [1] error(s::String)
[stderr] nil
[stderr] nil
[stderr]    @ Base ./error.jl:33
[stderr]    @ Base ./error.jl:33
[stderr]  [2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, ignore_loaded_modules::Bool)
[stderr]    @ Base ./loading.jl:1466
[stderr]  [3] compilecache(pkg::Base.PkgId, path::String)
[stderr]    @ Base ./loading.jl:1410
[stderr]  [4] _require(pkg::Base.PkgId)
[stderr]    @ Base ./loading.jl:1120
[stderr]  [5] require(uuidkey::Base.PkgId)
[stderr]    @ Base ./loading.jl:1013
[stderr]  [6] require(into::Module, mod::Symbol)
[stderr]    @ Base ./loading.jl:997
[stderr] in expression starting at /home/tomas/.emacs.d/elpa/eglot-jl-20210415.1207/eglot-jl.jl:47
[internal] Mon Dec  6 17:18:17 2021:
(:message "Connection state changed" :change "exited abnormally with code 1\n")

----------b---y---e---b---y---e----------
[stderr] 
[stderr] 
[stderr] nil
[stderr] nil
[stderr] Process EGLOT (julia/julia-mode) stderr<1> finished

And line 47 in eglot-jl-jl is simply “using LanguageServer SymbolServer”. I don’t get it.

I used the REPL to activate the eglot-jl enviroment by itself, which has only LanguageServer.jl and SymbolServer.jl as active packages (as expected).
In the REPL, I get the same error as I get in emacs just by calling “using LanguageServer.jl”.
No such error occurs when I do the same on my main environment.

tomas@tomas-Lenovo-ideapad:~/.emacs.d/elpa/eglot-jl-20210415.1207$ julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.7.0 (2021-11-30)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

(@v1.7) pkg> activate .
  Activating project at `~/.emacs.d/elpa/eglot-jl-20210415.1207`

(eglot-jl-20210415.1207) pkg> st
      Status `~/.emacs.d/elpa/eglot-jl-20210415.1207/Project.toml`
  [2b0e0bc5] LanguageServer v3.2.0
  [cf896787] SymbolServer v5.1.1

julia> using LanguageServer
[ Info: Precompiling LanguageServer [2b0e0bc5-e4fd-59b4-8912-456d1b03d8d7]
ERROR: LoadError: type DataType has no field isconcretetype
Stacktrace:
  [1] getproperty(x::Type, f::Symbol)
    @ Base ./Base.jl:37
  [2] SymbolServer.DataTypeStore(t::Any, parent_mod::Module, exported::Bool)
    @ SymbolServer ~/.julia/packages/SymbolServer/2m6sg/src/symbols.jl:72
  [3] symbols(env::Dict{Symbol, SymbolServer.ModuleStore}, m::Module, allnames::Base.IdSet{Symbol}, visited::Base.IdSet{Module})
    @ SymbolServer ~/.julia/packages/SymbolServer/2m6sg/src/symbols.jl:390
  [4] symbols(env::Dict{Symbol, SymbolServer.ModuleStore}, m::Nothing, allnames::Base.IdSet{Symbol}, visited::Base.IdSet{Module})
    @ SymbolServer ~/.julia/packages/SymbolServer/2m6sg/src/symbols.jl:433
  [5] symbols (repeats 3 times)
    @ ~/.julia/packages/SymbolServer/2m6sg/src/symbols.jl:380 [inlined]
  [6] load_core()
    @ SymbolServer ~/.julia/packages/SymbolServer/2m6sg/src/symbols.jl:442
  [7] top-level scope
    @ ~/.julia/packages/SymbolServer/2m6sg/src/SymbolServer.jl:203
  [8] include
    @ ./Base.jl:418 [inlined]
  [9] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::String)
    @ Base ./loading.jl:1318
 [10] top-level scope
    @ none:1
 [11] eval
    @ ./boot.jl:373 [inlined]
 [12] eval(x::Expr)
    @ Base.MainInclude ./client.jl:453
 [13] top-level scope
    @ none:1
in expression starting at /home/tomas/.julia/packages/SymbolServer/2m6sg/src/SymbolServer.jl:1
ERROR: LoadError: Failed to precompile SymbolServer [cf896787-08d5-524d-9de7-132aaa0cb996] to /home/tomas/.julia/compiled/v1.7/SymbolServer/jl_Yq2Ock.
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:33
  [2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, ignore_loaded_modules::Bool)
    @ Base ./loading.jl:1466
  [3] compilecache(pkg::Base.PkgId, path::String)
    @ Base ./loading.jl:1410
  [4] _require(pkg::Base.PkgId)
    @ Base ./loading.jl:1120
  [5] require(uuidkey::Base.PkgId)
    @ Base ./loading.jl:1013
  [6] require(into::Module, mod::Symbol)
    @ Base ./loading.jl:997
  [7] include
    @ ./Base.jl:418 [inlined]
  [8] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
    @ Base ./loading.jl:1318
  [9] top-level scope
    @ none:1
 [10] eval
    @ ./boot.jl:373 [inlined]
 [11] eval(x::Expr)
    @ Base.MainInclude ./client.jl:453
 [12] top-level scope
    @ none:1
in expression starting at /home/tomas/.julia/packages/LanguageServer/y1ebo/src/LanguageServer.jl:1
ERROR: Failed to precompile LanguageServer [2b0e0bc5-e4fd-59b4-8912-456d1b03d8d7] to /home/tomas/.julia/compiled/v1.7/LanguageServer/jl_tzd7m4.
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:33
 [2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, ignore_loaded_modules::Bool)
   @ Base ./loading.jl:1466
 [3] compilecache(pkg::Base.PkgId, path::String)
   @ Base ./loading.jl:1410
 [4] _require(pkg::Base.PkgId)
   @ Base ./loading.jl:1120
 [5] require(uuidkey::Base.PkgId)
   @ Base ./loading.jl:1013
 [6] require(into::Module, mod::Symbol)
   @ Base ./loading.jl:997

I don’t know if this will work for everyone, but a lot of problems I had with eglot-jl disappeared when I upgraded LanguageServer and SymbolServer in the eglot-jl environment. I was very happily running LanguageServer 4.1.0 and SymbolSever 7.0.1 in Julia 1.6.3 and everything seemed to keep working with the upgrade to 1.7.0. I have the same version of eglot-jl that you are using. It might be worth a try.

CC: @non-Jedi

Yes, it looks like the version of LanguageServer that we enforce is incompatible with Julia 1.7. And we enforce it both in the compat bounds defined in Project.toml and in the shipped Manifest.toml.

I’m actually not sure how to make sure that eglot-jl works for a range of julia versions. But in the meantime, try updating your eglot-jl environment as @mstewart said. The process should involve the following steps:

  1. edit ~/.emacs.d/elpa/eglot-jl-20210415.1207/Project.toml and remove the line LanguageServer="3" in the [compat] section.

  2. update the environment:

    pkg> activate ~/.emacs.d/elpa/eglot-jl-20210415.1207
    pkg> update
    

Please report back whether this works. And maybe file an issue on github to help us avoid forgetting about this issue.

I’m thinking that if LanguageServer v4.x works well for Julia 1.6 (which is now LTS), the fix might be as simple as shipping a Manifest that enforces a version in the 4.x family. We’ll have to try that for a while and see whether it breaks things.

1 Like

It’s just one data point, but I used it for what was probably a few months with LanguageServer 4.1.0 on 1.6. It was completely smooth sailing for me. I got a lot of missing reference errors when using the older LanguageServer. For me eglot-jl with LanguageServer 4.x has been a real pleasure to use on both Julia 1.6 and 1.7 (so far, with 1.7; I just upgraded to that last week).

Thanks @mstewart and @ffevotte .
When I posted here with the error trace, I directed eglot-jl to use my main environment to start the server, which allowed me to keep using it.
I removed the compatibility line in the Project.toml file, as you suggested, and after the Pkg update command, eglot-jl’s own environment proceed to point to a v4 binary (already on my machine).
Back on emacs, I removed the line that pointed to my main environment, so eglot-jl would default to its own, and the server connected - success!
I’ll create an issue on the project’s repository.

Unrelated questions:

  1. Is it normal for the server to always take around 20 seconds to connect? My machine isn’t particularly weak.
  2. How can we maximize our chances that the language server will be able to access docstrings or function definitions from functions and constructors in installed libraries? I mean functions we call from libraries we are including in our file through “using LibraryX”.

Since it appears some people are having issues I figured I should post how I got it to work.

Note: I’m using Doom, but it shouldn’t make a difference.

First: Make sure you download the git version of LanguageServer.jl Not the Pkg version. https://github.com/non-Jedi/lsp-julia/issues/56#issuecomment-976676210

Second: Use this to force lsp-julia to use your Julia install, not it’s built-in install:

(setq lsp-julia-package-dir nil) in config.el

Third: Then indicate the environment where Julia is ran:

(setq lsp-julia-default-environment "~/.julia/environments/v1.7")
(setq eglot-jl-language-server-project "~/.julia/environments/v1.7")

Fourth: At the first install, the server may take a moment to initialize the environment. However, this usually takes more than the default timeout. Use this to avoid that:

(setq eglot-connect-timeout 5000)

(Any big number will do, if it keeps crashing due to weaker PC increase the number).

Optional: I like folding so I use this to enable it.

(after! julia-mode
  (add-hook! 'julia-mode-hook
    (setq-local lsp-enable-folding t
                lsp-folding-range-limit 100)))

Optional 2: For those working on multithreaded tasks you may want to use this as well to launch Julia with more workers (Replace NCORES with what you want to use) but note that it takes longer to start:

(setq julia-repl-switches "-p NCORES")
3 Likes

Note that workers are separate processes for Distributed. For multiple threads, you can add -tauto.

Your reply is mixing together lsp-mode variables and eglot variables. The two are unrelated although they can both provide similar features while using LanguageServer.jl as a backend. It looks like you’re ultimately trying to use lsp-mode, so you shouldn’t be messing with the eglot-jl- prefixed variables.

Also, I highly recommend against setting eglot-jl-language-server-project to your default environment. If you want to use a different version of LanguageServer.jl, install it and SymbolServer into an isolated environment rather than the default.

Just a quick question for you, what do you recommend in terms of usability and general stability eglot.jl or lsp (both packages under your github)?
I am using doom emacs, and I see preconfigured eglot in the julia module so maybe you have some recommendations here?

I recommend eglot personally, but I think a lot more people use lsp-mode than eglot. I will say that I’ve heard that lsp-mode’s codebase has been almost entirely rewritten in the past couple years and is much cleaner now, but that’s pure hearsay.

I don’t maintain lsp-julia personally any more (@gdkrmr has been doing a good job of it); the only reason it’s still under my github is to avoid breaking links.

I couldn’t agree more with this.

It looks like upstream issues with LanguageServer are currently affecting eglot-jl (and probably lsp-julia in the same way). Since these issues appear to have been fixed in LanguageServer#master, the most straightforward workaround that I can think of (while waiting for a new release of LanguageServer) is to indeed use a custom version of LanguageServer. The following github issue contains a ste-by-step guide on how to do this without messing with the default environment:

If this is useful and clear enough, I’m considering adding this as an entry in eglot-jl’s FAQ.

1 Like

Each time I try to set Language server I see this

[stderr] nil
[stderr]   No Changes to `~/.doom.d/julia-lsp/Project.toml`
[stderr]   No Changes to `~/.doom.d/julia-lsp/Manifest.toml`
[stderr] [ Info: Environment successfully resolved
[stderr] ┌ Info: Running language server
[stderr] │   env = "/home/username/.doom.d/julia-lsp/Project.toml"
[stderr] │   src_path = "/home/username/aoc2021/src/"
[stderr] │   project_path = "/home/username/aoc2021"
[stderr] └   depot_path = ""
[internal] Mon Dec 20 13:59:04 2021:
(:message "Connection state changed" :change "killed\n")

----------b---y---e---b---y---e----------
[stderr] 
[stderr] 
[stderr] nil
[stderr] nil
[stderr] Process EGLOT (aoc2021/julia-mode) stderr finished

Specifically depot_path is missing, should it?

I’m running LanguageServer#master branch with eglot jl, should I bump eglot ?