dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

clojure-mode.el (124541B)


      1 ;;; clojure-mode.el --- Major mode for Clojure code -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright © 2007-2013 Jeffrey Chu, Lennart Staflin, Phil Hagelberg
      4 ;; Copyright © 2013-2022 Bozhidar Batsov, Artur Malabarba, Magnar Sveen
      5 ;;
      6 ;; Authors: Jeffrey Chu <jochu0@gmail.com>
      7 ;;       Lennart Staflin <lenst@lysator.liu.se>
      8 ;;       Phil Hagelberg <technomancy@gmail.com>
      9 ;;       Bozhidar Batsov <bozhidar@batsov.dev>
     10 ;;       Artur Malabarba <bruce.connor.am@gmail.com>
     11 ;;       Magnar Sveen <magnars@gmail.com>
     12 ;; Maintainer: Bozhidar Batsov <bozhidar@batsov.dev>
     13 ;; URL: http://github.com/clojure-emacs/clojure-mode
     14 ;; Keywords: languages clojure clojurescript lisp
     15 ;; Version: 5.16.0
     16 ;; Package-Requires: ((emacs "25.1"))
     17 
     18 ;; This file is not part of GNU Emacs.
     19 
     20 ;;; Commentary:
     21 
     22 ;; Provides font-lock, indentation, navigation and basic refactoring for the
     23 ;; Clojure programming language (http://clojure.org).
     24 
     25 ;; Using clojure-mode with paredit or smartparens is highly recommended.
     26 
     27 ;; Here are some example configurations:
     28 
     29 ;;   ;; require or autoload paredit-mode
     30 ;;   (add-hook 'clojure-mode-hook #'paredit-mode)
     31 
     32 ;;   ;; require or autoload smartparens
     33 ;;   (add-hook 'clojure-mode-hook #'smartparens-strict-mode)
     34 
     35 ;; See inf-clojure (http://github.com/clojure-emacs/inf-clojure) for
     36 ;; basic interaction with Clojure subprocesses.
     37 
     38 ;; See CIDER (http://github.com/clojure-emacs/cider) for
     39 ;; better interaction with subprocesses via nREPL.
     40 
     41 ;;; License:
     42 
     43 ;; This program is free software; you can redistribute it and/or
     44 ;; modify it under the terms of the GNU General Public License
     45 ;; as published by the Free Software Foundation; either version 3
     46 ;; of the License, or (at your option) any later version.
     47 ;;
     48 ;; This program is distributed in the hope that it will be useful,
     49 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     50 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     51 ;; GNU General Public License for more details.
     52 ;;
     53 ;; You should have received a copy of the GNU General Public License
     54 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
     55 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     56 ;; Boston, MA 02110-1301, USA.
     57 
     58 ;;; Code:
     59 
     60 
     61 (defvar calculate-lisp-indent-last-sexp)
     62 (defvar delete-pair-blink-delay)
     63 (defvar font-lock-beg)
     64 (defvar font-lock-end)
     65 (defvar paredit-space-for-delimiter-predicates)
     66 (defvar paredit-version)
     67 (defvar paredit-mode)
     68 
     69 (require 'cl-lib)
     70 (require 'imenu)
     71 (require 'newcomment)
     72 (require 'thingatpt)
     73 (require 'align)
     74 (require 'subr-x)
     75 (require 'lisp-mnt)
     76 
     77 (declare-function lisp-fill-paragraph  "lisp-mode" (&optional justify))
     78 
     79 (defgroup clojure nil
     80   "Major mode for editing Clojure code."
     81   :prefix "clojure-"
     82   :group 'languages
     83   :link '(url-link :tag "GitHub" "https://github.com/clojure-emacs/clojure-mode")
     84   :link '(emacs-commentary-link :tag "Commentary" "clojure-mode"))
     85 
     86 (defconst clojure-mode-version
     87   (eval-when-compile
     88     (lm-version (or load-file-name buffer-file-name)))
     89   "The current version of `clojure-mode'.")
     90 
     91 (defface clojure-keyword-face
     92   '((t (:inherit font-lock-constant-face)))
     93   "Face used to font-lock Clojure keywords (:something)."
     94   :package-version '(clojure-mode . "3.0.0"))
     95 
     96 (defface clojure-character-face
     97   '((t (:inherit font-lock-string-face)))
     98   "Face used to font-lock Clojure character literals."
     99   :package-version '(clojure-mode . "3.0.0"))
    100 
    101 (defcustom clojure-indent-style 'always-align
    102   "Indentation style to use for function forms and macro forms.
    103 There are two cases of interest configured by this variable.
    104 
    105 - Case (A) is when at least one function argument is on the same
    106   line as the function name.
    107 - Case (B) is the opposite (no arguments are on the same line as
    108   the function name).  Note that the body of macros is not
    109   affected by this variable, it is always indented by
    110   `lisp-body-indent' (default 2) spaces.
    111 
    112 Note that this variable configures the indentation of function
    113 forms (and function-like macros), it does not affect macros that
    114 already use special indentation rules.
    115 
    116 The possible values for this variable are keywords indicating how
    117 to indent function forms.
    118 
    119     `always-align' - Follow the same rules as `lisp-mode'.  All
    120     args are vertically aligned with the first arg in case (A),
    121     and vertically aligned with the function name in case (B).
    122     For instance:
    123         (reduce merge
    124                 some-coll)
    125         (reduce
    126          merge
    127          some-coll)
    128 
    129     `always-indent' - All args are indented like a macro body.
    130         (reduce merge
    131           some-coll)
    132         (reduce
    133           merge
    134           some-coll)
    135 
    136     `align-arguments' - Case (A) is indented like `lisp', and
    137     case (B) is indented like a macro body.
    138         (reduce merge
    139                 some-coll)
    140         (reduce
    141           merge
    142           some-coll)"
    143   :safe #'symbolp
    144   :type '(choice (const :tag "Same as `lisp-mode'" always-align)
    145                  (const :tag "Indent like a macro body" always-indent)
    146                  (const :tag "Indent like a macro body unless first arg is on the same line"
    147                         align-arguments))
    148   :package-version '(clojure-mode . "5.2.0"))
    149 
    150 (defcustom clojure-use-backtracking-indent t
    151   "When non-nil, enable context sensitive indentation."
    152   :type 'boolean
    153   :safe 'booleanp)
    154 
    155 (defcustom clojure-max-backtracking 3
    156   "Maximum amount to backtrack up a list to check for context."
    157   :type 'integer
    158   :safe 'integerp)
    159 
    160 (defcustom clojure-docstring-fill-column fill-column
    161   "Value of `fill-column' to use when filling a docstring."
    162   :type 'integer
    163   :safe 'integerp)
    164 
    165 (defcustom clojure-docstring-fill-prefix-width 2
    166   "Width of `fill-prefix' when filling a docstring.
    167 The default value conforms with the de facto convention for
    168 Clojure docstrings, aligning the second line with the opening
    169 double quotes on the third column."
    170   :type 'integer
    171   :safe 'integerp)
    172 
    173 (defcustom clojure-omit-space-between-tag-and-delimiters '(?\[ ?\{ ?\()
    174   "Allowed opening delimiter characters after a reader literal tag.
    175 For example, \[ is allowed in :db/id[:db.part/user]."
    176   :type '(set (const :tag "[" ?\[)
    177               (const :tag "{" ?\{)
    178               (const :tag "(" ?\()
    179               (const :tag "\"" ?\"))
    180   :safe (lambda (value)
    181           (and (listp value)
    182                (cl-every 'characterp value))))
    183 
    184 (defcustom clojure-build-tool-files
    185   '("project.clj"      ; Leiningen
    186     "build.boot"       ; Boot
    187     "build.gradle"     ; Gradle
    188     "build.gradle.kts" ; Gradle
    189     "deps.edn"         ; Clojure CLI (a.k.a. tools.deps)
    190     "shadow-cljs.edn"  ; shadow-cljs
    191     "bb.edn"           ; babashka
    192     "nbb.edn"          ; nbb
    193     )
    194   "A list of files, which identify a Clojure project's root.
    195 Out-of-the box `clojure-mode' understands lein, boot, gradle,
    196  shadow-cljs, tools.deps, babashka and nbb."
    197   :type '(repeat string)
    198   :package-version '(clojure-mode . "5.0.0")
    199   :safe (lambda (value)
    200           (and (listp value)
    201                (cl-every 'stringp value))))
    202 
    203 (defcustom clojure-directory-prefixes
    204   '("\\`clj[scxd]?\\.")
    205   "A list of directory prefixes used by `clojure-expected-ns'.
    206 The prefixes are used to generate the correct namespace."
    207   :type '(repeat string)
    208   :package-version '(clojure-mode . "5.14.0")
    209   :safe (lambda (value)
    210           (and (listp value)
    211                (cl-every 'stringp value))))
    212 
    213 (defcustom clojure-project-root-function #'clojure-project-root-path
    214   "Function to locate clojure project root directory."
    215   :type 'function
    216   :risky t
    217   :package-version '(clojure-mode . "5.7.0"))
    218 
    219 (defcustom clojure-refactor-map-prefix (kbd "C-c C-r")
    220   "Clojure refactor keymap prefix."
    221   :type 'string
    222   :package-version '(clojure-mode . "5.6.0"))
    223 
    224 (defvar clojure-refactor-map
    225   (let ((map (make-sparse-keymap)))
    226     (define-key map (kbd "C-t") #'clojure-thread)
    227     (define-key map (kbd "t") #'clojure-thread)
    228     (define-key map (kbd "C-u") #'clojure-unwind)
    229     (define-key map (kbd "u") #'clojure-unwind)
    230     (define-key map (kbd "C-f") #'clojure-thread-first-all)
    231     (define-key map (kbd "f") #'clojure-thread-first-all)
    232     (define-key map (kbd "C-l") #'clojure-thread-last-all)
    233     (define-key map (kbd "l") #'clojure-thread-last-all)
    234     (define-key map (kbd "C-p") #'clojure-cycle-privacy)
    235     (define-key map (kbd "p") #'clojure-cycle-privacy)
    236     (define-key map (kbd "C-(") #'clojure-convert-collection-to-list)
    237     (define-key map (kbd "(") #'clojure-convert-collection-to-list)
    238     (define-key map (kbd "C-'") #'clojure-convert-collection-to-quoted-list)
    239     (define-key map (kbd "'") #'clojure-convert-collection-to-quoted-list)
    240     (define-key map (kbd "C-{") #'clojure-convert-collection-to-map)
    241     (define-key map (kbd "{") #'clojure-convert-collection-to-map)
    242     (define-key map (kbd "C-[") #'clojure-convert-collection-to-vector)
    243     (define-key map (kbd "[") #'clojure-convert-collection-to-vector)
    244     (define-key map (kbd "C-#") #'clojure-convert-collection-to-set)
    245     (define-key map (kbd "#") #'clojure-convert-collection-to-set)
    246     (define-key map (kbd "C-i") #'clojure-cycle-if)
    247     (define-key map (kbd "i") #'clojure-cycle-if)
    248     (define-key map (kbd "C-w") #'clojure-cycle-when)
    249     (define-key map (kbd "w") #'clojure-cycle-when)
    250     (define-key map (kbd "C-o") #'clojure-cycle-not)
    251     (define-key map (kbd "o") #'clojure-cycle-not)
    252     (define-key map (kbd "n i") #'clojure-insert-ns-form)
    253     (define-key map (kbd "n h") #'clojure-insert-ns-form-at-point)
    254     (define-key map (kbd "n u") #'clojure-update-ns)
    255     (define-key map (kbd "n s") #'clojure-sort-ns)
    256     (define-key map (kbd "n r") #'clojure-rename-ns-alias)
    257     (define-key map (kbd "s i") #'clojure-introduce-let)
    258     (define-key map (kbd "s m") #'clojure-move-to-let)
    259     (define-key map (kbd "s f") #'clojure-let-forward-slurp-sexp)
    260     (define-key map (kbd "s b") #'clojure-let-backward-slurp-sexp)
    261     (define-key map (kbd "C-a") #'clojure-add-arity)
    262     (define-key map (kbd "a") #'clojure-add-arity)
    263     (define-key map (kbd "-") #'clojure-toggle-ignore)
    264     (define-key map (kbd "C--") #'clojure-toggle-ignore)
    265     (define-key map (kbd "_") #'clojure-toggle-ignore-surrounding-form)
    266     (define-key map (kbd "C-_") #'clojure-toggle-ignore-surrounding-form)
    267     (define-key map (kbd "P") #'clojure-promote-fn-literal)
    268     (define-key map (kbd "C-P") #'clojure-promote-fn-literal)
    269     map)
    270   "Keymap for Clojure refactoring commands.")
    271 (fset 'clojure-refactor-map clojure-refactor-map)
    272 
    273 (defvar clojure-mode-map
    274   (let ((map (make-sparse-keymap)))
    275     (set-keymap-parent map prog-mode-map)
    276     (define-key map (kbd "C-:") #'clojure-toggle-keyword-string)
    277     (define-key map (kbd "C-c SPC") #'clojure-align)
    278     (define-key map clojure-refactor-map-prefix 'clojure-refactor-map)
    279     (easy-menu-define clojure-mode-menu map "Clojure Mode Menu"
    280       '("Clojure"
    281         ["Toggle between string & keyword" clojure-toggle-keyword-string]
    282         ["Align expression" clojure-align]
    283         ["Cycle privacy" clojure-cycle-privacy]
    284         ["Cycle if, if-not" clojure-cycle-if]
    285         ["Cycle when, when-not" clojure-cycle-when]
    286         ["Cycle not" clojure-cycle-not]
    287         ["Toggle #_ ignore form" clojure-toggle-ignore]
    288         ["Toggle #_ ignore of surrounding form" clojure-toggle-ignore-surrounding-form]
    289         ["Add function arity" clojure-add-arity]
    290         ["Promote #() fn literal" clojure-promote-fn-literal]
    291         ("ns forms"
    292          ["Insert ns form at the top" clojure-insert-ns-form]
    293          ["Insert ns form here" clojure-insert-ns-form-at-point]
    294          ["Update ns form" clojure-update-ns]
    295          ["Sort ns form" clojure-sort-ns]
    296          ["Rename ns alias" clojure-rename-ns-alias])
    297         ("Convert collection"
    298          ["Convert to list" clojure-convert-collection-to-list]
    299          ["Convert to quoted list" clojure-convert-collection-to-quoted-list]
    300          ["Convert to map" clojure-convert-collection-to-map]
    301          ["Convert to vector" clojure-convert-collection-to-vector]
    302          ["Convert to set" clojure-convert-collection-to-set])
    303         ("Refactor -> and ->>"
    304          ["Thread once more" clojure-thread]
    305          ["Fully thread a form with ->" clojure-thread-first-all]
    306          ["Fully thread a form with ->>" clojure-thread-last-all]
    307          "--"
    308          ["Unwind once" clojure-unwind]
    309          ["Fully unwind a threading macro" clojure-unwind-all])
    310         ("Let expression"
    311          ["Introduce let" clojure-introduce-let]
    312          ["Move to let" clojure-move-to-let]
    313          ["Forward slurp form into let" clojure-let-forward-slurp-sexp]
    314          ["Backward slurp form into let" clojure-let-backward-slurp-sexp])
    315         ("Documentation"
    316          ["View a Clojure guide" clojure-view-guide]
    317          ["View a Clojure reference section" clojure-view-reference-section]
    318          ["View the Clojure cheatsheet" clojure-view-cheatsheet]
    319          ["View the Clojure style guide" clojure-view-style-guide])
    320         "--"
    321         ["Report a clojure-mode bug" clojure-mode-report-bug]
    322         ["Clojure-mode version" clojure-mode-display-version]))
    323     map)
    324   "Keymap for Clojure mode.")
    325 
    326 (defvar clojure-mode-syntax-table
    327   (let ((table (make-syntax-table)))
    328     ;; Initialize ASCII charset as symbol syntax
    329     (modify-syntax-entry '(0 . 127) "_" table)
    330 
    331     ;; Word syntax
    332     (modify-syntax-entry '(?0 . ?9) "w" table)
    333     (modify-syntax-entry '(?a . ?z) "w" table)
    334     (modify-syntax-entry '(?A . ?Z) "w" table)
    335 
    336     ;; Whitespace
    337     (modify-syntax-entry ?\s " " table)
    338     (modify-syntax-entry ?\xa0 " " table) ; non-breaking space
    339     (modify-syntax-entry ?\t " " table)
    340     (modify-syntax-entry ?\f " " table)
    341     ;; Setting commas as whitespace makes functions like `delete-trailing-whitespace' behave unexpectedly (#561)
    342     (modify-syntax-entry ?, "." table)
    343 
    344     ;; Delimiters
    345     (modify-syntax-entry ?\( "()" table)
    346     (modify-syntax-entry ?\) ")(" table)
    347     (modify-syntax-entry ?\[ "(]" table)
    348     (modify-syntax-entry ?\] ")[" table)
    349     (modify-syntax-entry ?\{ "(}" table)
    350     (modify-syntax-entry ?\} "){" table)
    351 
    352     ;; Prefix chars
    353     (modify-syntax-entry ?` "'" table)
    354     (modify-syntax-entry ?~ "'" table)
    355     (modify-syntax-entry ?^ "'" table)
    356     (modify-syntax-entry ?@ "'" table)
    357     (modify-syntax-entry ?? "_ p" table) ; ? is a prefix outside symbols
    358     (modify-syntax-entry ?# "_ p" table) ; # is allowed inside keywords (#399)
    359     (modify-syntax-entry ?' "_ p" table) ; ' is allowed anywhere but the start of symbols
    360 
    361     ;; Others
    362     (modify-syntax-entry ?\; "<" table) ; comment start
    363     (modify-syntax-entry ?\n ">" table) ; comment end
    364     (modify-syntax-entry ?\" "\"" table) ; string
    365     (modify-syntax-entry ?\\ "\\" table) ; escape
    366 
    367     table)
    368   "Syntax table for Clojure mode.")
    369 
    370 (defconst clojure--prettify-symbols-alist
    371   '(("fn"  . ?λ)))
    372 
    373 (defvar-local clojure-expected-ns-function nil
    374   "The function used to determine the expected namespace of a file.
    375 `clojure-mode' ships a basic function named `clojure-expected-ns'
    376 that does basic heuristics to figure this out.
    377 CIDER provides a more complex version which does classpath analysis.")
    378 
    379 (defun clojure-mode-display-version ()
    380   "Display the current `clojure-mode-version' in the minibuffer."
    381   (interactive)
    382   (message "clojure-mode (version %s)" clojure-mode-version))
    383 
    384 (defconst clojure-mode-report-bug-url "https://github.com/clojure-emacs/clojure-mode/issues/new"
    385   "The URL to report a `clojure-mode' issue.")
    386 
    387 (defun clojure-mode-report-bug ()
    388   "Report a bug in your default browser."
    389   (interactive)
    390   (browse-url clojure-mode-report-bug-url))
    391 
    392 (defconst clojure-guides-base-url "https://clojure.org/guides/"
    393   "The base URL for official Clojure guides.")
    394 
    395 (defconst clojure-guides '(("Getting Started" . "getting_started")
    396                            ("FAQ" . "faq")
    397                            ("spec" . "spec")
    398                            ("Destructuring" . "destructuring")
    399                            ("Threading Macros" . "threading_macros")
    400                            ("Comparators" . "comparators")
    401                            ("Reader Conditionals" . "reader_conditionals"))
    402   "A list of all official Clojure guides.")
    403 
    404 (defun clojure-view-guide ()
    405   "Open a Clojure guide in your default browser.
    406 
    407 The command will prompt you to select one of the available guides."
    408   (interactive)
    409   (let ((guide (completing-read "Select a guide: " (mapcar #'car clojure-guides))))
    410     (when guide
    411       (let ((guide-url (concat clojure-guides-base-url (cdr (assoc guide clojure-guides)))))
    412         (browse-url guide-url)))))
    413 
    414 (defconst clojure-reference-base-url "https://clojure.org/reference/"
    415   "The base URL for the official Clojure reference.")
    416 
    417 (defconst clojure-reference-sections '(("The Reader" . "reader")
    418                                        ("The REPL and main" . "repl_and_main")
    419                                        ("Evaluation" . "evaluation")
    420                                        ("Special Forms" . "special_forms")
    421                                        ("Macros" . "macros")
    422                                        ("Other Functions" . "other_functions")
    423                                        ("Data Structures" . "data_structures")
    424                                        ("Datatypes" . "datatypes")
    425                                        ("Sequences" . "sequences")
    426                                        ("Transients" . "transients")
    427                                        ("Transducers" . "transducers")
    428                                        ("Multimethods and Hierarchies" . "multimethods")
    429                                        ("Protocols" . "protocols")
    430                                        ("Metadata" . "metadata")
    431                                        ("Namespaces" . "namespaces")
    432                                        ("Libs" . "libs")
    433                                        ("Vars and Environments" . "vars")
    434                                        ("Refs and Transactions" . "refs")
    435                                        ("Agents" . "agents")
    436                                        ("Atoms" . "atoms")
    437                                        ("Reducers" . "reducers")
    438                                        ("Java Interop" . "java_interop")
    439                                        ("Compilation and Class Generation" . "compilation")
    440                                        ("Other Libraries" . "other_libraries")
    441                                        ("Differences with Lisps" . "lisps")))
    442 
    443 (defun clojure-view-reference-section ()
    444   "Open a Clojure reference section in your default browser.
    445 
    446 The command will prompt you to select one of the available sections."
    447   (interactive)
    448   (let ((section (completing-read "Select a reference section: " (mapcar #'car clojure-reference-sections))))
    449     (when section
    450       (let ((section-url (concat clojure-reference-base-url (cdr (assoc section clojure-reference-sections)))))
    451         (browse-url section-url)))))
    452 
    453 (defconst clojure-cheatsheet-url "https://clojure.org/api/cheatsheet"
    454   "The URL of the official Clojure cheatsheet.")
    455 
    456 (defun clojure-view-cheatsheet ()
    457   "Open the Clojure cheatsheet in your default browser."
    458   (interactive)
    459   (browse-url clojure-cheatsheet-url))
    460 
    461 (defconst clojure-style-guide-url "https://guide.clojure.style"
    462   "The URL of the Clojure style guide.")
    463 
    464 (defun clojure-view-style-guide ()
    465   "Open the Clojure style guide in your default browser."
    466   (interactive)
    467   (browse-url clojure-style-guide-url))
    468 
    469 (defun clojure-space-for-delimiter-p (endp delim)
    470   "Prevent paredit from inserting useless spaces.
    471 See `paredit-space-for-delimiter-predicates' for the meaning of
    472 ENDP and DELIM."
    473   (and (not endp)
    474        ;; don't insert after opening quotes, auto-gensym syntax, or reader tags
    475        (not (looking-back
    476              (if (member delim clojure-omit-space-between-tag-and-delimiters)
    477                  "\\_<\\(?:'+\\|#.*\\)"
    478                "\\_<\\(?:'+\\|#\\)")
    479              (line-beginning-position)))))
    480 
    481 (defconst clojure--collection-tag-regexp "#\\(::[a-zA-Z0-9._-]*\\|:?\\([a-zA-Z0-9._-]+/\\)?[a-zA-Z0-9._-]+\\)"
    482   "Collection reader macro tag regexp.
    483 It is intended to check for allowed strings that can come before a
    484 collection literal (e.g. '[]' or '{}'), as reader macro tags.
    485 This includes #fully.qualified/my-ns[:kw val] and #::my-ns{:kw
    486 val} as of Clojure 1.9.")
    487 
    488 (make-obsolete-variable 'clojure--collection-tag-regexp nil "5.12.0")
    489 (make-obsolete 'clojure-no-space-after-tag 'clojure-space-for-delimiter-p "5.12.0")
    490 
    491 (declare-function paredit-open-curly "ext:paredit" t t)
    492 (declare-function paredit-close-curly "ext:paredit" t t)
    493 (declare-function paredit-convolute-sexp "ext:paredit")
    494 
    495 (defvar clojure--let-regexp
    496   "\(\\(when-let\\|if-let\\|let\\)\\(\\s-*\\|\\[\\)"
    497   "Regexp matching let like expressions, i.e. \"let\", \"when-let\", \"if-let\".
    498 
    499 The first match-group is the let expression.
    500 
    501 The second match-group is the whitespace or the opening square
    502 bracket if no whitespace between the let expression and the
    503 bracket.")
    504 
    505 (defun clojure--replace-let-bindings-and-indent (&rest _)
    506   "Replace let bindings and indent."
    507   (save-excursion
    508     (backward-sexp)
    509     (when (looking-back clojure--let-regexp nil)
    510       (clojure--replace-sexps-with-bindings-and-indent))))
    511 
    512 (defun clojure-paredit-setup (&optional keymap)
    513   "Make \"paredit-mode\" play nice with `clojure-mode'.
    514 
    515 If an optional KEYMAP is passed the changes are applied to it,
    516 instead of to `clojure-mode-map'.
    517 Also advice `paredit-convolute-sexp' when used on a let form as drop in
    518 replacement for `cljr-expand-let`."
    519   (when (>= paredit-version 21)
    520     (let ((keymap (or keymap clojure-mode-map)))
    521       (define-key keymap "{" #'paredit-open-curly)
    522       (define-key keymap "}" #'paredit-close-curly))
    523     (make-local-variable 'paredit-space-for-delimiter-predicates)
    524     (add-to-list 'paredit-space-for-delimiter-predicates
    525                  #'clojure-space-for-delimiter-p)
    526     (advice-add 'paredit-convolute-sexp :after #'clojure--replace-let-bindings-and-indent)))
    527 
    528 (defun clojure-current-defun-name ()
    529   "Return the name of the defun at point, or nil.
    530 
    531 `add-log-current-defun-function' is set to this, for use by `which-func'."
    532   (save-excursion
    533     (let ((location (point)))
    534       ;; If we are now precisely at the beginning of a defun, make sure
    535       ;; beginning-of-defun finds that one rather than the previous one.
    536       (or (eobp) (forward-char 1))
    537       (beginning-of-defun)
    538       ;; Make sure we are really inside the defun found, not after it.
    539       (when (and (looking-at "\\s(")
    540                  (progn (end-of-defun)
    541                         (< location (point)))
    542                  (progn (forward-sexp -1)
    543                         (>= location (point))))
    544         (if (looking-at "\\s(")
    545             (forward-char 1))
    546         ;; Skip the defining construct name, e.g. "defn" or "def".
    547         (forward-sexp 1)
    548         ;; The second element is usually a symbol being defined.  If it
    549         ;; is not, use the first symbol in it.
    550         (skip-chars-forward " \t\n'(")
    551         ;; Skip metadata
    552         (while (looking-at "\\^")
    553           (forward-sexp 1)
    554           (skip-chars-forward " \t\n'("))
    555         (buffer-substring-no-properties (point)
    556                                         (progn (forward-sexp 1)
    557                                                (point)))))))
    558 
    559 (defun clojure-mode-variables ()
    560   "Set up initial buffer-local variables for Clojure mode."
    561   (add-to-list 'imenu-generic-expression '(nil clojure-match-next-def 0))
    562   (setq-local indent-tabs-mode nil)
    563   (setq-local paragraph-ignore-fill-prefix t)
    564   (setq-local outline-regexp ";;;;* ")
    565   (setq-local outline-level 'lisp-outline-level)
    566   (setq-local comment-start ";")
    567   (setq-local comment-start-skip ";+ *")
    568   (setq-local comment-add 1) ; default to `;;' in comment-region
    569   (setq-local comment-column 40)
    570   (setq-local comment-use-syntax t)
    571   (setq-local multibyte-syntax-as-symbol t)
    572   (setq-local electric-pair-skip-whitespace 'chomp)
    573   (setq-local electric-pair-open-newline-between-pairs nil)
    574   (setq-local fill-paragraph-function #'clojure-fill-paragraph)
    575   (setq-local adaptive-fill-function #'clojure-adaptive-fill-function)
    576   (setq-local normal-auto-fill-function #'clojure-auto-fill-function)
    577   (setq-local comment-start-skip
    578               "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *")
    579   (setq-local indent-line-function #'clojure-indent-line)
    580   (setq-local indent-region-function #'clojure-indent-region)
    581   (setq-local lisp-indent-function #'clojure-indent-function)
    582   (setq-local lisp-doc-string-elt-property 'clojure-doc-string-elt)
    583   (setq-local clojure-expected-ns-function #'clojure-expected-ns)
    584   (setq-local parse-sexp-ignore-comments t)
    585   (setq-local prettify-symbols-alist clojure--prettify-symbols-alist)
    586   (setq-local open-paren-in-column-0-is-defun-start nil)
    587   (setq-local add-log-current-defun-function #'clojure-current-defun-name)
    588   (setq-local beginning-of-defun-function #'clojure-beginning-of-defun-function))
    589 
    590 (defsubst clojure-in-docstring-p ()
    591   "Check whether point is in a docstring."
    592   (let ((ppss (syntax-ppss)))
    593     ;; are we in a string?
    594     (when (nth 3 ppss)
    595       ;; check font lock at the start of the string
    596       (eq (get-text-property (nth 8 ppss) 'face)
    597           'font-lock-doc-face))))
    598 
    599 ;;;###autoload
    600 (define-derived-mode clojure-mode prog-mode "Clojure"
    601   "Major mode for editing Clojure code.
    602 
    603 \\{clojure-mode-map}"
    604   (clojure-mode-variables)
    605   (clojure-font-lock-setup)
    606   (add-hook 'paredit-mode-hook #'clojure-paredit-setup)
    607   ;; `electric-layout-post-self-insert-function' prevents indentation in strings
    608   ;; and comments, force indentation of non-inlined docstrings:
    609   (add-hook 'electric-indent-functions
    610             (lambda (_char) (if (and (clojure-in-docstring-p)
    611                                      ;; make sure we're not dealing with an inline docstring
    612                                      ;; e.g. (def foo "inline docstring" bar)
    613                                      (save-excursion
    614                                        (beginning-of-line-text)
    615                                        (eq (get-text-property (point) 'face)
    616                                            'font-lock-doc-face)))
    617                                 'do-indent))))
    618 
    619 (defcustom clojure-verify-major-mode t
    620   "If non-nil, warn when activating the wrong `major-mode'."
    621   :type 'boolean
    622   :safe #'booleanp
    623   :package-version '(clojure-mode "5.3.0"))
    624 
    625 (defun clojure--check-wrong-major-mode ()
    626   "Check if the current `major-mode' matches the file extension.
    627 
    628 If it doesn't, issue a warning if `clojure-verify-major-mode' is
    629 non-nil."
    630   (when (and clojure-verify-major-mode
    631              (stringp (buffer-file-name)))
    632     (let* ((case-fold-search t)
    633            (problem (cond ((and (string-match "\\.clj\\'" (buffer-file-name))
    634                                 (not (eq major-mode 'clojure-mode)))
    635                            'clojure-mode)
    636                           ((and (string-match "\\.cljs\\'" (buffer-file-name))
    637                                 (not (eq major-mode 'clojurescript-mode)))
    638                            'clojurescript-mode)
    639                           ((and (string-match "\\.cljc\\'" (buffer-file-name))
    640                                 (not (eq major-mode 'clojurec-mode)))
    641                            'clojurec-mode))))
    642       (when problem
    643         (message "[WARNING] %s activated `%s' instead of `%s' in this buffer.
    644 This could cause problems.
    645 \(See `clojure-verify-major-mode' to disable this message.)"
    646                  (if (eq major-mode real-this-command)
    647                      "You have"
    648                    "Something in your configuration")
    649                  major-mode
    650                  problem)))))
    651 
    652 (add-hook 'clojure-mode-hook #'clojure--check-wrong-major-mode)
    653 
    654 (defsubst clojure-docstring-fill-prefix ()
    655   "The prefix string used by `clojure-fill-paragraph'.
    656 It is simply `clojure-docstring-fill-prefix-width' number of spaces."
    657   (make-string clojure-docstring-fill-prefix-width ? ))
    658 
    659 (defun clojure-adaptive-fill-function ()
    660   "Clojure adaptive fill function.
    661 This only takes care of filling docstring correctly."
    662   (when (clojure-in-docstring-p)
    663     (clojure-docstring-fill-prefix)))
    664 
    665 (defun clojure-fill-paragraph (&optional justify)
    666   "Like `fill-paragraph', but can handle Clojure docstrings.
    667 If JUSTIFY is non-nil, justify as well as fill the paragraph."
    668   (if (clojure-in-docstring-p)
    669       (let ((paragraph-start
    670              (concat paragraph-start
    671                      "\\|\\s-*\\([(:\"[]\\|~@\\|`(\\|#'(\\)"))
    672             (paragraph-separate
    673              (concat paragraph-separate "\\|\\s-*\".*[,\\.]$"))
    674             (fill-column (or clojure-docstring-fill-column fill-column))
    675             (fill-prefix (clojure-docstring-fill-prefix)))
    676         ;; we are in a string and string start pos (8th element) is non-nil
    677         (let* ((beg-doc (nth 8 (syntax-ppss)))
    678                (end-doc (save-excursion
    679                           (goto-char beg-doc)
    680                           (or (ignore-errors (forward-sexp) (point))
    681                               (point-max)))))
    682           (save-restriction
    683             (narrow-to-region beg-doc end-doc)
    684             (fill-paragraph justify))))
    685     (let ((paragraph-start (concat paragraph-start
    686                                    "\\|\\s-*\\([(:\"[]\\|`(\\|#'(\\)"))
    687           (paragraph-separate
    688            (concat paragraph-separate "\\|\\s-*\".*[,\\.[]$")))
    689       (or (fill-comment-paragraph justify)
    690           (fill-paragraph justify))
    691       ;; Always return `t'
    692       t)))
    693 
    694 (defun clojure-auto-fill-function ()
    695   "Clojure auto-fill function."
    696   ;; Check if auto-filling is meaningful.
    697   (let ((fc (current-fill-column)))
    698     (when (and fc (> (current-column) fc))
    699       (let ((fill-column (if (clojure-in-docstring-p)
    700                              clojure-docstring-fill-column
    701                            fill-column))
    702             (fill-prefix (clojure-adaptive-fill-function)))
    703         (do-auto-fill)))))
    704 
    705 
    706 ;;; #_ comments font-locking
    707 ;; Code heavily borrowed from Slime.
    708 ;; https://github.com/slime/slime/blob/master/contrib/slime-fontifying-fu.el#L186
    709 (defvar clojure--comment-macro-regexp
    710   (rx (seq (+ (seq "#_" (* " ")))) (group-n 1 (not (any " "))))
    711   "Regexp matching the start of a comment sexp.
    712 The beginning of match-group 1 should be before the sexp to be
    713 marked as a comment.  The end of sexp is found with
    714 `clojure-forward-logical-sexp'.")
    715 
    716 (defvar clojure--reader-and-comment-regexp
    717   (rx (or (seq (+ (seq "#_" (* " ")))
    718                (group-n 1 (not (any " "))))
    719           (seq (group-n 1 "(comment" symbol-end))))
    720   "Regexp matching both `#_' macro and a comment sexp." )
    721 
    722 (defcustom clojure-comment-regexp clojure--comment-macro-regexp
    723   "Comment mode.
    724 
    725 The possible values for this variable are keywords indicating
    726 what is considered a comment (affecting font locking).
    727 
    728     - Reader macro `#_' only - the default
    729     - Reader macro `#_' and `(comment)'"
    730   :type '(choice (const :tag "Reader macro `#_' and `(comment)'" clojure--reader-and-comment-regexp)
    731                  (other :tag "Reader macro `#_' only" clojure--comment-macro-regexp))
    732   :package-version '(clojure-mode . "5.7.0"))
    733 
    734 (defun clojure--search-comment-macro-internal (limit)
    735   "Search for a comment forward stopping at LIMIT."
    736   (when (search-forward-regexp clojure-comment-regexp limit t)
    737     (let* ((md (match-data))
    738            (start (match-beginning 1))
    739            (state (syntax-ppss start)))
    740       ;; inside string or comment?
    741       (if (or (nth 3 state)
    742               (nth 4 state))
    743           (clojure--search-comment-macro-internal limit)
    744         (goto-char start)
    745         ;; Count how many #_ we got and step by that many sexps
    746         ;; For (comment ...), step at least 1 sexp
    747         (clojure-forward-logical-sexp
    748          (max (count-matches (rx "#_") (elt md 0) (elt md 1))
    749               1))
    750         ;; Data for (match-end 1).
    751         (setf (elt md 3) (point))
    752         (set-match-data md)
    753         t))))
    754 
    755 (defun clojure--search-comment-macro (limit)
    756   "Find comment macros and set the match data.
    757 Search from point up to LIMIT.  The region that should be
    758 considered a comment is between `(match-beginning 1)'
    759 and `(match-end 1)'."
    760   (let ((result 'retry))
    761     (while (and (eq result 'retry) (<= (point) limit))
    762       (condition-case nil
    763           (setq result (clojure--search-comment-macro-internal limit))
    764         (end-of-file (setq result nil))
    765         (scan-error  (setq result 'retry))))
    766     result))
    767 
    768 
    769 ;;; General font-locking
    770 (defun clojure-match-next-def ()
    771   "Scans the buffer backwards for the next \"top-level\" definition.
    772 Called by `imenu--generic-function'."
    773   ;; we have to take into account namespace-definition forms
    774   ;; e.g. s/defn
    775   (when (re-search-backward "^[ \t]*(\\([a-z0-9.-]+/\\)?\\(def\\sw*\\)" nil t)
    776     (save-excursion
    777       (let (found?
    778             (deftype (match-string 2))
    779             (start (point)))
    780         ;; ignore user-error from down-list when called from inside a string or comment
    781         ;; TODO: a better workaround would be to wrap it in
    782         ;; unless (ppss-comment-or-string-start (syntax-ppss)) instead of ignore-errors,
    783         ;; but ppss-comment-or-string-start is only available since Emacs 27
    784         (ignore-errors
    785           (down-list))
    786         (forward-sexp)
    787         (while (not found?)
    788           (ignore-errors
    789             (forward-sexp))
    790           (or (when (char-equal ?\[ (char-after (point)))
    791                 (backward-sexp))
    792               (when (char-equal ?\) (char-after (point)))
    793                 (backward-sexp)))
    794           (cl-destructuring-bind (def-beg . def-end) (bounds-of-thing-at-point 'sexp)
    795             (when (char-equal ?^ (char-after def-beg))
    796               ;; move to the beginning of next sexp
    797               (progn (forward-sexp) (backward-sexp)))
    798             (when (or (not (char-equal ?^ (char-after def-beg)))
    799                       (and (char-equal ?^ (char-after (point))) (= def-beg (point))))
    800               (setq found? t)
    801               (when (string= deftype "defmethod")
    802                 (setq def-end (progn (goto-char def-end)
    803                                      (forward-sexp)
    804                                      (point))))
    805               (set-match-data (list def-beg def-end)))))
    806         (goto-char start)))))
    807 
    808 (eval-and-compile
    809   (defconst clojure--sym-forbidden-rest-chars "][\";@\\^`~\(\)\{\}\\,\s\t\n\r"
    810     "A list of chars that a Clojure symbol cannot contain.
    811 See definition of `macros': URL `http://git.io/vRGLD'.")
    812   (defconst clojure--sym-forbidden-1st-chars (concat clojure--sym-forbidden-rest-chars "0-9:'")
    813     "A list of chars that a Clojure symbol cannot start with.
    814 See the for-loop: URL `http://git.io/vRGTj' lines: URL
    815 `http://git.io/vRGIh', URL `http://git.io/vRGLE' and value
    816 definition of `macros': URL `http://git.io/vRGLD'.")
    817   (defconst clojure--sym-regexp
    818     (concat "[^" clojure--sym-forbidden-1st-chars "][^" clojure--sym-forbidden-rest-chars "]*")
    819     "A regexp matching a Clojure symbol or namespace alias.
    820 Matches the rule `clojure--sym-forbidden-1st-chars' followed by
    821 any number of matches of `clojure--sym-forbidden-rest-chars'.")
    822   (defconst clojure--keyword-sym-forbidden-1st-chars
    823     (concat clojure--sym-forbidden-rest-chars ":'")
    824     "A list of chars that a Clojure keyword symbol cannot start with.")
    825   (defconst clojure--keyword-sym-regexp
    826     (concat "[^" clojure--keyword-sym-forbidden-1st-chars "]"
    827             "[^" clojure--sym-forbidden-rest-chars "]*")
    828     "A regexp matching a Clojure keyword name or keyword namespace.
    829 Matches the rule `clojure--keyword-sym-forbidden-1st-chars' followed by
    830 any number of matches of `clojure--sym-forbidden-rest-chars'."))
    831 
    832 (defconst clojure-font-lock-keywords
    833   (eval-when-compile
    834     `(;; Any def form
    835       (,(concat "(\\(?:" clojure--sym-regexp "/\\)?"
    836                 "\\("
    837                 (regexp-opt '("def"
    838                               "defonce"
    839                               "defn"
    840                               "defn-"
    841                               "defmacro"
    842                               "definline"
    843                               "defmulti"
    844                               "defmethod"
    845                               "defprotocol"
    846                               "definterface"
    847                               "defrecord"
    848                               "deftype"
    849                               "defstruct"
    850                               ;; clojure.test
    851                               "deftest"
    852                               "deftest-"
    853                               ;; clojure.logic
    854                               "defne"
    855                               "defnm"
    856                               "defnu"
    857                               "defnc"
    858                               "defna"
    859                               ;; Third party
    860                               "deftask"
    861                               "defstate"))
    862                 "\\)\\>")
    863        (1 font-lock-keyword-face))
    864       ;; Top-level variable definition
    865       (,(concat "(\\(?:clojure.core/\\)?\\("
    866                 (regexp-opt '("def" "defonce"))
    867                 ;; variable declarations
    868                 "\\)\\>"
    869                 ;; Any whitespace
    870                 "[ \r\n\t]*"
    871                 ;; Possibly type or metadata
    872                 "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*"
    873                 "\\(\\sw+\\)?")
    874        (2 font-lock-variable-name-face nil t))
    875       ;; Type definition
    876       (,(concat "(\\(?:clojure.core/\\)?\\("
    877                 (regexp-opt '("defstruct" "deftype" "defprotocol"
    878                               "defrecord"))
    879                 ;; type declarations
    880                 "\\)\\>"
    881                 ;; Any whitespace
    882                 "[ \r\n\t]*"
    883                 ;; Possibly type or metadata
    884                 "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*"
    885                 "\\(\\sw+\\)?")
    886        (2 font-lock-type-face nil t))
    887       ;; Function definition
    888       (,(concat "(\\(?:clojure.core/\\)?\\("
    889                 (regexp-opt '("defn"
    890                               "defn-"
    891                               "defmulti"
    892                               "defmethod"
    893                               "deftest"
    894                               "deftest-"
    895                               "defmacro"
    896                               "definline"))
    897                 "\\)"
    898                 ;; Function declarations
    899                 "\\>"
    900                 ;; Any whitespace
    901                 "[ \r\n\t]*"
    902                 ;; Possibly type or metadata
    903                 "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*"
    904                 (concat "\\(" clojure--sym-regexp "\\)?"))
    905        (2 font-lock-function-name-face nil t))
    906       ;; (fn name? args ...)
    907       (,(concat "(\\(?:clojure.core/\\)?\\(fn\\)[ \t]+"
    908                 ;; Possibly type
    909                 "\\(?:#?^\\sw+[ \t]*\\)?"
    910                 ;; Possibly name
    911                 "\\(\\sw+\\)?" )
    912        (2 font-lock-function-name-face nil t))
    913       ;; Special forms
    914       (,(concat
    915          "("
    916          (regexp-opt
    917           '("do" "if" "let*" "var" "fn" "fn*" "loop*"
    918             "recur" "throw" "try" "catch" "finally"
    919             "set!" "new" "."
    920             "monitor-enter" "monitor-exit" "quote") t)
    921          "\\>")
    922        1 font-lock-keyword-face)
    923       ;; Built-in binding and flow of control forms
    924       (,(concat
    925          "(\\(?:clojure.core/\\)?"
    926          (regexp-opt
    927           '(
    928             "->"
    929             "->>"
    930             ".."
    931             "amap"
    932             "and"
    933             "areduce"
    934             "as->"
    935             "assert"
    936             "binding"
    937             "bound-fn"
    938             "case"
    939             "comment"
    940             "cond"
    941             "cond->"
    942             "cond->>"
    943             "condp"
    944             "declare"
    945             "delay"
    946             "doall"
    947             "dorun"
    948             "doseq"
    949             "dosync"
    950             "dotimes"
    951             "doto"
    952             "extend-protocol"
    953             "extend-type"
    954             "for"
    955             "future"
    956             "gen-class"
    957             "gen-interface"
    958             "if-let"
    959             "if-not"
    960             "if-some"
    961             "import"
    962             "in-ns"
    963             "io!"
    964             "lazy-cat"
    965             "lazy-seq"
    966             "let"
    967             "letfn"
    968             "locking"
    969             "loop"
    970             "memfn"
    971             "ns"
    972             "or"
    973             "proxy"
    974             "proxy-super"
    975             "pvalues"
    976             "refer-clojure"
    977             "reify"
    978             "some->"
    979             "some->>"
    980             "sync"
    981             "time"
    982             "vswap!"
    983             "when"
    984             "when-first"
    985             "when-let"
    986             "when-not"
    987             "when-some"
    988             "while"
    989             "with-bindings"
    990             "with-in-str"
    991             "with-loading-context"
    992             "with-local-vars"
    993             "with-open"
    994             "with-out-str"
    995             "with-precision"
    996             "with-redefs"
    997             "with-redefs-fn"
    998             )
    999           t)
   1000          "\\>")
   1001        1 font-lock-keyword-face)
   1002       ;; Macros similar to let, when, and while
   1003       (,(rx symbol-start
   1004             (or "let" "when" "while") "-"
   1005             (1+ (or (syntax word) (syntax symbol)))
   1006             symbol-end)
   1007        0 font-lock-keyword-face)
   1008       (,(concat
   1009          "\\<"
   1010          (regexp-opt
   1011           '("*1" "*2" "*3" "*agent*"
   1012             "*allow-unresolved-vars*" "*assert*" "*clojure-version*"
   1013             "*command-line-args*" "*compile-files*"
   1014             "*compile-path*" "*data-readers*" "*default-data-reader-fn*"
   1015             "*e" "*err*" "*file*" "*flush-on-newline*"
   1016             "*in*" "*macro-meta*" "*math-context*" "*ns*" "*out*"
   1017             "*print-dup*" "*print-length*" "*print-level*"
   1018             "*print-meta*" "*print-readably*"
   1019             "*read-eval*" "*source-path*"
   1020             "*unchecked-math*"
   1021             "*use-context-classloader*" "*warn-on-reflection*")
   1022           t)
   1023          "\\>")
   1024        0 font-lock-builtin-face)
   1025       ;; Dynamic variables - *something* or @*something*
   1026       (,(concat "\\(?:\\<\\|/\\)@?\\(\\*" clojure--sym-regexp "\\*\\)\\>")
   1027        1 font-lock-variable-name-face)
   1028       ;; Global constants - nil, true, false
   1029       (,(concat
   1030          "\\<"
   1031          (regexp-opt
   1032           '("true" "false" "nil") t)
   1033          "\\>")
   1034        0 font-lock-constant-face)
   1035       ;; Character literals - \1, \a, \newline, \u0000
   1036       (,(rx (group "\\" (or any
   1037                             "newline" "space" "tab" "formfeed" "backspace"
   1038                             "return"
   1039                             (: "u" (= 4 (char "0-9a-fA-F")))
   1040                             (: "o" (repeat 1 3 (char "0-7")))))
   1041             (or (not word) word-boundary))
   1042        1 'clojure-character-face)
   1043       ;; lambda arguments - %, %&, %1, %2, etc
   1044       ;; must come after character literals for \% to be handled properly
   1045       ("\\<%[&1-9]?" (0 font-lock-variable-name-face))
   1046       ;; namespace definitions: (ns foo.bar)
   1047       (,(concat "(\\<ns\\>[ \r\n\t]*"
   1048                 ;; Possibly metadata, shorthand and/or longhand
   1049                 "\\(?:\\^?\\(?:{[^}]+}\\|:[^ \r\n\t]+[ \r\n\t]\\)[ \r\n\t]*\\)*"
   1050                 ;; namespace
   1051                 "\\(" clojure--sym-regexp "\\)")
   1052        (1 font-lock-type-face))
   1053 
   1054       ;; TODO dedupe the code for matching of keywords, type-hints and unmatched symbols
   1055 
   1056       ;; keywords: {:oneword/ve/yCom|pLex.stu-ff 0}
   1057       (,(concat "\\(:\\{1,2\\}\\)\\(" clojure--keyword-sym-regexp "?\\)\\(/\\)"
   1058                 "\\(" clojure--keyword-sym-regexp "\\)")
   1059        (1 'clojure-keyword-face)
   1060        (2 font-lock-type-face)
   1061        ;; (2 'clojure-keyword-face)
   1062        (3 'default)
   1063        (4 'clojure-keyword-face))
   1064       (,(concat "\\(:\\{1,2\\}\\)\\(" clojure--keyword-sym-regexp "\\)")
   1065        (1 'clojure-keyword-face)
   1066        (2 'clojure-keyword-face))
   1067 
   1068       ;; type-hints: #^oneword
   1069       (,(concat "\\(#?\\^\\)\\(" clojure--sym-regexp "?\\)\\(/\\)\\(" clojure--sym-regexp "\\)")
   1070        (1 'default)
   1071        (2 font-lock-type-face)
   1072        (3 'default)
   1073        (4 'default))
   1074       (,(concat "\\(#?\\^\\)\\(" clojure--sym-regexp "\\)")
   1075        (1 'default)
   1076        (2 font-lock-type-face))
   1077 
   1078       ;; clojure symbols not matched by the previous regexps; influences CIDER's
   1079       ;; dynamic syntax highlighting (CDSH). See https://git.io/vxEEA:
   1080       (,(concat "\\(" clojure--sym-regexp "?\\)\\(/\\)\\(" clojure--sym-regexp "\\)")
   1081        (1 font-lock-type-face)
   1082        ;; 2nd and 3th matching groups can be font-locked to `nil' or `default'.
   1083        ;; CDSH seems to kick in only for functions and variables referenced w/o
   1084        ;; writing their namespaces.
   1085        (2 nil)
   1086        (3 nil))
   1087       (,(concat "\\(" clojure--sym-regexp "\\)")
   1088        ;; this matching group must be font-locked to `nil' otherwise CDSH breaks.
   1089        (1 nil))
   1090 
   1091       ;; #_ and (comment ...) macros.
   1092       (clojure--search-comment-macro 1 font-lock-comment-face t)
   1093       ;; Highlight `code` marks, just like `elisp'.
   1094       (,(rx "`" (group-n 1 (optional "#'")
   1095                          (+ (or (syntax symbol) (syntax word)))) "`")
   1096        (1 'font-lock-constant-face prepend))
   1097       ;; Highlight [[var]] comments
   1098       (,(rx "[[" (group-n 1 (optional "#'")
   1099                           (+ (or (syntax symbol) (syntax word)))) "]]")
   1100        (1 'font-lock-constant-face prepend))
   1101       ;; Highlight escaped characters in strings.
   1102       (clojure-font-lock-escaped-chars 0 'bold prepend)
   1103       ;; Highlight grouping constructs in regular expressions
   1104       (clojure-font-lock-regexp-groups
   1105        (1 'font-lock-regexp-grouping-construct prepend))))
   1106   "Default expressions to highlight in Clojure mode.")
   1107 
   1108 (defun clojure-font-lock-syntactic-face-function (state)
   1109   "Find and highlight text with a Clojure-friendly syntax table.
   1110 
   1111 This function is passed to `font-lock-syntactic-face-function',
   1112 which is called with a single parameter, STATE (which is, in
   1113 turn, returned by `parse-partial-sexp' at the beginning of the
   1114 highlighted region)."
   1115   (if (nth 3 state)
   1116       ;; This is a (doc)string
   1117       (let* ((startpos (nth 8 state))
   1118              (listbeg (nth 1 state))
   1119              (firstsym (and listbeg
   1120                             (save-excursion
   1121                               (goto-char listbeg)
   1122                               (and (looking-at "([ \t\n]*\\(\\(\\sw\\|\\s_\\)+\\)")
   1123                                    (match-string 1)))))
   1124              (docelt (and firstsym
   1125                           (function-get (intern-soft firstsym)
   1126                                         lisp-doc-string-elt-property))))
   1127         (if (and docelt
   1128                  ;; It's a string in a form that can have a docstring.
   1129                  ;; Check whether it's in docstring position.
   1130                  (save-excursion
   1131                    (when (functionp docelt)
   1132                      (goto-char (match-end 1))
   1133                      (setq docelt (funcall docelt)))
   1134                    (goto-char listbeg)
   1135                    (forward-char 1)
   1136                    (ignore-errors
   1137                      (while (and (> docelt 0) (< (point) startpos)
   1138                                  (progn (forward-sexp 1) t))
   1139                        ;; ignore metadata and type hints
   1140                        (unless (looking-at "[ \n\t]*\\(\\^[A-Z:].+\\|\\^?{.+\\)")
   1141                          (setq docelt (1- docelt)))))
   1142                    (and (zerop docelt) (<= (point) startpos)
   1143                         (progn (forward-comment (point-max)) t)
   1144                         (= (point) (nth 8 state))))
   1145                  ;; In a def, at last position is not a docstring
   1146                  (not (and (string= "def" firstsym)
   1147                            (save-excursion
   1148                              (goto-char startpos)
   1149                              (goto-char (end-of-thing 'sexp))
   1150                              (looking-at "[ \r\n\t]*\)")))))
   1151             font-lock-doc-face
   1152           font-lock-string-face))
   1153     font-lock-comment-face))
   1154 
   1155 (defun clojure-font-lock-setup ()
   1156   "Configures font-lock for editing Clojure code."
   1157   (setq-local font-lock-multiline t)
   1158   (add-to-list 'font-lock-extend-region-functions
   1159                #'clojure-font-lock-extend-region-def t)
   1160   (setq font-lock-defaults
   1161         '(clojure-font-lock-keywords    ; keywords
   1162           nil nil
   1163           (("+-*/.<>=!?$%_&:" . "w")) ; syntax alist
   1164           nil
   1165           (font-lock-mark-block-function . mark-defun)
   1166           (font-lock-syntactic-face-function
   1167            . clojure-font-lock-syntactic-face-function))))
   1168 
   1169 (defun clojure-font-lock-def-at-point (point)
   1170   "Range between the top-most def* and the fourth element after POINT.
   1171 Note that this means that there is no guarantee of proper font
   1172 locking in def* forms that are not at top level."
   1173   (goto-char point)
   1174   (ignore-errors
   1175     (beginning-of-defun))
   1176 
   1177   (let ((beg-def (point)))
   1178     (when (and (not (= point beg-def))
   1179                (looking-at "(def"))
   1180       (ignore-errors
   1181         ;; move forward as much as possible until failure (or success)
   1182         (forward-char)
   1183         (dotimes (_ 4)
   1184           (forward-sexp)))
   1185       (cons beg-def (point)))))
   1186 
   1187 (defun clojure-font-lock-extend-region-def ()
   1188   "Set region boundaries to include the first four elements of def* forms."
   1189   (let ((changed nil))
   1190     (let ((def (clojure-font-lock-def-at-point font-lock-beg)))
   1191       (when def
   1192         (cl-destructuring-bind (def-beg . def-end) def
   1193           (when (and (< def-beg font-lock-beg)
   1194                      (< font-lock-beg def-end))
   1195             (setq font-lock-beg def-beg
   1196                   changed t)))))
   1197     (let ((def (clojure-font-lock-def-at-point font-lock-end)))
   1198       (when def
   1199         (cl-destructuring-bind (def-beg . def-end) def
   1200           (when (and (< def-beg font-lock-end)
   1201                      (< font-lock-end def-end))
   1202             (setq font-lock-end def-end
   1203                   changed t)))))
   1204     changed))
   1205 
   1206 (defun clojure--font-locked-as-string-p (&optional regexp)
   1207   "Non-nil if the char before point is font-locked as a string.
   1208 If REGEXP is non-nil, also check whether current string is
   1209 preceeded by a #."
   1210   (let ((face (get-text-property (1- (point)) 'face)))
   1211     (and (or (and (listp face)
   1212                   (memq 'font-lock-string-face face))
   1213              (eq 'font-lock-string-face face))
   1214          (or (clojure-string-start t)
   1215              (unless regexp
   1216                (clojure-string-start nil))))))
   1217 
   1218 (defun clojure-font-lock-escaped-chars (bound)
   1219   "Highlight \escaped chars in strings.
   1220 BOUND denotes a buffer position to limit the search."
   1221   (let ((found nil))
   1222     (while (and (not found)
   1223                 (re-search-forward "\\\\." bound t))
   1224 
   1225       (setq found (clojure--font-locked-as-string-p)))
   1226     found))
   1227 
   1228 (defun clojure-font-lock-regexp-groups (bound)
   1229   "Highlight grouping constructs in regular expression.
   1230 
   1231 BOUND denotes the maximum number of characters (relative to the
   1232 point) to check."
   1233   (let ((found nil))
   1234     (while (and (not found)
   1235                 (re-search-forward (eval-when-compile
   1236                                      (concat
   1237                                       ;; A group may start using several alternatives:
   1238                                       "\\(\\(?:"
   1239                                       ;; 1. (? special groups
   1240                                       "(\\?\\(?:"
   1241                                       ;; a) non-capturing group (?:X)
   1242                                       ;; b) independent non-capturing group (?>X)
   1243                                       ;; c) zero-width positive lookahead (?=X)
   1244                                       ;; d) zero-width negative lookahead (?!X)
   1245                                       "[:=!>]\\|"
   1246                                       ;; e) zero-width positive lookbehind (?<=X)
   1247                                       ;; f) zero-width negative lookbehind (?<!X)
   1248                                       "<[=!]\\|"
   1249                                       ;; g) named capturing group (?<name>X)
   1250                                       "<[[:alnum:]]+>"
   1251                                       "\\)\\|" ;; end of special groups
   1252                                       ;; 2. normal capturing groups (
   1253                                       ;; 3. we also highlight alternative
   1254                                       ;; separarators |, and closing parens )
   1255                                       "[|()]"
   1256                                       "\\)\\)"))
   1257                                    bound t))
   1258       (setq found (clojure--font-locked-as-string-p 'regexp)))
   1259     found))
   1260 
   1261 ;; Docstring positions
   1262 (put 'ns 'clojure-doc-string-elt 2)
   1263 (put 'def 'clojure-doc-string-elt 2)
   1264 (put 'defn 'clojure-doc-string-elt 2)
   1265 (put 'defn- 'clojure-doc-string-elt 2)
   1266 (put 'defmulti 'clojure-doc-string-elt 2)
   1267 (put 'defmacro 'clojure-doc-string-elt 2)
   1268 (put 'definline 'clojure-doc-string-elt 2)
   1269 (put 'defprotocol 'clojure-doc-string-elt 2)
   1270 (put 'deftask 'clojure-doc-string-elt 2) ;; common Boot macro
   1271 
   1272 ;;; Vertical alignment
   1273 (defcustom clojure-align-forms-automatically nil
   1274   "If non-nil, vertically align some forms automatically.
   1275 Automatically means it is done as part of indenting code.  This
   1276 applies to binding forms (`clojure-align-binding-forms'), to cond
   1277 forms (`clojure-align-cond-forms') and to map literals.  For
   1278 instance, selecting a map a hitting \\<clojure-mode-map>`\\[indent-for-tab-command]'
   1279 will align the values like this:
   1280     {:some-key 10
   1281      :key2     20}"
   1282   :package-version '(clojure-mode . "5.1")
   1283   :safe #'booleanp
   1284   :type 'boolean)
   1285 
   1286 (defconst clojure--align-separator-newline-regexp "^ *$")
   1287 
   1288 (defcustom clojure-align-separator clojure--align-separator-newline-regexp
   1289   "Separator passed to `align-region' when performing vertical alignment."
   1290   :package-version '(clojure-mode . "5.10")
   1291   :type `(choice (const :tag "Make blank lines prevent vertical alignment from happening."
   1292                         ,clojure--align-separator-newline-regexp)
   1293                  (other :tag "Allow blank lines to happen within a vertically-aligned expression."
   1294                         entire)))
   1295 
   1296 (defcustom clojure-align-reader-conditionals nil
   1297   "Whether to align reader conditionals, as if they were maps."
   1298   :package-version '(clojure-mode . "5.10")
   1299   :safe #'booleanp
   1300   :type 'boolean)
   1301 
   1302 (defcustom clojure-align-binding-forms
   1303   '("let" "when-let" "when-some" "if-let" "if-some" "binding" "loop"
   1304     "doseq" "for" "with-open" "with-local-vars" "with-redefs")
   1305   "List of strings matching forms that have binding forms."
   1306   :package-version '(clojure-mode . "5.1")
   1307   :safe #'listp
   1308   :type '(repeat string))
   1309 
   1310 (defcustom clojure-align-cond-forms
   1311   '("condp" "cond" "cond->" "cond->>" "case" "are"
   1312     "clojure.core/condp" "clojure.core/cond" "clojure.core/cond->"
   1313     "clojure.core/cond->>" "clojure.core/case" "clojure.test/are")
   1314   "List of strings identifying cond-like forms."
   1315   :package-version '(clojure-mode . "5.1")
   1316   :safe #'listp
   1317   :type '(repeat string))
   1318 
   1319 (defcustom clojure-special-arg-indent-factor
   1320   2
   1321   "Factor of the `lisp-body-indent' used to indent special arguments."
   1322   :package-version '(clojure-mode . "5.13")
   1323   :type 'integer
   1324   :safe 'integerp)
   1325 
   1326 (defvar clojure--beginning-of-reader-conditional-regexp
   1327   "#\\?@(\\|#\\?("
   1328   "Regexp denoting the beginning of a reader conditional.")
   1329 
   1330 (defun clojure--position-for-alignment ()
   1331   "Non-nil if the sexp around point should be automatically aligned.
   1332 This function expects to be called immediately after an
   1333 open-brace or after the function symbol in a function call.
   1334 
   1335 First check if the sexp around point is a map literal, or is a
   1336 call to one of the vars listed in `clojure-align-cond-forms'.  If
   1337 it isn't, return nil.  If it is, return non-nil and place point
   1338 immediately before the forms that should be aligned.
   1339 
   1340 For instance, in a map literal point is left immediately before
   1341 the first key; while, in a let-binding, point is left inside the
   1342 binding vector and immediately before the first binding
   1343 construct."
   1344   (let ((point (point)))
   1345     ;; Are we in a map?
   1346     (or (and (eq (char-before) ?{)
   1347              (not (eq (char-before (1- point)) ?\#)))
   1348         ;; Are we in a reader conditional?
   1349         (and clojure-align-reader-conditionals
   1350              (looking-back clojure--beginning-of-reader-conditional-regexp (- (point) 4)))
   1351         ;; Are we in a cond form?
   1352         (let* ((fun    (car (member (thing-at-point 'symbol) clojure-align-cond-forms)))
   1353                (method (and fun (clojure--get-indent-method fun)))
   1354                ;; The number of special arguments in the cond form is
   1355                ;; the number of sexps we skip before aligning.
   1356                (skip   (cond ((numberp method) method)
   1357                              ((null method) 0)
   1358                              ((sequencep method) (elt method 0)))))
   1359           (when (and fun (numberp skip))
   1360             (clojure-forward-logical-sexp skip)
   1361             (comment-forward (point-max))
   1362             fun)) ; Return non-nil (the var name).
   1363         ;; Are we in a let-like form?
   1364         (when (member (thing-at-point 'symbol)
   1365                       clojure-align-binding-forms)
   1366           ;; Position inside the binding vector.
   1367           (clojure-forward-logical-sexp)
   1368           (backward-sexp)
   1369           (when (eq (char-after) ?\[)
   1370             (forward-char 1)
   1371             (comment-forward (point-max))
   1372             ;; Return non-nil.
   1373             t)))))
   1374 
   1375 (defun clojure--find-sexp-to-align (end)
   1376   "Non-nil if there's a sexp ahead to be aligned before END.
   1377 Place point as in `clojure--position-for-alignment'."
   1378   ;; Look for a relevant sexp.
   1379   (let ((found))
   1380     (while (and (not found)
   1381                 (search-forward-regexp
   1382                  (concat (when clojure-align-reader-conditionals
   1383                            (concat clojure--beginning-of-reader-conditional-regexp
   1384                                    "\\|"))
   1385                          "{\\|("
   1386                          (regexp-opt
   1387                           (append clojure-align-binding-forms
   1388                                   clojure-align-cond-forms)
   1389                           'symbols))
   1390                  end 'noerror))
   1391 
   1392       (let ((ppss (syntax-ppss)))
   1393         ;; If we're in a string or comment.
   1394         (unless (or (elt ppss 3)
   1395                     (elt ppss 4))
   1396           ;; Only stop looking if we successfully position
   1397           ;; the point.
   1398           (setq found (clojure--position-for-alignment)))))
   1399     found))
   1400 
   1401 (defun clojure--search-whitespace-after-next-sexp (&optional bound _noerror)
   1402   "Move point after all whitespace after the next sexp.
   1403 Additionally, move past a comment if one exists (this is only
   1404 possible when the end of the sexp coincides with the end of a
   1405 line).
   1406 
   1407 Set the match data group 1 to be this region of whitespace and
   1408 return point.
   1409 
   1410 BOUND is bounds the whitespace search."
   1411   (unwind-protect
   1412       (ignore-errors
   1413         (clojure-forward-logical-sexp 1)
   1414         ;; Move past any whitespace or comment.
   1415         (search-forward-regexp "\\([,\s\t]*\\)\\(;+.*\\)?" bound)
   1416         (pcase (syntax-after (point))
   1417           ;; End-of-line, try again on next line.
   1418           (`(12) (clojure--search-whitespace-after-next-sexp bound))
   1419           ;; Closing paren, stop here.
   1420           (`(5 . ,_) nil)
   1421           ;; Anything else is something to align.
   1422           (_ (point))))
   1423     (when (and bound (> (point) bound))
   1424       (goto-char bound))))
   1425 
   1426 (defun clojure-align (beg end)
   1427   "Vertically align the contents of the sexp around point.
   1428 If region is active, align it.  Otherwise, align everything in the
   1429 current \"top-level\" sexp.
   1430 When called from lisp code align everything between BEG and END."
   1431   (interactive (if (use-region-p)
   1432                    (list (region-beginning) (region-end))
   1433                  (save-excursion
   1434                    (let ((end (progn (end-of-defun)
   1435                                      (point))))
   1436                      (clojure-backward-logical-sexp)
   1437                      (list (point) end)))))
   1438   (setq end (copy-marker end))
   1439   (save-excursion
   1440     (goto-char beg)
   1441     (while (clojure--find-sexp-to-align end)
   1442       (let ((sexp-end (save-excursion
   1443                         (backward-up-list)
   1444                         (forward-sexp 1)
   1445                         (point-marker)))
   1446             (clojure-align-forms-automatically nil)
   1447             (count 1))
   1448         ;; For some bizarre reason, we need to `align-region' once for each
   1449         ;; group.
   1450         (save-excursion
   1451           (while (search-forward-regexp "^ *\n" sexp-end 'noerror)
   1452             (cl-incf count)))
   1453         ;; Pre-indent the region to avoid aligning to improperly indented
   1454         ;; contents (#551). Also fixes #360.
   1455         (indent-region (point) sexp-end)
   1456         (dotimes (_ count)
   1457           (align-region (point) sexp-end nil
   1458                         `((clojure-align (regexp . clojure--search-whitespace-after-next-sexp)
   1459                                          (group . 1)
   1460                                          (separate . ,clojure-align-separator)
   1461                                          (repeat . t)))
   1462                         nil))))))
   1463 
   1464 ;;; Indentation
   1465 (defun clojure-indent-region (beg end)
   1466   "Like `indent-region', but also maybe align forms.
   1467 Forms between BEG and END are aligned according to
   1468 `clojure-align-forms-automatically'."
   1469   (prog1 (let ((indent-region-function nil))
   1470            (indent-region beg end))
   1471     (when clojure-align-forms-automatically
   1472       (condition-case nil
   1473           (clojure-align beg end)
   1474         (scan-error nil)))))
   1475 
   1476 (defun clojure-indent-line ()
   1477   "Indent current line as Clojure code."
   1478   (if (clojure-in-docstring-p)
   1479       (save-excursion
   1480         (beginning-of-line)
   1481         (when (and (looking-at "^\\s-*")
   1482                    (<= (string-width (match-string-no-properties 0))
   1483                        (string-width (clojure-docstring-fill-prefix))))
   1484           (replace-match (clojure-docstring-fill-prefix))))
   1485     (lisp-indent-line)))
   1486 
   1487 (defvar clojure-get-indent-function nil
   1488   "Function to get the indent spec of a symbol.
   1489 This function should take one argument, the name of the symbol as
   1490 a string.  This name will be exactly as it appears in the buffer,
   1491 so it might start with a namespace alias.
   1492 
   1493 This function is analogous to the `clojure-indent-function'
   1494 symbol property, and its return value should match one of the
   1495 allowed values of this property.  See `clojure-indent-function'
   1496 for more information.")
   1497 
   1498 (defun clojure--get-indent-method (function-name)
   1499   "Return the indent spec for the symbol named FUNCTION-NAME.
   1500 FUNCTION-NAME is a string.  If it contains a `/', also try only
   1501 the part after the `/'.
   1502 
   1503 Look for a spec using `clojure-get-indent-function', then try the
   1504 `clojure-indent-function' and `clojure-backtracking-indent'
   1505 symbol properties."
   1506   (or (when (functionp clojure-get-indent-function)
   1507         (funcall clojure-get-indent-function function-name))
   1508       (get (intern-soft function-name) 'clojure-indent-function)
   1509       (get (intern-soft function-name) 'clojure-backtracking-indent)
   1510       (when (string-match "/\\([^/]+\\)\\'" function-name)
   1511         (or (get (intern-soft (match-string 1 function-name))
   1512                  'clojure-indent-function)
   1513             (get (intern-soft (match-string 1 function-name))
   1514                  'clojure-backtracking-indent)))
   1515       ;; indent symbols starting with if, when, ...
   1516       ;; such as if-let, when-let, ...
   1517       ;; like if, when, ...
   1518       (when (string-match (rx string-start (or "if" "when" "let" "while") (syntax symbol))
   1519                           function-name)
   1520         (clojure--get-indent-method (substring (match-string 0 function-name) 0 -1)))))
   1521 
   1522 (defvar clojure--current-backtracking-depth 0)
   1523 
   1524 (defun clojure--find-indent-spec-backtracking ()
   1525   "Return the indent sexp that applies to the sexp at point.
   1526 Implementation function for `clojure--find-indent-spec'."
   1527   (when (and (>= clojure-max-backtracking clojure--current-backtracking-depth)
   1528              (not (looking-at "^")))
   1529     (let ((clojure--current-backtracking-depth (1+ clojure--current-backtracking-depth))
   1530           (pos 0))
   1531       ;; Count how far we are from the start of the sexp.
   1532       (while (ignore-errors (clojure-backward-logical-sexp 1)
   1533                             (not (or (bobp)
   1534                                      (eq (char-before) ?\n))))
   1535         (cl-incf pos))
   1536       (let* ((function (thing-at-point 'symbol))
   1537              (method (or (when function ;; Is there a spec here?
   1538                            (clojure--get-indent-method function))
   1539                          (ignore-errors
   1540                            ;; Otherwise look higher up.
   1541                            (pcase (syntax-ppss)
   1542                              (`(,(pred (< 0)) ,start . ,_)
   1543                               (goto-char start)
   1544                               (clojure--find-indent-spec-backtracking)))))))
   1545         (when (numberp method)
   1546           (setq method (list method)))
   1547         (pcase method
   1548           ((pred functionp)
   1549            (when (= pos 0)
   1550              method))
   1551           ((pred sequencep)
   1552            (pcase (length method)
   1553              (`0 nil)
   1554              (`1 (let ((head (elt method 0)))
   1555                    (when (or (= pos 0) (sequencep head))
   1556                      head)))
   1557              (l (if (>= pos l)
   1558                     (elt method (1- l))
   1559                   (elt method pos)))))
   1560           ((or `defun `:defn)
   1561            (when (= pos 0)
   1562              :defn))
   1563           (_
   1564            (message "Invalid indent spec for `%s': %s" function method)
   1565            nil))))))
   1566 
   1567 (defun clojure--find-indent-spec ()
   1568   "Return the indent spec that applies to current sexp.
   1569 If `clojure-use-backtracking-indent' is non-nil, also do
   1570 backtracking up to a higher-level sexp in order to find the
   1571 spec."
   1572   (if clojure-use-backtracking-indent
   1573       (save-excursion
   1574         (clojure--find-indent-spec-backtracking))
   1575     (let ((function (thing-at-point 'symbol)))
   1576       (clojure--get-indent-method function))))
   1577 
   1578 (defun clojure--keyword-to-symbol (keyword)
   1579   "Convert KEYWORD to symbol."
   1580   (intern (substring (symbol-name keyword) 1)))
   1581 
   1582 (defun clojure--normal-indent (last-sexp indent-mode)
   1583   "Return the normal indentation column for a sexp.
   1584 Point should be after the open paren of the _enclosing_ sexp, and
   1585 LAST-SEXP is the start of the previous sexp (immediately before
   1586 the sexp being indented).  INDENT-MODE is any of the values
   1587 accepted by `clojure-indent-style'."
   1588   (goto-char last-sexp)
   1589   (forward-sexp 1)
   1590   (clojure-backward-logical-sexp 1)
   1591   (let ((last-sexp-start nil))
   1592     (if (ignore-errors
   1593           ;; `backward-sexp' until we reach the start of a sexp that is the
   1594           ;; first of its line (the start of the enclosing sexp).
   1595           (while (string-match
   1596                   "[^[:blank:]]"
   1597                   (buffer-substring (line-beginning-position) (point)))
   1598             (setq last-sexp-start (prog1 (point)
   1599                                     (forward-sexp -1))))
   1600           t)
   1601         ;; Here we have found an arg before the arg we're indenting which is at
   1602         ;; the start of a line. Every mode simply aligns on this case.
   1603         (current-column)
   1604       ;; Here we have reached the start of the enclosing sexp (point is now at
   1605       ;; the function name), so the behaviour depends on INDENT-MODE and on
   1606       ;; whether there's also an argument on this line (case A or B).
   1607       (let ((indent-mode (if (keywordp indent-mode)
   1608                              ;; needed for backwards compatibility
   1609                              ;; as before clojure-mode 5.10 indent-mode was a keyword
   1610                              (clojure--keyword-to-symbol indent-mode)
   1611                            indent-mode))
   1612             (case-a ; The meaning of case-a is explained in `clojure-indent-style'.
   1613              (and last-sexp-start
   1614                   (< last-sexp-start (line-end-position)))))
   1615         (cond
   1616          ((eq indent-mode 'always-indent)
   1617           (+ (current-column) lisp-body-indent -1))
   1618          ;; There's an arg after the function name, so align with it.
   1619          (case-a (goto-char last-sexp-start)
   1620                  (current-column))
   1621          ;; Not same line.
   1622          ((eq indent-mode 'align-arguments)
   1623           (+ (current-column) lisp-body-indent -1))
   1624          ;; Finally, just align with the function name.
   1625          (t (current-column)))))))
   1626 
   1627 (defun clojure--not-function-form-p ()
   1628   "Non-nil if form at point doesn't represent a function call."
   1629   (or (member (char-after) '(?\[ ?\{))
   1630       (save-excursion ;; Catch #?@ (:cljs ...)
   1631         (skip-chars-backward "\r\n[:blank:]")
   1632         (when (eq (char-before) ?@)
   1633           (forward-char -1))
   1634         (and (eq (char-before) ?\?)
   1635              (eq (char-before (1- (point))) ?\#)))
   1636       ;; Car of form is not a symbol.
   1637       (not (looking-at ".\\(?:\\sw\\|\\s_\\)"))))
   1638 
   1639 ;; Check the general context, and provide indentation for data structures and
   1640 ;; special macros. If current form is a function (or non-special macro),
   1641 ;; delegate indentation to `clojure--normal-indent'.
   1642 (defun clojure-indent-function (indent-point state)
   1643   "When indenting a line within a function call, indent properly.
   1644 
   1645 INDENT-POINT is the position where the user typed TAB, or equivalent.
   1646 Point is located at the point to indent under (for default indentation);
   1647 STATE is the `parse-partial-sexp' state for that position.
   1648 
   1649 If the current line is in a call to a Clojure function with a
   1650 non-nil property `clojure-indent-function', that specifies how to do
   1651 the indentation.
   1652 
   1653 The property value can be
   1654 
   1655 - `:defn', meaning indent `defn'-style;
   1656 - an integer N, meaning indent the first N arguments specially
   1657   like ordinary function arguments and then indent any further
   1658   arguments like a body;
   1659 - a function to call just as this function was called.
   1660   If that function returns nil, that means it doesn't specify
   1661   the indentation.
   1662 - a list, which is used by `clojure-backtracking-indent'.
   1663 
   1664 This function also returns nil meaning don't specify the indentation."
   1665   ;; Goto to the open-paren.
   1666   (goto-char (elt state 1))
   1667   ;; Maps, sets, vectors and reader conditionals.
   1668   (if (clojure--not-function-form-p)
   1669       (1+ (current-column))
   1670     ;; Function or macro call.
   1671     (forward-char 1)
   1672     (let ((method (clojure--find-indent-spec))
   1673           (last-sexp calculate-lisp-indent-last-sexp)
   1674           (containing-form-column (1- (current-column))))
   1675       (pcase method
   1676         ((or (and (pred integerp) method) `(,method))
   1677          (let ((pos -1))
   1678            (condition-case nil
   1679                (while (and (<= (point) indent-point)
   1680                            (not (eobp)))
   1681                  (clojure-forward-logical-sexp 1)
   1682                  (cl-incf pos))
   1683              ;; If indent-point is _after_ the last sexp in the
   1684              ;; current sexp, we detect that by catching the
   1685              ;; `scan-error'. In that case, we should return the
   1686              ;; indentation as if there were an extra sexp at point.
   1687              (scan-error (cl-incf pos)))
   1688            (cond
   1689             ;; The first non-special arg. Rigidly reduce indentation.
   1690             ((= pos (1+ method))
   1691              (+ lisp-body-indent containing-form-column))
   1692             ;; Further non-special args, align with the arg above.
   1693             ((> pos (1+ method))
   1694              (clojure--normal-indent last-sexp 'always-align))
   1695             ;; Special arg. Rigidly indent with a large indentation.
   1696             (t
   1697              (+ (* clojure-special-arg-indent-factor lisp-body-indent)
   1698                 containing-form-column)))))
   1699         (`:defn
   1700          (+ lisp-body-indent containing-form-column))
   1701         ((pred functionp)
   1702          (funcall method indent-point state))
   1703         ;; No indent spec, do the default.
   1704         (`nil
   1705          (let ((function (thing-at-point 'symbol)))
   1706            (cond
   1707             ;; Preserve useful alignment of :require (and friends) in `ns' forms.
   1708             ((and function (string-match "^:" function))
   1709              (clojure--normal-indent last-sexp 'always-align))
   1710             ;; This should be identical to the :defn above.
   1711             ((and function
   1712                   (string-match "\\`\\(?:\\S +/\\)?\\(def[a-z]*\\|with-\\)"
   1713                                 function)
   1714                   (not (string-match "\\`default" (match-string 1 function))))
   1715              (+ lisp-body-indent containing-form-column))
   1716             ;; Finally, nothing special here, just respect the user's
   1717             ;; preference.
   1718             (t (clojure--normal-indent last-sexp clojure-indent-style)))))))))
   1719 
   1720 ;;; Setting indentation
   1721 (defun put-clojure-indent (sym indent)
   1722   "Instruct `clojure-indent-function' to indent the body of SYM by INDENT."
   1723   (put sym 'clojure-indent-function indent))
   1724 
   1725 (defun clojure--maybe-quoted-symbol-p (x)
   1726   "Check that X is either a symbol or a quoted symbol like :foo or \\='foo."
   1727   (or (symbolp x)
   1728       (and (listp x)
   1729            (= 2 (length x))
   1730            (eq 'quote (car x))
   1731            (symbolp (cadr x)))))
   1732 
   1733 (defun clojure--valid-unquoted-indent-spec-p (spec)
   1734   "Check that the indentation SPEC is valid.
   1735 Validate it with respect to
   1736 https://docs.cider.mx/cider/indent_spec.html e.g. (2 :form
   1737 :form (1)))."
   1738   (or (integerp spec)
   1739       (memq spec '(:form :defn))
   1740       (and (listp spec)
   1741            (not (null spec))
   1742            (or (integerp (car spec))
   1743                (memq (car spec) '(:form :defn)))
   1744            (cl-every 'clojure--valid-unquoted-indent-spec-p (cdr spec)))))
   1745 
   1746 (defun clojure--valid-indent-spec-p (spec)
   1747   "Check that the indentation SPEC (quoted if a list) is valid.
   1748 Validate it with respect to
   1749 https://docs.cider.mx/cider/indent_spec.html e.g. (2 :form
   1750 :form (1)))."
   1751   (or (integerp spec)
   1752       (and (keywordp spec) (memq spec '(:form :defn)))
   1753       (and (listp spec)
   1754            (= 2 (length spec))
   1755            (eq 'quote (car spec))
   1756            (clojure--valid-unquoted-indent-spec-p (cadr spec)))))
   1757 
   1758 (defun clojure--valid-put-clojure-indent-call-p (exp)
   1759   "Check that EXP is a valid `put-clojure-indent' expression.
   1760 For example: (put-clojure-indent \\='defrecord \\='(2 :form :form (1))."
   1761   (unless (and (listp exp)
   1762                (= 3 (length exp))
   1763                (eq 'put-clojure-indent (nth 0 exp))
   1764                (clojure--maybe-quoted-symbol-p (nth 1 exp))
   1765                (clojure--valid-indent-spec-p (nth 2 exp)))
   1766     (error "Unrecognized put-clojure-indent call: %s" exp))
   1767   t)
   1768 
   1769 (put 'put-clojure-indent 'safe-local-eval-function
   1770      'clojure--valid-put-clojure-indent-call-p)
   1771 
   1772 (defmacro define-clojure-indent (&rest kvs)
   1773   "Call `put-clojure-indent' on a series, KVS."
   1774   `(progn
   1775      ,@(mapcar (lambda (x) `(put-clojure-indent
   1776                              (quote ,(car x)) ,(cadr x)))
   1777                kvs)))
   1778 
   1779 (defun add-custom-clojure-indents (name value)
   1780   "Allow `clojure-defun-indents' to indent user-specified macros.
   1781 
   1782 Requires the macro's NAME and a VALUE."
   1783   (custom-set-default name value)
   1784   (mapcar (lambda (x)
   1785             (put-clojure-indent x 'defun))
   1786           value))
   1787 
   1788 (defcustom clojure-defun-indents nil
   1789   "List of additional symbols with defun-style indentation in Clojure.
   1790 
   1791 You can use this to let Emacs indent your own macros the same way
   1792 that it indents built-in macros like with-open.  This variable
   1793 only works when set via the customize interface (`setq' won't
   1794 work).  To set it from Lisp code, use
   1795      (put-clojure-indent \\='some-symbol :defn)."
   1796   :type '(repeat symbol)
   1797   :set 'add-custom-clojure-indents)
   1798 
   1799 (define-clojure-indent
   1800   ;; built-ins
   1801   (ns 1)
   1802   (fn :defn)
   1803   (def :defn)
   1804   (defn :defn)
   1805   (bound-fn :defn)
   1806   (if 1)
   1807   (if-not 1)
   1808   (case 1)
   1809   (cond 0)
   1810   (condp 2)
   1811   (cond-> 1)
   1812   (cond->> 1)
   1813   (when 1)
   1814   (while 1)
   1815   (when-not 1)
   1816   (when-first 1)
   1817   (do 0)
   1818   (delay 0)
   1819   (future 0)
   1820   (comment 0)
   1821   (doto 1)
   1822   (locking 1)
   1823   (proxy '(2 nil nil (:defn)))
   1824   (as-> 2)
   1825   (fdef 1)
   1826 
   1827   (reify '(:defn (1)))
   1828   (deftype '(2 nil nil (:defn)))
   1829   (defrecord '(2 nil nil (:defn)))
   1830   (defprotocol '(1 (:defn)))
   1831   (definterface '(1 (:defn)))
   1832   (extend 1)
   1833   (extend-protocol '(1 :defn))
   1834   (extend-type '(1 :defn))
   1835   ;; specify and specify! are from ClojureScript
   1836   (specify '(1 :defn))
   1837   (specify! '(1 :defn))
   1838   (try 0)
   1839   (catch 2)
   1840   (finally 0)
   1841 
   1842   ;; binding forms
   1843   (let 1)
   1844   (letfn '(1 ((:defn)) nil))
   1845   (binding 1)
   1846   (loop 1)
   1847   (for 1)
   1848   (doseq 1)
   1849   (dotimes 1)
   1850   (when-let 1)
   1851   (if-let 1)
   1852   (when-some 1)
   1853   (if-some 1)
   1854   (this-as 1) ; ClojureScript
   1855 
   1856   (defmethod :defn)
   1857 
   1858   ;; clojure.test
   1859   (testing 1)
   1860   (deftest :defn)
   1861   (are 2)
   1862   (use-fixtures :defn)
   1863 
   1864   ;; core.logic
   1865   (run :defn)
   1866   (run* :defn)
   1867   (fresh :defn)
   1868 
   1869   ;; core.async
   1870   (alt! 0)
   1871   (alt!! 0)
   1872   (go 0)
   1873   (go-loop 1)
   1874   (thread 0))
   1875 
   1876 
   1877 
   1878 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1879 ;;
   1880 ;; Better docstring filling for clojure-mode
   1881 ;;
   1882 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1883 
   1884 (defun clojure-string-start (&optional regex)
   1885   "Return the position of the \" that begins the string at point.
   1886 If REGEX is non-nil, return the position of the # that begins the
   1887 regex at point.  If point is not inside a string or regex, return
   1888 nil."
   1889   (when (nth 3 (syntax-ppss)) ;; Are we really in a string?
   1890     (let* ((beg (nth 8 (syntax-ppss)))
   1891            (hash (eq ?# (char-before beg))))
   1892       (if regex
   1893           (and hash (1- beg))
   1894         (and (not hash) beg)))))
   1895 
   1896 (defun clojure-char-at-point ()
   1897   "Return the char at point or nil if at buffer end."
   1898   (when (not (= (point) (point-max)))
   1899     (buffer-substring-no-properties (point) (1+ (point)))))
   1900 
   1901 (defun clojure-char-before-point ()
   1902   "Return the char before point or nil if at buffer beginning."
   1903   (when (not (= (point) (point-min)))
   1904     (buffer-substring-no-properties (point) (1- (point)))))
   1905 
   1906 (defun clojure-toggle-keyword-string ()
   1907   "Convert the string or keyword at point to keyword or string."
   1908   (interactive)
   1909   (let ((original-point (point)))
   1910     (while (and (> (point) 1)
   1911                 (not (equal "\"" (buffer-substring-no-properties (point) (+ 1 (point)))))
   1912                 (not (equal ":" (buffer-substring-no-properties (point) (+ 1 (point))))))
   1913       (backward-char))
   1914     (cond
   1915      ((equal 1 (point))
   1916       (error "Beginning of file reached, this was probably a mistake"))
   1917      ((equal "\"" (buffer-substring-no-properties (point) (+ 1 (point))))
   1918       (insert ":" (substring (clojure-delete-and-extract-sexp) 1 -1)))
   1919      ((equal ":" (buffer-substring-no-properties (point) (+ 1 (point))))
   1920       (insert "\"" (substring (clojure-delete-and-extract-sexp) 1) "\"")))
   1921     (goto-char original-point)))
   1922 
   1923 (defun clojure-delete-and-extract-sexp ()
   1924   "Delete the surrounding sexp and return it."
   1925   (let ((begin (point)))
   1926     (forward-sexp)
   1927     (let ((result (buffer-substring begin (point))))
   1928       (delete-region begin (point))
   1929       result)))
   1930 
   1931 
   1932 
   1933 (defcustom clojure-cache-project-dir t
   1934   "Whether to cache the results of `clojure-project-dir'."
   1935   :type 'boolean
   1936   :safe #'booleanp
   1937   :package-version '(clojure-mode . "5.8.0"))
   1938 
   1939 (defvar-local clojure-cached-project-dir nil
   1940   "A project dir cache used to speed up related operations.")
   1941 
   1942 (defun clojure-project-dir (&optional dir-name)
   1943   "Return the absolute path to the project's root directory.
   1944 
   1945 Call is delegated down to `clojure-project-root-function' with
   1946 optional DIR-NAME as argument.
   1947 
   1948 When `clojure-cache-project-dir' is t the results of the command
   1949 are cached in a buffer local variable (`clojure-cached-project-dir')."
   1950   (let ((project-dir (or clojure-cached-project-dir
   1951                          (funcall clojure-project-root-function dir-name))))
   1952     (when (and clojure-cache-project-dir
   1953                (derived-mode-p 'clojure-mode)
   1954                (not clojure-cached-project-dir))
   1955       (setq clojure-cached-project-dir project-dir))
   1956     project-dir))
   1957 
   1958 (defun clojure-project-root-path (&optional dir-name)
   1959   "Return the absolute path to the project's root directory.
   1960 
   1961 Use `default-directory' if DIR-NAME is nil.
   1962 Return nil if not inside a project."
   1963   (let* ((dir-name (or dir-name default-directory))
   1964          (choices (delq nil
   1965                         (mapcar (lambda (fname)
   1966                                   (locate-dominating-file dir-name fname))
   1967                                 clojure-build-tool-files))))
   1968     (when (> (length choices) 0)
   1969       (car (sort choices #'file-in-directory-p)))))
   1970 
   1971 (defun clojure-project-relative-path (path)
   1972   "Denormalize PATH by making it relative to the project root."
   1973   (file-relative-name path (clojure-project-dir)))
   1974 
   1975 
   1976 ;;; ns manipulation
   1977 (defun clojure-expected-ns (&optional path)
   1978   "Return the namespace matching PATH.
   1979 
   1980 PATH is expected to be an absolute file path.
   1981 
   1982 If PATH is nil, use the path to the file backing the current buffer."
   1983   (let* ((path (or path (file-truename (buffer-file-name))))
   1984          (relative (clojure-project-relative-path path))
   1985          (sans-file-type (substring relative 0 (- (length (file-name-extension path t)))))
   1986          (sans-file-sep (mapconcat 'identity (cdr (split-string sans-file-type "/")) "."))
   1987          (sans-underscores (replace-regexp-in-string "_" "-" sans-file-sep)))
   1988     ;; Drop prefix from ns for projects with structure src/{clj,cljs,cljc}
   1989     (cl-reduce (lambda (a x) (replace-regexp-in-string x "" a))
   1990                clojure-directory-prefixes
   1991                :initial-value sans-underscores)))
   1992 
   1993 (defun clojure-insert-ns-form-at-point ()
   1994   "Insert a namespace form at point."
   1995   (interactive)
   1996   (insert (format "(ns %s)" (funcall clojure-expected-ns-function))))
   1997 
   1998 (defun clojure-insert-ns-form ()
   1999   "Insert a namespace form at the beginning of the buffer."
   2000   (interactive)
   2001   (widen)
   2002   (goto-char (point-min))
   2003   (clojure-insert-ns-form-at-point))
   2004 
   2005 (defvar-local clojure-cached-ns nil
   2006   "A buffer ns cache used to speed up ns-related operations.")
   2007 
   2008 (defun clojure-update-ns ()
   2009   "Update the namespace of the current buffer.
   2010 Useful if a file has been renamed."
   2011   (interactive)
   2012   (let ((nsname (funcall clojure-expected-ns-function)))
   2013     (when nsname
   2014       (save-excursion
   2015         (save-match-data
   2016           (if (clojure-find-ns)
   2017               (progn
   2018                 (replace-match nsname nil nil nil 4)
   2019                 (message "ns form updated to `%s'" nsname)
   2020                 (setq clojure-cached-ns nsname))
   2021             (user-error "Can't find ns form")))))))
   2022 
   2023 (defun clojure--sort-following-sexps ()
   2024   "Sort sexps between point and end of current sexp.
   2025 Comments at the start of a line are considered part of the
   2026 following sexp.  Comments at the end of a line (after some other
   2027 content) are considered part of the preceding sexp."
   2028   ;; Here we're after the :require/:import symbol.
   2029   (save-restriction
   2030     (narrow-to-region (point) (save-excursion
   2031                                 (up-list)
   2032                                 (1- (point))))
   2033     (skip-chars-forward "\r\n[:blank:]")
   2034     (sort-subr nil
   2035                (lambda () (skip-chars-forward "\r\n[:blank:]"))
   2036                ;; Move to end of current top-level thing.
   2037                (lambda ()
   2038                  (condition-case nil
   2039                      (while t (up-list))
   2040                    (scan-error nil))
   2041                  ;; We could be inside a symbol instead of a sexp.
   2042                  (unless (looking-at "\\s-\\|$")
   2043                    (clojure-forward-logical-sexp))
   2044                  ;; move past comments at the end of the line.
   2045                  (search-forward-regexp "$"))
   2046                ;; Move to start of ns name.
   2047                (lambda ()
   2048                  (comment-forward)
   2049                  (skip-chars-forward "[:blank:]\n\r[(")
   2050                  (clojure-forward-logical-sexp)
   2051                  (forward-sexp -1)
   2052                  nil)
   2053                ;; Move to end of ns name.
   2054                (lambda ()
   2055                  (clojure-forward-logical-sexp)))
   2056     (goto-char (point-max))
   2057     ;; Does the last line now end in a comment?
   2058     (when (nth 4 (parse-partial-sexp (point-min) (point)))
   2059       (insert "\n"))))
   2060 
   2061 (defun clojure-sort-ns ()
   2062   "Internally sort each sexp inside the ns form."
   2063   (interactive)
   2064   (comment-normalize-vars)
   2065   (if (clojure-find-ns)
   2066       (save-excursion
   2067         (goto-char (match-beginning 0))
   2068         (let ((beg (point))
   2069               (ns))
   2070           (forward-sexp 1)
   2071           (setq ns (buffer-substring beg (point)))
   2072           (forward-char -1)
   2073           (while (progn (forward-sexp -1)
   2074                         (looking-at "(:[a-z]"))
   2075             (save-excursion
   2076               (forward-char 1)
   2077               (forward-sexp 1)
   2078               (clojure--sort-following-sexps)))
   2079           (goto-char beg)
   2080           (if (looking-at (regexp-quote ns))
   2081               (message "ns form is already sorted")
   2082             (message "ns form has been sorted"))))
   2083     (user-error "Can't find ns form")))
   2084 
   2085 (defconst clojure-namespace-name-regex
   2086   (rx line-start
   2087       "("
   2088       (zero-or-one (group (regexp "clojure.core/")))
   2089       (zero-or-one (submatch "in-"))
   2090       "ns"
   2091       (zero-or-one "+")
   2092       (one-or-more (any whitespace "\n"))
   2093       (zero-or-more (or (submatch (zero-or-one "#")
   2094                                   "^{"
   2095                                   (zero-or-more (not (any "}")))
   2096                                   "}")
   2097                         (zero-or-more "^:"
   2098                                       (one-or-more (not (any whitespace)))))
   2099                     (one-or-more (any whitespace "\n")))
   2100       (zero-or-one (any ":'")) ;; (in-ns 'foo) or (ns+ :user)
   2101       (group (one-or-more (not (any "()\"" whitespace))) symbol-end)))
   2102 
   2103 (make-obsolete-variable 'clojure-namespace-name-regex 'clojure-namespace-regexp "5.12.0")
   2104 
   2105 (defconst clojure-namespace-regexp
   2106   (rx line-start "(" (? "clojure.core/") (or "in-ns" "ns" "ns+") symbol-end))
   2107 
   2108 (defcustom clojure-cache-ns nil
   2109   "Whether to cache the results of `clojure-find-ns'.
   2110 
   2111 Note that this won't work well in buffers with multiple namespace
   2112 declarations (which rarely occur in practice) and you'll
   2113 have to invalidate this manually after changing the ns for
   2114 a buffer.  If you update the ns using `clojure-update-ns'
   2115 the cached value will be updated automatically."
   2116   :type 'boolean
   2117   :safe #'booleanp
   2118   :package-version '(clojure-mode . "5.8.0"))
   2119 
   2120 (defun clojure--find-ns-in-direction (direction)
   2121   "Return the nearest namespace in a specific DIRECTION.
   2122 DIRECTION is `forward' or `backward'."
   2123   (let ((candidate)
   2124         (fn (if (eq direction 'forward)
   2125                 #'search-forward-regexp
   2126               #'search-backward-regexp)))
   2127     (while (and (not candidate)
   2128                 (funcall fn clojure-namespace-regexp nil t))
   2129       (let ((end (match-end 0)))
   2130         (save-excursion
   2131           (save-match-data
   2132             (goto-char end)
   2133             (clojure-forward-logical-sexp)
   2134             (unless (or (clojure--in-string-p) (clojure--in-comment-p))
   2135               (setq candidate (string-remove-prefix "'" (thing-at-point 'symbol))))))))
   2136     candidate))
   2137 
   2138 (defun clojure-find-ns ()
   2139   "Return the namespace of the current Clojure buffer.
   2140 Return the namespace closest to point and above it.  If there are
   2141 no namespaces above point, return the first one in the buffer.
   2142 
   2143 The results will be cached if `clojure-cache-ns' is set to t."
   2144   (if (and clojure-cache-ns clojure-cached-ns)
   2145       clojure-cached-ns
   2146     (let ((ns (save-excursion
   2147                 (save-restriction
   2148                   (widen)
   2149 
   2150                   ;; Move to top-level to avoid searching from inside ns
   2151                   (ignore-errors (while t (up-list nil t t)))
   2152 
   2153                   (or (clojure--find-ns-in-direction 'backward)
   2154                       (clojure--find-ns-in-direction 'forward))))))
   2155       (setq clojure-cached-ns ns)
   2156       ns)))
   2157 
   2158 (defun clojure-show-cache ()
   2159   "Display cached values if present.
   2160 Useful for debugging."
   2161   (interactive)
   2162   (message "Cached Project: %s, Cached Namespace: %s" clojure-cached-project-dir clojure-cached-ns))
   2163 
   2164 (defun clojure-clear-cache ()
   2165   "Clear all buffer-local cached values.
   2166 
   2167 Normally you'd need to do this very infrequently - e.g.
   2168 after renaming the root folder of project or after
   2169 renaming a namespace."
   2170   (interactive)
   2171   (setq clojure-cached-project-dir nil
   2172         clojure-cached-ns nil)
   2173   (message "Buffer-local clojure-mode cache cleared"))
   2174 
   2175 (defconst clojure-def-type-and-name-regex
   2176   (concat "(\\(?:\\(?:\\sw\\|\\s_\\)+/\\)?"
   2177           ;; Declaration
   2178           "\\(def\\(?:\\sw\\|\\s_\\)*\\)\\>"
   2179           ;; Any whitespace
   2180           "[ \r\n\t]*"
   2181           ;; Possibly type or metadata
   2182           "\\(?:#?^\\(?:{[^}]*}\\|\\(?:\\sw\\|\\s_\\)+\\)[ \r\n\t]*\\)*"
   2183           ;; Symbol name
   2184           "\\(\\(?:\\sw\\|\\s_\\)+\\)"))
   2185 
   2186 (defun clojure-find-def ()
   2187   "Find the var declaration macro and symbol name of the current form.
   2188 Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")."
   2189   (save-excursion
   2190     (unless (looking-at clojure-def-type-and-name-regex)
   2191       (beginning-of-defun))
   2192     (when (search-forward-regexp clojure-def-type-and-name-regex nil t)
   2193       (list (match-string-no-properties 1)
   2194             (match-string-no-properties 2)))))
   2195 
   2196 
   2197 ;;; Sexp navigation
   2198 
   2199 (defun clojure--looking-at-non-logical-sexp ()
   2200   "Return non-nil if text after point is \"non-logical\" sexp.
   2201 \"Non-logical\" sexp are ^metadata and #reader.macros."
   2202   (comment-normalize-vars)
   2203   (comment-forward (point-max))
   2204   (looking-at-p "\\(?:#?\\^\\)\\|#:?:?[[:alpha:]]"))
   2205 
   2206 (defun clojure-forward-logical-sexp (&optional n)
   2207   "Move forward N logical sexps.
   2208 This will skip over sexps that don't represent objects, so that ^hints and
   2209 #reader.macros are considered part of the following sexp."
   2210   (interactive "p")
   2211   (unless n (setq n 1))
   2212   (if (< n 0)
   2213       (clojure-backward-logical-sexp (- n))
   2214     (let ((forward-sexp-function nil))
   2215       (while (> n 0)
   2216         (while (clojure--looking-at-non-logical-sexp)
   2217           (forward-sexp 1))
   2218         ;; The actual sexp
   2219         (forward-sexp 1)
   2220         (skip-chars-forward ",")
   2221         (setq n (1- n))))))
   2222 
   2223 (defun clojure-backward-logical-sexp (&optional n)
   2224   "Move backward N logical sexps.
   2225 This will skip over sexps that don't represent objects, so that ^hints and
   2226 #reader.macros are considered part of the following sexp."
   2227   (interactive "p")
   2228   (unless n (setq n 1))
   2229   (if (< n 0)
   2230       (clojure-forward-logical-sexp (- n))
   2231     (let ((forward-sexp-function nil))
   2232       (while (> n 0)
   2233         ;; The actual sexp
   2234         (backward-sexp 1)
   2235         ;; Non-logical sexps.
   2236         (while (and (not (bobp))
   2237                     (ignore-errors
   2238                       (save-excursion
   2239                         (backward-sexp 1)
   2240                         (clojure--looking-at-non-logical-sexp))))
   2241           (backward-sexp 1))
   2242         (setq n (1- n))))))
   2243 
   2244 (defun clojure-top-level-form-p (first-form)
   2245   "Return truthy if the first form matches FIRST-FORM."
   2246   (condition-case nil
   2247       (save-excursion
   2248         (beginning-of-defun)
   2249         (forward-char 1)
   2250         (clojure-forward-logical-sexp 1)
   2251         (clojure-backward-logical-sexp 1)
   2252         (looking-at-p first-form))
   2253     (scan-error nil)
   2254     (end-of-buffer nil)))
   2255 
   2256 (defun clojure-sexp-starts-until-position (position)
   2257   "Return the starting points for forms before POSITION.
   2258 Positions are in descending order to aide in finding the first starting
   2259 position before the current position."
   2260   (save-excursion
   2261     (let (sexp-positions)
   2262       (condition-case nil
   2263           (while (< (point) position)
   2264             (clojure-forward-logical-sexp 1)
   2265             (clojure-backward-logical-sexp 1)
   2266             (push (point) sexp-positions)
   2267             (clojure-forward-logical-sexp 1))
   2268         (scan-error nil))
   2269       sexp-positions)))
   2270 
   2271 (defcustom clojure-toplevel-inside-comment-form nil
   2272   "Eval top level forms inside comment forms instead of the comment form itself.
   2273 Experimental.  Function `cider-defun-at-point' is used extensively so if we
   2274 change this heuristic it needs to be bullet-proof and desired.  While
   2275 testing, give an easy way to turn this new behavior off."
   2276   :type 'boolean
   2277   :safe #'booleanp
   2278   :package-version '(clojure-mode . "5.9.0"))
   2279 
   2280 (defun clojure-find-first (pred coll)
   2281   "Find first element of COLL for which PRED return truthy."
   2282   (let ((found)
   2283         (haystack coll))
   2284     (while (and (not found)
   2285                 haystack)
   2286       (if (funcall pred (car haystack))
   2287           (setq found (car haystack))
   2288         (setq haystack (cdr haystack))))
   2289     found))
   2290 
   2291 (defun clojure-beginning-of-defun-function (&optional n)
   2292   "Go to top level form.
   2293 Set as `beginning-of-defun-function' so that these generic
   2294 operators can be used.  Given a positive N it will do it that
   2295 many times."
   2296   (let ((beginning-of-defun-function nil))
   2297     (if (and clojure-toplevel-inside-comment-form
   2298              (clojure-top-level-form-p "comment"))
   2299         (condition-case nil
   2300             (save-match-data
   2301               (let ((original-position (point))
   2302                     clojure-comment-end)
   2303                 (beginning-of-defun)
   2304                 (end-of-defun)
   2305                 (setq clojure-comment-end (point))
   2306                 (beginning-of-defun)
   2307                 (forward-char 1)              ;; skip paren so we start at comment
   2308                 (clojure-forward-logical-sexp) ;; skip past the comment form itself
   2309                 (if-let ((sexp-start (clojure-find-first (lambda (beg-pos)
   2310                                                            (< beg-pos original-position))
   2311                                                          (clojure-sexp-starts-until-position
   2312                                                           clojure-comment-end))))
   2313                     (progn (goto-char sexp-start) t)
   2314                   (beginning-of-defun n))))
   2315           (scan-error (beginning-of-defun n)))
   2316       (beginning-of-defun n))))
   2317 
   2318 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   2319 ;;
   2320 ;; Refactoring support
   2321 ;;
   2322 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   2323 
   2324 ;;; Threading macros related
   2325 (defcustom clojure-thread-all-but-last nil
   2326   "Non-nil means do not thread the last expression.
   2327 This means that `clojure-thread-first-all' and
   2328 `clojure-thread-last-all' not thread the deepest sexp inside the
   2329 current sexp."
   2330   :package-version '(clojure-mode . "5.4.0")
   2331   :safe #'booleanp
   2332   :type 'boolean)
   2333 
   2334 (defun clojure--point-after (&rest actions)
   2335   "Return POINT after performing ACTIONS.
   2336 
   2337 An action is either the symbol of a function or a two element
   2338 list of (fn args) to pass to `apply''"
   2339   (save-excursion
   2340     (dolist (fn-and-args actions)
   2341       (let ((f (if (listp fn-and-args) (car fn-and-args) fn-and-args))
   2342             (args (if (listp fn-and-args) (cdr fn-and-args) nil)))
   2343         (apply f args)))
   2344     (point)))
   2345 
   2346 (defun clojure--maybe-unjoin-line ()
   2347   "Undo a `join-line' done by a threading command."
   2348   (when (get-text-property (point) 'clojure-thread-line-joined)
   2349     (remove-text-properties (point) (1+ (point)) '(clojure-thread-line-joined t))
   2350     (insert "\n")))
   2351 
   2352 (defun clojure--unwind-last ()
   2353   "Unwind a thread last macro once.
   2354 
   2355 Point must be between the opening paren and the ->> symbol."
   2356   (forward-sexp)
   2357   (save-excursion
   2358     (let ((contents (clojure-delete-and-extract-sexp)))
   2359       (when (looking-at " *\n")
   2360         (join-line 'following))
   2361       (clojure--ensure-parens-around-function-names)
   2362       (let* ((sexp-beg-line (line-number-at-pos))
   2363              (sexp-end-line (progn (forward-sexp)
   2364                                    (line-number-at-pos)))
   2365              (multiline-sexp-p (not (= sexp-beg-line sexp-end-line))))
   2366         (down-list -1)
   2367         (if multiline-sexp-p
   2368             (insert "\n")
   2369           ;; `clojure--maybe-unjoin-line' only works when unwinding sexps that were
   2370           ;; threaded in the same Emacs session, but it also catches cases that
   2371           ;; `multiline-sexp-p' doesn't.
   2372           (clojure--maybe-unjoin-line))
   2373         (insert contents))))
   2374   (forward-char))
   2375 
   2376 (defun clojure--ensure-parens-around-function-names ()
   2377   "Insert parens around function names if necessary."
   2378   (clojure--looking-at-non-logical-sexp)
   2379   (unless (looking-at "(")
   2380     (insert-parentheses 1)
   2381     (backward-up-list)))
   2382 
   2383 (defun clojure--unwind-first ()
   2384   "Unwind a thread first macro once.
   2385 
   2386 Point must be between the opening paren and the -> symbol."
   2387   (forward-sexp)
   2388   (save-excursion
   2389     (let ((contents (clojure-delete-and-extract-sexp)))
   2390       (when (looking-at " *\n")
   2391         (join-line 'following))
   2392       (clojure--ensure-parens-around-function-names)
   2393       (down-list)
   2394       (forward-sexp)
   2395       (insert contents)
   2396       (forward-sexp -1)
   2397       (clojure--maybe-unjoin-line)))
   2398   (forward-char))
   2399 
   2400 (defun clojure--pop-out-of-threading ()
   2401   "Raise a sexp up a level to unwind a threading form."
   2402   (save-excursion
   2403     (down-list 2)
   2404     (backward-up-list)
   2405     (raise-sexp)))
   2406 
   2407 (defun clojure--nothing-more-to-unwind ()
   2408   "Return non-nil if a threaded form cannot be unwound further."
   2409   (save-excursion
   2410     (let ((beg (point)))
   2411       (forward-sexp)
   2412       (down-list -1)
   2413       (backward-sexp 2) ;; the last sexp, the threading macro
   2414       (when (looking-back "(\\s-*" (line-beginning-position))
   2415         (backward-up-list)) ;; and the paren
   2416       (= beg (point)))))
   2417 
   2418 (defun clojure--fix-sexp-whitespace (&optional move-out)
   2419   "Fix whitespace after unwinding a threading form.
   2420 
   2421 Optional argument MOVE-OUT, if non-nil, means moves up a list
   2422 before fixing whitespace."
   2423   (save-excursion
   2424     (when move-out (backward-up-list))
   2425     (let ((sexp (bounds-of-thing-at-point 'sexp)))
   2426       (clojure-indent-region (car sexp) (cdr sexp))
   2427       (delete-trailing-whitespace (car sexp) (cdr sexp)))))
   2428 
   2429 ;;;###autoload
   2430 (defun clojure-unwind (&optional n)
   2431   "Unwind thread at point or above point by N levels.
   2432 With universal argument \\[universal-argument], fully unwind thread."
   2433   (interactive "P")
   2434   (setq n (cond ((equal n '(4)) 999)
   2435                 (n) (1)))
   2436   (save-excursion
   2437     (let ((limit (save-excursion
   2438                    (beginning-of-defun)
   2439                    (point))))
   2440       (ignore-errors
   2441         (when (looking-at "(")
   2442           (forward-char 1)
   2443           (forward-sexp 1)))
   2444       (while (> n 0)
   2445         (search-backward-regexp "([^-]*->" limit)
   2446         (if (clojure--nothing-more-to-unwind)
   2447             (progn (clojure--pop-out-of-threading)
   2448                    (clojure--fix-sexp-whitespace)
   2449                    (setq n 0)) ;; break out of loop
   2450           (down-list)
   2451           (cond
   2452            ((looking-at "[^-]*->\\_>")  (clojure--unwind-first))
   2453            ((looking-at "[^-]*->>\\_>") (clojure--unwind-last)))
   2454           (clojure--fix-sexp-whitespace 'move-out)
   2455           (setq n (1- n)))))))
   2456 
   2457 ;;;###autoload
   2458 (defun clojure-unwind-all ()
   2459   "Fully unwind thread at point or above point."
   2460   (interactive)
   2461   (clojure-unwind '(4)))
   2462 
   2463 (defun clojure--remove-superfluous-parens ()
   2464   "Remove extra parens from a form."
   2465   (when (looking-at "([^ )]+)")
   2466     (let ((delete-pair-blink-delay 0))
   2467       (delete-pair))))
   2468 
   2469 (defun clojure--thread-first ()
   2470   "Thread a nested sexp using ->."
   2471   (down-list)
   2472   (forward-symbol 1)
   2473   (unless (looking-at ")")
   2474     (let ((contents (clojure-delete-and-extract-sexp)))
   2475       (backward-up-list)
   2476       (just-one-space 0)
   2477       (save-excursion
   2478         (insert contents "\n")
   2479         (clojure--remove-superfluous-parens))
   2480       (when (looking-at "\\s-*\n")
   2481         (join-line 'following)
   2482         (forward-char 1)
   2483         (put-text-property (point) (1+ (point))
   2484                            'clojure-thread-line-joined t))
   2485       t)))
   2486 
   2487 (defun clojure--thread-last ()
   2488   "Thread a nested sexp using ->>."
   2489   (forward-sexp 2)
   2490   (down-list -1)
   2491   (backward-sexp)
   2492   (unless (eq (char-before) ?\()
   2493     (let ((contents (clojure-delete-and-extract-sexp)))
   2494       (just-one-space 0)
   2495       (backward-up-list)
   2496       (insert contents "\n")
   2497       (clojure--remove-superfluous-parens)
   2498       ;; cljr #255 Fix dangling parens
   2499       (forward-sexp)
   2500       (when (looking-back "^\\s-*\\()+\\)\\s-*" (line-beginning-position))
   2501         (let ((pos (match-beginning 1)))
   2502           (put-text-property pos (1+ pos) 'clojure-thread-line-joined t))
   2503         (join-line))
   2504       t)))
   2505 
   2506 (defun clojure--threadable-p ()
   2507   "Return non-nil if a form can be threaded."
   2508   (save-excursion
   2509     (forward-symbol 1)
   2510     (looking-at "[\n\r\t ]*(")))
   2511 
   2512 ;;;###autoload
   2513 (defun clojure-thread ()
   2514   "Thread by one more level an existing threading macro."
   2515   (interactive)
   2516   (ignore-errors
   2517     (when (looking-at "(")
   2518       (forward-char 1)
   2519       (forward-sexp 1)))
   2520   (search-backward-regexp "([^-]*->")
   2521   (down-list)
   2522   (when (clojure--threadable-p)
   2523     (prog1 (cond
   2524             ((looking-at "[^-]*->\\_>")  (clojure--thread-first))
   2525             ((looking-at "[^-]*->>\\_>") (clojure--thread-last)))
   2526       (clojure--fix-sexp-whitespace 'move-out))))
   2527 
   2528 (defun clojure--thread-all (first-or-last-thread but-last)
   2529   "Fully thread the form at point.
   2530 
   2531 FIRST-OR-LAST-THREAD is \"->\" or \"->>\".
   2532 
   2533 When BUT-LAST is non-nil, the last expression is not threaded.
   2534 Default value is `clojure-thread-all-but-last'."
   2535   (save-excursion
   2536     (insert-parentheses 1)
   2537     (insert first-or-last-thread))
   2538   (while (save-excursion (clojure-thread)))
   2539   (when (or but-last clojure-thread-all-but-last)
   2540     (clojure-unwind)))
   2541 
   2542 ;;;###autoload
   2543 (defun clojure-thread-first-all (but-last)
   2544   "Fully thread the form at point using ->.
   2545 
   2546 When BUT-LAST is non-nil, the last expression is not threaded.
   2547 Default value is `clojure-thread-all-but-last'."
   2548   (interactive "P")
   2549   (clojure--thread-all "-> " but-last))
   2550 
   2551 ;;;###autoload
   2552 (defun clojure-thread-last-all (but-last)
   2553   "Fully thread the form at point using ->>.
   2554 
   2555 When BUT-LAST is non-nil, the last expression is not threaded.
   2556 Default value is `clojure-thread-all-but-last'."
   2557   (interactive "P")
   2558   (clojure--thread-all "->> " but-last))
   2559 
   2560 ;;; Cycling stuff
   2561 
   2562 (defcustom clojure-use-metadata-for-privacy nil
   2563   "If nil, `clojure-cycle-privacy' will use (defn- f []).
   2564 If t, it will use (defn ^:private f [])."
   2565   :package-version '(clojure-mode . "5.5.0")
   2566   :safe #'booleanp
   2567   :type 'boolean)
   2568 
   2569 ;;;###autoload
   2570 (defun clojure-cycle-privacy ()
   2571   "Make public the current private def, or vice-versa.
   2572 See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy"
   2573   (interactive)
   2574   (save-excursion
   2575     (ignore-errors (forward-char 7))
   2576     (search-backward-regexp "(defn?\\(-\\| ^:private\\)?\\_>")
   2577     (if (match-string 1)
   2578         (replace-match "" nil nil nil 1)
   2579       (goto-char (match-end 0))
   2580       (insert (if (or clojure-use-metadata-for-privacy
   2581                       (equal (match-string 0) "(def"))
   2582                   " ^:private"
   2583                 "-")))))
   2584 
   2585 (defun clojure--convert-collection (coll-open coll-close)
   2586   "Convert the collection at (point)
   2587 by unwrapping it an wrapping it between COLL-OPEN and COLL-CLOSE."
   2588   (save-excursion
   2589     (while (and
   2590             (not (bobp))
   2591             (not (looking-at "(\\|{\\|\\[")))
   2592       (backward-char))
   2593     (when (or (eq ?\# (char-before))
   2594               (eq ?\' (char-before)))
   2595       (delete-char -1))
   2596     (when (and (bobp)
   2597                (not (memq (char-after) '(?\{ ?\( ?\[))))
   2598       (user-error "Beginning of file reached, collection is not found"))
   2599     (insert coll-open (substring (clojure-delete-and-extract-sexp) 1 -1) coll-close)))
   2600 
   2601 ;;;###autoload
   2602 (defun clojure-convert-collection-to-list ()
   2603   "Convert collection at (point) to list."
   2604   (interactive)
   2605   (clojure--convert-collection "(" ")"))
   2606 
   2607 ;;;###autoload
   2608 (defun clojure-convert-collection-to-quoted-list ()
   2609   "Convert collection at (point) to quoted list."
   2610   (interactive)
   2611   (clojure--convert-collection "'(" ")"))
   2612 
   2613 ;;;###autoload
   2614 (defun clojure-convert-collection-to-map ()
   2615   "Convert collection at (point) to map."
   2616   (interactive)
   2617   (clojure--convert-collection "{" "}"))
   2618 
   2619 ;;;###autoload
   2620 (defun clojure-convert-collection-to-vector ()
   2621   "Convert collection at (point) to vector."
   2622   (interactive)
   2623   (clojure--convert-collection "[" "]"))
   2624 
   2625 ;;;###autoload
   2626 (defun clojure-convert-collection-to-set ()
   2627   "Convert collection at (point) to set."
   2628   (interactive)
   2629   (clojure--convert-collection "#{" "}"))
   2630 
   2631 (defun clojure--in-string-p ()
   2632   "Check whether the point is currently in a string."
   2633   (nth 3 (syntax-ppss)))
   2634 
   2635 (defun clojure--in-comment-p ()
   2636   "Check whether the point is currently in a comment."
   2637   (nth 4 (syntax-ppss)))
   2638 
   2639 (defun clojure--goto-if ()
   2640   "Find the first surrounding if or if-not expression."
   2641   (when (clojure--in-string-p)
   2642     (while (or (not (looking-at "("))
   2643                (clojure--in-string-p))
   2644       (backward-char)))
   2645   (while (not (looking-at "\\((if \\)\\|\\((if-not \\)"))
   2646     (condition-case nil
   2647         (backward-up-list)
   2648       (scan-error (user-error "No if or if-not found")))))
   2649 
   2650 ;;;###autoload
   2651 (defun clojure-cycle-if ()
   2652   "Change a surrounding if to if-not, or vice-versa.
   2653 
   2654 See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if"
   2655   (interactive)
   2656   (save-excursion
   2657     (clojure--goto-if)
   2658     (cond
   2659      ((looking-at "(if-not")
   2660       (forward-char 3)
   2661       (delete-char 4)
   2662       (forward-sexp 2)
   2663       (transpose-sexps 1))
   2664      ((looking-at "(if")
   2665       (forward-char 3)
   2666       (insert "-not")
   2667       (forward-sexp 2)
   2668       (transpose-sexps 1)))))
   2669 
   2670 ;; TODO: Remove code duplication with `clojure--goto-if'.
   2671 (defun clojure--goto-when ()
   2672   "Find the first surrounding when or when-not expression."
   2673   (when (clojure--in-string-p)
   2674     (while (or (not (looking-at "("))
   2675                (clojure--in-string-p))
   2676       (backward-char)))
   2677   (while (not (looking-at "\\((when \\)\\|\\((when-not \\)"))
   2678     (condition-case nil
   2679         (backward-up-list)
   2680       (scan-error (user-error "No when or when-not found")))))
   2681 
   2682 ;;;###autoload
   2683 (defun clojure-cycle-when ()
   2684   "Change a surrounding when to when-not, or vice-versa."
   2685   (interactive)
   2686   (save-excursion
   2687     (clojure--goto-when)
   2688     (cond
   2689      ((looking-at "(when-not")
   2690       (forward-char 9)
   2691       (delete-char -4))
   2692      ((looking-at "(when")
   2693       (forward-char 5)
   2694       (insert "-not")))))
   2695 
   2696 (defun clojure-cycle-not ()
   2697   "Add or remove a not form around the current form."
   2698   (interactive)
   2699   (save-excursion
   2700     (condition-case nil
   2701         (backward-up-list)
   2702       (scan-error (user-error "`clojure-cycle-not' must be invoked inside a list")))
   2703     (if (looking-back "(not " nil)
   2704         (progn
   2705           (delete-char -5)
   2706           (forward-sexp)
   2707           (delete-char 1))
   2708       (insert "(not ")
   2709       (forward-sexp)
   2710       (insert ")"))))
   2711 
   2712 ;;; let related stuff
   2713 
   2714 (defun clojure--goto-let ()
   2715   "Go to the beginning of the nearest let form."
   2716   (when (clojure--in-string-p)
   2717     (while (or (not (looking-at "("))
   2718                (clojure--in-string-p))
   2719       (backward-char)))
   2720   (ignore-errors
   2721     (while (not (looking-at clojure--let-regexp))
   2722       (backward-up-list)))
   2723   (looking-at clojure--let-regexp))
   2724 
   2725 (defun clojure--inside-let-binding-p ()
   2726   "Return non-nil if point is inside a let binding."
   2727   (ignore-errors
   2728     (save-excursion
   2729       (let ((pos (point)))
   2730         (clojure--goto-let)
   2731         (re-search-forward "\\[")
   2732         (if (< pos (point))
   2733             nil
   2734           (forward-sexp)
   2735           (up-list)
   2736           (< pos (point)))))))
   2737 
   2738 (defun clojure--beginning-of-current-let-binding ()
   2739   "Move before the bound name of the current binding.
   2740 Assume that point is in the binding form of a let."
   2741   (let ((current-point (point)))
   2742     (clojure--goto-let)
   2743     (search-forward "[")
   2744     (forward-char)
   2745     (while (> current-point (point))
   2746       (forward-sexp))
   2747     (backward-sexp 2)))
   2748 
   2749 (defun clojure--previous-line ()
   2750   "Keep the column position while go the previous line."
   2751   (let ((col (current-column)))
   2752     (forward-line -1)
   2753     (move-to-column col)))
   2754 
   2755 (defun clojure--prepare-to-insert-new-let-binding ()
   2756   "Move to right place in the let form to insert a new binding and indent."
   2757   (if (clojure--inside-let-binding-p)
   2758       (progn
   2759         (clojure--beginning-of-current-let-binding)
   2760         (newline-and-indent)
   2761         (clojure--previous-line)
   2762         (indent-for-tab-command))
   2763     (clojure--goto-let)
   2764     (search-forward "[")
   2765     (backward-up-list)
   2766     (forward-sexp)
   2767     (down-list -1)
   2768     (backward-char)
   2769     (if (looking-at "\\[\\s-*\\]")
   2770         (forward-char)
   2771       (forward-char)
   2772       (newline-and-indent))))
   2773 
   2774 (defun clojure--sexp-regexp (sexp)
   2775   "Return a regexp for matching SEXP."
   2776   (concat "\\([^[:word:]^-]\\)"
   2777           (mapconcat #'identity (mapcar 'regexp-quote (split-string sexp))
   2778                      "[[:space:]\n\r]+")
   2779           "\\([^[:word:]^-]\\)"))
   2780 
   2781 (defun clojure--replace-sexp-with-binding (bound-name init-expr)
   2782   "Replace a binding with its bound name in the let form.
   2783 
   2784 BOUND-NAME is the name (left-hand side) of a binding.
   2785 
   2786 INIT-EXPR is the value (right-hand side) of a binding."
   2787   (save-excursion
   2788     (while (re-search-forward
   2789             (clojure--sexp-regexp init-expr)
   2790             (clojure--point-after 'clojure--goto-let 'forward-sexp)
   2791             t)
   2792       (replace-match (concat "\\1" bound-name "\\2")))))
   2793 
   2794 (defun clojure--replace-sexps-with-bindings (bindings)
   2795   "Replace bindings with their respective bound names in the let form.
   2796 
   2797 BINDINGS is the list of bound names and init expressions."
   2798   (let ((bound-name (pop bindings))
   2799         (init-expr (pop bindings)))
   2800     (when bound-name
   2801       (clojure--replace-sexp-with-binding bound-name init-expr)
   2802       (clojure--replace-sexps-with-bindings bindings))))
   2803 
   2804 (defun clojure--replace-sexps-with-bindings-and-indent ()
   2805   "Replace sexps with bindings."
   2806   (clojure--replace-sexps-with-bindings
   2807    (clojure--read-let-bindings))
   2808   (clojure-indent-region
   2809    (clojure--point-after 'clojure--goto-let)
   2810    (clojure--point-after 'clojure--goto-let 'forward-sexp)))
   2811 
   2812 (defun clojure--read-let-bindings ()
   2813   "Read the bound-name and init expression pairs in the binding form.
   2814 Return a list: odd elements are bound names, even elements init expressions."
   2815   (clojure--goto-let)
   2816   (down-list 2)
   2817   (let* ((start (point))
   2818          (sexp-start start)
   2819          (end (save-excursion
   2820                 (backward-char)
   2821                 (forward-sexp)
   2822                 (down-list -1)
   2823                 (point)))
   2824          bindings)
   2825     (while (/= sexp-start end)
   2826       (forward-sexp)
   2827       (push
   2828        (string-trim (buffer-substring-no-properties sexp-start (point)))
   2829        bindings)
   2830       (skip-chars-forward "\r\n\t[:blank:]")
   2831       (setq sexp-start (point)))
   2832     (nreverse bindings)))
   2833 
   2834 (defun clojure--introduce-let-internal (name &optional n)
   2835   "Create a let form, binding the form at point with NAME.
   2836 
   2837 Optional numeric argument N, if non-nil, introduces the let N
   2838 lists up."
   2839   (if (numberp n)
   2840       (let ((init-expr-sexp (clojure-delete-and-extract-sexp)))
   2841         (insert name)
   2842         (ignore-errors (backward-up-list n))
   2843         (insert "(let" (clojure-delete-and-extract-sexp) ")")
   2844         (backward-sexp)
   2845         (down-list)
   2846         (forward-sexp)
   2847         (insert " [" name " " init-expr-sexp "]\n")
   2848         (clojure--replace-sexps-with-bindings-and-indent))
   2849     (insert "[ " (clojure-delete-and-extract-sexp) "]")
   2850     (backward-sexp)
   2851     (insert "(let " (clojure-delete-and-extract-sexp) ")")
   2852     (backward-sexp)
   2853     (down-list 2)
   2854     (insert name)
   2855     (forward-sexp)
   2856     (up-list)
   2857     (newline-and-indent)
   2858     (insert name)))
   2859 
   2860 (defun clojure--move-to-let-internal (name)
   2861   "Bind the form at point to NAME in the nearest let."
   2862   (if (not (save-excursion (clojure--goto-let)))
   2863       (clojure--introduce-let-internal name)
   2864     (let ((contents (clojure-delete-and-extract-sexp)))
   2865       (insert name)
   2866       (clojure--prepare-to-insert-new-let-binding)
   2867       (insert contents)
   2868       (backward-sexp)
   2869       (insert " ")
   2870       (backward-char)
   2871       (insert name)
   2872       (clojure--replace-sexps-with-bindings-and-indent))))
   2873 
   2874 (defun clojure--let-backward-slurp-sexp-internal ()
   2875   "Slurp the s-expression before the let form into the let form."
   2876   (clojure--goto-let)
   2877   (backward-sexp)
   2878   (let ((sexp (string-trim (clojure-delete-and-extract-sexp))))
   2879     (delete-blank-lines)
   2880     (down-list)
   2881     (forward-sexp 2)
   2882     (newline-and-indent)
   2883     (insert sexp)
   2884     (clojure--replace-sexps-with-bindings-and-indent)))
   2885 
   2886 ;;;###autoload
   2887 (defun clojure-let-backward-slurp-sexp (&optional n)
   2888   "Slurp the s-expression before the let form into the let form.
   2889 With a numeric prefix argument slurp the previous N s-expressions
   2890 into the let form."
   2891   (interactive "p")
   2892   (let ((n (or n 1)))
   2893     (dotimes (_ n)
   2894       (save-excursion (clojure--let-backward-slurp-sexp-internal)))))
   2895 
   2896 (defun clojure--let-forward-slurp-sexp-internal ()
   2897   "Slurp the next s-expression after the let form into the let form."
   2898   (clojure--goto-let)
   2899   (forward-sexp)
   2900   (let ((sexp (string-trim (clojure-delete-and-extract-sexp))))
   2901     (down-list -1)
   2902     (newline-and-indent)
   2903     (insert sexp)
   2904     (clojure--replace-sexps-with-bindings-and-indent)))
   2905 
   2906 ;;;###autoload
   2907 (defun clojure-let-forward-slurp-sexp (&optional n)
   2908   "Slurp the next s-expression after the let form into the let form.
   2909 With a numeric prefix argument slurp the next N s-expressions
   2910 into the let form."
   2911   (interactive "p")
   2912   (unless n (setq n 1))
   2913   (dotimes (_ n)
   2914     (save-excursion (clojure--let-forward-slurp-sexp-internal))))
   2915 
   2916 ;;;###autoload
   2917 (defun clojure-introduce-let (&optional n)
   2918   "Create a let form, binding the form at point.
   2919 With a numeric prefix argument the let is introduced N lists up."
   2920   (interactive "P")
   2921   (clojure--introduce-let-internal (read-from-minibuffer "Name of bound symbol: ") n))
   2922 
   2923 ;;;###autoload
   2924 (defun clojure-move-to-let ()
   2925   "Move the form at point to a binding in the nearest let."
   2926   (interactive)
   2927   (clojure--move-to-let-internal (read-from-minibuffer "Name of bound symbol: ")))
   2928 
   2929 ;;; Promoting #() function literals
   2930 (defun clojure--gather-fn-literal-args ()
   2931   "Return a cons cell (ARITY . VARARG)
   2932 ARITY is number of arguments in the function,
   2933 VARARG is a boolean of whether it takes a variable argument %&."
   2934   (save-excursion
   2935     (let ((end (save-excursion (clojure-forward-logical-sexp) (point)))
   2936           (rgx (rx symbol-start "%" (group (?  (or "&" (+ (in "0-9"))))) symbol-end))
   2937           (arity 0)
   2938           (vararg nil))
   2939       (while (re-search-forward rgx end 'noerror)
   2940         (when (not (or (clojure--in-comment-p) (clojure--in-string-p)))
   2941           (let ((s (match-string 1)))
   2942             (if (string= s "&")
   2943                 (setq vararg t)
   2944               (setq arity
   2945                     (max arity
   2946                          (if (string= s "") 1
   2947                            (string-to-number s))))))))
   2948       (cons arity vararg))))
   2949 
   2950 (defun clojure--substitute-fn-literal-arg (arg sub end)
   2951   "ARG is either a number or the symbol '&.
   2952 SUB is a string to substitute with, and
   2953 END marks the end of the fn expression"
   2954   (save-excursion
   2955     (let ((rgx (format "\\_<%%%s\\_>" (if (eq arg 1) "1?" arg))))
   2956       (while (re-search-forward rgx end 'noerror)
   2957         (when (and (not (clojure--in-comment-p))
   2958                    (not (clojure--in-string-p)))
   2959           (replace-match sub))))))
   2960 
   2961 (defun clojure-promote-fn-literal ()
   2962   "Convert a #(...) function into (fn [...] ...), prompting for the argument names."
   2963   (interactive)
   2964   (when-let (beg (clojure-string-start))
   2965     (goto-char beg))
   2966   (if (or (looking-at-p "#(")
   2967           (ignore-errors (forward-char 1))
   2968           (re-search-backward "#(" (save-excursion (beginning-of-defun) (point)) 'noerror))
   2969       (let* ((end (save-excursion (clojure-forward-logical-sexp) (point-marker)))
   2970              (argspec (clojure--gather-fn-literal-args))
   2971              (arity (car argspec))
   2972              (vararg (cdr argspec)))
   2973         (delete-char 1)
   2974         (save-excursion (forward-sexp 1) (insert ")"))
   2975         (save-excursion
   2976           (insert "(fn [] ")
   2977           (backward-char 2)
   2978           (mapc (lambda (n)
   2979                   (let ((name (read-string (format "Name of argument %d: " n))))
   2980                     (when (/= n 1) (insert " "))
   2981                     (insert name)
   2982                     (clojure--substitute-fn-literal-arg n name end)))
   2983                 (number-sequence 1 arity))
   2984           (when vararg
   2985             (insert " & ")
   2986             (let ((name (read-string "Name of variadic argument: ")))
   2987               (insert name)
   2988               (clojure--substitute-fn-literal-arg '& name end)))))
   2989     (user-error "No #() literal at point!")))
   2990 
   2991 ;;; Renaming ns aliases
   2992 
   2993 (defun clojure--alias-usage-regexp (alias)
   2994   "Regexp for matching usages of ALIAS in qualified symbols, keywords and maps.
   2995 When nil, match all namespace usages.
   2996 The first match-group is the alias."
   2997   (let ((alias (if alias (regexp-quote alias) clojure--sym-regexp)))
   2998     (concat "#::\\(?1:" alias "\\)[ ,\r\n\t]*{"
   2999             "\\|"
   3000             "\\_<\\(?1:" alias "\\)/")))
   3001 
   3002 (defun clojure--rename-ns-alias-usages (current-alias new-alias beg end)
   3003   "Rename all usages of CURRENT-ALIAS in region BEG to END with NEW-ALIAS."
   3004   (let ((rgx (clojure--alias-usage-regexp current-alias)))
   3005     (save-mark-and-excursion
   3006       (goto-char end)
   3007       (setq end (point-marker))
   3008       (goto-char beg)
   3009       (while (re-search-forward rgx end 'noerror)
   3010         (when (not (clojure--in-string-p)) ;; replace in comments, but not strings
   3011           (goto-char (match-beginning 1))
   3012           (delete-region (point) (match-end 1))
   3013           (insert new-alias))))))
   3014 
   3015 (defun clojure--collect-ns-aliases (beg end ns-form-p)
   3016   "Collect all aliases between BEG and END.
   3017 When NS-FORM-P is non-nil, treat the region as a ns form
   3018 and pick up aliases from [... :as alias] forms,
   3019 otherwise pick up alias usages from keywords / symbols."
   3020   (let ((res ()))
   3021     (save-excursion
   3022       (let ((rgx (if ns-form-p
   3023                      (rx ":as" (+ space)
   3024                          (group-n 1 (+ (not (in " ,]\n")))))
   3025                    (clojure--alias-usage-regexp nil))))
   3026         (goto-char beg)
   3027         (while (re-search-forward rgx end 'noerror)
   3028           (unless (or (clojure--in-string-p) (clojure--in-comment-p))
   3029             (cl-pushnew (match-string-no-properties 1) res
   3030                         :test #'equal)))
   3031         (reverse res)))))
   3032 
   3033 (defun clojure--rename-ns-alias-internal (current-alias new-alias)
   3034   "Rename a namespace alias CURRENT-ALIAS to NEW-ALIAS.
   3035 Assume point is at the start of ns form."
   3036   (clojure--find-ns-in-direction 'backward)
   3037   (let ((rgx (concat ":as +" (regexp-quote current-alias) "\\_>"))
   3038         (bound (save-excursion (forward-list 1) (point-marker))))
   3039     (when (search-forward-regexp rgx bound t)
   3040       (replace-match (concat ":as " new-alias))
   3041       (clojure--rename-ns-alias-usages current-alias new-alias bound (point-max)))))
   3042 
   3043 ;;;###autoload
   3044 (defun clojure-rename-ns-alias ()
   3045   "Rename a namespace alias.
   3046 If a region is active, only pick up and rename aliases within the region."
   3047   (interactive)
   3048   (if (use-region-p)
   3049       (let ((beg (region-beginning))
   3050             (end (copy-marker (region-end)))
   3051             current-alias new-alias)
   3052         ;; while loop for renaming multiple aliases in the region.
   3053         ;; C-g or leave blank to break out of the loop
   3054         (while (not (string-empty-p
   3055                      (setq current-alias
   3056                            (completing-read "Current alias: "
   3057                                             (clojure--collect-ns-aliases beg end nil)))))
   3058           (setq new-alias (read-from-minibuffer (format "Replace %s with: " current-alias)))
   3059           (clojure--rename-ns-alias-usages current-alias new-alias beg end)))
   3060     (save-excursion
   3061       (clojure--find-ns-in-direction 'backward)
   3062       (let* ((bounds (bounds-of-thing-at-point 'list))
   3063              (current-alias (completing-read "Current alias: "
   3064                                              (clojure--collect-ns-aliases
   3065                                               (car bounds) (cdr bounds) t)))
   3066              (new-alias (read-from-minibuffer (format "Replace %s with: " current-alias))))
   3067         (clojure--rename-ns-alias-internal current-alias new-alias)))))
   3068 
   3069 (defun clojure--add-arity-defprotocol-internal ()
   3070   "Add an arity to a signature inside a defprotocol.
   3071 
   3072 Assumes cursor is at beginning of signature."
   3073   (re-search-forward "\\[")
   3074   (save-excursion (insert "] [")))
   3075 
   3076 (defun clojure--add-arity-reify-internal ()
   3077   "Add an arity to a function inside a reify.
   3078 
   3079 Assumes cursor is at beginning of function."
   3080   (re-search-forward "\\(\\w+ \\)")
   3081   (insert "[")
   3082   (save-excursion (insert "])\n(" (match-string 0))))
   3083 
   3084 (defun clojure--add-arity-internal ()
   3085   "Add an arity to a function.
   3086 
   3087 Assumes cursor is at beginning of function."
   3088   (let ((beg-line (what-line))
   3089         (end (save-excursion (forward-sexp)
   3090                              (point))))
   3091     (down-list 2)
   3092     (when (looking-back "{" 1) ;; skip metadata if present
   3093       (up-list)
   3094       (down-list))
   3095     (cond
   3096      ((looking-back "(" 1) ;; multi-arity fn
   3097       (insert "[")
   3098       (save-excursion (insert "])\n(")))
   3099      ((looking-back "\\[" 1)  ;; single-arity fn
   3100       (let* ((same-line (string= beg-line (what-line)))
   3101              (new-arity-text (concat (when same-line "\n") "([")))
   3102         (save-excursion
   3103           (goto-char end)
   3104           (insert ")"))
   3105 
   3106         (re-search-backward " +\\[")
   3107         (replace-match new-arity-text)
   3108         (save-excursion (insert "])\n([")))))))
   3109 
   3110 ;;;###autoload
   3111 (defun clojure-add-arity ()
   3112   "Add an arity to a function."
   3113   (interactive)
   3114   (let ((original-pos (point))
   3115         (n 0))
   3116     (while (not (looking-at-p "(\\(defn\\|letfn\\|fn\\|defmacro\\|defmethod\\|defprotocol\\|reify\\|proxy\\)"))
   3117       (setq n (1+ n))
   3118       (backward-up-list 1 t))
   3119     (let ((beg (point))
   3120           (end-marker (make-marker))
   3121           (end (save-excursion (forward-sexp)
   3122                                (point)))
   3123           (jump-up (lambda (x)
   3124                      (goto-char original-pos)
   3125                      (backward-up-list x t))))
   3126       (set-marker end-marker end)
   3127       (cond
   3128        ((looking-at-p "(\\(defn\\|fn\\|defmethod\\|defmacro\\)")
   3129         (clojure--add-arity-internal))
   3130        ((looking-at-p "(letfn")
   3131         (funcall jump-up (- n 2))
   3132         (clojure--add-arity-internal))
   3133        ((looking-at-p "(proxy")
   3134         (funcall jump-up (- n 1))
   3135         (clojure--add-arity-internal))
   3136        ((looking-at-p "(defprotocol")
   3137         (funcall jump-up (- n 1))
   3138         (clojure--add-arity-defprotocol-internal))
   3139        ((looking-at-p "(reify")
   3140         (funcall jump-up (- n 1))
   3141         (clojure--add-arity-reify-internal)))
   3142       (indent-region beg end-marker))))
   3143 
   3144 
   3145 ;;; Toggle Ignore forms
   3146 
   3147 (defun clojure--toggle-ignore-next-sexp (&optional n)
   3148   "Insert or delete N `#_' ignore macros at the current point.
   3149 Point must be directly before a sexp or the #_ characters.
   3150 When acting on a top level form, insert #_ on a new line
   3151 preceding the form to prevent indentation changes."
   3152   (let ((rgx (rx-to-string `(repeat ,(or n 1) (seq "#_" (* (in "\r\n" blank)))))))
   3153     (backward-prefix-chars)
   3154     (skip-chars-backward "#_ \r\n")
   3155     (skip-chars-forward " \r\n")
   3156     (if (looking-at rgx)
   3157         (delete-region (point) (match-end 0))
   3158       (dotimes (_ (or  n 1)) (insert-before-markers "#_"))
   3159       (when (zerop (car (syntax-ppss)))
   3160         (insert-before-markers "\n")))))
   3161 
   3162 (defun clojure-toggle-ignore (&optional n)
   3163   "Toggle the #_ ignore reader form for the sexp at point.
   3164 With numeric argument, toggle N number of #_ forms at the same point.
   3165 
   3166   e.g. with N = 2:
   3167   |a b c  => #_#_a b c"
   3168   (interactive "p")
   3169   (save-excursion
   3170     (ignore-errors
   3171       (goto-char (or (nth 8 (syntax-ppss)) ;; beginning of string
   3172                      (beginning-of-thing 'sexp))))
   3173     (clojure--toggle-ignore-next-sexp n)))
   3174 
   3175 (defun clojure-toggle-ignore-surrounding-form (&optional arg)
   3176   "Toggle the #_ ignore reader form for the surrounding form at point.
   3177 With optional ARG, move up by ARG surrounding forms first.
   3178 With universal argument \\[universal-argument], act on the \"top-level\" form."
   3179   (interactive "P")
   3180   (save-excursion
   3181     (if (consp arg)
   3182         (clojure-toggle-ignore-defun)
   3183       (condition-case nil
   3184           (backward-up-list arg t t)
   3185         (scan-error nil)))
   3186     (clojure--toggle-ignore-next-sexp)))
   3187 
   3188 (defun clojure-toggle-ignore-defun ()
   3189   "Toggle the #_ ignore reader form for the \"top-level\" form at point."
   3190   (interactive)
   3191   (save-excursion
   3192     (beginning-of-defun)
   3193     (clojure--toggle-ignore-next-sexp)))
   3194 
   3195 
   3196 ;;; ClojureScript
   3197 (defconst clojurescript-font-lock-keywords
   3198   (eval-when-compile
   3199     `(;; ClojureScript built-ins
   3200       (,(concat "(\\(?:\.*/\\)?"
   3201                 (regexp-opt '("js-obj" "js-delete" "clj->js" "js->clj"))
   3202                 "\\>")
   3203        0 font-lock-builtin-face)))
   3204   "Additional font-locking for `clojurescript-mode'.")
   3205 
   3206 ;;;###autoload
   3207 (define-derived-mode clojurescript-mode clojure-mode "ClojureScript"
   3208   "Major mode for editing ClojureScript code.
   3209 
   3210 \\{clojurescript-mode-map}"
   3211   (font-lock-add-keywords nil clojurescript-font-lock-keywords))
   3212 
   3213 ;;;###autoload
   3214 (define-derived-mode clojurec-mode clojure-mode "ClojureC"
   3215   "Major mode for editing ClojureC code.
   3216 
   3217 \\{clojurec-mode-map}")
   3218 
   3219 ;;;###autoload
   3220 (progn
   3221   (add-to-list 'auto-mode-alist
   3222                '("\\.\\(clj\\|cljd\\|dtm\\|edn\\)\\'" . clojure-mode))
   3223   (add-to-list 'auto-mode-alist '("\\.cljc\\'" . clojurec-mode))
   3224   (add-to-list 'auto-mode-alist '("\\.cljs\\'" . clojurescript-mode))
   3225   ;; boot build scripts are Clojure source files
   3226   (add-to-list 'auto-mode-alist '("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode))
   3227   ;; babashka scripts are Clojure source files
   3228   (add-to-list 'interpreter-mode-alist '("bb" . clojure-mode))
   3229   ;; nbb scripts are ClojureScript source files
   3230   (add-to-list 'interpreter-mode-alist '("nbb" . clojurescript-mode)))
   3231 
   3232 (provide 'clojure-mode)
   3233 
   3234 ;; Local Variables:
   3235 ;; coding: utf-8
   3236 ;; End:
   3237 
   3238 ;;; clojure-mode.el ends here