Skip to content

Make the remote experience easier #3018

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

Closed
toshokan opened this issue Jul 26, 2021 · 4 comments · Fixed by #4204
Closed

Make the remote experience easier #3018

toshokan opened this issue Jul 26, 2021 · 4 comments · Fixed by #4204

Comments

@toshokan
Copy link

As the docs mention, if we want to use lsp-mode over TRAMP, we have to register a new client with :remote t.
From crawling other people's dotfiles, it seems like the way people do this is:

  1. Find the client you normally use locally in the lsp-mode client sources
  2. Copy it into your emacs config
  3. add remote: t
  4. change lsp-stdio-connection to lsp-tramp-connection in :new-connection
  5. append -remote to the server id

If #2531 is merged, lsp-tramp-connection will be very similar to lsp-stdio-connection, since the logic used to determine whether the process is started locally or remotely is encapsulated in make-processalready via the :file-handler t arg.
These functions may then be candidates for merging into a single function.

Currently, in my config, I have

(defun tkn/make-remote-lsp-client (original-server-id new-server-id new-connection)
  (let* ((client (copy-lsp--client (gethash original-server-id lsp-clients))))
    (setf (lsp--client-new-connection client) new-connection)
    (setf (lsp--client-remote? client) t)
    (setf (lsp--client-server-id client) new-server-id)
    (lsp-register-client client)))

Then for servers I want to use remotely, say rust-analyzer, I can do

(tkn/make-remote-lsp-client
 'rust-analyzer
 'rust-analyzer-remote
 (lsp-tramp-connection (lambda () lsp-rust-analyzer-server-command)))

The new-server-id can easily be auto-generated from the non-remote server id according to a pattern.
If, once #2531 is merged, we merge lsp-stdio-connection and lsp-tramp-connection into a single function, an even nicer interface can be provided out of the box.

lsp-mode could provide an interface where users simply list the server-ids they also want to use remotely (and maybe any lsp--client config overrides they want to apply to the remote version) and the remote clients could be autogenerated.

This would reduce duplication from people copying clients from the lsp-mode sources and not updating them, allow fixes to local clients to be automatically integrated into remote clients, and make using lsp-mode over TRAMP easier out of the box since now, in the best case, only configuration is required rather than writing or copying elisp to get it to work.

Another option, if lsp-[stdio/tramp]-connection become a single function, is to just drop the :remote slot on lsp--client entirely, allow all clients to be used remotely by default, and let the :file-handler/default-directory determine which should be started. This might lead to an even better user experience, but might be cumbersome if different configurations are desired for remote and local clients.

I can put together a PR if this sounds interesting to the project members, but I wanted to get some feedback on which option is preferable

@ericdallo
Copy link
Member

That makes sense to me, actually, @yyoncho suggested something like that last week

@yyoncho
Copy link
Member

yyoncho commented Jul 26, 2021

I like your proposal, this is something that I was planning to implement. I want to alter the signature, to allow calling the function like this:

(tkn/make-remote-lsp-client 'rust-analyzer
 'rust-analyzer-remote
 (lsp-tramp-connection (lambda () lsp-rust-analyzer-server-command))
 :foo-bar-client-property new-value)

We can even make it a bit more generic:

(lsp-register-remote-client 
 'clangd
 :server-id 'cland-remote
 :new-connection ...)

Another option, if lsp-[stdio/tramp]-connection become a single function, is to just drop the :remote slot on lsp--client entirely, allow all clients to be used remotely by default, and let the :file-handler/default-directory determine which should be started. This might lead to an even better user experience, but might be cumbersome if different configurations are desired for remote and local clients.

I didn't go in that direction initially due to the fact that some clients need a specific configuration when they are run remotely. E. g. lsp-java has several paths that are local and they will crash the server if they do not exist on the remote server.

@toshokan
Copy link
Author

toshokan commented Jul 26, 2021

@yyoncho I think that signature is nice. What do you think of an implementation like

(defun lsp-register-remote-client (based-on &rest args)
  ;; Create a copy of based-on client
  (let ((client (copy-lsp--client (gethash based-on lsp-clients))))
    ;; Set a default server id and set remote
    (setf (lsp--client-server-id client) (make-symbol (format "%s-remote" based-on)))
    (setf (lsp--client-remote? client) t)
    (cl-loop for (prop val) on args by 'cddr do
             (let* ((slot-sym (intern (substring (symbol-name prop) 1)))
                    (slot (ignore-error 'cl-struct-unknown-slot
                            (cl-struct-slot-value 'lsp--client slot-sym client))))
               (when slot
                 (setf slot val))))
    (lsp-register-client client)))

This would let you do (lsp-register-remote-client 'ruby-ls) to define 'ruby-ls-remote identically to the local 'ruby-ls, and

(lsp-register-remote-client 'rust-analyzer
  :server-id 'rust-analyzer-custom-remote-name
  :new-command (lsp-tramp-connection (lambda () lsp-rust-analyzer-server-command)))

to define 'rust-analyzer-custom-remote-name, with :new-command overridden.

Replicating the constructor's cl-style keyword args is a little awkward.

@ericdallo
Copy link
Member

@toshokan looks good to me, maybe you can open a PR and we can discuss there, WDYT?

yyoncho added a commit to yyoncho/lsp-mode that referenced this issue Oct 27, 2023
- This fixes the implementation of `lsp-mode` tramp support. After this PR the
remote clients will be automatically registered and in most of the cases it will
work out of the box. The remote connection is managed to a way similar to what
eglot does.

Fixes emacs-lsp#4158
Fixes emacs-lsp#4150
Fixes emacs-lsp#4158
Fixes emacs-lsp#4150
Fixes emacs-lsp#3841
Fixes emacs-lsp#3642
Fixes emacs-lsp#3579
Fixes emacs-lsp#3530
Fixes emacs-lsp#3491
Fixes emacs-lsp#3490
Fixes emacs-lsp#3391
Fixes emacs-lsp#3369
Fixes emacs-lsp#3364
Fixes emacs-lsp#3020
Fixes emacs-lsp#3018
Fixes emacs-lsp#3020
yyoncho added a commit to yyoncho/lsp-mode that referenced this issue Oct 30, 2023
- This fixes the implementation of `lsp-mode` tramp support. After this PR the
remote clients will be automatically registered and in most of the cases it will
work out of the box. The remote connection is managed to a way similar to what
eglot does.

Fixes emacs-lsp#4158
Fixes emacs-lsp#4150
Fixes emacs-lsp#4158
Fixes emacs-lsp#4150
Fixes emacs-lsp#3841
Fixes emacs-lsp#3642
Fixes emacs-lsp#3579
Fixes emacs-lsp#3530
Fixes emacs-lsp#3491
Fixes emacs-lsp#3490
Fixes emacs-lsp#3391
Fixes emacs-lsp#3369
Fixes emacs-lsp#3364
Fixes emacs-lsp#3020
Fixes emacs-lsp#3018
Fixes emacs-lsp#3020
@yyoncho yyoncho closed this as completed in d45aca0 Nov 1, 2023
yyoncho added a commit to yyoncho/lsp-mode that referenced this issue Nov 2, 2023
* Fix lsp-mode's tramp support

- This fixes the implementation of `lsp-mode` tramp support. After this PR the
remote clients will be automatically registered and in most of the cases it will
work out of the box. The remote connection is managed to a way similar to what
eglot does.

Fixes emacs-lsp#4158
Fixes emacs-lsp#4150
Fixes emacs-lsp#4158
Fixes emacs-lsp#4150
Fixes emacs-lsp#3841
Fixes emacs-lsp#3642
Fixes emacs-lsp#3579
Fixes emacs-lsp#3530
Fixes emacs-lsp#3491
Fixes emacs-lsp#3490
Fixes emacs-lsp#3391
Fixes emacs-lsp#3369
Fixes emacs-lsp#3364
Fixes emacs-lsp#3020
Fixes emacs-lsp#3018
Fixes emacs-lsp#3020

* Use executable-find with remote = t everywhere
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 a pull request may close this issue.

3 participants