ruby-lsp icon indicating copy to clipboard operation
ruby-lsp copied to clipboard

Add file watching in the server as a fallback for editors that don't support it

Open vinistock opened this issue 1 year ago • 11 comments

We ask editors to watch Ruby files for us here, which we use to update the codebase index upon modifications.

However, the registration may fail in two ways: some editors don't support dynamic feature registration and some don't support file watching. In those cases, only the declarations from the original indexing will be available since we will never receive the modification events.

We can use the listen gem to provide a fallback and listen to modifications from the server when file watching is not available.

This is suboptimal from a performance standpoint since having the editor watch files means that it can broadcast the modification events to all parties interested in handling those - as opposed to having each server watch files on their own. However, it's better than never updating the index.

Questions

File watching is available for VS Code. Is it also available for other popular editors such as NeoVim, Emacs, Sublime?

Implementation suggestion

My suggestion is adding an else statement where we register for file watching. In the else branch, we would:

  • Require the listen gem
  • Use listen to register the callbacks for when files are modified

We should evaluate if there's any way to share the current handler for modifications, with the one using listen.

vinistock avatar Mar 05 '24 16:03 vinistock

Neovim supports file watching, but doesn't support dynamic registration:

      workspace = {
        applyEdit = true,
        configuration = true,
        didChangeWatchedFiles = {
          dynamicRegistration = false,
          relativePatternSupport = true
        },
        semanticTokens = {
          refreshSupport = true
        },
        symbol = {
          dynamicRegistration = false,
          hierarchicalWorkspaceSymbolSupport = true,
          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 }
          }
        },
        workspaceEdit = {
          resourceOperations = { "rename", "create", "delete" }
        },
        workspaceFolders = true
      }

Is it possible to register it standard way? Would it make sense to update indexes on didSave in case when file watching is not possible?

bjarosze avatar Apr 19 '24 06:04 bjarosze

My understanding is that there are only two types of registration: static and dynamic. The static registrations are basically the response of the initialize request, which is composed of the server capabilities - indicating what the server supports and needs.

Dynamic registrations happen as notifications sent by the server to the client to enable some functionality.

Based on the specification, there does not seem to be a way to ask for file watching using static registrations.

Would it make sense to update indexes on didSave in case when file watching is not possible?

We do want to index files even before they are saved (see https://github.com/Shopify/ruby-lsp/issues/1908). That certainly helps, but unfortunately it's not enough because files can be modified outside of the editor context, which means we'd fail to track those modifications.

Consider the case of switching git branches. When you switch, several files may be added, changed or deleted because of the state differences. With file watching, the LSP receives notifications about all of those modifications and we can update the index to reflect it. Same story if you run something like RuboCop on the terminal, the only way to know about those is through file watching. But didSave or didChange notifications don't capture this type of thing, which means the index would go stale.

vinistock avatar Apr 22 '24 17:04 vinistock

Thanks for clarification.

I've double checked if neovim supports file watching and it indeed does, but it's disabled by default. When I enabled it, it works as expected.

bjarosze avatar Apr 22 '24 18:04 bjarosze

@bjarosze : How did you enable file watching in neovim? Via :set noautoread / vim.opt.autoread = true, or via a plugin?

miguno avatar Oct 30 '24 10:10 miguno

If it requires changing some configuration, that would be a great contribution for our docs.

vinistock avatar Oct 30 '24 12:10 vinistock

I only tested it briefly, I not use it, so can't tell how well it works.

The setting:

capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true

Whole

      local capabilities = vim.lsp.protocol.make_client_capabilities()
      capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true

      require("lspconfig").ruby_lsp.setup({
        capabilities = capabilities,
        init_options = {
          formatter = 'auto',
          enabledFeatures = {
            "codeActions",
            "diagnostics",
            "documentHighlights",
            "documentLink",
            "documentSymbols",
            "foldingRanges",
            "formatting",
            "hover",
            "inlayHint",
            "selectionRanges",
            "completion",
            "codeLens",
            "definition",
            "workspaceSymbol",
            "signatureHelp"
          }
        },
      })

I use my version of ruby-lsp that updates on save: here is diff https://github.com/bjarosze/ruby-lsp/commit/b64a73e3719da27c542aff948d798376dd6fc6ee#diff-839ccc66503b74e2992f46cfc787178a068f33d77c9c90e4a0da4b91ca06219e

changes in lib/ruby_lsp/server.rb

bjarosze avatar Oct 31 '24 09:10 bjarosze

For future readers, the below line from the above post fixed a duplicate indexing issue that I had for a long time:

capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true

jacobdaddario avatar Dec 16 '24 18:12 jacobdaddario

@jacobdaddario is it worth documenting it in editors?

vinistock avatar Dec 16 '24 18:12 vinistock

I was just thinking about that. I'm at work, so I can't do it right now. I can do it later though.

In general, Vim is always a choose your own adventure so there's an expectation of the programmer to do it themselves. That said, when it comes to Lazy, there's that whole set of presets. I think it'd be great for an ideal configuration to be added to the Ruby Lazy Extra. That'd be a fantastic experience rather than folks having to do it themselves.

EDIT: Never mind, I still get multiple results after reindexing occurs.

jacobdaddario avatar Dec 16 '24 19:12 jacobdaddario

capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true

This is the default for Neovim 0.10, as far as I'm aware.

I'm also seeing the duplicate indexing issue. This didn't used to happen for me earlier in 2024 (sadly can't remember what versions I was on).

I have since tried older Neovim version as well as using older ruby-lsp versions; they all had the same issues though, oddly.

RichGuk avatar Jan 09 '25 14:01 RichGuk

I've had better success setting this in Neovim:

:set backupcopy=yes

RichGuk avatar Feb 04 '25 12:02 RichGuk