From: Andreas Gerler Date: Tue, 13 Nov 2018 11:38:11 +0000 (+0100) Subject: update clojure and clomacs. X-Git-Url: http://git.bundesbrandschatzamt.de/?a=commitdiff_plain;h=3093abbabcfabdf31872fab83cebc55111f19ceb;p=emacsconfig.git update clojure and clomacs. Signed-off-by: Andreas Gerler --- diff --git a/elpa/clojure-mode-20170120.2239/clojure-mode-autoloads.el b/elpa/clojure-mode-20170120.2239/clojure-mode-autoloads.el deleted file mode 100644 index 86af7ebd..00000000 --- a/elpa/clojure-mode-20170120.2239/clojure-mode-autoloads.el +++ /dev/null @@ -1,149 +0,0 @@ -;;; clojure-mode-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil "clojure-mode" "clojure-mode.el" (22692 30285 -;;;;;; 0 0)) -;;; Generated autoloads from clojure-mode.el - -(autoload 'clojure-mode "clojure-mode" "\ -Major mode for editing Clojure code. - -\\{clojure-mode-map} - -\(fn)" t nil) - -(autoload 'clojure-unwind "clojure-mode" "\ -Unwind thread at point or above point by one level. -Return nil if there are no more levels to unwind. - -\(fn)" t nil) - -(autoload 'clojure-unwind-all "clojure-mode" "\ -Fully unwind thread at point or above point. - -\(fn)" t nil) - -(autoload 'clojure-thread "clojure-mode" "\ -Thread by one more level an existing threading macro. - -\(fn)" t nil) - -(autoload 'clojure-thread-first-all "clojure-mode" "\ -Fully thread the form at point using ->. -When BUT-LAST is passed the last expression is not threaded. - -\(fn BUT-LAST)" t nil) - -(autoload 'clojure-thread-last-all "clojure-mode" "\ -Fully thread the form at point using ->>. -When BUT-LAST is passed the last expression is not threaded. - -\(fn BUT-LAST)" t nil) - -(autoload 'clojure-cycle-privacy "clojure-mode" "\ -Make public the current private def, or vice-versa. -See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy - -\(fn)" t nil) - -(autoload 'clojure-convert-collection-to-list "clojure-mode" "\ -Convert collection at (point) to list. - -\(fn)" t nil) - -(autoload 'clojure-convert-collection-to-quoted-list "clojure-mode" "\ -Convert collection at (point) to quoted list. - -\(fn)" t nil) - -(autoload 'clojure-convert-collection-to-map "clojure-mode" "\ -Convert collection at (point) to map. - -\(fn)" t nil) - -(autoload 'clojure-convert-collection-to-vector "clojure-mode" "\ -Convert collection at (point) to vector. - -\(fn)" t nil) - -(autoload 'clojure-convert-collection-to-set "clojure-mode" "\ -Convert collection at (point) to set. - -\(fn)" t nil) - -(autoload 'clojure-cycle-if "clojure-mode" "\ -Change a surrounding if to if-not, or vice-versa. - -See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if - -\(fn)" t nil) - -(autoload 'clojure-cycle-when "clojure-mode" "\ -Change a surrounding when to when-not, or vice-versa. - -\(fn)" t nil) - -(autoload 'clojure-let-backward-slurp-sexp "clojure-mode" "\ -Slurp the s-expression before the let form into the let form. -With a numberic prefix argument slurp the previous N s-expression into the let form. - -\(fn &optional N)" t nil) - -(autoload 'clojure-let-forward-slurp-sexp "clojure-mode" "\ -Slurp the next s-expression after the let form into the let form. -With a numeric prefix argument slurp the next N s-expressions into the let form. - -\(fn &optional N)" t nil) - -(autoload 'clojure-introduce-let "clojure-mode" "\ -Create a let form, binding the form at point. -With a numeric prefix argument the let is introduced N lists up. - -\(fn &optional N)" t nil) - -(autoload 'clojure-move-to-let "clojure-mode" "\ -Move the form at point to a binding in the nearest let. - -\(fn)" t nil) - -(autoload 'clojurescript-mode "clojure-mode" "\ -Major mode for editing ClojureScript code. - -\\{clojurescript-mode-map} - -\(fn)" t nil) - -(autoload 'clojurec-mode "clojure-mode" "\ -Major mode for editing ClojureC code. - -\\{clojurec-mode-map} - -\(fn)" t nil) - -(autoload 'clojurex-mode "clojure-mode" "\ -Major mode for editing ClojureX code. - -\\{clojurex-mode-map} - -\(fn)" t nil) - -(add-to-list 'auto-mode-alist '("\\.\\(clj\\|dtm\\|edn\\)\\'" . clojure-mode)) - -(add-to-list 'auto-mode-alist '("\\.cljc\\'" . clojurec-mode)) - -(add-to-list 'auto-mode-alist '("\\.cljx\\'" . clojurex-mode)) - -(add-to-list 'auto-mode-alist '("\\.cljs\\'" . clojurescript-mode)) - -(add-to-list 'auto-mode-alist '("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; clojure-mode-autoloads.el ends here diff --git a/elpa/clojure-mode-20170120.2239/clojure-mode-pkg.el b/elpa/clojure-mode-20170120.2239/clojure-mode-pkg.el deleted file mode 100644 index d7718c0b..00000000 --- a/elpa/clojure-mode-20170120.2239/clojure-mode-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; -*- no-byte-compile: t -*- -(define-package "clojure-mode" "20170120.2239" "Major mode for Clojure code" '((emacs "24.4")) :url "http://github.com/clojure-emacs/clojure-mode" :keywords '("languages" "clojure" "clojurescript" "lisp")) diff --git a/elpa/clojure-mode-20170120.2239/clojure-mode.el b/elpa/clojure-mode-20170120.2239/clojure-mode.el deleted file mode 100644 index c3c192d5..00000000 --- a/elpa/clojure-mode-20170120.2239/clojure-mode.el +++ /dev/null @@ -1,2403 +0,0 @@ -;;; clojure-mode.el --- Major mode for Clojure code -*- lexical-binding: t; -*- - -;; Copyright © 2007-2016 Jeffrey Chu, Lennart Staflin, Phil Hagelberg -;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba -;; -;; Authors: Jeffrey Chu -;; Lennart Staflin -;; Phil Hagelberg -;; Bozhidar Batsov -;; Artur Malabarba -;; URL: http://github.com/clojure-emacs/clojure-mode -;; Package-Version: 20170120.2239 -;; Keywords: languages clojure clojurescript lisp -;; Version: 5.7.0-snapshot -;; Package-Requires: ((emacs "24.4")) - -;; This file is not part of GNU Emacs. - -;;; Commentary: - -;; Provides font-lock, indentation, navigation and basic refactoring for the -;; Clojure programming language (http://clojure.org). - -;; Using clojure-mode with paredit or smartparens is highly recommended. - -;; Here are some example configurations: - -;; ;; require or autoload paredit-mode -;; (add-hook 'clojure-mode-hook #'paredit-mode) - -;; ;; require or autoload smartparens -;; (add-hook 'clojure-mode-hook #'smartparens-strict-mode) - -;; See inf-clojure (http://github.com/clojure-emacs/inf-clojure) for -;; basic interaction with Clojure subprocesses. - -;; See CIDER (http://github.com/clojure-emacs/cider) for -;; better interaction with subprocesses via nREPL. - -;;; License: - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. -;; -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. - -;;; Code: - - -(eval-when-compile - (defvar calculate-lisp-indent-last-sexp) - (defvar font-lock-beg) - (defvar font-lock-end) - (defvar paredit-space-for-delimiter-predicates) - (defvar paredit-version) - (defvar paredit-mode)) - -(require 'cl-lib) -(require 'imenu) -(require 'newcomment) -(require 'align) -(require 'subr-x) - -(declare-function lisp-fill-paragraph "lisp-mode" (&optional justify)) - -(defgroup clojure nil - "Major mode for editing Clojure code." - :prefix "clojure-" - :group 'languages - :link '(url-link :tag "Github" "https://github.com/clojure-emacs/clojure-mode") - :link '(emacs-commentary-link :tag "Commentary" "clojure-mode")) - -(defconst clojure-mode-version "5.7.0-snapshot" - "The current version of `clojure-mode'.") - -(defface clojure-keyword-face - '((t (:inherit font-lock-constant-face))) - "Face used to font-lock Clojure keywords (:something)." - :package-version '(clojure-mode . "3.0.0")) - -(defface clojure-character-face - '((t (:inherit font-lock-string-face))) - "Face used to font-lock Clojure character literals." - :package-version '(clojure-mode . "3.0.0")) - -(defface clojure-interop-method-face - '((t (:inherit font-lock-preprocessor-face))) - "Face used to font-lock interop method names (camelCase)." - :package-version '(clojure-mode . "3.0.0")) - -(defcustom clojure-indent-style :always-align - "Indentation style to use for function forms and macro forms. -There are two cases of interest configured by this variable. - -- Case (A) is when at least one function argument is on the same - line as the function name. -- Case (B) is the opposite (no arguments are on the same line as - the function name). Note that the body of macros is not - affected by this variable, it is always indented by - `lisp-body-indent' (default 2) spaces. - -Note that this variable configures the indentation of function -forms (and function-like macros), it does not affect macros that -already use special indentation rules. - -The possible values for this variable are keywords indicating how -to indent function forms. - - `:always-align' - Follow the same rules as `lisp-mode'. All - args are vertically aligned with the first arg in case (A), - and vertically aligned with the function name in case (B). - For instance: - (reduce merge - some-coll) - (reduce - merge - some-coll) - - `:always-indent' - All args are indented like a macro body. - (reduce merge - some-coll) - (reduce - merge - some-coll) - - `:align-arguments' - Case (A) is indented like `lisp', and - case (B) is indented like a macro body. - (reduce merge - some-coll) - (reduce - merge - some-coll)" - :safe #'keywordp - :type '(choice (const :tag "Same as `lisp-mode'" :always-align) - (const :tag "Indent like a macro body" :always-indent) - (const :tag "Indent like a macro body unless first arg is on the same line" - :align-arguments)) - :package-version '(clojure-mode . "5.2.0")) - -(define-obsolete-variable-alias 'clojure-defun-style-default-indent - 'clojure-indent-style "5.2.0") - -(defcustom clojure-use-backtracking-indent t - "When non-nil, enable context sensitive indentation." - :type 'boolean - :safe 'booleanp) - -(defcustom clojure-max-backtracking 3 - "Maximum amount to backtrack up a list to check for context." - :type 'integer - :safe 'integerp) - -(defcustom clojure-docstring-fill-column fill-column - "Value of `fill-column' to use when filling a docstring." - :type 'integer - :safe 'integerp) - -(defcustom clojure-docstring-fill-prefix-width 2 - "Width of `fill-prefix' when filling a docstring. -The default value conforms with the de facto convention for -Clojure docstrings, aligning the second line with the opening -double quotes on the third column." - :type 'integer - :safe 'integerp) - -(defcustom clojure-omit-space-between-tag-and-delimiters '(?\[ ?\{) - "Allowed opening delimiter characters after a reader literal tag. -For example, \[ is allowed in :db/id[:db.part/user]." - :type '(set (const :tag "[" ?\[) - (const :tag "{" ?\{) - (const :tag "(" ?\() - (const :tag "\"" ?\")) - :safe (lambda (value) - (and (listp value) - (cl-every 'characterp value)))) - -(defcustom clojure-build-tool-files '("project.clj" "build.boot" "build.gradle") - "A list of files, which identify a Clojure project's root. -Out-of-the box clojure-mode understands lein, boot and gradle." - :type '(repeat string) - :package-version '(clojure-mode . "5.0.0") - :safe (lambda (value) - (and (listp value) - (cl-every 'stringp value)))) - -(defcustom clojure-refactor-map-prefix (kbd "C-c C-r") - "Clojure refactor keymap prefix." - :type 'string - :package-version '(clojure-mode . "5.6.0")) - -(defvar clojure-refactor-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-t") #'clojure-thread) - (define-key map (kbd "t") #'clojure-thread) - (define-key map (kbd "C-u") #'clojure-unwind) - (define-key map (kbd "u") #'clojure-unwind) - (define-key map (kbd "C-f") #'clojure-thread-first-all) - (define-key map (kbd "f") #'clojure-thread-first-all) - (define-key map (kbd "C-l") #'clojure-thread-last-all) - (define-key map (kbd "l") #'clojure-thread-last-all) - (define-key map (kbd "C-a") #'clojure-unwind-all) - (define-key map (kbd "a") #'clojure-unwind-all) - (define-key map (kbd "C-p") #'clojure-cycle-privacy) - (define-key map (kbd "p") #'clojure-cycle-privacy) - (define-key map (kbd "C-(") #'clojure-convert-collection-to-list) - (define-key map (kbd "(") #'clojure-convert-collection-to-list) - (define-key map (kbd "C-'") #'clojure-convert-collection-to-quoted-list) - (define-key map (kbd "'") #'clojure-convert-collection-to-quoted-list) - (define-key map (kbd "C-{") #'clojure-convert-collection-to-map) - (define-key map (kbd "{") #'clojure-convert-collection-to-map) - (define-key map (kbd "C-[") #'clojure-convert-collection-to-vector) - (define-key map (kbd "[") #'clojure-convert-collection-to-vector) - (define-key map (kbd "C-#") #'clojure-convert-collection-to-set) - (define-key map (kbd "#") #'clojure-convert-collection-to-set) - (define-key map (kbd "C-i") #'clojure-cycle-if) - (define-key map (kbd "i") #'clojure-cycle-if) - (define-key map (kbd "C-w") #'clojure-cycle-when) - (define-key map (kbd "w") #'clojure-cycle-when) - (define-key map (kbd "C-o") #'clojure-cycle-not) - (define-key map (kbd "o") #'clojure-cycle-not) - (define-key map (kbd "n i") #'clojure-insert-ns-form) - (define-key map (kbd "n h") #'clojure-insert-ns-form-at-point) - (define-key map (kbd "n u") #'clojure-update-ns) - (define-key map (kbd "n s") #'clojure-sort-ns) - (define-key map (kbd "s i") #'clojure-introduce-let) - (define-key map (kbd "s m") #'clojure-move-to-let) - (define-key map (kbd "s f") #'clojure-let-forward-slurp-sexp) - (define-key map (kbd "s b") #'clojure-let-backward-slurp-sexp) - map) - "Keymap for Clojure refactoring commands.") -(fset 'clojure-refactor-map clojure-refactor-map) - -(defvar clojure-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-:") #'clojure-toggle-keyword-string) - (define-key map (kbd "C-c SPC") #'clojure-align) - (define-key map clojure-refactor-map-prefix 'clojure-refactor-map) - (easy-menu-define clojure-mode-menu map "Clojure Mode Menu" - '("Clojure" - ["Toggle between string & keyword" clojure-toggle-keyword-string] - ["Align expression" clojure-align] - ["Cycle privacy" clojure-cycle-privacy] - ["Cycle if, if-not" clojure-cycle-if] - ["Cycle when, when-not" clojure-cycle-when] - ["Cycle not" clojure-cycle-not] - ("ns forms" - ["Insert ns form at the top" clojure-insert-ns-form] - ["Insert ns form here" clojure-insert-ns-form-at-point] - ["Update ns form" clojure-update-ns] - ["Sort ns form" clojure-sort-ns]) - ("Convert collection" - ["Convert to list" clojure-convert-collection-to-list] - ["Convert to quoted list" clojure-convert-collection-to-quoted-list] - ["Convert to map" clojure-convert-collection-to-map] - ["Convert to vector" clojure-convert-collection-to-vector] - ["Convert to set" clojure-convert-collection-to-set]) - ("Refactor -> and ->>" - ["Thread once more" clojure-thread] - ["Fully thread a form with ->" clojure-thread-first-all] - ["Fully thread a form with ->>" clojure-thread-last-all] - "--" - ["Unwind once" clojure-unwind] - ["Fully unwind a threading macro" clojure-unwind-all]) - ("Let expression" - ["Introduce let" clojure-introduce-let] - ["Move to let" clojure-move-to-let] - ["Forward slurp form into let" clojure-let-forward-slurp-sexp] - ["Backward slurp form into let" clojure-let-backward-slurp-sexp]) - ("Documentation" - ["View a Clojure guide" clojure-view-guide] - ["View a Clojure reference section" clojure-view-reference-section] - ["View the Clojure cheatsheet" clojure-view-cheatsheet] - ["View the Clojure Grimoire" clojure-view-grimoire] - ["View the Clojure style guide" clojure-view-style-guide]) - "--" - ["Report a clojure-mode bug" clojure-mode-report-bug] - ["Clojure-mode version" clojure-mode-display-version])) - map) - "Keymap for Clojure mode.") - -(defvar clojure-mode-syntax-table - (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table))) - (modify-syntax-entry ?\{ "(}" table) - (modify-syntax-entry ?\} "){" table) - (modify-syntax-entry ?\[ "(]" table) - (modify-syntax-entry ?\] ")[" table) - (modify-syntax-entry ?? "_ p" table) ; ? is a prefix outside symbols - (modify-syntax-entry ?# "_ p" table) ; # is allowed inside keywords (#399) - (modify-syntax-entry ?~ "'" table) - (modify-syntax-entry ?^ "'" table) - (modify-syntax-entry ?@ "'" table) - table) - "Syntax table for Clojure mode. -Inherits from `emacs-lisp-mode-syntax-table'.") - -(defconst clojure--prettify-symbols-alist - '(("fn" . ?λ))) - -(defvar-local clojure-expected-ns-function nil - "The function used to determine the expected namespace of a file. -`clojure-mode' ships a basic function named `clojure-expected-ns' -that does basic heuristics to figure this out. -CIDER provides a more complex version which does classpath analysis.") - -(defun clojure-mode-display-version () - "Display the current `clojure-mode-version' in the minibuffer." - (interactive) - (message "clojure-mode (version %s)" clojure-mode-version)) - -(defconst clojure-mode-report-bug-url "https://github.com/clojure-emacs/clojure-mode/issues/new" - "The URL to report a clojure-mode issue.") - -(defun clojure-mode-report-bug () - "Report a bug in your default browser." - (interactive) - (browse-url clojure-mode-report-bug-url)) - -(defconst clojure-guides-base-url "https://clojure.org/guides/" - "The base URL for official Clojure guides.") - -(defconst clojure-guides '(("Getting Started" . "getting_started") - ("FAQ" . "faq") - ("spec" . "spec") - ("Destructuring" . "destructuring") - ("Threading Macros" . "threading_macros") - ("Comparators" . "comparators") - ("Reader Conditionals" . "reader_conditionals")) - "A list of all official Clojure guides.") - -(defun clojure-view-guide () - "Open a Clojure guide in your default browser. - -The command will prompt you to select one of the available guides." - (interactive) - (let ((guide (completing-read "Select a guide: " (mapcar #'car clojure-guides)))) - (when guide - (let ((guide-url (concat clojure-guides-base-url (cdr (assoc guide clojure-guides))))) - (browse-url guide-url))))) - -(defconst clojure-reference-base-url "https://clojure.org/reference/" - "The base URL for the official Clojure reference.") - -(defconst clojure-reference-sections '(("The Reader" . "reader") - ("The REPL and main" . "repl_and_main") - ("Evaluation" . "evaluation") - ("Special Forms" . "special_forms") - ("Macros" . "macros") - ("Other Functions" . "other_functions") - ("Data Structures" . "data_structures") - ("Datatypes" . "datatypes") - ("Sequences" . "sequences") - ("Transients" . "transients") - ("Transducers" . "transducers") - ("Multimethods and Hierarchies" . "multimethods") - ("Protocols" . "protocols") - ("Metadata" . "metadata") - ("Namespaces" . "namespaces") - ("Libs" . "libs") - ("Vars and Environments" . "vars") - ("Refs and Transactions" . "refs") - ("Agents" . "agents") - ("Atoms" . "atoms") - ("Reducers" . "reducers") - ("Java Interop" . "java_interop") - ("Compilation and Class Generation" . "compilation") - ("Other Libraries" . "other_libraries") - ("Differences with Lisps" . "lisps"))) - -(defun clojure-view-reference-section () - "Open a Clojure reference section in your default browser. - -The command will prompt you to select one of the available sections." - (interactive) - (let ((section (completing-read "Select a reference section: " (mapcar #'car clojure-reference-sections)))) - (when section - (let ((section-url (concat clojure-reference-base-url (cdr (assoc section clojure-reference-sections))))) - (browse-url section-url))))) - -(defconst clojure-cheatsheet-url "http://clojure.org/api/cheatsheet" - "The URL of the official Clojure cheatsheet.") - -(defun clojure-view-cheatsheet () - "Open the Clojure cheatsheet in your default browser." - (interactive) - (browse-url clojure-cheatsheet-url)) - -(defconst clojure-grimoire-url "https://www.conj.io/" - "The URL of the Grimoire community documentation site.") - -(defun clojure-view-grimoire () - "Open the Clojure Grimoire in your default browser." - (interactive) - (browse-url clojure-grimoire-url)) - -(defconst clojure-style-guide-url "https://github.com/bbatsov/clojure-style-guide" - "The URL of the Clojure style guide.") - -(defun clojure-view-style-guide () - "Open the Clojure style guide in your default browser." - (interactive) - (browse-url clojure-style-guide-url)) - -(defun clojure-space-for-delimiter-p (endp delim) - "Prevent paredit from inserting useless spaces. -See `paredit-space-for-delimiter-predicates' for the meaning of -ENDP and DELIM." - (or endp - (not (memq delim '(?\" ?{ ?\( ))) - (not (or (derived-mode-p 'clojure-mode) - (derived-mode-p 'cider-repl-mode))) - (save-excursion - (backward-char) - (cond ((eq (char-after) ?#) - (and (not (bobp)) - (or (char-equal ?w (char-syntax (char-before))) - (char-equal ?_ (char-syntax (char-before)))))) - ((and (eq delim ?\() - (eq (char-after) ??) - (eq (char-before) ?#)) - nil) - (t))))) - -(defun clojure-no-space-after-tag (endp delimiter) - "Prevent inserting a space after a reader-literal tag? - -When a reader-literal tag is followed be an opening delimiter -listed in `clojure-omit-space-between-tag-and-delimiters', this -function returns t. - -This allows you to write things like #db/id[:db.part/user] -without inserting a space between the tag and the opening -bracket. - -See `paredit-space-for-delimiter-predicates' for the meaning of -ENDP and DELIMITER." - (if endp - t - (or (not (member delimiter clojure-omit-space-between-tag-and-delimiters)) - (save-excursion - (let ((orig-point (point))) - (not (and (re-search-backward - "#\\([a-zA-Z0-9._-]+/\\)?[a-zA-Z0-9._-]+" - (line-beginning-position) - t) - (= orig-point (match-end 0))))))))) - -(declare-function paredit-open-curly "ext:paredit") -(declare-function paredit-close-curly "ext:paredit") -(declare-function paredit-convolute-sexp "ext:paredit") - -(defun clojure--replace-let-bindings-and-indent (orig-fun &rest args) - "Advise `paredit-convolute-sexp' to replace s-expressions with their bound name if a let form was convoluted." - (save-excursion - (backward-sexp) - (when (looking-back clojure--let-regexp) - (clojure--replace-sexps-with-bindings-and-indent)))) - -(defun clojure-paredit-setup (&optional keymap) - "Make \"paredit-mode\" play nice with `clojure-mode'. - -If an optional KEYMAP is passed the changes are applied to it, -instead of to `clojure-mode-map'. -Also advice `paredit-convolute-sexp' when used on a let form as drop in -replacement for `cljr-expand-let`." - (when (>= paredit-version 21) - (let ((keymap (or keymap clojure-mode-map))) - (define-key keymap "{" #'paredit-open-curly) - (define-key keymap "}" #'paredit-close-curly)) - (add-to-list 'paredit-space-for-delimiter-predicates - #'clojure-space-for-delimiter-p) - (add-to-list 'paredit-space-for-delimiter-predicates - #'clojure-no-space-after-tag) - (advice-add 'paredit-convolute-sexp :after #'clojure--replace-let-bindings-and-indent))) - -(defun clojure-mode-variables () - "Set up initial buffer-local variables for Clojure mode." - (add-to-list 'imenu-generic-expression '(nil clojure-match-next-def 0)) - (setq-local indent-tabs-mode nil) - (setq-local paragraph-ignore-fill-prefix t) - (setq-local outline-regexp ";;;\\(;* [^ \t\n]\\)\\|(") - (setq-local outline-level 'lisp-outline-level) - (setq-local comment-start ";") - (setq-local comment-start-skip ";+ *") - (setq-local comment-add 1) ; default to `;;' in comment-region - (setq-local comment-column 40) - (setq-local comment-use-syntax t) - (setq-local multibyte-syntax-as-symbol t) - (setq-local electric-pair-skip-whitespace 'chomp) - (setq-local electric-pair-open-newline-between-pairs nil) - (setq-local fill-paragraph-function #'clojure-fill-paragraph) - (setq-local adaptive-fill-function #'clojure-adaptive-fill-function) - (setq-local normal-auto-fill-function #'clojure-auto-fill-function) - (setq-local comment-start-skip - "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *") - (setq-local indent-line-function #'clojure-indent-line) - (setq-local indent-region-function #'clojure-indent-region) - (setq-local lisp-indent-function #'clojure-indent-function) - (setq-local lisp-doc-string-elt-property 'clojure-doc-string-elt) - (setq-local clojure-expected-ns-function #'clojure-expected-ns) - (setq-local parse-sexp-ignore-comments t) - (setq-local prettify-symbols-alist clojure--prettify-symbols-alist) - (setq-local open-paren-in-column-0-is-defun-start nil)) - -;;;###autoload -(define-derived-mode clojure-mode prog-mode "Clojure" - "Major mode for editing Clojure code. - -\\{clojure-mode-map}" - (clojure-mode-variables) - (clojure-font-lock-setup) - (add-hook 'paredit-mode-hook #'clojure-paredit-setup)) - -(defcustom clojure-verify-major-mode t - "If non-nil, warn when activating the wrong major-mode." - :type 'boolean - :safe #'booleanp - :package-version '(clojure-mode "5.3.0")) - -(defun clojure--check-wrong-major-mode () - "Check if the current major-mode matches the file extension. -If it doesn't, issue a warning if `clojure-verify-major-mode' is -non-nil." - (when (and clojure-verify-major-mode - (stringp (buffer-file-name))) - (let* ((case-fold-search t) - (problem (cond ((and (string-match "\\.clj\\'" (buffer-file-name)) - (not (eq major-mode 'clojure-mode))) - 'clojure-mode) - ((and (string-match "\\.cljs\\'" (buffer-file-name)) - (not (eq major-mode 'clojurescript-mode))) - 'clojurescript-mode) - ((and (string-match "\\.cljc\\'" (buffer-file-name)) - (not (eq major-mode 'clojurec-mode))) - 'clojurec-mode) - ((and (string-match "\\.cljx\\'" (buffer-file-name)) - (not (eq major-mode 'clojurex-mode))) - 'clojurex-mode)))) - (when problem - (message "[WARNING] %s activated `%s' instead of `%s' in this buffer. -This could cause problems. -\(See `clojure-verify-major-mode' to disable this message.)" - (if (eq major-mode real-this-command) - "You have" - "Something in your configuration") - major-mode - problem))))) - -(add-hook 'clojure-mode-hook #'clojure--check-wrong-major-mode) - -(defsubst clojure-in-docstring-p () - "Check whether point is in a docstring." - (eq (get-text-property (point) 'face) 'font-lock-doc-face)) - -(defsubst clojure-docstring-fill-prefix () - "The prefix string used by `clojure-fill-paragraph'. - -It is simply `clojure-docstring-fill-prefix-width' number of spaces." - (make-string clojure-docstring-fill-prefix-width ? )) - -(defun clojure-adaptive-fill-function () - "Clojure adaptive fill function. -This only takes care of filling docstring correctly." - (when (clojure-in-docstring-p) - (clojure-docstring-fill-prefix))) - -(defun clojure-fill-paragraph (&optional justify) - "Like `fill-paragraph', but can handle Clojure docstrings. - -If JUSTIFY is non-nil, justify as well as fill the paragraph." - (if (clojure-in-docstring-p) - (let ((paragraph-start - (concat paragraph-start - "\\|\\s-*\\([(;:\"[]\\|~@\\|`(\\|#'(\\)")) - (paragraph-separate - (concat paragraph-separate "\\|\\s-*\".*[,\\.]$")) - (fill-column (or clojure-docstring-fill-column fill-column)) - (fill-prefix (clojure-docstring-fill-prefix))) - (fill-paragraph justify)) - (let ((paragraph-start (concat paragraph-start - "\\|\\s-*\\([(;:\"[]\\|`(\\|#'(\\)")) - (paragraph-separate - (concat paragraph-separate "\\|\\s-*\".*[,\\.[]$"))) - (or (fill-comment-paragraph justify) - (fill-paragraph justify)) - ;; Always return `t' - t))) - -(defun clojure-auto-fill-function () - "Clojure auto-fill function." - ;; Check if auto-filling is meaningful. - (let ((fc (current-fill-column))) - (when (and fc (> (current-column) fc)) - (let ((fill-column (if (clojure-in-docstring-p) - clojure-docstring-fill-column - fill-column)) - (fill-prefix (clojure-adaptive-fill-function))) - (do-auto-fill))))) - - -;;; #_ comments font-locking -;; Code heavily borrowed from Slime. -;; https://github.com/slime/slime/blob/master/contrib/slime-fontifying-fu.el#L186 -(defvar clojure--comment-macro-regexp - (rx "#_" (* " ") (group-n 1 (not (any " ")))) - "Regexp matching the start of a comment sexp. -The beginning of match-group 1 should be before the sexp to be -marked as a comment. The end of sexp is found with -`clojure-forward-logical-sexp'. - -By default, this only applies to code after the `#_' reader -macro. In order to also font-lock the `(comment ...)' macro as a -comment, you can set the value to: - \"#_ *\\\\(?1:[^ ]\\\\)\\\\|\\\\(?1:(comment\\\\_>\\\\)\"") - -(defun clojure--search-comment-macro-internal (limit) - (when (search-forward-regexp clojure--comment-macro-regexp limit t) - (let* ((md (match-data)) - (start (match-beginning 1)) - (state (syntax-ppss start))) - ;; inside string or comment? - (if (or (nth 3 state) - (nth 4 state)) - (clojure--search-comment-macro-internal limit) - (goto-char start) - (clojure-forward-logical-sexp 1) - ;; Data for (match-end 1). - (setf (elt md 3) (point)) - (set-match-data md) - t)))) - -(defun clojure--search-comment-macro (limit) - "Find comment macros and set the match data. -Search from point up to LIMIT. The region that should be -considered a comment is between `(match-beginning 1)' -and `(match-end 1)'." - (let ((result 'retry)) - (while (and (eq result 'retry) (<= (point) limit)) - (condition-case nil - (setq result (clojure--search-comment-macro-internal limit)) - (end-of-file (setq result nil)) - (scan-error (setq result 'retry)))) - result)) - - -;;; General font-locking -(defun clojure-match-next-def () - "Scans the buffer backwards for the next \"top-level\" definition. -Called by `imenu--generic-function'." - ;; we have to take into account namespace-definition forms - ;; e.g. s/defn - (when (re-search-backward "^(\\([a-z0-9.-]+/\\)?def\\sw*" nil t) - (save-excursion - (let (found? - (start (point))) - (down-list) - (forward-sexp) - (while (not found?) - (forward-sexp) - (or (if (char-equal ?[ (char-after (point))) - (backward-sexp)) - (if (char-equal ?) (char-after (point))) - (backward-sexp))) - (cl-destructuring-bind (def-beg . def-end) (bounds-of-thing-at-point 'sexp) - (if (char-equal ?^ (char-after def-beg)) - (progn (forward-sexp) (backward-sexp)) - (setq found? t) - (set-match-data (list def-beg def-end))))) - (goto-char start))))) - -(eval-and-compile - (defconst clojure--sym-forbidden-rest-chars "][\";\'@\\^`~\(\)\{\}\\,\s\t\n\r" - "A list of chars that a Clojure symbol cannot contain. -See definition of 'macros': URL `http://git.io/vRGLD'.") - (defconst clojure--sym-forbidden-1st-chars (concat clojure--sym-forbidden-rest-chars "0-9:") - "A list of chars that a Clojure symbol cannot start with. -See the for-loop: URL `http://git.io/vRGTj' lines: URL -`http://git.io/vRGIh', URL `http://git.io/vRGLE' and value -definition of 'macros': URL `http://git.io/vRGLD'.") - (defconst clojure--sym-regexp - (concat "[^" clojure--sym-forbidden-1st-chars "][^" clojure--sym-forbidden-rest-chars "]*") - "A regexp matching a Clojure symbol or namespace alias. -Matches the rule `clojure--sym-forbidden-1st-chars' followed by -any number of matches of `clojure--sym-forbidden-rest-chars'.")) - -(defconst clojure-font-lock-keywords - (eval-when-compile - `( ;; Top-level variable definition - (,(concat "(\\(?:clojure.core/\\)?\\(" - (regexp-opt '("def" "defonce")) - ;; variable declarations - "\\)\\>" - ;; Any whitespace - "[ \r\n\t]*" - ;; Possibly type or metadata - "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" - "\\(\\sw+\\)?") - (1 font-lock-keyword-face) - (2 font-lock-variable-name-face nil t)) - ;; Type definition - (,(concat "(\\(?:clojure.core/\\)?\\(" - (regexp-opt '("defstruct" "deftype" "defprotocol" - "defrecord")) - ;; type declarations - "\\)\\>" - ;; Any whitespace - "[ \r\n\t]*" - ;; Possibly type or metadata - "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" - "\\(\\sw+\\)?") - (1 font-lock-keyword-face) - (2 font-lock-type-face nil t)) - ;; Function definition (anything that starts with def and is not - ;; listed above) - (,(concat "(\\(?:" clojure--sym-regexp "/\\)?" - "\\(def[^ \r\n\t]*\\)" - ;; Function declarations - "\\>" - ;; Any whitespace - "[ \r\n\t]*" - ;; Possibly type or metadata - "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" - "\\(\\sw+\\)?") - (1 font-lock-keyword-face) - (2 font-lock-function-name-face nil t)) - ;; (fn name? args ...) - (,(concat "(\\(?:clojure.core/\\)?\\(fn\\)[ \t]+" - ;; Possibly type - "\\(?:#?^\\sw+[ \t]*\\)?" - ;; Possibly name - "\\(\\sw+\\)?" ) - (1 font-lock-keyword-face) - (2 font-lock-function-name-face nil t)) - ;; lambda arguments - %, %&, %1, %2, etc - ("\\<%[&1-9]?" (0 font-lock-variable-name-face)) - ;; Special forms - (,(concat - "(" - (regexp-opt - '("def" "do" "if" "let" "let*" "var" "fn" "fn*" "loop" "loop*" - "recur" "throw" "try" "catch" "finally" - "set!" "new" "." - "monitor-enter" "monitor-exit" "quote") t) - "\\>") - 1 font-lock-keyword-face) - ;; Built-in binding and flow of control forms - (,(concat - "(\\(?:clojure.core/\\)?" - (regexp-opt - '("letfn" "case" "cond" "cond->" "cond->>" "condp" - "for" "when" "when-not" "when-let" "when-first" "when-some" - "if-let" "if-not" "if-some" - ".." "->" "->>" "as->" "doto" "and" "or" - "dosync" "doseq" "dotimes" "dorun" "doall" - "ns" "in-ns" - "with-open" "with-local-vars" "binding" - "with-redefs" "with-redefs-fn" - "declare") t) - "\\>") - 1 font-lock-keyword-face) - ;; Macros similar to let, when, and while - (,(rx symbol-start - (or "let" "when" "while") "-" - (1+ (or (syntax word) (syntax symbol))) - symbol-end) - 0 font-lock-keyword-face) - (,(concat - "\\<" - (regexp-opt - '("*1" "*2" "*3" "*agent*" - "*allow-unresolved-vars*" "*assert*" "*clojure-version*" - "*command-line-args*" "*compile-files*" - "*compile-path*" "*data-readers*" "*default-data-reader-fn*" - "*e" "*err*" "*file*" "*flush-on-newline*" - "*in*" "*macro-meta*" "*math-context*" "*ns*" "*out*" - "*print-dup*" "*print-length*" "*print-level*" - "*print-meta*" "*print-readably*" - "*read-eval*" "*source-path*" - "*unchecked-math*" - "*use-context-classloader*" "*warn-on-reflection*") - t) - "\\>") - 0 font-lock-builtin-face) - ;; Dynamic variables - *something* or @*something* - ("\\(?:\\<\\|/\\)@?\\(\\*[a-z-]*\\*\\)\\>" 1 font-lock-variable-name-face) - ;; Global constants - nil, true, false - (,(concat - "\\<" - (regexp-opt - '("true" "false" "nil") t) - "\\>") - 0 font-lock-constant-face) - ;; Character literals - \1, \a, \newline, \u0000 - ("\\\\\\([[:punct:]]\\|[a-z0-9]+\\>\\)" 0 'clojure-character-face) - ;; foo/ Foo/ @Foo/ /FooBar - (,(concat "\\(?:\\<:?\\|\\.\\)@?\\(" clojure--sym-regexp "\\)\\(/\\)") - (1 font-lock-type-face) (2 'default)) - ;; Constant values (keywords), including as metadata e.g. ^:static - ("\\<^?\\(:\\(\\sw\\|\\s_\\)+\\(\\>\\|\\_>\\)\\)" 1 'clojure-keyword-face append) - ;; Java interop highlighting - ;; CONST SOME_CONST (optionally prefixed by /) - ("\\(?:\\<\\|/\\)\\([A-Z]+\\|\\([A-Z]+_[A-Z1-9_]+\\)\\)\\>" 1 font-lock-constant-face) - ;; .foo .barBaz .qux01 .-flibble .-flibbleWobble - ("\\<\\.-?[a-z][a-zA-Z0-9]*\\>" 0 'clojure-interop-method-face) - ;; Foo Bar$Baz Qux_ World_OpenUDP Foo. Babylon15. - ("\\(?:\\<\\|\\.\\|/\\|#?^\\)\\([A-Z][a-zA-Z0-9_]*[a-zA-Z0-9$_]+\\.?\\>\\)" 1 font-lock-type-face) - ;; foo.bar.baz - ("\\<^?\\([a-z][a-z0-9_-]+\\.\\([a-z][a-z0-9_-]*\\.?\\)+\\)" 1 font-lock-type-face) - ;; (ns namespace) - special handling for single segment namespaces - (,(concat "(\\[ \r\n\t]*" - ;; Possibly metadata - "\\(?:\\^?{[^}]+}[ \r\n\t]*\\)*" - ;; namespace - "\\([a-z0-9-]+\\)") - (1 font-lock-type-face nil t)) - ;; fooBar - ("\\(?:\\<\\|/\\)\\([a-z]+[A-Z]+[a-zA-Z0-9$]*\\>\\)" 1 'clojure-interop-method-face) - ;; #_ and (comment ...) macros. - (clojure--search-comment-macro 1 font-lock-comment-face t) - ;; Highlight `code` marks, just like `elisp'. - (,(rx "`" (group-n 1 (optional "#'") - (+ (or (syntax symbol) (syntax word)))) "`") - (1 'font-lock-constant-face prepend)) - ;; Highlight escaped characters in strings. - (clojure-font-lock-escaped-chars 0 'bold prepend) - ;; Highlight grouping constructs in regular expressions - (clojure-font-lock-regexp-groups - (1 'font-lock-regexp-grouping-construct prepend)))) - "Default expressions to highlight in Clojure mode.") - -(defun clojure-font-lock-syntactic-face-function (state) - "Find and highlight text with a Clojure-friendly syntax table. - -This function is passed to `font-lock-syntactic-face-function', -which is called with a single parameter, STATE (which is, in -turn, returned by `parse-partial-sexp' at the beginning of the -highlighted region)." - (if (nth 3 state) - ;; This might be a (doc)string or a |...| symbol. - (let ((startpos (nth 8 state))) - (if (eq (char-after startpos) ?|) - ;; This is not a string, but a |...| symbol. - nil - (let* ((listbeg (nth 1 state)) - (firstsym (and listbeg - (save-excursion - (goto-char listbeg) - (and (looking-at "([ \t\n]*\\(\\(\\sw\\|\\s_\\)+\\)") - (match-string 1))))) - (docelt (and firstsym - (function-get (intern-soft firstsym) - lisp-doc-string-elt-property)))) - (if (and docelt - ;; It's a string in a form that can have a docstring. - ;; Check whether it's in docstring position. - (save-excursion - (when (functionp docelt) - (goto-char (match-end 1)) - (setq docelt (funcall docelt))) - (goto-char listbeg) - (forward-char 1) - (condition-case nil - (while (and (> docelt 0) (< (point) startpos) - (progn (forward-sexp 1) t)) - ;; ignore metadata and type hints - (unless (looking-at "[ \n\t]*\\(\\^[A-Z:].+\\|\\^?{.+\\)") - (setq docelt (1- docelt)))) - (error nil)) - (and (zerop docelt) (<= (point) startpos) - (progn (forward-comment (point-max)) t) - (= (point) (nth 8 state))))) - font-lock-doc-face - font-lock-string-face)))) - font-lock-comment-face)) - -(defun clojure-font-lock-setup () - "Configures font-lock for editing Clojure code." - (setq-local font-lock-multiline t) - (add-to-list 'font-lock-extend-region-functions - #'clojure-font-lock-extend-region-def t) - (setq font-lock-defaults - '(clojure-font-lock-keywords ; keywords - nil nil - (("+-*/.<>=!?$%_&:" . "w")) ; syntax alist - nil - (font-lock-mark-block-function . mark-defun) - (font-lock-syntactic-face-function - . clojure-font-lock-syntactic-face-function)))) - -(defun clojure-font-lock-def-at-point (point) - "Range between the top-most def* and the fourth element after POINT. -Note that this means that there is no guarantee of proper font -locking in def* forms that are not at top level." - (goto-char point) - (condition-case nil - (beginning-of-defun) - (error nil)) - - (let ((beg-def (point))) - (when (and (not (= point beg-def)) - (looking-at "(def")) - (condition-case nil - (progn - ;; move forward as much as possible until failure (or success) - (forward-char) - (dotimes (_ 4) - (forward-sexp))) - (error nil)) - (cons beg-def (point))))) - -(defun clojure-font-lock-extend-region-def () - "Set region boundaries to include the first four elements of def* forms." - (let ((changed nil)) - (let ((def (clojure-font-lock-def-at-point font-lock-beg))) - (when def - (cl-destructuring-bind (def-beg . def-end) def - (when (and (< def-beg font-lock-beg) - (< font-lock-beg def-end)) - (setq font-lock-beg def-beg - changed t))))) - (let ((def (clojure-font-lock-def-at-point font-lock-end))) - (when def - (cl-destructuring-bind (def-beg . def-end) def - (when (and (< def-beg font-lock-end) - (< font-lock-end def-end)) - (setq font-lock-end def-end - changed t))))) - changed)) - -(defun clojure--font-locked-as-string-p (&optional regexp) - "Non-nil if the char before point is font-locked as a string. -If REGEXP is non-nil, also check whether current string is -preceeded by a #." - (let ((face (get-text-property (1- (point)) 'face))) - (and (or (and (listp face) - (memq 'font-lock-string-face face)) - (eq 'font-lock-string-face face)) - (or (clojure-string-start t) - (unless regexp - (clojure-string-start nil)))))) - -(defun clojure-font-lock-escaped-chars (bound) - "Highlight \escaped chars in strings. -BOUND denotes a buffer position to limit the search." - (let ((found nil)) - (while (and (not found) - (re-search-forward "\\\\." bound t)) - - (setq found (clojure--font-locked-as-string-p))) - found)) - -(defun clojure-font-lock-regexp-groups (bound) - "Highlight grouping constructs in regular expression. - -BOUND denotes the maximum number of characters (relative to the -point) to check." - (let ((found nil)) - (while (and (not found) - (re-search-forward (eval-when-compile - (concat - ;; A group may start using several alternatives: - "\\(\\(?:" - ;; 1. (? special groups - "(\\?\\(?:" - ;; a) non-capturing group (?:X) - ;; b) independent non-capturing group (?>X) - ;; c) zero-width positive lookahead (?=X) - ;; d) zero-width negative lookahead (?!X) - "[:=!>]\\|" - ;; e) zero-width positive lookbehind (?<=X) - ;; f) zero-width negative lookbehind (?X) - "<[[:alnum:]]+>" - "\\)\\|" ;; end of special groups - ;; 2. normal capturing groups ( - ;; 3. we also highlight alternative - ;; separarators |, and closing parens ) - "[|()]" - "\\)\\)")) - bound t)) - (setq found (clojure--font-locked-as-string-p 'regexp))) - found)) - -;; Docstring positions -(put 'ns 'clojure-doc-string-elt 2) -(put 'def 'clojure-doc-string-elt 2) -(put 'defn 'clojure-doc-string-elt 2) -(put 'defn- 'clojure-doc-string-elt 2) -(put 'defmulti 'clojure-doc-string-elt 2) -(put 'defmacro 'clojure-doc-string-elt 2) -(put 'definline 'clojure-doc-string-elt 2) -(put 'defprotocol 'clojure-doc-string-elt 2) - -;;; Vertical alignment -(defcustom clojure-align-forms-automatically nil - "If non-nil, vertically align some forms automatically. -Automatically means it is done as part of indenting code. This -applies to binding forms (`clojure-align-binding-forms'), to cond -forms (`clojure-align-cond-forms') and to map literals. For -instance, selecting a map a hitting \\`\\[indent-for-tab-command]' -will align the values like this: - {:some-key 10 - :key2 20}" - :package-version '(clojure-mode . "5.1") - :safe #'booleanp - :type 'boolean) - -(defcustom clojure-align-binding-forms - '("let" "when-let" "when-some" "if-let" "if-some" "binding" "loop" - "doseq" "for" "with-open" "with-local-vars" "with-redefs") - "List of strings matching forms that have binding forms." - :package-version '(clojure-mode . "5.1") - :safe #'listp - :type '(repeat string)) - -(defcustom clojure-align-cond-forms '("condp" "cond" "cond->" "cond->>" "case" "are") - "List of strings identifying cond-like forms." - :package-version '(clojure-mode . "5.1") - :safe #'listp - :type '(repeat string)) - -(defun clojure--position-for-alignment () - "Non-nil if the sexp around point should be automatically aligned. -This function expects to be called immediately after an -open-brace or after the function symbol in a function call. - -First check if the sexp around point is a map literal, or is a -call to one of the vars listed in `clojure-align-cond-forms'. If -it isn't, return nil. If it is, return non-nil and place point -immediately before the forms that should be aligned. - -For instance, in a map literal point is left immediately before -the first key; while, in a let-binding, point is left inside the -binding vector and immediately before the first binding -construct." - ;; Are we in a map? - (or (and (eq (char-before) ?{) - (not (eq (char-before (1- (point))) ?\#))) - ;; Are we in a cond form? - (let* ((fun (car (member (thing-at-point 'symbol) clojure-align-cond-forms))) - (method (and fun (clojure--get-indent-method fun))) - ;; The number of special arguments in the cond form is - ;; the number of sexps we skip before aligning. - (skip (cond ((numberp method) method) - ((null method) 0) - ((sequencep method) (elt method 0))))) - (when (and fun (numberp skip)) - (clojure-forward-logical-sexp skip) - (comment-forward (point-max)) - fun)) ; Return non-nil (the var name). - ;; Are we in a let-like form? - (when (member (thing-at-point 'symbol) - clojure-align-binding-forms) - ;; Position inside the binding vector. - (clojure-forward-logical-sexp) - (backward-sexp) - (when (eq (char-after) ?\[) - (forward-char 1) - (comment-forward (point-max)) - ;; Return non-nil. - t)))) - -(defun clojure--find-sexp-to-align (end) - "Non-nil if there's a sexp ahead to be aligned before END. -Place point as in `clojure--position-for-alignment'." - ;; Look for a relevant sexp. - (let ((found)) - (while (and (not found) - (search-forward-regexp - (concat "{\\|(" (regexp-opt - (append clojure-align-binding-forms - clojure-align-cond-forms) - 'symbols)) - end 'noerror)) - - (let ((ppss (syntax-ppss))) - ;; If we're in a string or comment. - (unless (or (elt ppss 3) - (elt ppss 4)) - ;; Only stop looking if we successfully position - ;; the point. - (setq found (clojure--position-for-alignment))))) - found)) - -(defun clojure--search-whitespace-after-next-sexp (&optional bound _noerror) - "Move point after all whitespace after the next sexp. -Set the match data group 1 to be this region of whitespace and -return point." - (unwind-protect - (ignore-errors - (clojure-forward-logical-sexp 1) - (search-forward-regexp "\\([,\s\t]*\\)" bound) - (pcase (syntax-after (point)) - ;; End-of-line, try again on next line. - (`(12) (clojure--search-whitespace-after-next-sexp bound)) - ;; Closing paren, stop here. - (`(5 . ,_) nil) - ;; Anything else is something to align. - (_ (point)))) - (when (and bound (> (point) bound)) - (goto-char bound)))) - -(defun clojure-align (beg end) - "Vertically align the contents of the sexp around point. -If region is active, align it. Otherwise, align everything in the -current \"top-level\" sexp. -When called from lisp code align everything between BEG and END." - (interactive (if (use-region-p) - (list (region-beginning) (region-end)) - (save-excursion - (let ((end (progn (end-of-defun) - (point)))) - (clojure-backward-logical-sexp) - (list (point) end))))) - (setq end (copy-marker end)) - (save-excursion - (goto-char beg) - (while (clojure--find-sexp-to-align end) - (let ((sexp-end (save-excursion - (backward-up-list) - (forward-sexp 1) - (point-marker))) - (clojure-align-forms-automatically nil) - (count 1)) - ;; For some bizarre reason, we need to `align-region' once for each - ;; group. - (save-excursion - (while (search-forward-regexp "^ *\n" sexp-end 'noerror) - (cl-incf count))) - (dotimes (_ count) - (align-region (point) sexp-end nil - '((clojure-align (regexp . clojure--search-whitespace-after-next-sexp) - (group . 1) - (separate . "^ *$") - (repeat . t))) - nil)) - ;; Reindent after aligning because of #360. - (indent-region (point) sexp-end))))) - -;;; Indentation -(defun clojure-indent-region (beg end) - "Like `indent-region', but also maybe align forms. -Forms between BEG and END are aligned according to -`clojure-align-forms-automatically'." - (prog1 (let ((indent-region-function nil)) - (indent-region beg end)) - (when clojure-align-forms-automatically - (condition-case nil - (clojure-align beg end) - (scan-error nil))))) - -(defun clojure-indent-line () - "Indent current line as Clojure code." - (if (clojure-in-docstring-p) - (save-excursion - (beginning-of-line) - (when (and (looking-at "^\\s-*") - (<= (string-width (match-string-no-properties 0)) - (string-width (clojure-docstring-fill-prefix)))) - (replace-match (clojure-docstring-fill-prefix)))) - (lisp-indent-line))) - -(defvar clojure-get-indent-function nil - "Function to get the indent spec of a symbol. -This function should take one argument, the name of the symbol as -a string. This name will be exactly as it appears in the buffer, -so it might start with a namespace alias. - -This function is analogous to the `clojure-indent-function' -symbol property, and its return value should match one of the -allowed values of this property. See `clojure-indent-function' -for more information.") - -(defun clojure--get-indent-method (function-name) - "Return the indent spec for the symbol named FUNCTION-NAME. -FUNCTION-NAME is a string. If it contains a `/', also try only -the part after the `/'. - -Look for a spec using `clojure-get-indent-function', then try the -`clojure-indent-function' and `clojure-backtracking-indent' -symbol properties." - (or (when (functionp clojure-get-indent-function) - (funcall clojure-get-indent-function function-name)) - (get (intern-soft function-name) 'clojure-indent-function) - (get (intern-soft function-name) 'clojure-backtracking-indent) - (when (string-match "/\\([^/]+\\)\\'" function-name) - (or (get (intern-soft (match-string 1 function-name)) - 'clojure-indent-function) - (get (intern-soft (match-string 1 function-name)) - 'clojure-backtracking-indent))) - (when (string-match (rx (or "let" "when" "while") (syntax symbol)) - function-name) - (clojure--get-indent-method (substring (match-string 0 function-name) 0 -1))))) - -(defvar clojure--current-backtracking-depth 0) - -(defun clojure--find-indent-spec-backtracking () - "Return the indent sexp that applies to the sexp at point. -Implementation function for `clojure--find-indent-spec'." - (when (and (>= clojure-max-backtracking clojure--current-backtracking-depth) - (not (looking-at "^"))) - (let ((clojure--current-backtracking-depth (1+ clojure--current-backtracking-depth)) - (pos 0)) - ;; Count how far we are from the start of the sexp. - (while (ignore-errors (clojure-backward-logical-sexp 1) - (not (or (bobp) - (eq (char-before) ?\n)))) - (cl-incf pos)) - (let* ((function (thing-at-point 'symbol)) - (method (or (when function ;; Is there a spec here? - (clojure--get-indent-method function)) - (ignore-errors - ;; Otherwise look higher up. - (pcase (syntax-ppss) - (`(,(pred (< 0)) ,start . ,_) - (goto-char start) - (clojure--find-indent-spec-backtracking))))))) - (when (numberp method) - (setq method (list method))) - (pcase method - ((pred functionp) - (when (= pos 0) - method)) - ((pred sequencep) - (pcase (length method) - (`0 nil) - (`1 (let ((head (elt method 0))) - (when (or (= pos 0) (sequencep head)) - head))) - (l (if (>= pos l) - (elt method (1- l)) - (elt method pos))))) - ((or `defun `:defn) - (when (= pos 0) - :defn)) - (_ - (message "Invalid indent spec for `%s': %s" function method) - nil)))))) - -(defun clojure--find-indent-spec () - "Return the indent spec that applies to current sexp. -If `clojure-use-backtracking-indent' is non-nil, also do -backtracking up to a higher-level sexp in order to find the -spec." - (if clojure-use-backtracking-indent - (save-excursion - (clojure--find-indent-spec-backtracking)) - (let ((function (thing-at-point 'symbol))) - (clojure--get-indent-method function)))) - -(defun clojure--normal-indent (last-sexp indent-mode) - "Return the normal indentation column for a sexp. -Point should be after the open paren of the _enclosing_ sexp, and -LAST-SEXP is the start of the previous sexp (immediately before -the sexp being indented). INDENT-MODE is any of the values -accepted by `clojure-indent-style'." - (goto-char last-sexp) - (forward-sexp 1) - (clojure-backward-logical-sexp 1) - (let ((last-sexp-start nil)) - (if (ignore-errors - ;; `backward-sexp' until we reach the start of a sexp that is the - ;; first of its line (the start of the enclosing sexp). - (while (string-match - "[^[:blank:]]" - (buffer-substring (line-beginning-position) (point))) - (setq last-sexp-start (prog1 (point) - (forward-sexp -1)))) - t) - ;; Here we have found an arg before the arg we're indenting which is at - ;; the start of a line. Every mode simply aligns on this case. - (current-column) - ;; Here we have reached the start of the enclosing sexp (point is now at - ;; the function name), so the behaviour depends on INDENT-MODE and on - ;; whether there's also an argument on this line (case A or B). - (let ((case-a ; The meaning of case-a is explained in `clojure-indent-style'. - (and last-sexp-start - (< last-sexp-start (line-end-position))))) - (cond - ;; For compatibility with the old `clojure-defun-style-default-indent', any - ;; value other than these 3 is equivalent to `always-body'. - ((not (memq indent-mode '(:always-align :align-arguments nil))) - (+ (current-column) lisp-body-indent -1)) - ;; There's an arg after the function name, so align with it. - (case-a (goto-char last-sexp-start) - (current-column)) - ;; Not same line. - ((eq indent-mode :align-arguments) - (+ (current-column) lisp-body-indent -1)) - ;; Finally, just align with the function name. - (t (current-column))))))) - -(defun clojure--not-function-form-p () - "Non-nil if form at point doesn't represent a function call." - (or (member (char-after) '(?\[ ?\{)) - (save-excursion ;; Catch #?@ (:cljs ...) - (skip-chars-backward "\r\n[:blank:]") - (when (eq (char-before) ?@) - (forward-char -1)) - (and (eq (char-before) ?\?) - (eq (char-before (1- (point))) ?\#))) - ;; Car of form is not a symbol. - (not (looking-at ".\\(?:\\sw\\|\\s_\\)")))) - -;; Check the general context, and provide indentation for data structures and -;; special macros. If current form is a function (or non-special macro), -;; delegate indentation to `clojure--normal-indent'. -(defun clojure-indent-function (indent-point state) - "When indenting a line within a function call, indent properly. - -INDENT-POINT is the position where the user typed TAB, or equivalent. -Point is located at the point to indent under (for default indentation); -STATE is the `parse-partial-sexp' state for that position. - -If the current line is in a call to a Clojure function with a -non-nil property `clojure-indent-function', that specifies how to do -the indentation. - -The property value can be - -- `defun', meaning indent `defun'-style; -- an integer N, meaning indent the first N arguments specially - like ordinary function arguments and then indent any further - arguments like a body; -- a function to call just as this function was called. - If that function returns nil, that means it doesn't specify - the indentation. -- a list, which is used by `clojure-backtracking-indent'. - -This function also returns nil meaning don't specify the indentation." - ;; Goto to the open-paren. - (goto-char (elt state 1)) - ;; Maps, sets, vectors and reader conditionals. - (if (clojure--not-function-form-p) - (1+ (current-column)) - ;; Function or macro call. - (forward-char 1) - (let ((method (clojure--find-indent-spec)) - (last-sexp calculate-lisp-indent-last-sexp) - (containing-form-column (1- (current-column)))) - (pcase method - ((or (pred integerp) `(,method)) - (let ((pos -1)) - (condition-case nil - (while (and (<= (point) indent-point) - (not (eobp))) - (clojure-forward-logical-sexp 1) - (cl-incf pos)) - ;; If indent-point is _after_ the last sexp in the - ;; current sexp, we detect that by catching the - ;; `scan-error'. In that case, we should return the - ;; indentation as if there were an extra sexp at point. - (scan-error (cl-incf pos))) - (cond - ;; The first non-special arg. Rigidly reduce indentation. - ((= pos (1+ method)) - (+ lisp-body-indent containing-form-column)) - ;; Further non-special args, align with the arg above. - ((> pos (1+ method)) - (clojure--normal-indent last-sexp :always-align)) - ;; Special arg. Rigidly indent with a large indentation. - (t - (+ (* 2 lisp-body-indent) containing-form-column))))) - (`:defn - (+ lisp-body-indent containing-form-column)) - ((pred functionp) - (funcall method indent-point state)) - ;; No indent spec, do the default. - (`nil - (let ((function (thing-at-point 'symbol))) - (cond - ;; Preserve useful alignment of :require (and friends) in `ns' forms. - ((and function (string-match "^:" function)) - (clojure--normal-indent last-sexp :always-align)) - ;; This is should be identical to the :defn above. - ((and function - (string-match "\\`\\(?:\\S +/\\)?\\(def[a-z]*\\|with-\\)" - function) - (not (string-match "\\`default" (match-string 1 function)))) - (+ lisp-body-indent containing-form-column)) - ;; Finally, nothing special here, just respect the user's - ;; preference. - (t (clojure--normal-indent last-sexp clojure-indent-style))))))))) - -;;; Setting indentation -(defun put-clojure-indent (sym indent) - "Instruct `clojure-indent-function' to indent the body of SYM by INDENT." - (put sym 'clojure-indent-function indent)) - -(defmacro define-clojure-indent (&rest kvs) - "Call `put-clojure-indent' on a series, KVS." - `(progn - ,@(mapcar (lambda (x) `(put-clojure-indent - (quote ,(car x)) ,(cadr x))) - kvs))) - -(defun add-custom-clojure-indents (name value) - "Allow `clojure-defun-indents' to indent user-specified macros. - -Requires the macro's NAME and a VALUE." - (custom-set-default name value) - (mapcar (lambda (x) - (put-clojure-indent x 'defun)) - value)) - -(defcustom clojure-defun-indents nil - "List of additional symbols with defun-style indentation in Clojure. - -You can use this to let Emacs indent your own macros the same way -that it indents built-in macros like with-open. This variable -only works when set via the customize interface (`setq' won't -work). To set it from Lisp code, use - (put-clojure-indent \\='some-symbol :defn)." - :type '(repeat symbol) - :set 'add-custom-clojure-indents) - -(define-clojure-indent - ;; built-ins - (ns 1) - (fn :defn) - (def :defn) - (defn :defn) - (bound-fn :defn) - (if 1) - (if-not 1) - (case 1) - (cond 0) - (condp 2) - (cond-> 1) - (cond->> 1) - (when 1) - (while 1) - (when-not 1) - (when-first 1) - (do 0) - (future 0) - (comment 0) - (doto 1) - (locking 1) - (proxy '(2 nil nil (:defn))) - (as-> 2) - - (reify '(:defn (1))) - (deftype '(2 nil nil (:defn))) - (defrecord '(2 nil nil (:defn))) - (defprotocol '(1 (:defn))) - (definterface '(1 (:defn))) - (extend 1) - (extend-protocol '(1 :defn)) - (extend-type '(1 :defn)) - ;; specify and specify! are from ClojureScript - (specify '(1 :defn)) - (specify! '(1 :defn)) - (try 0) - (catch 2) - (finally 0) - - ;; binding forms - (let 1) - (letfn '(1 ((:defn)) nil)) - (binding 1) - (loop 1) - (for 1) - (doseq 1) - (dotimes 1) - (when-let 1) - (if-let 1) - (when-some 1) - (if-some 1) - (this-as 1) ; ClojureScript - - (defmethod :defn) - - ;; clojure.test - (testing 1) - (deftest :defn) - (are 2) - (use-fixtures :defn) - - ;; core.logic - (run :defn) - (run* :defn) - (fresh :defn) - - ;; core.async - (alt! 0) - (alt!! 0) - (go 0) - (go-loop 1) - (thread 0)) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Better docstring filling for clojure-mode -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun clojure-string-start (&optional regex) - "Return the position of the \" that begins the string at point. -If REGEX is non-nil, return the position of the # that begins the -regex at point. If point is not inside a string or regex, return -nil." - (when (nth 3 (syntax-ppss)) ;; Are we really in a string? - (save-excursion - (save-match-data - ;; Find a quote that appears immediately after whitespace, - ;; beginning of line, hash, or an open paren, brace, or bracket - (re-search-backward "\\(\\s-\\|^\\|#\\|(\\|\\[\\|{\\)\\(\"\\)") - (let ((beg (match-beginning 2))) - (when beg - (if regex - (and (char-before beg) (eq ?# (char-before beg)) (1- beg)) - (when (not (eq ?# (char-before beg))) - beg)))))))) - -(defun clojure-char-at-point () - "Return the char at point or nil if at buffer end." - (when (not (= (point) (point-max))) - (buffer-substring-no-properties (point) (1+ (point))))) - -(defun clojure-char-before-point () - "Return the char before point or nil if at buffer beginning." - (when (not (= (point) (point-min))) - (buffer-substring-no-properties (point) (1- (point))))) - -(defun clojure-toggle-keyword-string () - "Convert the string or keyword at point to keyword or string." - (interactive) - (let ((original-point (point))) - (while (and (> (point) 1) - (not (equal "\"" (buffer-substring-no-properties (point) (+ 1 (point))))) - (not (equal ":" (buffer-substring-no-properties (point) (+ 1 (point)))))) - (backward-char)) - (cond - ((equal 1 (point)) - (error "Beginning of file reached, this was probably a mistake")) - ((equal "\"" (buffer-substring-no-properties (point) (+ 1 (point)))) - (insert ":" (substring (clojure-delete-and-extract-sexp) 1 -1))) - ((equal ":" (buffer-substring-no-properties (point) (+ 1 (point)))) - (insert "\"" (substring (clojure-delete-and-extract-sexp) 1) "\""))) - (goto-char original-point))) - -(defun clojure-delete-and-extract-sexp () - "Delete the surrounding sexp and return it." - (let ((begin (point))) - (forward-sexp) - (let ((result (buffer-substring begin (point)))) - (delete-region begin (point)) - result))) - - - -(defun clojure-project-dir (&optional dir-name) - "Return the absolute path to the project's root directory. - -Use `default-directory' if DIR-NAME is nil. -Return nil if not inside a project." - (let* ((dir-name (or dir-name default-directory)) - (choices (delq nil - (mapcar (lambda (fname) - (locate-dominating-file dir-name fname)) - clojure-build-tool-files)))) - (when (> (length choices) 0) - (car (sort choices #'file-in-directory-p))))) - -(defun clojure-project-relative-path (path) - "Denormalize PATH by making it relative to the project root." - (file-relative-name path (clojure-project-dir))) - - -;;; ns manipulation -(defun clojure-expected-ns (&optional path) - "Return the namespace matching PATH. - -PATH is expected to be an absolute file path. - -If PATH is nil, use the path to the file backing the current buffer." - (let* ((path (or path (file-truename (buffer-file-name)))) - (relative (clojure-project-relative-path path)) - (sans-file-type (substring relative 0 (- (length (file-name-extension path t))))) - (sans-file-sep (mapconcat 'identity (cdr (split-string sans-file-type "/")) ".")) - (sans-underscores (replace-regexp-in-string "_" "-" sans-file-sep))) - ;; Drop prefix from ns for projects with structure src/{clj,cljs,cljc} - (replace-regexp-in-string "\\`clj[scx]?\\." "" sans-underscores))) - -(defun clojure-insert-ns-form-at-point () - "Insert a namespace form at point." - (interactive) - (insert (format "(ns %s)" (funcall clojure-expected-ns-function)))) - -(defun clojure-insert-ns-form () - "Insert a namespace form at the beginning of the buffer." - (interactive) - (widen) - (goto-char (point-min)) - (clojure-insert-ns-form-at-point)) - -(defun clojure-update-ns () - "Update the namespace of the current buffer. -Useful if a file has been renamed." - (interactive) - (let ((nsname (funcall clojure-expected-ns-function))) - (when nsname - (save-excursion - (save-match-data - (if (clojure-find-ns) - (progn (replace-match nsname nil nil nil 4) - (message "ns form updated")) - (error "Namespace not found"))))))) - -(defun clojure--sort-following-sexps () - "Sort sexps between point and end of current sexp. -Comments at the start of a line are considered part of the -following sexp. Comments at the end of a line (after some other -content) are considered part of the preceding sexp." - ;; Here we're after the :require/:import symbol. - (save-restriction - (narrow-to-region (point) (save-excursion - (up-list) - (1- (point)))) - (skip-chars-forward "\r\n[:blank:]") - (sort-subr nil - (lambda () (skip-chars-forward "\r\n[:blank:]")) - ;; Move to end of current top-level thing. - (lambda () - (condition-case nil - (while t (up-list)) - (scan-error nil)) - ;; We could be inside a symbol instead of a sexp. - (unless (looking-at "\\s-\\|$") - (clojure-forward-logical-sexp)) - ;; move past comments at the end of the line. - (search-forward-regexp "$")) - ;; Move to start of ns name. - (lambda () - (comment-forward) - (skip-chars-forward "[:blank:]\n\r[(") - (clojure-forward-logical-sexp) - (forward-sexp -1) - nil) - ;; Move to end of ns name. - (lambda () - (clojure-forward-logical-sexp))) - (goto-char (point-max)) - ;; Does the last line now end in a comment? - (when (nth 4 (parse-partial-sexp (point-min) (point))) - (insert "\n")))) - -(defun clojure-sort-ns () - "Internally sort each sexp inside the ns form." - (interactive) - (comment-normalize-vars) - (if (clojure-find-ns) - (save-excursion - (goto-char (match-beginning 0)) - (redisplay) - (let ((beg (point)) - (ns)) - (forward-sexp 1) - (setq ns (buffer-substring beg (point))) - (forward-char -1) - (while (progn (forward-sexp -1) - (looking-at "(:[a-z]")) - (save-excursion - (forward-char 1) - (forward-sexp 1) - (clojure--sort-following-sexps))) - (goto-char beg) - (if (looking-at (regexp-quote ns)) - (message "ns form is already sorted") - (sleep-for 0.1) - (redisplay) - (message "ns form has been sorted") - (sleep-for 0.1)))) - (user-error "Namespace not found"))) - -(defconst clojure-namespace-name-regex - (rx line-start - "(" - (zero-or-one (group (regexp "clojure.core/"))) - (zero-or-one (submatch "in-")) - "ns" - (zero-or-one "+") - (one-or-more (any whitespace "\n")) - (zero-or-more (or (submatch (zero-or-one "#") - "^{" - (zero-or-more (not (any "}"))) - "}") - (zero-or-more "^:" - (one-or-more (not (any whitespace))))) - (one-or-more (any whitespace "\n"))) - (zero-or-one (any ":'")) ;; (in-ns 'foo) or (ns+ :user) - (group (one-or-more (not (any "()\"" whitespace))) symbol-end))) - -(defun clojure-find-ns () - "Return the namespace of the current Clojure buffer. -Return the namespace closest to point and above it. If there are -no namespaces above point, return the first one in the buffer." - (save-excursion - (save-restriction - (widen) - ;; The closest ns form above point. - (when (or (re-search-backward clojure-namespace-name-regex nil t) - ;; Or any form at all. - (and (goto-char (point-min)) - (re-search-forward clojure-namespace-name-regex nil t))) - (match-string-no-properties 4))))) - -(defconst clojure-def-type-and-name-regex - (concat "(\\(?:\\(?:\\sw\\|\\s_\\)+/\\)?" - ;; Declaration - "\\(def\\(?:\\sw\\|\\s_\\)*\\)\\>" - ;; Any whitespace - "[ \r\n\t]*" - ;; Possibly type or metadata - "\\(?:#?^\\(?:{[^}]*}\\|\\(?:\\sw\\|\\s_\\)+\\)[ \r\n\t]*\\)*" - ;; Symbol name - "\\(\\(?:\\sw\\|\\s_\\)+\\)")) - -(defun clojure-find-def () - "Find the var declaration macro and symbol name of the current form. -Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")." - (save-excursion - (unless (looking-at clojure-def-type-and-name-regex) - (beginning-of-defun)) - (when (search-forward-regexp clojure-def-type-and-name-regex nil t) - (list (match-string-no-properties 1) - (match-string-no-properties 2))))) - - -;;; Sexp navigation -(defun clojure--looking-at-non-logical-sexp () - "Return non-nil if sexp after point represents code. -Sexps that don't represent code are ^metadata or #reader.macros." - (comment-normalize-vars) - (comment-forward (point-max)) - (looking-at-p "\\^\\|#[?[:alpha:]]")) - -(defun clojure-forward-logical-sexp (&optional n) - "Move forward N logical sexps. -This will skip over sexps that don't represent objects, so that ^hints and -#reader.macros are considered part of the following sexp." - (interactive "p") - (unless n (setq n 1)) - (if (< n 0) - (clojure-backward-logical-sexp (- n)) - (let ((forward-sexp-function nil)) - (while (> n 0) - (while (clojure--looking-at-non-logical-sexp) - (forward-sexp 1)) - ;; The actual sexp - (forward-sexp 1) - (skip-chars-forward ",") - (setq n (1- n)))))) - -(defun clojure-backward-logical-sexp (&optional n) - "Move backward N logical sexps. -This will skip over sexps that don't represent objects, so that ^hints and -#reader.macros are considered part of the following sexp." - (interactive "p") - (unless n (setq n 1)) - (if (< n 0) - (clojure-forward-logical-sexp (- n)) - (let ((forward-sexp-function nil)) - (while (> n 0) - ;; The actual sexp - (backward-sexp 1) - ;; Non-logical sexps. - (while (and (not (bobp)) - (ignore-errors - (save-excursion - (backward-sexp 1) - (clojure--looking-at-non-logical-sexp)))) - (backward-sexp 1)) - (setq n (1- n)))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Refactoring support -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; Threading macros related -(defcustom clojure-thread-all-but-last nil - "Non-nil means do not thread the last expression. -This means that `clojure-thread-first-all' and -`clojure-thread-last-all' not thread the deepest sexp inside the -current sexp." - :package-version '(clojure-mode . "5.4.0") - :safe #'booleanp - :type 'boolean) - -(defun clojure--point-after (&rest actions) - "Return POINT after performing ACTIONS. - -An action is either the symbol of a function or a two element -list of (fn args) to pass to `apply''" - (save-excursion - (dolist (fn-and-args actions) - (let ((f (if (listp fn-and-args) (car fn-and-args) fn-and-args)) - (args (if (listp fn-and-args) (cdr fn-and-args) nil))) - (apply f args))) - (point))) - -(defun clojure--maybe-unjoin-line () - "Undo a `join-line' done by a threading command." - (when (get-text-property (point) 'clojure-thread-line-joined) - (remove-text-properties (point) (1+ (point)) '(clojure-thread-line-joined t)) - (insert "\n"))) - -(defun clojure--unwind-last () - (forward-sexp) - (save-excursion - (let ((beg (point)) - (contents (clojure-delete-and-extract-sexp))) - (when (looking-at " *\n") - (join-line 'following)) - (clojure--ensure-parens-around-function-names) - (let* ((sexp-beg-line (line-number-at-pos)) - (sexp-end-line (progn (forward-sexp) - (line-number-at-pos))) - (multiline-sexp-p (not (= sexp-beg-line sexp-end-line)))) - (down-list -1) - (if multiline-sexp-p - (insert "\n") - ;; `clojure--maybe-unjoin-line' only works when unwinding sexps that were - ;; threaded in the same Emacs session, but it also catches cases that - ;; `multiline-sexp-p' doesn't. - (clojure--maybe-unjoin-line)) - (insert contents)))) - (forward-char)) - -(defun clojure--ensure-parens-around-function-names () - (clojure--looking-at-non-logical-sexp) - (unless (looking-at "(") - (insert-parentheses 1) - (backward-up-list))) - -(defun clojure--unwind-first () - "Unwind a thread first macro once. -Point must be between the opening paren and the -> symbol." - (forward-sexp) - (save-excursion - (let ((contents (clojure-delete-and-extract-sexp))) - (when (looking-at " *\n") - (join-line 'following)) - (clojure--ensure-parens-around-function-names) - (down-list) - (forward-sexp) - (insert contents) - (forward-sexp -1) - (clojure--maybe-unjoin-line))) - (forward-char)) - -(defun clojure--pop-out-of-threading () - (save-excursion - (down-list 2) - (backward-up-list) - (raise-sexp))) - -(defun clojure--nothing-more-to-unwind () - (save-excursion - (let ((beg (point))) - (forward-sexp) - (down-list -1) - (backward-sexp 2) ;; the last sexp, the threading macro - (when (looking-back "(\\s-*" (line-beginning-position)) - (backward-up-list)) ;; and the paren - (= beg (point))))) - -(defun clojure--fix-sexp-whitespace (&optional move-out) - (save-excursion - (when move-out (backward-up-list)) - (let ((sexp (bounds-of-thing-at-point 'sexp))) - (clojure-indent-region (car sexp) (cdr sexp)) - (delete-trailing-whitespace (car sexp) (cdr sexp))))) - -;;;###autoload -(defun clojure-unwind () - "Unwind thread at point or above point by one level. -Return nil if there are no more levels to unwind." - (interactive) - (save-excursion - (let ((limit (save-excursion - (beginning-of-defun) - (point)))) - (ignore-errors - (when (looking-at "(") - (forward-char 1) - (forward-sexp 1))) - (search-backward-regexp "([^-]*->" limit) - (if (clojure--nothing-more-to-unwind) - (progn (clojure--pop-out-of-threading) - (clojure--fix-sexp-whitespace) - nil) - (down-list) - (prog1 (cond - ((looking-at "[^-]*->\\_>") (clojure--unwind-first)) - ((looking-at "[^-]*->>\\_>") (clojure--unwind-last))) - (clojure--fix-sexp-whitespace 'move-out)) - t)))) - -;;;###autoload -(defun clojure-unwind-all () - "Fully unwind thread at point or above point." - (interactive) - (while (clojure-unwind))) - -(defun clojure--remove-superfluous-parens () - (when (looking-at "([^ )]+)") - (delete-pair))) - -(defun clojure--thread-first () - (down-list) - (forward-symbol 1) - (unless (looking-at ")") - (let ((contents (clojure-delete-and-extract-sexp))) - (backward-up-list) - (just-one-space 0) - (save-excursion - (insert contents "\n") - (clojure--remove-superfluous-parens)) - (when (looking-at "\\s-*\n") - (join-line 'following) - (forward-char 1) - (put-text-property (point) (1+ (point)) - 'clojure-thread-line-joined t)) - t))) - -(defun clojure--thread-last () - (forward-sexp 2) - (down-list -1) - (backward-sexp) - (unless (eq (char-before) ?\() - (let ((contents (clojure-delete-and-extract-sexp))) - (just-one-space 0) - (backward-up-list) - (insert contents "\n") - (clojure--remove-superfluous-parens) - ;; cljr #255 Fix dangling parens - (forward-sexp) - (when (looking-back "^\\s-*\\()+\\)\\s-*" (line-beginning-position)) - (let ((pos (match-beginning 1))) - (put-text-property pos (1+ pos) 'clojure-thread-line-joined t)) - (join-line)) - t))) - -(defun clojure--threadable-p () - (save-excursion - (forward-symbol 1) - (looking-at "[\n\r\t ]*("))) - -;;;###autoload -(defun clojure-thread () - "Thread by one more level an existing threading macro." - (interactive) - (ignore-errors - (when (looking-at "(") - (forward-char 1) - (forward-sexp 1))) - (search-backward-regexp "([^-]*->") - (down-list) - (when (clojure--threadable-p) - (prog1 (cond - ((looking-at "[^-]*->\\_>") (clojure--thread-first)) - ((looking-at "[^-]*->>\\_>") (clojure--thread-last))) - (clojure--fix-sexp-whitespace 'move-out)))) - -(defun clojure--thread-all (first-or-last-thread but-last) - (save-excursion - (insert-parentheses 1) - (insert first-or-last-thread)) - (while (save-excursion (clojure-thread))) - (when (or but-last clojure-thread-all-but-last) - (clojure-unwind))) - -;;;###autoload -(defun clojure-thread-first-all (but-last) - "Fully thread the form at point using ->. -When BUT-LAST is passed the last expression is not threaded." - (interactive "P") - (clojure--thread-all "-> " but-last)) - -;;;###autoload -(defun clojure-thread-last-all (but-last) - "Fully thread the form at point using ->>. -When BUT-LAST is passed the last expression is not threaded." - (interactive "P") - (clojure--thread-all "->> " but-last)) - -;;; Cycling stuff - -(defcustom clojure-use-metadata-for-privacy nil - "If nil, `clojure-cycle-privacy' will use (defn- f []). -If t, it will use (defn ^:private f [])." - :package-version '(clojure-mode . "5.5.0") - :safe #'booleanp - :type 'boolean) - -;;;###autoload -(defun clojure-cycle-privacy () - "Make public the current private def, or vice-versa. -See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy" - (interactive) - (save-excursion - (ignore-errors (forward-char 7)) - (search-backward-regexp "(defn?\\(-\\| ^:private\\)?\\_>") - (if (match-string 1) - (replace-match "" nil nil nil 1) - (goto-char (match-end 0)) - (insert (if (or clojure-use-metadata-for-privacy - (equal (match-string 0) "(def")) - " ^:private" - "-"))))) - -(defun clojure--convert-collection (coll-open coll-close) - "Convert the collection at (point) by unwrapping it an wrapping it between COLL-OPEN and COLL-CLOSE." - (save-excursion - (while (and - (not (bobp)) - (not (looking-at "(\\|{\\|\\["))) - (backward-char)) - (when (or (eq ?\# (char-before)) - (eq ?\' (char-before))) - (delete-char -1)) - (when (and (bobp) - (not (memq (char-after) '(?\{ ?\( ?\[)))) - (user-error "Beginning of file reached, collection is not found")) - (insert coll-open (substring (clojure-delete-and-extract-sexp) 1 -1) coll-close))) - -;;;###autoload -(defun clojure-convert-collection-to-list () - "Convert collection at (point) to list." - (interactive) - (clojure--convert-collection "(" ")")) - -;;;###autoload -(defun clojure-convert-collection-to-quoted-list () - "Convert collection at (point) to quoted list." - (interactive) - (clojure--convert-collection "'(" ")")) - -;;;###autoload -(defun clojure-convert-collection-to-map () - "Convert collection at (point) to map." - (interactive) - (clojure--convert-collection "{" "}")) - -;;;###autoload -(defun clojure-convert-collection-to-vector () - "Convert collection at (point) to vector." - (interactive) - (clojure--convert-collection "[" "]")) - -;;;###autoload -(defun clojure-convert-collection-to-set () - "Convert collection at (point) to set." - (interactive) - (clojure--convert-collection "#{" "}")) - -(defun clojure--goto-if () - "Find the first surrounding if or if-not expression." - (when (in-string-p) - (while (or (not (looking-at "(")) - (in-string-p)) - (backward-char))) - (while (not (looking-at "\\((if \\)\\|\\((if-not \\)")) - (condition-case nil - (backward-up-list) - (scan-error (user-error "No if or if-not found"))))) - -;;;###autoload -(defun clojure-cycle-if () - "Change a surrounding if to if-not, or vice-versa. - -See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if" - (interactive) - (save-excursion - (clojure--goto-if) - (cond - ((looking-at "(if-not") - (forward-char 3) - (delete-char 4) - (forward-sexp 2) - (transpose-sexps 1)) - ((looking-at "(if") - (forward-char 3) - (insert "-not") - (forward-sexp 2) - (transpose-sexps 1))))) - -;; TODO: Remove code duplication with `clojure--goto-if'. -(defun clojure--goto-when () - "Find the first surrounding when or when-not expression." - (when (in-string-p) - (while (or (not (looking-at "(")) - (in-string-p)) - (backward-char))) - (while (not (looking-at "\\((when \\)\\|\\((when-not \\)")) - (condition-case nil - (backward-up-list) - (scan-error (user-error "No when or when-not found"))))) - -;;;###autoload -(defun clojure-cycle-when () - "Change a surrounding when to when-not, or vice-versa." - (interactive) - (save-excursion - (clojure--goto-when) - (cond - ((looking-at "(when-not") - (forward-char 9) - (delete-char -4)) - ((looking-at "(when") - (forward-char 5) - (insert "-not"))))) - -(defun clojure-cycle-not () - "Add or remove a not form around the current form." - (interactive) - (save-excursion - (condition-case nil - (backward-up-list) - (scan-error (user-error "`clojure-cycle-not' must be invoked inside a list"))) - (if (looking-back "(not ") - (progn - (delete-char -5) - (forward-sexp) - (delete-char 1)) - (insert "(not ") - (forward-sexp) - (insert ")")))) - -;;; let related stuff - -(defvar clojure--let-regexp - "\(\\(when-let\\|if-let\\|let\\)\\(\\s-*\\|\\[\\)" - "Regexp matching let like expressions, i.e. let, when-let, if-let. - -The first match-group is the let expression, the second match-group is the whitespace or the opening square bracket if no whitespace between the let expression and the bracket.") - -(defun clojure--goto-let () - "Go to the beginning of the nearest let form." - (when (in-string-p) - (while (or (not (looking-at "(")) - (in-string-p)) - (backward-char))) - (ignore-errors - (while (not (looking-at clojure--let-regexp)) - (backward-up-list))) - (looking-at clojure--let-regexp)) - -(defun clojure--inside-let-binding-p () - (ignore-errors - (save-excursion - (let ((pos (point))) - (clojure--goto-let) - (re-search-forward "\\[") - (if (< pos (point)) - nil - (forward-sexp) - (up-list) - (< pos (point))))))) - -(defun clojure--beginning-of-current-let-binding () - "Move before the bound name of the current binding. -Assume that point is in the binding form of a let." - (let ((current-point (point))) - (clojure--goto-let) - (search-forward "[") - (forward-char) - (while (> current-point (point)) - (forward-sexp)) - (backward-sexp 2))) - -(defun clojure--previous-line () - "Keep the column position while go the previous line." - (let ((col (current-column))) - (forward-line -1) - (move-to-column col))) - -(defun clojure--prepare-to-insert-new-let-binding () - "Move to right place in the let form to insert a new binding and indent." - (if (clojure--inside-let-binding-p) - (progn - (clojure--beginning-of-current-let-binding) - (newline-and-indent) - (clojure--previous-line) - (indent-for-tab-command)) - (clojure--goto-let) - (search-forward "[") - (backward-up-list) - (forward-sexp) - (down-list -1) - (backward-char) - (if (looking-at "\\[\\s-*\\]") - (forward-char) - (forward-char) - (newline-and-indent)))) - -(defun clojure--sexp-regexp (sexp) - (concat "\\([^[:word:]^-]\\)" - (mapconcat #'identity (mapcar 'regexp-quote (split-string sexp)) - "[[:space:]\n\r]+") - "\\([^[:word:]^-]\\)")) - -(defun clojure--replace-sexp-with-binding (bound-name init-expr end) - (save-excursion - (while (re-search-forward (clojure--sexp-regexp init-expr) end t) - (replace-match (concat "\\1" bound-name "\\2"))))) - -(defun clojure--replace-sexps-with-bindings (bindings end) - "Replace bindings with their respective bound names in the let form. -BINDINGS is the list of bound names and init expressions, END denotes the end of the let expression." - (let ((bound-name (pop bindings)) - (init-expr (pop bindings))) - (when bound-name - (clojure--replace-sexp-with-binding bound-name init-expr end) - (clojure--replace-sexps-with-bindings bindings end)))) - -(defun clojure--replace-sexps-with-bindings-and-indent () - (clojure--replace-sexps-with-bindings - (clojure--read-let-bindings) - (clojure--point-after 'clojure--goto-let 'forward-sexp)) - (clojure-indent-region - (clojure--point-after 'clojure--goto-let) - (clojure--point-after 'clojure--goto-let 'forward-sexp))) - -(defun clojure--read-let-bindings () - "Read the bound-name and init expression pairs in the binding form. -Return a list: odd elements are bound names, even elements init expressions." - (clojure--goto-let) - (down-list 2) - (let* ((start (point)) - (sexp-start start) - (end (save-excursion - (backward-char) - (forward-sexp) - (down-list -1) - (point))) - bindings) - (while (/= sexp-start end) - (forward-sexp) - (push - (string-trim (buffer-substring-no-properties sexp-start (point))) - bindings) - (skip-chars-forward "\r\n\t[:blank:]") - (setq sexp-start (point))) - (nreverse bindings))) - -(defun clojure--introduce-let-internal (name &optional n) - (if (numberp n) - (let ((init-expr-sexp (clojure-delete-and-extract-sexp))) - (insert name) - (ignore-errors (backward-up-list n)) - (insert "(let" (clojure-delete-and-extract-sexp) ")") - (backward-sexp) - (down-list) - (forward-sexp) - (insert " [" name " " init-expr-sexp "]\n") - (clojure--replace-sexps-with-bindings-and-indent)) - (insert "[ " (clojure-delete-and-extract-sexp) "]") - (backward-sexp) - (insert "(let " (clojure-delete-and-extract-sexp) ")") - (backward-sexp) - (down-list 2) - (insert name) - (forward-sexp) - (up-list) - (newline-and-indent) - (insert name))) - -(defun clojure--move-to-let-internal (name) - (if (not (save-excursion (clojure--goto-let))) - (clojure--introduce-let-internal name) - (let ((contents (clojure-delete-and-extract-sexp))) - (insert name) - (clojure--prepare-to-insert-new-let-binding) - (insert contents) - (backward-sexp) - (insert " ") - (backward-char) - (insert name) - (clojure--replace-sexps-with-bindings-and-indent)))) - -(defun clojure--let-backward-slurp-sexp-internal () - "Slurp the s-expression before the let form into the let form." - (clojure--goto-let) - (backward-sexp) - (let ((sexp (string-trim (clojure-delete-and-extract-sexp)))) - (delete-blank-lines) - (down-list) - (forward-sexp 2) - (newline-and-indent) - (insert sexp) - (clojure--replace-sexps-with-bindings-and-indent))) - -;;;###autoload -(defun clojure-let-backward-slurp-sexp (&optional n) - "Slurp the s-expression before the let form into the let form. -With a numberic prefix argument slurp the previous N s-expression into the let form." - (interactive "p") - (unless n (setq n 1)) - (dotimes (k n) - (save-excursion (clojure--let-backward-slurp-sexp-internal)))) - -(defun clojure--let-forward-slurp-sexp-internal () - "Slurp the next s-expression after the let form into the let form." - (clojure--goto-let) - (forward-sexp) - (let ((sexp (string-trim (clojure-delete-and-extract-sexp)))) - (down-list -1) - (newline-and-indent) - (insert sexp) - (clojure--replace-sexps-with-bindings-and-indent))) - -;;;###autoload -(defun clojure-let-forward-slurp-sexp (&optional n) - "Slurp the next s-expression after the let form into the let form. -With a numeric prefix argument slurp the next N s-expressions into the let form." - (interactive "p") - (unless n (setq n 1)) - (dotimes (k n) - (save-excursion (clojure--let-forward-slurp-sexp-internal)))) - -;;;###autoload -(defun clojure-introduce-let (&optional n) - "Create a let form, binding the form at point. -With a numeric prefix argument the let is introduced N lists up." - (interactive "P") - (clojure--introduce-let-internal (read-from-minibuffer "Name of bound symbol: ") n)) - -;;;###autoload -(defun clojure-move-to-let () - "Move the form at point to a binding in the nearest let." - (interactive) - (clojure--move-to-let-internal (read-from-minibuffer "Name of bound symbol: "))) - - -;;; ClojureScript -(defconst clojurescript-font-lock-keywords - (eval-when-compile - `(;; ClojureScript built-ins - (,(concat "(\\(?:\.*/\\)?" - (regexp-opt '("js-obj" "js-delete" "clj->js" "js->clj")) - "\\>") - 0 font-lock-builtin-face))) - "Additional font-locking for `clojurescrip-mode'.") - -;;;###autoload -(define-derived-mode clojurescript-mode clojure-mode "ClojureScript" - "Major mode for editing ClojureScript code. - -\\{clojurescript-mode-map}" - (font-lock-add-keywords nil clojurescript-font-lock-keywords)) - -;;;###autoload -(define-derived-mode clojurec-mode clojure-mode "ClojureC" - "Major mode for editing ClojureC code. - -\\{clojurec-mode-map}") - -(defconst clojurex-font-lock-keywords - ;; cljx annotations (#+clj and #+cljs) - '(("#\\+cljs?\\>" 0 font-lock-preprocessor-face)) - "Additional font-locking for `clojurex-mode'.") - -;;;###autoload -(define-derived-mode clojurex-mode clojure-mode "ClojureX" - "Major mode for editing ClojureX code. - -\\{clojurex-mode-map}" - (font-lock-add-keywords nil clojurex-font-lock-keywords)) - -;;;###autoload -(progn - (add-to-list 'auto-mode-alist - '("\\.\\(clj\\|dtm\\|edn\\)\\'" . clojure-mode)) - (add-to-list 'auto-mode-alist '("\\.cljc\\'" . clojurec-mode)) - (add-to-list 'auto-mode-alist '("\\.cljx\\'" . clojurex-mode)) - (add-to-list 'auto-mode-alist '("\\.cljs\\'" . clojurescript-mode)) - ;; boot build scripts are Clojure source files - (add-to-list 'auto-mode-alist '("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode))) - -(provide 'clojure-mode) - -;; Local Variables: -;; coding: utf-8 -;; End: - -;;; clojure-mode.el ends here diff --git a/elpa/clojure-mode-20181024.2224/clojure-mode-autoloads.el b/elpa/clojure-mode-20181024.2224/clojure-mode-autoloads.el new file mode 100644 index 00000000..977bbb04 --- /dev/null +++ b/elpa/clojure-mode-20181024.2224/clojure-mode-autoloads.el @@ -0,0 +1,150 @@ +;;; clojure-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "clojure-mode" "clojure-mode.el" (0 0 0 0)) +;;; Generated autoloads from clojure-mode.el + +(autoload 'clojure-mode "clojure-mode" "\ +Major mode for editing Clojure code. + +\\{clojure-mode-map} + +\(fn)" t nil) + +(autoload 'clojure-unwind "clojure-mode" "\ +Unwind thread at point or above point by one level. +Return nil if there are no more levels to unwind. + +\(fn)" t nil) + +(autoload 'clojure-unwind-all "clojure-mode" "\ +Fully unwind thread at point or above point. + +\(fn)" t nil) + +(autoload 'clojure-thread "clojure-mode" "\ +Thread by one more level an existing threading macro. + +\(fn)" t nil) + +(autoload 'clojure-thread-first-all "clojure-mode" "\ +Fully thread the form at point using ->. + +When BUT-LAST is non-nil, the last expression is not threaded. +Default value is `clojure-thread-all-but-last'. + +\(fn BUT-LAST)" t nil) + +(autoload 'clojure-thread-last-all "clojure-mode" "\ +Fully thread the form at point using ->>. + +When BUT-LAST is non-nil, the last expression is not threaded. +Default value is `clojure-thread-all-but-last'. + +\(fn BUT-LAST)" t nil) + +(autoload 'clojure-cycle-privacy "clojure-mode" "\ +Make public the current private def, or vice-versa. +See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy + +\(fn)" t nil) + +(autoload 'clojure-convert-collection-to-list "clojure-mode" "\ +Convert collection at (point) to list. + +\(fn)" t nil) + +(autoload 'clojure-convert-collection-to-quoted-list "clojure-mode" "\ +Convert collection at (point) to quoted list. + +\(fn)" t nil) + +(autoload 'clojure-convert-collection-to-map "clojure-mode" "\ +Convert collection at (point) to map. + +\(fn)" t nil) + +(autoload 'clojure-convert-collection-to-vector "clojure-mode" "\ +Convert collection at (point) to vector. + +\(fn)" t nil) + +(autoload 'clojure-convert-collection-to-set "clojure-mode" "\ +Convert collection at (point) to set. + +\(fn)" t nil) + +(autoload 'clojure-cycle-if "clojure-mode" "\ +Change a surrounding if to if-not, or vice-versa. + +See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if + +\(fn)" t nil) + +(autoload 'clojure-cycle-when "clojure-mode" "\ +Change a surrounding when to when-not, or vice-versa. + +\(fn)" t nil) + +(autoload 'clojure-let-backward-slurp-sexp "clojure-mode" "\ +Slurp the s-expression before the let form into the let form. +With a numberic prefix argument slurp the previous N s-expression +into the let form. + +\(fn &optional N)" t nil) + +(autoload 'clojure-let-forward-slurp-sexp "clojure-mode" "\ +Slurp the next s-expression after the let form into the let form. +With a numeric prefix argument slurp the next N s-expressions into the let form. + +\(fn &optional N)" t nil) + +(autoload 'clojure-introduce-let "clojure-mode" "\ +Create a let form, binding the form at point. +With a numeric prefix argument the let is introduced N lists up. + +\(fn &optional N)" t nil) + +(autoload 'clojure-move-to-let "clojure-mode" "\ +Move the form at point to a binding in the nearest let. + +\(fn)" t nil) + +(autoload 'clojurescript-mode "clojure-mode" "\ +Major mode for editing ClojureScript code. + +\\{clojurescript-mode-map} + +\(fn)" t nil) + +(autoload 'clojurec-mode "clojure-mode" "\ +Major mode for editing ClojureC code. + +\\{clojurec-mode-map} + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("\\.\\(clj\\|dtm\\|edn\\)\\'" . clojure-mode)) + +(add-to-list 'auto-mode-alist '("\\.cljc\\'" . clojurec-mode)) + +(add-to-list 'auto-mode-alist '("\\.cljs\\'" . clojurescript-mode)) + +(add-to-list 'auto-mode-alist '("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode)) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "clojure-mode" '("clojure" "add-custom-clojure-indents" "define-clojure-indent" "put-clojure-indent"))) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; clojure-mode-autoloads.el ends here diff --git a/elpa/clojure-mode-20181024.2224/clojure-mode-pkg.el b/elpa/clojure-mode-20181024.2224/clojure-mode-pkg.el new file mode 100644 index 00000000..8efb1390 --- /dev/null +++ b/elpa/clojure-mode-20181024.2224/clojure-mode-pkg.el @@ -0,0 +1,2 @@ +;;; -*- no-byte-compile: t -*- +(define-package "clojure-mode" "20181024.2224" "Major mode for Clojure code" '((emacs "25.1")) :commit "71b32ee95e8ec99098e212cd0ec74cb4c002e8c6" :keywords '("languages" "clojure" "clojurescript" "lisp") :url "http://github.com/clojure-emacs/clojure-mode") diff --git a/elpa/clojure-mode-20181024.2224/clojure-mode.el b/elpa/clojure-mode-20181024.2224/clojure-mode.el new file mode 100644 index 00000000..5f71f3e4 --- /dev/null +++ b/elpa/clojure-mode-20181024.2224/clojure-mode.el @@ -0,0 +1,2703 @@ +;;; clojure-mode.el --- Major mode for Clojure code -*- lexical-binding: t; -*- + +;; Copyright © 2007-2018 Jeffrey Chu, Lennart Staflin, Phil Hagelberg +;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba +;; +;; Authors: Jeffrey Chu +;; Lennart Staflin +;; Phil Hagelberg +;; Bozhidar Batsov +;; Artur Malabarba +;; URL: http://github.com/clojure-emacs/clojure-mode +;; Package-Version: 20181024.2224 +;; Keywords: languages clojure clojurescript lisp +;; Version: 5.10.0-snapshot +;; Package-Requires: ((emacs "25.1")) + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; Provides font-lock, indentation, navigation and basic refactoring for the +;; Clojure programming language (http://clojure.org). + +;; Using clojure-mode with paredit or smartparens is highly recommended. + +;; Here are some example configurations: + +;; ;; require or autoload paredit-mode +;; (add-hook 'clojure-mode-hook #'paredit-mode) + +;; ;; require or autoload smartparens +;; (add-hook 'clojure-mode-hook #'smartparens-strict-mode) + +;; See inf-clojure (http://github.com/clojure-emacs/inf-clojure) for +;; basic interaction with Clojure subprocesses. + +;; See CIDER (http://github.com/clojure-emacs/cider) for +;; better interaction with subprocesses via nREPL. + +;;; License: + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Code: + + +(eval-when-compile + (defvar calculate-lisp-indent-last-sexp) + (defvar font-lock-beg) + (defvar font-lock-end) + (defvar paredit-space-for-delimiter-predicates) + (defvar paredit-version) + (defvar paredit-mode)) + +(require 'cl-lib) +(require 'imenu) +(require 'newcomment) +(require 'align) +(require 'subr-x) + +(declare-function lisp-fill-paragraph "lisp-mode" (&optional justify)) + +(defgroup clojure nil + "Major mode for editing Clojure code." + :prefix "clojure-" + :group 'languages + :link '(url-link :tag "GitHub" "https://github.com/clojure-emacs/clojure-mode") + :link '(emacs-commentary-link :tag "Commentary" "clojure-mode")) + +(defconst clojure-mode-version "5.8.2" + "The current version of `clojure-mode'.") + +(defface clojure-keyword-face + '((t (:inherit font-lock-constant-face))) + "Face used to font-lock Clojure keywords (:something)." + :package-version '(clojure-mode . "3.0.0")) + +(defface clojure-character-face + '((t (:inherit font-lock-string-face))) + "Face used to font-lock Clojure character literals." + :package-version '(clojure-mode . "3.0.0")) + +(defcustom clojure-indent-style :always-align + "Indentation style to use for function forms and macro forms. +There are two cases of interest configured by this variable. + +- Case (A) is when at least one function argument is on the same + line as the function name. +- Case (B) is the opposite (no arguments are on the same line as + the function name). Note that the body of macros is not + affected by this variable, it is always indented by + `lisp-body-indent' (default 2) spaces. + +Note that this variable configures the indentation of function +forms (and function-like macros), it does not affect macros that +already use special indentation rules. + +The possible values for this variable are keywords indicating how +to indent function forms. + + `:always-align' - Follow the same rules as `lisp-mode'. All + args are vertically aligned with the first arg in case (A), + and vertically aligned with the function name in case (B). + For instance: + (reduce merge + some-coll) + (reduce + merge + some-coll) + + `:always-indent' - All args are indented like a macro body. + (reduce merge + some-coll) + (reduce + merge + some-coll) + + `:align-arguments' - Case (A) is indented like `lisp', and + case (B) is indented like a macro body. + (reduce merge + some-coll) + (reduce + merge + some-coll)" + :safe #'keywordp + :type '(choice (const :tag "Same as `lisp-mode'" :always-align) + (const :tag "Indent like a macro body" :always-indent) + (const :tag "Indent like a macro body unless first arg is on the same line" + :align-arguments)) + :package-version '(clojure-mode . "5.2.0")) + +(define-obsolete-variable-alias 'clojure-defun-style-default-indent + 'clojure-indent-style "5.2.0") + +(defcustom clojure-use-backtracking-indent t + "When non-nil, enable context sensitive indentation." + :type 'boolean + :safe 'booleanp) + +(defcustom clojure-max-backtracking 3 + "Maximum amount to backtrack up a list to check for context." + :type 'integer + :safe 'integerp) + +(defcustom clojure-docstring-fill-column fill-column + "Value of `fill-column' to use when filling a docstring." + :type 'integer + :safe 'integerp) + +(defcustom clojure-docstring-fill-prefix-width 2 + "Width of `fill-prefix' when filling a docstring. +The default value conforms with the de facto convention for +Clojure docstrings, aligning the second line with the opening +double quotes on the third column." + :type 'integer + :safe 'integerp) + +(defcustom clojure-omit-space-between-tag-and-delimiters '(?\[ ?\{ ?\() + "Allowed opening delimiter characters after a reader literal tag. +For example, \[ is allowed in :db/id[:db.part/user]." + :type '(set (const :tag "[" ?\[) + (const :tag "{" ?\{) + (const :tag "(" ?\() + (const :tag "\"" ?\")) + :safe (lambda (value) + (and (listp value) + (cl-every 'characterp value)))) + +(defcustom clojure-build-tool-files + '("project.clj" ; Leiningen + "build.boot" ; Boot + "build.gradle" ; Gradle + "deps.edn" ; Clojure CLI (a.k.a. tools.deps) + "shadow-cljs.edn" ; shadow-cljs + ) + "A list of files, which identify a Clojure project's root. +Out-of-the box `clojure-mode' understands lein, boot, gradle, + shadow-cljs and tools.deps." + :type '(repeat string) + :package-version '(clojure-mode . "5.0.0") + :safe (lambda (value) + (and (listp value) + (cl-every 'stringp value)))) + +(defcustom clojure-project-root-function #'clojure-project-root-path + "Function to locate clojure project root directory." + :type 'function + :risky t + :package-version '(clojure-mode . "5.7.0")) + +(defcustom clojure-refactor-map-prefix (kbd "C-c C-r") + "Clojure refactor keymap prefix." + :type 'string + :package-version '(clojure-mode . "5.6.0")) + +(defvar clojure-refactor-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-t") #'clojure-thread) + (define-key map (kbd "t") #'clojure-thread) + (define-key map (kbd "C-u") #'clojure-unwind) + (define-key map (kbd "u") #'clojure-unwind) + (define-key map (kbd "C-f") #'clojure-thread-first-all) + (define-key map (kbd "f") #'clojure-thread-first-all) + (define-key map (kbd "C-l") #'clojure-thread-last-all) + (define-key map (kbd "l") #'clojure-thread-last-all) + (define-key map (kbd "C-a") #'clojure-unwind-all) + (define-key map (kbd "a") #'clojure-unwind-all) + (define-key map (kbd "C-p") #'clojure-cycle-privacy) + (define-key map (kbd "p") #'clojure-cycle-privacy) + (define-key map (kbd "C-(") #'clojure-convert-collection-to-list) + (define-key map (kbd "(") #'clojure-convert-collection-to-list) + (define-key map (kbd "C-'") #'clojure-convert-collection-to-quoted-list) + (define-key map (kbd "'") #'clojure-convert-collection-to-quoted-list) + (define-key map (kbd "C-{") #'clojure-convert-collection-to-map) + (define-key map (kbd "{") #'clojure-convert-collection-to-map) + (define-key map (kbd "C-[") #'clojure-convert-collection-to-vector) + (define-key map (kbd "[") #'clojure-convert-collection-to-vector) + (define-key map (kbd "C-#") #'clojure-convert-collection-to-set) + (define-key map (kbd "#") #'clojure-convert-collection-to-set) + (define-key map (kbd "C-i") #'clojure-cycle-if) + (define-key map (kbd "i") #'clojure-cycle-if) + (define-key map (kbd "C-w") #'clojure-cycle-when) + (define-key map (kbd "w") #'clojure-cycle-when) + (define-key map (kbd "C-o") #'clojure-cycle-not) + (define-key map (kbd "o") #'clojure-cycle-not) + (define-key map (kbd "n i") #'clojure-insert-ns-form) + (define-key map (kbd "n h") #'clojure-insert-ns-form-at-point) + (define-key map (kbd "n u") #'clojure-update-ns) + (define-key map (kbd "n s") #'clojure-sort-ns) + (define-key map (kbd "s i") #'clojure-introduce-let) + (define-key map (kbd "s m") #'clojure-move-to-let) + (define-key map (kbd "s f") #'clojure-let-forward-slurp-sexp) + (define-key map (kbd "s b") #'clojure-let-backward-slurp-sexp) + map) + "Keymap for Clojure refactoring commands.") +(fset 'clojure-refactor-map clojure-refactor-map) + +(defvar clojure-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map prog-mode-map) + (define-key map (kbd "C-:") #'clojure-toggle-keyword-string) + (define-key map (kbd "C-c SPC") #'clojure-align) + (define-key map clojure-refactor-map-prefix 'clojure-refactor-map) + (easy-menu-define clojure-mode-menu map "Clojure Mode Menu" + '("Clojure" + ["Toggle between string & keyword" clojure-toggle-keyword-string] + ["Align expression" clojure-align] + ["Cycle privacy" clojure-cycle-privacy] + ["Cycle if, if-not" clojure-cycle-if] + ["Cycle when, when-not" clojure-cycle-when] + ["Cycle not" clojure-cycle-not] + ("ns forms" + ["Insert ns form at the top" clojure-insert-ns-form] + ["Insert ns form here" clojure-insert-ns-form-at-point] + ["Update ns form" clojure-update-ns] + ["Sort ns form" clojure-sort-ns]) + ("Convert collection" + ["Convert to list" clojure-convert-collection-to-list] + ["Convert to quoted list" clojure-convert-collection-to-quoted-list] + ["Convert to map" clojure-convert-collection-to-map] + ["Convert to vector" clojure-convert-collection-to-vector] + ["Convert to set" clojure-convert-collection-to-set]) + ("Refactor -> and ->>" + ["Thread once more" clojure-thread] + ["Fully thread a form with ->" clojure-thread-first-all] + ["Fully thread a form with ->>" clojure-thread-last-all] + "--" + ["Unwind once" clojure-unwind] + ["Fully unwind a threading macro" clojure-unwind-all]) + ("Let expression" + ["Introduce let" clojure-introduce-let] + ["Move to let" clojure-move-to-let] + ["Forward slurp form into let" clojure-let-forward-slurp-sexp] + ["Backward slurp form into let" clojure-let-backward-slurp-sexp]) + ("Documentation" + ["View a Clojure guide" clojure-view-guide] + ["View a Clojure reference section" clojure-view-reference-section] + ["View the Clojure cheatsheet" clojure-view-cheatsheet] + ["View the Clojure Grimoire" clojure-view-grimoire] + ["View the Clojure style guide" clojure-view-style-guide]) + "--" + ["Report a clojure-mode bug" clojure-mode-report-bug] + ["Clojure-mode version" clojure-mode-display-version])) + map) + "Keymap for Clojure mode.") + +(defvar clojure-mode-syntax-table + (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table))) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + (modify-syntax-entry ?\[ "(]" table) + (modify-syntax-entry ?\] ")[" table) + (modify-syntax-entry ?? "_ p" table) ; ? is a prefix outside symbols + (modify-syntax-entry ?# "_ p" table) ; # is allowed inside keywords (#399) + (modify-syntax-entry ?~ "'" table) + (modify-syntax-entry ?^ "'" table) + (modify-syntax-entry ?@ "'" table) + table) + "Syntax table for Clojure mode. +Inherits from `emacs-lisp-mode-syntax-table'.") + +(defconst clojure--prettify-symbols-alist + '(("fn" . ?λ))) + +(defvar-local clojure-expected-ns-function nil + "The function used to determine the expected namespace of a file. +`clojure-mode' ships a basic function named `clojure-expected-ns' +that does basic heuristics to figure this out. +CIDER provides a more complex version which does classpath analysis.") + +(defun clojure-mode-display-version () + "Display the current `clojure-mode-version' in the minibuffer." + (interactive) + (message "clojure-mode (version %s)" clojure-mode-version)) + +(defconst clojure-mode-report-bug-url "https://github.com/clojure-emacs/clojure-mode/issues/new" + "The URL to report a `clojure-mode' issue.") + +(defun clojure-mode-report-bug () + "Report a bug in your default browser." + (interactive) + (browse-url clojure-mode-report-bug-url)) + +(defconst clojure-guides-base-url "https://clojure.org/guides/" + "The base URL for official Clojure guides.") + +(defconst clojure-guides '(("Getting Started" . "getting_started") + ("FAQ" . "faq") + ("spec" . "spec") + ("Destructuring" . "destructuring") + ("Threading Macros" . "threading_macros") + ("Comparators" . "comparators") + ("Reader Conditionals" . "reader_conditionals")) + "A list of all official Clojure guides.") + +(defun clojure-view-guide () + "Open a Clojure guide in your default browser. + +The command will prompt you to select one of the available guides." + (interactive) + (let ((guide (completing-read "Select a guide: " (mapcar #'car clojure-guides)))) + (when guide + (let ((guide-url (concat clojure-guides-base-url (cdr (assoc guide clojure-guides))))) + (browse-url guide-url))))) + +(defconst clojure-reference-base-url "https://clojure.org/reference/" + "The base URL for the official Clojure reference.") + +(defconst clojure-reference-sections '(("The Reader" . "reader") + ("The REPL and main" . "repl_and_main") + ("Evaluation" . "evaluation") + ("Special Forms" . "special_forms") + ("Macros" . "macros") + ("Other Functions" . "other_functions") + ("Data Structures" . "data_structures") + ("Datatypes" . "datatypes") + ("Sequences" . "sequences") + ("Transients" . "transients") + ("Transducers" . "transducers") + ("Multimethods and Hierarchies" . "multimethods") + ("Protocols" . "protocols") + ("Metadata" . "metadata") + ("Namespaces" . "namespaces") + ("Libs" . "libs") + ("Vars and Environments" . "vars") + ("Refs and Transactions" . "refs") + ("Agents" . "agents") + ("Atoms" . "atoms") + ("Reducers" . "reducers") + ("Java Interop" . "java_interop") + ("Compilation and Class Generation" . "compilation") + ("Other Libraries" . "other_libraries") + ("Differences with Lisps" . "lisps"))) + +(defun clojure-view-reference-section () + "Open a Clojure reference section in your default browser. + +The command will prompt you to select one of the available sections." + (interactive) + (let ((section (completing-read "Select a reference section: " (mapcar #'car clojure-reference-sections)))) + (when section + (let ((section-url (concat clojure-reference-base-url (cdr (assoc section clojure-reference-sections))))) + (browse-url section-url))))) + +(defconst clojure-cheatsheet-url "http://clojure.org/api/cheatsheet" + "The URL of the official Clojure cheatsheet.") + +(defun clojure-view-cheatsheet () + "Open the Clojure cheatsheet in your default browser." + (interactive) + (browse-url clojure-cheatsheet-url)) + +(defconst clojure-grimoire-url "https://www.conj.io/" + "The URL of the Grimoire community documentation site.") + +(defun clojure-view-grimoire () + "Open the Clojure Grimoire in your default browser." + (interactive) + (browse-url clojure-grimoire-url)) + +(defconst clojure-style-guide-url "https://github.com/bbatsov/clojure-style-guide" + "The URL of the Clojure style guide.") + +(defun clojure-view-style-guide () + "Open the Clojure style guide in your default browser." + (interactive) + (browse-url clojure-style-guide-url)) + +(defun clojure-space-for-delimiter-p (endp delim) + "Prevent paredit from inserting useless spaces. +See `paredit-space-for-delimiter-predicates' for the meaning of +ENDP and DELIM." + (or endp + (not (memq delim '(?\" ?{ ?\( ))) + (not (or (derived-mode-p 'clojure-mode) + (derived-mode-p 'cider-repl-mode))) + (save-excursion + (backward-char) + (cond ((eq (char-after) ?#) + (and (not (bobp)) + (or (char-equal ?w (char-syntax (char-before))) + (char-equal ?_ (char-syntax (char-before)))))) + ((and (eq delim ?\() + (eq (char-after) ??) + (eq (char-before) ?#)) + nil) + (t))))) + +(defconst clojure--collection-tag-regexp "#\\(::[a-zA-Z0-9._-]*\\|:?\\([a-zA-Z0-9._-]+/\\)?[a-zA-Z0-9._-]+\\)" + "Collection reader macro tag regexp. +It is intended to check for allowed strings that can come before a +collection literal (e.g. '[]' or '{}'), as reader macro tags. +This includes #fully.qualified/my-ns[:kw val] and #::my-ns{:kw +val} as of Clojure 1.9.") + +(defun clojure-no-space-after-tag (endp delimiter) + "Prevent inserting a space after a reader-literal tag. + +When a reader-literal tag is followed be an opening delimiter +listed in `clojure-omit-space-between-tag-and-delimiters', this +function returns t. + +This allows you to write things like #db/id[:db.part/user] +and #::my-ns{:some \"map\"} without inserting a space between +the tag and the opening bracket. + +See `paredit-space-for-delimiter-predicates' for the meaning of +ENDP and DELIMITER." + (if endp + t + (or (not (member delimiter clojure-omit-space-between-tag-and-delimiters)) + (save-excursion + (let ((orig-point (point))) + (not (and (re-search-backward + clojure--collection-tag-regexp + (line-beginning-position) + t) + (= orig-point (match-end 0))))))))) + +(declare-function paredit-open-curly "ext:paredit") +(declare-function paredit-close-curly "ext:paredit") +(declare-function paredit-convolute-sexp "ext:paredit") + +(defun clojure--replace-let-bindings-and-indent (orig-fun &rest args) + "Advise ORIG-FUN to replace let bindings. + +Sexps are replace by their bound name if a let form was +convoluted. + +ORIG-FUN should be `paredit-convolute-sexp'. + +ARGS are passed to ORIG-FUN, as with all advice." + (save-excursion + (backward-sexp) + (when (looking-back clojure--let-regexp) + (clojure--replace-sexps-with-bindings-and-indent)))) + +(defun clojure-paredit-setup (&optional keymap) + "Make \"paredit-mode\" play nice with `clojure-mode'. + +If an optional KEYMAP is passed the changes are applied to it, +instead of to `clojure-mode-map'. +Also advice `paredit-convolute-sexp' when used on a let form as drop in +replacement for `cljr-expand-let`." + (when (>= paredit-version 21) + (let ((keymap (or keymap clojure-mode-map))) + (define-key keymap "{" #'paredit-open-curly) + (define-key keymap "}" #'paredit-close-curly)) + (add-to-list 'paredit-space-for-delimiter-predicates + #'clojure-space-for-delimiter-p) + (add-to-list 'paredit-space-for-delimiter-predicates + #'clojure-no-space-after-tag) + (advice-add 'paredit-convolute-sexp :after #'clojure--replace-let-bindings-and-indent))) + +(defun clojure-mode-variables () + "Set up initial buffer-local variables for Clojure mode." + (add-to-list 'imenu-generic-expression '(nil clojure-match-next-def 0)) + (setq-local indent-tabs-mode nil) + (setq-local paragraph-ignore-fill-prefix t) + (setq-local outline-regexp ";;;\\(;* [^ \t\n]\\)\\|(") + (setq-local outline-level 'lisp-outline-level) + (setq-local comment-start ";") + (setq-local comment-start-skip ";+ *") + (setq-local comment-add 1) ; default to `;;' in comment-region + (setq-local comment-column 40) + (setq-local comment-use-syntax t) + (setq-local multibyte-syntax-as-symbol t) + (setq-local electric-pair-skip-whitespace 'chomp) + (setq-local electric-pair-open-newline-between-pairs nil) + (setq-local fill-paragraph-function #'clojure-fill-paragraph) + (setq-local adaptive-fill-function #'clojure-adaptive-fill-function) + (setq-local normal-auto-fill-function #'clojure-auto-fill-function) + (setq-local comment-start-skip + "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *") + (setq-local indent-line-function #'clojure-indent-line) + (setq-local indent-region-function #'clojure-indent-region) + (setq-local lisp-indent-function #'clojure-indent-function) + (setq-local lisp-doc-string-elt-property 'clojure-doc-string-elt) + (setq-local clojure-expected-ns-function #'clojure-expected-ns) + (setq-local parse-sexp-ignore-comments t) + (setq-local prettify-symbols-alist clojure--prettify-symbols-alist) + (setq-local open-paren-in-column-0-is-defun-start nil) + (setq-local beginning-of-defun-function #'clojure-beginning-of-defun-function)) + +(defsubst clojure-in-docstring-p () + "Check whether point is in a docstring." + (let ((ppss (syntax-ppss))) + ;; are we in a string? + (when (nth 3 ppss) + ;; check font lock at the start of the string + (eq (get-text-property (nth 8 ppss) 'face) + 'font-lock-doc-face)))) + +;;;###autoload +(define-derived-mode clojure-mode prog-mode "Clojure" + "Major mode for editing Clojure code. + +\\{clojure-mode-map}" + (clojure-mode-variables) + (clojure-font-lock-setup) + (add-hook 'paredit-mode-hook #'clojure-paredit-setup) + ;; `electric-layout-post-self-insert-function' prevents indentation in strings + ;; and comments, force indentation in docstrings: + (add-hook 'electric-indent-functions + (lambda (_char) (if (clojure-in-docstring-p) 'do-indent))) + ;; integration with project.el + (add-hook 'project-find-functions #'clojure-current-project)) + +(defcustom clojure-verify-major-mode t + "If non-nil, warn when activating the wrong `major-mode'." + :type 'boolean + :safe #'booleanp + :package-version '(clojure-mode "5.3.0")) + +(defun clojure--check-wrong-major-mode () + "Check if the current `major-mode' matches the file extension. + +If it doesn't, issue a warning if `clojure-verify-major-mode' is +non-nil." + (when (and clojure-verify-major-mode + (stringp (buffer-file-name))) + (let* ((case-fold-search t) + (problem (cond ((and (string-match "\\.clj\\'" (buffer-file-name)) + (not (eq major-mode 'clojure-mode))) + 'clojure-mode) + ((and (string-match "\\.cljs\\'" (buffer-file-name)) + (not (eq major-mode 'clojurescript-mode))) + 'clojurescript-mode) + ((and (string-match "\\.cljc\\'" (buffer-file-name)) + (not (eq major-mode 'clojurec-mode))) + 'clojurec-mode)))) + (when problem + (message "[WARNING] %s activated `%s' instead of `%s' in this buffer. +This could cause problems. +\(See `clojure-verify-major-mode' to disable this message.)" + (if (eq major-mode real-this-command) + "You have" + "Something in your configuration") + major-mode + problem))))) + +(add-hook 'clojure-mode-hook #'clojure--check-wrong-major-mode) + +(defsubst clojure-docstring-fill-prefix () + "The prefix string used by `clojure-fill-paragraph'. +It is simply `clojure-docstring-fill-prefix-width' number of spaces." + (make-string clojure-docstring-fill-prefix-width ? )) + +(defun clojure-adaptive-fill-function () + "Clojure adaptive fill function. +This only takes care of filling docstring correctly." + (when (clojure-in-docstring-p) + (clojure-docstring-fill-prefix))) + +(defun clojure-fill-paragraph (&optional justify) + "Like `fill-paragraph', but can handle Clojure docstrings. +If JUSTIFY is non-nil, justify as well as fill the paragraph." + (if (clojure-in-docstring-p) + (let ((paragraph-start + (concat paragraph-start + "\\|\\s-*\\([(:\"[]\\|~@\\|`(\\|#'(\\)")) + (paragraph-separate + (concat paragraph-separate "\\|\\s-*\".*[,\\.]$")) + (fill-column (or clojure-docstring-fill-column fill-column)) + (fill-prefix (clojure-docstring-fill-prefix))) + ;; we are in a string and string start pos (8th element) is non-nil + (let* ((beg-doc (nth 8 (syntax-ppss))) + (end-doc (save-excursion + (goto-char beg-doc) + (or (ignore-errors (forward-sexp) (point)) + (point-max))))) + (save-restriction + (narrow-to-region beg-doc end-doc) + (fill-paragraph justify)))) + (let ((paragraph-start (concat paragraph-start + "\\|\\s-*\\([(:\"[]\\|`(\\|#'(\\)")) + (paragraph-separate + (concat paragraph-separate "\\|\\s-*\".*[,\\.[]$"))) + (or (fill-comment-paragraph justify) + (fill-paragraph justify)) + ;; Always return `t' + t))) + +(defun clojure-auto-fill-function () + "Clojure auto-fill function." + ;; Check if auto-filling is meaningful. + (let ((fc (current-fill-column))) + (when (and fc (> (current-column) fc)) + (let ((fill-column (if (clojure-in-docstring-p) + clojure-docstring-fill-column + fill-column)) + (fill-prefix (clojure-adaptive-fill-function))) + (do-auto-fill))))) + + +;;; #_ comments font-locking +;; Code heavily borrowed from Slime. +;; https://github.com/slime/slime/blob/master/contrib/slime-fontifying-fu.el#L186 +(defvar clojure--comment-macro-regexp + (rx "#_" (* " ") (group-n 1 (not (any " ")))) + "Regexp matching the start of a comment sexp. +The beginning of match-group 1 should be before the sexp to be +marked as a comment. The end of sexp is found with +`clojure-forward-logical-sexp'.") + +(defvar clojure--reader-and-comment-regexp + "#_ *\\(?1:[^ ]\\)\\|\\(?1:(comment\\_>\\)" + "Regexp matching both `#_' macro and a comment sexp." ) + +(defcustom clojure-comment-regexp clojure--comment-macro-regexp + "Comment mode. + +The possible values for this variable are keywords indicating +what is considered a comment (affecting font locking). + + - Reader macro `#_' only - the default + - Reader macro `#_' and `(comment)'" + :type '(choice (const :tag "Reader macro `#_' and `(comment)'" clojure--reader-and-comment-regexp) + (other :tag "Reader macro `#_' only" clojure--comment-macro-regexp)) + :package-version '(clojure-mode . "5.7.0")) + +(defun clojure--search-comment-macro-internal (limit) + "Search for a comment forward stopping at LIMIT." + (when (search-forward-regexp clojure-comment-regexp limit t) + (let* ((md (match-data)) + (start (match-beginning 1)) + (state (syntax-ppss start))) + ;; inside string or comment? + (if (or (nth 3 state) + (nth 4 state)) + (clojure--search-comment-macro-internal limit) + (goto-char start) + (clojure-forward-logical-sexp 1) + ;; Data for (match-end 1). + (setf (elt md 3) (point)) + (set-match-data md) + t)))) + +(defun clojure--search-comment-macro (limit) + "Find comment macros and set the match data. +Search from point up to LIMIT. The region that should be +considered a comment is between `(match-beginning 1)' +and `(match-end 1)'." + (let ((result 'retry)) + (while (and (eq result 'retry) (<= (point) limit)) + (condition-case nil + (setq result (clojure--search-comment-macro-internal limit)) + (end-of-file (setq result nil)) + (scan-error (setq result 'retry)))) + result)) + + +;;; General font-locking +(defun clojure-match-next-def () + "Scans the buffer backwards for the next \"top-level\" definition. +Called by `imenu--generic-function'." + ;; we have to take into account namespace-definition forms + ;; e.g. s/defn + (when (re-search-backward "^[ \t]*(\\([a-z0-9.-]+/\\)?\\(def\\sw*\\)" nil t) + (save-excursion + (let (found? + (deftype (match-string 2)) + (start (point))) + (down-list) + (forward-sexp) + (while (not found?) + (ignore-errors + (forward-sexp)) + (or (when (char-equal ?\[ (char-after (point))) + (backward-sexp)) + (when (char-equal ?\) (char-after (point))) + (backward-sexp))) + (cl-destructuring-bind (def-beg . def-end) (bounds-of-thing-at-point 'sexp) + (if (char-equal ?^ (char-after def-beg)) + (progn (forward-sexp) (backward-sexp)) + (setq found? t) + (when (string= deftype "defmethod") + (setq def-end (progn (goto-char def-end) + (forward-sexp) + (point)))) + (set-match-data (list def-beg def-end))))) + (goto-char start))))) + +(eval-and-compile + (defconst clojure--sym-forbidden-rest-chars "][\";\'@\\^`~\(\)\{\}\\,\s\t\n\r" + "A list of chars that a Clojure symbol cannot contain. +See definition of 'macros': URL `http://git.io/vRGLD'.") + (defconst clojure--sym-forbidden-1st-chars (concat clojure--sym-forbidden-rest-chars "0-9:") + "A list of chars that a Clojure symbol cannot start with. +See the for-loop: URL `http://git.io/vRGTj' lines: URL +`http://git.io/vRGIh', URL `http://git.io/vRGLE' and value +definition of 'macros': URL `http://git.io/vRGLD'.") + (defconst clojure--sym-regexp + (concat "[^" clojure--sym-forbidden-1st-chars "][^" clojure--sym-forbidden-rest-chars "]*") + "A regexp matching a Clojure symbol or namespace alias. +Matches the rule `clojure--sym-forbidden-1st-chars' followed by +any number of matches of `clojure--sym-forbidden-rest-chars'.")) + +(defconst clojure-font-lock-keywords + (eval-when-compile + `( ;; Top-level variable definition + (,(concat "(\\(?:clojure.core/\\)?\\(" + (regexp-opt '("def" "defonce")) + ;; variable declarations + "\\)\\>" + ;; Any whitespace + "[ \r\n\t]*" + ;; Possibly type or metadata + "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" + "\\(\\sw+\\)?") + (1 font-lock-keyword-face) + (2 font-lock-variable-name-face nil t)) + ;; Type definition + (,(concat "(\\(?:clojure.core/\\)?\\(" + (regexp-opt '("defstruct" "deftype" "defprotocol" + "defrecord")) + ;; type declarations + "\\)\\>" + ;; Any whitespace + "[ \r\n\t]*" + ;; Possibly type or metadata + "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" + "\\(\\sw+\\)?") + (1 font-lock-keyword-face) + (2 font-lock-type-face nil t)) + ;; Function definition (anything that starts with def and is not + ;; listed above) + (,(concat "(\\(?:" clojure--sym-regexp "/\\)?" + "\\(def[^ \r\n\t]*\\)" + ;; Function declarations + "\\>" + ;; Any whitespace + "[ \r\n\t]*" + ;; Possibly type or metadata + "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" + "\\(\\sw+\\)?") + (1 font-lock-keyword-face) + (2 font-lock-function-name-face nil t)) + ;; (fn name? args ...) + (,(concat "(\\(?:clojure.core/\\)?\\(fn\\)[ \t]+" + ;; Possibly type + "\\(?:#?^\\sw+[ \t]*\\)?" + ;; Possibly name + "\\(\\sw+\\)?" ) + (1 font-lock-keyword-face) + (2 font-lock-function-name-face nil t)) + ;; lambda arguments - %, %&, %1, %2, etc + ("\\<%[&1-9]?" (0 font-lock-variable-name-face)) + ;; Special forms + (,(concat + "(" + (regexp-opt + '("def" "do" "if" "let" "let*" "var" "fn" "fn*" "loop" "loop*" + "recur" "throw" "try" "catch" "finally" + "set!" "new" "." + "monitor-enter" "monitor-exit" "quote") t) + "\\>") + 1 font-lock-keyword-face) + ;; Built-in binding and flow of control forms + (,(concat + "(\\(?:clojure.core/\\)?" + (regexp-opt + '("letfn" "case" "cond" "cond->" "cond->>" "condp" + "for" "when" "when-not" "when-let" "when-first" "when-some" + "if-let" "if-not" "if-some" + ".." "->" "->>" "as->" "doto" "and" "or" + "dosync" "doseq" "dotimes" "dorun" "doall" + "ns" "in-ns" + "with-open" "with-local-vars" "binding" + "with-redefs" "with-redefs-fn" + "declare") t) + "\\>") + 1 font-lock-keyword-face) + ;; Macros similar to let, when, and while + (,(rx symbol-start + (or "let" "when" "while") "-" + (1+ (or (syntax word) (syntax symbol))) + symbol-end) + 0 font-lock-keyword-face) + (,(concat + "\\<" + (regexp-opt + '("*1" "*2" "*3" "*agent*" + "*allow-unresolved-vars*" "*assert*" "*clojure-version*" + "*command-line-args*" "*compile-files*" + "*compile-path*" "*data-readers*" "*default-data-reader-fn*" + "*e" "*err*" "*file*" "*flush-on-newline*" + "*in*" "*macro-meta*" "*math-context*" "*ns*" "*out*" + "*print-dup*" "*print-length*" "*print-level*" + "*print-meta*" "*print-readably*" + "*read-eval*" "*source-path*" + "*unchecked-math*" + "*use-context-classloader*" "*warn-on-reflection*") + t) + "\\>") + 0 font-lock-builtin-face) + ;; Dynamic variables - *something* or @*something* + ("\\(?:\\<\\|/\\)@?\\(\\*[a-z-]*\\*\\)\\>" 1 font-lock-variable-name-face) + ;; Global constants - nil, true, false + (,(concat + "\\<" + (regexp-opt + '("true" "false" "nil") t) + "\\>") + 0 font-lock-constant-face) + ;; Character literals - \1, \a, \newline, \u0000 + ("\\\\\\([[:punct:]]\\|[a-z0-9]+\\>\\)" 0 'clojure-character-face) + + ;; namespace definitions: (ns foo.bar) + (,(concat "(\\[ \r\n\t]*" + ;; Possibly metadata + "\\(?:\\^?{[^}]+}[ \r\n\t]*\\)*" + ;; namespace + "\\(" clojure--sym-regexp "\\)") + (1 font-lock-type-face)) + + ;; TODO dedupe the code for matching of keywords, type-hints and unmatched symbols + + ;; keywords: {:oneword/ve/yCom|pLex.stu-ff 0} + (,(concat "\\(:\\{1,2\\}\\)\\(" clojure--sym-regexp "?\\)\\(/\\)\\(" clojure--sym-regexp "\\)") + (1 'clojure-keyword-face) + (2 font-lock-type-face) + ;; (2 'clojure-keyword-face) + (3 'default) + (4 'clojure-keyword-face)) + (,(concat "\\(:\\{1,2\\}\\)\\(" clojure--sym-regexp "\\)") + (1 'clojure-keyword-face) + (2 'clojure-keyword-face)) + + ;; type-hints: #^oneword + (,(concat "\\(#?\\^\\)\\(" clojure--sym-regexp "?\\)\\(/\\)\\(" clojure--sym-regexp "\\)") + (1 'default) + (2 font-lock-type-face) + (3 'default) + (4 'default)) + (,(concat "\\(#?\\^\\)\\(" clojure--sym-regexp "\\)") + (1 'default) + (2 font-lock-type-face)) + + ;; clojure symbols not matched by the previous regexps; influences CIDER's + ;; dynamic syntax highlighting (CDSH). See https://git.io/vxEEA: + (,(concat "\\(" clojure--sym-regexp "?\\)\\(/\\)\\(" clojure--sym-regexp "\\)") + (1 font-lock-type-face) + ;; 2nd and 3th matching groups can be font-locked to `nil' or `default'. + ;; CDSH seems to kick in only for functions and variables referenced w/o + ;; writing their namespaces. + (2 nil) + (3 nil)) + (,(concat "\\(" clojure--sym-regexp "\\)") + ;; this matching group must be font-locked to `nil' otherwise CDSH breaks. + (1 nil)) + + ;; #_ and (comment ...) macros. + (clojure--search-comment-macro 1 font-lock-comment-face t) + ;; Highlight `code` marks, just like `elisp'. + (,(rx "`" (group-n 1 (optional "#'") + (+ (or (syntax symbol) (syntax word)))) "`") + (1 'font-lock-constant-face prepend)) + ;; Highlight escaped characters in strings. + (clojure-font-lock-escaped-chars 0 'bold prepend) + ;; Highlight grouping constructs in regular expressions + (clojure-font-lock-regexp-groups + (1 'font-lock-regexp-grouping-construct prepend)))) + "Default expressions to highlight in Clojure mode.") + +(defun clojure-font-lock-syntactic-face-function (state) + "Find and highlight text with a Clojure-friendly syntax table. + +This function is passed to `font-lock-syntactic-face-function', +which is called with a single parameter, STATE (which is, in +turn, returned by `parse-partial-sexp' at the beginning of the +highlighted region)." + (if (nth 3 state) + ;; This might be a (doc)string or a |...| symbol. + (let ((startpos (nth 8 state))) + (if (eq (char-after startpos) ?|) + ;; This is not a string, but a |...| symbol. + nil + (let* ((listbeg (nth 1 state)) + (firstsym (and listbeg + (save-excursion + (goto-char listbeg) + (and (looking-at "([ \t\n]*\\(\\(\\sw\\|\\s_\\)+\\)") + (match-string 1))))) + (docelt (and firstsym + (function-get (intern-soft firstsym) + lisp-doc-string-elt-property)))) + (if (and docelt + ;; It's a string in a form that can have a docstring. + ;; Check whether it's in docstring position. + (save-excursion + (when (functionp docelt) + (goto-char (match-end 1)) + (setq docelt (funcall docelt))) + (goto-char listbeg) + (forward-char 1) + (ignore-errors + (while (and (> docelt 0) (< (point) startpos) + (progn (forward-sexp 1) t)) + ;; ignore metadata and type hints + (unless (looking-at "[ \n\t]*\\(\\^[A-Z:].+\\|\\^?{.+\\)") + (setq docelt (1- docelt))))) + (and (zerop docelt) (<= (point) startpos) + (progn (forward-comment (point-max)) t) + (= (point) (nth 8 state))))) + font-lock-doc-face + font-lock-string-face)))) + font-lock-comment-face)) + +(defun clojure-font-lock-setup () + "Configures font-lock for editing Clojure code." + (setq-local font-lock-multiline t) + (add-to-list 'font-lock-extend-region-functions + #'clojure-font-lock-extend-region-def t) + (setq font-lock-defaults + '(clojure-font-lock-keywords ; keywords + nil nil + (("+-*/.<>=!?$%_&:" . "w")) ; syntax alist + nil + (font-lock-mark-block-function . mark-defun) + (font-lock-syntactic-face-function + . clojure-font-lock-syntactic-face-function)))) + +(defun clojure-font-lock-def-at-point (point) + "Range between the top-most def* and the fourth element after POINT. +Note that this means that there is no guarantee of proper font +locking in def* forms that are not at top level." + (goto-char point) + (ignore-errors + (beginning-of-defun)) + + (let ((beg-def (point))) + (when (and (not (= point beg-def)) + (looking-at "(def")) + (ignore-errors + ;; move forward as much as possible until failure (or success) + (forward-char) + (dotimes (_ 4) + (forward-sexp))) + (cons beg-def (point))))) + +(defun clojure-font-lock-extend-region-def () + "Set region boundaries to include the first four elements of def* forms." + (let ((changed nil)) + (let ((def (clojure-font-lock-def-at-point font-lock-beg))) + (when def + (cl-destructuring-bind (def-beg . def-end) def + (when (and (< def-beg font-lock-beg) + (< font-lock-beg def-end)) + (setq font-lock-beg def-beg + changed t))))) + (let ((def (clojure-font-lock-def-at-point font-lock-end))) + (when def + (cl-destructuring-bind (def-beg . def-end) def + (when (and (< def-beg font-lock-end) + (< font-lock-end def-end)) + (setq font-lock-end def-end + changed t))))) + changed)) + +(defun clojure--font-locked-as-string-p (&optional regexp) + "Non-nil if the char before point is font-locked as a string. +If REGEXP is non-nil, also check whether current string is +preceeded by a #." + (let ((face (get-text-property (1- (point)) 'face))) + (and (or (and (listp face) + (memq 'font-lock-string-face face)) + (eq 'font-lock-string-face face)) + (or (clojure-string-start t) + (unless regexp + (clojure-string-start nil)))))) + +(defun clojure-font-lock-escaped-chars (bound) + "Highlight \escaped chars in strings. +BOUND denotes a buffer position to limit the search." + (let ((found nil)) + (while (and (not found) + (re-search-forward "\\\\." bound t)) + + (setq found (clojure--font-locked-as-string-p))) + found)) + +(defun clojure-font-lock-regexp-groups (bound) + "Highlight grouping constructs in regular expression. + +BOUND denotes the maximum number of characters (relative to the +point) to check." + (let ((found nil)) + (while (and (not found) + (re-search-forward (eval-when-compile + (concat + ;; A group may start using several alternatives: + "\\(\\(?:" + ;; 1. (? special groups + "(\\?\\(?:" + ;; a) non-capturing group (?:X) + ;; b) independent non-capturing group (?>X) + ;; c) zero-width positive lookahead (?=X) + ;; d) zero-width negative lookahead (?!X) + "[:=!>]\\|" + ;; e) zero-width positive lookbehind (?<=X) + ;; f) zero-width negative lookbehind (?X) + "<[[:alnum:]]+>" + "\\)\\|" ;; end of special groups + ;; 2. normal capturing groups ( + ;; 3. we also highlight alternative + ;; separarators |, and closing parens ) + "[|()]" + "\\)\\)")) + bound t)) + (setq found (clojure--font-locked-as-string-p 'regexp))) + found)) + +;; Docstring positions +(put 'ns 'clojure-doc-string-elt 2) +(put 'def 'clojure-doc-string-elt 2) +(put 'defn 'clojure-doc-string-elt 2) +(put 'defn- 'clojure-doc-string-elt 2) +(put 'defmulti 'clojure-doc-string-elt 2) +(put 'defmacro 'clojure-doc-string-elt 2) +(put 'definline 'clojure-doc-string-elt 2) +(put 'defprotocol 'clojure-doc-string-elt 2) +(put 'deftask 'clojure-doc-string-eld 2) ;; common Boot macro + +;;; Vertical alignment +(defcustom clojure-align-forms-automatically nil + "If non-nil, vertically align some forms automatically. +Automatically means it is done as part of indenting code. This +applies to binding forms (`clojure-align-binding-forms'), to cond +forms (`clojure-align-cond-forms') and to map literals. For +instance, selecting a map a hitting \\`\\[indent-for-tab-command]' +will align the values like this: + {:some-key 10 + :key2 20}" + :package-version '(clojure-mode . "5.1") + :safe #'booleanp + :type 'boolean) + +(defconst clojure--align-separator-newline-regexp "^ *$") + +(defcustom clojure-align-separator clojure--align-separator-newline-regexp + "The separator that will be passed to `align-region' when performing vertical alignment." + :package-version '(clojure-mode . "5.10") + :type `(choice (const :tag "Make blank lines prevent vertical alignment from happening." + ,clojure--align-separator-newline-regexp) + (other :tag "Allow blank lines to happen within a vertically-aligned expression." + 'entire))) + +(defcustom clojure-align-reader-conditionals nil + "Whether to align reader conditionals, as if they were maps." + :package-version '(clojure-mode . "5.10") + :safe #'booleanp + :type 'boolean) + +(defcustom clojure-align-binding-forms + '("let" "when-let" "when-some" "if-let" "if-some" "binding" "loop" + "doseq" "for" "with-open" "with-local-vars" "with-redefs") + "List of strings matching forms that have binding forms." + :package-version '(clojure-mode . "5.1") + :safe #'listp + :type '(repeat string)) + +(defcustom clojure-align-cond-forms '("condp" "cond" "cond->" "cond->>" "case" "are") + "List of strings identifying cond-like forms." + :package-version '(clojure-mode . "5.1") + :safe #'listp + :type '(repeat string)) + +(defvar clojure--beginning-of-reader-conditional-regexp + "#\\?@(\\|#\\?(" + "Regexp denoting the beginning of a reader conditional.") + +(defun clojure--position-for-alignment () + "Non-nil if the sexp around point should be automatically aligned. +This function expects to be called immediately after an +open-brace or after the function symbol in a function call. + +First check if the sexp around point is a map literal, or is a +call to one of the vars listed in `clojure-align-cond-forms'. If +it isn't, return nil. If it is, return non-nil and place point +immediately before the forms that should be aligned. + +For instance, in a map literal point is left immediately before +the first key; while, in a let-binding, point is left inside the +binding vector and immediately before the first binding +construct." + (let ((point (point))) + ;; Are we in a map? + (or (and (eq (char-before) ?{) + (not (eq (char-before (1- point)) ?\#))) + ;; Are we in a reader conditional? + (and clojure-align-reader-conditionals + (looking-back clojure--beginning-of-reader-conditional-regexp (- (point) 4))) + ;; Are we in a cond form? + (let* ((fun (car (member (thing-at-point 'symbol) clojure-align-cond-forms))) + (method (and fun (clojure--get-indent-method fun))) + ;; The number of special arguments in the cond form is + ;; the number of sexps we skip before aligning. + (skip (cond ((numberp method) method) + ((null method) 0) + ((sequencep method) (elt method 0))))) + (when (and fun (numberp skip)) + (clojure-forward-logical-sexp skip) + (comment-forward (point-max)) + fun)) ; Return non-nil (the var name). + ;; Are we in a let-like form? + (when (member (thing-at-point 'symbol) + clojure-align-binding-forms) + ;; Position inside the binding vector. + (clojure-forward-logical-sexp) + (backward-sexp) + (when (eq (char-after) ?\[) + (forward-char 1) + (comment-forward (point-max)) + ;; Return non-nil. + t))))) + +(defun clojure--find-sexp-to-align (end) + "Non-nil if there's a sexp ahead to be aligned before END. +Place point as in `clojure--position-for-alignment'." + ;; Look for a relevant sexp. + (let ((found)) + (while (and (not found) + (search-forward-regexp + (concat (when clojure-align-reader-conditionals + (concat clojure--beginning-of-reader-conditional-regexp + "\\|")) + "{\\|(" + (regexp-opt + (append clojure-align-binding-forms + clojure-align-cond-forms) + 'symbols)) + end 'noerror)) + + (let ((ppss (syntax-ppss))) + ;; If we're in a string or comment. + (unless (or (elt ppss 3) + (elt ppss 4)) + ;; Only stop looking if we successfully position + ;; the point. + (setq found (clojure--position-for-alignment))))) + found)) + +(defun clojure--search-whitespace-after-next-sexp (&optional bound _noerror) + "Move point after all whitespace after the next sexp. + +Set the match data group 1 to be this region of whitespace and +return point. + +BOUND is bounds the whitespace search." + (unwind-protect + (ignore-errors + (clojure-forward-logical-sexp 1) + (search-forward-regexp "\\([,\s\t]*\\)" bound) + (pcase (syntax-after (point)) + ;; End-of-line, try again on next line. + (`(12) (clojure--search-whitespace-after-next-sexp bound)) + ;; Closing paren, stop here. + (`(5 . ,_) nil) + ;; Anything else is something to align. + (_ (point)))) + (when (and bound (> (point) bound)) + (goto-char bound)))) + +(defun clojure-align (beg end) + "Vertically align the contents of the sexp around point. +If region is active, align it. Otherwise, align everything in the +current \"top-level\" sexp. +When called from lisp code align everything between BEG and END." + (interactive (if (use-region-p) + (list (region-beginning) (region-end)) + (save-excursion + (let ((end (progn (end-of-defun) + (point)))) + (clojure-backward-logical-sexp) + (list (point) end))))) + (setq end (copy-marker end)) + (save-excursion + (goto-char beg) + (while (clojure--find-sexp-to-align end) + (let ((sexp-end (save-excursion + (backward-up-list) + (forward-sexp 1) + (point-marker))) + (clojure-align-forms-automatically nil) + (count 1)) + ;; For some bizarre reason, we need to `align-region' once for each + ;; group. + (save-excursion + (while (search-forward-regexp "^ *\n" sexp-end 'noerror) + (cl-incf count))) + (dotimes (_ count) + (align-region (point) sexp-end nil + `((clojure-align (regexp . clojure--search-whitespace-after-next-sexp) + (group . 1) + (separate . ,clojure-align-separator) + (repeat . t))) + nil)) + ;; Reindent after aligning because of #360. + (indent-region (point) sexp-end))))) + +;;; Indentation +(defun clojure-indent-region (beg end) + "Like `indent-region', but also maybe align forms. +Forms between BEG and END are aligned according to +`clojure-align-forms-automatically'." + (prog1 (let ((indent-region-function nil)) + (indent-region beg end)) + (when clojure-align-forms-automatically + (condition-case nil + (clojure-align beg end) + (scan-error nil))))) + +(defun clojure-indent-line () + "Indent current line as Clojure code." + (if (clojure-in-docstring-p) + (save-excursion + (beginning-of-line) + (when (and (looking-at "^\\s-*") + (<= (string-width (match-string-no-properties 0)) + (string-width (clojure-docstring-fill-prefix)))) + (replace-match (clojure-docstring-fill-prefix)))) + (lisp-indent-line))) + +(defvar clojure-get-indent-function nil + "Function to get the indent spec of a symbol. +This function should take one argument, the name of the symbol as +a string. This name will be exactly as it appears in the buffer, +so it might start with a namespace alias. + +This function is analogous to the `clojure-indent-function' +symbol property, and its return value should match one of the +allowed values of this property. See `clojure-indent-function' +for more information.") + +(defun clojure--get-indent-method (function-name) + "Return the indent spec for the symbol named FUNCTION-NAME. +FUNCTION-NAME is a string. If it contains a `/', also try only +the part after the `/'. + +Look for a spec using `clojure-get-indent-function', then try the +`clojure-indent-function' and `clojure-backtracking-indent' +symbol properties." + (or (when (functionp clojure-get-indent-function) + (funcall clojure-get-indent-function function-name)) + (get (intern-soft function-name) 'clojure-indent-function) + (get (intern-soft function-name) 'clojure-backtracking-indent) + (when (string-match "/\\([^/]+\\)\\'" function-name) + (or (get (intern-soft (match-string 1 function-name)) + 'clojure-indent-function) + (get (intern-soft (match-string 1 function-name)) + 'clojure-backtracking-indent))) + (when (string-match (rx (or "let" "when" "while") (syntax symbol)) + function-name) + (clojure--get-indent-method (substring (match-string 0 function-name) 0 -1))))) + +(defvar clojure--current-backtracking-depth 0) + +(defun clojure--find-indent-spec-backtracking () + "Return the indent sexp that applies to the sexp at point. +Implementation function for `clojure--find-indent-spec'." + (when (and (>= clojure-max-backtracking clojure--current-backtracking-depth) + (not (looking-at "^"))) + (let ((clojure--current-backtracking-depth (1+ clojure--current-backtracking-depth)) + (pos 0)) + ;; Count how far we are from the start of the sexp. + (while (ignore-errors (clojure-backward-logical-sexp 1) + (not (or (bobp) + (eq (char-before) ?\n)))) + (cl-incf pos)) + (let* ((function (thing-at-point 'symbol)) + (method (or (when function ;; Is there a spec here? + (clojure--get-indent-method function)) + (ignore-errors + ;; Otherwise look higher up. + (pcase (syntax-ppss) + (`(,(pred (< 0)) ,start . ,_) + (goto-char start) + (clojure--find-indent-spec-backtracking))))))) + (when (numberp method) + (setq method (list method))) + (pcase method + ((pred functionp) + (when (= pos 0) + method)) + ((pred sequencep) + (pcase (length method) + (`0 nil) + (`1 (let ((head (elt method 0))) + (when (or (= pos 0) (sequencep head)) + head))) + (l (if (>= pos l) + (elt method (1- l)) + (elt method pos))))) + ((or `defun `:defn) + (when (= pos 0) + :defn)) + (_ + (message "Invalid indent spec for `%s': %s" function method) + nil)))))) + +(defun clojure--find-indent-spec () + "Return the indent spec that applies to current sexp. +If `clojure-use-backtracking-indent' is non-nil, also do +backtracking up to a higher-level sexp in order to find the +spec." + (if clojure-use-backtracking-indent + (save-excursion + (clojure--find-indent-spec-backtracking)) + (let ((function (thing-at-point 'symbol))) + (clojure--get-indent-method function)))) + +(defun clojure--normal-indent (last-sexp indent-mode) + "Return the normal indentation column for a sexp. +Point should be after the open paren of the _enclosing_ sexp, and +LAST-SEXP is the start of the previous sexp (immediately before +the sexp being indented). INDENT-MODE is any of the values +accepted by `clojure-indent-style'." + (goto-char last-sexp) + (forward-sexp 1) + (clojure-backward-logical-sexp 1) + (let ((last-sexp-start nil)) + (if (ignore-errors + ;; `backward-sexp' until we reach the start of a sexp that is the + ;; first of its line (the start of the enclosing sexp). + (while (string-match + "[^[:blank:]]" + (buffer-substring (line-beginning-position) (point))) + (setq last-sexp-start (prog1 (point) + (forward-sexp -1)))) + t) + ;; Here we have found an arg before the arg we're indenting which is at + ;; the start of a line. Every mode simply aligns on this case. + (current-column) + ;; Here we have reached the start of the enclosing sexp (point is now at + ;; the function name), so the behaviour depends on INDENT-MODE and on + ;; whether there's also an argument on this line (case A or B). + (let ((case-a ; The meaning of case-a is explained in `clojure-indent-style'. + (and last-sexp-start + (< last-sexp-start (line-end-position))))) + (cond + ;; For compatibility with the old `clojure-defun-style-default-indent', any + ;; value other than these 3 is equivalent to `always-body'. + ((not (memq indent-mode '(:always-align :align-arguments nil))) + (+ (current-column) lisp-body-indent -1)) + ;; There's an arg after the function name, so align with it. + (case-a (goto-char last-sexp-start) + (current-column)) + ;; Not same line. + ((eq indent-mode :align-arguments) + (+ (current-column) lisp-body-indent -1)) + ;; Finally, just align with the function name. + (t (current-column))))))) + +(defun clojure--not-function-form-p () + "Non-nil if form at point doesn't represent a function call." + (or (member (char-after) '(?\[ ?\{)) + (save-excursion ;; Catch #?@ (:cljs ...) + (skip-chars-backward "\r\n[:blank:]") + (when (eq (char-before) ?@) + (forward-char -1)) + (and (eq (char-before) ?\?) + (eq (char-before (1- (point))) ?\#))) + ;; Car of form is not a symbol. + (not (looking-at ".\\(?:\\sw\\|\\s_\\)")))) + +;; Check the general context, and provide indentation for data structures and +;; special macros. If current form is a function (or non-special macro), +;; delegate indentation to `clojure--normal-indent'. +(defun clojure-indent-function (indent-point state) + "When indenting a line within a function call, indent properly. + +INDENT-POINT is the position where the user typed TAB, or equivalent. +Point is located at the point to indent under (for default indentation); +STATE is the `parse-partial-sexp' state for that position. + +If the current line is in a call to a Clojure function with a +non-nil property `clojure-indent-function', that specifies how to do +the indentation. + +The property value can be + +- `defun', meaning indent `defun'-style; +- an integer N, meaning indent the first N arguments specially + like ordinary function arguments and then indent any further + arguments like a body; +- a function to call just as this function was called. + If that function returns nil, that means it doesn't specify + the indentation. +- a list, which is used by `clojure-backtracking-indent'. + +This function also returns nil meaning don't specify the indentation." + ;; Goto to the open-paren. + (goto-char (elt state 1)) + ;; Maps, sets, vectors and reader conditionals. + (if (clojure--not-function-form-p) + (1+ (current-column)) + ;; Function or macro call. + (forward-char 1) + (let ((method (clojure--find-indent-spec)) + (last-sexp calculate-lisp-indent-last-sexp) + (containing-form-column (1- (current-column)))) + (pcase method + ((or (pred integerp) `(,method)) + (let ((pos -1)) + (condition-case nil + (while (and (<= (point) indent-point) + (not (eobp))) + (clojure-forward-logical-sexp 1) + (cl-incf pos)) + ;; If indent-point is _after_ the last sexp in the + ;; current sexp, we detect that by catching the + ;; `scan-error'. In that case, we should return the + ;; indentation as if there were an extra sexp at point. + (scan-error (cl-incf pos))) + (cond + ;; The first non-special arg. Rigidly reduce indentation. + ((= pos (1+ method)) + (+ lisp-body-indent containing-form-column)) + ;; Further non-special args, align with the arg above. + ((> pos (1+ method)) + (clojure--normal-indent last-sexp :always-align)) + ;; Special arg. Rigidly indent with a large indentation. + (t + (+ (* 2 lisp-body-indent) containing-form-column))))) + (`:defn + (+ lisp-body-indent containing-form-column)) + ((pred functionp) + (funcall method indent-point state)) + ;; No indent spec, do the default. + (`nil + (let ((function (thing-at-point 'symbol))) + (cond + ;; Preserve useful alignment of :require (and friends) in `ns' forms. + ((and function (string-match "^:" function)) + (clojure--normal-indent last-sexp :always-align)) + ;; This should be identical to the :defn above. + ((and function + (string-match "\\`\\(?:\\S +/\\)?\\(def[a-z]*\\|with-\\)" + function) + (not (string-match "\\`default" (match-string 1 function)))) + (+ lisp-body-indent containing-form-column)) + ;; Finally, nothing special here, just respect the user's + ;; preference. + (t (clojure--normal-indent last-sexp clojure-indent-style))))))))) + +;;; Setting indentation +(defun put-clojure-indent (sym indent) + "Instruct `clojure-indent-function' to indent the body of SYM by INDENT." + (put sym 'clojure-indent-function indent)) + +(defmacro define-clojure-indent (&rest kvs) + "Call `put-clojure-indent' on a series, KVS." + `(progn + ,@(mapcar (lambda (x) `(put-clojure-indent + (quote ,(car x)) ,(cadr x))) + kvs))) + +(defun add-custom-clojure-indents (name value) + "Allow `clojure-defun-indents' to indent user-specified macros. + +Requires the macro's NAME and a VALUE." + (custom-set-default name value) + (mapcar (lambda (x) + (put-clojure-indent x 'defun)) + value)) + +(defcustom clojure-defun-indents nil + "List of additional symbols with defun-style indentation in Clojure. + +You can use this to let Emacs indent your own macros the same way +that it indents built-in macros like with-open. This variable +only works when set via the customize interface (`setq' won't +work). To set it from Lisp code, use + (put-clojure-indent \\='some-symbol :defn)." + :type '(repeat symbol) + :set 'add-custom-clojure-indents) + +(define-clojure-indent + ;; built-ins + (ns 1) + (fn :defn) + (def :defn) + (defn :defn) + (bound-fn :defn) + (if 1) + (if-not 1) + (case 1) + (cond 0) + (condp 2) + (cond-> 1) + (cond->> 1) + (when 1) + (while 1) + (when-not 1) + (when-first 1) + (do 0) + (future 0) + (comment 0) + (doto 1) + (locking 1) + (proxy '(2 nil nil (:defn))) + (as-> 2) + (fdef 1) + + (reify '(:defn (1))) + (deftype '(2 nil nil (:defn))) + (defrecord '(2 nil nil (:defn))) + (defprotocol '(1 (:defn))) + (definterface '(1 (:defn))) + (extend 1) + (extend-protocol '(1 :defn)) + (extend-type '(1 :defn)) + ;; specify and specify! are from ClojureScript + (specify '(1 :defn)) + (specify! '(1 :defn)) + (try 0) + (catch 2) + (finally 0) + + ;; binding forms + (let 1) + (letfn '(1 ((:defn)) nil)) + (binding 1) + (loop 1) + (for 1) + (doseq 1) + (dotimes 1) + (when-let 1) + (if-let 1) + (when-some 1) + (if-some 1) + (this-as 1) ; ClojureScript + + (defmethod :defn) + + ;; clojure.test + (testing 1) + (deftest :defn) + (are 2) + (use-fixtures :defn) + + ;; core.logic + (run :defn) + (run* :defn) + (fresh :defn) + + ;; core.async + (alt! 0) + (alt!! 0) + (go 0) + (go-loop 1) + (thread 0)) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Better docstring filling for clojure-mode +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun clojure-string-start (&optional regex) + "Return the position of the \" that begins the string at point. +If REGEX is non-nil, return the position of the # that begins the +regex at point. If point is not inside a string or regex, return +nil." + (when (nth 3 (syntax-ppss)) ;; Are we really in a string? + (save-excursion + (save-match-data + ;; Find a quote that appears immediately after whitespace, + ;; beginning of line, hash, or an open paren, brace, or bracket + (re-search-backward "\\(\\s-\\|^\\|#\\|(\\|\\[\\|{\\)\\(\"\\)") + (let ((beg (match-beginning 2))) + (when beg + (if regex + (and (char-before beg) (eq ?# (char-before beg)) (1- beg)) + (when (not (eq ?# (char-before beg))) + beg)))))))) + +(defun clojure-char-at-point () + "Return the char at point or nil if at buffer end." + (when (not (= (point) (point-max))) + (buffer-substring-no-properties (point) (1+ (point))))) + +(defun clojure-char-before-point () + "Return the char before point or nil if at buffer beginning." + (when (not (= (point) (point-min))) + (buffer-substring-no-properties (point) (1- (point))))) + +(defun clojure-toggle-keyword-string () + "Convert the string or keyword at point to keyword or string." + (interactive) + (let ((original-point (point))) + (while (and (> (point) 1) + (not (equal "\"" (buffer-substring-no-properties (point) (+ 1 (point))))) + (not (equal ":" (buffer-substring-no-properties (point) (+ 1 (point)))))) + (backward-char)) + (cond + ((equal 1 (point)) + (error "Beginning of file reached, this was probably a mistake")) + ((equal "\"" (buffer-substring-no-properties (point) (+ 1 (point)))) + (insert ":" (substring (clojure-delete-and-extract-sexp) 1 -1))) + ((equal ":" (buffer-substring-no-properties (point) (+ 1 (point)))) + (insert "\"" (substring (clojure-delete-and-extract-sexp) 1) "\""))) + (goto-char original-point))) + +(defun clojure-delete-and-extract-sexp () + "Delete the surrounding sexp and return it." + (let ((begin (point))) + (forward-sexp) + (let ((result (buffer-substring begin (point)))) + (delete-region begin (point)) + result))) + + + +(defcustom clojure-cache-project-dir t + "Whether to cache the results of `clojure-project-dir'." + :type 'boolean + :safe #'booleanp + :package-version '(clojure-mode . "5.8.0")) + +(defvar-local clojure-cached-project-dir nil + "A project dir cache used to speed up related operations.") + +(defun clojure-project-dir (&optional dir-name) + "Return the absolute path to the project's root directory. + +Call is delegated down to `clojure-project-root-function' with +optional DIR-NAME as argument. + +When `clojure-cache-project-dir' is t the results of the command +are cached in a buffer local variable (`clojure-cached-project-dir')." + (let ((project-dir (or clojure-cached-project-dir + (funcall clojure-project-root-function dir-name)))) + (when (and clojure-cache-project-dir + (derived-mode-p 'clojure-mode) + (not clojure-cached-project-dir)) + (setq clojure-cached-project-dir project-dir)) + project-dir)) + +(defun clojure-current-project (&optional dir-name) + "Return the current project as a cons cell usable by project.el. + +Call is delegated down to `clojure-clojure-dir' with +optional DIR-NAME as argument." + (let ((project-dir (clojure-project-dir dir-name))) + (if project-dir + (cons 'clojure project-dir) + nil))) + +(defun clojure-project-root-path (&optional dir-name) + "Return the absolute path to the project's root directory. + +Use `default-directory' if DIR-NAME is nil. +Return nil if not inside a project." + (let* ((dir-name (or dir-name default-directory)) + (choices (delq nil + (mapcar (lambda (fname) + (locate-dominating-file dir-name fname)) + clojure-build-tool-files)))) + (when (> (length choices) 0) + (car (sort choices #'file-in-directory-p))))) + +(defun clojure-project-relative-path (path) + "Denormalize PATH by making it relative to the project root." + (file-relative-name path (clojure-project-dir))) + + +;;; ns manipulation +(defun clojure-expected-ns (&optional path) + "Return the namespace matching PATH. + +PATH is expected to be an absolute file path. + +If PATH is nil, use the path to the file backing the current buffer." + (let* ((path (or path (file-truename (buffer-file-name)))) + (relative (clojure-project-relative-path path)) + (sans-file-type (substring relative 0 (- (length (file-name-extension path t))))) + (sans-file-sep (mapconcat 'identity (cdr (split-string sans-file-type "/")) ".")) + (sans-underscores (replace-regexp-in-string "_" "-" sans-file-sep))) + ;; Drop prefix from ns for projects with structure src/{clj,cljs,cljc} + (replace-regexp-in-string "\\`clj[scx]?\\." "" sans-underscores))) + +(defun clojure-insert-ns-form-at-point () + "Insert a namespace form at point." + (interactive) + (insert (format "(ns %s)" (funcall clojure-expected-ns-function)))) + +(defun clojure-insert-ns-form () + "Insert a namespace form at the beginning of the buffer." + (interactive) + (widen) + (goto-char (point-min)) + (clojure-insert-ns-form-at-point)) + +(defun clojure-update-ns () + "Update the namespace of the current buffer. +Useful if a file has been renamed." + (interactive) + (let ((nsname (funcall clojure-expected-ns-function))) + (when nsname + (save-excursion + (save-match-data + (if (clojure-find-ns) + (progn + (replace-match nsname nil nil nil 4) + (message "ns form updated to `%s'" nsname) + (setq clojure-cached-ns nsname)) + (user-error "Can't find ns form"))))))) + +(defun clojure--sort-following-sexps () + "Sort sexps between point and end of current sexp. +Comments at the start of a line are considered part of the +following sexp. Comments at the end of a line (after some other +content) are considered part of the preceding sexp." + ;; Here we're after the :require/:import symbol. + (save-restriction + (narrow-to-region (point) (save-excursion + (up-list) + (1- (point)))) + (skip-chars-forward "\r\n[:blank:]") + (sort-subr nil + (lambda () (skip-chars-forward "\r\n[:blank:]")) + ;; Move to end of current top-level thing. + (lambda () + (condition-case nil + (while t (up-list)) + (scan-error nil)) + ;; We could be inside a symbol instead of a sexp. + (unless (looking-at "\\s-\\|$") + (clojure-forward-logical-sexp)) + ;; move past comments at the end of the line. + (search-forward-regexp "$")) + ;; Move to start of ns name. + (lambda () + (comment-forward) + (skip-chars-forward "[:blank:]\n\r[(") + (clojure-forward-logical-sexp) + (forward-sexp -1) + nil) + ;; Move to end of ns name. + (lambda () + (clojure-forward-logical-sexp))) + (goto-char (point-max)) + ;; Does the last line now end in a comment? + (when (nth 4 (parse-partial-sexp (point-min) (point))) + (insert "\n")))) + +(defun clojure-sort-ns () + "Internally sort each sexp inside the ns form." + (interactive) + (comment-normalize-vars) + (if (clojure-find-ns) + (save-excursion + (goto-char (match-beginning 0)) + (redisplay) + (let ((beg (point)) + (ns)) + (forward-sexp 1) + (setq ns (buffer-substring beg (point))) + (forward-char -1) + (while (progn (forward-sexp -1) + (looking-at "(:[a-z]")) + (save-excursion + (forward-char 1) + (forward-sexp 1) + (clojure--sort-following-sexps))) + (goto-char beg) + (if (looking-at (regexp-quote ns)) + (message "ns form is already sorted") + (sleep-for 0.1) + (redisplay) + (message "ns form has been sorted") + (sleep-for 0.1)))) + (user-error "Can't find ns form"))) + +(defconst clojure-namespace-name-regex + (rx line-start + "(" + (zero-or-one (group (regexp "clojure.core/"))) + (zero-or-one (submatch "in-")) + "ns" + (zero-or-one "+") + (one-or-more (any whitespace "\n")) + (zero-or-more (or (submatch (zero-or-one "#") + "^{" + (zero-or-more (not (any "}"))) + "}") + (zero-or-more "^:" + (one-or-more (not (any whitespace))))) + (one-or-more (any whitespace "\n"))) + (zero-or-one (any ":'")) ;; (in-ns 'foo) or (ns+ :user) + (group (one-or-more (not (any "()\"" whitespace))) symbol-end))) + +(defcustom clojure-cache-ns nil + "Whether to cache the results of `clojure-find-ns'. + +Note that this won't work well in buffers with multiple namespace +declarations (which rarely occur in practice) and you'll +have to invalidate this manually after changing the ns for +a buffer. If you update the ns using `clojure-update-ns' +the cached value will be updated automatically." + :type 'boolean + :safe #'booleanp + :package-version '(clojure-mode . "5.8.0")) + +(defvar-local clojure-cached-ns nil + "A buffer ns cache used to speed up ns-related operations.") + +(defun clojure-find-ns () + "Return the namespace of the current Clojure buffer. +Return the namespace closest to point and above it. If there are +no namespaces above point, return the first one in the buffer. + +The results will be cached if `clojure-cache-ns' is set to t." + (if (and clojure-cache-ns clojure-cached-ns) + clojure-cached-ns + (let ((ns (save-excursion + (save-restriction + (widen) + + ;; Move to top-level to avoid searching from inside ns + (ignore-errors (while t (up-list nil t t))) + + ;; The closest ns form above point. + (when (or (re-search-backward clojure-namespace-name-regex nil t) + ;; Or any form at all. + (and (goto-char (point-min)) + (re-search-forward clojure-namespace-name-regex nil t))) + (match-string-no-properties 4)))))) + (setq clojure-cached-ns ns) + ns))) + +(defun clojure-show-cache () + "Display cached values if present. +Useful for debugging." + (interactive) + (message "Cached Project: %s, Cached Namespace: %s" clojure-cached-project-dir clojure-cached-ns)) + +(defun clojure-clear-cache () + "Clear all buffer-local cached values. + +Normally you'd need to do this very infrequently - e.g. +after renaming the root folder of project or after +renaming a namespace." + (interactive) + (setq clojure-cached-project-dir nil + clojure-cached-ns nil) + (message "Buffer-local clojure-mode cache cleared")) + +(defconst clojure-def-type-and-name-regex + (concat "(\\(?:\\(?:\\sw\\|\\s_\\)+/\\)?" + ;; Declaration + "\\(def\\(?:\\sw\\|\\s_\\)*\\)\\>" + ;; Any whitespace + "[ \r\n\t]*" + ;; Possibly type or metadata + "\\(?:#?^\\(?:{[^}]*}\\|\\(?:\\sw\\|\\s_\\)+\\)[ \r\n\t]*\\)*" + ;; Symbol name + "\\(\\(?:\\sw\\|\\s_\\)+\\)")) + +(defun clojure-find-def () + "Find the var declaration macro and symbol name of the current form. +Returns a list pair, e.g. (\"defn\" \"abc\") or (\"deftest\" \"some-test\")." + (save-excursion + (unless (looking-at clojure-def-type-and-name-regex) + (beginning-of-defun)) + (when (search-forward-regexp clojure-def-type-and-name-regex nil t) + (list (match-string-no-properties 1) + (match-string-no-properties 2))))) + + +;;; Sexp navigation + +(defun clojure--looking-at-non-logical-sexp () + "Return non-nil if text after point is \"non-logical\" sexp. +\"Non-logical\" sexp are ^metadata and #reader.macros." + (comment-normalize-vars) + (comment-forward (point-max)) + (looking-at-p "\\^\\|#[[:alpha:]]")) + +(defun clojure-forward-logical-sexp (&optional n) + "Move forward N logical sexps. +This will skip over sexps that don't represent objects, so that ^hints and +#reader.macros are considered part of the following sexp." + (interactive "p") + (unless n (setq n 1)) + (if (< n 0) + (clojure-backward-logical-sexp (- n)) + (let ((forward-sexp-function nil)) + (while (> n 0) + (while (clojure--looking-at-non-logical-sexp) + (forward-sexp 1)) + ;; The actual sexp + (forward-sexp 1) + (skip-chars-forward ",") + (setq n (1- n)))))) + +(defun clojure-backward-logical-sexp (&optional n) + "Move backward N logical sexps. +This will skip over sexps that don't represent objects, so that ^hints and +#reader.macros are considered part of the following sexp." + (interactive "p") + (unless n (setq n 1)) + (if (< n 0) + (clojure-forward-logical-sexp (- n)) + (let ((forward-sexp-function nil)) + (while (> n 0) + ;; The actual sexp + (backward-sexp 1) + ;; Non-logical sexps. + (while (and (not (bobp)) + (ignore-errors + (save-excursion + (backward-sexp 1) + (clojure--looking-at-non-logical-sexp)))) + (backward-sexp 1)) + (setq n (1- n)))))) + +(defun clojure-top-level-form-p (first-form) + "Return truthy if the first form matches FIRST-FORM." + (condition-case nil + (save-excursion + (beginning-of-defun) + (forward-char 1) + (clojure-forward-logical-sexp 1) + (clojure-backward-logical-sexp 1) + (looking-at-p first-form)) + (scan-error nil) + (end-of-buffer nil))) + +(defun clojure-sexp-starts-until-position (position) + "Return the starting points for forms before POSITION. +Positions are in descending order to aide in finding the first starting +position before the current position." + (save-excursion + (let (sexp-positions) + (condition-case nil + (while (< (point) position) + (clojure-forward-logical-sexp 1) + (clojure-backward-logical-sexp 1) + (push (point) sexp-positions) + (clojure-forward-logical-sexp 1)) + (scan-error nil)) + sexp-positions))) + +(defcustom clojure-toplevel-inside-comment-form nil + "Eval top level forms inside comment forms instead of the comment form itself. +Experimental. Function `cider-defun-at-point' is used extensively so if we +change this heuristic it needs to be bullet-proof and desired. While +testing, give an easy way to turn this new behavior off." + :type 'boolean + :safe #'booleanp + :package-version '(clojure-mode . "5.9.0")) + +(defun clojure-find-first (pred coll) + "Find first element of COLL for which PRED return truthy." + (let ((found) + (haystack coll)) + (while (and (not found) + haystack) + (if (funcall pred (car haystack)) + (setq found (car haystack)) + (setq haystack (cdr haystack)))) + found)) + +(defun clojure-beginning-of-defun-function (&optional n) + "Go to top level form. +Set as `beginning-of-defun-function' so that these generic +operators can be used. Given a positive N it will do it that +many times." + (let ((beginning-of-defun-function nil)) + (if (and clojure-toplevel-inside-comment-form + (clojure-top-level-form-p "comment")) + (condition-case nil + (save-match-data + (let ((original-position (point)) + clojure-comment-start clojure-comment-end) + (beginning-of-defun) + (setq clojure-comment-start (point)) + (end-of-defun) + (setq clojure-comment-end (point)) + (beginning-of-defun) + (forward-char 1) ;; skip paren so we start at comment + (clojure-forward-logical-sexp) ;; skip past the comment form itself + (if-let ((sexp-start (clojure-find-first (lambda (beg-pos) + (< beg-pos original-position)) + (clojure-sexp-starts-until-position + clojure-comment-end)))) + (progn (goto-char sexp-start) t) + (beginning-of-defun n)))) + (scan-error (beginning-of-defun n))) + (beginning-of-defun n)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Refactoring support +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Threading macros related +(defcustom clojure-thread-all-but-last nil + "Non-nil means do not thread the last expression. +This means that `clojure-thread-first-all' and +`clojure-thread-last-all' not thread the deepest sexp inside the +current sexp." + :package-version '(clojure-mode . "5.4.0") + :safe #'booleanp + :type 'boolean) + +(defun clojure--point-after (&rest actions) + "Return POINT after performing ACTIONS. + +An action is either the symbol of a function or a two element +list of (fn args) to pass to `apply''" + (save-excursion + (dolist (fn-and-args actions) + (let ((f (if (listp fn-and-args) (car fn-and-args) fn-and-args)) + (args (if (listp fn-and-args) (cdr fn-and-args) nil))) + (apply f args))) + (point))) + +(defun clojure--maybe-unjoin-line () + "Undo a `join-line' done by a threading command." + (when (get-text-property (point) 'clojure-thread-line-joined) + (remove-text-properties (point) (1+ (point)) '(clojure-thread-line-joined t)) + (insert "\n"))) + +(defun clojure--unwind-last () + "Unwind a thread last macro once. + +Point must be between the opening paren and the ->> symbol." + (forward-sexp) + (save-excursion + (let ((beg (point)) + (contents (clojure-delete-and-extract-sexp))) + (when (looking-at " *\n") + (join-line 'following)) + (clojure--ensure-parens-around-function-names) + (let* ((sexp-beg-line (line-number-at-pos)) + (sexp-end-line (progn (forward-sexp) + (line-number-at-pos))) + (multiline-sexp-p (not (= sexp-beg-line sexp-end-line)))) + (down-list -1) + (if multiline-sexp-p + (insert "\n") + ;; `clojure--maybe-unjoin-line' only works when unwinding sexps that were + ;; threaded in the same Emacs session, but it also catches cases that + ;; `multiline-sexp-p' doesn't. + (clojure--maybe-unjoin-line)) + (insert contents)))) + (forward-char)) + +(defun clojure--ensure-parens-around-function-names () + "Insert parens around function names if necessary." + (clojure--looking-at-non-logical-sexp) + (unless (looking-at "(") + (insert-parentheses 1) + (backward-up-list))) + +(defun clojure--unwind-first () + "Unwind a thread first macro once. + +Point must be between the opening paren and the -> symbol." + (forward-sexp) + (save-excursion + (let ((contents (clojure-delete-and-extract-sexp))) + (when (looking-at " *\n") + (join-line 'following)) + (clojure--ensure-parens-around-function-names) + (down-list) + (forward-sexp) + (insert contents) + (forward-sexp -1) + (clojure--maybe-unjoin-line))) + (forward-char)) + +(defun clojure--pop-out-of-threading () + "Raise a sexp up a level to unwind a threading form." + (save-excursion + (down-list 2) + (backward-up-list) + (raise-sexp))) + +(defun clojure--nothing-more-to-unwind () + "Return non-nil if a threaded form cannot be unwound further." + (save-excursion + (let ((beg (point))) + (forward-sexp) + (down-list -1) + (backward-sexp 2) ;; the last sexp, the threading macro + (when (looking-back "(\\s-*" (line-beginning-position)) + (backward-up-list)) ;; and the paren + (= beg (point))))) + +(defun clojure--fix-sexp-whitespace (&optional move-out) + "Fix whitespace after unwinding a threading form. + +Optional argument MOVE-OUT, if non-nil, means moves up a list +before fixing whitespace." + (save-excursion + (when move-out (backward-up-list)) + (let ((sexp (bounds-of-thing-at-point 'sexp))) + (clojure-indent-region (car sexp) (cdr sexp)) + (delete-trailing-whitespace (car sexp) (cdr sexp))))) + +;;;###autoload +(defun clojure-unwind () + "Unwind thread at point or above point by one level. +Return nil if there are no more levels to unwind." + (interactive) + (save-excursion + (let ((limit (save-excursion + (beginning-of-defun) + (point)))) + (ignore-errors + (when (looking-at "(") + (forward-char 1) + (forward-sexp 1))) + (search-backward-regexp "([^-]*->" limit) + (if (clojure--nothing-more-to-unwind) + (progn (clojure--pop-out-of-threading) + (clojure--fix-sexp-whitespace) + nil) + (down-list) + (prog1 (cond + ((looking-at "[^-]*->\\_>") (clojure--unwind-first)) + ((looking-at "[^-]*->>\\_>") (clojure--unwind-last))) + (clojure--fix-sexp-whitespace 'move-out)) + t)))) + +;;;###autoload +(defun clojure-unwind-all () + "Fully unwind thread at point or above point." + (interactive) + (while (clojure-unwind))) + +(defun clojure--remove-superfluous-parens () + "Remove extra parens from a form." + (when (looking-at "([^ )]+)") + (delete-pair))) + +(defun clojure--thread-first () + "Thread a nested sexp using ->." + (down-list) + (forward-symbol 1) + (unless (looking-at ")") + (let ((contents (clojure-delete-and-extract-sexp))) + (backward-up-list) + (just-one-space 0) + (save-excursion + (insert contents "\n") + (clojure--remove-superfluous-parens)) + (when (looking-at "\\s-*\n") + (join-line 'following) + (forward-char 1) + (put-text-property (point) (1+ (point)) + 'clojure-thread-line-joined t)) + t))) + +(defun clojure--thread-last () + "Thread a nested sexp using ->>." + (forward-sexp 2) + (down-list -1) + (backward-sexp) + (unless (eq (char-before) ?\() + (let ((contents (clojure-delete-and-extract-sexp))) + (just-one-space 0) + (backward-up-list) + (insert contents "\n") + (clojure--remove-superfluous-parens) + ;; cljr #255 Fix dangling parens + (forward-sexp) + (when (looking-back "^\\s-*\\()+\\)\\s-*" (line-beginning-position)) + (let ((pos (match-beginning 1))) + (put-text-property pos (1+ pos) 'clojure-thread-line-joined t)) + (join-line)) + t))) + +(defun clojure--threadable-p () + "Return non-nil if a form can be threaded." + (save-excursion + (forward-symbol 1) + (looking-at "[\n\r\t ]*("))) + +;;;###autoload +(defun clojure-thread () + "Thread by one more level an existing threading macro." + (interactive) + (ignore-errors + (when (looking-at "(") + (forward-char 1) + (forward-sexp 1))) + (search-backward-regexp "([^-]*->") + (down-list) + (when (clojure--threadable-p) + (prog1 (cond + ((looking-at "[^-]*->\\_>") (clojure--thread-first)) + ((looking-at "[^-]*->>\\_>") (clojure--thread-last))) + (clojure--fix-sexp-whitespace 'move-out)))) + +(defun clojure--thread-all (first-or-last-thread but-last) + "Fully thread the form at point. + +FIRST-OR-LAST-THREAD is \"->\" or \"->>\". + +When BUT-LAST is non-nil, the last expression is not threaded. +Default value is `clojure-thread-all-but-last'." + (save-excursion + (insert-parentheses 1) + (insert first-or-last-thread)) + (while (save-excursion (clojure-thread))) + (when (or but-last clojure-thread-all-but-last) + (clojure-unwind))) + +;;;###autoload +(defun clojure-thread-first-all (but-last) + "Fully thread the form at point using ->. + +When BUT-LAST is non-nil, the last expression is not threaded. +Default value is `clojure-thread-all-but-last'." + (interactive "P") + (clojure--thread-all "-> " but-last)) + +;;;###autoload +(defun clojure-thread-last-all (but-last) + "Fully thread the form at point using ->>. + +When BUT-LAST is non-nil, the last expression is not threaded. +Default value is `clojure-thread-all-but-last'." + (interactive "P") + (clojure--thread-all "->> " but-last)) + +;;; Cycling stuff + +(defcustom clojure-use-metadata-for-privacy nil + "If nil, `clojure-cycle-privacy' will use (defn- f []). +If t, it will use (defn ^:private f [])." + :package-version '(clojure-mode . "5.5.0") + :safe #'booleanp + :type 'boolean) + +;;;###autoload +(defun clojure-cycle-privacy () + "Make public the current private def, or vice-versa. +See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy" + (interactive) + (save-excursion + (ignore-errors (forward-char 7)) + (search-backward-regexp "(defn?\\(-\\| ^:private\\)?\\_>") + (if (match-string 1) + (replace-match "" nil nil nil 1) + (goto-char (match-end 0)) + (insert (if (or clojure-use-metadata-for-privacy + (equal (match-string 0) "(def")) + " ^:private" + "-"))))) + +(defun clojure--convert-collection (coll-open coll-close) + "Convert the collection at (point) by unwrapping it an wrapping it between COLL-OPEN and COLL-CLOSE." + (save-excursion + (while (and + (not (bobp)) + (not (looking-at "(\\|{\\|\\["))) + (backward-char)) + (when (or (eq ?\# (char-before)) + (eq ?\' (char-before))) + (delete-char -1)) + (when (and (bobp) + (not (memq (char-after) '(?\{ ?\( ?\[)))) + (user-error "Beginning of file reached, collection is not found")) + (insert coll-open (substring (clojure-delete-and-extract-sexp) 1 -1) coll-close))) + +;;;###autoload +(defun clojure-convert-collection-to-list () + "Convert collection at (point) to list." + (interactive) + (clojure--convert-collection "(" ")")) + +;;;###autoload +(defun clojure-convert-collection-to-quoted-list () + "Convert collection at (point) to quoted list." + (interactive) + (clojure--convert-collection "'(" ")")) + +;;;###autoload +(defun clojure-convert-collection-to-map () + "Convert collection at (point) to map." + (interactive) + (clojure--convert-collection "{" "}")) + +;;;###autoload +(defun clojure-convert-collection-to-vector () + "Convert collection at (point) to vector." + (interactive) + (clojure--convert-collection "[" "]")) + +;;;###autoload +(defun clojure-convert-collection-to-set () + "Convert collection at (point) to set." + (interactive) + (clojure--convert-collection "#{" "}")) + +(defun clojure--in-string-p () + "Check whether the point is currently in a string." + (nth 3 (syntax-ppss))) + +(defun clojure--goto-if () + "Find the first surrounding if or if-not expression." + (when (clojure--in-string-p) + (while (or (not (looking-at "(")) + (clojure--in-string-p)) + (backward-char))) + (while (not (looking-at "\\((if \\)\\|\\((if-not \\)")) + (condition-case nil + (backward-up-list) + (scan-error (user-error "No if or if-not found"))))) + +;;;###autoload +(defun clojure-cycle-if () + "Change a surrounding if to if-not, or vice-versa. + +See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if" + (interactive) + (save-excursion + (clojure--goto-if) + (cond + ((looking-at "(if-not") + (forward-char 3) + (delete-char 4) + (forward-sexp 2) + (transpose-sexps 1)) + ((looking-at "(if") + (forward-char 3) + (insert "-not") + (forward-sexp 2) + (transpose-sexps 1))))) + +;; TODO: Remove code duplication with `clojure--goto-if'. +(defun clojure--goto-when () + "Find the first surrounding when or when-not expression." + (when (clojure--in-string-p) + (while (or (not (looking-at "(")) + (clojure--in-string-p)) + (backward-char))) + (while (not (looking-at "\\((when \\)\\|\\((when-not \\)")) + (condition-case nil + (backward-up-list) + (scan-error (user-error "No when or when-not found"))))) + +;;;###autoload +(defun clojure-cycle-when () + "Change a surrounding when to when-not, or vice-versa." + (interactive) + (save-excursion + (clojure--goto-when) + (cond + ((looking-at "(when-not") + (forward-char 9) + (delete-char -4)) + ((looking-at "(when") + (forward-char 5) + (insert "-not"))))) + +(defun clojure-cycle-not () + "Add or remove a not form around the current form." + (interactive) + (save-excursion + (condition-case nil + (backward-up-list) + (scan-error (user-error "`clojure-cycle-not' must be invoked inside a list"))) + (if (looking-back "(not ") + (progn + (delete-char -5) + (forward-sexp) + (delete-char 1)) + (insert "(not ") + (forward-sexp) + (insert ")")))) + +;;; let related stuff + +(defvar clojure--let-regexp + "\(\\(when-let\\|if-let\\|let\\)\\(\\s-*\\|\\[\\)" + "Regexp matching let like expressions, i.e. \"let\", \"when-let\", \"if-let\". + +The first match-group is the let expression. + +The second match-group is the whitespace or the opening square +bracket if no whitespace between the let expression and the +bracket.") + +(defun clojure--goto-let () + "Go to the beginning of the nearest let form." + (when (clojure--in-string-p) + (while (or (not (looking-at "(")) + (clojure--in-string-p)) + (backward-char))) + (ignore-errors + (while (not (looking-at clojure--let-regexp)) + (backward-up-list))) + (looking-at clojure--let-regexp)) + +(defun clojure--inside-let-binding-p () + "Return non-nil if point is inside a let binding." + (ignore-errors + (save-excursion + (let ((pos (point))) + (clojure--goto-let) + (re-search-forward "\\[") + (if (< pos (point)) + nil + (forward-sexp) + (up-list) + (< pos (point))))))) + +(defun clojure--beginning-of-current-let-binding () + "Move before the bound name of the current binding. +Assume that point is in the binding form of a let." + (let ((current-point (point))) + (clojure--goto-let) + (search-forward "[") + (forward-char) + (while (> current-point (point)) + (forward-sexp)) + (backward-sexp 2))) + +(defun clojure--previous-line () + "Keep the column position while go the previous line." + (let ((col (current-column))) + (forward-line -1) + (move-to-column col))) + +(defun clojure--prepare-to-insert-new-let-binding () + "Move to right place in the let form to insert a new binding and indent." + (if (clojure--inside-let-binding-p) + (progn + (clojure--beginning-of-current-let-binding) + (newline-and-indent) + (clojure--previous-line) + (indent-for-tab-command)) + (clojure--goto-let) + (search-forward "[") + (backward-up-list) + (forward-sexp) + (down-list -1) + (backward-char) + (if (looking-at "\\[\\s-*\\]") + (forward-char) + (forward-char) + (newline-and-indent)))) + +(defun clojure--sexp-regexp (sexp) + "Return a regexp for matching SEXP." + (concat "\\([^[:word:]^-]\\)" + (mapconcat #'identity (mapcar 'regexp-quote (split-string sexp)) + "[[:space:]\n\r]+") + "\\([^[:word:]^-]\\)")) + +(defun clojure--replace-sexp-with-binding (bound-name init-expr) + "Replace a binding with its bound name in the let form. + +BOUND-NAME is the name (left-hand side) of a binding. + +INIT-EXPR is the value (right-hand side) of a binding." + (save-excursion + (while (re-search-forward + (clojure--sexp-regexp init-expr) + (clojure--point-after 'clojure--goto-let 'forward-sexp) + t) + (replace-match (concat "\\1" bound-name "\\2"))))) + +(defun clojure--replace-sexps-with-bindings (bindings) + "Replace bindings with their respective bound names in the let form. + +BINDINGS is the list of bound names and init expressions." + (let ((bound-name (pop bindings)) + (init-expr (pop bindings))) + (when bound-name + (clojure--replace-sexp-with-binding bound-name init-expr) + (clojure--replace-sexps-with-bindings bindings)))) + +(defun clojure--replace-sexps-with-bindings-and-indent () + "Replace sexps with bindings." + (clojure--replace-sexps-with-bindings + (clojure--read-let-bindings)) + (clojure-indent-region + (clojure--point-after 'clojure--goto-let) + (clojure--point-after 'clojure--goto-let 'forward-sexp))) + +(defun clojure--read-let-bindings () + "Read the bound-name and init expression pairs in the binding form. +Return a list: odd elements are bound names, even elements init expressions." + (clojure--goto-let) + (down-list 2) + (let* ((start (point)) + (sexp-start start) + (end (save-excursion + (backward-char) + (forward-sexp) + (down-list -1) + (point))) + bindings) + (while (/= sexp-start end) + (forward-sexp) + (push + (string-trim (buffer-substring-no-properties sexp-start (point))) + bindings) + (skip-chars-forward "\r\n\t[:blank:]") + (setq sexp-start (point))) + (nreverse bindings))) + +(defun clojure--introduce-let-internal (name &optional n) + "Create a let form, binding the form at point with NAME. + +Optional numeric argument N, if non-nil, introduces the let N +lists up." + (if (numberp n) + (let ((init-expr-sexp (clojure-delete-and-extract-sexp))) + (insert name) + (ignore-errors (backward-up-list n)) + (insert "(let" (clojure-delete-and-extract-sexp) ")") + (backward-sexp) + (down-list) + (forward-sexp) + (insert " [" name " " init-expr-sexp "]\n") + (clojure--replace-sexps-with-bindings-and-indent)) + (insert "[ " (clojure-delete-and-extract-sexp) "]") + (backward-sexp) + (insert "(let " (clojure-delete-and-extract-sexp) ")") + (backward-sexp) + (down-list 2) + (insert name) + (forward-sexp) + (up-list) + (newline-and-indent) + (insert name))) + +(defun clojure--move-to-let-internal (name) + "Bind the form at point to NAME in the nearest let." + (if (not (save-excursion (clojure--goto-let))) + (clojure--introduce-let-internal name) + (let ((contents (clojure-delete-and-extract-sexp))) + (insert name) + (clojure--prepare-to-insert-new-let-binding) + (insert contents) + (backward-sexp) + (insert " ") + (backward-char) + (insert name) + (clojure--replace-sexps-with-bindings-and-indent)))) + +(defun clojure--let-backward-slurp-sexp-internal () + "Slurp the s-expression before the let form into the let form." + (clojure--goto-let) + (backward-sexp) + (let ((sexp (string-trim (clojure-delete-and-extract-sexp)))) + (delete-blank-lines) + (down-list) + (forward-sexp 2) + (newline-and-indent) + (insert sexp) + (clojure--replace-sexps-with-bindings-and-indent))) + +;;;###autoload +(defun clojure-let-backward-slurp-sexp (&optional n) + "Slurp the s-expression before the let form into the let form. +With a numberic prefix argument slurp the previous N s-expression +into the let form." + (interactive "p") + (let ((n (or n 1))) + (dotimes (_ n) + (save-excursion (clojure--let-backward-slurp-sexp-internal))))) + +(defun clojure--let-forward-slurp-sexp-internal () + "Slurp the next s-expression after the let form into the let form." + (clojure--goto-let) + (forward-sexp) + (let ((sexp (string-trim (clojure-delete-and-extract-sexp)))) + (down-list -1) + (newline-and-indent) + (insert sexp) + (clojure--replace-sexps-with-bindings-and-indent))) + +;;;###autoload +(defun clojure-let-forward-slurp-sexp (&optional n) + "Slurp the next s-expression after the let form into the let form. +With a numeric prefix argument slurp the next N s-expressions into the let form." + (interactive "p") + (unless n (setq n 1)) + (dotimes (k n) + (save-excursion (clojure--let-forward-slurp-sexp-internal)))) + +;;;###autoload +(defun clojure-introduce-let (&optional n) + "Create a let form, binding the form at point. +With a numeric prefix argument the let is introduced N lists up." + (interactive "P") + (clojure--introduce-let-internal (read-from-minibuffer "Name of bound symbol: ") n)) + +;;;###autoload +(defun clojure-move-to-let () + "Move the form at point to a binding in the nearest let." + (interactive) + (clojure--move-to-let-internal (read-from-minibuffer "Name of bound symbol: "))) + + +;;; ClojureScript +(defconst clojurescript-font-lock-keywords + (eval-when-compile + `(;; ClojureScript built-ins + (,(concat "(\\(?:\.*/\\)?" + (regexp-opt '("js-obj" "js-delete" "clj->js" "js->clj")) + "\\>") + 0 font-lock-builtin-face))) + "Additional font-locking for `clojurescript-mode'.") + +;;;###autoload +(define-derived-mode clojurescript-mode clojure-mode "ClojureScript" + "Major mode for editing ClojureScript code. + +\\{clojurescript-mode-map}" + (font-lock-add-keywords nil clojurescript-font-lock-keywords)) + +;;;###autoload +(define-derived-mode clojurec-mode clojure-mode "ClojureC" + "Major mode for editing ClojureC code. + +\\{clojurec-mode-map}") + +;;;###autoload +(progn + (add-to-list 'auto-mode-alist + '("\\.\\(clj\\|dtm\\|edn\\)\\'" . clojure-mode)) + (add-to-list 'auto-mode-alist '("\\.cljc\\'" . clojurec-mode)) + (add-to-list 'auto-mode-alist '("\\.cljs\\'" . clojurescript-mode)) + ;; boot build scripts are Clojure source files + (add-to-list 'auto-mode-alist '("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode))) + +(provide 'clojure-mode) + +;; Local Variables: +;; coding: utf-8 +;; End: + +;;; clojure-mode.el ends here diff --git a/elpa/clomacs-20170128.850/clomacs-autoloads.el b/elpa/clomacs-20170128.850/clomacs-autoloads.el deleted file mode 100644 index c80b217a..00000000 --- a/elpa/clomacs-20170128.850/clomacs-autoloads.el +++ /dev/null @@ -1,37 +0,0 @@ -;;; clomacs-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil "clomacs" "clomacs.el" (22692 30292 0 0)) -;;; Generated autoloads from clomacs.el - -(autoload 'clomacs-def "clomacs" "\ -Wrap CL-ENTITY-NAME, evaluated on clojure side by EL-ENTITY-NAME. -DOC - optional elisp function docstring (when nil it constructed from -underlying clojure entity docstring if possible). -TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, -or it may be a custom function (:string by default). - -\(fn EL-ENTITY-NAME CL-ENTITY-NAME &key (DOC nil) (TYPE :string) LIB-NAME NAMESPACE)" nil t) - -(autoload 'clomacs-defun "clomacs" "\ -Wrap CL-FUNC-NAME, evaluated on clojure side by EL-FUNC-NAME. -CALL-TYPE - call Clojure side :sync or :async. -CALLBACK - callback function for :async CALL-TYPE case. -DOC - optional elisp function docstring (when nil it constructed from -underlying clojure entity docstring if possible). -RETURN-TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, -or it may be a custom function (:string by default). -RETURN-VALUE may be :value or :stdout (:value by default). - -\(fn EL-FUNC-NAME CL-FUNC-NAME &key (CALL-TYPE :sync) (CALLBACK nil) (DOC nil) (RETURN-TYPE :string) (RETURN-VALUE :value) LIB-NAME NAMESPACE)" nil t) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; clomacs-autoloads.el ends here diff --git a/elpa/clomacs-20170128.850/clomacs-pkg.el b/elpa/clomacs-20170128.850/clomacs-pkg.el deleted file mode 100644 index 082e32dd..00000000 --- a/elpa/clomacs-20170128.850/clomacs-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; -*- no-byte-compile: t -*- -(define-package "clomacs" "20170128.850" "Simplifies Emacs Lisp interaction with Clojure." '((emacs "24.3") (cider "0.11") (s "1.10.0")) :url "https://github.com/clojure-emacs/clomacs" :keywords '("clojure" "interaction")) diff --git a/elpa/clomacs-20170128.850/clomacs.el b/elpa/clomacs-20170128.850/clomacs.el deleted file mode 100644 index bf2f2f2d..00000000 --- a/elpa/clomacs-20170128.850/clomacs.el +++ /dev/null @@ -1,371 +0,0 @@ -;;; clomacs.el --- Simplifies Emacs Lisp interaction with Clojure. -*- lexical-binding: t -*- - -;; Copyright (C) 2013-2017 Kostafey - -;; Author: Kostafey -;; URL: https://github.com/clojure-emacs/clomacs -;; Package-Version: 20170128.850 -;; Keywords: clojure, interaction -;; Version: 0.0.2 -;; Package-Requires: ((emacs "24.3") (cider "0.11") (s "1.10.0")) - -;; This file is not part of GNU Emacs. - -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; `clomacs-defun' - core Clojure to Elisp function wrapper. -;; -;; See README.md for detailed description. - - -(require 'cl-lib) -(require 'cider) -(require 's) - -(defvar clomacs-verify-nrepl-on-call t) -(defvar clomacs-autoload-nrepl-on-call t) - -(defun clomacs-search-connection (repl-buffer-project-name) - "Search nREPL connection buffer. -E.g. if you want to find \"*cider-repl clomacs-20160419.258*\" you shold pass -REPL-BUFFER-PROJECT-NAME \"clomacs\"." - (cl-reduce - (lambda (x y) (or x y)) - (mapcar - (lambda (x) - (let ((this-repl - (s-chop-suffix - "*" - (cadr (split-string (buffer-name (car cider-connections)) " "))))) - (if (and - (>= (length this-repl) - (length repl-buffer-project-name)) - (string= repl-buffer-project-name - (substring this-repl - 0 - (length repl-buffer-project-name)))) - x))) - cider-connections))) - -(defun clomacs-get-connection (&optional library) - "Return buffer with nREPL process related to LIBRARY. -If LIBRARY is nil, attempts to use \"clomacs\", \"localhost\" or -any current connection. -If can't find any nREPL process return nil." - (if (> (length cider-connections) 0) - (if library - (clomacs-search-connection library) - (or (clomacs-search-connection "clomacs") - (clomacs-search-connection "localhost") - (cider-current-connection))))) - -(defun clomacs-get-session (connection) - "Return current session for this CONNECTION." - (assert connection) - (with-current-buffer - (set-buffer connection) - (cider-current-session))) - -(defun clomacs-launch-nrepl (library &optional sync) - (let* ((starting-msg (format - "Starting nREPL server for %s..." - (propertize (or library "current-buffer") - 'face 'font-lock-keyword-face))) - (lib-file (if library (find-library-name library))) - (is-opened (if lib-file (find-buffer-visiting lib-file))) - (lib-buff (or is-opened - (if lib-file - (find-file-noselect lib-file))))) - ;; simple run lein - (if lib-buff - (with-current-buffer lib-buff - (cider-jack-in)) - (cider-jack-in)) - (message starting-msg) - (if sync - (let ((old-cider-repl-pop cider-repl-pop-to-buffer-on-connect)) - (setq cider-repl-pop-to-buffer-on-connect nil) - (while (not (clomacs-get-connection library)) - (sleep-for 0.1)) - (setq cider-repl-pop-to-buffer-on-connect old-cider-repl-pop) - (if (and library (not is-opened)) - (kill-buffer lib-buff)))) - (message "Started."))) - -(defun clomacs-return-stringp (raw-string) - (and - raw-string - (equal (substring raw-string 0 1) "\"") - (equal (substring raw-string - (1- (length raw-string)) (length raw-string)) "\""))) - -(defun clomacs-strip-string (raw-string) - (if (clomacs-return-stringp raw-string) - (substring raw-string 1 (1- (length raw-string))) - raw-string)) - -(defun clomacs-format-result (raw-string return-type) - "Format Elisp representation of Clojure evaluation result." - (cl-assert return-type) - (if raw-string - (let ((return-string (clomacs-strip-string raw-string))) - (cond - ((functionp return-type) (funcall return-type raw-string)) - ((eq return-type :string) return-string) - ((eq return-type :int) (string-to-number return-string)) - ((eq return-type :number) (string-to-number return-string)) - ((eq return-type :list) (read raw-string)) - ((eq return-type :char) (string-to-char return-string)) - ((eq return-type :vector) (string-to-vector return-string)))))) - -(declare clomacs-format-arg) - -(defun clomacs-plist-p (object) - "Return t if OBJECT is a plist, otherwise, return nil." - (when (and (listp object) - (car object) - (listp (car object)) - (not (listp (cdr (car object))))) - t)) - -(defun clomacs-plist-to-map (lst) - "Build string representation of Clojure map from Elisp plist LST." - (let ((tail (car (last lst)))) - (concat - "{" - (cl-reduce - (lambda (acc pair) - (concat acc - (clomacs-format-arg (car pair)) " " - (clomacs-format-arg (cdr pair)) - (if (eq pair tail) "" " "))) - lst - :initial-value "") - "}"))) - -(defun clomacs-format-arg (a) - "Format Clojure representation of Elisp argument." - (cond - ((numberp a) (number-to-string a)) - ((stringp a) (clomacs-add-quotes a)) - ((booleanp a) (if a "true" "false")) - ((clomacs-plist-p a) (clomacs-plist-to-map a)) - ((and (listp a) (equal (car a) 'quote)) - (concat "'" (clomacs-force-symbol-name - (cadr a)))) - ((symbolp a) (clomacs-force-symbol-name a)) - (t (replace-regexp-in-string - "\\\\." "." (format "'%S" a))))) - -(defvar clomacs-possible-return-types - (list :string - :int - :number - :list - :char - :vector)) - -(defun clomacs-highlight-initialize () - (font-lock-add-keywords - 'emacs-lisp-mode - '(("clomacs-defun\\b" . font-lock-keyword-face) - ("clomacs-def\\b" . font-lock-keyword-face)))) - -(defun clomacs-force-symbol-name (some-symbol) - "Return lisp symbol SOME-SYMBOL as a string at all costs!" - (mapconcat 'char-to-string - (string-to-list (symbol-name some-symbol)) "")) - -(eval-after-load "clomacs" - (lambda () - (clomacs-highlight-initialize) - - ;; Should be last `clomacs-defun' - (clomacs-defun clomacs--doc - clojure.repl/doc - :return-value :stdout) - - (defun clomacs-doc (x) - (if (clomacs-get-connection) - (clomacs--doc x))))) - -(defun clomacs-get-doc (doc cl-entity-name) - "Form the emacs-lisp side entity docstring. -DOC - user-defined docsting. -CL-ENTITY-NAME - clojure side entity name. -CL-ENTITY-TYPE - \"value\" or \"function\"" - (if doc doc - (concat "Wrapped clojure entity:" - (let ((cl-entity-doc (when (fboundp 'clomacs-doc) - (clomacs-doc cl-entity-name)))) - (if cl-entity-doc (concat "\n" cl-entity-doc) - (clomacs-force-symbol-name cl-entity-name)))))) - -(defun clomacs-ensure-nrepl-run (&optional lib-name) - "Ensure nrepl is running." - (when clomacs-verify-nrepl-on-call - (unless (clomacs-get-connection lib-name) - (if clomacs-autoload-nrepl-on-call - ;; Raise up the world - sync nrepl launch - (clomacs-launch-nrepl lib-name t) - (error - (concat "CIDER is not launched!")))))) - -(defun clomacs-get-result (result value type namespace) - "Parse result of clojure code evaluation from CIDER. -Handle errors. Handle difference between CIDER versions." - (let ((val-new (cond - ((equal value :value) "value") - ((equal value :stdout) "out")))) - (if (or (plist-get result :stderr) - (nrepl-dict-get result "err")) - (error (or (plist-get result :stderr) - (nrepl-dict-get result "err"))) - (clomacs-format-result - (let ((essence-result (or - (plist-get result value) - (nrepl-dict-get result val-new)))) - (if (and namespace (equal value :value)) - (substring essence-result 3) - essence-result)) - type)))) - -(defun clomacs-add-quotes (str) - (concat "\"" str "\"")) - -(cl-defun clomacs-prepare-vars (cl-entity-name - &key - (doc nil) - (return-type :string) - namespace) - "Prepare intermediate variables for clomacs wrapper macros." - (cl-assert (and return-type - (or (functionp return-type) - (member return-type clomacs-possible-return-types))) - t - (format (concat "Wrong return-type %s! See C-h v " - "clomacs-possible-return-types") - (clomacs-force-symbol-name return-type))) - (let* ((doc (clomacs-get-doc doc cl-entity-name)) - (cl-entity-name-str (clomacs-force-symbol-name cl-entity-name)) - (namespace-str (clomacs-force-symbol-name namespace)) - (ns-slash-pos (string-match "/" cl-entity-name-str)) - (implicit-ns (if ns-slash-pos - (substring cl-entity-name-str 0 ns-slash-pos))) - (cl-entity-full-name (if (and namespace (not implicit-ns)) - (concat namespace-str "/" cl-entity-name-str) - cl-entity-name-str))) - (list doc namespace-str cl-entity-full-name))) - -;;;###autoload -(cl-defmacro clomacs-def (el-entity-name - cl-entity-name - &key - (doc nil) - (type :string) - lib-name - namespace) - "Wrap CL-ENTITY-NAME, evaluated on clojure side by EL-ENTITY-NAME. -DOC - optional elisp function docstring (when nil it constructed from -underlying clojure entity docstring if possible). -TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, -or it may be a custom function (:string by default)." - (cl-multiple-value-bind - (doc namespace-str cl-entity-full-name) - (clomacs-prepare-vars cl-entity-name - :doc doc - :namespace namespace) - `(defvar ,el-entity-name - (progn - (clomacs-ensure-nrepl-run ,lib-name) - (let* ((connection (clomacs-get-connection ,lib-name)) - (result - (nrepl-sync-request:eval - (concat - (if ',namespace - (concat "(require '" ',namespace-str ") ") "") - ',cl-entity-full-name) - connection))) - (clomacs-get-result result :value ',type ',namespace))) - ,doc))) - -;;;###autoload -(cl-defmacro clomacs-defun (el-func-name - cl-func-name - &key - (call-type :sync) - (callback nil) - (doc nil) - (return-type :string) - (return-value :value) - lib-name - namespace) - "Wrap CL-FUNC-NAME, evaluated on clojure side by EL-FUNC-NAME. -CALL-TYPE - call Clojure side :sync or :async. -CALLBACK - callback function for :async CALL-TYPE case. -DOC - optional elisp function docstring (when nil it constructed from -underlying clojure entity docstring if possible). -RETURN-TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, -or it may be a custom function (:string by default). -RETURN-VALUE may be :value or :stdout (:value by default)." - (cl-multiple-value-bind - (doc namespace-str cl-entity-full-name) - (clomacs-prepare-vars cl-func-name - :doc doc - :namespace namespace) - `(defun ,el-func-name (&rest attributes) - ,doc - (clomacs-ensure-nrepl-run ,lib-name) - (let* ((attrs "")) - (dolist (a attributes) - (setq attrs (concat attrs " " - (clomacs-format-arg a)))) - (let* ((connection (clomacs-get-connection ,lib-name)) - (request (concat - (if ',namespace - (concat "(require '" ',namespace-str ") ") "") - "(" ',cl-entity-full-name attrs ")"))) - (if (equal ,call-type :async) - (nrepl-request:eval - request - (lambda (result) - (if ,callback - (let ((el-result (clomacs-get-result - result - ,return-value ',return-type ',namespace))) - (if el-result - (,callback el-result))))) - connection) - (clomacs-get-result - (nrepl-sync-request:eval - request - connection) - ,return-value ',return-type ',namespace))))))) - -(defun clomacs-load-file (file-path) - "Sync and straightforward load clojure file." - (let* ((connection (cider-current-connection)) - (session (clomacs-get-session connection))) - (nrepl-sync-request:eval - (with-temp-buffer - (insert-file-contents file-path) - (buffer-string)) - connection - session))) - -(provide 'clomacs) - -;;; clomacs.el ends here diff --git a/elpa/clomacs-20181003.1735/clomacs-autoloads.el b/elpa/clomacs-20181003.1735/clomacs-autoloads.el new file mode 100644 index 00000000..cd6bfb4a --- /dev/null +++ b/elpa/clomacs-20181003.1735/clomacs-autoloads.el @@ -0,0 +1,50 @@ +;;; clomacs-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "clomacs" "clomacs.el" (0 0 0 0)) +;;; Generated autoloads from clomacs.el + +(autoload 'clomacs-def "clomacs" "\ +Wrap CL-ENTITY-NAME, evaluated on clojure side by EL-ENTITY-NAME. +DOC - optional elisp function docstring (when nil it constructed from +underlying clojure entity docstring if possible). +TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, +or it may be a custom function (:string by default). + +\(fn EL-ENTITY-NAME CL-ENTITY-NAME &key (DOC nil) (TYPE :string) LIB-NAME NAMESPACE)" nil t) + +(autoload 'clomacs-defun "clomacs" "\ +Wrap CL-FUNC-NAME, evaluated on clojure side by EL-FUNC-NAME. +CALL-TYPE - call Clojure side :sync or :async. +CALLBACK - callback function for :async CALL-TYPE case. +DOC - optional elisp function docstring (when nil it constructed from +underlying clojure entity docstring if possible). +INTERACTIVE - when defined and is a boolean `t` mark function (interactive), +if not boolean - insert interactive value into the function beginning as is. +RETURN-TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, +or it may be a custom function (:string by default). +RETURN-VALUE may be :value or :stdout (:value by default). +LIB-NAME - Elisp library name used in end-user .emacs config by `require'. +HTTPD-STARTER - in the case Clojure side code needs to call Elisp side code, +http-server should be started to pass http requests from Clojure REPL +to Emacs. This parameter is Elisp function to do it. Such function can +be created by `clomacs-create-httpd-start' macro. + +\(fn EL-FUNC-NAME CL-FUNC-NAME &key (CALL-TYPE :sync) (CALLBACK nil) (DOC nil) (INTERACTIVE nil) (RETURN-TYPE :string) (RETURN-VALUE :value) LIB-NAME NAMESPACE (HTTPD-STARTER nil))" nil t) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "clomacs" '("clo" "execute"))) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; clomacs-autoloads.el ends here diff --git a/elpa/clomacs-20181003.1735/clomacs-pkg.el b/elpa/clomacs-20181003.1735/clomacs-pkg.el new file mode 100644 index 00000000..fbc5d47c --- /dev/null +++ b/elpa/clomacs-20181003.1735/clomacs-pkg.el @@ -0,0 +1,2 @@ +;;; -*- no-byte-compile: t -*- +(define-package "clomacs" "20181003.1735" "Simplifies Emacs Lisp interaction with Clojure." '((emacs "24.3") (cider "0.18.0") (s "1.12.0") (simple-httpd "1.4.6")) :commit "6c83a0e2ac13e8fcc4b99183dbb3426bfe8bbb9c" :keywords '("clojure" "interaction") :authors '(("Kostafey" . "kostafey@gmail.com")) :maintainer '("Kostafey" . "kostafey@gmail.com") :url "https://github.com/clojure-emacs/clomacs") diff --git a/elpa/clomacs-20181003.1735/clomacs.el b/elpa/clomacs-20181003.1735/clomacs.el new file mode 100644 index 00000000..4cbf92e3 --- /dev/null +++ b/elpa/clomacs-20181003.1735/clomacs.el @@ -0,0 +1,557 @@ +;;; clomacs.el --- Simplifies Emacs Lisp interaction with Clojure. -*- lexical-binding: t -*- + +;; Copyright (C) 2013-2018 Kostafey + +;; Author: Kostafey +;; URL: https://github.com/clojure-emacs/clomacs +;; Package-Version: 20181003.1735 +;; Keywords: clojure, interaction +;; Version: 0.0.3 +;; Package-Requires: ((emacs "24.3") (cider "0.18.0") (s "1.12.0") (simple-httpd "1.4.6")) + +;; This file is not part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; `clomacs-defun' - core Clojure to Elisp function wrapper. +;; +;; Elisp to Clojure calls helper functions: +;; `clomacs-create-httpd-start' - package-specific httpd connection setup. +;; `clomacs-create-httpd-stop' - package-specific httpd connection stop. +;; +;; See README.md for detailed description. + + +(require 'cl-lib) +(require 'net-utils) +(require 'cider) +(require 's) +(require 'simple-httpd) + +(defvar clomacs-verify-nrepl-on-call t) +(defvar clomacs-autoload-nrepl-on-call t) + +(defcustom clomacs-httpd-default-port 8080 + "Http port to listen for requests from Clojure side." + :group 'clomacs + :type 'integer) + +(defcustom clomacs-httpd-port-scan-limit 100 + "Available Http ports scan range limit." + :group 'clomacs + :type 'integer) + +(defcustom clomacs-print-length 100000 + "Value for *print-length* set during `clomacs-defun' macros evaluation. +Restricts list length passed from Clojure to Emacs lisp. +Set `nil' for unlimited list length." + :group 'clomacs + :type 'integer) + +(defcustom clomacs-restore-print-length nil + "When t restore *print-length* acording to `cider-repl-print-length' value. +After any `clomacs-defun' wraped funtion call, restore *print-length*. +Can be useful for debugging purpose to run `clomacs-defun' functions and +Clojure code directly in the same REPL." + :group 'clomacs + :type 'boolean) + +(defun cloamcs-get-dir (repl-info) + (if repl-info + (file-name-nondirectory + (car (split-string repl-info ":"))))) + +(defun clomacs-search-connection (project-name) + "Search nREPL connection buffer. +E.g. if you want to find \"*cider-repl clomacs-20160419.258*\" you shold pass +REPL-BUFFER-PROJECT-NAME \"clomacs\"." + (if project-name + (let ((result nil)) + (maphash + (lambda (k v) + (let ((current-project-dir (cloamcs-get-dir (cdr k)))) + (if (and current-project-dir + (or (s-contains? project-name current-project-dir) + (s-contains? project-name (buffer-name (cadr v))))) + (setq result (cadr v))))) + sesman-sessions-hashmap) + result) + (cider-current-connection))) + +(defun clomacs-get-connection (&optional library) + "Return buffer with nREPL process related to LIBRARY. +If LIBRARY is nil, attempts to use \"clomacs\", \"localhost\" or +any current connection. +If can't find any nREPL process return nil." + (if library + (clomacs-search-connection library) + (or (clomacs-search-connection "clomacs") + (clomacs-search-connection "localhost") + (cider-current-repl)))) + +(defun clomacs-get-session (connection) + "Return current session for this CONNECTION." + (assert connection) + (with-current-buffer + (set-buffer connection) + (cider-current-session))) + +(defun clomacs-launch-nrepl (library &optional sync) + (let* ((starting-msg (format + "Starting nREPL server for %s..." + (propertize (or library "current-buffer") + 'face 'font-lock-keyword-face))) + (lib-file (if library (find-library-name library))) + (is-opened (if lib-file (find-buffer-visiting lib-file))) + (lib-buff (or is-opened + (if lib-file + (find-file-noselect lib-file))))) + ;; simple run lein + (if lib-buff + (with-current-buffer lib-buff + (cider-jack-in nil)) + (cider-jack-in nil)) + (message starting-msg) + (if sync + (let ((old-cider-repl-pop cider-repl-pop-to-buffer-on-connect)) + (setq cider-repl-pop-to-buffer-on-connect nil) + (while (not (clomacs-get-connection library)) + (sleep-for 0.1)) + (setq cider-repl-pop-to-buffer-on-connect old-cider-repl-pop) + (if (and library (not is-opened)) + (kill-buffer lib-buff)))) + (message "Started."))) + +(defun clomacs-return-stringp (raw-string) + (and + raw-string + (equal (substring raw-string 0 1) "\"") + (equal (substring raw-string + (1- (length raw-string)) (length raw-string)) "\""))) + +(defun clomacs-strip-string (raw-string) + (if (clomacs-return-stringp raw-string) + (substring raw-string 1 (1- (length raw-string))) + raw-string)) + +(defun clomacs-clean-result-string (return-string) + (if (stringp return-string) + (condition-case nil + (s-replace-all '(("\\\\" . "\\") + ("\\\"" . "\"") + ("\\n" . "\n") + ("\\t" . "\t")) + return-string) + (error return-string)) + return-string)) + +(defun clomacs-string-to-boolean (s) + (not (or (not s) + (equal s "nil") + (equal s "false")))) + +(defun clomacs-format-result (raw-string return-type) + "Format Elisp representation of Clojure evaluation result." + (cl-assert return-type) + (if raw-string + (let ((return-string (clomacs-strip-string raw-string))) + (cond + ((functionp return-type) (funcall return-type raw-string)) + ((eq return-type :string) (clomacs-clean-result-string return-string)) + ((eq return-type :int) (string-to-number return-string)) + ((eq return-type :number) (string-to-number return-string)) + ((eq return-type :boolean) (clomacs-string-to-boolean return-string)) + ((eq return-type :list) (read raw-string)) + ((eq return-type :char) (string-to-char return-string)) + ((eq return-type :vector) (string-to-vector return-string)))))) + +(declare clomacs-format-arg) + +(defun clomacs-alist-p (object) + "Return t if OBJECT is a alist, otherwise, return nil." + (when (and (listp object) + (car object) + (listp (car object)) + (not (listp (cdr (car object))))) + t)) + +(defun clomacs-alist-to-map (lst) + "Build string representation of Clojure map from Elisp alist LST." + (let ((tail (car (last lst)))) + (concat + "{" + (cl-reduce + (lambda (acc pair) + (concat acc + (clomacs-format-arg (car pair)) " " + (clomacs-format-arg (cdr pair)) + (if (eq pair tail) "" " "))) + lst + :initial-value "") + "}"))) + +(defun clomacs-format-arg (a) + "Format Clojure representation of Elisp argument." + (cond + ((numberp a) (number-to-string a)) + ((stringp a) (clomacs-add-quotes a)) + ((booleanp a) (if a "true" "false")) + ((clomacs-alist-p a) (clomacs-alist-to-map a)) + ((and (listp a) (equal (car a) 'quote)) + (concat "'" (clomacs-force-symbol-name + (cadr a)))) + ((symbolp a) (clomacs-force-symbol-name a)) + (t (replace-regexp-in-string + "\\\\." "." (format "'%S" a))))) + +(defvar clomacs-possible-return-types + (list :string + :int + :number + :list + :char + :vector)) + +(defun clomacs-highlight-initialize () + (font-lock-add-keywords + 'emacs-lisp-mode + '(("clomacs-defun\\b" . font-lock-keyword-face) + ("clomacs-def\\b" . font-lock-keyword-face)))) + +(defun clomacs-force-symbol-name (some-symbol) + "Return lisp symbol SOME-SYMBOL as a string at all costs!" + (mapconcat 'char-to-string + (string-to-list (symbol-name some-symbol)) "")) + +(eval-after-load "clomacs" + (lambda () + (clomacs-highlight-initialize) + + ;; Should be last `clomacs-defun' + (clomacs-defun clomacs--doc + clojure.repl/doc + :return-value :stdout) + + (defun clomacs-doc (x) + (if (clomacs-get-connection) + (clomacs--doc x))))) + +(defun clomacs-get-doc (doc cl-entity-name) + "Form the emacs-lisp side entity docstring. +DOC - user-defined docsting. +CL-ENTITY-NAME - clojure side entity name. +CL-ENTITY-TYPE - \"value\" or \"function\"" + (if doc doc + (concat "Wrapped clojure entity:" + (let ((cl-entity-doc (when (fboundp 'clomacs-doc) + (clomacs-doc cl-entity-name)))) + (if cl-entity-doc (concat "\n" cl-entity-doc) + (clomacs-force-symbol-name cl-entity-name)))))) + +(defun clomacs-ensure-nrepl-run (&optional lib-name) + "Ensure nrepl is running." + (when clomacs-verify-nrepl-on-call + (unless (clomacs-get-connection lib-name) + (if clomacs-autoload-nrepl-on-call + ;; Raise up the world - sync nrepl launch + (clomacs-launch-nrepl lib-name t) + (error + (concat "CIDER is not launched!")))))) + +(defun clomacs-get-result (result value type namespace) + "Parse result of clojure code evaluation from CIDER. +Handle errors. Handle difference between CIDER versions." + (let ((val-new (cond + ((equal value :value) "value") + ((equal value :stdout) "out")))) + (if (or (plist-get result :stderr) + (nrepl-dict-get result "err")) + (error (or (plist-get result :stderr) + (nrepl-dict-get result "err"))) + (clomacs-format-result + (let ((essence-result (or + (plist-get result value) + (nrepl-dict-get result val-new)))) + (if (and namespace (equal value :value)) + (substring essence-result 3) + essence-result)) + type)))) + +(defun clomacs-add-quotes (str) + (format "%S" + (progn + (set-text-properties 0 (length str) nil str) + str))) + +(cl-defun clomacs-prepare-vars (cl-entity-name + &key + (doc nil) + (return-type :string) + namespace) + "Prepare intermediate variables for clomacs wrapper macros." + (cl-assert (and return-type + (or (functionp return-type) + (member return-type clomacs-possible-return-types))) + t + (format (concat "Wrong return-type %s! See C-h v " + "clomacs-possible-return-types") + (clomacs-force-symbol-name return-type))) + (let* ((doc (clomacs-get-doc doc cl-entity-name)) + (cl-entity-name-str (clomacs-force-symbol-name cl-entity-name)) + (ns-slash-pos (string-match "/" cl-entity-name-str)) + (implicit-ns (if ns-slash-pos + (substring cl-entity-name-str 0 ns-slash-pos))) + (namespace-str (if namespace + (clomacs-force-symbol-name namespace) + implicit-ns)) + (cl-entity-full-name (if (and namespace (not implicit-ns)) + (concat namespace-str "/" cl-entity-name-str) + cl-entity-name-str))) + (list doc namespace-str cl-entity-full-name))) + +;;;###autoload +(cl-defmacro clomacs-def (el-entity-name + cl-entity-name + &key + (doc nil) + (type :string) + lib-name + namespace) + "Wrap CL-ENTITY-NAME, evaluated on clojure side by EL-ENTITY-NAME. +DOC - optional elisp function docstring (when nil it constructed from +underlying clojure entity docstring if possible). +TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, +or it may be a custom function (:string by default)." + (cl-multiple-value-bind + (doc namespace-str cl-entity-full-name) + (clomacs-prepare-vars cl-entity-name + :doc doc + :namespace namespace) + `(defvar ,el-entity-name + (progn + (clomacs-ensure-nrepl-run ,lib-name) + (let* ((connection (clomacs-get-connection ,lib-name)) + (result + (nrepl-sync-request:eval + (concat + (if ',namespace + (concat "(require '" ',namespace-str ") ") "") + ',cl-entity-full-name) + connection))) + (clomacs-get-result result :value ',type ',namespace))) + ,doc))) + +;;;###autoload +(cl-defmacro clomacs-defun (el-func-name + cl-func-name + &key + (call-type :sync) + (callback nil) + (doc nil) + (interactive nil) + (return-type :string) + (return-value :value) + lib-name + namespace + (httpd-starter nil)) + "Wrap CL-FUNC-NAME, evaluated on clojure side by EL-FUNC-NAME. +CALL-TYPE - call Clojure side :sync or :async. +CALLBACK - callback function for :async CALL-TYPE case. +DOC - optional elisp function docstring (when nil it constructed from +underlying clojure entity docstring if possible). +INTERACTIVE - when defined and is a boolean `t` mark function (interactive), +if not boolean - insert interactive value into the function beginning as is. +RETURN-TYPE possible values are listed in the CLOMACS-POSSIBLE-RETURN-TYPES, +or it may be a custom function (:string by default). +RETURN-VALUE may be :value or :stdout (:value by default). +LIB-NAME - Elisp library name used in end-user .emacs config by `require'. +HTTPD-STARTER - in the case Clojure side code needs to call Elisp side code, +http-server should be started to pass http requests from Clojure REPL +to Emacs. This parameter is Elisp function to do it. Such function can +be created by `clomacs-create-httpd-start' macro." + (cl-multiple-value-bind + (doc namespace-str cl-entity-full-name) + (clomacs-prepare-vars cl-func-name + :doc doc + :namespace namespace) + `(defun ,el-func-name (&rest attributes) + ,doc + ,(if interactive + (if (booleanp interactive) + '(interactive) + interactive)) + (clomacs-ensure-nrepl-run ,lib-name) + (when (and (functionp ,httpd-starter) + (not (process-status "httpd"))) + (funcall ,httpd-starter)) + (let* ((attrs "")) + (dolist (a attributes) + (setq attrs (concat attrs " " + (clomacs-format-arg a)))) + (let* ((connection (clomacs-get-connection ,lib-name)) + (request (concat + (if ',namespace + (format "(require '%s) " ',namespace-str) "") + (format + "(do (set! *print-length* %s) + (%s %s))" + ,(number-to-string clomacs-print-length) + ',cl-entity-full-name + attrs)))) + (if (equal ,call-type :async) + ;; async + (nrepl-request:eval + request + (lambda (result) + (if ,callback + (let ((el-result (clomacs-get-result + result + ,return-value ',return-type ',namespace))) + (if el-result + (,callback el-result)) + (if clomacs-restore-print-length + (cider-repl-set-config))))) + connection) + ;; sync + (let ((el-result (clomacs-get-result + (nrepl-sync-request:eval + request + connection) + ,return-value ',return-type ',namespace))) + (if clomacs-restore-print-length + (cider-repl-set-config)) + el-result))))))) + +(defun clomacs-load-file (file-path) + "Sync and straightforward load clojure file." + (let* ((connection (cider-current-connection)) + (session (clomacs-get-session connection))) + (nrepl-sync-request:eval + (with-temp-buffer + (insert-file-contents file-path) + (buffer-string)) + connection + session))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Emacs http server + +(defun clomacs-eval-elisp (string) + "Evaluate elisp code stored in a STRING." + (eval (car (read-from-string string)))) + +(defservlet* execute text/plain (elisp) + (let ((result (clomacs-eval-elisp elisp))) + (if result + (insert (format "%s" result))))) + +(defun clomacs-get-httpd-port () + "Search available port for httpd process." + (let ((netstat-ouptut (shell-command-to-string + (concat netstat-program " -an"))) + (value) + (i 0)) + (while (not value) + (let ((port (+ clomacs-httpd-default-port i))) + (setq i (+ i 1)) + (if (> i clomacs-httpd-port-scan-limit) + (error (format "All ports from %d to %d are busy." + clomacs-httpd-default-port + port))) + (if (not (s-contains? (number-to-string port) netstat-ouptut)) + (setq value port)))) + value)) + +(cl-defmacro clomacs-create-httpd-start (func-name + &key + lib-prefix + lib-name) + "Create lib-specific function FUNC-NAME, aimed to start Emacs httpd process. +LIB-PREFIX - Custom Elisp library name prefix. +LIB-NAME - Elisp library name used in end-user .emacs config by `require'. +The result function FUNC-NAME can be used as `clomacs-defun' +`:httpd-starter' parameter." + (let ((lib-require + (make-symbol (concat lib-prefix "-require"))) + (lib-set-emacs-connection + (make-symbol (concat lib-prefix "-set-emacs-connection")))) + `(progn + (clomacs-defun ,lib-require + clojure.core/require + :lib-name ,lib-name) + (clomacs-defun ,lib-set-emacs-connection + clomacs/set-emacs-connection + :lib-name ,lib-name) + (defun ,func-name () + "Start Emacs http server and set host and port on Clojure side." + (let ((httpd-port (clomacs-get-httpd-port))) + (,lib-require `'clomacs) + (,lib-set-emacs-connection "localhost" httpd-port) + (httpd-start)))))) + +(cl-defmacro clomacs-create-httpd-stop (func-name + &key + lib-prefix + lib-name) + "Create lib-specific function FUNC-NAME, aimed to stop Emacs httpd process. +LIB-PREFIX - Custom Elisp library name prefix. +LIB-NAME - Elisp library name used in end-user .emacs config by `require'." + (let ((lib-require + (make-symbol (concat lib-prefix "-require"))) + (lib-close-emacs-connection + (make-symbol (concat lib-prefix "-close-emacs-connection")))) + `(progn + (clomacs-defun ,lib-require + clojure.core/require + :lib-name ,lib-name) + (clomacs-defun ,lib-close-emacs-connection + clomacs/close-emacs-connection + :lib-name ,lib-name) + (defun ,func-name () + "Stop Emacs http server and reset host and port on Clojure side." + (when (clomacs-get-connection ,lib-name) + (,lib-require `'clomacs) + (,lib-close-emacs-connection)) + (httpd-stop))))) + +(clomacs-defun clomacs-require + clojure.core/require) + +(clomacs-defun clomacs-set-emacs-connection + clomacs/set-emacs-connection) + +(clomacs-defun clomacs-get-emacs-connection + clomacs/get-emacs-connection) + +(clomacs-defun clomacs-close-emacs-connection + clomacs/close-emacs-connection) + +(defun clomacs-httpd-start () + "Start Emacs http server and set host and port on Clojure side." + (let ((httpd-port (clomacs-get-httpd-port))) + (clomacs-require `'clomacs) + (clomacs-set-emacs-connection "localhost" httpd-port) + (httpd-start))) + +(defun clomacs-httpd-stop () + "Stop Emacs http server and reset host and port on Clojure side." + (clomacs-require `'clomacs) + (clomacs-close-emacs-connection) + (httpd-stop)) + +(provide 'clomacs) + +;;; clomacs.el ends here