Skip to content

Keyword-lead-lists are improperly indented when using the fixed (tonsky-style) clojure-ts-indent-style #124

@samuelludwig

Description

@samuelludwig

Expected behavior

Given a cursor at the position marked by the $, in the following form:

(ns my-ns
  (:require
   [clojure.string :as str]
   [malli.core :as m]$
   [malli.transform :as mt]))

Hitting RET/ENTER/your-carriage-returning-preference should open a newline pre-indented at the same level of the leading :require keyword, like so:

(ns my-ns
  (:require
   [clojure.string :as str]
   [malli.core :as m]
   $
   [malli.transform :as mt]))

Actual behavior

It mucks up the indentation of the new+previous line to be two-spaces:

(ns my-ns
  (:require
   [clojure.string :as str]
    [malli.core :as m]
    $
   [malli.transform :as mt]))

Steps to reproduce the problem

A clean environment with clojure-ts-mode enabled and the config

(setopt clojure-ts-indent-style 'fixed)

should do it.

Environment & Version information

clojure-ts-mode version

clojure-ts-mode (0.6.0-snapshot)

tree-sitter-clojure grammar version

unstable-20250526

Emacs version

30.1

Operating system

Pop_OS

Note

Based on the following code, this formatting seems intentional:

(defvar clojure-ts--fixed-indent-rules
  ;; This is in contrast to semantic
  ;; fixed-indent-rules come from https://tonsky.me/blog/clojurefmt/
  `((clojure
     ((parent-is "source") parent-bol 0)
     ;; ((query "(list_lit . [(sym_lit) (kwd_lit)] _* @node)") parent 2)
     ;; Using the above `query' rule here doesn't always work because sometimes `node' is nil.
     ;; `query' requires `node' to be matched.
     ;; We really only care about the parent node being a function-call like list.
     ;; with it's first named child being a symbol
     ((lambda (node parent _)
        (and (clojure-ts--list-node-p parent)
             ;; Should we also check for keyword first child, as in (:k map) calls?
             (let ((first-child (treesit-node-child parent 0 t)))
               (or (clojure-ts--symbol-node-p first-child)
                   (clojure-ts--keyword-node-p first-child)))))
      parent 2)
     ((parent-is "vec_lit") parent 1)
     ((parent-is "map_lit") parent 1)
     ((parent-is "list_lit") parent 1)
     ((parent-is "set_lit") parent 2))))

I see the carve-out pontificating on treating leading-keywords as symbols.

Let's review the rules-as-written from the linked blog-post:

  1. Multi-line lists that start with a symbol are always indented with two spaces
  2. Other multi-line lists, vectors, maps and sets are aligned with the first element

For what it's worth, I DM'd tonsky himself, and asked him about indenting keyword-lead-lists, including when the keyword may act as/similar to a function call, and his ruling was consistent: that keyword-lead lists should not be indented like symbol-lead lists, even when the keyword acts as a symbol. I proposed the above ns form as an explicit example, and he confirmed that the 1-space indentation was correct:

(ns my-ns
  (:require
   [clojure.string :as str]
   [malli.core :as m]
   [malli.transform :as mt]))

This all, of course, only matters if we want the fixed indentation style to fully match the "tonsky-style". I would personally prefer the consistency of full-adherence, but an argument could be made that treating keywords as symbols is 'spiritually' valid (in that they can act similarly to symbols in some cases).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions