thirdparty/clojure-mode.el
changeset 50 6590d340a568
equal deleted inserted replaced
49:942fe8d1a653 50:6590d340a568
       
     1 ;;; clojure-mode.el --- Major mode for Clojure code
       
     2 
       
     3 ;; Copyright (C) 2007-2011 Jeffrey Chu, Lennart Staflin, Phil Hagelberg
       
     4 ;;
       
     5 ;; Authors: Jeffrey Chu <[email protected]>
       
     6 ;;          Lennart Staflin <[email protected]>
       
     7 ;;          Phil Hagelberg <[email protected]>
       
     8 ;; URL: http://github.com/technomancy/clojure-mode
       
     9 ;; Version: 1.11.5
       
    10 ;; Keywords: languages, lisp
       
    11 
       
    12 ;; This file is not part of GNU Emacs.
       
    13 
       
    14 ;;; Commentary:
       
    15 
       
    16 ;; Provides font-lock, indentation, and navigation for the Clojure
       
    17 ;; language. (http://clojure.org)
       
    18 
       
    19 ;; Users of older Emacs (pre-22) should get version 1.4:
       
    20 ;; http://github.com/technomancy/clojure-mode/tree/1.4
       
    21 
       
    22 ;;; Installation:
       
    23 
       
    24 ;; Use package.el. You'll need to add Marmalade to your archives:
       
    25 
       
    26 ;; (require 'package)
       
    27 ;; (add-to-list 'package-archives
       
    28 ;;              '("marmalade" . "http://marmalade-repo.org/packages/"))
       
    29 
       
    30 ;; If you use a version of Emacs prior to 24 that doesn't include
       
    31 ;; package.el, you can get it from http://bit.ly/pkg-el23. If you have
       
    32 ;; an older package.el installed from tromey.com, you should upgrade
       
    33 ;; in order to support installation from multiple sources.
       
    34 
       
    35 ;; Of course, it's possible to just place it on your load-path and
       
    36 ;; require it as well if you don't mind missing out on
       
    37 ;; byte-compilation and autoloads.
       
    38 
       
    39 ;; Using clojure-mode with paredit is highly recommended. It is also
       
    40 ;; available using package.el from the above archive.
       
    41 
       
    42 ;; Use paredit as you normally would with any other mode; for instance:
       
    43 ;;
       
    44 ;;   ;; require or autoload paredit-mode
       
    45 ;;   (defun turn-on-paredit () (paredit-mode 1))
       
    46 ;;   (add-hook 'clojure-mode-hook 'turn-on-paredit)
       
    47 
       
    48 ;; See Swank Clojure (http://github.com/technomancy/swank-clojure) for
       
    49 ;; better interaction with subprocesses via SLIME.
       
    50 
       
    51 ;;; License:
       
    52 
       
    53 ;; This program is free software; you can redistribute it and/or
       
    54 ;; modify it under the terms of the GNU General Public License
       
    55 ;; as published by the Free Software Foundation; either version 3
       
    56 ;; of the License, or (at your option) any later version.
       
    57 ;;
       
    58 ;; This program is distributed in the hope that it will be useful,
       
    59 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    60 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    61 ;; GNU General Public License for more details.
       
    62 ;;
       
    63 ;; You should have received a copy of the GNU General Public License
       
    64 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
       
    65 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    66 ;; Boston, MA 02110-1301, USA.
       
    67 
       
    68 ;;; Code:
       
    69 
       
    70 (require 'cl)
       
    71 
       
    72 (defgroup clojure-mode nil
       
    73   "A mode for Clojure"
       
    74   :prefix "clojure-mode-"
       
    75   :group 'applications)
       
    76 
       
    77 (defcustom clojure-mode-font-lock-comment-sexp nil
       
    78   "Set to non-nil in order to enable font-lock of (comment...)
       
    79 forms. This option is experimental. Changing this will require a
       
    80 restart (ie. M-x clojure-mode) of existing clojure mode buffers."
       
    81   :type 'boolean
       
    82   :group 'clojure-mode)
       
    83 
       
    84 (defcustom clojure-mode-load-command  "(clojure.core/load-file \"%s\")\n"
       
    85   "*Format-string for building a Clojure expression to load a file.
       
    86 This format string should use `%s' to substitute a file name
       
    87 and should result in a Clojure expression that will command the inferior
       
    88 Clojure to load that file."
       
    89   :type 'string
       
    90   :group 'clojure-mode)
       
    91 
       
    92 (defcustom clojure-mode-use-backtracking-indent t
       
    93   "Set to non-nil to enable backtracking/context sensitive indentation."
       
    94   :type 'boolean
       
    95   :group 'clojure-mode)
       
    96 
       
    97 (defcustom clojure-max-backtracking 3
       
    98   "Maximum amount to backtrack up a list to check for context."
       
    99   :type 'integer
       
   100   :group 'clojure-mode)
       
   101 
       
   102 (defvar clojure-mode-map
       
   103   (let ((map (make-sparse-keymap)))
       
   104     (set-keymap-parent map lisp-mode-shared-map)
       
   105     (define-key map "\e\C-x" 'lisp-eval-defun)
       
   106     (define-key map "\C-x\C-e" 'lisp-eval-last-sexp)
       
   107     (define-key map "\C-c\C-e" 'lisp-eval-last-sexp)
       
   108     (define-key map "\C-c\C-l" 'clojure-load-file)
       
   109     (define-key map "\C-c\C-r" 'lisp-eval-region)
       
   110     (define-key map "\C-c\C-z" 'clojure-display-inferior-lisp-buffer)
       
   111     (define-key map (kbd "RET") 'reindent-then-newline-and-indent)
       
   112     (define-key map (kbd "C-c t") 'clojure-jump-to-test)
       
   113     (define-key map (kbd "C-c M-q") 'clojure-fill-docstring)
       
   114     map)
       
   115   "Keymap for Clojure mode. Inherits from `lisp-mode-shared-map'.")
       
   116 
       
   117 (defvar clojure-mode-syntax-table
       
   118   (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
       
   119     (modify-syntax-entry ?~ "'   " table)
       
   120     ;; can't safely make commas whitespace since it will apply even
       
   121     ;; inside string literals--ick!
       
   122     ;; (modify-syntax-entry ?, "    " table)
       
   123     (modify-syntax-entry ?\{ "(}" table)
       
   124     (modify-syntax-entry ?\} "){" table)
       
   125     (modify-syntax-entry ?\[ "(]" table)
       
   126     (modify-syntax-entry ?\] ")[" table)
       
   127     (modify-syntax-entry ?^ "'" table)
       
   128     table))
       
   129 
       
   130 (defvar clojure-mode-abbrev-table nil
       
   131   "Abbrev table used in clojure-mode buffers.")
       
   132 
       
   133 (define-abbrev-table 'clojure-mode-abbrev-table ())
       
   134 
       
   135 (defvar clojure-prev-l/c-dir/file nil
       
   136   "Record last directory and file used in loading or compiling.
       
   137 This holds a cons cell of the form `(DIRECTORY . FILE)'
       
   138 describing the last `clojure-load-file' or `clojure-compile-file' command.")
       
   139 
       
   140 (defvar clojure-test-ns-segment-position -1
       
   141   "Which segment of the ns is \"test\" inserted in your test name convention.
       
   142 
       
   143 Customize this depending on your project's conventions. Negative
       
   144 numbers count from the end:
       
   145 
       
   146   leiningen.compile -> leiningen.test.compile (uses 1)
       
   147   clojure.http.client -> clojure.http.test.client (uses -1)")
       
   148 
       
   149 (defun clojure-mode-version ()
       
   150   "Currently package.el doesn't support prerelease version numbers."
       
   151   "1.11.5")
       
   152 
       
   153 ;;;###autoload
       
   154 (defun clojure-mode ()
       
   155   "Major mode for editing Clojure code - similar to Lisp mode.
       
   156 Commands:
       
   157 Delete converts tabs to spaces as it moves back.
       
   158 Blank lines separate paragraphs.  Semicolons start comments.
       
   159 \\{clojure-mode-map}
       
   160 Note that `run-lisp' may be used either to start an inferior Lisp job
       
   161 or to switch back to an existing one.
       
   162 
       
   163 Entry to this mode calls the value of `clojure-mode-hook'
       
   164 if that value is non-nil."
       
   165   (interactive)
       
   166   (kill-all-local-variables)
       
   167   (use-local-map clojure-mode-map)
       
   168   (setq mode-name "Clojure"
       
   169         major-mode 'clojure-mode
       
   170         imenu-create-index-function
       
   171         (lambda ()
       
   172           (imenu--generic-function '((nil clojure-match-next-def 0))))
       
   173         local-abbrev-table clojure-mode-abbrev-table
       
   174         indent-tabs-mode nil)
       
   175   (lisp-mode-variables nil)
       
   176   (set-syntax-table clojure-mode-syntax-table)
       
   177   (set (make-local-variable 'comment-start-skip)
       
   178        "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *")
       
   179   (set (make-local-variable 'lisp-indent-function)
       
   180        'clojure-indent-function)
       
   181   (when (< emacs-major-version 24)
       
   182     (set (make-local-variable 'forward-sexp-function)
       
   183          'clojure-forward-sexp))
       
   184   (set (make-local-variable 'lisp-doc-string-elt-property)
       
   185        'clojure-doc-string-elt)
       
   186   (set (make-local-variable 'inferior-lisp-program) "lein repl")
       
   187   (set (make-local-variable 'parse-sexp-ignore-comments) t)
       
   188 
       
   189   (clojure-mode-font-lock-setup)
       
   190 
       
   191   (run-mode-hooks 'clojure-mode-hook)
       
   192   (run-hooks 'prog-mode-hook)
       
   193 
       
   194   ;; Enable curly braces when paredit is enabled in clojure-mode-hook
       
   195   (when (and (featurep 'paredit) paredit-mode (>= paredit-version 21))
       
   196     (define-key clojure-mode-map "{" 'paredit-open-curly)
       
   197     (define-key clojure-mode-map "}" 'paredit-close-curly)))
       
   198 
       
   199 (defun clojure-display-inferior-lisp-buffer ()
       
   200   "Display a buffer bound to `inferior-lisp-buffer'."
       
   201   (interactive)
       
   202   (if (and inferior-lisp-buffer (get-buffer inferior-lisp-buffer))
       
   203       (pop-to-buffer inferior-lisp-buffer t)
       
   204       (run-lisp inferior-lisp-program)))
       
   205 
       
   206 (defun clojure-load-file (file-name)
       
   207   "Load a Lisp file into the inferior Lisp process."
       
   208   (interactive (comint-get-source "Load Clojure file: "
       
   209                                   clojure-prev-l/c-dir/file
       
   210                                   '(clojure-mode) t))
       
   211   (comint-check-source file-name) ; Check to see if buffer needs saved.
       
   212   (setq clojure-prev-l/c-dir/file (cons (file-name-directory file-name)
       
   213                                         (file-name-nondirectory file-name)))
       
   214   (comint-send-string (inferior-lisp-proc)
       
   215                       (format clojure-mode-load-command file-name))
       
   216   (switch-to-lisp t))
       
   217 
       
   218 
       
   219 
       
   220 (defun clojure-match-next-def ()
       
   221   "Scans the buffer backwards for the next top-level definition.
       
   222 Called by `imenu--generic-function'."
       
   223   (when (re-search-backward "^\\s *(def\\S *[ \n\t]+" nil t)
       
   224     (save-excursion
       
   225       (goto-char (match-end 0))
       
   226       (when (looking-at "#?\\^")
       
   227         (let (forward-sexp-function) ; using the built-in one
       
   228           (forward-sexp)))           ; skip the metadata
       
   229       (re-search-forward "[^ \n\t)]+"))))
       
   230 
       
   231 (defun clojure-mode-font-lock-setup ()
       
   232   "Configures font-lock for editing Clojure code."
       
   233   (interactive)
       
   234   (set (make-local-variable 'font-lock-multiline) t)
       
   235   (add-to-list 'font-lock-extend-region-functions
       
   236                'clojure-font-lock-extend-region-def t)
       
   237 
       
   238   (when clojure-mode-font-lock-comment-sexp
       
   239     (add-to-list 'font-lock-extend-region-functions
       
   240                  'clojure-font-lock-extend-region-comment t)
       
   241     (make-local-variable 'clojure-font-lock-keywords)
       
   242     (add-to-list 'clojure-font-lock-keywords
       
   243                  'clojure-font-lock-mark-comment t)
       
   244     (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil))
       
   245 
       
   246   (setq font-lock-defaults
       
   247         '(clojure-font-lock-keywords    ; keywords
       
   248           nil nil
       
   249           (("+-*/.<>=!?$%_&~^:@" . "w")) ; syntax alist
       
   250           nil
       
   251           (font-lock-mark-block-function . mark-defun)
       
   252           (font-lock-syntactic-face-function
       
   253            . lisp-font-lock-syntactic-face-function))))
       
   254 
       
   255 (defun clojure-font-lock-def-at-point (point)
       
   256   "Find the position range between the top-most def* and the
       
   257 fourth element afterwards. Note that this means there's no
       
   258 gaurantee of proper font locking in def* forms that are not at
       
   259 top-level."
       
   260   (goto-char point)
       
   261   (condition-case nil
       
   262       (beginning-of-defun)
       
   263     (error nil))
       
   264 
       
   265   (let ((beg-def (point)))
       
   266     (when (and (not (= point beg-def))
       
   267                (looking-at "(def"))
       
   268       (condition-case nil
       
   269           (progn
       
   270             ;; move forward as much as possible until failure (or success)
       
   271             (forward-char)
       
   272             (dotimes (i 4)
       
   273               (forward-sexp)))
       
   274         (error nil))
       
   275       (cons beg-def (point)))))
       
   276 
       
   277 (defun clojure-font-lock-extend-region-def ()
       
   278   "Move fontification boundaries to always include the first four
       
   279 elements of a def* forms."
       
   280   (let ((changed nil))
       
   281     (let ((def (clojure-font-lock-def-at-point font-lock-beg)))
       
   282       (when def
       
   283         (destructuring-bind (def-beg . def-end) def
       
   284           (when (and (< def-beg font-lock-beg)
       
   285                      (< font-lock-beg def-end))
       
   286             (setq font-lock-beg def-beg
       
   287                   changed t)))))
       
   288 
       
   289     (let ((def (clojure-font-lock-def-at-point font-lock-end)))
       
   290       (when def
       
   291         (destructuring-bind (def-beg . def-end) def
       
   292           (when (and (< def-beg font-lock-end)
       
   293                      (< font-lock-end def-end))
       
   294             (setq font-lock-end def-end
       
   295                   changed t)))))
       
   296     changed))
       
   297 
       
   298 (defun clojure-font-lock-extend-region-comment ()
       
   299   "Move fontification boundaries to always contain
       
   300   entire (comment ..) sexp. Does not work if you have a
       
   301   white-space between ( and comment, but that is omitted to make
       
   302   this run faster."
       
   303   (let ((changed nil))
       
   304     (goto-char font-lock-beg)
       
   305     (condition-case nil (beginning-of-defun) (error nil))
       
   306     (let ((pos (re-search-forward "(comment\\>" font-lock-end t)))
       
   307       (when pos
       
   308         (forward-char -8)
       
   309         (when (< (point) font-lock-beg)
       
   310           (setq font-lock-beg (point)
       
   311                 changed t))
       
   312         (condition-case nil (forward-sexp) (error nil))
       
   313         (when (> (point) font-lock-end)
       
   314           (setq font-lock-end (point)
       
   315                 changed t))))
       
   316     changed))
       
   317 
       
   318 (defun clojure-font-lock-mark-comment (limit)
       
   319   "Marks all (comment ..) forms with font-lock-comment-face."
       
   320   (let (pos)
       
   321     (while (and (< (point) limit)
       
   322                 (setq pos (re-search-forward "(comment\\>" limit t)))
       
   323       (when pos
       
   324         (forward-char -8)
       
   325         (condition-case nil
       
   326             (add-text-properties (1+ (point)) (progn
       
   327                                                 (forward-sexp) (1- (point)))
       
   328                                  '(face font-lock-comment-face multiline t))
       
   329           (error (forward-char 8))))))
       
   330   nil)
       
   331 
       
   332 (defconst clojure-font-lock-keywords
       
   333   (eval-when-compile
       
   334     `( ;; Definitions.
       
   335       (,(concat "(\\(?:clojure.core/\\)?\\("
       
   336                 (regexp-opt '("defn" "defn-" "def" "def-" "defonce"
       
   337                               "defmulti" "defmethod" "defmacro"
       
   338                               "defstruct" "deftype" "defprotocol"
       
   339                               "defrecord" "deftest"
       
   340                               "slice" "def\\[a-z\\]"
       
   341                               "defalias" "defhinted" "defmacro-"
       
   342                               "defn-memo" "defnk" "defonce-"
       
   343                               "defstruct-" "defunbound" "defunbound-"
       
   344                               "defvar" "defvar-"))
       
   345                 ;; Function declarations.
       
   346                 "\\)\\>"
       
   347                 ;; Any whitespace
       
   348                 "[ \r\n\t]*"
       
   349                 ;; Possibly type or metadata
       
   350                 "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*"
       
   351                 "\\(\\sw+\\)?")
       
   352        (1 font-lock-keyword-face)
       
   353        (2 font-lock-function-name-face nil t))
       
   354       ;; Deprecated functions
       
   355       (,(concat
       
   356          "(\\(?:clojure.core/\\)?"
       
   357          (regexp-opt
       
   358           '("add-watcher" "remove-watcher" "add-classpath") t)
       
   359          "\\>")
       
   360        1 font-lock-warning-face)
       
   361       ;; Control structures
       
   362       (,(concat
       
   363          "(\\(?:clojure.core/\\)?"
       
   364          (regexp-opt
       
   365           '("let" "letfn" "do"
       
   366             "case" "cond" "condp"
       
   367             "for" "loop" "recur"
       
   368             "when" "when-not" "when-let" "when-first"
       
   369             "if" "if-let" "if-not"
       
   370             "." ".." "->" "->>" "doto"
       
   371             "and" "or"
       
   372             "dosync" "doseq" "dotimes" "dorun" "doall"
       
   373             "load" "import" "unimport" "ns" "in-ns" "refer"
       
   374             "try" "catch" "finally" "throw"
       
   375             "with-open" "with-local-vars" "binding"
       
   376             "gen-class" "gen-and-load-class" "gen-and-save-class"
       
   377             "handler-case" "handle") t)
       
   378          "\\>")
       
   379        1 font-lock-builtin-face)
       
   380       ;; Built-ins
       
   381       (,(concat
       
   382          "(\\(?:clojure.core/\\)?"
       
   383          (regexp-opt
       
   384           '("*" "*1" "*2" "*3" "*agent*"
       
   385         "*allow-unresolved-vars*" "*assert*" "*clojure-version*" "*command-line-args*" "*compile-files*"
       
   386         "*compile-path*" "*e" "*err*" "*file*" "*flush-on-newline*"
       
   387         "*in*" "*macro-meta*" "*math-context*" "*ns*" "*out*"
       
   388         "*print-dup*" "*print-length*" "*print-level*" "*print-meta*" "*print-readably*"
       
   389         "*read-eval*" "*source-path*" "*use-context-classloader*" "*warn-on-reflection*" "+"
       
   390         "-" "/"
       
   391         "<" "<=" "=" "==" ">"
       
   392         ">=" "accessor" "aclone"
       
   393         "agent" "agent-errors" "aget" "alength" "alias"
       
   394         "all-ns" "alter" "alter-meta!" "alter-var-root" "amap"
       
   395         "ancestors" "and" "apply" "areduce" "array-map"
       
   396         "aset" "aset-boolean" "aset-byte" "aset-char" "aset-double"
       
   397         "aset-float" "aset-int" "aset-long" "aset-short" "assert"
       
   398         "assoc" "assoc!" "assoc-in" "associative?" "atom"
       
   399         "await" "await-for" "await1" "bases" "bean"
       
   400         "bigdec" "bigint" "binding" "bit-and" "bit-and-not"
       
   401         "bit-clear" "bit-flip" "bit-not" "bit-or" "bit-set"
       
   402         "bit-shift-left" "bit-shift-right" "bit-test" "bit-xor" "boolean"
       
   403         "boolean-array" "booleans" "bound-fn" "bound-fn*" "butlast"
       
   404         "byte" "byte-array" "bytes" "case" "cast" "char"
       
   405         "char-array" "char-escape-string" "char-name-string" "char?" "chars"
       
   406         "chunk" "chunk-append" "chunk-buffer" "chunk-cons" "chunk-first"
       
   407         "chunk-next" "chunk-rest" "chunked-seq?" "class" "class?"
       
   408         "clear-agent-errors" "clojure-version" "coll?" "comment" "commute"
       
   409         "comp" "comparator" "compare" "compare-and-set!" "compile"
       
   410         "complement" "concat" "cond" "condp" "conj"
       
   411         "conj!" "cons" "constantly" "construct-proxy" "contains?"
       
   412         "count" "counted?" "create-ns" "create-struct" "cycle"
       
   413         "dec" "decimal?" "declare" "definline" "defmacro"
       
   414         "defmethod" "defmulti" "defn" "defn-" "defonce"
       
   415         "defstruct" "delay" "delay?" "deliver" "deref"
       
   416         "derive" "descendants" "destructure" "disj" "disj!"
       
   417         "dissoc" "dissoc!" "distinct" "distinct?" "doall"
       
   418         "doc" "dorun" "doseq" "dosync" "dotimes"
       
   419         "doto" "double" "double-array" "doubles" "drop"
       
   420         "drop-last" "drop-while" "empty" "empty?" "ensure"
       
   421         "enumeration-seq" "eval" "even?" "every?"
       
   422         "extend" "extend-protocol" "extend-type" "extends?" "extenders"
       
   423         "false?" "ffirst" "file-seq" "filter" "find" "find-doc"
       
   424         "find-ns" "find-var" "first" "flatten" "float" "float-array"
       
   425         "float?" "floats" "flush" "fn" "fn?"
       
   426         "fnext" "for" "force" "format" "future"
       
   427         "future-call" "future-cancel" "future-cancelled?" "future-done?" "future?"
       
   428         "gen-class" "gen-interface" "gensym" "get" "get-in"
       
   429         "get-method" "get-proxy-class" "get-thread-bindings" "get-validator" "hash"
       
   430         "hash-map" "hash-set" "identical?" "identity" "if-let"
       
   431         "if-not" "ifn?" "import" "in-ns" "inc"
       
   432         "init-proxy" "instance?" "int" "int-array" "integer?"
       
   433         "interleave" "intern" "interpose" "into" "into-array"
       
   434         "ints" "io!" "isa?" "iterate" "iterator-seq"
       
   435         "juxt" "key" "keys" "keyword" "keyword?"
       
   436         "last" "lazy-cat" "lazy-seq" "let" "letfn"
       
   437         "line-seq" "list" "list*" "list?" "load"
       
   438         "load-file" "load-reader" "load-string" "loaded-libs" "locking"
       
   439         "long" "long-array" "longs" "loop" "macroexpand"
       
   440         "macroexpand-1" "make-array" "make-hierarchy" "map" "map?"
       
   441         "mapcat" "max" "max-key" "memfn" "memoize"
       
   442         "merge" "merge-with" "meta" "method-sig" "methods"
       
   443         "min" "min-key" "mod" "name" "namespace"
       
   444         "neg?" "newline" "next" "nfirst" "nil?"
       
   445         "nnext" "not" "not-any?" "not-empty" "not-every?"
       
   446         "not=" "ns" "ns-aliases" "ns-imports" "ns-interns"
       
   447         "ns-map" "ns-name" "ns-publics" "ns-refers" "ns-resolve"
       
   448         "ns-unalias" "ns-unmap" "nth" "nthnext" "num"
       
   449         "number?" "odd?" "or" "parents" "partial"
       
   450         "partition" "pcalls" "peek" "persistent!" "pmap"
       
   451         "pop" "pop!" "pop-thread-bindings" "pos?" "pr"
       
   452         "pr-str" "prefer-method" "prefers" "primitives-classnames" "print"
       
   453         "print-ctor" "print-doc" "print-dup" "print-method" "print-namespace-doc"
       
   454         "print-simple" "print-special-doc" "print-str" "printf" "println"
       
   455         "println-str" "prn" "prn-str" "promise" "proxy"
       
   456         "proxy-call-with-super" "proxy-mappings" "proxy-name" "proxy-super" "push-thread-bindings"
       
   457         "pvalues" "quot" "rand" "rand-int" "range"
       
   458         "ratio?" "rational?" "rationalize" "re-find" "re-groups"
       
   459         "re-matcher" "re-matches" "re-pattern" "re-seq" "read"
       
   460         "read-line" "read-string" "reify" "reduce" "ref" "ref-history-count"
       
   461         "ref-max-history" "ref-min-history" "ref-set" "refer" "refer-clojure"
       
   462         "release-pending-sends" "rem" "remove" "remove-method" "remove-ns"
       
   463         "repeat" "repeatedly" "replace" "replicate"
       
   464         "require" "reset!" "reset-meta!" "resolve" "rest"
       
   465         "resultset-seq" "reverse" "reversible?" "rseq" "rsubseq"
       
   466         "satisfies?" "second" "select-keys" "send" "send-off" "seq"
       
   467         "seq?" "seque" "sequence" "sequential?" "set"
       
   468         "set-validator!" "set?" "short" "short-array" "shorts"
       
   469         "shutdown-agents" "slurp" "some" "sort" "sort-by"
       
   470         "sorted-map" "sorted-map-by" "sorted-set" "sorted-set-by" "sorted?"
       
   471         "special-form-anchor" "special-symbol?" "split-at" "split-with" "str"
       
   472         "stream?" "string?" "struct" "struct-map" "subs"
       
   473         "subseq" "subvec" "supers" "swap!" "symbol"
       
   474         "symbol?" "sync" "syntax-symbol-anchor" "take" "take-last"
       
   475         "take-nth" "take-while" "test" "the-ns" "time"
       
   476         "to-array" "to-array-2d" "trampoline" "transient" "tree-seq"
       
   477         "true?" "type" "unchecked-add" "unchecked-dec" "unchecked-divide"
       
   478         "unchecked-inc" "unchecked-multiply" "unchecked-negate" "unchecked-remainder" "unchecked-subtract"
       
   479         "underive" "unquote" "unquote-splicing" "update-in" "update-proxy"
       
   480         "use" "val" "vals" "var-get" "var-set"
       
   481         "var?" "vary-meta" "vec" "vector" "vector?"
       
   482         "when" "when-first" "when-let" "when-not" "while"
       
   483         "with-bindings" "with-bindings*" "with-in-str" "with-loading-context" "with-local-vars"
       
   484         "with-meta" "with-open" "with-out-str" "with-precision" "xml-seq" "zipmap"
       
   485         ) t)
       
   486          "\\>")
       
   487        1 font-lock-variable-name-face)
       
   488       ;; (fn name? args ...)
       
   489       (,(concat "(\\(?:clojure.core/\\)?\\(fn\\)[ \t]+"
       
   490                 ;; Possibly type
       
   491                 "\\(?:#?^\\sw+[ \t]*\\)?"
       
   492                 ;; Possibly name
       
   493                 "\\(\\sw+\\)?" )
       
   494        (1 font-lock-keyword-face)
       
   495        (2 font-lock-function-name-face nil t))
       
   496       ;;Other namespaces in clojure.jar
       
   497       (,(concat
       
   498          "(\\(?:\.*/\\)?"
       
   499          (regexp-opt
       
   500           '(;; clojure.inspector
       
   501         "atom?" "collection-tag" "get-child" "get-child-count" "inspect"
       
   502         "inspect-table" "inspect-tree" "is-leaf" "list-model" "list-provider"
       
   503         ;; clojure.main
       
   504         "load-script" "main" "repl" "repl-caught" "repl-exception"
       
   505         "repl-prompt" "repl-read" "skip-if-eol" "skip-whitespace" "with-bindings"
       
   506         ;; clojure.set
       
   507         "difference" "index" "intersection" "join" "map-invert"
       
   508         "project" "rename" "rename-keys" "select" "union"
       
   509         ;; clojure.stacktrace
       
   510         "e" "print-cause-trace" "print-stack-trace" "print-throwable" "print-trace-element"
       
   511         ;; clojure.template
       
   512         "do-template" "apply-template"
       
   513         ;; clojure.test
       
   514         "*initial-report-counters*" "*load-tests*" "*report-counters*" "*stack-trace-depth*" "*test-out*"
       
   515         "*testing-contexts*" "*testing-vars*" "are" "assert-any" "assert-expr"
       
   516         "assert-predicate" "compose-fixtures" "deftest" "deftest-" "file-position"
       
   517         "function?" "get-possibly-unbound-var" "inc-report-counter" "is" "join-fixtures"
       
   518         "report" "run-all-tests" "run-tests" "set-test" "successful?"
       
   519         "test-all-vars" "test-ns" "test-var" "testing" "testing-contexts-str"
       
   520         "testing-vars-str" "try-expr" "use-fixtures" "with-test" "with-test-out"
       
   521         ;; clojure.walk
       
   522         "keywordize-keys" "macroexpand-all" "postwalk" "postwalk-demo" "postwalk-replace"
       
   523         "prewalk" "prewalk-demo" "prewalk-replace" "stringify-keys" "walk"
       
   524         ;; clojure.xml
       
   525         "*current*" "*sb*" "*stack*" "*state*" "attrs"
       
   526         "content" "content-handler" "element" "emit" "emit-element"
       
   527         ;; clojure.zip
       
   528         "append-child" "branch?" "children" "down" "edit"
       
   529         "end?" "insert-child" "insert-left" "insert-right" "left"
       
   530         "leftmost" "lefts" "make-node" "next" "node"
       
   531         "path" "prev" "remove" "replace" "right"
       
   532         "rightmost" "rights" "root" "seq-zip" "up"
       
   533         ) t)
       
   534          "\\>")
       
   535        1 font-lock-type-face)
       
   536       ;; Constant values (keywords), including as metadata e.g. ^:static
       
   537       ("\\<^?:\\(\\sw\\|#\\)+\\>" 0 font-lock-constant-face)
       
   538       ;; Meta type annotation #^Type or ^Type
       
   539       ("#?^\\sw+" 0 font-lock-type-face)
       
   540       ("\\<io\\!\\>" 0 font-lock-warning-face)
       
   541 
       
   542       ;;Java interop highlighting
       
   543       ("\\<\\.[a-z][a-zA-Z0-9]*\\>" 0 font-lock-preprocessor-face) ;; .foo .barBaz .qux01
       
   544       ("\\<[A-Z][a-zA-Z0-9]*/[a-zA-Z0-9/$_]+\\>" 0 font-lock-preprocessor-face) ;; Foo Bar$Baz Qux_
       
   545       ("\\<[a-zA-Z]+\\.[a-zA-Z0-9._]*[A-Z]+[a-zA-Z0-9/.$]*\\>" 0 font-lock-preprocessor-face) ;; Foo/Bar foo.bar.Baz foo.Bar/baz
       
   546       ("[a-z]*[A-Z]+[a-z][a-zA-Z0-9$]*\\>" 0 font-lock-preprocessor-face) ;; fooBar
       
   547       ("\\<[A-Z][a-zA-Z0-9$]*\\.\\>" 0 font-lock-preprocessor-face))) ;; Foo. BarBaz. Qux$Quux. Corge9.
       
   548 
       
   549 
       
   550   "Default expressions to highlight in Clojure mode.")
       
   551 
       
   552 ;; Docstring positions
       
   553 (put 'defn 'clojure-doc-string-elt 2)
       
   554 (put 'defn- 'clojure-doc-string-elt 2)
       
   555 (put 'defmulti 'clojure-doc-string-elt 2)
       
   556 (put 'defmacro 'clojure-doc-string-elt 2)
       
   557 (put 'definline 'clojure-doc-string-elt 2)
       
   558 (put 'defprotocol 'clojure-doc-string-elt 2)
       
   559 
       
   560 ;; Docstring positions - contrib
       
   561 (put 'defalias 'clojure-doc-string-elt 3)
       
   562 (put 'defmacro- 'clojure-doc-string-elt 2)
       
   563 (put 'defn-memo 'clojure-doc-string-elt 2)
       
   564 (put 'defnk 'clojure-doc-string-elt 2)
       
   565 (put 'defonce- 'clojure-doc-string-elt 3)
       
   566 (put 'defunbound 'clojure-doc-string-elt 2)
       
   567 (put 'defunbound- 'clojure-doc-string-elt 2)
       
   568 (put 'defvar 'clojure-doc-string-elt 3)
       
   569 (put 'defvar- 'clojure-doc-string-elt 3)
       
   570 
       
   571 
       
   572 
       
   573 (defun clojure-forward-sexp (n)
       
   574   "Treat record literals like #user.Foo[1] and #user.Foo{:size 1}
       
   575 as a single sexp so that slime will send them properly. Arguably
       
   576 this behavior is unintuitive for the user pressing (eg) C-M-f
       
   577 himself, but since these are single objects I think it's right."
       
   578   (let ((dir (if (> n 0) 1 -1))
       
   579         (forward-sexp-function nil)) ; force the built-in version
       
   580     (while (not (zerop n))
       
   581       (forward-sexp dir)
       
   582       (when (save-excursion ; move back to see if we're in a record literal
       
   583               (and
       
   584                (condition-case nil
       
   585                    (progn (backward-sexp) 't)
       
   586                  ('scan-error nil))
       
   587                (looking-at "#\\w")))
       
   588         (forward-sexp dir)) ; if so, jump over it
       
   589       (setq n (- n dir)))))
       
   590 
       
   591 (defun clojure-indent-function (indent-point state)
       
   592   "This function is the normal value of the variable `lisp-indent-function'.
       
   593 It is used when indenting a line within a function call, to see if the
       
   594 called function says anything special about how to indent the line.
       
   595 
       
   596 INDENT-POINT is the position where the user typed TAB, or equivalent.
       
   597 Point is located at the point to indent under (for default indentation);
       
   598 STATE is the `parse-partial-sexp' state for that position.
       
   599 
       
   600 If the current line is in a call to a Lisp function
       
   601 which has a non-nil property `lisp-indent-function',
       
   602 that specifies how to do the indentation.  The property value can be
       
   603 * `defun', meaning indent `defun'-style;
       
   604 * an integer N, meaning indent the first N arguments specially
       
   605   like ordinary function arguments and then indent any further
       
   606   arguments like a body;
       
   607 * a function to call just as this function was called.
       
   608   If that function returns nil, that means it doesn't specify
       
   609   the indentation.
       
   610 
       
   611 This function also returns nil meaning don't specify the indentation."
       
   612   (let ((normal-indent (current-column)))
       
   613     (goto-char (1+ (elt state 1)))
       
   614     (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
       
   615     (if (and (elt state 2)
       
   616              (not (looking-at "\\sw\\|\\s_")))
       
   617         ;; car of form doesn't seem to be a symbol
       
   618         (progn
       
   619           (if (not (> (save-excursion (forward-line 1) (point))
       
   620                       calculate-lisp-indent-last-sexp))
       
   621               (progn (goto-char calculate-lisp-indent-last-sexp)
       
   622                      (beginning-of-line)
       
   623                      (parse-partial-sexp (point)
       
   624                                          calculate-lisp-indent-last-sexp 0 t)))
       
   625           ;; Indent under the list or under the first sexp on the same
       
   626           ;; line as calculate-lisp-indent-last-sexp.  Note that first
       
   627           ;; thing on that line has to be complete sexp since we are
       
   628           ;; inside the innermost containing sexp.
       
   629           (backward-prefix-chars)
       
   630           (if (and (eq (char-after (point)) ?\[)
       
   631                    (eq (char-after (elt state 1)) ?\())
       
   632               (+ (current-column) 2) ;; this is probably inside a defn
       
   633             (current-column)))
       
   634       (let ((function (buffer-substring (point)
       
   635                                         (progn (forward-sexp 1) (point))))
       
   636             (open-paren (elt state 1))
       
   637             method)
       
   638         (setq method (get (intern-soft function) 'clojure-indent-function))
       
   639 
       
   640         (cond ((member (char-after open-paren) '(?\[ ?\{))
       
   641                (goto-char open-paren)
       
   642                (1+ (current-column)))
       
   643               ((or (eq method 'defun)
       
   644                    (and (null method)
       
   645                         (> (length function) 3)
       
   646                         (string-match "\\`\\(?:\\S +/\\)?def\\|with-"
       
   647                                       function)))
       
   648                (lisp-indent-defform state indent-point))
       
   649 
       
   650               ((integerp method)
       
   651                (lisp-indent-specform method state
       
   652                                      indent-point normal-indent))
       
   653               (method
       
   654                (funcall method indent-point state))
       
   655               (clojure-mode-use-backtracking-indent
       
   656                (clojure-backtracking-indent
       
   657                 indent-point state normal-indent)))))))
       
   658 
       
   659 (defun clojure-backtracking-indent (indent-point state normal-indent)
       
   660   "Experimental backtracking support. Will upwards in an sexp to
       
   661 check for contextual indenting."
       
   662   (let (indent (path) (depth 0))
       
   663     (goto-char (elt state 1))
       
   664     (while (and (not indent)
       
   665                 (< depth clojure-max-backtracking))
       
   666       (let ((containing-sexp (point)))
       
   667         (parse-partial-sexp (1+ containing-sexp) indent-point 1 t)
       
   668         (when (looking-at "\\sw\\|\\s_")
       
   669           (let* ((start (point))
       
   670                  (fn (buffer-substring start (progn (forward-sexp 1) (point))))
       
   671                  (meth (get (intern-soft fn) 'clojure-backtracking-indent)))
       
   672             (let ((n 0))
       
   673               (when (< (point) indent-point)
       
   674                 (condition-case ()
       
   675                     (progn
       
   676                       (forward-sexp 1)
       
   677                       (while (< (point) indent-point)
       
   678                         (parse-partial-sexp (point) indent-point 1 t)
       
   679                         (incf n)
       
   680                         (forward-sexp 1)))
       
   681                   (error nil)))
       
   682               (push n path))
       
   683             (when meth
       
   684               (let ((def meth))
       
   685                 (dolist (p path)
       
   686                   (if (and (listp def)
       
   687                            (< p (length def)))
       
   688                       (setq def (nth p def))
       
   689                     (if (listp def)
       
   690                         (setq def (car (last def)))
       
   691                       (setq def nil))))
       
   692                 (goto-char (elt state 1))
       
   693                 (when def
       
   694                   (setq indent (+ (current-column) def)))))))
       
   695         (goto-char containing-sexp)
       
   696         (condition-case ()
       
   697             (progn
       
   698               (backward-up-list 1)
       
   699               (incf depth))
       
   700           (error (setq depth clojure-max-backtracking)))))
       
   701     indent))
       
   702 
       
   703 ;; clojure backtracking indent is experimental and the format for these
       
   704 ;; entries are subject to change
       
   705 (put 'implement 'clojure-backtracking-indent '(4 (2)))
       
   706 (put 'letfn 'clojure-backtracking-indent '((2) 2))
       
   707 (put 'proxy 'clojure-backtracking-indent '(4 4 (2)))
       
   708 (put 'reify 'clojure-backtracking-indent '((2)))
       
   709 (put 'deftype 'clojure-backtracking-indent '(4 4 (2)))
       
   710 (put 'defrecord 'clojure-backtracking-indent '(4 4 (2)))
       
   711 (put 'defprotocol 'clojure-backtracking-indent '(4 (2)))
       
   712 (put 'extend-type 'clojure-backtracking-indent '(4 (2)))
       
   713 (put 'extend-protocol 'clojure-backtracking-indent '(4 (2)))
       
   714 
       
   715 (defun put-clojure-indent (sym indent)
       
   716   (put sym 'clojure-indent-function indent))
       
   717 
       
   718 (defmacro define-clojure-indent (&rest kvs)
       
   719   `(progn
       
   720      ,@(mapcar (lambda (x) `(put-clojure-indent
       
   721                         (quote ,(first x)) ,(second x))) kvs)))
       
   722 
       
   723 (defun add-custom-clojure-indents (name value)
       
   724   (setq clojure-defun-indents value)
       
   725   (mapcar (lambda (x)
       
   726             (put-clojure-indent x 'defun))
       
   727           value))
       
   728 
       
   729 (defcustom clojure-defun-indents nil
       
   730   "List of symbols to give defun-style indentation to in Clojure
       
   731 code, in addition to those that are built-in. You can use this to
       
   732 get emacs to indent your own macros the same as it does the
       
   733 built-ins like with-open. To set manually from lisp code,
       
   734 use (put-clojure-indent 'some-symbol 'defun)."
       
   735   :type '(repeat symbol)
       
   736   :group 'clojure-mode
       
   737   :set 'add-custom-clojure-indents)
       
   738 
       
   739 (define-clojure-indent
       
   740   ;; built-ins
       
   741   (ns 1)
       
   742   (fn 'defun)
       
   743   (def 'defun)
       
   744   (defn 'defun)
       
   745   (bound-fn 'defun)
       
   746   (if 1)
       
   747   (if-not 1)
       
   748   (case 1)
       
   749   (condp 2)
       
   750   (when 1)
       
   751   (while 1)
       
   752   (when-not 1)
       
   753   (when-first 1)
       
   754   (do 0)
       
   755   (future 0)
       
   756   (comment 0)
       
   757   (doto 1)
       
   758   (locking 1)
       
   759   (proxy 2)
       
   760   (with-open 1)
       
   761   (with-precision 1)
       
   762   (with-local-vars 1)
       
   763 
       
   764   (reify 'defun)
       
   765   (deftype 2)
       
   766   (defrecord 2)
       
   767   (defprotocol 1)
       
   768   (extend 1)
       
   769   (extend-protocol 1)
       
   770   (extend-type 1)
       
   771 
       
   772   (try 0)
       
   773   (catch 2)
       
   774   (finally 0)
       
   775 
       
   776   ;; binding forms
       
   777   (let 1)
       
   778   (letfn 1)
       
   779   (binding 1)
       
   780   (loop 1)
       
   781   (for 1)
       
   782   (doseq 1)
       
   783   (dotimes 1)
       
   784   (when-let 1)
       
   785   (if-let 1)
       
   786 
       
   787   ;; data structures
       
   788   (defstruct 1)
       
   789   (struct-map 1)
       
   790   (assoc 1)
       
   791 
       
   792   (defmethod 'defun)
       
   793 
       
   794   ;; clojure.test
       
   795   (testing 1)
       
   796   (deftest 'defun)
       
   797 
       
   798   ;; contrib
       
   799   (handler-case 1)
       
   800   (handle 1)
       
   801   (dotrace 1)
       
   802   (deftrace 'defun))
       
   803 
       
   804 
       
   805 
       
   806 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
       
   807 ;;
       
   808 ;; Better docstring filling for clojure-mode
       
   809 ;;
       
   810 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
       
   811 
       
   812 (defun clojure-string-start ()
       
   813   "Return the position of the \" that begins the string at point."
       
   814   (save-excursion
       
   815     (save-match-data
       
   816       ;; Find a quote that appears immediately after whitespace,
       
   817       ;; beginning of line, or an open paren, brace, or bracket
       
   818       (re-search-backward "\\(\\s-\\|^\\|(\\|\\[\\|{\\)\\(\"\\)")
       
   819       (match-beginning 2))))
       
   820 
       
   821 (defun clojure-char-at-point ()
       
   822   "Return the char at point or nil if at buffer end."
       
   823   (when (not (= (point) (point-max)))
       
   824    (buffer-substring-no-properties (point) (1+ (point)))))
       
   825 
       
   826 (defun clojure-char-before-point ()
       
   827   "Return the char before point or nil if at buffer beginning."
       
   828   (when (not (= (point) (point-min)))
       
   829     (buffer-substring-no-properties (point) (1- (point)))))
       
   830 
       
   831 ;; TODO: Deal with the fact that when point is exactly at the
       
   832 ;; beginning of a string, it thinks that is the end.
       
   833 (defun clojure-string-end ()
       
   834   "Return the position of the \" that ends the string at point.
       
   835 
       
   836 Note that point must be inside the string - if point is
       
   837 positioned at the opening quote, incorrect results will be
       
   838 returned."
       
   839   (save-excursion
       
   840     (save-match-data
       
   841       ;; If we're at the end of the string, just return point.
       
   842       (if (and (string= (clojure-char-at-point) "\"")
       
   843                (not (string= (clojure-char-before-point) "\\")))
       
   844           (point)
       
   845         ;; We don't want to get screwed by starting out at the
       
   846         ;; backslash in an escaped quote.
       
   847         (when (string= (clojure-char-at-point) "\\")
       
   848           (backward-char))
       
   849         ;; Look for a quote not preceeded by a backslash
       
   850         (re-search-forward "[^\\]\\\(\\\"\\)")
       
   851         (match-beginning 1)))))
       
   852 
       
   853 (defun clojure-docstring-start+end-points ()
       
   854   "Return the start and end points of the string at point as a cons."
       
   855   (if (and (fboundp 'paredit-string-start+end-points) paredit-mode)
       
   856       (paredit-string-start+end-points)
       
   857     (cons (clojure-string-start) (clojure-string-end))))
       
   858 
       
   859 (defun clojure-mark-string ()
       
   860   "Mark the string at point."
       
   861   (interactive)
       
   862   (goto-char (clojure-string-start))
       
   863   (forward-char)
       
   864   (set-mark (clojure-string-end)))
       
   865 
       
   866 (defun clojure-fill-docstring (&optional argument)
       
   867   "Fill the definition that the point is on appropriate for Clojure.
       
   868 
       
   869   Fills so that every paragraph has a minimum of two initial spaces,
       
   870   with the exception of the first line. Fill margins are taken from
       
   871   paragraph start, so a paragraph that begins with four spaces will
       
   872   remain indented by four spaces after refilling."
       
   873   (interactive "P")
       
   874   (if (and (fboundp 'paredit-in-string-p) paredit-mode)
       
   875       (unless (paredit-in-string-p)
       
   876         (error "Must be inside a string")))
       
   877   ;; Oddly, save-excursion doesn't do a good job of preserving point.
       
   878   ;; It's probably because we delete the string and then re-insert it.
       
   879   (let ((old-point (point)))
       
   880     (save-restriction
       
   881       (save-excursion
       
   882         (let* ((string-region (clojure-docstring-start+end-points))
       
   883                (string-start (1+ (car string-region)))
       
   884                (string-end (cdr string-region))
       
   885                (string (buffer-substring-no-properties (1+ (car string-region))
       
   886                                                        (cdr string-region))))
       
   887           (delete-region string-start string-end)
       
   888           (insert
       
   889            (with-temp-buffer
       
   890              (insert string)
       
   891              (let ((left-margin 2))
       
   892                (delete-trailing-whitespace)
       
   893                (fill-region (point-min) (point-max))
       
   894                (buffer-substring-no-properties (+ 2 (point-min)) (point-max))))))))
       
   895     (goto-char old-point)))
       
   896 
       
   897 
       
   898 
       
   899 (defconst clojure-namespace-name-regex
       
   900   (rx line-start
       
   901       "("
       
   902       (zero-or-one (group (regexp "clojure.core/")))
       
   903       (zero-or-one (submatch "in-"))
       
   904       "ns"
       
   905       (zero-or-one "+")
       
   906       (one-or-more (any whitespace "\n"))
       
   907       (zero-or-more (or (submatch (zero-or-one "#")
       
   908                                   "^{"
       
   909                                   (zero-or-more (not (any "}")))
       
   910                                   "}")
       
   911                         (zero-or-more "^:"
       
   912                                       (one-or-more (not (any whitespace)))))
       
   913                     (one-or-more (any whitespace "\n")))
       
   914       ;; why is this here? oh (in-ns 'foo) or (ns+ :user)
       
   915       (zero-or-one (any ":'"))
       
   916       (group (one-or-more (not (any "()\"" whitespace))) word-end)))
       
   917 
       
   918 ;; for testing clojure-namespace-name-regex, you can evaluate this code and make
       
   919 ;; sure foo (or whatever the namespace name is) shows up in results. some of
       
   920 ;; these currently fail.
       
   921 ;; (mapcar (lambda (s) (let ((n (string-match clojure-namespace-name-regex s)))
       
   922 ;;                       (if n (match-string 4 s))))
       
   923 ;;         '("(ns foo)"
       
   924 ;;           "(ns
       
   925 ;; foo)"
       
   926 ;;           "(ns foo.baz)"
       
   927 ;;           "(ns ^:bar foo)"
       
   928 ;;           "(ns ^:bar ^:baz foo)"
       
   929 ;;           "(ns ^{:bar true} foo)"
       
   930 ;;           "(ns #^{:bar true} foo)"
       
   931 ;;           "(ns #^{:fail {}} foo)"
       
   932 ;;           "(ns ^{:fail2 {}} foo.baz)"
       
   933 ;;           "(ns ^{} foo)"
       
   934 ;;           "(ns ^{:skip-wiki true}
       
   935 ;;   aleph.netty
       
   936 ;; "
       
   937 ;;           "(ns
       
   938 ;;  foo)"
       
   939 ;;     "foo"))
       
   940 
       
   941 
       
   942 
       
   943 (defun clojure-insert-ns-form ()
       
   944   (interactive)
       
   945   (goto-char (point-min))
       
   946   (let* ((rel (car (last (split-string buffer-file-name "src/\\|test/"))))
       
   947          (relative (car (split-string rel "\\.clj")))
       
   948          (segments (split-string relative "/")))
       
   949     (insert (format "(ns %s)" (mapconcat #'identity segments ".")))))
       
   950 
       
   951 
       
   952 ;;; Slime help
       
   953 
       
   954 (defvar clojure-project-root-file "project.clj")
       
   955 
       
   956 ;; Pipe to $SHELL to work around mackosecks GUI Emacs $PATH issues.
       
   957 (defcustom clojure-swank-command
       
   958   (if (or (locate-file "lein" exec-path) (locate-file "lein.bat" exec-path))
       
   959       "lein jack-in %s"
       
   960     "echo \"lein jack-in %s\" | $SHELL -l")
       
   961   "The command used to start swank via clojure-jack-in.
       
   962 For remote swank it is lein must be in your PATH and the remote
       
   963 proc is launched via sh rather than bash, so it might be necessary
       
   964 to specific the full path to it. The argument is the port to connect on.
       
   965 Localhost is assumed."
       
   966   :type 'string
       
   967   :group 'clojure-mode)
       
   968 
       
   969 (defcustom clojure-generate-remote-swank-command-function
       
   970   'clojure-generate-remote-swank-command-ssh-tunnel
       
   971   "A function that is called to determine the swank command that
       
   972 `clojure-jack-in` will execute and the hostname/port that slime
       
   973 should connect to for remote projects that are opened via tramp.
       
   974 
       
   975 The arguments are dir, hostname, and port.  The return value should be an `alist` of the form
       
   976 (:cmd \"command string\" :hostname \"hostname\" :port 1234)"
       
   977   :type 'function
       
   978   :group 'clojure-mode)
       
   979 
       
   980 (defun clojure-generate-local-swank-command-default (dir hostname port)
       
   981   (if (not (string-equal "localhost" hostname))
       
   982       (error (concat
       
   983               "If you need to jack-in to remote projects/jvms over tramp, "
       
   984               "you need to define a custom `clojure-generate-swank-command-function`"))
       
   985     (list :cmd (format clojure-swank-command port)
       
   986           :hostname hostname
       
   987           :port port)))
       
   988 
       
   989 (defun clojure-generate-remote-swank-command-ssh-tunnel (dir hostname port)
       
   990   (destructuring-bind (_method user host localname)
       
   991       (append (tramp-dissect-file-name dir) nil)
       
   992     (list :cmd (format-spec
       
   993                 "ssh -L %p:localhost:%p -l '%u' '%h' 'cd \'%d\'; lein jack-in \'%p\''"
       
   994                 `((?p . ,port)
       
   995                   (?h . ,host)
       
   996                   (?u . ,(or user (getenv "USER")))
       
   997                   (?d . ,localname)))
       
   998           :hostname "localhost"
       
   999           :port port)))
       
  1000 
       
  1001 (defun clojure-generate-swank-cmd (dir hostname port)
       
  1002   (if (file-remote-p dir)
       
  1003       (if (functionp clojure-generate-remote-swank-command-function)
       
  1004           (funcall clojure-generate-remote-swank-command-function dir hostname port)
       
  1005         (error (concat
       
  1006                 "If you need to jack-in to remote projects/jvms over tramp "
       
  1007                 "you need to define a custom `clojure-generate-remote-swank-command-function`")))
       
  1008     (clojure-generate-local-swank-command-default dir hostname port)))
       
  1009 
       
  1010 (defun clojure-jack-in-sentinel (process event)
       
  1011   (let ((debug-on-error t))
       
  1012     (error "Could not start swank server: %s"
       
  1013            (let ((b (process-buffer process)))
       
  1014              (if (and b (buffer-live-p b))
       
  1015                  (with-current-buffer b
       
  1016                    (buffer-substring (point-min) (point-max))))))))
       
  1017 
       
  1018 (defun clojure-eval-bootstrap-region (process)
       
  1019   "Eval only the elisp in between the markers."
       
  1020   (with-current-buffer (process-buffer process)
       
  1021     (save-excursion
       
  1022       (goto-char 0)
       
  1023       (search-forward ";;; Bootstrapping bundled version of SLIME")
       
  1024       (let ((begin (point)))
       
  1025         (when (not (search-forward ";;; Done bootstrapping." nil t))
       
  1026           ;; fall back to possibly-ambiguous string if above isn't found
       
  1027           (search-forward "(run-hooks 'slime-load-hook)"))
       
  1028         (eval-region begin (point))))))
       
  1029 
       
  1030 (defun clojure-kill-swank-buffer (swank-buffer-name)
       
  1031   (when (get-buffer swank-buffer-name)
       
  1032     (let ((process (get-buffer-process (get-buffer swank-buffer-name))))
       
  1033       (if process
       
  1034           (set-process-query-on-exit-flag process nil))
       
  1035       (kill-buffer swank-buffer-name))))
       
  1036 
       
  1037 (defun clojure-generate-swank-connection-name (dir hostname)
       
  1038   "swank")
       
  1039 
       
  1040 (defun clojure-jack-in-start-process (swank-connection-name swank-buffer-name dir hostname)
       
  1041   ;; The buffer has to be created before the proc if
       
  1042   ;; `start-file-process-shell-command` is used. It doesn't hurt to do
       
  1043   ;; it now even if `start-process-shell-command` is used:
       
  1044   (get-buffer-create swank-buffer-name)
       
  1045 
       
  1046   (let ((port (- 65535 (mod (caddr (current-time)) 4096))))
       
  1047     (destructuring-bind (&key cmd hostname port)
       
  1048         (clojure-generate-swank-cmd dir hostname port)
       
  1049       (lexical-let* ((proc (start-process-shell-command
       
  1050                             ;; this command runs locally
       
  1051                             ;; `start-file-process-shell-command` would
       
  1052                             ;; run remote for tramp buffers
       
  1053                             swank-connection-name
       
  1054                             swank-buffer-name
       
  1055                             cmd))
       
  1056                      (dir dir)
       
  1057                      (hostname hostname)
       
  1058                      (port port)
       
  1059                      (connect-callback (lambda () (slime-connect hostname port))))
       
  1060         (set-process-sentinel proc 'clojure-jack-in-sentinel)
       
  1061         (set-process-query-on-exit-flag proc nil)
       
  1062         (set-process-filter proc
       
  1063                             (lambda (process output)
       
  1064                               (with-current-buffer (process-buffer process)
       
  1065                                 (insert output))
       
  1066                               (when (string-match "proceed to jack in" output)
       
  1067                                 (clojure-eval-bootstrap-region process)
       
  1068                                 (with-current-buffer
       
  1069                                     ;; this block is an attempt to avoid
       
  1070                                     ;; creating duplicate repl windows
       
  1071                                     (or
       
  1072                                      (get-buffer "*slime-repl clojure*")
       
  1073                                      (get-buffer "*slime-repl nil*")
       
  1074                                      (current-buffer))
       
  1075                                   (funcall connect-callback)
       
  1076                                   (when (string-match "slime-repl" (buffer-name))
       
  1077                                     ;; this is most likely an old repl
       
  1078                                     ;; buffer that existed prior to the
       
  1079                                     ;; jack-in call.
       
  1080                                     (setq default-directory dir)
       
  1081                                     (goto-char (point-max))))
       
  1082                                 (set-process-sentinel process nil)
       
  1083                                 (set-process-filter process nil))))))))
       
  1084 
       
  1085 ;;;###autoload
       
  1086 (defun clojure-jack-in ()
       
  1087   (interactive)
       
  1088   (setq slime-net-coding-system 'utf-8-unix)
       
  1089   (let* ((dir default-directory)
       
  1090          (hostname (if (file-remote-p default-directory)
       
  1091                        tramp-current-host "localhost"))
       
  1092          (connection-name (clojure-generate-swank-connection-name dir hostname))
       
  1093          (swank-buffer-name (format "*%s*" connection-name)))
       
  1094 
       
  1095     (when (and (functionp 'slime-disconnect)
       
  1096                (slime-current-connection)
       
  1097                ;; TODO: ask for permission once jack-in supports multiple connections
       
  1098                ;; (and (interactive-p) (y-or-n-p "Close old connections first? "))
       
  1099                )
       
  1100       (slime-disconnect))
       
  1101     (clojure-kill-swank-buffer swank-buffer-name)
       
  1102     (clojure-jack-in-start-process connection-name swank-buffer-name dir hostname))
       
  1103   (message "Starting swank server..."))
       
  1104 
       
  1105 (defun clojure-find-ns ()
       
  1106   (let ((regexp clojure-namespace-name-regex))
       
  1107     (save-excursion
       
  1108       (when (or (re-search-backward regexp nil t)
       
  1109                 (re-search-forward regexp nil t))
       
  1110         (match-string-no-properties 4)))))
       
  1111 
       
  1112 (defalias 'clojure-find-package 'clojure-find-ns)
       
  1113 
       
  1114 (defun clojure-enable-slime ()
       
  1115   (slime-mode t)
       
  1116   (set (make-local-variable 'slime-find-buffer-package-function)
       
  1117        'clojure-find-ns))
       
  1118 
       
  1119 ;;;###autoload
       
  1120 (defun clojure-enable-slime-on-existing-buffers ()
       
  1121   (interactive)
       
  1122   (add-hook 'clojure-mode-hook 'clojure-enable-slime)
       
  1123   (save-window-excursion
       
  1124     (dolist (buffer (buffer-list))
       
  1125       (with-current-buffer buffer
       
  1126         (when (eq major-mode 'clojure-mode)
       
  1127           (clojure-enable-slime))))))
       
  1128 
       
  1129 ;; Test navigation:
       
  1130 (defun clojure-in-tests-p ()
       
  1131   (or (string-match-p "test\." (clojure-find-ns))
       
  1132       (string-match-p "/test" (buffer-file-name))))
       
  1133 
       
  1134 (defun clojure-underscores-for-hyphens (namespace)
       
  1135   (replace-regexp-in-string "-" "_" namespace))
       
  1136 
       
  1137 (defun clojure-test-for (namespace)
       
  1138   (let* ((namespace (clojure-underscores-for-hyphens namespace))
       
  1139          (segments (split-string namespace "\\."))
       
  1140          (before (subseq segments 0 clojure-test-ns-segment-position))
       
  1141          (after (subseq segments clojure-test-ns-segment-position))
       
  1142          (test-segments (append before (list "test") after)))
       
  1143     (mapconcat 'identity test-segments "/")))
       
  1144 
       
  1145 (defun clojure-jump-to-test ()
       
  1146   "Jump from implementation file to test."
       
  1147   (interactive)
       
  1148   (find-file (format "%stest/%s.clj"
       
  1149                      (file-name-as-directory
       
  1150                       (locate-dominating-file buffer-file-name "src/"))
       
  1151                      (clojure-test-for (clojure-find-ns)))))
       
  1152 
       
  1153 (defun clojure-jump-between-tests-and-code ()
       
  1154   (interactive)
       
  1155   (if (clojure-in-tests-p)
       
  1156       (clojure-test-jump-to-implementation)
       
  1157     (clojure-jump-to-test)))
       
  1158 
       
  1159 ;;; slime filename translation for tramp
       
  1160 (defun clojure-slime-tramp-local-filename (f)
       
  1161   (if (file-remote-p f)
       
  1162       (tramp-file-name-localname
       
  1163        (tramp-dissect-file-name f))
       
  1164     f))
       
  1165 
       
  1166 (defun clojure-slime-tramp-remote-filename (f)
       
  1167   (if (file-remote-p default-directory)
       
  1168       (tramp-make-tramp-file-name
       
  1169        (tramp-file-name-method
       
  1170         (tramp-dissect-file-name default-directory))
       
  1171        (tramp-file-name-user
       
  1172         (tramp-dissect-file-name default-directory))
       
  1173        (tramp-file-name-host
       
  1174         (tramp-dissect-file-name default-directory))
       
  1175        f)
       
  1176     f))
       
  1177 
       
  1178 (defun clojure-slime-remote-file-name-hook ()
       
  1179   (setq slime-from-lisp-filename-function
       
  1180         'clojure-slime-tramp-remote-filename)
       
  1181   (setq slime-to-lisp-filename-function
       
  1182         'clojure-slime-tramp-local-filename))
       
  1183 
       
  1184 (add-hook 'slime-connected-hook 'clojure-slime-remote-file-name-hook)
       
  1185 
       
  1186 ;;;###autoload
       
  1187 (add-hook 'slime-connected-hook 'clojure-enable-slime-on-existing-buffers)
       
  1188 
       
  1189 (add-hook 'slime-indentation-update-hooks 'put-clojure-indent)
       
  1190 
       
  1191 
       
  1192 
       
  1193 ;;;###autoload
       
  1194 (add-to-list 'auto-mode-alist '("\\.clj$" . clojure-mode))
       
  1195 (add-to-list 'interpreter-mode-alist '("jark" . clojure-mode))
       
  1196 (add-to-list 'interpreter-mode-alist '("cake" . clojure-mode))
       
  1197 
       
  1198 (provide 'clojure-mode)
       
  1199 ;;; clojure-mode.el ends here