oxalica / nil

NIx Language server, an incremental analysis assistant for writing in Nix.
Apache License 2.0
1.28k stars 39 forks source link

Emacs eglot with nil locks up when opening Nix file in large directory #98

Closed 2xsaiko closed 1 year ago

2xsaiko commented 1 year ago

I'm currently trying out Emacs, using eglot for LSP and nix-mode. When I open up a Nix file in a large directory, such as ~, and then enable eglot, it spawns a find process that locks up the editor.

Killing the process results in this error which comes from nil:

[eglot] Server reports (type=1): Failed to watch flake files: Internal error (jsonrpc error -32603)

The command line for the process:

find -H . ( -path */SCCS/* -o -path */RCS/* -o -path */CVS/* -o -path */MCVS/* -o -path */.src/* -o -path */.svn/* -o -path */.git/* -o -path */.hg/* -o -path */.bzr/* -o -path */_MTN/* -o -path */_darcs/* -o -path */{arch}/* -o -path */.#* -o -path *.o -o -path *~ -o -path *.bin -o -path *.lbin -o -path *.so -o -path *.a -o -path *.ln -o -path *.blg -o -path *.bbl -o -path *.elc -o -path *.lof -o -path *.glo -o -path *.idx -o -path *.lot -o -path *.fmt -o -path *.tfm -o -path *.class -o -path *.fas -o -path *.lib -o -path *.mem -o -path *.x86f -o -path *.sparcf -o -path *.dfsl -o -path *.pfsl -o -path *.d64fsl -o -path *.p64fsl -o -path *.lx64fsl -o -path *.lx32fsl -o -path *.dx64fsl -o -path *.dx32fsl -o -path *.fx64fsl -o -path *.fx32fsl -o -path *.sx64fsl -o -path *.sx32fsl -o -path *.wx64fsl -o -path *.wx32fsl -o -path *.fasl -o -path *.ufsl -o -path *.fsl -o -path *.dxl -o -path *.lo -o -path *.la -o -path *.gmo -o -path *.mo -o -path *.toc -o -path *.aux -o -path *.cp -o -path *.fn -o -path *.ky -o -path *.pg -o -path *.tp -o -path *.vr -o -path *.cps -o -path *.fns -o -path *.kys -o -path *.pgs -o -path *.tps -o -path *.vrs -o -path *.pyc -o -path *.pyo ) -prune -o -type f -print0

This does not happen in another editor with nil, or eglot in the same directory with another language server (i.e. clangd). Not sure who's at fault here. My guess is some weird interaction between the two where eglot's implementation of something isn't as efficient as nil expects it to be.

oxalica commented 1 year ago

Killing the process results in this error which comes from nil:

[eglot] Server reports (type=1): Failed to watch flake files: Internal error (jsonrpc error -32603)

This error messages is related to the file watching registration of <workdir>/flake.{nix,lock}.

https://github.com/oxalica/nil/blob/59bcad0b13b5d77668c0c125fef71d7b41406d7a/crates/nil/src/server.rs#L252-L262

I guess the client (emacs, or its plugins) have a poor implementation which scans the whole directory to filter out the pattern, instead of watching the specific files directly. The find command you are given does not contain keyword flake or nix at all. We also never invoke find.

The command line for the process:

find -H . ([..]) -prune -o -type f -print0

Please forward this issue to the language client, possibly eglot, if you are sure the issue still exists without any other plugins.

sauricat commented 1 year ago

Reproduced and backtraced the same problem.

Debugger entered--Lisp error: (error "File listing failed: ./.cache/dconf/user\0./.cache/...")
  error("File listing failed: %s" "./.cache/dconf/user\0./.cache/ibus/bus/registry\0./....")
  project--files-in-directory("/home/shu/" ("SCCS/" "RCS/" "CVS/" "MCVS/" ".src/" ".svn/" ".git/" ".hg/" ".bzr/" "_MTN/" "_darcs/" "{arch}/" ".#*" "*.o" "*~" "*.bin" "*.lbin" "*.so" "*.a" "*.ln" "*.blg" "*.bbl" "*.elc" "*.lof" "*.glo" "*.idx" "*.lot" "*.fmt" "*.tfm" "*.class" "*.fas" "*.lib" "*.mem" "*.x86f" "*.sparcf" "*.dfsl" "*.pfsl" "*.d64fsl" "*.p64fsl" "*.lx64fsl" "*.lx32fsl" "*.dx64fsl" "*.dx32fsl" "*.fx64fsl" "*.fx32fsl" "*.sx64fsl" "*.sx32fsl" "*.wx64fsl" "*.wx32fsl" "*.fasl" ...))
  #f(compiled-function (dir) #<bytecode -0x165cd3afcda95a77>)("/home/shu/")
  mapcan(#f(compiled-function (dir) #<bytecode -0x165cd3afcda95a77>) ("/home/shu/"))
  #f(compiled-function (project &optional dirs) #<bytecode -0x4095f58103c1ab9>)((transient . "/home/shu/"))
  apply(#f(compiled-function (project &optional dirs) #<bytecode -0x4095f58103c1ab9>) (transient . "/home/shu/") nil)
  project-files((transient . "/home/shu/"))
  #f(compiled-function (arg1 arg2 arg3 &rest rest) "Handle dynamic registration of workspace/didChangeWatchedFiles." #<bytecode -0x159888921696cd27>)(#<eglot-lsp-server eglot-lsp-server-12327734> workspace/didChangeWatchedFiles "workspace/didChangeWatchedFiles" :watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")])
  apply(#f(compiled-function (arg1 arg2 arg3 &rest rest) "Handle dynamic registration of workspace/didChangeWatchedFiles." #<bytecode -0x159888921696cd27>) #<eglot-lsp-server eglot-lsp-server-12327734> workspace/didChangeWatchedFiles ("workspace/didChangeWatchedFiles" :watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))
  eglot-register-capability(#<eglot-lsp-server eglot-lsp-server-12327734> workspace/didChangeWatchedFiles "workspace/didChangeWatchedFiles" :watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")])
  eglot--register-unregister(#<eglot-lsp-server eglot-lsp-server-12327734> [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))] register)
  #f(compiled-function (arg1 arg2 &rest rest) "Handle server request client/registerCapability." #<bytecode -0x178e21679664faab>)(#<eglot-lsp-server eglot-lsp-server-12327734> client/registerCapability :registrations [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))])
  apply(#f(compiled-function (arg1 arg2 &rest rest) "Handle server request client/registerCapability." #<bytecode -0x178e21679664faab>) #<eglot-lsp-server eglot-lsp-server-12327734> client/registerCapability (:registrations [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))]))
  eglot-handle-request(#<eglot-lsp-server eglot-lsp-server-12327734> client/registerCapability :registrations [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))])
  apply(eglot-handle-request #<eglot-lsp-server eglot-lsp-server-12327734> client/registerCapability (:registrations [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))]))
  #f(compiled-function (server method params) #<bytecode -0xa0c4724fdc37f61>)(#<eglot-lsp-server eglot-lsp-server-12327734> client/registerCapability (:registrations [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))]))
  jsonrpc-connection-receive(#<eglot-lsp-server eglot-lsp-server-12327734> (:jsonrpc "2.0" :id 1 :method "client/registerCapability" :params (:registrations [(:id "workspace/didChangeWatchedFiles" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "/home/shu/flake.lock") (:globPattern "/home/shu/flake.nix")]))])))
  jsonrpc--process-filter(#<process EGLOT (shu/(nix-mode))> "Content-Length: 98\15\n\15\n{\"jsonrpc\":\"2.0\",\"id\":0,\"met...")

where shu is my username, showing that it might be the problem with eglot. I suggest you post an issue in their repo with a link to this closed issue.