clojure-emacs / clojure-mode

Emacs support for the Clojure(Script) programming language
914 stars 246 forks source link

Better way to maintain custom indentation #464

Open poernahi opened 6 years ago

poernahi commented 6 years ago

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.

https://github.com/clojure-emacs/clojure-mode/issues/398 The idea here is to infer indentation based on arg list. The concern was that it is unreliable.

https://github.com/clojure-emacs/clojure-mode/issues/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 https://github.com/omcljs/om/issues/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?

Thank you for contributing!

j-cr commented 6 years ago

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?

bbatsov commented 6 years ago

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

DogLooksGood commented 4 years ago

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.

rap1ds commented 3 years ago

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)
jdf-id-au commented 2 years ago

@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)