@@ -638,13 +638,100 @@ point) to check."
638
638
(replace-match (clojure-docstring-fill-prefix))))
639
639
(lisp-indent-line )))
640
640
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))))
641
+ (defvar clojure-get-indent-function nil
642
+ " Function to get the indent spec of a symbol.
643
+ This function should take one argument, the name of the symbol as
644
+ a string. This name will be exactly as it appears in the buffer,
645
+ so it might start with a namespace alias.
646
+
647
+ This function is analogous to the `clojure-indent-function'
648
+ symbol property, and its return value should match one of the
649
+ allowed values of this property. See `clojure-indent-function'
650
+ for more information." )
651
+
652
+ (defun clojure--get-indent-method (function-name )
653
+ " Return the indent spec for the symbol named FUNCTION-NAME.
654
+ FUNCTION-NAME is a string. If it contains a `/' , also try only
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 )
663
+ (get (intern-soft function-name) 'clojure-backtracking-indent )
664
+ (when (string-match " /\\ ([^/]+\\ )\\ '" function-name)
665
+ (or (get (intern-soft (match-string 1 function-name))
666
+ 'clojure-indent-function )
667
+ (get (intern-soft (match-string 1 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 )))
648
735
649
736
(defun clojure-indent-function (indent-point state )
650
737
" When indenting a line within a function call, indent properly.
@@ -666,123 +753,55 @@ The property value can be
666
753
- a function to call just as this function was called.
667
754
If that function returns nil, that means it doesn't specify
668
755
the indentation.
756
+ - a list, which is used by `clojure-backtracking-indent' .
669
757
670
758
This function also returns nil meaning don't specify the indentation."
671
- (let ((normal-indent (current-column )))
672
- (goto-char (1+ (elt state 1 )))
673
- (parse-partial-sexp (point ) calculate-lisp-indent-last-sexp 0 t )
674
- (if (and (elt state 2 )
675
- (not (looking-at " \\ sw\\ |\\ s_" )))
676
- ; ; car of form doesn't seem to be a symbol
677
- (progn
678
- (if (not (> (save-excursion (forward-line 1 ) (point ))
679
- calculate-lisp-indent-last-sexp))
680
- (progn (goto-char calculate-lisp-indent-last-sexp)
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:]" ))
687
- (parse-partial-sexp (point )
688
- calculate-lisp-indent-last-sexp 0 t )))
689
- ; ; Indent under the list or under the first sexp on the same
690
- ; ; line as calculate-lisp-indent-last-sexp. Note that first
691
- ; ; thing on that line has to be complete sexp since we are
692
- ; ; inside the innermost containing sexp.
693
- (backward-prefix-chars )
694
- (current-column ))
695
- (let* ((function (buffer-substring (point )
696
- (progn (forward-sexp 1 ) (point ))))
697
- (open-paren (elt state 1 ))
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)) ?\# ))))
705
- (goto-char open-paren)
706
- (1+ (current-column )))
707
- ((or (eq method 'defun )
708
- (and clojure-defun-style-default-indent
709
- ; ; largely to preserve useful alignment of :require, etc in ns
710
- (not (string-match " ^:" function))
711
- (not method))
712
- (and (null method)
713
- (> (length function) 3 )
714
- (string-match " \\ `\\ (?:\\ S +/\\ )?\\ (def\\ |with-\\ )"
715
- function)))
716
- (lisp-indent-defform state indent-point))
717
- ((integerp method)
718
- (lisp-indent-specform method state
719
- indent-point normal-indent))
720
- (method
721
- (funcall method indent-point state))
722
- (clojure-use-backtracking-indent
723
- (clojure-backtracking-indent
724
- indent-point state normal-indent)))))))
725
-
726
- (defun clojure-backtracking-indent (indent-point state _normal-indent )
727
- " Experimental backtracking support.
728
-
729
- Given an INDENT-POINT, the STATE, and the NORMAL-INDENT, will
730
- move upwards in an sexp to check for contextual indenting."
731
- (let (indent (path) (depth 0 ))
759
+ (let* ((forward-sexp-function #'clojure-forward-logical-sexp ))
760
+ ; ; Goto to the open-paren.
732
761
(goto-char (elt state 1 ))
733
- (while (and (not indent)
734
- (< depth clojure-max-backtracking))
735
- (let ((containing-sexp (point )))
736
- (parse-partial-sexp (1+ containing-sexp) indent-point 1 t )
737
- (when (looking-at " \\ sw\\ |\\ s_" )
738
- (let* ((start (point ))
739
- (fn (buffer-substring start (progn (forward-sexp 1 ) (point ))))
740
- (meth (clojure--symbol-get fn 'clojure-backtracking-indent )))
741
- (let ((n 0 ))
742
- (when (< (point ) indent-point)
743
- (condition-case ()
744
- (progn
745
- (forward-sexp 1 )
746
- (while (< (point ) indent-point)
747
- (parse-partial-sexp (point ) indent-point 1 t )
748
- (cl-incf n)
749
- (forward-sexp 1 )))
750
- (error nil )))
751
- (push n path))
752
- (when meth
753
- (let ((def meth))
754
- (dolist (p path)
755
- (if (and (listp def)
756
- (< p (length def)))
757
- (setq def (nth p def))
758
- (if (listp def)
759
- (setq def (car (last def)))
760
- (setq def nil ))))
761
- (goto-char (elt state 1 ))
762
- (when def
763
- (setq indent (+ (current-column ) def)))))))
764
- (goto-char containing-sexp)
765
- (condition-case ()
766
- (progn
767
- (backward-up-list 1 )
768
- (cl-incf depth))
769
- (error (setq depth clojure-max-backtracking)))))
770
- indent))
771
-
772
- ; ; clojure backtracking indent is experimental and the format for these
773
- ; ; entries are subject to change
774
- (put 'implement 'clojure-backtracking-indent '(4 (2 )))
775
- (put 'letfn 'clojure-backtracking-indent '((2 ) 2 ))
776
- (put 'proxy 'clojure-backtracking-indent '(4 4 (2 )))
777
- (put 'reify 'clojure-backtracking-indent '((2 )))
778
- (put 'deftype 'clojure-backtracking-indent '(4 4 (2 )))
779
- (put 'defrecord 'clojure-backtracking-indent '(4 4 (2 )))
780
- (put 'defprotocol 'clojure-backtracking-indent '(4 (2 )))
781
- (put 'extend-type 'clojure-backtracking-indent '(4 (2 )))
782
- (put 'extend-protocol 'clojure-backtracking-indent '(4 (2 )))
783
- (put 'specify 'clojure-backtracking-indent '(4 (2 )))
784
- (put 'specify! 'clojure-backtracking-indent '(4 (2 )))
785
-
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
786
805
(defun put-clojure-indent (sym indent )
787
806
" Instruct `clojure-indent-function' to indent the body of SYM by INDENT."
788
807
(put sym 'clojure-indent-function indent))
@@ -808,18 +827,18 @@ Requires the macro's NAME and a VALUE."
808
827
809
828
You can use this to let Emacs indent your own macros the same way
810
829
that it indents built-in macros like with-open. To manually set
811
- it from Lisp code, use (put-clojure-indent 'some-symbol 'defun )."
830
+ it from Lisp code, use (put-clojure-indent 'some-symbol :defn )."
812
831
:type '(repeat symbol)
813
832
:group 'clojure
814
833
:set 'add-custom-clojure-indents )
815
834
816
835
(define-clojure-indent
817
836
; ; built-ins
818
837
(ns 1 )
819
- (fn 'defun )
820
- (def 'defun )
821
- (defn 'defun )
822
- (bound-fn 'defun )
838
+ (fn :defn )
839
+ (def :defn )
840
+ (defn :defn )
841
+ (bound-fn :defn )
823
842
(if 1 )
824
843
(if-not 1 )
825
844
(case 1 )
@@ -836,26 +855,26 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
836
855
(comment 0 )
837
856
(doto 1 )
838
857
(locking 1 )
839
- (proxy 2 )
858
+ (proxy '( 2 nil nil ( 1 )) )
840
859
(as-> 2 )
841
860
842
- (reify 'defun )
843
- (deftype 2 )
844
- (defrecord 2 )
845
- (defprotocol 1 )
861
+ (reify '( 1 nil ( 1 )) )
862
+ (deftype '( 2 nil nil ( 1 )) )
863
+ (defrecord '( 2 nil nil ( 1 )) )
864
+ (defprotocol '( 1 ) )
846
865
(extend 1 )
847
- (extend-protocol 1 )
848
- (extend-type 1 )
849
- (specify 1 )
850
- (specify! 1 )
851
-
866
+ (extend-protocol '( 1 ( 1 )) )
867
+ (extend-type '( 1 ( 1 )) )
868
+ (specify '( 1 ( 1 )) )
869
+ (specify! '( 1 ( 1 )) )
870
+ (implement '( 1 ( 1 )))
852
871
(try 0 )
853
872
(catch 2 )
854
873
(finally 0 )
855
874
856
875
; ; binding forms
857
876
(let 1 )
858
- (letfn 1 )
877
+ (letfn '( 1 (( 1 )) nil ) )
859
878
(binding 1 )
860
879
(loop 1 )
861
880
(for 1 )
@@ -866,18 +885,18 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
866
885
(when-some 1 )
867
886
(if-some 1 )
868
887
869
- (defmethod 'defun )
888
+ (defmethod :defn )
870
889
871
890
; ; clojure.test
872
891
(testing 1 )
873
- (deftest 'defun )
892
+ (deftest :defn )
874
893
(are 2 )
875
- (use-fixtures 'defun )
894
+ (use-fixtures :defn )
876
895
877
896
; ; core.logic
878
- (run 'defun )
879
- (run* 'defun )
880
- (fresh 'defun )
897
+ (run :defn )
898
+ (run* :defn )
899
+ (fresh :defn )
881
900
882
901
; ; core.async
883
902
(alt! 0 )
@@ -1089,7 +1108,7 @@ Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")."
1089
1108
Sexps that don't represent code are ^metadata or #reader.macros."
1090
1109
(forward-sexp 1 )
1091
1110
(forward-sexp -1 )
1092
- (not (looking-at-p " \\ ^\\ |#[[:alpha:]]" )))
1111
+ (not (looking-at-p " \\ ^\\ |#[? [:alpha:]]" )))
1093
1112
1094
1113
(defun clojure-forward-logical-sexp (&optional n )
1095
1114
" Move forward N logical sexps.
@@ -1121,7 +1140,7 @@ This will skip over sexps that don't represent objects, so that ^hints and
1121
1140
(ignore-errors
1122
1141
(save-excursion
1123
1142
(backward-sexp 1 )
1124
- (looking-at-p " \\ ^ \\ |#[[:alpha:]] " ))))
1143
+ (not (clojure-- looking-at-logical-sexp) ))))
1125
1144
(backward-sexp 1 ))
1126
1145
(setq n (1- n)))))
1127
1146
0 commit comments