@@ -649,19 +649,89 @@ symbol property, and its return value should match one of the
649649allowed values of this property. See `clojure-indent-function'
650650for more information." )
651651
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.
654654FUNCTION-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 )
657663 (get (intern-soft function-name) 'clojure-backtracking-indent )
658664 (when (string-match " /\\ ([^/]+\\ )\\ '" function-name)
659665 (or (get (intern-soft (match-string 1 function-name))
660666 'clojure-indent-function )
661667 (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 )))
665735
666736(defun clojure-indent-function (indent-point state )
667737 " When indenting a line within a function call, indent properly.
@@ -686,122 +756,52 @@ The property value can be
686756- a list, which is used by `clojure-backtracking-indent' .
687757
688758This 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.
750761 (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
805805(defun put-clojure-indent (sym indent )
806806 " Instruct `clojure-indent-function' to indent the body of SYM by INDENT."
807807 (put sym 'clojure-indent-function indent))
@@ -827,18 +827,18 @@ Requires the macro's NAME and a VALUE."
827827
828828You can use this to let Emacs indent your own macros the same way
829829that 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 )."
831831 :type '(repeat symbol)
832832 :group 'clojure
833833 :set 'add-custom-clojure-indents )
834834
835835(define-clojure-indent
836836 ; ; built-ins
837837 (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 )
842842 (if 1 )
843843 (if-not 1 )
844844 (case 1 )
@@ -855,26 +855,26 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
855855 (comment 0 )
856856 (doto 1 )
857857 (locking 1 )
858- (proxy 2 )
858+ (proxy '( 2 nil nil ( 1 )) )
859859 (as-> 2 )
860860
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 ) )
865865 (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 )))
871871 (try 0 )
872872 (catch 2 )
873873 (finally 0 )
874874
875875 ; ; binding forms
876876 (let 1 )
877- (letfn 1 )
877+ (letfn '( 1 (( 1 )) nil ) )
878878 (binding 1 )
879879 (loop 1 )
880880 (for 1 )
@@ -885,18 +885,18 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
885885 (when-some 1 )
886886 (if-some 1 )
887887
888- (defmethod 'defun )
888+ (defmethod :defn )
889889
890890 ; ; clojure.test
891891 (testing 1 )
892- (deftest 'defun )
892+ (deftest :defn )
893893 (are 2 )
894- (use-fixtures 'defun )
894+ (use-fixtures :defn )
895895
896896 ; ; core.logic
897- (run 'defun )
898- (run* 'defun )
899- (fresh 'defun )
897+ (run :defn )
898+ (run* :defn )
899+ (fresh :defn )
900900
901901 ; ; core.async
902902 (alt! 0 )
@@ -1108,7 +1108,7 @@ Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")."
11081108Sexps that don't represent code are ^metadata or #reader.macros."
11091109 (forward-sexp 1 )
11101110 (forward-sexp -1 )
1111- (not (looking-at-p " \\ ^\\ |#[[:alpha:]]" )))
1111+ (not (looking-at-p " \\ ^\\ |#[? [:alpha:]]" )))
11121112
11131113(defun clojure-forward-logical-sexp (&optional n )
11141114 " Move forward N logical sexps.
@@ -1140,7 +1140,7 @@ This will skip over sexps that don't represent objects, so that ^hints and
11401140 (ignore-errors
11411141 (save-excursion
11421142 (backward-sexp 1 )
1143- (looking-at-p " \\ ^ \\ |#[[:alpha:]] " ))))
1143+ (not (clojure-- looking-at-logical-sexp) ))))
11441144 (backward-sexp 1 ))
11451145 (setq n (1- n)))))
11461146
0 commit comments