Fork me on GitHub
#lsp
<
2022-02-07
>
borkdude16:02:30

@ericdallo At nextjournal there is a project on which lsp constantly consumes 1 core and doesn't get any further. I can reproduce this from the command line:

borkdude@MBP2019 ~/nj/viewers (elide*?) $ clojure-lsp diagnostics
[  0%] clojure-lsp
The project is actually public: https://github.com/nextjournal/viewers

borkdude16:02:28

I can reproduce this also with the JVM version:

clojure -Sdeps '{:deps {com.github.clojure-lsp/clojure-lsp {:mvn/version "2022.02.01-20.02.32"}}}' -X clojure-lsp.api/diagnostics

ericdallo16:02:03

oh. that's interesting, will take a look

borkdude16:02:09

The last version which works on this project is 2021.11.16-16.52.14

ericdallo16:02:38

hum, good to know

ericdallo16:02:30

@U04V15CAJ I suspect it's related with a infinte loop when scanning local/roots

borkdude16:02:02

perhaps @U6T7M9DBR is also using local roots?

ericdallo16:02:21

nope, his issue is a different one, related with performance, not project stuck

borkdude16:02:15

sorry I pinged you Emil, you can unsubscribe :)

❤️ 1
borkdude16:02:53

why would clojure-lsp itself loop through local roots? just doing clojure -Spath gives you all the paths already?

ericdallo16:02:46

it's not related with classpath this one, but classpath hash

ericdallo16:02:15

maybe we should hash classpath strings instead of files

ericdallo16:02:24

it'd be easier and probably as assertive as before

ericdallo16:02:12

oh, but to get classpath string we need to get classpath, which is done later

ericdallo16:02:25

otherwise we would check classpath even if we already have it cached

ericdallo16:02:34

anyway, I'll try to fix the loop

ericdallo16:02:46

@U04V15CAJ for now we can add a configuration to fix it on .lsp/config.edn

{:source-paths ["modules/viewers/src"
                "modules/cljs-extensions/src"
                "modules/markdown/src"
                "modules/log/src"
                "modules/devcards/src"
                "modules/inspector/src"
                "modules/command-bar/src"
                "modules/view/src"
                "modules/devdocs/src"
                "modules/ui/src"]}

ericdallo16:02:44

For some reason, clojure-lsp is entering in a infinte loop when resolving a local-root that points to other previous root or something like that, I'll take a closer look later, but that config should fix it, it's what clojure-lsp would find automatically

🙏 1
ericdallo17:02:38

the "cyclic" deps local/root depedency is: deps.edn -> viewer -> devcards -> viewer -> devcards ...

ericdallo17:02:50

I have no idea how that works on deps.edn/clojure cli 😂

mkvlr18:02:39

I do think such mutually recursive deps are allowed in deps.edn?

ericdallo18:02:26

yeah, somehow it works on deps.edn, maybe it's statically only when it uses the deps or something, not sure

ericdallo18:02:21

@U04V15CAJ how deps.clj handle that kind of cyclic local-roots?

mkvlr18:02:55

it also uses tools.deps.alpha afaik

mkvlr18:02:46

does lsp have a separate classpath computation?

ericdallo18:02:52

actually clojure-lsp has two things: • classpath scan, this one we totally delegate to clojure , lein etc • source-paths discovery, where we find what are the source paths from deps.edn manually scanning it

ericdallo18:02:02

this issue is related with the latter

ericdallo18:02:13

clojure-lsp logs this:

2022-02-07T18:36:26.482Z  WARN [clojure-lsp.source-paths:78] - Max deps source-paths resolve level found 500 , maybe a cyclic dependency?
2022-02-07T18:36:26.522Z  INFO [clojure-lsp.source-paths:194] - Empty deps.edn source-paths, using default source-paths: #{"src" "test"}
so we already have a ciclyc dependency check

ericdallo18:02:24

why that doesn't work for viewers project :thinking_face:

mkvlr18:02:22

should we keep a set of paths we’ve already looked at and only look at each path once?

ericdallo18:02:57

yeah, I was thinking about something like that, I think that could work

borkdude18:02:10

> I do think such mutually recursive deps are allowed in deps.edn? Mutually recursive doesn't sound right. I would verify in #tools-deps if this is supported, but it sounds like a bad idea to begin with. Dependencies should form an acyclic graph/tree.

mkvlr18:02:21

it does work to produce a classpath from it

borkdude18:02:05

If you look at the output of clojure -Stree in viewers you can see that it ignores certain parts of the mutual recursiveness because the dep was declared at the top and it always chooses the top dependency

👀 1
borkdude18:02:21

That something works doesn't mean that it is supported or a good idea

ericdallo18:02:40

You are right, maybe clojure-lsp shold try to do the same

mkvlr18:02:55

this recursive nature is why it’s in a single repo to begin with…

mkvlr18:02:10

I’ll ask in tools-deps later

borkdude18:02:40

this is probably a sign that the mutual recursive deps both depend on something which should be a third dep

borkdude18:02:55

or that they must be merged to one dep

borkdude18:02:52

but it's good that this issue was caught, thanks both!

ericdallo18:02:36

yeah, good to know that exists and works somehow

ericdallo18:02:42

will think about it how to solve

mkvlr19:02:29

thank you both!

mkvlr19:02:21

is there a reason to rebuild this functionality in lsp or could we also rely on tools.deps.alpha to compute the source path?

borkdude19:02:51

I'm not sure what lsp is doing here. a local/root dependency is not a source path, it's a dependency

borkdude19:02:23

only :paths are project sources

mkvlr19:02:30

yeah but :local/root deps can add :paths again?

borkdude19:02:45

so can mvn deps and git deps, that's not the point?

borkdude19:02:43

I mean, the only difference conceptually between local/root and git deps is that they come from disk or a network. But both are just deps, not project sources.

borkdude19:02:36

so I would treat them like that. if users like to add more sources to their lsp config, they should do it manually probably

borkdude19:02:48

keep it simple

👍 1
jvtrigueros03:02:43

Thank you so much for this workaround, I was banging my head trying to figure out why LSP was stuck at 0%, I also have a deps.edn with local/root dependencies. 🙇

borkdude10:02:02

Btw, if you really want to get the directories from local/roots (still don't know why) you could just get the classpath using clojure and then filter out the directories that fall within the project root.

ericdallo11:02:11

@U04V15CAJ I was trying to avoid shelling out, maybe deps.clj can help with that

ericdallo11:02:54

This source-path discovery is something that is really fast during startup

borkdude12:02:37

Deps.clj doesn't help since it uses the same mechanisms as the clojure CLI

borkdude12:02:24

But since Clojure uses classpath caching both things shouldn't really matter on the second invocation and it will simplify your logic a lot

ericdallo12:02:13

the issue is that we don't call clojure 100%, only when cache is not available

ericdallo12:02:24

with that we would start calling clojure all the time

borkdude12:02:09

You still haven't explained why you are looking through local roots though

ericdallo12:02:51

to get all :paths/:extra-paths of subprojects if any, since users open the mono-repo as root, clojure-lsp needs to know all project source-code

ericdallo12:02:08

to when refactoring a var know that there is somewhere using, to navigation work and things like that

ericdallo12:02:48

imagine a project root use local/root for subprojects, we would check the subprojects to get the paths

ericdallo12:02:10

otherwise we would only know the paths of the root deps.edn, which usually is none for mono-repos

borkdude12:02:42

I still don't understand why getting the classpath via clojure is problematic. e.g. babashka does this every time you invoke it when you have deps in bb.edn and it's bloody fast if the classpath has already been calculated

ericdallo12:02:52

yeah, it could work, I was just avoiding to add something that consumes startup time, we already have too much time spent on startup

ericdallo12:02:40

but it would be easier for users indeed

ericdallo12:02:50

and probably a more straightforward config

borkdude12:02:14

I don't understand how this is different for users: it's only a change of what you're doing behind the scenes?

ericdallo12:02:26

ATM users need to configure properly both project-specs if the default doesn't work for they project and the source-paths/source-aliases if changing the default

ericdallo12:02:42

so 2 configs related to the project arch, which is not as good as a single one

ericdallo12:02:20

former is related with analysis, and the other on how other features would consider project source code

ericdallo12:02:37

making both check a single place configured by user would be better indeed

borkdude12:02:42

yes, but your local/root inspection thing vs walking through the dirs of the classpath and filter dirs that fall within the project, is an implementation detail - I don't get how changing that would affect users.

borkdude12:02:50

(except for fixing the above bugs)

ericdallo12:02:53

Would not affect negatively users, it won't work for users that configured source-aliases or source-paths but not project-specs , but I don't see a problem since that would not be consistent already

ericdallo12:02:03

I think I could make a POC testing your idea @U04V15CAJ, the tradeoffs are good and check how that affect startup time for big projects

borkdude12:02:09

it doesn't really affect startup time that much

borkdude12:02:18

just one time

ericdallo12:02:55

for deps.edn right

borkdude12:02:06

local/root is a deps.edn feature

ericdallo12:02:20

for lein projects I would need to have the discovery logic

ericdallo12:02:49

ATM we have the discovery logic for lein, deps and bb projects

borkdude12:02:07

bb projects are essentially the same as deps.edn projects

ericdallo12:02:18

only the lein ones that are not that fast

ericdallo12:02:38

but we don't have any recursive logic for lein projects

ericdallo12:02:47

so I'm ok leaving as it is the lein one

👍 1
borkdude12:02:53

sounds like a good compromise