Fork me on GitHub
#lsp
<
2022-10-12
>
Carlo10:10:52

Is there a way to export the data that the lsp server generates given a codebase? I'd like to visualize the info contained in the call hierarchy with graphviz

ericdallo13:10:08

โ˜๏ธ it was introduced on latest release, so LMK what you think

๐Ÿ™Œ 1
Carlo15:10:23

@UKFSJSM38 I got a dump of the analysis via the cli, but it only contains the data for the module level dependency graph, right? Not for the function level one (but maybe it's in the clj-kondo analysis). I would like to query the call hierarchy for the functions.

ericdallo15:10:35

That would be possible only via the analysis data, but it may be interesting to support call hierarchy or something like that, but maybe that would be a different command, not a dump one since you would want for specific function

Carlo15:10:11

If the info is in the analysis data, where should I look to reconstruct the call graph for a function? I'm not sure about what the data in the dump covers. Alternatively, is there a way of getting the raw data of the call hierarchy (like, the outgoing function) directly from the repl? Could you explain how?

ericdallo15:10:42

The call hierarchy is not available via the API yet, but something I never considered to support, I may add support soon, only available during editor usage unfortunately. About the analysis data, it's 99% clj-kondo analysis, which you can understand deeply here:, https://github.com/clj-kondo/clj-kondo/blob/master/analysis/README.md

๐Ÿ™Œ 1
Carlo15:10:42

Thank you, that seems awesome. What I meant before is: if I include clojure-lsp in my dependencies and then require clojure-lsp.feature.call-hierarchy , is there a simple way for me to call incoming or outgoing directly?

ericdallo15:10:16

Yeah, that would hacky work if you call the analyze! function from the API first :)

ericdallo15:10:46

The support of call hierarchy would basically wrap those things in a single function with better UX too

Carlo17:10:39

In this call to ongoing , where should I grab components?

(defn outgoing [uri row col {:keys [db*] :as components}] ...)

ericdallo17:10:47

Check how we build it on clojure-lsp.api, there is a build-conponents function

ericdallo17:10:11

We may improve that to be used outside of that like you are doing

ericdallo18:10:51

BTW @UA7E6DU04 what you would expect of return about a function clojure-lsp.api/definition or clojure-lsp.api/incoming-call-hierarchy ?

Carlo18:10:56

Hmm, let's see, ideally what I want out of this is a graph, and I think that's the right data choice. The kind of data that is displayed in emacs via lsp is so redundant to be almost inusable I would say. What do you think?

ericdallo18:10:05

maybe for definition?

{:uri "file:///home/foo/project/src/foo.clj"
 :row 2
 :col 4
 :external-file false}

ericdallo18:10:54

Yes, for call hierarhcy a graph similar to the dependency-graph of the dump command sounds good to me

Carlo19:10:17

yes, that definition could be good. If I can also grab the form it's probably useful too

Carlo19:10:18

so that a tool could directly choose to present the informations without querying the data again. If not the entire form, I think I would appreciate a :range if possible

ericdallo19:10:18

hum, you mean?

{:uri "file:///home/foo/project/src/foo.clj"
 :row 2
 :col 4
 :external-file false
 :var-definition-form "(defn foo []\n     (+ 1 2))"}

ericdallo19:10:02

not sure if that would work for all/most cases, need to do a double check, but I can see how convenient it could be

Carlo19:10:13

something like that yes (I guess the :range is a good secondary choice too, especially if we provide a convenience function to return the text afterwards)

ericdallo19:10:36

Yes, I think we can just return the clj-kondo analysis definition which has all of that including range and data about the function name, args, doc etc

๐Ÿ™Œ 1
Carlo19:10:40

For the graph, I'm a bit torn myself: I would like to illustrate a case study of sorts:

๐Ÿ‘ 1
ericdallo19:10:48

cool, I will start with the definition as sounds like a good first case to support and learn about the API. LMK if you have any output examples about the call hierarchy

๐Ÿ™Œ 1
ericdallo19:10:05

about the call of it, do you think (api/definition {:from 'my.ns/my-function}) is a good usage?

Carlo19:10:03

Could be, yes, I wonder why the wrapper instead of e.g. (api/definition 'my.ns/my-function)

ericdallo19:10:42

oh, all clojure-lsp api functions can receive extra things like setting or project-root, receive a map is easier to support and avoid breaking changes

Carlo19:10:55

oh in that case I agree!

๐Ÿ˜„ 1
Carlo19:10:09

ok, I created my graph use-case on paper, and I'll write it here now. It's not directly about graph hierarchy, but it is about exposing the right tools so that the user can do the kind of analysis I'm describing

Carlo19:10:56

(defn foo []
  (let [a (bar)]
    a))

(defn quux [z]
  (let [b (foo)
        c (zorg1 b)
        d (zorg2 z)]
    (baz c d)))

Carlo19:10:40

consider this setup ^. Now suppose that I'm doing a refactoring, and I have to change the internals of foo. I want to "follow the changes" to see which other parts of the code I should amend. Now, if we do a call graph, we see that quux calls foo, zorg1, zorg2 and baz, but crucially, if I modify foo, I would only need to amend zorg1 and baz, while zorg2 can be left alone

Carlo19:10:38

essentially because the flow of the changes never impact what zorg2 was doing. Now, in this contrived example it seems that we're gaining a little, but in an actual codebase proportionally only a few places have to actually be changed.

Carlo19:10:35

Ideally I would want the api to expose enough info to make this kind of reasoning possible, even when, strictly speaking, it falls outside the scope of a call hierarchy. But I wouldn't know what's the right balance. What do you think? Do you see the appeal of the kind of analysis I'm suggesting?

Carlo19:10:49

The answer I would want in that case, is "the dataflow is foo -> zorg1 -> baz (in this example it's not branched but in the general case it's a graph)

ericdallo19:10:52

Yes, I understand how convenient that would be, but not simple for static analysis, this is something that start to touch the runtime analysis, not sure there is a data structure using current kondo analysis that we could easily (and more importantly, safely) know that

ericdallo19:10:25

yeah, I think clojure-lsp could present all call hierarchy like does on editor, nothing that much more than that

Carlo19:10:10

I agree that it's not in the scope of clojure-lsp to do the analysis I've described, my suggestion was that, if it proves to be easy, we could return, as the result of the "proper" call hierarchy, enough enrichment to be able to build other tools on top.

Carlo19:10:38

Why do you think the issue I'm describing would be unsafe?

Carlo19:10:19

I would not want to go into run-time analysis, that's totally another set of problems that's better solved by other means ๐Ÿ˜‚

ericdallo19:10:55

> Why do you think the issue I'm describing would be unsafe? Thinking about that now, we would need to actually build the function and usage call hierarchy, not only var-definitions call-hierarchy, since we would need to check each local-usage and see if it's used on other places in that same function. I'm not saying it's not possible, but sounds harder Yes, I do think we could research and see if it's possible, I'd just not spend that much time as things tend to become complex when we want to offer things that are more easily available via runtime.

Carlo19:10:17

Oh I totally agree that just exposing the "proper" call-hierarchy is the correct first step. Moreover, the case I described would start with examining quux as the proper parent of foo . Maybe we could return all the information that clj-kondo exposes about quux, so that one is free to do its own analysis? Oh, I guess the new definition is probably the place to include the detailed data, no?

Carlo19:10:42

So ideally a user would call the hierarchy function, discover that my.module/quux -> my.module/foo and then call (api/definition 'my.module/quux) to continue with the flow analysis

ericdallo19:10:56

I think so, we actually have a command that does that, return the kondo analysis of the current point: lsp-clojure-cursor-info

๐Ÿ‘€ 1
ericdallo19:10:04

We could make it available on api

๐Ÿ™Œ 1
Carlo19:10:03

I didn't know ๐Ÿ˜ฎ. I see using that in emacs on a function call, I get back the file and position of the function definition. Does clj-kondo do more analysis of the body of the function?

ericdallo20:10:38

Yes, it has the locals and local-usages which are the local vars, args and bindings

ericdallo20:10:01

Yeah, I created that function for debugging purposes but I can see how it would be nice to have on API

ericdallo20:10:46

I'll spend some time until next release thinking on those things to try to offer more features via API

Carlo20:10:11

Awesome! It's always nice to chat! Feel free to ping me, if I can be of help!

ericdallo20:10:45

Sure, thank you for the talk!

ericdallo21:10:19

I just realized (api/definition {:from 'my.ns/my-function}) doesn't make sense at all as you want usually a definition from a call or some var usage, so you don't have a proper symbol available

ericdallo21:10:41

probably passing the file + line and col would be what would work :thinking_face:

Carlo22:10:03

on the other hand, one could just go to definition in the editor, and invoke the hierarchy analysis from there. What extra functionalities would we get calling from var usage?

ericdallo11:10:16

In a ideal future, I'd like to offer most editors features in the API, so people could use it as a REPL dev dep instead of in your Editor as LSP

Carlo12:10:48

Ah, in that case it makes sense, if we plan to not have an editor available

borkdude13:10:09

In case anyone wondered if clojure-lsp could already be used in Fleet, the new IntelliJ VSCode-ish thing: https://twitter.com/borkdude/status/1580189060991455232

snoe13:10:52

Weird choice to hard code specific servers.

borkdude13:10:11

Closed source, closed world semantics? ;)

๐Ÿ‘† 2
ericdallo13:10:24

Oh, that's sad, I was even interested on creating a plugin focused on LSP support or/and help in the clojure-sp integration

borkdude13:10:06

It will come in the future, but not now

๐Ÿ‘ 1
borkdude13:10:47

I have a hard time imagining that this will be a serious competitor for VSCode..

ericdallo13:10:24

Yeah, I wonder how different will be from intellij, how easy will be to customize it

pez14:10:37

These things are hard to guess, but here's hoping that it will start seriously competing with VS Code! Big Tech are not good at behaving well when they get too dominant in an area. Fleet could make interesting bets that benefits VS Code users if the bets succeed (or fail).

borkdude14:10:49

Yes, for sure!

elken14:10:11

Pretty sure I do remember them confirming it will be closed, so I can't see it making a dent in vscode. Tried it for a bit and it's "fine". I feel the big sellers are the collab tools, otherwise it's just vsatombrackets

snoe14:10:22

if they started putting devs into contributing to LSPs that could be good too.