Skip to content

Commit ec312af

Browse files
committed
Merge pull request #319 from Malabarba/master
Many indentation improvements
2 parents d3f12ab + c3a4235 commit ec312af

File tree

2 files changed

+75
-22
lines changed

2 files changed

+75
-22
lines changed

clojure-mode.el

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,14 @@ point) to check."
638638
(replace-match (clojure-docstring-fill-prefix))))
639639
(lisp-indent-line)))
640640

641+
(defun clojure--symbol-get (function-name property)
642+
"Return the symbol PROPERTY for the symbol named FUNCTION-NAME.
643+
FUNCTION-NAME is a string. If it contains a `/', also try only the part after the `/'."
644+
(or (get (intern-soft function-name) property)
645+
(and (string-match "/\\([^/]+\\)\\'" function-name)
646+
(get (intern-soft (match-string 1 function-name))
647+
property))))
648+
641649
(defun clojure-indent-function (indent-point state)
642650
"When indenting a line within a function call, indent properly.
643651
@@ -670,7 +678,12 @@ This function also returns nil meaning don't specify the indentation."
670678
(if (not (> (save-excursion (forward-line 1) (point))
671679
calculate-lisp-indent-last-sexp))
672680
(progn (goto-char calculate-lisp-indent-last-sexp)
673-
(beginning-of-line)
681+
(skip-chars-backward "[:blank:]")
682+
;; We're done if we find the start of line,
683+
(while (and (not (looking-at-p "^"))
684+
;; or start of sexp.
685+
(ignore-errors (forward-sexp -1) t))
686+
(skip-chars-backward "[:blank:]"))
674687
(parse-partial-sexp (point)
675688
calculate-lisp-indent-last-sexp 0 t)))
676689
;; Indent under the list or under the first sexp on the same
@@ -682,13 +695,13 @@ This function also returns nil meaning don't specify the indentation."
682695
(let* ((function (buffer-substring (point)
683696
(progn (forward-sexp 1) (point))))
684697
(open-paren (elt state 1))
685-
(method nil)
686-
(function-tail (car
687-
(reverse
688-
(split-string (substring-no-properties function) "/")))))
689-
(setq method (or (get (intern-soft function) 'clojure-indent-function)
690-
(get (intern-soft function-tail) 'clojure-indent-function)))
691-
(cond ((member (char-after open-paren) '(?\[ ?\{))
698+
(forward-sexp-function #'clojure-forward-logical-sexp)
699+
(method (clojure--symbol-get function 'clojure-indent-function)))
700+
;; Maps, sets, vectors and reader conditionals.
701+
(cond ((or (member (char-after open-paren) '(?\[ ?\{))
702+
(ignore-errors
703+
(and (eq (char-before open-paren) ?\?)
704+
(eq (char-before (1- open-paren)) ?\#))))
692705
(goto-char open-paren)
693706
(1+ (current-column)))
694707
((or (eq method 'defun)
@@ -724,15 +737,15 @@ move upwards in an sexp to check for contextual indenting."
724737
(when (looking-at "\\sw\\|\\s_")
725738
(let* ((start (point))
726739
(fn (buffer-substring start (progn (forward-sexp 1) (point))))
727-
(meth (get (intern-soft fn) 'clojure-backtracking-indent)))
740+
(meth (clojure--symbol-get fn 'clojure-backtracking-indent)))
728741
(let ((n 0))
729742
(when (< (point) indent-point)
730743
(condition-case ()
731744
(progn
732745
(forward-sexp 1)
733746
(while (< (point) indent-point)
734747
(parse-partial-sexp (point) indent-point 1 t)
735-
(incf n)
748+
(cl-incf n)
736749
(forward-sexp 1)))
737750
(error nil)))
738751
(push n path))
@@ -752,7 +765,7 @@ move upwards in an sexp to check for contextual indenting."
752765
(condition-case ()
753766
(progn
754767
(backward-up-list 1)
755-
(incf depth))
768+
(cl-incf depth))
756769
(error (setq depth clojure-max-backtracking)))))
757770
indent))
758771

@@ -1071,22 +1084,27 @@ Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")."
10711084

10721085

10731086
;;; Sexp navigation
1087+
(defun clojure--looking-at-logical-sexp ()
1088+
"Return non-nil if sexp after point represents code.
1089+
Sexps that don't represent code are ^metadata or #reader.macros."
1090+
(forward-sexp 1)
1091+
(forward-sexp -1)
1092+
(not (looking-at-p "\\^\\|#[[:alpha:]]")))
1093+
10741094
(defun clojure-forward-logical-sexp (&optional n)
10751095
"Move forward N logical sexps.
10761096
This will skip over sexps that don't represent objects, so that ^hints and
10771097
#reader.macros are considered part of the following sexp."
10781098
(interactive "p")
1079-
(if (< n 0)
1080-
(clojure-backward-logical-sexp (- n))
1081-
(while (> n 0)
1082-
;; Non-logical sexps.
1083-
(while (progn (forward-sexp 1)
1084-
(forward-sexp -1)
1085-
(looking-at-p "\\^\\|#[[:alpha:]]"))
1086-
(forward-sexp 1))
1087-
;; The actual sexp
1088-
(forward-sexp 1)
1089-
(setq n (1- n)))))
1099+
(let ((forward-sexp-function nil))
1100+
(if (< n 0)
1101+
(clojure-backward-logical-sexp (- n))
1102+
(while (> n 0)
1103+
(while (not (clojure--looking-at-logical-sexp))
1104+
(forward-sexp 1))
1105+
;; The actual sexp
1106+
(forward-sexp 1)
1107+
(setq n (1- n))))))
10901108

10911109
(defun clojure-backward-logical-sexp (&optional n)
10921110
"Move backward N logical sexps.

test/clojure-mode-indentation-test.el

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,41 @@ values of customisable variables."
153153
(ala/bala top
154154
|one)")
155155

156+
(check-indentation form-with-metadata
157+
"
158+
(ns ^:doc app.core
159+
|(:gen-class))"
160+
"
161+
(ns ^:doc app.core
162+
|(:gen-class))")
163+
164+
(check-indentation multiline-sexps
165+
"
166+
[[
167+
2] a
168+
|x]"
169+
"
170+
[[
171+
2] a
172+
|x]")
173+
174+
(check-indentation reader-conditionals
175+
"
176+
#?(:clj :foo
177+
|:cljs :bar)"
178+
"
179+
#?(:clj :foo
180+
|:cljs :bar)")
181+
182+
(check-indentation backtracking-with-aliases
183+
"
184+
(clojure.core/letfn [(twice [x]
185+
|(* x 2))]
186+
:a)"
187+
"
188+
(clojure.core/letfn [(twice [x]
189+
|(* x 2))]
190+
:a)")
156191

157192
(provide 'clojure-mode-indentation-test)
158193

0 commit comments

Comments
 (0)