Skip to content

Better way to maintain custom indentation #464

Open

Description

@poernahi

Problem

Sometimes, it is desirable to indent specific forms in a non-standard(?) way. Below is a couple of use-case exhibit to better illustrate the issue.

;;;; om

;; default
(dom/div #js{:className "container"}
         (dom/ul #js{:className "list"}
                 (dom/li #js{:className "item"} "Hello")))

;; better
(dom/div #js{:className "container"}
  (dom/ul #js{:className "list"}
    (dom/li #js{:className "item"} "Hello")))

;; this is ok too?
(dom/div #js{:className "container"}
 (dom/ul #js{:className "list"}
  (dom/li #js{:className "item"} "Hello")))


;;;; re-frame

;; default
(reg-cofx :now      
          (fn [cofx _]
            (assoc cofx :now (js/Date.))))

;; better
(reg-cofx :now      
  (fn [cofx _]
    (assoc cofx :now (js/Date.))))


;;;; Fulcro

;; default
(defmutation something
  "docstring"
  [args]
  (action [{:keys [state]}]
          (do-something-to state))
  (remote [env]
          (some-logic env)))

;; better
(defmutation something
  "docstring"
  [args]
  (action [{:keys [state]}]
    (do-something-to state))
  (remote [env]
    (some-logic env)))

Current Solutions

Currently, the most practical solution is to maintain individual/per-project list of symbol-indentation lookup table. A relatively small example is shown below, but imagine that for om, one would need to list every single dom element in the file.
https://github.com/metabase/metabase/blob/master/.dir-locals.el

Other Solutions

This issue has been reported a couple times in the past and some suggestions have been voiced and/or implemented, but I feel that we have not really solved this problem.

#398
The idea here is to infer indentation based on arg list. The concern was that it is unreliable.

#309
Metadata based indentation is technically a good solution. This requires buy-in from library maintainer and at least in one case was shot down as seen here omcljs/om#728 . These are Cognitect staff, so I feel it will be very unlikely we will be getting any metadata support in core libraries. I speculate that quite a number of library authors and Cognitect staffs are using Cursive and simply don't have this problem.

Proposed Solutions

Maybe we can brainstorm for a better way to handle this?

  • Maybe an option that allow user to override indentation and trigger auto-indent of current form with a keystroke?
  • Shorten the symbol lookup table by allowing regex pattern matching?
  • Infer indentation based on other uses in the project/file? User will only have to fix indentation once and emacs will try to conform to the file's current indentation rule. Not sure what to do if there is only one use in a project/file.
  • I'm sure clojure-mode users will have other ideas?

Thank you for contributing!

Activity

  1. j-cr commented on Jul 31, 2018

    @j-cr
    Contributor

    I think the (only?) correct way to solve it is to use metadata, as proposed and implemented in #309 and subsequent issues. We should promote this and apply pressure to library maintainers, encouraging them to correctly annotate their macros and special functions.

    The cider-free, clojure-mode only workaround is to use define-clojure-indent, which was there forever. I think adding regex support to that, as you proposed, may be a good idea though. Another idea that comes to mind is to have some centralized repository of symbols + indentation rules, so users wouldn't be forced to add every symbol themselves.

    EDIT: by the way, how is it done in Cursive?

  2. bbatsov commented on Jul 31, 2018

    @bbatsov
    Member

    I think it simply maintains a list of custom indentations for some popular libraries.

  3. DogLooksGood commented on Nov 25, 2019

    @DogLooksGood

    If the alias of namespace can be resolved, like s/fdef -> clojure.spec.alpha/fdef statically , a community maintained list could be the solution. Currently, people use different alias style, it's hard to share this configuration.

  4. rap1ds commented on Jan 25, 2021

    @rap1ds

    One more example, which might be a bit trickier:

    ;;;; datomic
    
    ;; default
    
    (d/q '[:find ?name ?year
           :where [?artist :artist/name ?name]
           [?artist :artist/startYear ?year]
           [(< ?year 1600)]]
         db)
    
    ;; better
    
    (d/q '[:find ?name ?year
           :where [?artist :artist/name ?name]
                  [?artist :artist/startYear ?year]
                  [(< ?year 1600)]]
         db)
    
  5. jdf-id-au commented on Dec 22, 2021

    @jdf-id-au

    @poernahi I don't have time to look into this properly, but what about this controversial solution for your "this is ok too" example?

    (setq
     clojure-indent-style 'always-indent
     lisp-indent-offset 1
     lsp-enable-indentation nil)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions