Skip to content

Commit 8ed99ba

Browse files
committed
Support alignment for reader conditionals
Fixes #483
1 parent e6bd584 commit 8ed99ba

File tree

4 files changed

+104
-39
lines changed

4 files changed

+104
-39
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## master (unreleased)
44

5+
### New features
6+
7+
* [#483](https://github.com/clojure-emacs/clojure-mode/issues/483): Support alignment for reader conditionals, with the new `clojure-align-reader-conditionals` user option.
8+
9+
## 5.9.1 (2018-08-27)
10+
11+
* [#485](https://github.com/clojure-emacs/clojure-mode/issues/485): Fix a regression in `end-f-defun`.
12+
513
## 5.9.0 (2018-08-18)
614

715
### Changes

clojure-mode.el

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
;; Artur Malabarba <bruce.connor.am@gmail.com>
1111
;; URL: http://github.com/clojure-emacs/clojure-mode
1212
;; Keywords: languages clojure clojurescript lisp
13-
;; Version: 5.9.0
13+
;; Version: 5.9.1
1414
;; Package-Requires: ((emacs "25.1"))
1515

1616
;; This file is not part of GNU Emacs.
@@ -439,7 +439,7 @@ ENDP and DELIM."
439439
(t)))))
440440

441441
(defconst clojure--collection-tag-regexp "#\\(::[a-zA-Z0-9._-]*\\|:?\\([a-zA-Z0-9._-]+/\\)?[a-zA-Z0-9._-]+\\)"
442-
"Collection reader macro tag regexp.
442+
"Collection reader macro tag regexp.
443443
It is intended to check for allowed strings that can come before a
444444
collection literal (e.g. '[]' or '{}'), as reader macro tags.
445445
This includes #fully.qualified/my-ns[:kw val] and #::my-ns{:kw
@@ -1090,6 +1090,12 @@ will align the values like this:
10901090
:safe #'booleanp
10911091
:type 'boolean)
10921092

1093+
(defcustom clojure-align-reader-conditionals nil
1094+
"Whether to align reader conditionals, as if they were maps."
1095+
:package-version '(clojure-mode . "5.10")
1096+
:safe #'booleanp
1097+
:type 'boolean)
1098+
10931099
(defcustom clojure-align-binding-forms
10941100
'("let" "when-let" "when-some" "if-let" "if-some" "binding" "loop"
10951101
"doseq" "for" "with-open" "with-local-vars" "with-redefs")
@@ -1104,6 +1110,10 @@ will align the values like this:
11041110
:safe #'listp
11051111
:type '(repeat string))
11061112

1113+
(defvar clojure--beginning-of-reader-conditional-regexp
1114+
"#\\?@(\\|#\\?("
1115+
"Regexp denoting the beginning of a reader conditional.")
1116+
11071117
(defun clojure--position-for-alignment ()
11081118
"Non-nil if the sexp around point should be automatically aligned.
11091119
This function expects to be called immediately after an
@@ -1118,32 +1128,36 @@ For instance, in a map literal point is left immediately before
11181128
the first key; while, in a let-binding, point is left inside the
11191129
binding vector and immediately before the first binding
11201130
construct."
1121-
;; Are we in a map?
1122-
(or (and (eq (char-before) ?{)
1123-
(not (eq (char-before (1- (point))) ?\#)))
1124-
;; Are we in a cond form?
1125-
(let* ((fun (car (member (thing-at-point 'symbol) clojure-align-cond-forms)))
1126-
(method (and fun (clojure--get-indent-method fun)))
1127-
;; The number of special arguments in the cond form is
1128-
;; the number of sexps we skip before aligning.
1129-
(skip (cond ((numberp method) method)
1130-
((null method) 0)
1131-
((sequencep method) (elt method 0)))))
1132-
(when (and fun (numberp skip))
1133-
(clojure-forward-logical-sexp skip)
1134-
(comment-forward (point-max))
1135-
fun)) ; Return non-nil (the var name).
1136-
;; Are we in a let-like form?
1137-
(when (member (thing-at-point 'symbol)
1138-
clojure-align-binding-forms)
1139-
;; Position inside the binding vector.
1140-
(clojure-forward-logical-sexp)
1141-
(backward-sexp)
1142-
(when (eq (char-after) ?\[)
1143-
(forward-char 1)
1144-
(comment-forward (point-max))
1145-
;; Return non-nil.
1146-
t))))
1131+
(let ((point (point)))
1132+
;; Are we in a map?
1133+
(or (and (eq (char-before) ?{)
1134+
(not (eq (char-before (1- point)) ?\#)))
1135+
;; Are we in a reader conditional?
1136+
(and clojure-align-reader-conditionals
1137+
(looking-back clojure--beginning-of-reader-conditional-regexp (- (point) 4)))
1138+
;; Are we in a cond form?
1139+
(let* ((fun (car (member (thing-at-point 'symbol) clojure-align-cond-forms)))
1140+
(method (and fun (clojure--get-indent-method fun)))
1141+
;; The number of special arguments in the cond form is
1142+
;; the number of sexps we skip before aligning.
1143+
(skip (cond ((numberp method) method)
1144+
((null method) 0)
1145+
((sequencep method) (elt method 0)))))
1146+
(when (and fun (numberp skip))
1147+
(clojure-forward-logical-sexp skip)
1148+
(comment-forward (point-max))
1149+
fun)) ; Return non-nil (the var name).
1150+
;; Are we in a let-like form?
1151+
(when (member (thing-at-point 'symbol)
1152+
clojure-align-binding-forms)
1153+
;; Position inside the binding vector.
1154+
(clojure-forward-logical-sexp)
1155+
(backward-sexp)
1156+
(when (eq (char-after) ?\[)
1157+
(forward-char 1)
1158+
(comment-forward (point-max))
1159+
;; Return non-nil.
1160+
t)))))
11471161

11481162
(defun clojure--find-sexp-to-align (end)
11491163
"Non-nil if there's a sexp ahead to be aligned before END.
@@ -1152,10 +1166,14 @@ Place point as in `clojure--position-for-alignment'."
11521166
(let ((found))
11531167
(while (and (not found)
11541168
(search-forward-regexp
1155-
(concat "{\\|(" (regexp-opt
1156-
(append clojure-align-binding-forms
1157-
clojure-align-cond-forms)
1158-
'symbols))
1169+
(concat (when clojure-align-reader-conditionals
1170+
(concat clojure--beginning-of-reader-conditional-regexp
1171+
"\\|"))
1172+
"{\\|("
1173+
(regexp-opt
1174+
(append clojure-align-binding-forms
1175+
clojure-align-cond-forms)
1176+
'symbols))
11591177
end 'noerror))
11601178

11611179
(let ((ppss (syntax-ppss)))
@@ -1955,7 +1973,8 @@ This will skip over sexps that don't represent objects, so that ^hints and
19551973
(clojure-forward-logical-sexp 1)
19561974
(clojure-backward-logical-sexp 1)
19571975
(looking-at-p first-form))
1958-
(scan-error nil)))
1976+
(scan-error nil)
1977+
(end-of-buffer nil)))
19591978

19601979
(defun clojure-sexp-starts-until-position (position)
19611980
"Return the starting points for forms before POSITION.
@@ -1992,10 +2011,11 @@ testing, give an easy way to turn this new behavior off."
19922011
(setq haystack (cdr haystack))))
19932012
found))
19942013

1995-
(defun clojure-beginning-of-defun-function ()
2014+
(defun clojure-beginning-of-defun-function (&optional n)
19962015
"Go to top level form.
19972016
Set as `beginning-of-defun-function' so that these generic
1998-
operators can be used."
2017+
operators can be used. Given a positive N it will do it that
2018+
many times."
19992019
(let ((beginning-of-defun-function nil))
20002020
(if (and clojure-toplevel-inside-comment-form
20012021
(clojure-top-level-form-p "comment"))
@@ -2013,8 +2033,8 @@ operators can be used."
20132033
(clojure-sexp-starts-until-position
20142034
clojure-comment-end))))
20152035
(progn (goto-char sexp-start) t)
2016-
(progn (beginning-of-defun) t))))
2017-
(progn (beginning-of-defun) t))))
2036+
(beginning-of-defun n))))
2037+
(beginning-of-defun n))))
20182038

20192039
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20202040
;;

test/clojure-mode-indentation-test.el

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,8 @@ x
485485
"Verify that all FORMs correspond to a properly indented sexps."
486486
(declare (indent defun))
487487
`(ert-deftest ,(intern (format "test-align-%s" name)) ()
488-
(let ((clojure-align-forms-automatically t))
488+
(let ((clojure-align-forms-automatically t)
489+
(clojure-align-reader-conditionals t))
489490
,@(mapcar (lambda (form)
490491
`(with-temp-buffer
491492
(clojure-mode)
@@ -596,6 +597,28 @@ x
596597
:b {:a :a,
597598
:aa :a}}")
598599

600+
(def-full-align-test reader-conditional
601+
"#?(:clj 2
602+
:cljs 2)")
603+
604+
(def-full-align-test reader-conditional-splicing
605+
"#?@(:clj [2]
606+
:cljs [2])")
607+
608+
(ert-deftest reader-conditional-alignment-disabled-by-default ()
609+
(let ((content "#?(:clj 2\n :cljs 2)"))
610+
(with-temp-buffer
611+
(clojure-mode)
612+
(insert content)
613+
(call-interactively #'clojure-align)
614+
(should (string= (buffer-string) content)))
615+
(with-temp-buffer
616+
(clojure-mode)
617+
(setq-local clojure-align-reader-conditionals t)
618+
(insert content)
619+
(call-interactively #'clojure-align)
620+
(should-not (string= (buffer-string) content)))))
621+
599622
(ert-deftest clojure-align-remove-extra-commas ()
600623
(with-temp-buffer
601624
(clojure-mode)

test/clojure-mode-sexp-test.el

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,21 @@ and point left there."
6565
(wrong))"
6666
(let ((clojure-toplevel-inside-comment-form t))
6767
(beginning-of-defun))
68-
(should (looking-at-p "(right)"))))
68+
(should (looking-at-p "[[:space:]]*(right)"))))
69+
70+
(ert-deftest test-clojure-end-of-defun-function ()
71+
(clojure-buffer-with-text
72+
"
73+
(first form)
74+
|
75+
(second form)
76+
77+
(third form)"
78+
79+
(end-of-defun)
80+
(backward-char)
81+
(should (looking-back "(second form)"))))
82+
6983

7084
(ert-deftest test-sexp-with-commas ()
7185
(with-temp-buffer

0 commit comments

Comments
 (0)