lsp

2025-09-08T10:24:19.394579Z

Hello, I'd like to use clojure-lsp to get the list of all qualified keywords in my clj project ... I'm using Emacs so i thought that i could reuse current/connected lsp-server db. I've downloaded a fork and connected to lsp-server using

(defun my/connect-to-clojure-lsp-repl ()                                                                                                         
  "Connect to the running clojure-lsp nREPL server"                                                                                               
  (interactive)                                                                                                                                   
  (let ((info (lsp-clojure-server-info-raw)))                                                                                                     
    (when-let (port (and (string-match "\"port\":\\([0-9]+\\)" info)                                                                              
                         (match-string 1 info)))                                                                                                  
      (cider-connect-clj `(:host "localhost" :port ,port)))))                                                                                     
but once i can read @clojure-lsp.db/db* I get lost with db structure and queries 😬 any advices or help that i coudl follow? thanks in advance! PS: getting all qualified keywords is first purpose but after that I'd like to query my code to find all calls for foo.my-service/def, where first argument is also a qualified-kw eg : (my-service/def foo xxxx)

ericdallo 2025-09-08T12:07:24.881579Z

1. Keep in mind the repl is used for development only, especially because prod binaries are compiled with graaal and that's not possible, also anything inside db can change names and cause breaking changes, the recommended approach is to use https://clojure-lsp.io/api/what-is-it/ 2. I believe you can (q/find-keywords db uri) , the clojure-lsp.queries is a useful ns

2025-09-08T12:38:47.787369Z

@ericdallo Thanks for your quick feedback! I think i have also kind of a bug thus my lsp cache db.transit.json file is approx 300 Mb and emacs takes more than 3 mins to generate it ... perhaps my project has edn resources that clj-kondo is including 🤔 or perhaps target folders??

ericdallo 2025-09-08T12:40:20.898119Z

yes, probably, you can check on server logs the classpath considered by clojure-lsp

karol.adamiec 2025-09-08T12:46:23.181259Z

I'd like to query my code to find all calls for foo.my-service/def, where first argument is also a qualified-kw eg : (my-service/def foo xxxx) i am under the impression that it works like that today. LSP will query and find ALL forms of namespaced keyword. , Destructured etc.

2025-09-08T17:12:29.366389Z

not sure if I followed the best path but here it is how i got it working (using bb, edamame, rewrite-clj ): 1. clj-kondo with

{:analysis {:var-usages true :namespace-definitions true :keywords true}                                                                       
                              :output {:format :json :analysis true}}
2. find-function-calls
(defn find-function-calls [analysis-json target-ns target-fn]                                                                                                     
  (try                                                                                                                                                                      
    (let [analysis (json/parse-string analysis-json true)                                                                                                                   
          var-usages (->> (:analysis analysis)                                                                                                                              
                         :var-usages                                                                                                                                        
                         (filter (fn [{:keys [to name]}]                                                                                                                    
                                  (and (= (str to) target-ns)                                                                                                               
                                       (= (str name) target-fn)))))]                                                                                                        
      var-usages)                                                                                                                                                           
    (catch Exception e                                                                                                                                                      
      (println "Error parsing analysis:" (.getMessage e))                                                                                                                   
      [])))  
3. for each usage read the file, find the function seexp, parse it and finally get second item (the qualified keyword)
(defn read-kw-on-second-position [file-ns file line col] 1 reference                                                                                                        
  (try                                                                                                                                                                      
    (with-open [rdr (io/reader file)]                                                                                                                                       
      (let [lines (vec (line-seq rdr))                                                                                                                                      
            ;; Get lines around the target position                                                                                                                         
            target-line (get lines (dec line) "")                                                                                                                           
            paren-pos (.lastIndexOf target-line "(" (min col (count target-line)))                                                                                          
            text-lines (subvec lines (dec line) (min (count lines) (+ line 20)))                                                                                            
            text (str/join "\n" text-lines)                                                                                                                                 
            text-from-paren (subs text (max 0 paren-pos))                                                                                                                   
            ]                                                                                                                                                               
        (when (System/getenv "DEBUG")                                                                                                                                       
          (println "Parsing at" file line col file-ns)                                                                                                                      
          )                                                                                                                                                                 
        (-> (rewrite-clj-parser/parse-string text-from-paren)                                                                                                               
            (rewrite-clj-node/children )                                                                                                                                    
            (nth  2)                                                                                                                                                        
            (rewrite-clj-node/string )                                                                                                                                      
            (eda/parse-string  {:auto-resolve {:current file-ns}}    )                                                                                                      
            )))                                                                                                                                                             
    (catch Exception e                                                                                                                                                      
        (println "Error parsing form at" file line col "-" (.getMessage e))                                                                                                 
      nil)))                                                                                                                                                                
PS: 😅 I thought that it would be more straightforward

ericdallo 2025-09-08T17:19:16.391829Z

glad you made it work, but you are doing what clojure-lsp does (and more) under the hood, which should be possible too

2025-09-08T17:20:34.812919Z

well i thought that i should start with clojure-lsp to build on top of it but i couldn't following docs 😬 I'd love to keep trying anyway

ericdallo 2025-09-08T17:21:09.725249Z

maybe we could add examples in docs indeed

🙏 1
2025-09-08T17:23:30.215189Z

I listed 3 steps, which ones could clojure-lsp do? ... perhaps I'm doing more steps 😅 too

ericdallo 2025-09-08T17:25:28.348439Z

1. clojure-lsp already does by default 2. (clojure-lsp.queries/find-references ...) or maybe via clojure-lsp.api 3. should be done on your side but not parsing with rewrite-clj, probably using the analysis clojure-lsp provide

2025-09-08T17:28:08.310639Z

sounds great, is there any clojure-lsp.queries/find-references examples for function calks? or clojure-lsp.api?

ericdallo 2025-09-08T17:29:06.580129Z

the clojure-lsp project itself uses a lot clojure-lsp.queries for all features, this is the best we have so far, we may want to add to the docs more examples

2025-09-08T17:29:52.330729Z

ok, I'll try to find the good parts and try to contribute to the repo too 🙂 thanks a lot!

ericdallo 2025-09-08T17:30:11.729299Z

cool, thanks

2025-09-08T17:30:26.737949Z

BTW: I love LSP & clj-lsp!

💜 1
Max 2025-09-08T14:46:04.485219Z

Currently, the organize imports code action reformats ns forms from this style:

(ns foo
  (:require [foo.bar]))
To this style:
(ns foo
  (:require
   [foo.bar]))
I like allowing clojure-lsp to manage and organize imports otherwise, but this reformatting can result in disruptively large whitespace diffs in existing codebases. Is there any way to prevent it from happening? If not, would y'all be open to changing this behavior?

practicalli-johnny 2025-09-11T07:01:39.744309Z

I create a specific format / blank space git commit (without business or technical changes). I find this much simpler in the long run.

ericdallo 2025-09-08T14:47:41.878959Z

you can customize that with https://clojure-lsp.io/settings/#ns-inner-blocks-indentation

🙏 1
2025-09-08T17:56:09.845909Z

Hi again 🙂 trying to use clojure-lsp from bb i get cyclic dependency error here https://github.com/clojure-lsp/clojure-lsp/blob/master/lib/src/clojure_lsp/shared.clj#L5 my bb.edn

{:deps {borkdude/edamame {:mvn/version "1.0.0"}                                                                                                                             
        rewrite-clj/rewrite-clj {:mvn/version "1.2.50"}                                                                                                                     
        com.github.clojure-lsp/clojure-lsp {:mvn/version "2025.06.06-19.04.49"}                                                                                             
                                                                                                                                                                            
}} 
Type:     clojure.lang.ExceptionInfo
Message:  Cyclic load dependency: clojure-lsp.db->clojure-lsp.config->[ clojure-lsp.shared ]->[ clojure-lsp.shared ]
Data:     {:type :sci/error, :line nil, :column nil, :file "clojure_lsp/shared.clj"}
Location: clojure_lsp/shared.clj:2:3

----- Context ------------------------------------------------------------------
1: (ns clojure-lsp.shared
2:   (:require
     ^--- Cyclic load dependency: clojure-lsp.db->clojure-lsp.config->[ clojure-lsp.shared ]->[ clojure-lsp.shared ]
3:    [babashka.fs :as fs]
4:    [clojure-lsp.logger :as logger]
5:    [clojure-lsp.shared :as shared]
6:    [clojure.core.async :refer [<! >! alts! chan go-loop timeout]]
7:    [ :as io]

2025-09-08T17:58:14.008359Z

BTW: also tried babashka pod (2024.04.22-11.50.26) but perhaps not aligned with clojure-lsp.api thus i got "Could not resolve symbol: lsp-api/references"

borkdude 2025-09-08T18:21:24.876199Z

that's because of this self-requiring namespace here: https://github.com/clojure-lsp/clojure-lsp/blob/e8d078f7bafda75a6bf2f09722ea79fee3b6ecc3/lib/src/clojure_lsp/shared.clj#L5 perhaps SCI can allow this, but perhaps it's not necessary to do this at all?

borkdude 2025-09-08T18:21:51.490089Z

anyway, most of clojure-lsp isn't bb-compatible I think so you'll have to use the pod. I'm sure it can be updated

👍 1
ericdallo 2025-09-08T19:58:45.303579Z

hum yes, we may need to update the pod

borkdude 2025-09-08T20:08:39.268719Z

in the registry you mean right?

ericdallo 2025-09-08T21:11:58.600479Z

yes!