lsp

schadocalex 2026-03-11T17:18:21.383459Z

Hi there! Considering the following use case:

(defn get-data [{:keys [data]}]
  data)

(defmacro $ [op arg1 & args]
  `(~op ~arg1))

($ get-data {:| })
;      cursor ^
Is there a way to write a clj-kondo hook which would keep autocompletion for the args (keys) working? (Same as with a direct call to get-data). I haven't found a solution yet. I'm using Calva in VSCode, not sure whether that matters. Thanks! (Originally posted in #clj-kondo)

ericdallo 2026-03-11T17:21:05.194599Z

If your custom hook expand to something that defines those keywords it may work, the problem is get a working hook for this

ericdallo 2026-03-11T17:22:15.802759Z

clojure-lsp just suggests keywords that are available in analysis, so if your hook expands to a:

(do 
  :data 
  ...rest-of-the-hook...)
it will be available

schadocalex 2026-03-11T17:34:03.696199Z

It's not just to make it available, but to be the same as a direct call to the function with a closed list of keywords. Currently there is autocompletion but with all known keywords.

ericdallo 2026-03-11T17:36:08.797779Z

yeah that specifically would be quite hard to support, right now clojure-lsp scans for destructuring args in the function being called, but with a macro/hook I can't see how we would make that work

schadocalex 2026-03-11T17:59:51.336089Z

With clj-kondo, rewriting macros in hooks for linting works well, there might be a way to use that too in place of scanning args, for unknown macros.

schadocalex 2026-03-11T18:06:29.134809Z

Real use case is for libraries like uix for component props or dom properties (writing html-like trees).

ericdallo 2026-03-11T18:38:49.101649Z

maybe we could have some kinda of meta that you could append to the keyword so clojure-lsp could work on that, but I think I need more detailed cases, especially a hook

schadocalex 2026-03-11T19:35:49.255359Z

For the original easy case, the hook could be something like that:

(defn $ [{:keys [node]}]
  {:node (with-meta
           (api/list-node (rest (:children node)))
           (meta node))})
Analyzing the list-node as a function call might just work (as I expected at the beginning). It won't handle the case where the first argument is a keyword representing a HTML element, unless we create fake functions for those. Specifying a list of keywords for a map-node can do the job:
(defn $ [{:keys [node]}]
  (let [[element props-map & _children] (rest (:children node))]
    {:node (with-meta
             (api/list-node
              [(api/token-node 'identity)
               (with-meta
                 props-map ; assume it is already a map-node
                 (merge (meta props-map)
                        {:clj-kondo/allowed-keys (get-html-attrs-for-element element)}))])
             (meta node))}))
We could use this API for the first case too. Alternatively we could call a api/reg-map-keys! or similar.