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

TRAMP support #637

Closed
wants to merge 9 commits into from
Closed

TRAMP support #637

wants to merge 9 commits into from

Conversation

bjc
Copy link
Contributor

@bjc bjc commented Mar 3, 2021

This patch adds TRAMP support to eglot (fixes #84)

I have seen #463 but decided to have another go at it. Some differences include:

  • This patchset includes a test for TRAMP functionality (which doesn't require passwordless ssh to a remote host!)
  • I've copied in Emacs 27.1's executable-find to be used on older Emacsen. 27.1 and later will use the native definition.
  • I have not included any special instructions for TRAMP. I do not believe they are necessary. Things work as expected (at least as I expect them to: you define a custom path, it'll be used on the remote. If you don't, it uses your PATH).
  • I have made server a mandatory argument to eglot--uri-to-path, because I feel it's less error-prone than having a default. Instead, I've passed in the default where necessary.

The one thing I'm not super-keen on in this patch is the use of stty when creating the process for the LSP. I don't know any other way to do it, though, and it seems to be how it's done from what I could dig up. It does feel like a bug in Emacs itself, though, that it should be necessary.

bjc added 3 commits March 2, 2021 16:13
  * Copy in Emacs 27.1’s ‘executable-find’ implementation, so it can
  be used on older Emacsen, where we need the second argument to tell
  it to look for remote commands.

  * Adjust a number of functions to accomodate swapping between local
  and remote paths as appropriate.

  * Force unbuffered I/O on LSP subprocesses to prevent hanging during
  JSONRPC calls.
@joaotavora
Copy link
Owner

joaotavora commented Mar 3, 2021

Interesting! This looks very good!

I've copied in Emacs 27.1's executable-find to be used on older Emacsen. 27.1 and later will use the native definition.

If this works, then your executable-find shim could maybe be put in a GNU Elpa package executable-find.el. But for now it works, and it's not too much code, so no prob.

I have made server a mandatory argument to eglot--uri-to-path, because I feel it's less error-prone than having a default. Instead, I've passed in the default where necessary.

I'm not keen on this, but I can be convinced, if you explain why you "feel" it's less error-prone. In things run in the same stack frame, using a special variable should be just as effective.

The one thing I'm not super-keen on in this patch is the use of stty

Yes, but I like that you commented it and isolated it. I don't fully understand it, but that's OK. Can you make a bug report to Emacs and link the resulting discussion thread here?

eglot-tests.el Outdated Show resolved Hide resolved
eglot-tests.el Outdated Show resolved Hide resolved
eglot-tests.el Outdated Show resolved Hide resolved
eglot-tests.el Outdated Show resolved Hide resolved
eglot-tests.el Outdated Show resolved Hide resolved
eglot.el Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
eglot.el Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
  * `eglot--executable-find` is bound at run-time based on
  `emacs-major-version`.

  * TRAMP test calls into `auto-detect-running-server` directly, after
  setting `temporary-file-directory` to the loopback method.

  * Conditionalized aspects of calling `make-process` on whether or
  not the current file is remote:
    - Use of `stty` to ensure raw I/O, and
    - assuming Emacs > 27, appending `:file-handler t` to arguments.

  * Removed `server` argument to `eglot--uri-to-path`, opting instead
  to always call `eglot--current-server-or-lose` to determine
  mappings.

  * Miscellaneous whitespace changes.

  * Renamed `test` TRAMP method in test to `loopback` to better match
  what it’s doing.
Copy link
Owner

@joaotavora joaotavora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking really good. I'm going to make a commit, which is easier to show you the changes I mean, and then you can test and do further adjustments, OK?

eglot.el Outdated Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
eglot.el Outdated Show resolved Hide resolved
@bjc
Copy link
Contributor Author

bjc commented Mar 3, 2021

Sure thing. Let me know when to pull your changes.

Tests seem to all pass, though this is a bit odd:

    Tramp: Sending command ‘exec /bin/sh ’
    Tramp: Found remote shell prompt on ‘krug’

    Tramp: Sending command ‘exec /bin/sh ’
    Tramp: Found remote shell prompt on ‘krug’
    [eglot] Test body was A FAILURE
    [eglot] Killing nil, wiping /loopback:krug:/tmp/eglot--fixturefBjpKc, restoring nil
    Ran 1 tests, 0 results were as expected, 1 unexpected
   passed  10/43  eglot--tramp-test (0.518254 sec)

Either that "A FAILURE" is being misreported, or this must be tested
manually, to make sure.
@joaotavora
Copy link
Owner

There. I've simplified this. Please test. We probably still ahve to special-case the test to only run in > Emacs 27.1. Then I'll do the commit message and squash adjustments, crediting you foremost, of course.

Thanks, this really looks like the lean, simple TRAMP support PR I had been waiting for.

@joaotavora
Copy link
Owner

Oh no, I commited a bunch of useless files I had lying here. hehehe time to force push the branch!

* README.md (TRAMP support): New subsection.

* NEWS.md: Menation TRAMP support
@joaotavora
Copy link
Owner

Done.

@joaotavora joaotavora requested a review from muffinmad March 3, 2021 17:52
eglot.el Show resolved Hide resolved
eglot.el Show resolved Hide resolved
eglot.el Show resolved Hide resolved
@bjc
Copy link
Contributor Author

bjc commented Mar 3, 2021

Just pulled the latest from you and everything looks good on my end.

Regarding the test, it's probably best to:

(skip-unless (and (executable-find "pyls") (>= emacs-major-version 27)))

@joaotavora
Copy link
Owner

Regarding the test, it's probably best to:

yes, exactly

But did you test manually and play around a little bit with tramp? In Emacs 27.1? Are the simplistic instructions in the README.md correct?

@bjc
Copy link
Contributor Author

bjc commented Mar 3, 2021

The README seems fine to me. I haven't done any special setup for my TRAMP buffers. I have done some light testing with xref style stuff and variable/method renaming, and it seems to work well.

I do not have Emacs 27 installed, unfortunately. I'm running 28. I'll have to set up a container with 27 to verify it there.

@joaotavora
Copy link
Owner

Great.

From what I remember from the investigations of #463, I strongly suspect it won't work with Emacs 27.1, so maybe the conditions have to be tweaked But that's fine, really. Simplicity trumps it.

@bjc
Copy link
Contributor Author

bjc commented Mar 3, 2021

I've just run some tests against Emacs 27.1, and haven't run into any issues.

@joaotavora
Copy link
Owner

I've just run some tests against Emacs 27.1, and haven't run into any issues.

That's even more fantastic. To think this years-old request was so few lines of code away...

Finally, can you double-check that eglot--cmd is really needed?

@bjc
Copy link
Contributor Author

bjc commented Mar 3, 2021

I've just run some tests against Emacs 27.1, and haven't run into any issues.

That's even more fantastic. To think this years-old request was so few lines of code away...

To be fair, it does rely on features in 27.1 to do it as simply as it does.

Finally, can you double-check that eglot--cmd is really needed?

I have checked this so many times, because I hate it. I've just tried again without it, in the vain hope that maybe something miraculous has happened, but no, it's still very definitely required.

I would love to get rid of it, but I think it's going to take some work within Emacs itself to do so. Definitely a project I'm keen on when I get some time for it.

The test may be good, but it was running another instance of ert
inside it, meaning it would always pass, I think. It's better to have
both the normal and the TRAMP test call a function.  Then we can see
that the TRAMP automatic test is failing.

We have to understand why.
Copy link
Contributor Author

@bjc bjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is still passing for me. Is it failing for you?

@joaotavora
Copy link
Owner

This test is still passing for me. Is it failing for you?

Yes, last I checked. Curious. Are you sure you deleted your elc's and and ran a simple make check EMACS=whatever/emacs/you/want/src/emacs ?

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

I was testing in-Emacs with M-x ert, but I've since tried with make check with Emacs 27.1, and it still passes.

That said, I'm also using jedi-language-server for python, not pyls. I was never able to make pyls work with eglot (even before my changes) for reasons I never dug into, so maybe that's the issue here?

@joaotavora
Copy link
Owner

But the tests are running pyls, unless you have changed them. If the tests are passing then pyls must somehow be working on your machine. Here is the output I get around that single failing TRAMP test:

   passed   9/43  eglot--glob-test (0.034668 sec)
Tramp: Sending command ‘exec /bin/sh ’
Tramp: Found remote shell prompt on ‘krug’


Tramp: Sending command ‘exec /bin/sh ’
Tramp: Found remote shell prompt on ‘krug’
[eglot] Test body was A FAILURE
[eglot] Killing nil, wiping /loopback:krug:/tmp/eglot--fixtureQsFHHj, restoring nil
Test eglot--tramp-test backtrace:
  signal(wrong-type-argument ("inserted-chars 23"))
  tramp-signal-hook-function(wrong-type-argument ("inserted-chars 23"))
  signal(wrong-type-argument ("inserted-chars 23"))
  tramp-handle-insert-file-contents("/loopback:krug:/tmp/tramp.VKkVY4" nil nil nil nil)
  apply(tramp-handle-insert-file-contents ("/loopback:krug:/tmp/tramp.VKkVY4" nil nil nil nil))
  tramp-sh-file-name-handler(insert-file-contents "/loopback:krug:/tmp/tramp.VKkVY4" nil nil nil nil)
  apply(tramp-sh-file-name-handler insert-file-contents ("/loopback:krug:/tmp/tramp.VKkVY4" nil nil nil nil))
  tramp-file-name-handler(insert-file-contents "/loopback:krug:/tmp/tramp.VKkVY4" nil nil nil nil)
  insert-file-contents("/loopback:krug:/tmp/tramp.VKkVY4" nil nil nil nil)
  insert-file-contents-literally("/loopback:krug:/tmp/tramp.VKkVY4")
  tramp-sh-handle-make-process(:name "EGLOT (project/python-mode)" :command ("sh" "-c" "stty raw > /dev/null; pyls") :connection-type pipe :coding utf-8-emacs-unix :noquery t :stderr #<buffer *EGLOT (
  apply(tramp-sh-handle-make-process (:name "EGLOT (project/python-mode)" :command ("sh" "-c" "stty raw > /dev/null; pyls")
:connection-type pipe :coding utf-8-emacs-unix :noquery t :stderr #<buffer *
  tramp-sh-file-name-handler(make-process :name "EGLOT (project/python-mode)" :command ("sh" "-c" "stty raw > /dev/null; pyls") :connection-type pipe :coding utf-8-emacs-unix :noquery t :stderr #<buff
  apply(tramp-sh-file-name-handler make-process (:name "EGLOT (project/python-mode)" :command ("sh" "-c" "stty raw > /dev/null; pyls") :connection-type pipe :coding utf-8-emacs-unix :noquery t :stderr
  tramp-file-name-handler(make-process :name "EGLOT (project/python-mode)" :command ("sh" "-c" "stty raw > /dev/null; pyls") :connection-type pipe :coding utf-8-emacs-unix :noquery t :stderr #<buffer
  make-process(:name "EGLOT (project/python-mode)" :command ("sh" "-c" "stty raw > /dev/null; pyls") :connection-type pipe :coding utf-8-emacs-unix :noquery t :stderr #<buffer *EGLOT (project/python-m
  #f(compiled-function () #<bytecode 0x4cc6b877eb18e4f>)()
  #f(compiled-function (cl--cnm conn slots) #<bytecode 0x1e62ad5e5cb986fa>)(#f(compiled-function (&rest cnm-args) #<bytecode 0x69d8e578fbe01b9>) #<eglot-lsp-server eglot-lsp-server-155d58fb815c> (:nam
  apply(#f(compiled-function (cl--cnm conn slots) #<bytecode 0x1e62ad5e5cb986fa>) #f(compiled-function (&rest cnm-args) #<bytecode 0x69d8e578fbe01b9>) (#<eglot-lsp-server eglot-lsp-server-155d58fb815c
  #f(compiled-function (&rest args) #<bytecode -0x614c7f3b221184a>)(#<eglot-lsp-server eglot-lsp-server-155d58fb815c> (:name "EGLOT (project/python-mode)" :events-buffer-scrollback-size 2000000 :notif
  apply(#f(compiled-function (&rest args) #<bytecode -0x614c7f3b221184a>) #<eglot-lsp-server eglot-lsp-server-155d58fb815c>
(:name "EGLOT (project/python-mode)" :events-buffer-scrollback-size 2000000
  initialize-instance(#<eglot-lsp-server eglot-lsp-server-155d58fb815c> (:name "EGLOT (project/python-mode)" :events-buffer-scrollback-size 2000000 :notification-dispatcher #f(compiled-function (serve
  #f(compiled-function (class &rest slots) "Default constructor for CLASS `eieio-default-superclass'.\nSLOTS are the initialization slots used by `initialize-instance'.\nThis static method is called w
  apply(#f(compiled-function (class &rest slots) "Default constructor for CLASS `eieio-default-superclass'.\nSLOTS are the initialization slots used by `initialize-instance'.\nThis static method is ca
  make-instance(eglot-lsp-server :name "EGLOT (project/python-mode)" :events-buffer-scrollback-size 2000000 :notification-dispatcher #f(compiled-function (server method params) #<bytecode -0x12e904da4
  apply(make-instance eglot-lsp-server :name "EGLOT (project/python-mode)" :events-buffer-scrollback-size 2000000 :notification-dispatcher #f(compiled-function (server method params) #<bytecode -0x12e
  eglot--connect(python-mode (transient . "/loopback:krug:/tmp/eglot--fixtureQsFHHj/project/") eglot-lsp-server ("pyls"))
  apply(eglot--connect (python-mode (transient . "/loopback:krug:/tmp/eglot--fixtureQsFHHj/project/") eglot-lsp-server ("pyls")))
  eglot--tests-connect()
  #f(compiled-function () #<bytecode -0xb371af455e99614>)()
  eglot--call-with-fixture((("project" ("coiso.py" . "bla") ("merdix.py" . "bla")) ("anotherproject" ("cena.py" . "bla"))) #f(compiled-function () #<bytecode -0xb371af455e99614>))
  eglot-tests---auto-detect-running-server-1()
  #f(compiled-function () #<bytecode 0x19adf36146819a9f>)()
  ert--run-test-internal(#s(ert--test-execution-info :test ... :result ... :exit-continuation #f(compiled-function () #<bytecode 0xa3786383dad583>) :next-debugger debug :ert-debug-on-error nil))
  ert-run-test(#s(ert-test :name eglot--tramp-test :documentation "Ensure LSP servers can be used o..." :body #f(compiled-function () #<bytecode 0x19adf36146819a9f>) :most-recent-result #s(ert-test-fa
  ert-run-or-rerun-test(#s(ert--stats :selector t :tests ... :test-map #<hash-table eql 43/43 0x155d58f115bd> :test-results
... :test-start-times ... :test-end-times ... :passed-expected 7 :passed-une
  ert-run-tests(t #f(compiled-function (event-type &rest event-args) #<bytecode -0x78e624d0b6f97a5>) nil)
  ert-run-tests-batch(t)
  ert-run-tests-batch-and-exit(t)
  eval((ert-run-tests-batch-and-exit 't) t)
  command-line-1(("-f" "package-initialize" "-L" "." "-l" "eglot" "-l" "eglot-tests" "--eval" "(setq ert-batch-backtrace-right-margin 200)" "--eval" "(ert-run-tests-batch-and-exit (quote t))"))
  command-line()
  normal-top-level()
Test eglot--tramp-test condition:
    (wrong-type-argument "inserted-chars 23")
   FAILED  10/43  eglot--tramp-test (0.578749 sec)
   passed  11/43  eglot-capabilities (0.000092 sec)

@joaotavora
Copy link
Owner

Curiously, on Emacs 27, my failure is entirely different:

   passed   9/43  eglot--glob-test (0.067442 sec)


[eglot] Test body was A FAILURE
[eglot]  *EGLOT (project/python-mode) output*:
[eglot]  *EGLOT (project/python-mode) stderr*:
sh: 2: pyls: not found
[eglot] *EGLOT (project/python-mode) events*:
[eglot] Killing nil, wiping /loopback:krug:/tmp/eglot--fixtureLn3crZ, restoring nil
Test eglot--tramp-test backtrace:
  process-send-string(#<process EGLOT (project/python-mode)> "Content-Length: 1780\15\n\15\n{\"jsonrpc\":\"2.0\",\"id\":1,\"m...")
  #f(compiled-function (arg1 &rest rest) "Send MESSAGE, a JSON object, to CONNECTION." #<bytecode 0x1568a8fd8151>)(#<eglot-lsp-server eglot-lsp-server-1568a8f587a8> :id 1 :method :initialize :params (
  apply(#f(compiled-function (arg1 &rest rest) "Send MESSAGE, a JSON object, to CONNECTION." #<bytecode 0x1568a8fd8151>) #<eglot-lsp-server eglot-lsp-server-1568a8f587a8> (:id 1 :method :initialize :p
  jsonrpc-connection-send(#<eglot-lsp-server eglot-lsp-server-1568a8f587a8> :id 1 :method :initialize :params (:processId nil :rootPath "/tmp/eglot--fixtureLn3crZ/project/" :rootUri "file:///tmp/eglot
  jsonrpc--async-request-1(#<eglot-lsp-server eglot-lsp-server-1568a8f587a8> :initialize (:processId nil :rootPath "/tmp/eglot--fixtureLn3crZ/project/" :rootUri "file:///tmp/eglot--fixtureLn3crZ/proje
  apply(jsonrpc--async-request-1 #<eglot-lsp-server eglot-lsp-server-1568a8f587a8> :initialize (:processId nil :rootPath "/tmp/eglot--fixtureLn3crZ/project/" :rootUri "file:///tmp/eglot--fixtureLn3crZ
  jsonrpc-async-request(#<eglot-lsp-server eglot-lsp-server-1568a8f587a8> :initialize (:processId nil :rootPath "/tmp/eglot--fixtureLn3crZ/project/" :rootUri "file:///tmp/eglot--fixtureLn3crZ/project"
  eglot--connect(python-mode (transient . "/loopback:krug:/tmp/eglot--fixtureLn3crZ/project/") eglot-lsp-server ("pyls"))
  apply(eglot--connect (python-mode (transient . "/loopback:krug:/tmp/eglot--fixtureLn3crZ/project/") eglot-lsp-server ("pyls")))
  eglot--tests-connect()
  #f(compiled-function () #<bytecode 0x1568a8e9db2d>)()
  eglot--call-with-fixture((("project" ("coiso.py" . "bla") ("merdix.py" . "bla")) ("anotherproject" ("cena.py" . "bla"))) #f(compiled-function () #<bytecode 0x1568a8e9db2d>))
  eglot-tests---auto-detect-running-server-1()
  #f(compiled-function () #<bytecode 0x1568a8da7221>)()
  ert--run-test-internal(#s(ert--test-execution-info :test #s(ert-test :name eglot--tramp-test :documentation "Ensure LSP servers can be ..." :body #f(compiled-function () #<bytecode 0x1568a8da7221>)
  ert-run-test(#s(ert-test :name eglot--tramp-test :documentation "Ensure LSP servers can be used over TR..." :body #f(compiled-function () #<bytecode 0x1568a8da7221>) :most-recent-result #s(ert-test-
  ert-run-or-rerun-test(#s(ert--stats :selector t :tests ... :test-map #<hash-table eql 43/43 0x1568a8e147f9> :test-results ... :test-start-times ... :test-end-times ... :passed-expected
7 :passed-une
  ert-run-tests(t #f(compiled-function (event-type &rest event-args) #<bytecode 0x1568a8da7269>) nil)
  ert-run-tests-batch(t)
  ert-run-tests-batch-and-exit(t)
  eval((ert-run-tests-batch-and-exit 't) t)
  command-line-1(("--eval" "(package-initialize)" "--eval" "(package-refresh-contents)" "--eval" "(defun install-latest (p) (package-install (cadr (..." "--eval" "(install-latest (quote jsonrpc))" "--
  command-line()
  normal-top-level()
Test eglot--tramp-test condition:
    (error "Process EGLOT (project/python-mode) not running")
   FAILED  10/43  eglot--tramp-test (2.600265 sec)
   passed  11/43  eglot-capabilities (0.000284 sec)

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

I have pyls installed, which is why it's found in the PATH, but python-mode in eglot-server-programs is set to "jedi-language-server", so it gets used in the tests I've been running. When I change it to "pyls" I have completely separate issues where it doesn't even look like it's starting properly (I assume there's something wrong with my install of it).

Your backtrace doesn't make any sense to me. It looks like something's going wrong in TRAMP itself? I'll see what I can dig up.

@joaotavora
Copy link
Owner

@bjc but when you make check your configuration should be completely bypassed, and that's pretty improtant.

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

Oh, is the previous backtrace from Emacs 26? I don't have that installed, but maybe there's a dependency on a newer TRAMP that I'm not aware of. But if so, since we're only concerned with support for 27+, I don't know how important that actually is.

The second backtrace just shows that pyls isn't being found in the PATH for some reason.

The configuration isn't being bypassed -- since I never got pyls to work, I modified eglot.el with the language server change, under the assumption that I have some issue with my install of pyls and wanting to get some work done. I just never pushed the change up.

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

I'm going to see if I can figure out what's wrong with pyls and work from there, since it seems that's the difference between working and not-working tests.

@joaotavora
Copy link
Owner

Oh, is the previous backtrace from Emacs 26?

No sorry, from Emacs 28. Sorry for not being specific.

The second backtrace just shows that pyls isn't being found in the PATH for some reason.

Yes, but it should be I have it in my path . So maybe there's something wrong with that specific test. It mustn't fail like that.

The configuration isn't being bypassed -- since I never got pyls to work, I modified eglot.el with the language server change,

Wait, so all these tests you've been running are against a modified eglot.el, or did I miss something? You have to run them against the eglot.el that we're proposing in this PR, not anything else!

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

No sorry, from Emacs 28. Sorry for not being specific.

Ok, that's my main Emacs, so it's not a problem for me to test there.

The second backtrace just shows that pyls isn't being found in the PATH for some reason.

Yes, but it should be I have it in my path . So maybe there's something wrong with that specific test. It mustn't fail like that.

Agreed, but at least its tractable.

Wait, so all these tests you've been running are against a modified eglot.el, or did I miss something? You have to run them against the eglot.el that we're proposing in this PR, not anything else!

I've only changed the one language server. Everything else is unmodified. And that language server change was only because pyls has proven to be problematic on my system. Whereas, jedi-language-server works just fine, as do rls and rust-analyzer, both of which I'm currently using with eglot over TRAMP.

pyls, for some reason, is just not something I can test right now. Here's what it looks like when I try to run it from the command line:

emacs27:~/emacs-lisp/eglot% pyls                                            ±[●][bjc/tramp-support]
Traceback (most recent call last):
  File "/home/bjc/.local/bin/pyls", line 5, in <module>
    from pyls.main import main
  File "/home/bjc/.local/lib/python3.9/site-packages/pyls/main.py", line 65
    print '%s%s' % ('    ' * indent, name)
          ^
SyntaxError: invalid syntax

As you can see, the issue isn't with eglot, but pyls itself. I'm going to see if I can fix it, because I would hate for this patch to be held up on account of not being able to test with it, but hopefully you can see why I've opted to change the python language server I'm using for testing.

@joaotavora
Copy link
Owner

I would hate for this patch to be held up on account of not being able to test with it, but hopefully you can see why I've opted to change the python language server I'm using for testing.

Yeah it seems silly. Well, if jedi-language-server is a good/better option for Python, we could change the tests (and the CI config) to use that instead Is it easy to install?

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

Turns out I should have done some digging sooner. I'm not the only one with this problem: palantir/python-language-server#631

Installing the correct python-language-server has fixed that issue. I'll see about fixing the tests now.

As far as jedi-language-server vs pyls I have no opinion. I only used the former because my (incorrect) install of the latter seemed to be broken.

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

I've tracked down the issue, but I'm unsure how to resolve it. It is, indeed, a PATH problem. By default, TRAMP ignores a users custom PATH, such as what may be set in your .profile, and only uses the system value.

You can change this behavior with the following elisp:

(add-to-list 'tramp-remote-path 'tramp-own-remote-path)

I'd added that to my .emacs some time ago and had forgotten about it, which is why I didn't have the issue until I used the make test method.

The easiest thing to do would be amend the test by binding tramp-remote-path in a let form and consing tramp-own-remote-path to it:

(ert-deftest eglot--tramp-test ()
  "Ensure LSP servers can be used over TRAMP."
  (skip-unless (or (>= emacs-major-version 27) (executable-find "pyls")))
  ;; Set up a loopback TRAMP method that’s just a shell so the remote
  ;; host is really just the local host.
  (let ((tramp-remote-path (cons 'tramp-own-remote-path tramp-remote-path))
	(tramp-methods '(("loopback"

I'm somewhat ambivalent about this solution. It works, but feels a bit hacky. I can't see a better solution right now, though.

@joaotavora
Copy link
Owner

The easiest thing to do would be amend the test by binding tramp-remote-path in a let form and consing tramp-own-remote-path to it:

What's your concern about it? I think it should work...

TRAMP uses the system path, as determined by ‘getconf PATH’, as well
as built-in defaults. If the language server does not reside in that
PATH, but relies on being found by the user-level PATH, such as may be
defined in ‘$HOME/.profile’, this test would fail.

By adding the symbol ‘tramp-own-remote-path’ to TRAMP’s list of paths
to check, the user-lever PATH is taken into account and the language
server will be found.
@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

Thinking about it more, since the make method is how the test is supposed to be run, and it has a clean environment, I think it's fine.

Even in the case of running from within Emacs, where it may be set already, it doesn't really cause any harm. So I've pushed the change up.

@joaotavora
Copy link
Owner

Great, all tests pass here, I'm sqaushing this up for the master branch. Only one final hurdle remains: do you have a FSF copyright assignment? your name is not "Brendan" right?

@joaotavora
Copy link
Owner

Your change can go in early regadless, but if you somehow oppose getting one for the FSF, I'll have to revert and reimplement your work. Because the solution is so simple (a very good thing!), it might count as a "trivial" contribution. I'm hailing @monnier, former maintainer of Emacs, who should know about these things.

@bjc
Copy link
Contributor Author

bjc commented Mar 4, 2021

Brendan may be my brother. It's entirely possible he's signed an FSF agreement. I, however, have not, although I have no qualms doing so. I'm not sure how to go about doing it, however, so any help there is appreciated.

@joaotavora
Copy link
Owner

Brendan may be my brother. It's entirely possible he's signed an FSF agreement.

His name might have popped up in the copyright file I have access to ;-)

Anyway, please write to [email protected] and request a copyright assignment form, giving minimal context such as a link to this PR. They will mail the form offline to you. I'm going to write in the commit message that you have requested it.

@monnier
Copy link
Contributor

monnier commented Mar 4, 2021 via email

@bjc
Copy link
Contributor Author

bjc commented Mar 6, 2021

I submitted the form yesterday, and am awaiting response. I'll let you know when everything's done.

@joaotavora joaotavora closed this in 7918fac Mar 6, 2021
@joaotavora
Copy link
Owner

In the meantime, I pushed this to master. Let's be aware of possible bug reports in the next weeks.

@bjc
Copy link
Contributor Author

bjc commented Mar 11, 2021

I just got word that the copyright assignment is finished, so there should be no further issues.

@joaotavora
Copy link
Owner

I just got word that the copyright assignment is finished,

kööl

so there should be no further issues.

famous last words 😅 I wonder if anyone's actually using this. Anyway, it's a very minimal implementation, it should be relatively easy to debug.

bhankas pushed a commit to bhankas/emacs that referenced this pull request Sep 18, 2022
Also close joaotavora/eglot#463, close joaotavora/eglot#84.

Thanks to Brian Cully for the original simple idea.  The basic
technique is to pass :file-handler t to make-process, then tweak
eglot--uri-to-path and eglot--path-to-uri, along with some other
functions, to be aware of "trampy" paths".

Crucially, a "stty hack" was needed.  It has been encapsulated in a
new a new eglot--cmd helper, which contains a comment explaining the
hack.

Co-authored-by: João Távora <[email protected]>

* eglot.el (eglot--executable-find): Shim two-arg executable-find
function only available on Emacs 27.
(eglot--guess-contact): Use eglot--executable-find.
(eglot--cmd): New helper.
(eglot--connect): Use eglot--cmd.  Use :file-handler arg to
make-process.
(eglot--connect, eglot--path-to-uri): Be aware of trampy file
names.

* eglot-tests.el (eglot-tests--auto-detect-running-server-1): New helper.
(eglot--guessing-contact): Better mock for executable-find.
(eglot--tramp-test): New test.

* NEWS.md: mention TRAMP support.

* README.md: mention TRAMP support.
bhankas pushed a commit to bhankas/emacs that referenced this pull request Sep 19, 2022
Also close joaotavora/eglot#463, close joaotavora/eglot#84.

Thanks to Brian Cully for the original simple idea.  The basic
technique is to pass :file-handler t to make-process, then tweak
eglot--uri-to-path and eglot--path-to-uri, along with some other
functions, to be aware of "trampy" paths".

Crucially, a "stty hack" was needed.  It has been encapsulated in a
new a new eglot--cmd helper, which contains a comment explaining the
hack.

Co-authored-by: João Távora <[email protected]>

* eglot.el (eglot--executable-find): Shim two-arg executable-find
function only available on Emacs 27.
(eglot--guess-contact): Use eglot--executable-find.
(eglot--cmd): New helper.
(eglot--connect): Use eglot--cmd.  Use :file-handler arg to
make-process.
(eglot--connect, eglot--path-to-uri): Be aware of trampy file
names.

* eglot-tests.el (eglot-tests--auto-detect-running-server-1): New helper.
(eglot--guessing-contact): Better mock for executable-find.
(eglot--tramp-test): New test.

* NEWS.md: mention TRAMP support.

* README.md: mention TRAMP support.
bhankas pushed a commit to bhankas/emacs that referenced this pull request Sep 19, 2022
Also close #463, close #84.

Thanks to Brian Cully for the original simple idea.  The basic
technique is to pass :file-handler t to make-process, then tweak
eglot--uri-to-path and eglot--path-to-uri, along with some other
functions, to be aware of "trampy" paths".

Crucially, a "stty hack" was needed.  It has been encapsulated in a
new a new eglot--cmd helper, which contains a comment explaining the
hack.

Co-authored-by: João Távora <[email protected]>

* eglot.el (eglot--executable-find): Shim two-arg executable-find
function only available on Emacs 27.
(eglot--guess-contact): Use eglot--executable-find.
(eglot--cmd): New helper.
(eglot--connect): Use eglot--cmd.  Use :file-handler arg to
make-process.
(eglot--connect, eglot--path-to-uri): Be aware of trampy file
names.

* eglot-tests.el (eglot-tests--auto-detect-running-server-1): New helper.
(eglot--guessing-contact): Better mock for executable-find.
(eglot--tramp-test): New test.

* NEWS.md: mention TRAMP support.

* README.md: mention TRAMP support.

#637: joaotavora/eglot#637
#463: joaotavora/eglot#463
#84: joaotavora/eglot#84
jollaitbot pushed a commit to sailfishos-mirror/emacs that referenced this pull request Oct 12, 2022
Also close joaotavora/eglot#463, close joaotavora/eglot#84.

Thanks to Brian Cully for the original simple idea.  The basic
technique is to pass :file-handler t to make-process, then tweak
eglot--uri-to-path and eglot--path-to-uri, along with some other
functions, to be aware of "trampy" paths".

Crucially, a "stty hack" was needed.  It has been encapsulated in a
new a new eglot--cmd helper, which contains a comment explaining the
hack.

Co-authored-by: João Távora <[email protected]>

* eglot.el (eglot--executable-find): Shim two-arg executable-find
function only available on Emacs 27.
(eglot--guess-contact): Use eglot--executable-find.
(eglot--cmd): New helper.
(eglot--connect): Use eglot--cmd.  Use :file-handler arg to
make-process.
(eglot--connect, eglot--path-to-uri): Be aware of trampy file
names.

* eglot-tests.el (eglot-tests--auto-detect-running-server-1): New helper.
(eglot--guessing-contact): Better mock for executable-find.
(eglot--tramp-test): New test.

* NEWS.md: mention TRAMP support.

* README.md: mention TRAMP support.

GitHub-reference: close joaotavora/eglot#637
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.

could eglot work with remote project?
3 participants