@@ -649,19 +649,89 @@ symbol property, and its return value should match one of the
649
649
allowed values of this property. See `clojure-indent-function'
650
650
for more information." )
651
651
652
- (defun clojure--symbol- get (function-name )
653
- " Return the symbol PROPERTY for the symbol named FUNCTION-NAME.
652
+ (defun clojure--get-indent-method (function-name )
653
+ " Return the indent spec for the symbol named FUNCTION-NAME.
654
654
FUNCTION-NAME is a string. If it contains a `/' , also try only
655
- the part after the `/' ."
656
- (or (get (intern-soft function-name) 'clojure-indent-function )
655
+ the part after the `/' .
656
+
657
+ Look for a spec using `clojure-get-indent-function' , then try the
658
+ `clojure-indent-function' and `clojure-backtracking-indent'
659
+ symbol properties."
660
+ (or (when (functionp clojure-get-indent-function)
661
+ (funcall clojure-get-indent-function function-name))
662
+ (get (intern-soft function-name) 'clojure-indent-function )
657
663
(get (intern-soft function-name) 'clojure-backtracking-indent )
658
664
(when (string-match " /\\ ([^/]+\\ )\\ '" function-name)
659
665
(or (get (intern-soft (match-string 1 function-name))
660
666
'clojure-indent-function )
661
667
(get (intern-soft (match-string 1 function-name))
662
- 'clojure-backtracking-indent )))
663
- (when (functionp clojure-get-indent-function)
664
- (funcall clojure-get-indent-function function-name))))
668
+ 'clojure-backtracking-indent )))))
669
+
670
+ (defvar clojure--current-backtracking-depth 0 )
671
+
672
+ (defun clojure--find-indent-spec-backtracking ()
673
+ " Return the indent sexp that applies to the sexp at point.
674
+ Implementation function for `clojure--find-indent-spec' ."
675
+ (when (and (>= clojure-max-backtracking clojure--current-backtracking-depth)
676
+ (not (looking-at " ^" )))
677
+ (let ((clojure--current-backtracking-depth (1+ clojure--current-backtracking-depth))
678
+ (pos 0 ))
679
+ ; ; Count how far we are from the start of the sexp.
680
+ (while (ignore-errors (clojure-backward-logical-sexp 1 ) t )
681
+ (cl-incf pos))
682
+ (let* ((function (thing-at-point 'symbol ))
683
+ (method (or (when function ; ; Is there a spec here?
684
+ (clojure--get-indent-method function))
685
+ (progn (up-list ) ; ; Otherwise look higher up.
686
+ (clojure-backward-logical-sexp 1 )
687
+ (clojure--find-indent-spec-backtracking)))))
688
+ (when (numberp method)
689
+ (setq method (list method)))
690
+ (pcase method
691
+ ((pred sequencep)
692
+ (pcase (length method)
693
+ (`0 nil )
694
+ (`1 (let ((head (elt method 0 )))
695
+ (when (or (= pos 0 ) (sequencep head))
696
+ head)))
697
+ (l (if (>= pos l)
698
+ (elt method (1- l))
699
+ (elt method pos)))))
700
+ ((or `defun `:defn)
701
+ (when (= pos 0 )
702
+ :defn ))
703
+ (_
704
+ (message " Invalid indent spec for `%s' : %s " function method)
705
+ nil ))))))
706
+
707
+ (defun clojure--find-indent-spec ()
708
+ " Return the indent spec that applies to current sexp.
709
+ If `clojure-use-backtracking-indent' is non-nil, also do
710
+ backtracking up to a higher-level sexp in order to find the
711
+ spec."
712
+ (if clojure-use-backtracking-indent
713
+ (save-excursion
714
+ (clojure--find-indent-spec-backtracking))
715
+ (let ((function (thing-at-point 'symbol )))
716
+ (clojure--get-indent-method function))))
717
+
718
+ (defun clojure--normal-indent (last-sexp )
719
+ " Return the normal indentation column for a sexp.
720
+ LAST-SEXP is the start of the previous sexp."
721
+ (goto-char last-sexp)
722
+ (let ((last-sexp-start nil ))
723
+ (unless (ignore-errors
724
+ (while (progn (skip-chars-backward " #?'`~@[:blank:]" )
725
+ (not (looking-at " ^" )))
726
+ (setq last-sexp-start (prog1 (point )
727
+ (forward-sexp -1 ))))
728
+ t )
729
+ ; ; If the last sexp was on the same line.
730
+ (when (and last-sexp-start
731
+ (> (line-end-position ) last-sexp-start))
732
+ (goto-char last-sexp-start)))
733
+ (skip-chars-forward " [:blank:]" )
734
+ (current-column )))
665
735
666
736
(defun clojure-indent-function (indent-point state )
667
737
" When indenting a line within a function call, indent properly.
@@ -686,122 +756,52 @@ The property value can be
686
756
- a list, which is used by `clojure-backtracking-indent' .
687
757
688
758
This function also returns nil meaning don't specify the indentation."
689
- (let ((normal-indent (current-column )))
690
- (goto-char (1+ (elt state 1 )))
691
- (parse-partial-sexp (point ) calculate-lisp-indent-last-sexp 0 t )
692
- (if (and (elt state 2 )
693
- (not (looking-at " \\ sw\\ |\\ s_" )))
694
- ; ; car of form doesn't seem to be a symbol
695
- (progn
696
- (if (not (> (save-excursion (forward-line 1 ) (point ))
697
- calculate-lisp-indent-last-sexp))
698
- (progn (goto-char calculate-lisp-indent-last-sexp)
699
- (skip-chars-backward " [:blank:]" )
700
- ; ; We're done if we find the start of line,
701
- (while (and (not (looking-at-p " ^" ))
702
- ; ; or start of sexp.
703
- (ignore-errors (forward-sexp -1 ) t ))
704
- (skip-chars-backward " [:blank:]" ))
705
- (parse-partial-sexp (point )
706
- calculate-lisp-indent-last-sexp 0 t )))
707
- ; ; Indent under the list or under the first sexp on the same
708
- ; ; line as calculate-lisp-indent-last-sexp. Note that first
709
- ; ; thing on that line has to be complete sexp since we are
710
- ; ; inside the innermost containing sexp.
711
- (backward-prefix-chars )
712
- (current-column ))
713
- (let* ((function (buffer-substring (point )
714
- (progn (forward-sexp 1 ) (point ))))
715
- (open-paren (elt state 1 ))
716
- (forward-sexp-function #'clojure-forward-logical-sexp )
717
- (method (clojure--symbol-get function)))
718
- ; ; Maps, sets, vectors and reader conditionals.
719
- (cond ((or (member (char-after open-paren) '(?\[ ?\{ ))
720
- (ignore-errors
721
- (and (eq (char-before open-paren) ?\? )
722
- (eq (char-before (1- open-paren)) ?\# ))))
723
- (goto-char open-paren)
724
- (1+ (current-column )))
725
- ((or (eq method 'defun )
726
- (and clojure-defun-style-default-indent
727
- ; ; largely to preserve useful alignment of :require, etc in ns
728
- (not (string-match " ^:" function))
729
- (not method))
730
- (and (null method)
731
- (> (length function) 3 )
732
- (string-match " \\ `\\ (?:\\ S +/\\ )?\\ (def\\ |with-\\ )"
733
- function)))
734
- (lisp-indent-defform state indent-point))
735
- ((integerp method)
736
- (lisp-indent-specform method state
737
- indent-point normal-indent))
738
- ((functionp method)
739
- (funcall method indent-point state))
740
- (clojure-use-backtracking-indent
741
- (clojure-backtracking-indent
742
- indent-point state normal-indent)))))))
743
-
744
- (defun clojure-backtracking-indent (indent-point state _normal-indent )
745
- " Experimental backtracking support.
746
-
747
- Given an INDENT-POINT, the STATE, and the NORMAL-INDENT, will
748
- move upwards in an sexp to check for contextual indenting."
749
- (let (indent (path) (depth 0 ))
759
+ (let* ((forward-sexp-function #'clojure-forward-logical-sexp ))
760
+ ; ; Goto to the open-paren.
750
761
(goto-char (elt state 1 ))
751
- (while (and (not indent)
752
- (< depth clojure-max-backtracking))
753
- (let ((containing-sexp (point )))
754
- (parse-partial-sexp (1+ containing-sexp) indent-point 1 t )
755
- (when (looking-at " \\ sw\\ |\\ s_" )
756
- (let* ((start (point ))
757
- (fn (buffer-substring start (progn (forward-sexp 1 ) (point ))))
758
- (meth (clojure--symbol-get fn)))
759
- (let ((n 0 ))
760
- (when (< (point ) indent-point)
761
- (condition-case ()
762
- (progn
763
- (forward-sexp 1 )
764
- (while (< (point ) indent-point)
765
- (parse-partial-sexp (point ) indent-point 1 t )
766
- (cl-incf n)
767
- (forward-sexp 1 )))
768
- (error nil )))
769
- (push n path))
770
- (when (and (listp meth)
771
- (not (functionp meth)))
772
- (let ((def meth))
773
- (dolist (p path)
774
- (if (and (listp def)
775
- (< p (length def)))
776
- (setq def (nth p def))
777
- (if (listp def)
778
- (setq def (car (last def)))
779
- (setq def nil ))))
780
- (goto-char (elt state 1 ))
781
- (when def
782
- (setq indent (+ (current-column ) def)))))))
783
- (goto-char containing-sexp)
784
- (condition-case ()
785
- (progn
786
- (backward-up-list 1 )
787
- (cl-incf depth))
788
- (error (setq depth clojure-max-backtracking)))))
789
- indent))
790
-
791
- ; ; clojure backtracking indent is experimental and the format for these
792
- ; ; entries are subject to change
793
- (put 'implement 'clojure-indent-function '(4 (2 )))
794
- (put 'letfn 'clojure-indent-function '((2 ) 2 ))
795
- (put 'proxy 'clojure-indent-function '(4 4 (2 )))
796
- (put 'reify 'clojure-indent-function '((2 )))
797
- (put 'deftype 'clojure-indent-function '(4 4 (2 )))
798
- (put 'defrecord 'clojure-indent-function '(4 4 (2 )))
799
- (put 'defprotocol 'clojure-indent-function '(4 (2 )))
800
- (put 'extend-type 'clojure-indent-function '(4 (2 )))
801
- (put 'extend-protocol 'clojure-indent-function '(4 (2 )))
802
- (put 'specify 'clojure-indent-function '(4 (2 )))
803
- (put 'specify! 'clojure-indent-function '(4 (2 )))
804
-
762
+ ; ; Maps, sets, vectors and reader conditionals.
763
+ (if (or (member (char-after ) '(?\[ ?\{ ))
764
+ (and (eq (char-before ) ?\? )
765
+ (eq (char-before (1- (point ))) ?\# ))
766
+ ; ; Car of form is not a symbol.
767
+ (and (elt state 2 )
768
+ (not (looking-at " .\\ sw\\ |.\\ s_" ))))
769
+ (1+ (current-column ))
770
+ ; ; Function or macro call.
771
+ (forward-char 1 )
772
+ (let ((method (clojure--find-indent-spec))
773
+ (containing-form-column (1- (current-column ))))
774
+ (pcase method
775
+ ((or (pred integerp) `(, method ))
776
+ (let ((pos -1 ))
777
+ ; ; `forward-sexp' will error if indent-point is after
778
+ ; ; the last sexp in the current sexp.
779
+ (ignore-errors
780
+ (while (<= (point ) indent-point)
781
+ (clojure-forward-logical-sexp 1 )
782
+ (cl-incf pos)))
783
+ (cond
784
+ ((= pos (1+ method))
785
+ (+ lisp-body-indent containing-form-column))
786
+ ((> pos (1+ method))
787
+ (clojure--normal-indent calculate-lisp-indent-last-sexp))
788
+ (t
789
+ (+ (* 2 lisp-body-indent) containing-form-column)))))
790
+ (`:defn
791
+ (+ lisp-body-indent containing-form-column))
792
+ ((pred functionp)
793
+ (funcall method indent-point state))
794
+ ((and `nil
795
+ (guard (let ((function (thing-at-point 'sexp )))
796
+ (or (and clojure-defun-style-default-indent
797
+ ; ; largely to preserve useful alignment of :require, etc in ns
798
+ (not (string-match " ^:" function)))
799
+ (string-match " \\ `\\ (?:\\ S +/\\ )?\\ (def\\ |with-\\ )"
800
+ function)))))
801
+ (+ lisp-body-indent containing-form-column))
802
+ (_ (clojure--normal-indent calculate-lisp-indent-last-sexp)))))))
803
+
804
+ ; ;; Setting indentation
805
805
(defun put-clojure-indent (sym indent )
806
806
" Instruct `clojure-indent-function' to indent the body of SYM by INDENT."
807
807
(put sym 'clojure-indent-function indent))
@@ -827,18 +827,18 @@ Requires the macro's NAME and a VALUE."
827
827
828
828
You can use this to let Emacs indent your own macros the same way
829
829
that it indents built-in macros like with-open. To manually set
830
- it from Lisp code, use (put-clojure-indent 'some-symbol 'defun )."
830
+ it from Lisp code, use (put-clojure-indent 'some-symbol :defn )."
831
831
:type '(repeat symbol)
832
832
:group 'clojure
833
833
:set 'add-custom-clojure-indents )
834
834
835
835
(define-clojure-indent
836
836
; ; built-ins
837
837
(ns 1 )
838
- (fn 'defun )
839
- (def 'defun )
840
- (defn 'defun )
841
- (bound-fn 'defun )
838
+ (fn :defn )
839
+ (def :defn )
840
+ (defn :defn )
841
+ (bound-fn :defn )
842
842
(if 1 )
843
843
(if-not 1 )
844
844
(case 1 )
@@ -855,26 +855,26 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
855
855
(comment 0 )
856
856
(doto 1 )
857
857
(locking 1 )
858
- (proxy 2 )
858
+ (proxy '( 2 nil nil ( 1 )) )
859
859
(as-> 2 )
860
860
861
- (reify 'defun )
862
- (deftype 2 )
863
- (defrecord 2 )
864
- (defprotocol 1 )
861
+ (reify '( 1 nil ( 1 )) )
862
+ (deftype '( 2 nil nil ( 1 )) )
863
+ (defrecord '( 2 nil nil ( 1 )) )
864
+ (defprotocol '( 1 ) )
865
865
(extend 1 )
866
- (extend-protocol 1 )
867
- (extend-type 1 )
868
- (specify 1 )
869
- (specify! 1 )
870
-
866
+ (extend-protocol '( 1 ( 1 )) )
867
+ (extend-type '( 1 ( 1 )) )
868
+ (specify '( 1 ( 1 )) )
869
+ (specify! '( 1 ( 1 )) )
870
+ (implement '( 1 ( 1 )))
871
871
(try 0 )
872
872
(catch 2 )
873
873
(finally 0 )
874
874
875
875
; ; binding forms
876
876
(let 1 )
877
- (letfn 1 )
877
+ (letfn '( 1 (( 1 )) nil ) )
878
878
(binding 1 )
879
879
(loop 1 )
880
880
(for 1 )
@@ -885,18 +885,18 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
885
885
(when-some 1 )
886
886
(if-some 1 )
887
887
888
- (defmethod 'defun )
888
+ (defmethod :defn )
889
889
890
890
; ; clojure.test
891
891
(testing 1 )
892
- (deftest 'defun )
892
+ (deftest :defn )
893
893
(are 2 )
894
- (use-fixtures 'defun )
894
+ (use-fixtures :defn )
895
895
896
896
; ; core.logic
897
- (run 'defun )
898
- (run* 'defun )
899
- (fresh 'defun )
897
+ (run :defn )
898
+ (run* :defn )
899
+ (fresh :defn )
900
900
901
901
; ; core.async
902
902
(alt! 0 )
@@ -1108,7 +1108,7 @@ Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")."
1108
1108
Sexps that don't represent code are ^metadata or #reader.macros."
1109
1109
(forward-sexp 1 )
1110
1110
(forward-sexp -1 )
1111
- (not (looking-at-p " \\ ^\\ |#[[:alpha:]]" )))
1111
+ (not (looking-at-p " \\ ^\\ |#[? [:alpha:]]" )))
1112
1112
1113
1113
(defun clojure-forward-logical-sexp (&optional n )
1114
1114
" Move forward N logical sexps.
@@ -1140,7 +1140,7 @@ This will skip over sexps that don't represent objects, so that ^hints and
1140
1140
(ignore-errors
1141
1141
(save-excursion
1142
1142
(backward-sexp 1 )
1143
- (looking-at-p " \\ ^ \\ |#[[:alpha:]] " ))))
1143
+ (not (clojure-- looking-at-logical-sexp) ))))
1144
1144
(backward-sexp 1 ))
1145
1145
(setq n (1- n)))))
1146
1146
0 commit comments