diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 300b2818..45d833be 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -25,7 +25,7 @@ | cel | ✓ | | | | | | | circom | ✓ | | | | | `circom-lsp` | | clarity | ✓ | | | | | `clarinet` | -| clojure | ✓ | | | | ✓ | `clojure-lsp` | +| clojure | ✓ | ✓ | ✓ | | ✓ | `clojure-lsp` | | cmake | ✓ | ✓ | ✓ | | | `neocmakelsp`, `cmake-language-server` | | codeql | ✓ | ✓ | | | | `codeql` | | comment | ✓ | | | | | | diff --git a/runtime/queries/clojure/indents.scm b/runtime/queries/clojure/indents.scm new file mode 100644 index 00000000..115eea1f --- /dev/null +++ b/runtime/queries/clojure/indents.scm @@ -0,0 +1,60 @@ +; If a list has 2 elements on the first line, align to the second element. +; Exclude literals and special keywords that have different indentation rules. +(list_lit . (_) @first . (_) @anchor + (#same-line? @first @anchor) + (#set! "scope" "tail") + (#not-kind-eq? @first "bool_lit") + (#not-kind-eq? @first "nil_lit") + (#not-kind-eq? @first "str_lit") + (#not-kind-eq? @first "num_lit") + (#not-kind-eq? @first "kwd_lit") + (#not-match? @first "^(def|defn|defn-|defmacro|defmethod|defmulti|defonce|defprotocol|deftype|defrecord|defstruct|definline|definterface|deftest|ns|let|letfn|binding|loop|for|doseq|dotimes|when-let|if-let|when-some|if-some|with-.*|fn)$")) @align + +; If the first element in a list is also a list and on a line by itself, +; the outer list is aligned to it +(list_lit . (list_lit) @anchor . + (#set! "scope" "tail")) @align + +(list_lit . (list_lit) @anchor . (_) @second + (#not-same-line? @anchor @second) + (#set! "scope" "tail")) @align + +; If the first element in a list is not a list and on a line by itself, +; indent the list body by one level +(list_lit . (_) @first . + (#not-kind-eq? @first "bool_lit") + (#not-kind-eq? @first "nil_lit") + (#not-kind-eq? @first "str_lit") + (#not-kind-eq? @first "num_lit") + (#not-kind-eq? @first "kwd_lit") + (#not-match? @first "^(def|defn|defn-|defmacro|defmethod|defmulti|defonce|defprotocol|deftype|defrecord|defstruct|definline|definterface|deftest|ns|let|letfn|binding|loop|for|doseq|dotimes|when-let|if-let|when-some|if-some|with-.*|fn)$")) @indent + +(list_lit . (_) @first . (_) @second + (#not-same-line? @first @second) + (#not-kind-eq? @first "bool_lit") + (#not-kind-eq? @first "nil_lit") + (#not-kind-eq? @first "str_lit") + (#not-kind-eq? @first "num_lit") + (#not-kind-eq? @first "kwd_lit") + (#not-match? @first "^(def|defn|defn-|defmacro|defmethod|defmulti|defonce|defprotocol|deftype|defrecord|defstruct|definline|definterface|deftest|ns|let|letfn|binding|loop|for|doseq|dotimes|when-let|if-let|when-some|if-some|with-.*|fn)$")) @indent + +; If the first element is a literal, align the list to it +(list_lit . [(bool_lit) (nil_lit) (str_lit) (num_lit) (kwd_lit)] @anchor + (#set! "scope" "tail")) @align + +; Special indentation for def-like forms, let bindings, and other special forms +; These forms typically have the body indented by one level after the name/bindings +(list_lit . (sym_lit) @first + (#match? @first "^(def|defn|defn-|defmacro|defmethod|defmulti|defonce|defprotocol|deftype|defrecord|defstruct|definline|definterface|deftest|ns|let|letfn|binding|loop|for|doseq|dotimes|when-let|if-let|when-some|if-some|with-.*|fn)$")) @indent + +; Align vector/map elements when first two are on same line (e.g., let bindings) +(vec_lit . (_) @anchor . (_) @second + (#same-line? @anchor @second) + (#set! "scope" "tail")) @align + +(map_lit . (_) @anchor . (_) @second + (#same-line? @anchor @second) + (#set! "scope" "tail")) @align + +; Indent vectors, maps, and sets +[(vec_lit) (map_lit) (set_lit)] @indent diff --git a/runtime/queries/clojure/textobjects.scm b/runtime/queries/clojure/textobjects.scm new file mode 100644 index 00000000..1c53d22b --- /dev/null +++ b/runtime/queries/clojure/textobjects.scm @@ -0,0 +1,71 @@ +; Function definitions (defn, defn-, defmacro, defmethod, etc.) +(list_lit + . + (sym_lit) @_keyword + . + (sym_lit) + (_)* @function.inside + (#match? @_keyword "^(defn|defn-|defmacro|defmethod|defmulti|definline)$")) @function.around + +; Anonymous functions (fn) +(list_lit + . + (sym_lit) @_fn + (_)* @function.inside + (#match? @_fn "^fn$")) @function.around + +; Anonymous function shorthand #() +(anon_fn_lit + (_)* @function.inside) @function.around + +; deftype, defrecord, defprotocol +(list_lit + . + (sym_lit) @_keyword + . + (sym_lit) + (_)* @class.inside + (#match? @_keyword "^(deftype|defrecord|defprotocol|definterface|defstruct)$")) @class.around + +; Test definitions (deftest) +(list_lit + . + (sym_lit) @_keyword + . + (sym_lit) + (_)* @test.inside + (#match? @_keyword "^deftest$")) @test.around + +; Function parameters in vectors +(vec_lit + (_)* @parameter.inside) @parameter.around + +; List entries +(list_lit + (_) @entry.inside @entry.around) + +; Vector entries +(vec_lit + (_) @entry.inside @entry.around) + +; Map entries +(map_lit + (_) @entry.inside @entry.around) + +; Set entries +(set_lit + (_) @entry.inside @entry.around) + +; Comments +(comment) @comment.inside +(comment)+ @comment.around + +; Discard expressions (also treated as comments) +(dis_expr) @comment.inside + +; Comment special form (comment ...) +(list_lit + . + (sym_lit) @_comment + (_)* @comment.inside + (#match? @_comment "^comment$")) @comment.around