Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LSP server #475

Merged
merged 3 commits into from
Dec 3, 2022
Merged

LSP server #475

merged 3 commits into from
Dec 3, 2022

Conversation

will
Copy link
Contributor

@will will commented Oct 26, 2022

This adds an LSP server that runs with the flag --lsp. It does the lsp jsonrpc over stdio. Functionality wise, this works for everything I need. I've only tested with neovim 0.8 though. It only operates on documents that you open, rather than going through your entire project looking for ruby files.

Test coverage isn't great, but I think they cover the major parts:

  • telling the lsp client (an editor) on init what the server supports and to send full documents instead of patches
  • getting diagnostics
  • formatting

I'm okay with this level of testing to start, but I'd understand if you weren't.

I think this is a good starting point for functionality, but there some improvements left on the table:

If several changes come in while it was doing some other work, the stdin reader could collapse the changes and only take the last full change.

Editors could just send the small changes on each change, but then the server would have to be able to patch those into the copy it has in memory. Not impossible, but defiantly a lot more work. Would also make the previous collapsing improvement not necessary.

Format results could also be a series of patches rather than the entire document.

It could go through and find all the files in your workspace, but idk, I think just working on open files is good enough.

It might be possible to do the diagnostics and formatting in memory rather than bouncing through a tempfile. The solargraph moneky patch I was using before did this originally, but it stopped working and I couldn't figure out why, which lead me down to writing a small lsp form scratch in the first place.

Also please let me know what sort of git etiquette you'd like for changes on the PR. rebasing into the original commit or fixup additional patches or whatnot.

closes #472

@searls
Copy link
Contributor

searls commented Oct 26, 2022

Thanks! Will review soon. I'd encourage you to use whatever git technique you like

@searls
Copy link
Contributor

searls commented Nov 1, 2022

I dig your code style! Thanks for tidying a few things up.

What's the bare minimum setup I could use to test this actually working for myself locally?

@will
Copy link
Contributor Author

will commented Nov 1, 2022

Thanks :)

The most minimal config for neovim 0.8+ would be this, with replacing the path:

-- /tmp/init.lua
vim.opt.signcolumn = "yes" -- otherwise it bounces in and out, not strictly needed though
vim.api.nvim_create_autocmd("FileType", {
  pattern = "ruby",
  group = vim.api.nvim_create_augroup("RubyLSP", { clear = true }), -- also this is not /needed/ but it's good practice 
  callback = function()
    vim.lsp.start {
      name = "standard",
      cmd = { "/Users/will/code/standard/exe/standardrb", "--lsp" },
    }
  end,
})

Then you can edit a ruby file with it nvim -u /tmp/init.lua a.rb

It should auto do the in-line virtual text for diagnostics (which I find too noisy to have on all the time personally). Then to format you can do :lua vim.lsp.buf.formatting() which you would want to map to something like <leader>lf with an on_attach function, but it is not minimal to do that ;)

@foca
Copy link
Contributor

foca commented Nov 14, 2022

Been playing with this on a personal project and it's fantastic 😄❤️

(Beyond the setup code on that vim config file, I also added an autocmd to format the code on BufWrite that works instantly :))

On a different computer I would notice that sometimes reading from the
tempfile right after writing would result in an empty string, so force a
flush to make sure that can never happen.
@standardrb standardrb deleted a comment from will Dec 3, 2022
@searls
Copy link
Contributor

searls commented Dec 3, 2022

Looks great to me, thanks @will!

lsp-demo

@searls searls merged commit cafe199 into standardrb:main Dec 3, 2022
@searls
Copy link
Contributor

searls commented Dec 3, 2022

Landed in v1.19.0! Great work, @will. I'm super grateful for your initiative, your contribution, and your patience. I'm also really impressed with how well you structured your code similarly to what was already in place!

@will
Copy link
Contributor Author

will commented Dec 3, 2022

Thanks for the kind words and for merging it :)

croaky added a commit to croaky/laptop that referenced this pull request Dec 5, 2022
croaky added a commit to croaky/laptop that referenced this pull request Dec 5, 2022
@bluz71
Copy link

bluz71 commented Jan 4, 2023

I was using Standard Ruby via null-ls inside Neovim. I just switched over from that to the new LSP option, and the difference is incredible.

From very slow, to near instant. Wow!

null-ls invokes the standardrb command line on each edit and the lag is very noticeable; up to 2 seconds (sometimes more).

The new --lsp option on the other-hand is near realtime; I never thought that was possible for anything related to Rubocop. But seeing is believing.

This feature needs to be advertised wide and far; especially integration into nvim-lspconfig.

From I have seen, this simple LSP addition easily competes with Shopify's Ruby LSP which appears to be focussed on diagnostics and formatting, but not completion (similar to this new --lsp option).

Note, I still use Solargraph for code completion and navigation (whilst disabling diagnostics and formatting). Two LSP's running at the same time, with no overlap, works just fine.

Great work @will and @searls.

@will
Copy link
Contributor Author

will commented Jan 4, 2023

Thanks @bluz71 it's very nice to hear that you like it :)

koic added a commit to koic/rubocop that referenced this pull request Jun 12, 2023
Resolves rubocop#11686

This commit supports built-in language server.

The integrated language server feature is described in the docs/modules/ROOT/pages/usage/lsp.adoc.

## LSP option

```console
% bundle exec rubocop --help
(snip)

LSP Option:
        --lsp                        Start a language server listening on STDIN.
```

The language server `--lsp` is disabled by default because it is not yet
stable as an integrated feature.

Run `rubocop --lsp` command from LSP client.

When the language server is started, the command displays the language server's PID:

```console
$ ps aux | grep rubocop
user             17414   0.0  0.2  5557716 144376   ??  Ss    4:48PM   0:02.13 /Users/user/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bin/rubocop --lsp
```

The language server supports `textDocument/formatting` method and is autocorrectable. The autocorrection is safe.

## Design note

Same concept as standardrb/standard#475. See there for details.

## Usage Example (Eglot)

I'm running the LSP on Emacs with Eglot. It behaves roughly as expected.
https://github.com/joaotavora/eglot

Add the following to your Emacs configuration file (e.g. `~/.emacs.d/init.el`):

```lisp
(require 'eglot)

(add-to-list 'eglot-server-programs '(ruby-mode . ("bundle" "exec" "rubocop" "--lsp")))
(add-hook 'ruby-mode-hook 'eglot-ensure)
```

Below is an example of additional setting for autocorrecting on save:

```lisp
(add-hook 'ruby-mode-hook (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil 'local)))
```

In theory it should be available for other LSP clients as well.
bbatsov pushed a commit to rubocop/rubocop that referenced this pull request Jun 12, 2023
Resolves #11686

This commit supports built-in language server.

The integrated language server feature is described in the docs/modules/ROOT/pages/usage/lsp.adoc.

## LSP option

```console
% bundle exec rubocop --help
(snip)

LSP Option:
        --lsp                        Start a language server listening on STDIN.
```

The language server `--lsp` is disabled by default because it is not yet
stable as an integrated feature.

Run `rubocop --lsp` command from LSP client.

When the language server is started, the command displays the language server's PID:

```console
$ ps aux | grep rubocop
user             17414   0.0  0.2  5557716 144376   ??  Ss    4:48PM   0:02.13 /Users/user/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bin/rubocop --lsp
```

The language server supports `textDocument/formatting` method and is autocorrectable. The autocorrection is safe.

## Design note

Same concept as standardrb/standard#475. See there for details.

## Usage Example (Eglot)

I'm running the LSP on Emacs with Eglot. It behaves roughly as expected.
https://github.com/joaotavora/eglot

Add the following to your Emacs configuration file (e.g. `~/.emacs.d/init.el`):

```lisp
(require 'eglot)

(add-to-list 'eglot-server-programs '(ruby-mode . ("bundle" "exec" "rubocop" "--lsp")))
(add-hook 'ruby-mode-hook 'eglot-ensure)
```

Below is an example of additional setting for autocorrecting on save:

```lisp
(add-hook 'ruby-mode-hook (lambda () (add-hook 'before-save-hook 'eglot-format-buffer nil 'local)))
```

In theory it should be available for other LSP clients as well.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do you want a LSP server in this project?
4 participants