joaotavora / eglot

A client for Language Server Protocol servers
GNU General Public License v3.0
2.21k stars 203 forks source link

Eglot refuses to connect to an LSP server after deleting a folder from the projects root #1198

Closed VernonGrant closed 1 year ago

VernonGrant commented 1 year ago

I've been struggling with a strange bug the past two days. I think it's related to Git in some way. Let me briefly explain the issue:

I would open up a PHP project and navigate to a source file in the project root. Eglot will successfully connect to the LSP server, in my case Intelephense (the same issue exists using phpactor). Everything works as expected, but once I delete any directory inside of the projects root folder. All future Eglot connections, meaning closing and reopening Emacs, or calling eglot-shutdown-all and reconnecting, will just completely blow up and fail. No matter what I tried, Eglot refused to reconnect to the LSP server that was working before.

Now here's the stange thing, once I commit the changes into version control or just revert back the deleted folder, Eglot reconnects and everything works again. So I'm thinking this might be related to a misuse of git ls-files or something.

What I tried

Steps taken to reproducing this issue

Assuming you already have Intelephense installed (npm i -g intelephense) and you use this configuration:

(use-package eglot
  :config
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '((php-mode phps-mode) . ("intelephense" "--stdio")))))

You can do the following:

  1. Open a new terminal.
  2. Run git clone https://github.com/mattbanks/WordPress-Starter-Theme.git
  3. Run cd ./WordPress-Starter-Theme && emacs ./
  4. Perform C-x C-f and open functions.php
  5. Type M-x eglot and press enter and wait for Eglot to successfully connect to the LSP.
  6. Exit/Close Emacs.
  7. Run rm -R ./lib (in the same terminal window)
  8. Open Emacs emacs ./
  9. Perform C-x C-f and open functions.php
  10. Type M-x eglot and press enter and now Eglot's connection to the LSP will now fail.

Eglot events buffer output

[internal] Mon Apr  3 00:12:30 2023:
(:message "Running language server: intelephense --stdio")
[client-request] (id:1) Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :id 1 :method "initialize" :params
          (:processId 32357 :rootPath "/Users/vernon/Devenv/projects/WordPress-Starter-Theme/" :rootUri "file:///Users/vernon/Devenv/projects/WordPress-Starter-Theme" :initializationOptions #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                                                                                                            ())
                      :capabilities
                      (:workspace
                       (:applyEdit t :executeCommand
                                   (:dynamicRegistration :json-false)
                                   :workspaceEdit
                                   (:documentChanges t)
                                   :didChangeWatchedFiles
                                   (:dynamicRegistration t)
                                   :symbol
                                   (:dynamicRegistration :json-false)
                                   :configuration t :workspaceFolders t)
                       :textDocument
                       (:synchronization
                        (:dynamicRegistration :json-false :willSave t :willSaveWaitUntil t :didSave t)
                        :completion
                        (:dynamicRegistration :json-false :completionItem
                                              (:snippetSupport :json-false :deprecatedSupport t :resolveSupport
                                                               (:properties
                                                                ["documentation" "details" "additionalTextEdits"])
                                                               :tagSupport
                                                               (:valueSet
                                                                [1]))
                                              :contextSupport t)
                        :hover
                        (:dynamicRegistration :json-false :contentFormat
                                              ["markdown" "plaintext"])
                        :signatureHelp
                        (:dynamicRegistration :json-false :signatureInformation
                                              (:parameterInformation
                                               (:labelOffsetSupport t)
                                               :activeParameterSupport t))
                        :references
                        (:dynamicRegistration :json-false)
                        :definition
                        (:dynamicRegistration :json-false :linkSupport t)
                        :declaration
                        (:dynamicRegistration :json-false :linkSupport t)
                        :implementation
                        (:dynamicRegistration :json-false :linkSupport t)
                        :typeDefinition
                        (:dynamicRegistration :json-false :linkSupport t)
                        :documentSymbol
                        (:dynamicRegistration :json-false :hierarchicalDocumentSymbolSupport t :symbolKind
                                              (:valueSet
                                               [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]))
                        :documentHighlight
                        (:dynamicRegistration :json-false)
                        :codeAction
                        (:dynamicRegistration :json-false :codeActionLiteralSupport
                                              (:codeActionKind
                                               (:valueSet
                                                ["quickfix" "refactor" "refactor.extract" "refactor.inline" "refactor.rewrite" "source" "source.organizeImports"]))
                                              :isPreferredSupport t)
                        :formatting
                        (:dynamicRegistration :json-false)
                        :rangeFormatting
                        (:dynamicRegistration :json-false)
                        :rename
                        (:dynamicRegistration :json-false)
                        :inlayHint
                        (:dynamicRegistration :json-false)
                        :publishDiagnostics
                        (:relatedInformation :json-false :codeDescriptionSupport :json-false :tagSupport
                                             (:valueSet
                                              [1 2])))
                       :general
                       (:positionEncodings
                        ["utf-32" "utf-8" "utf-16"])
                       :experimental #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                   ()))
                      :workspaceFolders
                      [(:uri "file:///Users/vernon/Devenv/projects/WordPress-Starter-Theme" :name "~/Devenv/projects/WordPress-Starter-Theme/")]))
[server-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "window/logMessage" :params
          (:type 3 :message "Initialising intelephense 1.9.5"))
[server-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "window/logMessage" :params
          (:type 3 :message "Reading state from /var/folders/sw/md6v530x7h1d9s05_zg3qpkc0000gn/T/intelephense/425c0075."))
[server-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "window/logMessage" :params
          (:type 3 :message "Intelephense premium features enabled."))
[server-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "window/logMessage" :params
          (:type 3 :message "Initialised in 82 ms"))
[server-reply] (id:1) Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :id 1 :result
          (:capabilities
           (:textDocumentSync 2 :documentSymbolProvider t :workspaceSymbolProvider t :completionProvider
                              (:triggerCharacters
                               ["$" ">" ":" "\\" "/" "*" "." "<"]
                               :resolveProvider t)
                              :signatureHelpProvider
                              (:triggerCharacters
                               ["(" "," ":"])
                              :definitionProvider t :documentFormattingProvider t :documentRangeFormattingProvider t :referencesProvider t :hoverProvider t :documentHighlightProvider t :foldingRangeProvider t :implementationProvider t :declarationProvider t :workspace
                              (:workspaceFolders
                               (:supported t :changeNotifications t))
                              :renameProvider
                              (:prepareProvider t)
                              :typeDefinitionProvider t :selectionRangeProvider t :codeActionProvider t :executeCommandProvider
                              (:commands
                               ["intelephense.import.symbol" "intelephense.implement.abstract.method.all" "intelephense.phpdoc.add"]))))
[client-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "initialized" :params #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                                            ()))
[client-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "textDocument/didOpen" :params
          (:textDocument
           (:uri "file:///Users/vernon/Devenv/projects/WordPress-Starter-Theme/functions.php" :version 0 :languageId "php" :text "<?php\n/**\n * _mbbasetheme functions and definitions\n *\n * @package _mbbasetheme\n */\n\n/****************************************\nTheme Setup\n*****************************************/\n\n/**\n * Theme initialization\n */\nrequire get_template_directory() . '/lib/init.php';\n\n/**\n * Custom theme functions definited in /lib/init.php\n */\nrequire get_template_directory() . '/lib/theme-functions.php';\n\n/**\n * Helper functions for use in other areas of the theme\n */\nrequire get_template_directory() . '/lib/theme-helpers.php';\n\n/**\n * Implement the Custom Header feature.\n */\n//require get_template_directory() . '/lib/inc/custom-header.php';\n\n/**\n * Custom template tags for this theme.\n */\nrequire get_template_directory() . '/lib/inc/template-tags.php';\n\n/**\n * Custom functions that act independently of the theme templates.\n */\nrequire get_template_directory() . '/lib/inc/extras.php';\n\n/**\n * Customizer additions.\n */\nrequire get_template_directory() . '/lib/inc/customizer.php';\n\n/**\n * Load Jetpack compatibility file.\n */\nrequire get_template_directory() . '/lib/inc/jetpack.php';\n\n\n/****************************************\nRequire Plugins\n*****************************************/\n\nrequire get_template_directory() . '/lib/class-tgm-plugin-activation.php';\nrequire get_template_directory() . '/lib/theme-require-plugins.php';\n\n// add_action( 'tgmpa_register', 'mb_register_required_plugins' );\n\n\n/****************************************\nMisc Theme Functions\n*****************************************/\n\n/**\n * Define custom post type capabilities for use with Members\n */\nadd_action( 'admin_init', 'mb_add_post_type_caps' );\nfunction mb_add_post_type_caps() {\n   // mb_add_capabilities( 'portfolio' );\n}\n\n/**\n * Filter Yoast SEO Metabox Priority\n */\nadd_filter( 'wpseo_metabox_prio', 'mb_filter_yoast_seo_metabox' );\nfunction mb_filter_yoast_seo_metabox() {\n return 'low';\n}\n")))
[client-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "workspace/didChangeConfiguration" :params
          (:settings #s(hash-table size 1 test eql rehash-size 1.5 rehash-threshold 0.8125 data
                                   ())))
[server-request] (id:0) Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :id 0 :method "workspace/configuration" :params
          (:items
           [(:section "intelephense")
            (:section "intelephense" :scopeUri "file:///Users/vernon/Devenv/projects/WordPress-Starter-Theme")]))
[client-reply] (id:0) Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :id 0 :result
          [nil nil])
[server-request] (id:1) Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :id 1 :method "client/registerCapability" :params
          (:registrations
           [(:id "a67bcdc2-38d3-48ed-be95-54c4fbc6bf61" :method "workspace/didChangeWatchedFiles" :registerOptions
                 (:watchers
                  [(:globPattern "**/*.{php,phtml}")]))]))
[client-reply] (id:1) ERROR Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :id 1 :error
          (:code -32603 :message "Internal error"))
[server-notification] Mon Apr  3 00:12:30 2023:
(:jsonrpc "2.0" :method "window/logMessage" :params
          (:type 3 :message "Searching file:///Users/vernon/.nvm/versions/node/v18.14.1/lib/node_modules/intelephense/lib/stub for files to index."))
[stderr] /Users/vernon/.nvm/versions/node/v18.14.1/lib/node_modules/intelephense/lib/intelephense.js:2
[stderr] /Users/vernon/.nvm/versions/node/v18.14.1/lib/node_modules/intelephense/lib/intelephense.js:2
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] nil
[stderr] Node.js v18.14.1
[internal] Mon Apr  3 00:12:30 2023:
(: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 (WordPress-Starter-Theme/(php-mode phps-mode)) stderr finished

Backtrace

I ran toggle-debug-on-error in Emacs. Open the sample repository and deleted the lib folder. Eglot failed to connect, resulting in:

Debugger entered--Lisp error: (file-notify-error "Directory does not exist" "/Users/vernon/Devenv/projects/WordPress-Starter-Theme/lib")
  signal(file-notify-error ("Directory does not exist" "/Users/vernon/Devenv/projects/WordPress-Starter-Theme/lib"))
  file-notify-add-watch("/Users/vernon/Devenv/projects/WordPress-Starter-Theme/lib/" (change) #f(compiled-function (event) #<bytecode 0x2caae1490954e6e>))
  #f(compiled-function (arg1 arg2 arg3 &rest rest) "Handle dynamic registration of workspace/didChangeWatchedFiles." #<bytecode 0x9056e8b638029bb>)(#<eglot-lsp-server eglot-lsp-server-4b5c24e4> workspace/didChangeWatchedFiles "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :watchers [(:globPattern "**/*.{php,phtml}")])
  apply(#f(compiled-function (arg1 arg2 arg3 &rest rest) "Handle dynamic registration of workspace/didChangeWatchedFiles." #<bytecode 0x9056e8b638029bb>) #<eglot-lsp-server eglot-lsp-server-4b5c24e4> workspace/didChangeWatchedFiles ("0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :watchers [(:globPattern "**/*.{php,phtml}")]))
  eglot-register-capability(#<eglot-lsp-server eglot-lsp-server-4b5c24e4> workspace/didChangeWatchedFiles "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :watchers [(:globPattern "**/*.{php,phtml}")])
  apply(eglot-register-capability #<eglot-lsp-server eglot-lsp-server-4b5c24e4> workspace/didChangeWatchedFiles "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" (:watchers [(:globPattern "**/*.{php,phtml}")]))
  eglot--register-unregister(#<eglot-lsp-server eglot-lsp-server-4b5c24e4> [(:id "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "**/*.{php,phtml}")]))] register)
  #f(compiled-function (arg1 arg2 &rest rest) "Handle server request client/registerCapability." #<bytecode 0x1e95c7386a3bc152>)(#<eglot-lsp-server eglot-lsp-server-4b5c24e4> client/registerCapability :registrations [(:id "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "**/*.{php,phtml}")]))])
  apply(#f(compiled-function (arg1 arg2 &rest rest) "Handle server request client/registerCapability." #<bytecode 0x1e95c7386a3bc152>) #<eglot-lsp-server eglot-lsp-server-4b5c24e4> client/registerCapability (:registrations [(:id "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "**/*.{php,phtml}")]))]))
  eglot-handle-request(#<eglot-lsp-server eglot-lsp-server-4b5c24e4> client/registerCapability :registrations [(:id "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "**/*.{php,phtml}")]))])
  apply(eglot-handle-request #<eglot-lsp-server eglot-lsp-server-4b5c24e4> client/registerCapability (:registrations [(:id "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "**/*.{php,phtml}")]))]))
  #f(compiled-function (server method params) #<bytecode -0xa3563f33253ab61>)(#<eglot-lsp-server eglot-lsp-server-4b5c24e4> client/registerCapability (:registrations [(:id "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "**/*.{php,phtml}")]))]))
  jsonrpc-connection-receive(#<eglot-lsp-server eglot-lsp-server-4b5c24e4> (:jsonrpc "2.0" :id 1 :method "client/registerCapability" :params (:registrations [(:id "0b7bffdc-9c52-4f6a-8dae-0b8b4cd06ac3" :method "workspace/didChangeWatchedFiles" :registerOptions (:watchers [(:globPattern "**/*.{php,phtml}")]))])))

Emacs details

Installed packages

/Users/vernon/.emacs.d/elpa
drwxr-xr-x@  4 vernon  staff   128 Apr  1 18:18 archives
drwxr-xr-x@ 11 vernon  staff   352 Apr  1 18:18 bind-key-2.4.1
drwxr-xr-x@ 14 vernon  staff   448 Apr  1 18:18 clojure-mode-5.16.0
drwxr-xr-x@ 20 vernon  staff   640 Apr  1 18:18 compat-29.1.4.1
drwxr-xr-x@ 23 vernon  staff   736 Apr  1 18:18 corfu-0.36
drwxr-xr-x@ 24 vernon  staff   768 Apr  1 18:18 dash-2.19.1
drwxr-xr-x@ 24 vernon  staff   768 Apr  1 18:18 editorconfig-0.9.1
drwxr-xr-x@  6 vernon  staff   192 Apr  2 00:22 eglot-1.13
drwxr-xr-x@  6 vernon  staff   192 Apr  2 01:49 eldoc-1.13.0
drwxr-xr-x@  6 vernon  staff   192 Apr  1 18:18 external-completion-0.1
drwxr-xr-x@  7 vernon  staff   224 Apr  1 18:18 git-commit-3.3.0
drwxr-xr-x@  6 vernon  staff   192 Apr  1 18:18 jsonrpc-1.0.17
drwxr-xr-x@ 99 vernon  staff  3168 Apr  1 18:18 magit-3.3.0
drwxr-xr-x@  9 vernon  staff   288 Apr  1 18:18 magit-section-3.3.0
drwxr-xr-x@ 16 vernon  staff   512 Apr  1 18:18 markdown-mode-2.5
drwxr-xr-x@ 46 vernon  staff  1472 Apr  1 18:18 php-mode-1.24.3
drwxr-xr-x@  6 vernon  staff   192 Apr  1 18:18 project-0.9.8
drwxr-xr-x@ 12 vernon  staff   384 Apr  1 18:18 transient-0.3.7
drwxr-xr-x@ 30 vernon  staff   960 Apr  1 18:18 use-package-2.4.5
drwxr-xr-x@ 13 vernon  staff   416 Apr  1 18:18 with-editor-3.2.0
drwxr-xr-x@  6 vernon  staff   192 Apr  1 18:18 xref-1.6.3
drwxr-xr-x@  9 vernon  staff   288 Apr  1 18:18 yaml-mode-0.0.15
drwxr-xr-x@  9 vernon  staff   288 Apr  1 18:18 zenburn-theme-2.8.0

My system details:

Name Version
OS MacOS Ventura 13.3
Emacs 28.2
Git 2.40.0
Node v18.14.1
PHP 8.2.4
Intelephense 1.9.5
PHPActor latest from master

@joaotavora Let me know if there's anything I can help with, I have some time available this week and know a little bit of Emacs lisp 🙏🏻

nemethf commented 1 year ago

(Commit https://github.com/emacs-mirror/emacs/commit/b6a7b42b199d4e96312bf8147d36dddbcf14f4ea removes the intelephense support, I wonder why.)

You haven't attached the communication logs to your report. Without the logs, I'm just guessing, but they might show that Eglot sends the deleted directory the second time as well, which in turn, might be the result of the caching mechanisms of project.el.

VernonGrant commented 1 year ago

Hi @nemethf, thanks for responding. I'm sure its not an issue with Intelephense as it works just fine in VSCode and I did also try PHPActor, and it has the exact same problem. So it seems we have the same issue in two different language servers, that both work in fine other tools. Where can I find the communication logs?

Eglot sends the deleted directory the second time as well, which in turn, might be the result of the caching mechanisms of project.el.

Let me look into this, thanks!

joaotavora commented 1 year ago

Where can I find the communication logs?

This page has that info. I suggest you visit it and study it briefly to make sure you give us everything mentioned there, including, for example, how you started Emacs.

https://joaotavora.github.io/eglot/#Troubleshooting-Eglot

When you say that visiting a file makes Eglot connect, I find that surprising, because a stock Eglot doesn't do that. So you must have some user config somewhere.

joaotavora commented 1 year ago

In fact, i think you found the events log already, but you called it the "error message". An error in Elisp would be something different, you normally see it and hear it.

VernonGrant commented 1 year ago

When you say that visiting a file makes Eglot connect, I find that surprising, because a stock Eglot doesn't do that. So you must have some user config somewhere.

@joaotavora I apologize, I do have hooks configured through use-package. But you can obviously just run M-x: eglot in the buffer to connect to the LSP. I updated the issue statement with my Eglot config.

Let me take a look at the link, thanks!

VernonGrant commented 1 year ago

@joaotavora Ok, I updated the issue statement with more information. Please take a look at the backtrace, it reports Directory does not exist for the folder I deleted in the example. It seems to be related to workspace/didChangeWatchedFiles. Let me know if there's anything else you need.

joaotavora commented 1 year ago

I updated the issue statement with my Eglot config.

If you can reproduce it with a minimal configuration, as you said, then please I urge you provide that configuration, ideally that non-configuration. Else this is just going to take longer to analyze -- if if one ever picks it up. There's a inverse relation between the size of your config and the time it takes to solve a problem.

But fortunately, in the error backtrace you provided I'm starting to see glimpses of the cause for this. It has to do with the server asking Emacs/Eglot to watch files that don't exist anymore.

joaotavora commented 1 year ago

I still don't understand clearly what leads to this. There are instructions in "Reproducing the issue" and more instructions in "Backtrace". But when exactly does the error happen? I need to trace the steps you take exactly in my mind or in my reproduction of your experiment.

Please write the "steps taken:" like so:

  1. Start Emacs like this, with the configuration shown in this-or-that section, which I've tried to make as small as possible.
  2. Do step two
  3. M-x step-three
  4. Go to the shell and step four
  5. Back in Emacs C-x C-f file-of-step-five. Press enter.
  6. Shortly after I get the with the backtrace shown in section this-or-that
  7. The event log for all this process is attached in section that-or-this
VernonGrant commented 1 year ago

Ok, I updated the issue statement again. This video demonstration might also be helpful. Let me know if things are more clear now.

joaotavora commented 1 year ago

I'm still missing the full events buffer. It can't possible be just

[internal] Sun Apr  2 13:33:36 2023:
(: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 (WordPress-Starter-Theme/(php-mode phps-mode)) stderr finished

It also seems like a server bug to me. The server is asking to watch a directory that doesn't exist, and Emacs complains. But this shouldn't kill the connection. Maybe the server doesn't like the reply, but it shouldn't bail just because of that.

At least this is I can read in the minuscule portion of the Eglot events buffer that you posted. Can you post the full log for the second time you open Emacs? That's steps 8-10 in your recipe. Also please do consider not using eglot-ensure, as recommended. It's just a matter of typing M-x eglot after visiting the file, i.e. step 9.5 in that recipe would be "type M-x eglot".

eglot-ensure is known to be problematic, so I'd like to rule out that class of problems, i.e. know for sure if the problem happens with M-x eglot too.

VernonGrant commented 1 year ago

@joaotavora Noted, I updated the events buffer portion in the issue statement with the full log. And I also removed all eglot-ensure hooks from the config and updated the steps. I can confirm the same issue persists.

It also seems like a server bug to me. The server is asking to watch a directory that doesn't exist, and Emacs complains. But this shouldn't kill the connection. Maybe the server doesn't like the reply, but it shouldn't bail just because of that.

I see, but why does PHPactor, a different PHP LSP server have the exact same issue?

joaotavora commented 1 year ago

I see, but why does PHPactor, a different PHP LSP server have the exact same issue?

Maybe it uses the same logic? maybe one was inspired by the other, or maybe it uses the same code/library?

But after seeing the event log, I don't think the server is at fault here. It just asks to watch a "glob" and that is somewhat innocent. I think what happens is that the files that Emacs thinks are included in that glob isn't coming from the file system itself, but rather from project.el's idea of what the project files are.

So.... @nemethf was on to something from the beginning :-D

nemethf commented 1 year ago

So.... @nemethf was on to something from the beginning :-D

I had problems with project.el in the past, but my guess this time wasn't perfect as the recipe restarts Emacs.

At any rate, if I read the source correctly, project.el in project--vc-list-files collects the files of a project by basically executing git ls-files -c -o, which is probably much faster than actually examining a large directory tree. However, that command includes deleted files that are not yet committed.

I'm no git expert, so I cannot suggest a substitute command, but git ls-files -d lists the deleted files.

joaotavora commented 1 year ago

Maybe we can call @dgutov, maintainer of project.el here and ask him if project-files should return files that don't exist anymore.

If so, then a fix to Eglot is in order, and the matter seems easy to solve. Else, that fix can go in temporarily to eglot.el, but then if and when project.el fixes this, I don't know if the fix should stay, because theoretically we want Eglot to complain to the server if the file doesn't exist.

At any rate I think a server shouldn't do hara-kiri over an error report from the client, but that's another matter...

joaotavora commented 1 year ago

I've now fixed this in Emacs 29's Eglot, meaning it will make its way into GNU ELPA soon. In the meantime, @VernonGrant, you may try this patch:

commit 86cf9fd932c654cc502bedf634458a918ee5e9cb
Author: João Távora <joaotavora@gmail.com>
Date:   Sun Apr 2 23:01:29 2023 +0100

    Eglot: don't watch directories that don't exist

    project-files isn't guaranteed to return existing files, so better
    check if they exist because placing a watcher on them.

    Originally reported at:
    https://github.com/joaotavora/eglot/issues/1198

    * lisp/progmodes/eglot.el (eglot-register-capability
    workspace/didChangeWatchedFiles): Check if directories exist.

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 8f64f849d72..4a9209ab9b4 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -3433,8 +3433,9 @@ eglot-register-capability
       (unwind-protect
           (progn
             (dolist (dir dirs-to-watch)
-              (push (file-notify-add-watch dir '(change) #'handle-event)
-                    (gethash id (eglot--file-watches server))))
+              (when (file-readable-p dir)
+                (push (file-notify-add-watch dir '(change) #'handle-event)
+                      (gethash id (eglot--file-watches server)))))
             (setq
              success
              `(:message ,(format "OK, watching %s directories in %s watchers"
joaotavora commented 1 year ago

Thanks for the report! I'm closing this, but let me know if this doesn't fix it, in which case I'll reopen.

dgutov commented 1 year ago

Maybe we can call @dgutov, maintainer of project.el here and ask him if project-files should return files that don't exist anymore.

In theory, it shouldn't. In practice, deletion detection might be pretty costly.

E.g., in a larger project over here, the difference between git ls-files -cot >/dev/null and git ls-files -codt >/dev/null, is about 2x, which would slow down the full project-files call by about 1.5x, not to mention the more difficult parsing of the output.

This is definitely something to consider, but let's maybe see if most problems with this behavior can be solved by easier methods, and whether this comes up frequently enough. IIUC, this is a problem only when the file has been rm-d but not git rm-d.

Let's keep this discussion open somewhere: maybe file it with M-x report-emacs-bug.

joaotavora commented 1 year ago

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=62633 is the Emacs bug report.

VernonGrant commented 1 year ago

@joaotavora The patch is working just fine. Thanks for all the effort 🙏🏻

child404 commented 1 year ago

For me, executing project-forget-project on problematic project and re-opening the project did work, but I assume it's a temporarily solution

joaotavora commented 1 year ago

This problem is fixed/worked around in Eglot 1.14. you can add more information about the project.el problem by sending email to the bug tracker, https://debbugs.gnu.org/cgi/bugreport.cgi?bug=62633