Fork me on GitHub
#lsp
<
2024-02-14
>
magnars07:02:00

I added a namespace with a bunch of keywords that I would like completed by lsp, but I'm not getting completion for those keywords even with the file required. Turning off lsp-completion-mode falls back to CIDER, which does complete them for me. Any ideas what I must do to have lsp recognize these keywords as completable?

teodorlu14:02:36

I think it’s possible to get completion from both LSP and CIDER at the same time, but I haven’t (yet) been able to get that to work. I’m in the same position, I want completion of loaded keywords. @U5H74UNSF gave some quite compelling keywords for why this is nice in https://www.youtube.com/watch?v=KKvancXJJJg, from Babashka conf last summer.

ericdallo16:02:57

@U07FCNURX any repro I could try?

magnars19:02:49

@UKFSJSM38 In my case I just added a new namespace, added a bunch of keywords in a plain vector on the top level, and added a require from user.clj. I later also added it to (def my-kws ,,,) to see if that made a difference. I have now started using the keywords in my code, referencing the def , but still no completion for the keywords. Restarting lsp didn't do anything either.

magnars19:02:54

It's basically this:

ericdallo19:02:15

@U07FCNURX we on purpose complete simple keywords (not namespaced ones) with only the available on current namespace, otherwise we would provide tons of keywords from the whole project

ericdallo19:02:30

does cider provides all of them? or only the required ns that have keywords inside?

magnars06:02:17

Turning off lsp-completion-mode falls back to CIDER, which does complete these. That being said, I understand your rationale. I'm not sure what is best in total.

Ed11:02:24

I often use dabbrev-expand for stuff like that. (by default M-/ or C-M-/ to give you a selection you can narrow with vertico or whatever). It'll complete a word from any open buffer. It's well embedded in my muscle memory now, and works in zsh to complete arbitrary words from your history too.

magnars11:02:41

Yes, I use hippie-expand in the same way, but the language specific expansions have a better hit-rate.

Ed12:02:22

can't argue with that 😉

ericdallo12:02:52

we could offer keywords in completion of the required ns, I think that's ok, I'd like just to avoid providing all keywords of a project which probably is not a good idea or what user wants most of the time

Ed12:02:53

I just find it helpful to stop typos

ericdallo12:02:26

I mean, when completing, we offer keywords only of the ns that are required

Ed12:02:27

if we're taking feature requests, I'd quite like it to complete all fully qualified keywords (including ones expressed as ::thing in another namespace) and ideally include any aliases in the current namespace (so :some/thing will become ::s/thing` if I have an alias defined in the current ns)

Ed12:02:35

does any of that make sense?

ericdallo12:02:08

I think we already support that

ericdallo12:02:22

any simple code snippet so I can understand better your idea?

Ed12:02:08

Sure. Given these two namespaces, I currently get these completions (`lsp-version` returns LSP :: lsp-mode , Emacs 28.3, gnu/linux)

Ed12:02:08

I'm wondering if I could get completions that look like this

:as-alias
  :require
  :scratch.other/keyword -> ::o/keyword
  :scratch/kw            -> ::kw
  missing                -> :fully-quallified/keyword
  missing                -> :scratch.other/keyword
in the scratch ns.

Ed12:02:19

so any keyword with a namespace across my project gets completed and if there is an alias locally in the buffer it's used in the completion text.

Ed12:02:29

does that make sense?

Ed12:02:00

hmm ... I'm not sure that all those completions are coming from lsp-mode ... picard-facepalm

ericdallo12:02:46

Hm, interesting, we are not returning indeed the ones from clojure-sample.bar ns:

ericdallo12:02:25

your missings matches mine, I think we could improve that on LSP

Ed12:02:20

ok ... I take it back ... trying to complete from k rather than :k gives me a different list

Ed12:02:32

I think it's me expecting the : in the keyword to not be part of the matching algorithm (or something) 🤷

Ed12:02:33

although it might be nice if :scratch/kw was ::kw in the scratch ns - or both options were available? ... idk ... feel free to tell me that it's not a priority, like I say, dabbrev-expand usually saves me from a couple of keystrokes 😉 and lsp-mode has been a great addition to my workflow. Many thanks Eric.

ericdallo12:02:21

yeah, having both options would be nice but more complex to handle, I'd say is not a high prior, keyword completion is the hardest part of clojure-lsp completion logic, I'm constantly trying to improve it so feel free to leave an issue :)

Ed13:02:17

👍 ... many thanks - I'll do that later, got to go now

Timofey Sitnikov16:02:17

I have the following definition in the test file:

(defmethod t/assert-expr 'roughly [msg form]
  `(let [op1# ~(nth form 1)
         op2# ~(nth form 2)
         tolerance# (if (= 4 ~(count form)) ~(last form) 2)
         decimals# (/ 1. (Math/pow 10 tolerance#))
         result# (< (Math/abs (- op1# op2#)) decimals#)]
     (t/do-report 
       {:type (if result# :pass :fail)
        :message ~msg
        :expected (format "%s should be roughly %s with %s tolerance" 
                          op1# op2# decimals#)
        :actual result#})
     result#))
But, when I use it, I get error: Unresolved Symbol: roughly Below is the image of the error. Nevertheless, when I execute the test it seems to work fine. Why would clojure-lsp list it as an error?

borkdude16:02:58

lsp uses static analysis via clj-kondo you can unflag this symbol as follows:

{:linters {:unresolved-symbol {:exclude [roughly]}}}

Timofey Sitnikov16:02:51

@U04V15CAJ Feels like cheating, should I wrap the method with a function so that linter works for the function and method name would be excluded but would never be used in the code. Sorry, I am a bit inexperienced with this.

borkdude16:02:55

what you mean wrap with a function, can you give an example?

imre16:02:14

assert-exprs are a bit weird in that they don't create top-level symbols

imre16:02:13

then you'd require [matcher-combinators.test :refer [match?]] to get the symbol to "resolve"

Timofey Sitnikov16:02:29

@U04V15CAJ Well, something like this seems to work so far, where I change the name of the method and add a roughly function:

(defmethod t/assert-expr 'roughly-m [msg form]
  `(let [op1# ~(nth form 1)
         op2# ~(nth form 2)
         tolerance# (if (= 4 ~(count form)) ~(last form) 2)
         decimals# (/ 1. (Math/pow 10 tolerance#))
         result# (< (Math/abs (- op1# op2#)) decimals#)]
     (t/do-report 
       {:type (if result# :pass :fail)
        :message ~msg
        :expected (format "%s should be roughly %s with %s tolerance" 
                          op1# op2# decimals#)
        :actual result#})
     result#))

(defn roughly [op1 op2 tol] (roughly-m op1 op2 tol))

Timofey Sitnikov16:02:31

@U08BJGV6E, ahhh, I looked into it, its a bit over my head 🙂, I just do not have enough experience to do what is right.

imre16:02:11

basically you'd do your original defmethod

imre16:02:23

and additionally (declare roughly)

imre16:02:49

and then in the test ns you'd (:require [my-assertion.ns :refer [roughly]])

Timofey Sitnikov16:02:17

That seems to work, but I do like the function wrapper as I shared above, it provide syntax checking for me.

imre16:02:59

ah, I like that - kinda like the one core.async uses

👍 1
Noah Bogart16:02:28

so you'd have your assert-expr roughly and then (defmacro roughly [op1 op2 tol] (throw (IllegalArgumentException. "Use roughly only in an (is ...) call, please")))`

Noah Bogart16:02:59

that'll get you both syntax highlighting, docstrings, clj-kondo and lsp happiness, while also alerting you to misuses when not wrapped in an is call

imre16:02:14

and by throwing at macroexpansion time you're catching the usage error early, right?

Noah Bogart16:02:01

That's not how Sean or I wrote it, but that might work! i haven't tried it

Noah Bogart16:02:09

would be very nice if that is how it works

imre17:02:22

I mean, as opposed to it being a function which would throw when it got called

Noah Bogart17:02:26

this would expand to a call to (throw ...), it wouldn't call throw during macro expansion

imre19:02:33

Oh yes my bad. Why is it a macro then?

imre19:02:45

Just curious

Noah Bogart19:02:49

to make the stack trace point to the spot where it's called

Noah Bogart19:02:27

if it's a plain function,t hen the stack trace will say "can't use it!" but point to the function's definition which is unhelpful in a large test suite

Noah Bogart19:02:35

then you have to look at the stack trace to find out the offending line

Sam18:02:04

Is there a way to get LSP to pop up function documentation in a separate buffer?

hiredman19:02:56

I use eglot as my lsp client, and if I create an eldoc buffer(M-x eldoc or M-x eldoc-buffer), the documention all goes there

Sam19:02:47

I'm using lsp-mode right now, maybe Eglot could be worth trying.

Sam19:02:20

I should have written lsp-mode in my original post, it slipped my mind. Sorry.

hiredman19:02:34

if lsp-mode also uses eldoc to display the docs then the same eldoc buffer stuff should work

Sam19:02:09

Great! Your comments made me find (setq lsp-eldoc-render-all t) which seems to be what I want. Thank you!

otfrom08:02:17

ooh, thanks. I like that