From 39a9ee36aa49d68f1ac4edcad21f1a133148ba47 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 10 Sep 2015 18:19:56 +0100 Subject: [PATCH 1/5] [Fix #304] Indentation of forms with metadata specs We were indenting like this (ns ^:doc app.core (:gen-class)) Now we indent like this (ns ^:doc app.core (:gen-class)) --- clojure-mode.el | 24 +++++++++++++----------- test/clojure-mode-indentation-test.el | 8 ++++++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index e15275c1..bfefe0e4 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -683,6 +683,7 @@ This function also returns nil meaning don't specify the indentation." (progn (forward-sexp 1) (point)))) (open-paren (elt state 1)) (method nil) + (forward-sexp-function #'clojure-forward-logical-sexp) (function-tail (car (reverse (split-string (substring-no-properties function) "/"))))) @@ -1076,17 +1077,18 @@ Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")." This will skip over sexps that don't represent objects, so that ^hints and #reader.macros are considered part of the following sexp." (interactive "p") - (if (< n 0) - (clojure-backward-logical-sexp (- n)) - (while (> n 0) - ;; Non-logical sexps. - (while (progn (forward-sexp 1) - (forward-sexp -1) - (looking-at-p "\\^\\|#[[:alpha:]]")) - (forward-sexp 1)) - ;; The actual sexp - (forward-sexp 1) - (setq n (1- n))))) + (let ((forward-sexp-function nil)) + (if (< n 0) + (clojure-backward-logical-sexp (- n)) + (while (> n 0) + ;; Non-logical sexps. + (while (progn (forward-sexp 1) + (forward-sexp -1) + (looking-at-p "\\^\\|#[[:alpha:]]")) + (forward-sexp 1)) + ;; The actual sexp + (forward-sexp 1) + (setq n (1- n)))))) (defun clojure-backward-logical-sexp (&optional n) "Move backward N logical sexps. diff --git a/test/clojure-mode-indentation-test.el b/test/clojure-mode-indentation-test.el index 5c547cfd..60485cd4 100644 --- a/test/clojure-mode-indentation-test.el +++ b/test/clojure-mode-indentation-test.el @@ -153,6 +153,14 @@ values of customisable variables." (ala/bala top |one)") +(check-indentation form-with-metadata + " +(ns ^:doc app.core +|(:gen-class))" +" +(ns ^:doc app.core + |(:gen-class))") + (provide 'clojure-mode-indentation-test) From 40799136f88f842739b469b7fbac47adc68484b9 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 10 Sep 2015 18:22:12 +0100 Subject: [PATCH 2/5] [Fix #308 Fix #287 Fix #45] Corner-case indentation of multi-line sexps We were indenting like this [[ 2] a x] Now we indent like this [[ 2] a x] --- clojure-mode.el | 7 ++++++- test/clojure-mode-indentation-test.el | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/clojure-mode.el b/clojure-mode.el index bfefe0e4..04a627e7 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -670,7 +670,12 @@ This function also returns nil meaning don't specify the indentation." (if (not (> (save-excursion (forward-line 1) (point)) calculate-lisp-indent-last-sexp)) (progn (goto-char calculate-lisp-indent-last-sexp) - (beginning-of-line) + (skip-chars-backward "[:blank:]") + ;; We're done if we find the start of line, + (while (and (not (looking-at-p "^")) + ;; or start of sexp. + (ignore-errors (forward-sexp -1) t)) + (skip-chars-backward "[:blank:]")) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t))) ;; Indent under the list or under the first sexp on the same diff --git a/test/clojure-mode-indentation-test.el b/test/clojure-mode-indentation-test.el index 60485cd4..de0176d6 100644 --- a/test/clojure-mode-indentation-test.el +++ b/test/clojure-mode-indentation-test.el @@ -161,6 +161,16 @@ values of customisable variables." (ns ^:doc app.core |(:gen-class))") +(check-indentation multiline-sexps + " +[[ + 2] a +|x]" +" +[[ + 2] a + |x]") + (provide 'clojure-mode-indentation-test) From e8c71e1df617f147ee6eea523ab5e7b92afbebd6 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 10 Sep 2015 18:38:45 +0100 Subject: [PATCH 3/5] [Fix #292] Indentation in reader conditionals Before #?(:clj :foo :cljs :bar) Now #?(:clj :foo :cljs :bar) --- clojure-mode.el | 6 +++++- test/clojure-mode-indentation-test.el | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/clojure-mode.el b/clojure-mode.el index 04a627e7..d53673cd 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -694,7 +694,11 @@ This function also returns nil meaning don't specify the indentation." (split-string (substring-no-properties function) "/"))))) (setq method (or (get (intern-soft function) 'clojure-indent-function) (get (intern-soft function-tail) 'clojure-indent-function))) - (cond ((member (char-after open-paren) '(?\[ ?\{)) + ;; Maps, sets, vectors and reader conditionals. + (cond ((or (member (char-after open-paren) '(?\[ ?\{)) + (ignore-errors + (and (eq (char-before open-paren) ?\?) + (eq (char-before (1- open-paren)) ?\#)))) (goto-char open-paren) (1+ (current-column))) ((or (eq method 'defun) diff --git a/test/clojure-mode-indentation-test.el b/test/clojure-mode-indentation-test.el index de0176d6..867126ce 100644 --- a/test/clojure-mode-indentation-test.el +++ b/test/clojure-mode-indentation-test.el @@ -171,6 +171,14 @@ values of customisable variables." 2] a |x]") +(check-indentation reader-conditionals + " +#?(:clj :foo +|:cljs :bar)" + " +#?(:clj :foo + |:cljs :bar)") + (provide 'clojure-mode-indentation-test) From 8db56ec10905d00da9faf4b86659dd455216b247 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 10 Sep 2015 19:13:51 +0100 Subject: [PATCH 4/5] [Fix #278] Understand namespace aliases in backtracking indent --- clojure-mode.el | 21 ++++++++++++--------- test/clojure-mode-indentation-test.el | 9 +++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index d53673cd..0fbfec3c 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -638,6 +638,14 @@ point) to check." (replace-match (clojure-docstring-fill-prefix)))) (lisp-indent-line))) +(defun clojure--symbol-get (function-name property) + "Return the symbol PROPERTY for the symbol named FUNCTION-NAME. +FUNCTION-NAME is a string. If it contains a `/', also try only the part after the `/'." + (or (get (intern-soft function-name) property) + (and (string-match "/\\([^/]+\\)\\'" function-name) + (get (intern-soft (match-string 1 function-name)) + property)))) + (defun clojure-indent-function (indent-point state) "When indenting a line within a function call, indent properly. @@ -687,13 +695,8 @@ This function also returns nil meaning don't specify the indentation." (let* ((function (buffer-substring (point) (progn (forward-sexp 1) (point)))) (open-paren (elt state 1)) - (method nil) (forward-sexp-function #'clojure-forward-logical-sexp) - (function-tail (car - (reverse - (split-string (substring-no-properties function) "/"))))) - (setq method (or (get (intern-soft function) 'clojure-indent-function) - (get (intern-soft function-tail) 'clojure-indent-function))) + (method (clojure--symbol-get function 'clojure-indent-function))) ;; Maps, sets, vectors and reader conditionals. (cond ((or (member (char-after open-paren) '(?\[ ?\{)) (ignore-errors @@ -734,7 +737,7 @@ move upwards in an sexp to check for contextual indenting." (when (looking-at "\\sw\\|\\s_") (let* ((start (point)) (fn (buffer-substring start (progn (forward-sexp 1) (point)))) - (meth (get (intern-soft fn) 'clojure-backtracking-indent))) + (meth (clojure--symbol-get fn 'clojure-backtracking-indent))) (let ((n 0)) (when (< (point) indent-point) (condition-case () @@ -742,7 +745,7 @@ move upwards in an sexp to check for contextual indenting." (forward-sexp 1) (while (< (point) indent-point) (parse-partial-sexp (point) indent-point 1 t) - (incf n) + (cl-incf n) (forward-sexp 1))) (error nil))) (push n path)) @@ -762,7 +765,7 @@ move upwards in an sexp to check for contextual indenting." (condition-case () (progn (backward-up-list 1) - (incf depth)) + (cl-incf depth)) (error (setq depth clojure-max-backtracking))))) indent)) diff --git a/test/clojure-mode-indentation-test.el b/test/clojure-mode-indentation-test.el index 867126ce..93de7347 100644 --- a/test/clojure-mode-indentation-test.el +++ b/test/clojure-mode-indentation-test.el @@ -179,6 +179,15 @@ values of customisable variables." #?(:clj :foo |:cljs :bar)") +(check-indentation backtracking-with-aliases + " +(clojure.core/letfn [(twice [x] +|(* x 2))] + :a)" + " +(clojure.core/letfn [(twice [x] + |(* x 2))] + :a)") (provide 'clojure-mode-indentation-test) From c3a42353dc5b4bbd82b535aaaa770553e8622c61 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 10 Sep 2015 19:53:31 +0100 Subject: [PATCH 5/5] Refactor logical sexps code a bit --- clojure-mode.el | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index 0fbfec3c..ced6cb10 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -1084,6 +1084,13 @@ Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")." ;;; Sexp navigation +(defun clojure--looking-at-logical-sexp () + "Return non-nil if sexp after point represents code. +Sexps that don't represent code are ^metadata or #reader.macros." + (forward-sexp 1) + (forward-sexp -1) + (not (looking-at-p "\\^\\|#[[:alpha:]]"))) + (defun clojure-forward-logical-sexp (&optional n) "Move forward N logical sexps. This will skip over sexps that don't represent objects, so that ^hints and @@ -1093,10 +1100,7 @@ This will skip over sexps that don't represent objects, so that ^hints and (if (< n 0) (clojure-backward-logical-sexp (- n)) (while (> n 0) - ;; Non-logical sexps. - (while (progn (forward-sexp 1) - (forward-sexp -1) - (looking-at-p "\\^\\|#[[:alpha:]]")) + (while (not (clojure--looking-at-logical-sexp)) (forward-sexp 1)) ;; The actual sexp (forward-sexp 1)