@@ -638,13 +638,100 @@ point) to check."
638638 (replace-match (clojure-docstring-fill-prefix))))
639639 (lisp-indent-line )))
640640
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 )))
648735
649736(defun clojure-indent-function (indent-point state )
650737 " When indenting a line within a function call, indent properly.
@@ -666,123 +753,55 @@ The property value can be
666753- a function to call just as this function was called.
667754 If that function returns nil, that means it doesn't specify
668755 the indentation.
756+ - a list, which is used by `clojure-backtracking-indent' .
669757
670758This 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.
732761 (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
786805(defun put-clojure-indent (sym indent )
787806 " Instruct `clojure-indent-function' to indent the body of SYM by INDENT."
788807 (put sym 'clojure-indent-function indent))
@@ -808,18 +827,18 @@ Requires the macro's NAME and a VALUE."
808827
809828You can use this to let Emacs indent your own macros the same way
810829that 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 )."
812831 :type '(repeat symbol)
813832 :group 'clojure
814833 :set 'add-custom-clojure-indents )
815834
816835(define-clojure-indent
817836 ; ; built-ins
818837 (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 )
823842 (if 1 )
824843 (if-not 1 )
825844 (case 1 )
@@ -836,26 +855,26 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
836855 (comment 0 )
837856 (doto 1 )
838857 (locking 1 )
839- (proxy 2 )
858+ (proxy '( 2 nil nil ( 1 )) )
840859 (as-> 2 )
841860
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 ) )
846865 (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 )))
852871 (try 0 )
853872 (catch 2 )
854873 (finally 0 )
855874
856875 ; ; binding forms
857876 (let 1 )
858- (letfn 1 )
877+ (letfn '( 1 (( 1 )) nil ) )
859878 (binding 1 )
860879 (loop 1 )
861880 (for 1 )
@@ -866,18 +885,18 @@ it from Lisp code, use (put-clojure-indent 'some-symbol 'defun)."
866885 (when-some 1 )
867886 (if-some 1 )
868887
869- (defmethod 'defun )
888+ (defmethod :defn )
870889
871890 ; ; clojure.test
872891 (testing 1 )
873- (deftest 'defun )
892+ (deftest :defn )
874893 (are 2 )
875- (use-fixtures 'defun )
894+ (use-fixtures :defn )
876895
877896 ; ; core.logic
878- (run 'defun )
879- (run* 'defun )
880- (fresh 'defun )
897+ (run :defn )
898+ (run* :defn )
899+ (fresh :defn )
881900
882901 ; ; core.async
883902 (alt! 0 )
@@ -1089,7 +1108,7 @@ Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")."
10891108Sexps that don't represent code are ^metadata or #reader.macros."
10901109 (forward-sexp 1 )
10911110 (forward-sexp -1 )
1092- (not (looking-at-p " \\ ^\\ |#[[:alpha:]]" )))
1111+ (not (looking-at-p " \\ ^\\ |#[? [:alpha:]]" )))
10931112
10941113(defun clojure-forward-logical-sexp (&optional n )
10951114 " Move forward N logical sexps.
@@ -1121,7 +1140,7 @@ This will skip over sexps that don't represent objects, so that ^hints and
11211140 (ignore-errors
11221141 (save-excursion
11231142 (backward-sexp 1 )
1124- (looking-at-p " \\ ^ \\ |#[[:alpha:]] " ))))
1143+ (not (clojure-- looking-at-logical-sexp) ))))
11251144 (backward-sexp 1 ))
11261145 (setq n (1- n)))))
11271146
0 commit comments