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