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

Feature/lsp copilot #4635

Merged
merged 11 commits into from
Dec 20, 2024
Merged

Feature/lsp copilot #4635

merged 11 commits into from
Dec 20, 2024

Conversation

kassick
Copy link
Contributor

@kassick kassick commented Dec 5, 2024

This PR provides lsp-copilot, a client for the Copilot Node Server.

@github-actions github-actions bot added documentation client One or more of lsp-mode language clients labels Dec 5, 2024
@kassick kassick force-pushed the feature/lsp-copilot branch from e6e694c to a60adf3 Compare December 5, 2024 19:57
@kassick
Copy link
Contributor Author

kassick commented Dec 5, 2024

@kiennq here's the lsp-copilot client.

I've included in the PR a change that i've submitted previously that improves an issue that happens with ruff (probably with others as well) -- lsp-mode broadcasts executeCommands to all registered servers. Ruff does not like that and returns an error instead of silently ignoring the command. The result is that sometimes, after accepting an inline completion, the user sees an error message.

The first commit in the PR fixes that.

Let me know if you prefer to go another way with this, or keep this change outside of this PR and iterate over #4494

@kassick kassick force-pushed the feature/lsp-copilot branch 2 times, most recently from 85dab1c to 2df4263 Compare December 5, 2024 20:08
@kassick
Copy link
Contributor Author

kassick commented Dec 5, 2024

(CI is failing because the compiler is not understanding the -let bindings unpacking the newly defined interfaces... I'm not sure why, I'll take a look as soon as I've got some time, but if you see something obvious, please let me know!)

@kassick
Copy link
Contributor Author

kassick commented Dec 5, 2024

Regarding documentation: I'm not sure how to place the copilot in the language list, and even if that's where it should live. I've placed it in the Language section for lack of a better, obvious place.

@kiennq
Copy link
Member

kiennq commented Dec 6, 2024

The first commit in the PR fixes that.

Let me know if you prefer to go another way with this, or keep this change outside of this PR and iterate over #4494

I would prefer to separate them.

@kassick kassick force-pushed the feature/lsp-copilot branch from 2df4263 to 1752d1c Compare December 7, 2024 13:24
@kassick kassick force-pushed the feature/lsp-copilot branch from 2896279 to c529d46 Compare December 7, 2024 13:31
@kassick
Copy link
Contributor Author

kassick commented Dec 7, 2024

@kiennq I've fixed the issues you've pointed out, let me know if you think this is good to go or if it needs more work.

@kiennq
Copy link
Member

kiennq commented Dec 8, 2024

@kiennq I've fixed the issues you've pointed out, let me know if you think this is good to go or if it needs more work.

LGTM.
I wonder why you have to create two lsp clients? Shouldn't the second one (the one uses lsp-package-path) covers both of them? If there's system wide copilot lsp executable it will use it (the :system key), else it will use the :npm key that lsp-mode can install.

Now the lsp-controlled copilot npm install path contains a bin with a
copilot-node-server -- we no longer need to go out of our way to find the
language server js and execute it with node
@kassick
Copy link
Contributor Author

kassick commented Dec 9, 2024

I wonder why you have to create two lsp clients? Shouldn't the second one (the one uses lsp-package-path) covers both of them? If there's system wide copilot lsp executable it will use it (the :system key), else it will use the :npm key that lsp-mode can install.

You're right, it's no longer needed!

The second client should have been explicitly marked with :remote t. When I coded the first version of the client, there was no bin/copilot-node-server in the installation path, so (for the local client) I had to find language-server.js and execute it in node explicitly. That approach would not fly in remote, so I had to assume that the target environment would have a server command somewhere in the PATH.

But I've just checked the current version of the copilot-node-server and it contains bin/copilot-node-server. Now with lsp-package-path and this executable script, we no longer need to worry.

Thanks for pointing that out!

ACTUALLY: The executable script is there, but then it complains that it can't find the tokenizer ... calling node explicitly probably sets the CWD to the dirname of the script, then it can find the payload correctly...

I'll have to ammend the last commit to work around that, maybe keeping the same strategy as was present in c529d46

@kiennq
Copy link
Member

kiennq commented Dec 10, 2024

I've tried your last commit, the (lsp-package-path 'copilot-ls) works fine with the latest copilot-node-server.
There's an issue with plist that the :inlineCompletionProvider is mistakenly treated as nil even though it's null.
Here's my fix kiennq@ffe4564

@kassick
Copy link
Contributor Author

kassick commented Dec 10, 2024

I've tried your last commit, the (lsp-package-path 'copilot-ls) works fine with the latest copilot-node-server. There's an issue with plist that the :inlineCompletionProvider is mistakenly treated as nil even though it's null. Here's my fix kiennq@ffe4564

Weird -- I've tested with your fork and copilot did not complain about not finding the tokenizer model ... Let's hope it does not happen again ;)

@kassick
Copy link
Contributor Author

kassick commented Dec 10, 2024

About the changes in your fork -- I've left a couple of comments there.

Other than that, how do you want to proceed? Should I merge your changes here or do you prefer to merge them later?

@kiennq
Copy link
Member

kiennq commented Dec 10, 2024

Other than that, how do you want to proceed? Should I merge your changes here or do you prefer to merge them later?

Yes, please take my commit and make changes as you see fit.

@kassick
Copy link
Contributor Author

kassick commented Dec 11, 2024

Done @kiennq !

@kiennq
Copy link
Member

kiennq commented Dec 20, 2024

Thank you. Merge this now. @jcs090218 @yyoncho @ericdallo

@kiennq kiennq merged commit 83da5b6 into emacs-lsp:master Dec 20, 2024
10 of 13 checks passed
@thecsw
Copy link

thecsw commented Dec 26, 2024

Hi, @kassick, @kiennq ! Great change—but for my purposes, I'm trying to disable the copilot LSP server in emacs. When I'm setting the enabled to false in my config

 (setq lsp-copilot-enabled nil)

It doesn't turn off and whenever I start an lsp mode, say in golang, it always initializes. If I logout, it will bring up the annoying pop up of logging in. Is there another way to turn it off completely, so the copilot lsp backend doesn't activate?

I was reading https://emacs-lsp.github.io/lsp-mode/page/settings/mode/#lsp-disabled-clients and none of the following values passed, copilot-ls, copilot-ls', lsp-copilot, nor 'lsp-copilot work for lsp-disabled-clients

Update: the only way I found to forcibly disable this backend from activating on lsp buffers is to set lsp-copilot-applicable-fn to nil, so no buffer is allowed, but it's a bit of a workaround compared to simply relying on lsp-copilot-enabled

@kassick
Copy link
Contributor Author

kassick commented Dec 26, 2024

Hey @thecsw !

I think that the enabled flag ended up being forgotten in the final code, so indeed it is not doing what any user would expect it to. I'll open a new PR to try and sanitize that. I'll tag you so you can give some input if it works as you'd expect ;)

Meanwhile, if you want the copilot server not to start at all you can either:

  • Uninstall it
  • Make lsp-copilot-applicable-fn a function that always returns nil:
    (setq lsp-copilot-applicable-fn (-const nil))
  • Make lsp-copilot-applicable-fn a function that returns the value of the -enabled flag
    (setq lsp-copilot-applicable-fn (lambda (&rest _) lsp-copilot-enabled))

If you'd rather control when copilot is enabled (e.g. disable it for go but enable it for python), then you can use the applicable-fn to do so as well:

(setq lsp-copilot-applicable-fn 
      (lambda (buf-name buf-mode) 
         (pcase buf-mode
            ('python-mode t)
            ('go-mode nil)
            (t lsp-copilot-enabled))))

Let me know if any of that fixes your issue while the PR is not ready.

I'm not sure why lsp-disabled-clients did not work for you -- that's not something that the copilot client should have any control over. If you've added the symbol copilot-ls to the disabled client list, then it should not have started copilot...

@thecsw
Copy link

thecsw commented Dec 26, 2024

Hello, @kassick and thank you for such prompt response! re: disabled clients—I think the issue is with my emacs env, at least wherever the init happens, where it doesn't have access to those symbols and crashes during the startup. Same thing happens when setting

(setq lsp-copilot-applicable-fn (-const nil))

with Symbol's function definition is void: -const

Though the second way,

(setq lsp-copilot-applicable-fn (lambda (&rest _) lsp-copilot-enabled))

worked. I actually don't know how to uninstall, since I don't have any copilot packages installed through MELPA and had a hard time finding on where emacs downloads the backend to.

My configuration system is rather simple, I have a configuration.org file, where I define everything in emacs-lisp blocks and then reference it from .emacs

@kassick
Copy link
Contributor Author

kassick commented Dec 27, 2024

(setq lsp-copilot-applicable-fn (-const nil))

with Symbol's function definition is void: -const

Ah, -const is not autloaded, duh, you'd have to require it.
Though you'd have to take care where you place that in your init.el to avoid it happening before dash is installed/available, so its better to do it in the :config section of your use-package

(use-package lsp-mode
  ;; ... your other settings here
 :config
  (require 'dash) ;; actually not needed in the :config of lsp-mode, since it will have already been loaded ...
  (setq lsp-copilot-applicable-fn (-const nil)))

Though the second way,

(setq lsp-copilot-applicable-fn (lambda (&rest _) lsp-copilot-enabled))

worked.

Good!
I'll probably propose that as the default behaviour in the PR.

I actually don't know how to uninstall, since I don't have any copilot packages installed through MELPA and had a hard time finding on where emacs downloads the backend to.

Oh, no, the copilot-ls does not require anything else from melpa, only that the copilot-node-server package from npm be available.

You may have installed it globally somewhere (npm install -g copilot-node-server). If that's the case, uninstall it with npm.

If you've installed it via lsp-install-server, it should be available somewhere under your lsp-server-install-dir . That's usually $HOME/.emacs.d/.cache/lsp/npm/copilot-node-server, unless you've customized the install dir. Remove this path and then copilot-ls won't activate.

@thecsw
Copy link

thecsw commented Dec 27, 2024

Ahh! I see—even though I've been using emacs for a very long time, still nowhere near the comfort level I'd like to be at. Didn't know you needed dash for that. Makes sense!

Blehh, yeah—I opened one random file and it "downloaded" the lsp server—I kind of blanked out on how it did it, only when I saw this PR I realized it was through npm—deleting the node server also works!

Wonderful, thanks for your help, @kassick—much appreciate it and happy holidays!!

@farazshaikh
Copy link

Sir, this PR unintentionally enables this "under development" feature.
There is no clear way to disable it.
Also, there is no clear way to use it - i.e. when I enable it it prompts C- to accept. But C- key doesn't accept the completion.

I've been an Emacs user for 20+ years now. Please don't enable features if they are not ready for prime time.

@kassick
Copy link
Contributor Author

kassick commented Jan 5, 2025

This PR doesn't actually add the inline completion functionality @farazshaikh , that'd be #4623

You can disable inline completions by turning off lsp-inline-completion-mode. You can (setq lsp-inline-completion-enable nil) in your init el to avoid it being enabled in the first place .

@kassick
Copy link
Contributor Author

kassick commented Jan 5, 2025

As to the keys not reacting -- I've observed that too sometimes (but not always) when the completion is shown on idle. When I explicitly ask for it, I have no recollection of running into this issue.

I did not figure out why, since the (point) is well within the overlay, so its keymap should be active. Maybe some other overlay or posframe is popping up and interfering? I'm not sure.

I'm considering to rework the inline completion to work around this somehow (either go company's way with after-command or use a minor mode for when the completion is displaying).

@farazshaikh If you have observed anything that may hint on why the overlay keymap is not active when it should, please do share it so we can improve the feature.

kassick added a commit to kassick/lsp-mode that referenced this pull request Jan 7, 2025
kassick added a commit to kassick/lsp-mode that referenced this pull request Jan 7, 2025
kassick added a commit to kassick/lsp-mode that referenced this pull request Jan 8, 2025
@kassick kassick mentioned this pull request Jan 10, 2025
3 tasks
kassick added a commit to kassick/lsp-mode that referenced this pull request Jan 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client One or more of lsp-mode language clients documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants