Fork me on GitHub
#calva
<
2023-11-03
>
DrLjótsson15:11:26

I don't know if this is a calva or clojure-lsp issue, but I'll try here first. I often have a arguments or local bindings that share name with a namespace alias. For example permission, as in (:require [bar.permission :as permission]) while in another unrelated namespace I have`(defn foo [permission] ,,,)`. Inside the foo function, I will start writing perm and autocomplete shows me permission, I hit tab, and it seemingly achieve the desired effect. However, unknowingly, I have also added bar.permission as a dependency to the current namespace. Because the first autocomplete suggestion that I get is for the alias rather than the local variable. And I often never discover this or discover it because it has added a circular dependency. Would it be possible to prioritize local variables before namespace aliases in the autocomplete suggestions?

ericdallo15:11:22

I'm almost sure we already prioritize that in clojure-lsp but there may be some frontend sorting(vscode side)

ericdallo15:11:06

You can see as locals are really bottom

ericdallo15:11:21

You can debug if clojure-lsp is returning the completion items in the correct order

DrLjótsson15:11:13

Here is a screenshot of the autocompletion. The local variable does not even show up

DrLjótsson15:11:54

I turned on verbose logging but clearing it and just typing one character generates a massive log. Not sure where to look

ericdallo15:11:25

You should seek for completion response from server

DrLjótsson15:11:25

Ok, here are entries with "completion"

[Trace - 16:20:13] Sending request 'textDocument/completion - (4785)'.
Params: {
    "textDocument": {
        "uri": "file:///Users/brjljo/BASS/bass4/test/clj/bass4/replace_fns.clj"
    },
    "position": {
        "line": 26,
        "character": 11
    },
    "context": {
        "triggerKind": 1
    }
}


[Trace - 16:20:13] Received response 'textDocument/completion - (4785)' in 33ms.
Result: [
    {
        "label": "permissions",
        "kind": 6,
        "detail": "",
        "data": {
            "unresolved": [
                [
                    "documentation",
                    {
                        "name": "permissions",
                        "uri": "file:///Users/brjljo/BASS/bass4/test/clj/bass4/replace_fns.clj",
                        "name-row": 26,
                        "name-col": 6
                    }
                ]
            ]
        },
        "score": 15
    },
    {
        "label": "permissions",
        "kind": 10,
        "detail": "alias to: bass4.admin.permissions",
        "data": {
            "unresolved": [
                [
                    "alias",
                    {
                        "ns-to-add": "bass4.admin.permissions",
                        "alias-to-add": "permissions",
                        "uri": "file:///Users/brjljo/BASS/bass4/test/clj/bass4/replace_fns.clj"
                    }
                ]
            ]
        },
        "score": 9
    }
]


[Trace - 16:20:13] Sending request 'completionItem/resolve - (4786)'.
Params: {
    "label": "permissions",
    "detail": "alias to: bass4.admin.permissions",
    "insertTextFormat": 1,
    "kind": 10,
    "data": {
        "unresolved": [
            [
                "alias",
                {
                    "ns-to-add": "bass4.admin.permissions",
                    "alias-to-add": "permissions",
                    "uri": "file:///Users/brjljo/BASS/bass4/test/clj/bass4/replace_fns.clj"
                }
            ]
        ]
    }
}


[Trace - 16:20:13] Received response 'completionItem/resolve - (4786)' in 4ms.
Result: {
    "label": "permissions",
    "detail": "alias to: bass4.admin.permissions",
    "insertTextFormat": 1,
    "kind": 10
}

ericdallo15:11:38

So server is no returning the local at all indeed, could you test if the same happens for let bindings or defn body?

DrLjótsson15:11:43

Same for defn and let

DrLjótsson15:11:27

However, it does not happen if there is no matching alias (note permission without trailing s). So now the alias permissions is not even suggested

ericdallo15:11:22

Interesting, looks like a bug that we exclude the local only when there is a alias

ericdallo15:11:40

Could you please open a clojure-lsp issue with that detailed repro please?

DrLjótsson15:11:50

Sure! Is this related? An autocomplete suggestion when naming arguments

ericdallo15:11:00

I don't think so

DrLjótsson15:11:54

This will also require the namespace, which doesn't make much sense. But one should maybe not hit tab on autocompletion of argument names...

seancorfield16:11:15

☝️:skin-tone-2: I've run into this a few times but never made sense of what causes it -- so massive thanks to @UGDTSFM4M for figuring out a repro!

❤️ 2
metal 1
ericdallo16:11:44

thanks @UGDTSFM4M, something weird is that on emacs it works as expected :thinking_face: Will try with calva now

ericdallo16:11:45

Indeed, that only seems to happen on calva, which is odd

ericdallo16:11:38

@UGDTSFM4M this is what server returns for calva, which looks correct, so it's probably a calva bug, maybe <!subteam^S03BGSAUPTQ> can understand better that

2023-11-03T16:55:59.247Z  DEBUG [clojure-lsp.server:55] - [Trace - 2023-11-03T16:55:59.247Z] Sending response 'textDocument/completion - (69)'. Request took 37ms.                            
Result: [ {                                                                                                                                                                                   
  "label" : "ns-alias",                                                                                                                                                                       
  "kind" : 6,                                                                                                                                                                                 
  "data" : {                                                                                                                                                                                  
    "unresolved" : [ [ "documentation", {                                                                                                                                                     
      "name" : "ns-alias",                                                                                                                                                                    
      "uri" : "file:///home/greg/dev/clojure-lsp/lib/src/clojure_lsp/feature/issue/another_ns.clj",                                                                                           
      "name-row" : 3,                                                                                                                                                                         
      "name-col" : 6                                                                                                                                                                          
    } ] ]                                                                                                                                                                                     
  },                                                                                                                                                                                          
  "score" : 14                                                                                                                                                                                
}, {                                                                                                                                                                                          
  "label" : "ns-alias",                                                                                                                                                                       
  "kind" : 10,                                                                                                                                                                                
  "detail" : "alias to: clojure-lsp.feature.issue.aliased",                                                                                                                                   
  "data" : {                                                                                                                                                                                  
    "unresolved" : [ [ "alias", {                                                                                                                                                             
      "ns-to-add" : "clojure-lsp.feature.issue.aliased",                                                                                                                                      
      "alias-to-add" : "ns-alias",                                                                                                                                                            
      "uri" : "file:///home/greg/dev/clojure-lsp/lib/src/clojure_lsp/feature/issue/another_ns.clj"                                                                                            
    } ] ]                                                                                                                                                                                     
  },                                                                                                                                                                                          
  "score" : 9                                                                                                                                                                                 
}, {                                                                                                                                                                                          
  "label" : "ns-aliases",                                                                                                                                                                     
  "kind" : 3,                                                                                                                                                                                 
  "detail" : "clojure.core/ns-aliases",
  "data" : {
    "unresolved" : [ [ "documentation", {
      "uri" : "file:///clojure.core.clj",
      "name" : "ns-aliases",
      "ns" : "clojure.core"
    } ] ]
  },
  "score" : 6
} ]

pez17:11:47

I think it probably is outside what Calva can do much about. It’s VS Code’s lsp client that acts on this stuff. Calva basically only sets up the connection. (Speaking without having checked this specific feature, but it’s how almost all clojure-lsp functionality gets handled.

Lukas Domagala17:11:12

I don’t really have time to look into it, but I suspect this is a problem with nrepl. When we do completions we ask lsp + nrepl for suggestions and then merge them. I’d suspect that nrepl provides those alias completions with a higher score than the ones provided by clojure-lsp

Lukas Domagala17:11:54

Could you try asking for those completions without being connected to the repl?

pez17:11:44

Ah, thanks @U02EMBDU2JU. You read the thread better than I did. Could very well be nrepl.

Lukas Domagala17:11:11

btw we already have a completionProviderOptions = { priority: ['lsp', 'repl'], merge: true } that we hardcode. It works the same as our definitionProviderPriority. We could expose it and allow people to set this. We didn’t do it because we were hoping that the default would always work out ok.

ericdallo17:11:20

I never test with repl on, so I don't think it's nrepl

ericdallo17:11:38

More likely a bug in the merge?

1
Lukas Domagala17:11:50

There can’t be a bug in the merge if there is no merge because you don’t use a repl 😅

ericdallo17:11:16

Agree, would be nice if you could try to reproduce as well, but it's weird

Lukas Domagala17:11:40

I just checked the lsp spec. Score is not actually a thing for completion items. It’s supposed to use the label for sorting or, if available, the sortText. So since sortText is missing it’s probably using the label, which is the same for all three. So you’ll get a random sort?

ericdallo17:11:09

The issue is not the sort, there is a missing completion of the var-definition ns-alias

Lukas Domagala17:11:28

Ah crap, sorry. I guess I didn’t read enough of the thread 😞

ericdallo17:11:36

I suggest reading the issue, is pretty descriptive to avoid re-read the whole thread

Lukas Domagala18:11:52

Yeah, no you are actually correct, we do the merge wrong. We index ist by label so you don’t get the same item from lsp and from nrepl. That doesn’t work so well when you have the same label on multiple completions from the same source 😞

ericdallo18:11:58

But is that related to this issue? As there is no nrepl connected

Lukas Domagala18:11:50

Yes, because we always run through the merge, since it “shouldn’t” make a difference if there’s nothing to actually merge 😞

Lukas Domagala18:11:40

results = [
        ...completions
          .concat(results)
          .reduce(
            (m: Map<string | CompletionItemLabel, CompletionItem>, o: CompletionItem) =>
              m.set(o.label, Object.assign(m.get(o.label) || {}, o)),
            new Map()
          )
          .values(),
      ];

ericdallo18:11:31

Ah makes sense

pez12:11:06

Since this is on the Calva side of things, I created a new issue here: https://github.com/BetterThanTomorrow/calva/issues/2336 However, I fail to reproduce it. Put my repro project here: https://github.com/BetterThanTomorrow/calva/tree/dev/test-data/projects/completions Could be that I am still misunderstanding the expectations, so would appreciate more clarity there. Can you test with that project @UGDTSFM4M and provide a bit more info on what you expect to happen as comments to the new issue?

pez12:11:23

This is what I see, and what I expect to see, when completing.

ericdallo13:11:08

@U0ETXRFEW that first ns-alias is related to the alias from other ns, if you hit it, it will require the ms in that current ns, we don't want that, we want the ns-alias suggestion from line 4, which should only complete the var-definition from line 4 and nothing else

👍 1
pez13:11:20

Why do we get the aliased to: entry? Honest curiosity, I’m guessing there’s something I don’t understand.

ericdallo13:11:23

This is a way to complete an alias that is not already required, pretty common feature, but when you have alias with the same name of locals/vars you face that issue in calva it seems

🙏 1
DrLjótsson14:11:02

Thank you all for looking into this!

🙏 1
pez19:11:48

@UKFSJSM38 what’s the significance of the score in the completions? Should a higher score overshadow a lower score for the same label+`kind`?

Lukas Domagala19:11:28

I can't come up with any case where label + kind isn't unique. There might be some weird edge cases though

pez19:11:40

The test case that @UGDTSFM4M has provided is such a case. 😃

😎 1
Lukas Domagala20:11:07

Huh, that looks weird. Kind 6 is a variable, right? How could you have two variables with the same name in scope without one shadowing the other?

pez20:11:47

Yeah, they are not both in scope.

;; another_ns.clj
(ns another-ns)

(def ns-alias 0)

(defn bar
  []
  (ns-a)) ;; Autocomplete will only suggest "ns-alias alias to: aliased" LSP-OUTPUT

(defn foo
  [ns-alias]
  (ns-a|)) ;; Autocomplete will only suggest "ns-alias alias to: aliased"

(defn baz
  []
  (let [ns-alias 0]
    ns-a)) ;; Autocomplete will only suggest "ns-alias alias to: aliased"
(Cursor at |)

Lukas Domagala20:11:20

Huh, the def probably shouldn't be suggested at that point. Not that it makes any difference in the end, since only the details differ.

pez20:11:17

Well, we present the details, so it makes a difference. So here I suspect I have sorted out the wrong one, hence the question about score.

Lukas Domagala20:11:54

But you're probably correct that the higher score should win. Until now we also just took the order that clojure-lsp returned as the display order. That's technically correct according to lsp spec, but we should probably sort by score if it exists.

Lukas Domagala20:11:54

Ah ignore me, the score and the order agree...

pez20:11:58

Well, “until now” has changed. I couldn’t get the merge to work with the old code, not even the updated version you provided me, so I wrote a new one. I seem to have not retained the order priority. But it seems prudent to rely on score, rather than order? (If, indeed, he score is trying to tell me what I think it is telling me.)

Lukas Domagala20:11:14

Sorry for that, my TS is getting very rusty 😅

pez20:11:53

Haha, I think it is a kind of task where TS is just horrible compared to Clojure.

pez21:11:53

actually… kind 6 is Class in vscode index.d.ts. and 5 in the LSP protocol. In fact it all seems shifted by 1… VS Code types:

export enum CompletionItemKind {
        Text = 0,
        Method = 1,
        Function = 2,
        Constructor = 3,
        Field = 4,
        Variable = 5,
        Class = 6,
        Interface = 7,
        Module = 8,
        Property = 9,
        Unit = 10,
        Value = 11,
        Enum = 12,
        Keyword = 13,
        Snippet = 14,
        Color = 15,
        Reference = 17,
        File = 16,
        Folder = 18,
        EnumMember = 19,
        Constant = 20,
        Struct = 21,
        Event = 22,
        Operator = 23,
        TypeParameter = 24,
        User = 25,
        Issue = 26,
    }
LSP https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemKind:
export namespace CompletionItemKind {
	export const Text = 1;
	export const Method = 2;
	export const Function = 3;
	export const Constructor = 4;
	export const Field = 5;
	export const Variable = 6;
	export const Class = 7;
	export const Interface = 8;
	export const Module = 9;
	export const Property = 10;
	export const Unit = 11;
	export const Value = 12;
	export const Enum = 13;
	export const Keyword = 14;
	export const Snippet = 15;
	export const Color = 16;
	export const File = 17;
	export const Reference = 18;
	export const Folder = 19;
	export const EnumMember = 20;
	export const Constant = 21;
	export const Struct = 22;
	export const Event = 23;
	export const Operator = 24;
	export const TypeParameter = 25;
}
I wonder what’s up with that?

pez21:11:41

Ah, some stupid languages have 0 being falsy… So LSP avoids that… Currently trips our merge up in interesting ways. 😃

seancorfield22:11:50

That list only matches up to Color -- after that, the items are in a different order:

Reference = 17,
        File = 16,
... the same in both ... the following are in the first list and not in the second:
        User = 25,
        Issue = 26,
vs
export const File = 17;
	export const Reference = 18;

richiardiandrea15:11:00

Hi folks, I have a question - is there a way in calva to "remove" a namespace prefix? Say I have com.foo.bar.quuz - I would like to keep foo.quuz Is it possible?

pez15:11:37

I don’t think so, but the use case it is not completely clear to me. Is it a refactor rename operation you’re after? I think that is supported by clojure-lsp. To test, you would press F2 on the symbol.

richiardiandrea15:11:29

yeah it is a bulk refactor of every namespace (there are many, so F2) is not an option 😄

richiardiandrea15:11:49

(F2 works for a symbol namespace FWIW)

pez16:11:11

Maybe it works just moving the folders around in VS Code?

richiardiandrea16:11:26

yeah, would that change the ns form?

pez16:11:06

I’ve seen it happen. I think. It’s clojure-lsp in action if it works.

👍 1
Evan22:11:56

Hi there! running into an issue where keywords aren't being highlighted correctly. Issues: - Keywords do not get syntax highlighting unless I: 1. Save the file. 2. Tab out of the editor window to another tab and tab back. - Highlighted keywords lose their highlighting if I make any change to the editor (e.g add a newline). All other syntax highlighting seems to be working fine. Anyone know what the issue is?

pez09:11:43

Looks like it is clojure-lsp semantic tokens that get disabled. Can you reproduce it in a minimal project you can share?