Browse files

Add some demo files.

  • Loading branch information...
1 parent 40884bb commit e865f3835fe2c093539487c0a474170c1ca6b253 @technomancy committed Jul 2, 2009
Showing with 6,316 additions and 0 deletions.
  1. +707 −0 clojure-mode.el
  2. +19 −0 concourse.js
  3. +3,394 −0 espresso.el
  4. +2,196 −0 paredit.el
View
707 clojure-mode.el
@@ -0,0 +1,707 @@
+;;; clojure-mode.el --- Major mode for Clojure code
+
+;; Copyright (C) 2007, 2008, 2009 Jeffrey Chu and Lennart Staflin
+;;
+;; Authors: Jeffrey Chu <jochu0@gmail.com>
+;; Lennart Staflin <lenst@lysator.liu.se>
+;; Phil Hagelberg <technomancy@gmail.com>
+;; URL: http://www.emacswiki.org/cgi-bin/wiki/ClojureMode
+;; Version: 1.1
+;; Keywords: languages, lisp
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; Provides font-lock, indentation, and functions for communication
+;; with subprocesses for the Clojure language. (http://clojure.org)
+
+;;; Installation:
+
+;; If you use ELPA, you can install via the M-x package-list-packages
+;; interface. This is preferrable as you will have access to updates
+;; automatically.
+
+;; If you need to install by hand for some reason:
+
+;; (0) Add this file to your load-path, usually the ~/.emacs.d directory.
+;; (1) Either:
+;; Add these lines to your .emacs:
+;; (autoload 'clojure-mode "clojure-mode" "A major mode for Clojure" t)
+;; (add-to-list 'auto-mode-alist '("\\.clj$" . clojure-mode))
+;; Or generate autoloads with the `update-directory-autoloads' function.
+
+;; Paredit users:
+
+;; Download paredit v21 or greater
+;; http://mumble.net/~campbell/emacs/paredit.el
+
+;; Use paredit as you normally would with any other mode; for instance:
+;;
+;; ;; require or autoload paredit-mode
+;; (defun lisp-enable-paredit-hook () (paredit-mode 1))
+;; (add-hook 'clojure-mode-hook 'lisp-enable-paredit-hook)
+
+;; The clojure-install function can check out and configure all the
+;; dependencies get going with Clojure, including SLIME integration.
+;; To use this function, you may have to manually load clojure-mode.el
+;; using M-x load-file or M-x eval-buffer.
+
+;;; Todo:
+
+;; * installer doesn't work when git port is blocked
+;; * hashbang is also a valid comment character
+;; * do the inferior-lisp functions work without SLIME? needs documentation
+
+;;; 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:
+
+(require 'cl)
+
+(defgroup clojure-mode nil
+ "A mode for Clojure"
+ :prefix "clojure-mode-"
+ :group 'applications)
+
+(defcustom clojure-mode-font-lock-multiline-def t
+ "Set to non-nil in order to enable font-lock of
+multi-line (def...) forms. Changing this will require a
+restart (ie. M-x clojure-mode) of existing clojure mode buffers."
+ :type 'boolean
+ :group 'clojure-mode)
+
+(defcustom clojure-mode-font-lock-comment-sexp nil
+ "Set to non-nil in order to enable font-lock of (comment...)
+forms. This option is experimental. Changing this will require a
+restart (ie. M-x clojure-mode) of existing clojure mode buffers."
+ :type 'boolean
+ :group 'clojure-mode)
+
+(defcustom clojure-mode-load-command "(clojure.core/load-file \"%s\")\n"
+ "*Format-string for building a Clojure expression to load a file.
+This format string should use `%s' to substitute a file name
+and should result in a Clojure expression that will command the inferior Clojure
+to load that file."
+ :type 'string
+ :group 'clojure-mode)
+
+(defcustom clojure-mode-use-backtracking-indent nil
+ "Set to non-nil to enable backtracking/context sensitive
+indentation."
+ :type 'boolean
+ :group 'clojure-mode)
+
+(defcustom clojure-max-backtracking 3
+ "Maximum amount to backtrack up a list to check for context."
+ :type 'integer
+ :group 'clojure-mode)
+
+(defcustom clojure-src-root (expand-file-name "~/src")
+ "Directory that contains checkouts for clojure, clojure-contrib,
+slime, and swank-clojure. This value is used by `clojure-install'
+and `clojure-slime-config'."
+ :type 'string
+ :group 'clojure-mode)
+
+(defvar clojure-last-known-good-revisions
+ '(("clojure" . "origin/1.0")
+ ("clojure-contrib" . "66fc2f90afa4649675d115e611528f78e5ac0016")
+ ("swank-clojure" . "e2ec46fdd6533e093e26c4a0694cac4f29ca1d53")
+ ("slime" . "a4a75da81bbf44f51e5e7e9ba795857c95f07a4b"))
+ "Latest revision known to work with Slime.")
+
+(defvar clojure-mode-map
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map lisp-mode-shared-map)
+ (define-key map "\e\C-x" 'lisp-eval-defun)
+ (define-key map "\C-x\C-e" 'lisp-eval-last-sexp)
+ (define-key map "\C-c\C-e" 'lisp-eval-last-sexp)
+ (define-key map "\C-c\C-l" 'clojure-load-file)
+ (define-key map "\C-c\C-r" 'lisp-eval-region)
+ (define-key map "\C-c\C-z" 'run-lisp)
+ map)
+ "Keymap for ordinary Clojure mode.
+All commands in `lisp-mode-shared-map' are inherited by this map.")
+
+
+(easy-menu-define clojure-menu clojure-mode-map "Menu used in `clojure-mode'."
+ '("Clojure"
+ ["Eval defun" lisp-eval-defun t]
+ ["Eval defun and go" lisp-eval-defun-and-go t]
+ ["Eval last sexp" lisp-eval-last-sexp t]
+ ["Eval region" lisp-eval-region t]
+ ["Eval region and go" lisp-eval-region-and-go t]
+ ["Load file..." clojure-load-file t]
+ ["Run Lisp" run-lisp t]))
+
+
+(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 ?\[ "(]" table)
+ (modify-syntax-entry ?\] ")[" table)
+ (modify-syntax-entry ?^ "'" table)
+ table))
+
+(defvar clojure-mode-abbrev-table nil
+ "Abbrev table used in clojure-mode buffers.")
+
+(define-abbrev-table 'clojure-mode-abbrev-table ())
+
+(defvar clojure-prev-l/c-dir/file nil
+ "Record last directory and file used in loading or compiling.
+This holds a cons cell of the form `(DIRECTORY . FILE)'
+describing the last `clojure-load-file' or `clojure-compile-file' command.")
+
+(defvar clojure-def-regexp "^\\s *\\((def\\S *\\s +\\(\[^ \n\t\]+\\)\\)"
+ "A regular expression to match any top-level definitions.")
+
+;;;###autoload
+(defun clojure-mode ()
+ "Major mode for editing Clojure code - similar to Lisp mode..
+Commands:
+Delete converts tabs to spaces as it moves back.
+Blank lines separate paragraphs. Semicolons start comments.
+\\{clojure-mode-map}
+Note that `run-lisp' may be used either to start an inferior Lisp job
+or to switch back to an existing one.
+
+Entry to this mode calls the value of `clojure-mode-hook'
+if that value is non-nil."
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map clojure-mode-map)
+ (setq major-mode 'clojure-mode)
+ (setq mode-name "Clojure")
+ (lisp-mode-variables nil)
+ (set-syntax-table clojure-mode-syntax-table)
+
+ (setq local-abbrev-table clojure-mode-abbrev-table)
+
+ (set (make-local-variable 'comment-start-skip)
+ "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *")
+ (set (make-local-variable 'lisp-indent-function)
+ 'clojure-indent-function)
+ (set (make-local-variable 'font-lock-multiline) t)
+
+ (setq lisp-imenu-generic-expression
+ `((nil ,clojure-def-regexp 2)))
+ (setq imenu-create-index-function
+ (lambda ()
+ (imenu--generic-function lisp-imenu-generic-expression)))
+
+ (if (and (not (boundp 'font-lock-extend-region-functions))
+ (or clojure-mode-font-lock-multiline-def
+ clojure-mode-font-lock-comment-sexp))
+ (message "Clojure mode font lock extras are unavailable, please upgrade to atleast version 22 ")
+
+ (when clojure-mode-font-lock-multiline-def
+ (add-to-list 'font-lock-extend-region-functions 'clojure-font-lock-extend-region-def t))
+
+ (when clojure-mode-font-lock-comment-sexp
+ (add-to-list 'font-lock-extend-region-functions 'clojure-font-lock-extend-region-comment t)
+ (make-local-variable 'clojure-font-lock-keywords)
+ (add-to-list 'clojure-font-lock-keywords 'clojure-font-lock-mark-comment t)
+ (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil)))
+
+ (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 . lisp-font-lock-syntactic-face-function)))
+
+ (if (fboundp 'run-mode-hooks)
+ (run-mode-hooks 'clojure-mode-hook)
+ (run-hooks 'clojure-mode-hook))
+
+ ;; Enable curly braces when paredit is enabled in clojure-mode-hook
+ (when (and (featurep 'paredit) paredit-mode (>= paredit-version 21))
+ (define-key clojure-mode-map "{" 'paredit-open-curly)
+ (define-key clojure-mode-map "}" 'paredit-close-curly)))
+
+;; (define-key clojure-mode-map "{" 'self-insert-command)
+;; (define-key clojure-mode-map "}" 'self-insert-command)
+
+(defun clojure-font-lock-def-at-point (point)
+ "Find the position range between the top-most def* and the
+fourth element afterwards. Note that this means there's no
+gaurantee 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 (i 4)
+ (forward-sexp)))
+ (error nil))
+ (cons beg-def (point)))))
+
+(defun clojure-font-lock-extend-region-def ()
+ "Move fontification boundaries to always include the first four
+elements of a def* forms."
+ (let ((changed nil))
+ (let ((def (clojure-font-lock-def-at-point font-lock-beg)))
+ (when def
+ (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
+ (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-lock-extend-region-comment ()
+ "Move fontification boundaries to always contain
+ entire (comment ..) sexp. Does not work if you have a
+ white-space between ( and comment, but that is omitted to make
+ this run faster."
+ (let ((changed nil))
+ (goto-char font-lock-beg)
+ (condition-case nil (beginning-of-defun) (error nil))
+ (let ((pos (re-search-forward "(comment\\>" font-lock-end t)))
+ (when pos
+ (forward-char -8)
+ (when (< (point) font-lock-beg)
+ (setq font-lock-beg (point)
+ changed t))
+ (condition-case nil (forward-sexp) (error nil))
+ (when (> (point) font-lock-end)
+ (setq font-lock-end (point)
+ changed t))))
+ changed))
+
+
+(defun clojure-font-lock-mark-comment (limit)
+ "Marks all (comment ..) forms with font-lock-comment-face."
+ (let (pos)
+ (while (and (< (point) limit)
+ (setq pos (re-search-forward "(comment\\>" limit t)))
+ (when pos
+ (forward-char -8)
+ (condition-case nil
+ (add-text-properties (1+ (point)) (progn (forward-sexp) (1- (point)))
+ '(face font-lock-comment-face multiline t))
+ (error (forward-char 8))))))
+ nil)
+
+(defconst clojure-font-lock-keywords
+ (eval-when-compile
+ `( ;; Definitions.
+ (,(concat "(\\(?:clojure.core/\\)?\\(def"
+ ;; Function declarations.
+ "\\(n-?\\|multi\\|macro\\|method\\|test\\|"
+ ;; Variable declarations.
+ "struct\\|once\\|"
+ "\\)\\)\\>"
+ ;; Any whitespace
+ "[ \r\n\t]*"
+ ;; Possibly type or metadata
+ "\\(?:#^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)?"
+
+ "\\(\\sw+\\)?")
+ (1 font-lock-keyword-face)
+ (3 font-lock-function-name-face nil t))
+ ;; Control structures
+ (,(concat
+ "(\\(?:clojure.core/\\)?"
+ (regexp-opt
+ '("let" "letfn" "do"
+ "cond" "condp"
+ "for" "loop" "recur"
+ "when" "when-not" "when-let" "when-first"
+ "if" "if-let" "if-not"
+ "." ".." "->" "doto"
+ "and" "or"
+ "dosync" "doseq" "dotimes" "dorun" "doall"
+ "load" "import" "unimport" "ns" "in-ns" "refer"
+ "try" "catch" "finally" "throw"
+ "with-open" "with-local-vars" "binding"
+ "gen-class" "gen-and-load-class" "gen-and-save-class") t)
+ "\\>")
+ . 1)
+ ;; Built-ins
+ (,(concat
+ "(\\(?:clojure.core/\\)?"
+ (regexp-opt
+ '(
+ "implement" "proxy" "lazy-cons" "with-meta"
+ "struct" "struct-map" "delay" "locking" "sync" "time" "apply"
+ "remove" "merge" "interleave" "interpose" "distinct" "for"
+ "cons" "concat" "lazy-cat" "cycle" "rest" "frest" "drop" "drop-while"
+ "nthrest" "take" "take-while" "take-nth" "butlast" "drop-last"
+ "reverse" "sort" "sort-by" "split-at" "partition" "split-with"
+ "first" "ffirst" "rfirst" "when-first" "zipmap" "into" "set" "vec" "into-array"
+ "to-array-2d" "not-empty" "seq?" "not-every?" "every?" "not-any?" "empty?"
+ "map" "mapcat" "vector?" "list?" "hash-map" "reduce" "filter"
+ "vals" "keys" "rseq" "subseq" "rsubseq" "count"
+ "fnseq" "lazy-cons" "repeatedly" "iterate"
+ "repeat" "replicate" "range"
+ "line-seq" "resultset-seq" "re-seq" "re-find" "tree-seq" "file-seq" "xml-seq"
+ "iterator-seq" "enumeration-seq"
+ "symbol?" "string?" "vector" "conj" "str"
+ "pos?" "neg?" "zero?" "nil?" "inc" "format"
+ "alter" "commute" "ref-set" "floor" "assoc" "send" "send-off" ) t)
+ "\\>")
+ 1 font-lock-builtin-face)
+ ;; (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))
+ ;; Constant values.
+ ("\\<:\\sw+\\>" 0 font-lock-builtin-face)
+ ;; Meta type annotation #^Type
+ ("#^\\sw+" 0 font-lock-type-face)
+ ("\\<io\\!\\>" 0 font-lock-warning-face)))
+ "Default expressions to highlight in Clojure mode.")
+
+
+(defun clojure-load-file (file-name)
+ "Load a Lisp file into the inferior Lisp process."
+ (interactive (comint-get-source "Load Clojure file: " clojure-prev-l/c-dir/file
+ '(clojure-mode) t))
+ (comint-check-source file-name) ; Check to see if buffer needs saved.
+ (setq clojure-prev-l/c-dir/file (cons (file-name-directory file-name)
+ (file-name-nondirectory file-name)))
+ (comint-send-string (inferior-lisp-proc)
+ (format clojure-mode-load-command file-name))
+ (switch-to-lisp t))
+
+
+
+(defun clojure-indent-function (indent-point state)
+ "This function is the normal value of the variable `lisp-indent-function'.
+It is used when indenting a line within a function call, to see if the
+called function says anything special about how to indent the line.
+
+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 Lisp function
+which has a non-nil property `lisp-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.
+
+This function also returns nil meaning don't specify the indentation."
+ (let ((normal-indent (current-column)))
+ (goto-char (1+ (elt state 1)))
+ (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
+ (if (and (elt state 2)
+ (not (looking-at "\\sw\\|\\s_")))
+ ;; car of form doesn't seem to be a symbol
+ (progn
+ (if (not (> (save-excursion (forward-line 1) (point))
+ calculate-lisp-indent-last-sexp))
+ (progn (goto-char calculate-lisp-indent-last-sexp)
+ (beginning-of-line)
+ (parse-partial-sexp (point)
+ calculate-lisp-indent-last-sexp 0 t)))
+ ;; Indent under the list or under the first sexp on the same
+ ;; line as calculate-lisp-indent-last-sexp. Note that first
+ ;; thing on that line has to be complete sexp since we are
+ ;; inside the innermost containing sexp.
+ (backward-prefix-chars)
+ (if (and (eq (char-after (point)) ?\[)
+ (eq (char-after (elt state 1)) ?\())
+ (+ (current-column) 2) ;; this is probably inside a defn
+ (current-column)))
+ (let ((function (buffer-substring (point)
+ (progn (forward-sexp 1) (point))))
+ (open-paren (elt state 1))
+ method)
+ (setq method (get (intern-soft function) 'clojure-indent-function))
+
+ (cond ((member (char-after open-paren) '(?\[ ?\{))
+ (goto-char open-paren)
+ (1+ (current-column)))
+ ((or (eq method 'defun)
+ (and (null method)
+ (> (length function) 3)
+ (string-match "\\`\\(?:\\S +/\\)?def\\|with-" function)))
+ (lisp-indent-defform state indent-point))
+
+ ((integerp method)
+ (lisp-indent-specform method state
+ indent-point normal-indent))
+ (method
+ (funcall method indent-point state))
+ (clojure-mode-use-backtracking-indent
+ (clojure-backtracking-indent indent-point state normal-indent)))))))
+
+(defun clojure-backtracking-indent (indent-point state normal-indent)
+ "Experimental backtracking support. Will upwards in an sexp to
+check for contextual indenting."
+ (let (indent (path) (depth 0))
+ (goto-char (elt state 1))
+ (while (and (not indent)
+ (< depth clojure-max-backtracking))
+ (let ((containing-sexp (point)))
+ (parse-partial-sexp (1+ containing-sexp) indent-point 1 t)
+ (when (looking-at "\\sw\\|\\s_")
+ (let* ((start (point))
+ (fn (buffer-substring start (progn (forward-sexp 1) (point))))
+ (meth (get (intern-soft fn) 'clojure-backtracking-indent)))
+ (let ((n 0))
+ (when (< (point) indent-point)
+ (condition-case ()
+ (progn
+ (forward-sexp 1)
+ (while (< (point) indent-point)
+ (parse-partial-sexp (point) indent-point 1 t)
+ (incf n)
+ (forward-sexp 1)))
+ (error nil)))
+ (push n path))
+ (when meth
+ (let ((def meth))
+ (dolist (p path)
+ (if (and (listp def)
+ (< p (length def)))
+ (setq def (nth p def))
+ (if (listp def)
+ (setq def (car (last def)))
+ (setq def nil))))
+ (goto-char (elt state 1))
+ (when def
+ (setq indent (+ (current-column) def)))))))
+ (goto-char containing-sexp)
+ (condition-case ()
+ (progn
+ (backward-up-list 1)
+ (incf depth))
+ (error (setq depth clojure-max-backtracking)))))
+ indent))
+
+;; (defun clojure-indent-defn (indent-point state)
+;; "Indent by 2 if after a [] clause that's at the beginning of a
+;; line"
+;; (if (not (eq (char-after (elt state 2)) ?\[))
+;; (lisp-indent-defform state indent-point)
+;; (goto-char (elt state 2))
+;; (beginning-of-line)
+;; (skip-syntax-forward " ")
+;; (if (= (point) (elt state 2))
+;; (+ (current-column) 2)
+;; (lisp-indent-defform state indent-point))))
+
+;; (put 'defn 'clojure-indent-function 'clojure-indent-defn)
+;; (put 'defmacro 'clojure-indent-function 'clojure-indent-defn)
+
+;; clojure backtracking indent is experimental and the format for these
+
+;; entries are subject to change
+(put 'implement 'clojure-backtracking-indent '(4 (2)))
+(put 'proxy 'clojure-backtracking-indent '(4 4 (2)))
+
+
+(defun put-clojure-indent (sym indent)
+ (put sym 'clojure-indent-function indent)
+ (put (intern (format "clojure/%s" (symbol-name sym))) 'clojure-indent-function indent))
+
+(defmacro define-clojure-indent (&rest kvs)
+ `(progn
+ ,@(mapcar (lambda (x) `(put-clojure-indent (quote ,(first x)) ,(second x))) kvs)))
+
+(define-clojure-indent
+ (catch 2)
+ (defmuti 1)
+ (do 0)
+ (for 1)
+ (if 1)
+ (if-not 1)
+ (let 1)
+ (letfn 1)
+ (loop 1)
+ (struct-map 1)
+ (assoc 1)
+ (condp 2)
+
+ (fn 'defun))
+
+;; built-ins
+(define-clojure-indent
+ (ns 1)
+ (binding 1)
+ (comment 0)
+ (defstruct 1)
+ (doseq 1)
+ (dotimes 1)
+ (doto 1)
+ (implement 1)
+ (let 1)
+ (when-let 1)
+ (if-let 1)
+ (locking 1)
+ (proxy 2)
+ (sync 1)
+ (when 1)
+ (when-first 1)
+ (when-let 1)
+ (when-not 1)
+ (with-local-vars 1)
+ (with-open 1)
+ (with-precision 1))
+
+;;; SLIME integration
+
+;;;###autoload
+(progn
+ ;; We want this function to be able to be loaded without loading the
+ ;; whole of clojure-mode.el since it runs at every startup.
+ (defun clojure-slime-config (&optional src-root)
+ "Load Clojure SLIME support out of the `clojure-src-root' directory.
+
+Since there's no single conventional place to keep Clojure, this
+is bundled up as a function so that you can call it after you've set
+`clojure-src-root' in your personal config."
+
+ (if src-root (setq clojure-src-root src-root))
+
+ (add-to-list 'load-path (concat clojure-src-root "/slime"))
+ (add-to-list 'load-path (concat clojure-src-root "/slime/contrib"))
+ (add-to-list 'load-path (concat clojure-src-root "/swank-clojure"))
+
+ (require 'slime-autoloads)
+ (require 'swank-clojure-autoload)
+
+ (slime-setup '(slime-fancy))
+
+ (setq swank-clojure-jar-path (concat clojure-src-root "/clojure/clojure.jar"))
+ (unless (boundp 'swank-clojure-extra-classpaths)
+ (setq swank-clojure-extra-classpaths nil))
+ (add-to-list 'swank-clojure-extra-classpaths
+ (concat clojure-src-root "/clojure-contrib/src/"))))
+
+;;;###autoload
+(defun clojure-install (src-root)
+ "Perform the initial Clojure install along with Emacs support libs.
+
+This requires git, a JVM, ant, and an active Internet connection."
+ (interactive (list
+ (read-string (concat "Install Clojure in (default: "
+ clojure-src-root "): ")
+ nil nil clojure-src-root)))
+
+ (let ((orig-directory default-directory))
+ (make-directory src-root t)
+ (cd src-root)
+
+ (if (file-exists-p (concat src-root "/clojure"))
+ (error "Clojure is already installed at %s/clojure" src-root))
+
+ (message "Checking out source... this will take a while...")
+ (dolist (cmd '("git clone git://github.com/richhickey/clojure.git"
+ "git clone git://github.com/richhickey/clojure-contrib.git"
+ "git clone git://github.com/jochu/swank-clojure.git"
+ "git clone --depth 2 git://github.com/nablaone/slime.git"))
+ (unless (= 0 (shell-command cmd))
+ (error "Clojure installation step failed: %s" cmd)))
+
+ (dolist (repo clojure-last-known-good-revisions)
+ (cd (first repo))
+ (shell-command (format "git checkout %s" (second repo))))
+
+ (message "Compiling...")
+ (cd (concat src-root "/clojure"))
+ (unless (= 0 (shell-command "ant"))
+ (error "Couldn't compile Clojure."))
+
+ (with-output-to-temp-buffer "clojure-install-note"
+ (princ
+ (if (equal src-root clojure-src-root)
+ "Add a call to \"\(clojure-slime-config\)\"
+to your .emacs so you can use SLIME in future sessions."
+ (setq clojure-src-root src-root)
+ (format "You've installed clojure in a non-default location. If you want
+to use this installation in the future, you will need to add the following
+lines to your personal Emacs config somewhere:
+
+\(clojure-slime-config \"%s\"\)" src-root)))
+ (princ "\n\n Press M-x slime to launch Clojure."))
+
+ (clojure-slime-config)
+ (cd orig-directory)))
+
+(defun clojure-update ()
+ "Update clojure-related repositories and recompile clojure.
+
+Works with clojure etc. installed via `clojure-install'. Code
+should be checked out in the `clojure-src-root' directory."
+ (interactive)
+
+ (message "Updating...")
+ (let ((orig-directory default-directory))
+ (dolist (repo '("clojure" "clojure-contrib" "swank-clojure" "slime"))
+ (cd (concat clojure-src-root "/" repo))
+ (unless (= 0 (shell-command "git pull origin master"))
+ (error "Clojure update failed: %s" repo)))
+
+ (message "Compiling...")
+ (save-window-excursion
+ (cd clojure-src-root)
+ (unless (= 0 (shell-command "ant"))
+ (error "Couldn't compile Clojure.")))
+ (message "Finished updating Clojure.")
+ (cd orig-directory)))
+
+(defun clojure-enable-slime-on-existing-buffers ()
+ (interactive)
+ (add-hook 'clojure-mode-hook 'swank-clojure-slime-mode-hook)
+ (dolist (buffer (buffer-list))
+ (with-current-buffer buffer
+ (if (equal major-mode 'clojure-mode)
+ (swank-clojure-slime-mode-hook)))))
+
+(add-hook 'slime-connected-hook 'clojure-enable-slime-on-existing-buffers)
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.clj$" . clojure-mode))
+
+(provide 'clojure-mode)
+;;; clojure-mode.el ends here
View
19 concourse.js
@@ -0,0 +1,19 @@
+// concourse.js - functions needed across the application
+
+var dbname = "concourse"; // TODO: get dynamically?
+
+var url = function() {
+ // Wow, Javascript is horrifyingly bad. Somehow this keeps surprising me.
+ var parts = Array.prototype.slice.call(arguments);
+ return "/" + [dbname].concat(parts).join("/");
+};
+
+var current_user = function() {
+ return "technomancy@gmail.com";
+};
+
+var insert_description = function(name, description, length) {
+ $("h1#name").html(name);
+ $("#gathering-description p:first").html(description);
+ $("#length").html("<b>Length</b>: " + length + " hours");
+};
View
3,394 espresso.el
3,394 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
2,196 paredit.el
@@ -0,0 +1,2196 @@
+;;; -*- Mode: Emacs-Lisp; outline-regexp: " \n;;;;+" -*-
+
+;;;;;; Paredit: Parenthesis-Editing Minor Mode
+;;;;;; Version 22 (beta)
+
+;;; NOTE: THIS IS A BETA VERSION OF PAREDIT. USE AT YOUR OWN RISK.
+;;; THIS FILE IS SUBJECT TO CHANGE, AND NOT SUITABLE FOR DISTRIBUTION
+;;; BY PACKAGE MANAGERS SUCH AS APT, PKGSRC, MACPORTS, &C.
+
+;;; Copyright (c) 2008, 2009, Taylor R. Campbell
+;;;
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+;;;
+;;; * Redistributions of source code must retain the above copyright
+;;; notice, this list of conditions and the following disclaimer.
+;;;
+;;; * Redistributions in binary form must reproduce the above copyright
+;;; notice, this list of conditions and the following disclaimer in
+;;; the documentation and/or other materials provided with the
+;;; distribution.
+;;;
+;;; * Neither the names of the authors nor the names of contributors
+;;; may be used to endorse or promote products derived from this
+;;; software without specific prior written permission.
+;;;
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+;;; This file is permanently stored at
+;;; <http://mumble.net/~campbell/emacs/paredit-22.el>.
+;;;
+;;; The currently released version of paredit is available at
+;;; <http://mumble.net/~campbell/emacs/paredit.el>.
+;;;
+;;; The latest beta version of paredit is available at
+;;; <http://mumble.net/~campbell/emacs/paredit-beta.el>.
+;;;
+;;; Release notes are available at
+;;; <http://mumble.net/~campbell/emacs/paredit.release>.
+
+;;; Install paredit by placing `paredit.el' in `/path/to/elisp', a
+;;; directory of your choice, and adding to your .emacs file:
+;;;
+;;; (add-to-list 'load-path "/path/to/elisp")
+;;; (autoload 'paredit-mode "paredit"
+;;; "Minor mode for pseudo-structurally editing Lisp code."
+;;; t)
+;;;
+;;; Toggle Paredit Mode with `M-x paredit-mode RET', or enable it
+;;; always in a major mode `M' (e.g., `lisp' or `scheme') with:
+;;;
+;;; (add-hook M-mode-hook (lambda () (paredit-mode +1)))
+;;;
+;;; Customize paredit using `eval-after-load':
+;;;
+;;; (eval-after-load 'paredit
+;;; '(progn ...redefine keys, &c....))
+;;;
+;;; Paredit should run in GNU Emacs 21 or later and XEmacs 21.5 or
+;;; later. Paredit is highly unlikely to work in earlier versions of
+;;; GNU Emacs, and it may have obscure problems in earlier versions of
+;;; XEmacs due to the way its syntax parser reports conditions, as a
+;;; result of which the code that uses the syntax parser must mask all
+;;; error conditions, not just those generated by the syntax parser.
+;;;
+;;; Questions, bug reports, comments, feature suggestions, &c., may be
+;;; addressed via email to the author's surname at mumble.net or via
+;;; IRC to the user named Riastradh on irc.freenode.net in the #paredit
+;;; channel.
+;;;
+;;; Please contact the author rather than forking your own versions, to
+;;; prevent the dissemination of random variants floating about the
+;;; internet unbeknownst to the author. Laziness is not an excuse:
+;;; your laziness costs me confusion and time trying to support
+;;; paredit, so if you fork paredit, you make the world a worse place.
+;;;
+;;; *** WARNING *** IMPORTANT *** DO NOT SUBMIT BUGS BEFORE READING ***
+;;;
+;;; If you plan to submit a bug report, where some sequence of keys in
+;;; Paredit Mode, or some sequence of paredit commands, doesn't do what
+;;; you wanted, then it is helpful to isolate an example in a very
+;;; small buffer, and it is **ABSOLUTELY**ESSENTIAL** that you supply,
+;;; along with the sequence of keys or commands,
+;;;
+;;; (1) the version of Emacs,
+;;; (2) the version of paredit.el[*], and
+;;; (3) the **COMPLETE** state of the buffer used to reproduce the
+;;; problem, including major mode, minor modes, local key
+;;; bindings, entire contents of the buffer, leading line breaks
+;;; or spaces, &c.
+;;;
+;;; It is often extremely difficult to reproduce problems, especially
+;;; with commands like `paredit-kill'. If you do not supply **ALL** of
+;;; this information, then it is highly probable that I cannot
+;;; reproduce your problem no matter how hard I try, and the effect of
+;;; submitting a bug without this information is only to waste your
+;;; time and mine. So, please, include all of the above information.
+;;;
+;;; [*] If you are using a beta version of paredit, be sure that you
+;;; are using the *latest* edition of the beta version, available
+;;; at <http://mumble.net/~campbell/emacs/paredit-beta.el>. If you
+;;; are not using a beta version, then upgrade either to that or to
+;;; the latest release version; I cannot support older versions,
+;;; and I can't fathom any reason why you might be using them. So
+;;; the answer to item (2) should be either `release' or `beta'.
+
+;;; The paredit minor mode, Paredit Mode, binds a number of simple
+;;; keys, notably `(', `)', `"', and `\', to commands that more
+;;; carefully insert S-expression structures in the buffer. The
+;;; parenthesis delimiter keys (round or square) are defined to insert
+;;; parenthesis pairs and move past the closing delimiter,
+;;; respectively; the double-quote key is multiplexed to do both, and
+;;; also to insert an escape if within a string; and backslashes prompt
+;;; the user for the next character to input, because a lone backslash
+;;; can break structure inadvertently. These all have their ordinary
+;;; behaviour when inside comments, and, outside comments, if truly
+;;; necessary, you can insert them literally with `C-q'.
+;;;
+;;; The key bindings are designed so that when typing new code in
+;;; Paredit Mode, you can generally use exactly the same keystrokes as
+;;; you would have used without Paredit Mode. Earlier versions of
+;;; paredit.el did not conform to this, because Paredit Mode bound `)'
+;;; to a command that would insert a newline. Now `)' is bound to a
+;;; command that does not insert a newline, and `M-)' is bound to the
+;;; command that inserts a newline. To revert to the former behaviour,
+;;; add the following forms to an `eval-after-load' form for paredit.el
+;;; in your .emacs file:
+;;;
+;;; (define-key paredit-mode-map (kbd ")")
+;;; 'paredit-close-round-and-newline)
+;;; (define-key paredit-mode-map (kbd "M-)")
+;;; 'paredit-close-round)
+;;;
+;;; Paredit Mode also binds the usual keys for deleting and killing, so
+;;; that they will not destroy any S-expression structure by killing or
+;;; deleting only one side of a parenthesis or quote pair. If the
+;;; point is on a closing delimiter, `DEL' will move left over it; if
+;;; it is on an opening delimiter, `C-d' will move right over it. Only
+;;; if the point is between a pair of delimiters will `C-d' or `DEL'
+;;; delete them, and in that case it will delete both simultaneously.
+;;; `M-d' and `M-DEL' kill words, but skip over any S-expression
+;;; structure. `C-k' kills from the start of the line, either to the
+;;; line's end, if it contains only balanced expressions; to the first
+;;; closing delimiter, if the point is within a form that ends on the
+;;; line; or up to the end of the last expression that starts on the
+;;; line after the point.
+;;;
+;;; The behaviour of the commands for deleting and killing can be
+;;; overridden by passing a `C-u' prefix argument: `C-u DEL' will
+;;; delete a character backward, `C-u C-d' will delete a character
+;;; forward, and `C-u C-k' will kill text from the point to the end of
+;;; the line, irrespective of the S-expression structure in the buffer.
+;;; This can be used to fix mistakes in a buffer, but should generally
+;;; be avoided.
+;;;
+;;; Paredit performs automatic reindentation as locally as possible, to
+;;; avoid interfering with custom indentation used elsewhere in some
+;;; S-expression. Only the advanced S-expression manipulation commands
+;;; automatically reindent, and only the forms that were immediately
+;;; operated upon (and their subforms).
+;;;
+;;; This code is written for clarity, not efficiency. It frequently
+;;; walks over S-expressions redundantly. If you have problems with
+;;; the time it takes to execute some of the commands, let me know, but
+;;; first be sure that what you're doing is reasonable: it is
+;;; preferable to avoid immense S-expressions in code anyway.
+
+;;; This assumes Unix-style LF line endings.
+
+(defconst paredit-version 22)
+(defconst paredit-beta-p t)
+
+(eval-and-compile
+
+ (defun paredit-xemacs-p ()
+ ;; No idea where I got this definition from. Edward O'Connor
+ ;; (hober in #emacs) suggested the current definition.
+ ;; (and (boundp 'running-xemacs)
+ ;; running-xemacs)
+ (featurep 'xemacs))
+
+ (defun paredit-gnu-emacs-p ()
+ ;++ This could probably be improved.
+ (not (paredit-xemacs-p)))
+
+ (defmacro xcond (&rest clauses)
+ "Exhaustive COND.
+Signal an error if no clause matches."
+ `(cond ,@clauses
+ (t (error "XCOND lost."))))
+
+ (defalias 'paredit-warn (if (fboundp 'warn) 'warn 'message))
+
+ (defvar paredit-sexp-error-type
+ (with-temp-buffer
+ (insert "(")
+ (condition-case condition
+ (backward-sexp)
+ (error (if (eq (car condition) 'error)
+ (paredit-warn "%s%s%s%s%s"
+ "Paredit is unable to discriminate"
+ " S-expression parse errors from"
+ " other errors. "
+ " This may cause obscure problems. "
+ " Please upgrade Emacs."))
+ (car condition)))))
+
+ (defmacro paredit-handle-sexp-errors (body &rest handler)
+ `(condition-case ()
+ ,body
+ (,paredit-sexp-error-type ,@handler)))
+
+ (put 'paredit-handle-sexp-errors 'lisp-indent-function 1)
+
+ (defmacro paredit-ignore-sexp-errors (&rest body)
+ `(paredit-handle-sexp-errors (progn ,@body)
+ nil))
+
+ (put 'paredit-ignore-sexp-errors 'lisp-indent-function 0)
+
+ nil)
+
+;;;; Minor Mode Definition
+
+(defvar paredit-mode-map (make-sparse-keymap)
+ "Keymap for the paredit minor mode.")
+
+;;;###autoload
+(define-minor-mode paredit-mode
+ "Minor mode for pseudo-structurally editing Lisp code.
+With a prefix argument, enable Paredit Mode even if there are
+ imbalanced parentheses in the buffer.
+Paredit behaves badly if parentheses are imbalanced, so exercise
+ caution when forcing Paredit Mode to be enabled, and consider
+ fixing imbalanced parentheses instead.
+\\<paredit-mode-map>"
+ :lighter " Paredit"
+ ;; If we're enabling paredit-mode, the prefix to this code that
+ ;; DEFINE-MINOR-MODE inserts will have already set PAREDIT-MODE to
+ ;; true. If this is the case, then first check the parentheses, and
+ ;; if there are any imbalanced ones we must inhibit the activation of
+ ;; paredit mode. We skip the check, though, if the user supplied a
+ ;; prefix argument interactively.
+ (if (and paredit-mode
+ (not current-prefix-arg))
+ (if (not (fboundp 'check-parens))
+ (paredit-warn "`check-parens' is not defined; %s"
+ "be careful of malformed S-expressions.")
+ (condition-case condition
+ (check-parens)
+ (error (setq paredit-mode nil)
+ (signal (car condition) (cdr condition)))))))
+
+;;; Old functions from when there was a different mode for emacs -nw.
+
+(defun enable-paredit-mode ()
+ "Turn on pseudo-structural editing of Lisp code.
+
+Deprecated: use `paredit-mode' instead."
+ (interactive)
+ (paredit-mode +1))
+
+(defun disable-paredit-mode ()
+ "Turn off pseudo-structural editing of Lisp code.
+
+Deprecated: use `paredit-mode' instead."
+ (interactive)
+ (paredit-mode -1))
+
+(defvar paredit-backward-delete-key
+ (xcond ((paredit-xemacs-p) "BS")
+ ((paredit-gnu-emacs-p) "DEL")))
+
+(defvar paredit-forward-delete-keys
+ (xcond ((paredit-xemacs-p) '("DEL"))
+ ((paredit-gnu-emacs-p) '("<delete>" "<deletechar>"))))
+
+;;;; Paredit Keys
+
+;;; Separating the definition and initialization of this variable
+;;; simplifies the development of paredit, since re-evaluating DEFVAR
+;;; forms doesn't actually do anything.
+
+(defvar paredit-commands nil
+ "List of paredit commands with their keys and examples.")
+
+;;; Each specifier is of the form:
+;;; (key[s] function (example-input example-output) ...)
+;;; where key[s] is either a single string suitable for passing to KBD
+;;; or a list of such strings. Entries in this list may also just be
+;;; strings, in which case they are headings for the next entries.
+
+(progn (setq paredit-commands
+ `(
+ "Basic Insertion Commands"
+ ("(" paredit-open-round
+ ("(a b |c d)"
+ "(a b (|) c d)")
+ ("(foo \"bar |baz\" quux)"
+ "(foo \"bar (|baz\" quux)"))
+ (")" paredit-close-round
+ ("(a b |c )" "(a b c)|")
+ ("; Hello,| world!"
+ "; Hello,)| world!"))
+ ("M-)" paredit-close-round-and-newline
+ ("(defun f (x| ))"
+ "(defun f (x)\n |)")
+ ("; (Foo.|"
+ "; (Foo.)|"))
+ ("[" paredit-open-square
+ ("(a b |c d)"
+ "(a b [|] c d)")
+ ("(foo \"bar |baz\" quux)"
+ "(foo \"bar [baz\" quux)"))
+ ("]" paredit-close-square
+ ("(define-key keymap [frob| ] 'frobnicate)"
+ "(define-key keymap [frob]| 'frobnicate)")
+ ("; [Bar.|"
+ "; [Bar.]|"))
+ ("\"" paredit-doublequote
+ ("(frob grovel |full lexical)"
+ "(frob grovel \"|\" full lexical)")
+ ("(foo \"bar |baz\" quux)"
+ "(foo \"bar \\\"|baz\" quux)"))
+ ("M-\"" paredit-meta-doublequote
+ ("(foo \"bar |baz\" quux)"
+ "(foo \"bar baz\"\n |quux)")
+ ("(foo |(bar #\\x \"baz \\\\ quux\") zot)"
+ ,(concat "(foo \"|(bar #\\\\x \\\"baz \\\\"
+ "\\\\ quux\\\")\" zot)")))
+ ("\\" paredit-backslash
+ ("(string #|)\n ; Escaping character... (x)"
+ "(string #\\x|)")
+ ("\"foo|bar\"\n ; Escaping character... (\")"
+ "\"foo\\\"|bar\""))
+ ("M-;" paredit-comment-dwim
+ ("(foo |bar) ; baz"
+ "(foo bar) ; |baz")
+ ("(frob grovel)|"
+ "(frob grovel) ;|")
+ (" (foo bar)\n|\n (baz quux)"
+ " (foo bar)\n ;; |\n (baz quux)")
+ (" (foo bar) |(baz quux)"
+ " (foo bar)\n ;; |\n (baz quux)")
+ ("|(defun hello-world ...)"
+ ";;; |\n(defun hello-world ...)"))
+
+ ("C-j" paredit-newline
+ ("(let ((n (frobbotz))) |(display (+ n 1)\nport))"
+ ,(concat "(let ((n (frobbotz)))"
+ "\n |(display (+ n 1)"
+ "\n port))")))
+
+ "Deleting & Killing"
+ (("C-d" ,@paredit-forward-delete-keys)
+ paredit-forward-delete
+ ("(quu|x \"zot\")" "(quu| \"zot\")")
+ ("(quux |\"zot\")"
+ "(quux \"|zot\")"
+ "(quux \"|ot\")")
+ ("(foo (|) bar)" "(foo | bar)")
+ ("|(foo bar)" "(|foo bar)"))
+ (,paredit-backward-delete-key
+ paredit-backward-delete
+ ("(\"zot\" q|uux)" "(\"zot\" |uux)")
+ ("(\"zot\"| quux)"
+ "(\"zot|\" quux)"
+ "(\"zo|\" quux)")
+ ("(foo (|) bar)" "(foo | bar)")
+ ("(foo bar)|" "(foo bar|)"))
+ ("C-k" paredit-kill
+ ("(foo bar)| ; Useless comment!"
+ "(foo bar)|")
+ ("(|foo bar) ; Useful comment!"
+ "(|) ; Useful comment!")
+ ("|(foo bar) ; Useless line!"
+ "|")
+ ("(foo \"|bar baz\"\n quux)"
+ "(foo \"|\"\n quux)"))
+ ("M-d" paredit-forward-kill-word
+ ("|(foo bar) ; baz"
+ "(| bar) ; baz"
+ "(|) ; baz"
+ "() ;|")
+ (";;;| Frobnicate\n(defun frobnicate ...)"
+ ";;;|\n(defun frobnicate ...)"
+ ";;;\n(| frobnicate ...)"))
+ (,(concat "M-" paredit-backward-delete-key)
+ paredit-backward-kill-word
+ ("(foo bar) ; baz\n(quux)|"
+ "(foo bar) ; baz\n(|)"
+ "(foo bar) ; |\n()"
+ "(foo |) ; \n()"
+ "(|) ; \n()"))
+
+ "Movement & Navigation"
+ ("C-M-f" paredit-forward
+ ("(foo |(bar baz) quux)"
+ "(foo (bar baz)| quux)")
+ ("(foo (bar)|)"
+ "(foo (bar))|"))
+ ("C-M-b" paredit-backward
+ ("(foo (bar baz)| quux)"
+ "(foo |(bar baz) quux)")
+ ("(|(foo) bar)"
+ "|((foo) bar)"))
+;;;("C-M-u" backward-up-list) ; These two are built-in.
+;;;("C-M-d" down-list)
+ ("C-M-p" backward-down-list) ; Built-in, these are FORWARD-
+ ("C-M-n" up-list) ; & BACKWARD-LIST, which have
+ ; no need given C-M-f & C-M-b.
+
+ "Depth-Changing Commands"
+ ("M-(" paredit-wrap-round
+ ("(foo |bar baz)"
+ "(foo (|bar) baz)"))
+ ("M-s" paredit-splice-sexp
+ ("(foo (bar| baz) quux)"
+ "(foo bar| baz quux)"))
+ (("M-<up>" "ESC <up>")
+ paredit-splice-sexp-killing-backward
+ ("(foo (let ((x 5)) |(sqrt n)) bar)"
+ "(foo (sqrt n) bar)"))
+ (("M-<down>" "ESC <down>")
+ paredit-splice-sexp-killing-forward
+ ("(a (b c| d e) f)"
+ "(a b c f)"))
+ ("M-r" paredit-raise-sexp
+ ("(dynamic-wind in (lambda () |body) out)"
+ "(dynamic-wind in |body out)"
+ "|body"))
+
+ "Barfage & Slurpage"
+ (("C-)" "C-<right>")
+ paredit-forward-slurp-sexp
+ ("(foo (bar |baz) quux zot)"
+ "(foo (bar |baz quux) zot)")
+ ("(a b ((c| d)) e f)"
+ "(a b ((c| d) e) f)"))
+ (("C-}" "C-<left>")
+ paredit-forward-barf-sexp
+ ("(foo (bar |baz quux) zot)"
+ "(foo (bar |baz) quux zot)"))
+ (("C-(" "C-M-<left>" "ESC C-<left>")
+ paredit-backward-slurp-sexp
+ ("(foo bar (baz| quux) zot)"
+ "(foo (bar baz| quux) zot)")
+ ("(a b ((c| d)) e f)"
+ "(a (b (c| d)) e f)"))
+ (("C-{" "C-M-<right>" "ESC C-<right>")
+ paredit-backward-barf-sexp
+ ("(foo (bar baz |quux) zot)"
+ "(foo bar (baz |quux) zot)"))
+
+ "Miscellaneous Commands"
+ ("M-S" paredit-split-sexp
+ ("(hello| world)"
+ "(hello)| (world)")
+ ("\"Hello, |world!\""
+ "\"Hello, \"| \"world!\""))
+ ("M-J" paredit-join-sexps
+ ("(hello)| (world)"
+ "(hello| world)")
+ ("\"Hello, \"| \"world!\""
+ "\"Hello, |world!\"")
+ ("hello-\n| world"
+ "hello-|world"))
+ ("C-c C-M-l" paredit-recentre-on-sexp)
+ ("M-q" paredit-reindent-defun)
+ ))
+ nil) ; end of PROGN
+
+;;;;; Command Examples
+
+(eval-and-compile
+ (defmacro paredit-do-commands (vars string-case &rest body)
+ (let ((spec (nth 0 vars))
+ (keys (nth 1 vars))
+ (fn (nth 2 vars))
+ (examples (nth 3 vars)))
+ `(dolist (,spec paredit-commands)
+ (if (stringp ,spec)
+ ,string-case
+ (let ((,keys (let ((k (car ,spec)))
+ (cond ((stringp k) (list k))
+ ((listp k) k)
+ (t (error "Invalid paredit command %s."
+ ,spec)))))
+ (,fn (cadr ,spec))
+ (,examples (cddr ,spec)))
+ ,@body)))))
+
+ (put 'paredit-do-commands 'lisp-indent-function 2))
+
+(defun paredit-define-keys ()
+ (paredit-do-commands (spec keys fn examples)
+ nil ; string case
+ (dolist (key keys)
+ (define-key paredit-mode-map (read-kbd-macro key) fn))))
+
+(defun paredit-function-documentation (fn)
+ (let ((original-doc (get fn 'paredit-original-documentation))
+ (doc (documentation fn 'function-documentation)))
+ (or original-doc
+ (progn (put fn 'paredit-original-documentation doc)
+ doc))))
+
+(defun paredit-annotate-mode-with-examples ()
+ (let ((contents
+ (list (paredit-function-documentation 'paredit-mode))))
+ (paredit-do-commands (spec keys fn examples)
+ (push (concat "\n \n" spec "\n")
+ contents)
+ (let ((name (symbol-name fn)))
+ (if (string-match (symbol-name 'paredit-) name)
+ (push (concat "\n\n\\[" name "]\t" name
+ (if examples
+ (mapconcat (lambda (example)
+ (concat
+ "\n"
+ (mapconcat 'identity
+ example
+ "\n --->\n")
+ "\n"))
+ examples
+ "")
+ "\n (no examples)\n"))
+ contents))))
+ (put 'paredit-mode 'function-documentation
+ (apply 'concat (reverse contents))))
+ ;; PUT returns the huge string we just constructed, which we don't
+ ;; want it to return.
+ nil)
+
+(defun paredit-annotate-functions-with-examples ()
+ (paredit-do-commands (spec keys fn examples)
+ nil ; string case
+ (put fn 'function-documentation
+ (concat (paredit-function-documentation fn)
+ "\n\n\\<paredit-mode-map>\\[" (symbol-name fn) "]\n"
+ (mapconcat (lambda (example)
+ (concat "\n"
+ (mapconcat 'identity
+ example
+ "\n ->\n")
+ "\n"))
+ examples
+ "")))))
+
+;;;;; HTML Examples
+
+(defun paredit-insert-html-examples ()
+ "Insert HTML for a paredit quick reference table."
+ (interactive)
+ (let ((insert-lines
+ (lambda (&rest lines)
+ (mapc (lambda (line) (insert line) (newline))
+ lines)))
+ (html-keys
+ (lambda (keys)
+ (mapconcat 'paredit-html-quote keys ", ")))
+ (html-example
+ (lambda (example)
+ (concat "<table><tr><td><pre>"
+ (mapconcat 'paredit-html-quote
+ example
+ (concat "</pre></td></tr><tr><td>"
+ "&nbsp;&nbsp;&nbsp;&nbsp;---&gt;"
+ "</td></tr><tr><td><pre>"))
+ "</pre></td></tr></table>")))
+ (firstp t))
+ (paredit-do-commands (spec keys fn examples)
+ (progn (if (not firstp)
+ (insert "</table>\n")
+ (setq firstp nil))
+ (funcall insert-lines
+ (concat "<h3>" spec "</h3>")
+ "<table border=\"1\" cellpadding=\"1\">"
+ " <tr>"
+ " <th>Command</th>"
+ " <th>Keys</th>"
+ " <th>Examples</th>"
+ " </tr>"))
+ (let ((name (symbol-name fn)))
+ (if (string-match (symbol-name 'paredit-) name)
+ (funcall insert-lines
+ " <tr>"
+ (concat " <td><tt>" name "</tt></td>")
+ (concat " <td align=\"center\">"
+ (funcall html-keys keys)
+ "</td>")
+ (concat " <td>"
+ (if examples
+ (mapconcat html-example examples
+ "<hr>")
+ "(no examples)")
+ "</td>")
+ " </tr>")))))
+ (insert "</table>\n"))
+
+(defun paredit-html-quote (string)
+ (with-temp-buffer
+ (dotimes (i (length string))
+ (insert (let ((c (elt string i)))
+ (cond ((eq c ?\<) "&lt;")
+ ((eq c ?\>) "&gt;")
+ ((eq c ?\&) "&amp;")
+ ((eq c ?\') "&apos;")
+ ((eq c ?\") "&quot;")
+ (t c)))))
+ (buffer-string)))
+
+;;;; Delimiter Insertion
+
+(defvar paredit-space-delimiter-chars (list ?w ?_ ?\")
+ "Characters for which a space should be inserted between them
+and a delimiter. Set this to (list ?\\\") as a buffer-local value
+for non-lisp languages.")
+
+(eval-and-compile
+ (defun paredit-conc-name (&rest strings)
+ (intern (apply 'concat strings)))
+
+ (defmacro define-paredit-pair (open close name)
+ `(progn
+ (defun ,(paredit-conc-name "paredit-open-" name) (&optional n)
+ ,(concat "Insert a balanced " name " pair.
+With a prefix argument N, put the closing " name " after N
+ S-expressions forward.
+If the region is active, `transient-mark-mode' is enabled, and the
+ region's start and end fall in the same parenthesis depth, insert a
+ " name " pair around the region.
+If in a string or a comment, insert a single " name ".
+If in a character literal, do nothing. This prevents changing what was
+ in the character literal to a meaningful delimiter unintentionally.")
+ (interactive "P")
+ (cond ((or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (insert ,open))
+ ((not (paredit-in-char-p))
+ (paredit-insert-pair n ,open ,close 'goto-char))))
+ (defun ,(paredit-conc-name "paredit-close-" name) ()
+ ,(concat "Move past one closing delimiter and reindent.
+\(Agnostic to the specific closing delimiter.)
+If in a string or comment, insert a single closing " name ".
+If in a character literal, do nothing. This prevents changing what was
+ in the character literal to a meaningful delimiter unintentionally.")
+ (interactive)
+ (paredit-move-past-close ,close))
+ (defun ,(paredit-conc-name "paredit-close-" name "-and-newline") ()
+ ,(concat "Move past one closing delimiter, add a newline,"
+ " and reindent.
+If there was a margin comment after the closing delimiter, preserve it
+ on the same line.")
+ (interactive)
+ (paredit-move-past-close-and-newline ,close))
+ (defun ,(paredit-conc-name "paredit-wrap-" name)
+ (&optional argument)
+ ,(concat "Wrap the following S-expression.
+See `paredit-wrap-sexp' for more details.")
+ (interactive "P")
+ (paredit-wrap-sexp argument ,open ,close))
+ (add-to-list 'paredit-wrap-commands
+ ',(paredit-conc-name "paredit-wrap-" name)))))
+
+(defvar paredit-wrap-commands '(paredit-wrap-sexp)
+ "List of paredit commands that wrap S-expressions.
+Used by `paredit-yank-pop'; for internal paredit use only.")
+
+(define-paredit-pair ?\( ?\) "round")
+(define-paredit-pair ?\[ ?\] "square")
+(define-paredit-pair ?\{ ?\} "curly")
+(define-paredit-pair ?\< ?\> "angled")
+
+;;; Aliases for the old names.
+
+(defalias 'paredit-open-parenthesis 'paredit-open-round)
+(defalias 'paredit-close-parenthesis 'paredit-close-round)
+(defalias 'paredit-close-parenthesis-and-newline
+ 'paredit-close-round-and-newline)
+
+(defalias 'paredit-open-bracket 'paredit-open-square)
+(defalias 'paredit-close-bracket 'paredit-close-square)
+(defalias 'paredit-close-bracket-and-newline
+ 'paredit-close-square-and-newline)
+
+(defun paredit-move-past-close (close)
+ (cond ((or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (insert close))
+ ((not (paredit-in-char-p))
+ (paredit-move-past-close-and-reindent close)
+ (paredit-blink-paren-match nil))))
+
+(defun paredit-move-past-close-and-newline (close)
+ (if (or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (insert close)
+ (if (paredit-in-char-p) (forward-char))
+ (paredit-move-past-close-and-reindent close)
+ (let ((comment.point (paredit-find-comment-on-line)))
+ (newline)
+ (if comment.point
+ (save-excursion
+ (forward-line -1)
+ (end-of-line)
+ (indent-to (cdr comment.point))
+ (insert (car comment.point)))))
+ (lisp-indent-line)
+ (paredit-ignore-sexp-errors (indent-sexp))
+ (paredit-blink-paren-match t)))
+
+(defun paredit-find-comment-on-line ()
+ "Find a margin comment on the current line.
+Return nil if there is no such comment or if there is anything but
+ whitespace until such a comment.
+If such a comment exists, delete the comment (including all leading
+ whitespace) and return a cons whose car is the comment as a string
+ and whose cdr is the point of the comment's initial semicolon,
+ relative to the start of the line."
+ (save-excursion
+ (paredit-skip-whitespace t (point-at-eol))
+ (and (eq ?\; (char-after))
+ (not (eq ?\; (char-after (1+ (point)))))
+ (not (or (paredit-in-string-p)
+ (paredit-in-char-p)))
+ (let* ((start ;Move to before the semicolon.
+ (progn (backward-char) (point)))
+ (comment
+ (buffer-substring start (point-at-eol))))
+ (paredit-skip-whitespace nil (point-at-bol))
+ (delete-region (point) (point-at-eol))
+ (cons comment (- start (point-at-bol)))))))
+
+(defun paredit-insert-pair (n open close forward)
+ (let* ((regionp
+ (and (paredit-region-active-p)
+ (paredit-region-safe-for-insert-p)))
+ (end
+ (and regionp
+ (not n)
+ (prog1 (region-end) (goto-char (region-beginning))))))
+ (let ((spacep (paredit-space-for-delimiter-p nil open)))
+ (if spacep (insert " "))
+ (insert open)
+ (save-excursion
+ ;; Move past the desired region.
+ (cond (n (funcall forward
+ (save-excursion
+ (forward-sexp (prefix-numeric-value n))
+ (point))))
+ (regionp (funcall forward (+ end (if spacep 2 1)))))
+ (insert close)
+ (if (paredit-space-for-delimiter-p t close)
+ (insert " "))))))
+
+(defun paredit-region-safe-for-insert-p ()
+ (save-excursion
+ (let ((beginning (region-beginning))
+ (end (region-end)))
+ (goto-char beginning)
+ (let* ((beginning-state (paredit-current-parse-state))
+ (end-state
+ (parse-partial-sexp beginning end nil nil beginning-state)))
+ (and (= (nth 0 beginning-state) ; 0. depth in parens
+ (nth 0 end-state))
+ (eq (nth 3 beginning-state) ; 3. non-nil if inside a
+ (nth 3 end-state)) ; string
+ (eq (nth 4 beginning-state) ; 4. comment status, yada
+ (nth 4 end-state))
+ (eq (nth 5 beginning-state) ; 5. t if following char
+ (nth 5 end-state))))))) ; quote
+
+(defun paredit-space-for-delimiter-p (endp delimiter)
+ ;; If at the buffer limit, don't insert a space. If there is a word,
+ ;; symbol, other quote, or non-matching parenthesis delimiter (i.e. a
+ ;; close when want an open the string or an open when we want to
+ ;; close the string), do insert a space.
+ (and (not (if endp (eobp) (bobp)))
+ (memq (char-syntax (if endp (char-after) (char-before)))
+ (append paredit-space-delimiter-chars
+ (list (let ((matching (matching-paren delimiter)))
+ (and matching (char-syntax matching))))))))
+
+(defun paredit-move-past-close-and-reindent (close)
+ (let ((open (paredit-missing-close)))
+ (if open
+ (if (eq close (matching-paren open))
+ (save-excursion
+ (message "Missing closing delimiter: %c" close)
+ (insert close))
+ (error "Mismatched missing closing delimiter: %c ... %c"
+ open close))))
+ (let ((orig (point)))
+ (up-list)
+ (if (catch 'return ; This CATCH returns T if it
+ (while t ; should delete leading spaces
+ (save-excursion ; and NIL if not.
+ (let ((before-paren (1- (point))))
+ (back-to-indentation)
+ (cond ((not (eq (point) before-paren))
+ ;; Can't call PAREDIT-DELETE-LEADING-WHITESPACE
+ ;; here -- we must return from SAVE-EXCURSION
+ ;; first.
+ (throw 'return t))
+ ((save-excursion (forward-line -1)
+ (end-of-line)
+ (paredit-in-comment-p))
+ ;; Moving the closing delimiter any further
+ ;; would put it into a comment, so we just
+ ;; indent the closing delimiter where it is and
+ ;; abort the loop, telling its continuation that
+ ;; no leading whitespace should be deleted.
+ (lisp-indent-line)
+ (throw 'return nil))
+ (t (delete-indentation)))))))
+ (paredit-delete-leading-whitespace))))
+
+(defun paredit-missing-close ()
+ (save-excursion
+ (paredit-handle-sexp-errors (backward-up-list)
+ (error "Not inside a list."))
+ (let ((open (char-after)))
+ (paredit-handle-sexp-errors (progn (forward-sexp) nil)
+ open))))
+
+(defun paredit-delete-leading-whitespace ()
+ ;; This assumes that we're on the closing delimiter already.
+ (save-excursion
+ (backward-char)
+ (while (let ((syn (char-syntax (char-before))))
+ (and (or (eq syn ?\ ) (eq syn ?-)) ; whitespace syntax
+ ;; The above line is a perfect example of why the
+ ;; following test is necessary.
+ (not (paredit-in-char-p (1- (point))))))
+ (backward-delete-char 1))))
+
+(defun paredit-blink-paren-match (another-line-p)
+ (if (and blink-matching-paren
+ (or (not show-paren-mode) another-line-p))
+ (paredit-ignore-sexp-errors
+ (save-excursion
+ (backward-sexp)
+ (forward-sexp)
+ ;; SHOW-PAREN-MODE inhibits any blinking, so we disable it
+ ;; locally here.
+ (let ((show-paren-mode nil))
+ (blink-matching-open))))))
+
+(defun paredit-doublequote (&optional n)
+ "Insert a pair of double-quotes.
+With a prefix argument N, wrap the following N S-expressions in
+ double-quotes, escaping intermediate characters if necessary.
+If the region is active, `transient-mark-mode' is enabled, and the
+ region's start and end fall in the same parenthesis depth, insert a
+ pair of double-quotes around the region, again escaping intermediate
+ characters if necessary.
+Inside a comment, insert a literal double-quote.
+At the end of a string, move past the closing double-quote.
+In the middle of a string, insert a backslash-escaped double-quote.
+If in a character literal, do nothing. This prevents accidentally
+ changing a what was in the character literal to become a meaningful
+ delimiter unintentionally."
+ (interactive "P")
+ (cond ((paredit-in-string-p)
+ (if (eq (cdr (paredit-string-start+end-points))
+ (point))
+ (forward-char) ; We're on the closing quote.
+ (insert ?\\ ?\" )))
+ ((paredit-in-comment-p)
+ (insert ?\" ))
+ ((not (paredit-in-char-p))
+ (paredit-insert-pair n ?\" ?\" 'paredit-forward-for-quote))))
+
+(defun paredit-meta-doublequote (&optional n)
+ "Move to the end of the string, insert a newline, and indent.
+If not in a string, act as `paredit-doublequote'; if no prefix argument
+ is specified and the region is not active or `transient-mark-mode' is
+ disabled, the default is to wrap one S-expression, however, not
+ zero."
+ (interactive "P")
+ (if (not (paredit-in-string-p))
+ (paredit-doublequote (or n
+ (and (not (paredit-region-active-p))
+ 1)))
+ (let ((start+end (paredit-string-start+end-points)))
+ (goto-char (1+ (cdr start+end)))
+ (newline)
+ (lisp-indent-line)
+ (paredit-ignore-sexp-errors (indent-sexp)))))
+
+(defun paredit-forward-for-quote (end)
+ (let ((state (paredit-current-parse-state)))
+ (while (< (point) end)
+ (let ((new-state (parse-partial-sexp (point) (1+ (point))
+ nil nil state)))
+ (if (paredit-in-string-p new-state)
+ (if (not (paredit-in-string-escape-p))
+ (setq state new-state)
+ ;; Escape character: turn it into an escaped escape
+ ;; character by appending another backslash.
+ (insert ?\\ )
+ ;; Now the point is after both escapes, and we want to
+ ;; rescan from before the first one to after the second
+ ;; one.
+ (setq state
+ (parse-partial-sexp (- (point) 2) (point)
+ nil nil state))
+ ;; Advance the end point, since we just inserted a new
+ ;; character.
+ (setq end (1+ end)))
+ ;; String: escape by inserting a backslash before the quote.
+ (backward-char)
+ (insert ?\\ )
+ ;; The point is now between the escape and the quote, and we
+ ;; want to rescan from before the escape to after the quote.
+ (setq state
+ (parse-partial-sexp (1- (point)) (1+ (point))
+ nil nil state))
+ ;; Advance the end point for the same reason as above.
+ (setq end (1+ end)))))))
+
+;;;; Escape Insertion
+
+(defun paredit-backslash ()
+ "Insert a backslash followed by a character to escape."
+ (interactive)
+ (insert ?\\ )
+ ;; This funny conditional is necessary because PAREDIT-IN-COMMENT-P
+ ;; assumes that PAREDIT-IN-STRING-P already returned false; otherwise
+ ;; it may give erroneous answers.
+ (if (or (paredit-in-string-p)
+ (not (paredit-in-comment-p)))
+ (let ((delp t))
+ (unwind-protect (setq delp
+ (call-interactively 'paredit-escape))
+ ;; We need this in an UNWIND-PROTECT so that the backlash is
+ ;; left in there *only* if PAREDIT-ESCAPE return NIL normally
+ ;; -- in any other case, such as the user hitting C-g or an
+ ;; error occurring, we must delete the backslash to avoid
+ ;; leaving a dangling escape. (This control structure is a
+ ;; crock.)
+ (if delp (backward-delete-char 1))))))
+
+;;; This auxiliary interactive function returns true if the backslash
+;;; should be deleted and false if not.
+
+(defun paredit-escape (char)
+ ;; I'm too lazy to figure out how to do this without a separate
+ ;; interactive function.
+ (interactive "cEscaping character...")
+ (if (eq char 127) ; The backslash was a typo, so
+ t ; the luser wants to delete it.
+ (insert char) ; (Is there a better way to
+ nil)) ; express the rubout char?
+ ; ?\^? works, but ugh...)
+
+;;; The placement of these functions in this file is totally random.
+
+(defun paredit-newline ()
+ "Insert a newline and indent it.
+This is like `newline-and-indent', but it not only indents the line
+ that the point is on but also the S-expression following the point,
+ if there is one.
+Move forward one character first if on an escaped character.
+If in a string, just insert a literal newline."
+ (interactive)
+ (if (paredit-in-string-p)
+ (newline)
+ (if (and (not (paredit-in-comment-p)) (paredit-in-char-p))
+ (forward-char))
+ (newline-and-indent)
+ ;; Indent the following S-expression, but don't signal an error if
+ ;; there's only a closing delimiter after the point.
+ (paredit-ignore-sexp-errors (indent-sexp))))
+
+(defun paredit-reindent-defun (&optional argument)
+ "Reindent the definition that the point is on.
+If the point is in a string or a comment, fill the paragraph instead,
+ and with a prefix argument, justify as well."
+ (interactive "P")
+ (if (or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (fill-paragraph argument)
+ (save-excursion
+ (end-of-defun)
+ (beginning-of-defun)
+ (indent-sexp))))
+
+;;;; Comment Insertion
+
+;;; This is all a horrible, horrible hack, primarily for GNU Emacs 21,
+;;; in which there is no `comment-or-uncomment-region'.
+
+(autoload 'comment-forward "newcomment")
+(autoload 'comment-normalize-vars "newcomment")
+(autoload 'comment-region "newcomment")
+(autoload 'comment-search-forward "newcomment")
+(autoload 'uncomment-region "newcomment")
+
+(defun paredit-initialize-comment-dwim ()
+ (require 'newcomment)
+ (if (not (fboundp 'comment-or-uncomment-region))
+ (defalias 'comment-or-uncomment-region
+ (lambda (beginning end &optional argument)
+ (interactive "*r\nP")
+ (if (save-excursion (goto-char beginning)
+ (comment-forward (point-max))
+ (<= end (point)))
+ (uncomment-region beginning end argument)
+ (comment-region beginning end argument)))))
+ (defalias 'paredit-initialize-comment-dwim 'comment-normalize-vars)
+ (comment-normalize-vars))
+
+(defun paredit-comment-dwim (&optional argument)
+ "Call the Lisp comment command you want (Do What I Mean).
+This is like `comment-dwim', but it is specialized for Lisp editing.
+If transient mark mode is enabled and the mark is active, comment or
+ uncomment the selected region, depending on whether it was entirely
+ commented not not already.
+If there is already a comment on the current line, with no prefix
+ argument, indent to that comment; with a prefix argument, kill that
+ comment.
+Otherwise, insert a comment appropriate for the context and ensure that
+ any code following the comment is moved to the next line.
+At the top level, where indentation is calculated to be at column 0,
+ insert a triple-semicolon comment; within code, where the indentation
+ is calculated to be non-zero, and on the line there is either no code
+ at all or code after the point, insert a double-semicolon comment;
+ and if the point is after all code on the line, insert a single-
+ semicolon margin comment at `comment-column'."
+ (interactive "*P")
+ (paredit-initialize-comment-dwim)
+ (cond ((paredit-region-active-p)
+ (comment-or-uncomment-region (region-beginning)
+ (region-end)
+ argument))
+ ((paredit-comment-on-line-p)
+ (if argument
+ (comment-kill (if (integerp argument) argument nil))
+ (comment-indent)))
+ (t (paredit-insert-comment))))
+
+(defun paredit-comment-on-line-p ()
+ "True if there is a comment on the line following point.
+This is expected to be called only in `paredit-comment-dwim'; do not
+ call it elsewhere."
+ (save-excursion
+ (beginning-of-line)
+ (let ((comment-p nil))
+ ;; Search forward for a comment beginning. If there is one, set
+ ;; COMMENT-P to true; if not, it will be nil.
+ (while (progn
+ (setq comment-p ;t -> no error
+ (comment-search-forward (point-at-eol) t))
+ (and comment-p
+ (or (paredit-in-string-p)
+ (paredit-in-char-p (1- (point))))))
+ (forward-char))
+ comment-p)))
+
+(defun paredit-insert-comment ()
+ (let ((code-after-p
+ (save-excursion (paredit-skip-whitespace t (point-at-eol))
+ (not (eolp))))
+ (code-before-p
+ (save-excursion (paredit-skip-whitespace nil (point-at-bol))
+ (not (bolp)))))
+ (cond ((and (bolp)
+ (let ((indent
+ (let ((indent (calculate-lisp-indent)))
+ (if (consp indent) (car indent) indent))))
+ (and indent (zerop indent))))
+ ;; Top-level comment
+ (if code-after-p (save-excursion (newline)))
+ (insert ";;; "))
+ ((or code-after-p (not code-before-p))
+ ;; Code comment
+ (if code-before-p (newline))
+ (lisp-indent-line)
+ (insert ";; ")
+ (if code-after-p
+ (save-excursion
+ (newline)
+ (lisp-indent-line)
+ (indent-sexp))))
+ (t
+ ;; Margin comment
+ (indent-to comment-column 1) ; 1 -> force one leading space
+ (insert ?\; )))))
+
+;;;; Character Deletion
+
+(defun paredit-forward-delete (&optional argument)
+ "Delete a character forward or move forward over a delimiter.
+If on an opening S-expression delimiter, move forward into the
+ S-expression.
+If on a closing S-expression delimiter, refuse to delete unless the
+ S-expression is empty, in which case delete the whole S-expression.
+With a numeric prefix argument N, delete N characters forward.
+With a `C-u' prefix argument, simply delete a character forward,
+ without regard for delimiter balancing."
+ (interactive "P")
+ (cond ((or (consp argument) (eobp))
+ (delete-char 1))
+ ((integerp argument)
+ (if (< argument 0)
+ (paredit-backward-delete argument)
+ (while (> argument 0)
+ (paredit-forward-delete)
+ (setq argument (- argument 1)))))
+ ((paredit-in-string-p)
+ (paredit-forward-delete-in-string))
+ ((paredit-in-comment-p)
+ ;++ What to do here? This could move a partial S-expression
+ ;++ into a comment and thereby invalidate the file's form,
+ ;++ or move random text out of a comment.
+ (delete-char 1))
+ ((paredit-in-char-p) ; Escape -- delete both chars.
+ (backward-delete-char 1)
+ (delete-char 1))
+ ((eq (char-after) ?\\ ) ; ditto
+ (delete-char 2))
+ ((let ((syn (char-syntax (char-after))))
+ (or (eq syn ?\( )
+ (eq syn ?\" )))
+ (if (save-excursion
+ (paredit-handle-sexp-errors (progn (forward-sexp) t)
+ nil))
+ (forward-char)
+ (message "Deleting spurious opening delimiter.")
+ (delete-char 1)))
+ ((and (not (paredit-in-char-p (1- (point))))
+ (eq (char-syntax (char-after)) ?\) )
+ (eq (char-before) (matching-paren (char-after))))
+ (backward-delete-char 1) ; Empty list -- delete both
+ (delete-char 1)) ; delimiters.
+ ;; Just delete a single character, if it's not a closing
+ ;; delimiter. (The character literal case is already handled
+ ;; by now.)
+ ((not (eq (char-syntax (char-after)) ?\) ))
+ (delete-char 1))))
+
+(defun paredit-forward-delete-in-string ()
+ (let ((start+end (paredit-string-start+end-points)))
+ (cond ((not (eq (point) (cdr start+end)))
+ ;; If it's not the close-quote, it's safe to delete. But
+ ;; first handle the case that we're in a string escape.
+ (cond ((paredit-in-string-escape-p)
+ ;; We're right after the backslash, so backward
+ ;; delete it before deleting the escaped character.
+ (backward-delete-char 1))
+ ((eq (char-after) ?\\ )
+ ;; If we're not in a string escape, but we are on a
+ ;; backslash, it must start the escape for the next
+ ;; character, so delete the backslash before deleting
+ ;; the next character.
+ (delete-char 1)))
+ (delete-char 1))
+ ((eq (1- (point)) (car start+end))
+ ;; If it is the close-quote, delete only if we're also right
+ ;; past the open-quote (i.e. it's empty), and then delete
+ ;; both quotes. Otherwise we refuse to delete it.
+ (backward-delete-char 1)
+ (delete-char 1)))))
+
+(defun paredit-backward-delete (&optional argument)
+ "Delete a character backward or move backward over a delimiter.
+If on a closing S-expression delimiter, move backward into the
+ S-expression.
+If on an opening S-expression delimiter, refuse to delete unless the
+ S-expression is empty, in which case delete the whole S-expression.
+With a numeric prefix argument N, delete N characters backward.
+With a `C-u' prefix argument, simply delete a character backward,
+ without regard for delimiter balancing."
+ (interactive "P")
+ (cond ((or (consp argument) (bobp))
+ ;++ Should this untabify?
+ (backward-delete-char 1))
+ ((integerp argument)
+ (if (< argument 0)
+ (paredit-forward-delete (- 0 argument))
+ (while (> argument 0)
+ (paredit-backward-delete)
+ (setq argument (- argument 1)))))
+ ((paredit-in-string-p)
+ (paredit-backward-delete-in-string))
+ ((paredit-in-comment-p)
+ (backward-delete-char 1))
+ ((paredit-in-char-p) ; Escape -- delete both chars.
+ (backward-delete-char 1)
+ (delete-char 1))
+ ((paredit-in-char-p (1- (point)))
+ (backward-delete-char 2)) ; ditto
+ ((let ((syn (char-syntax (char-before))))
+ (or (eq syn ?\) )
+ (eq syn ?\" )))
+ (if (save-excursion
+ (paredit-handle-sexp-errors (progn (backward-sexp) t)
+ nil))
+ (backward-char)
+ (message "Deleting spurious closing delimiter.")
+ (backward-delete-char 1)))
+ ((and (eq (char-syntax (char-before)) ?\( )
+ (eq (char-after) (matching-paren (char-before))))
+ (backward-delete-char 1) ; Empty list -- delete both
+ (delete-char 1)) ; delimiters.
+ ;; Delete it, unless it's an opening delimiter. The case of
+ ;; character literals is already handled by now.
+ ((not (eq (char-syntax (char-before)) ?\( ))
+ (backward-delete-char-untabify 1))))
+
+(defun paredit-backward-delete-in-string ()
+ (let ((start+end (paredit-string-start+end-points)))
+ (cond ((not (eq (1- (point)) (car start+end)))
+ ;; If it's not the open-quote, it's safe to delete.
+ (if (paredit-in-string-escape-p)
+ ;; If we're on a string escape, since we're about to
+ ;; delete the backslash, we must first delete the
+ ;; escaped char.
+ (delete-char 1))
+ (backward-delete-char 1)
+ (if (paredit-in-string-escape-p)
+ ;; If, after deleting a character, we find ourselves in
+ ;; a string escape, we must have deleted the escaped
+ ;; character, and the backslash is behind the point, so
+ ;; backward delete it.
+ (backward-delete-char 1)))
+ ((eq (point) (cdr start+end))
+ ;; If it is the open-quote, delete only if we're also right
+ ;; past the close-quote (i.e. it's empty), and then delete
+ ;; both quotes. Otherwise we refuse to delete it.
+ (backward-delete-char 1)
+ (delete-char 1)))))
+
+;;;; Killing
+
+(defun paredit-kill (&optional argument)
+ "Kill a line as if with `kill-line', but respecting delimiters.
+In a string, act exactly as `kill-line' but do not kill past the
+ closing string delimiter.
+On a line with no S-expressions on it starting after the point or
+ within a comment, act exactly as `kill-line'.
+Otherwise, kill all S-expressions that start after the point.
+With a `C-u' prefix argument, just do the standard `kill-line'.
+With a numeric prefix argument N, do `kill-line' that many times."
+ (interactive "P")
+ (cond (argument
+ (kill-line (if (integerp argument) argument 1)))
+ ((paredit-in-string-p)
+ (paredit-kill-line-in-string))
+ ((or (paredit-in-comment-p)
+ (save-excursion
+ (paredit-skip-whitespace t (point-at-eol))
+ (or (eq (char-after) ?\; )
+ (eolp))))
+ ;** Be careful about trailing backslashes.
+ (kill-line))
+ (t (paredit-kill-sexps-on-line))))
+
+(defun paredit-kill-line-in-string ()
+ (if (save-excursion (paredit-skip-whitespace t (point-at-eol))
+ (eolp))
+ (kill-line)
+ (save-excursion
+ ;; Be careful not to split an escape sequence.
+ (if (paredit-in-string-escape-p)
+ (backward-char))
+ (let ((beginning (point)))
+ (while (not (or (eolp)
+ (eq (char-after) ?\" )))
+ (forward-char)
+ ;; Skip past escaped characters.
+ (if (eq (char-before) ?\\ )
+ (forward-char)))
+ (kill-region beginning (point))))))
+
+(defun paredit-kill-sexps-on-line ()
+ (if (paredit-in-char-p) ; Move past the \ and prefix.
+ (backward-char 2)) ; (# in Scheme/CL, ? in elisp)
+ (let ((beginning (point))
+ (eol (point-at-eol)))
+ (let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol)))
+ ;; If we got to the end of the list and it's on the same line,
+ ;; move backward past the closing delimiter before killing. (This
+ ;; allows something like killing the whitespace in ( ).)
+ (if end-of-list-p (progn (up-list) (backward-char)))
+ (if kill-whole-line
+ (paredit-kill-sexps-on-whole-line beginning)
+ (kill-region beginning
+ ;; If all of the S-expressions were on one line,
+ ;; i.e. we're still on that line after moving past
+ ;; the last one, kill the whole line, including
+ ;; any comments; otherwise just kill to the end of
+ ;; the last S-expression we found. Be sure,
+ ;; though, not to kill any closing parentheses.
+ (if (and (not end-of-list-p)
+ (eq (point-at-eol) eol))
+ eol
+ (point)))))))
+
+;;; Please do not try to understand this code unless you have a VERY
+;;; good reason to do so. I gave up trying to figure it out well
+;;; enough to explain it, long ago.
+
+(defun paredit-forward-sexps-to-kill (beginning eol)
+ (let ((end-of-list-p nil)
+ (firstp t))
+ ;; Move to the end of the last S-expression that started on this
+ ;; line, or to the closing delimiter if the last S-expression in
+ ;; this list is on the line.
+ (catch 'return
+ (while t
+ ;; This and the `kill-whole-line' business below fix a bug that
+ ;; inhibited any S-expression at the very end of the buffer
+ ;; (with no trailing newline) from being deleted. It's a
+ ;; bizarre fix that I ought to document at some point, but I am
+ ;; too busy at the moment to do so.
+ (if (and kill-whole-line (eobp)) (throw 'return nil))
+ (save-excursion
+ (paredit-handle-sexp-errors (forward-sexp)
+ (up-list)
+ (setq end-of-list-p (eq (point-at-eol) eol))
+ (throw 'return nil))
+ (if (or (and (not firstp)
+ (not kill-whole-line)
+ (eobp))
+ (paredit-handle-sexp-errors
+ (progn (backward-sexp) nil)
+ t)
+ (not (eq (point-at-eol) eol)))
+ (throw 'return nil)))
+ (forward-sexp)
+ (if (and firstp
+ (not kill-whole-line)
+ (eobp))
+ (throw 'return nil))
+ (setq firstp nil)))
+ end-of-list-p))
+
+(defun paredit-kill-sexps-on-whole-line (beginning)
+ (kill-region beginning
+ (or (save-excursion ; Delete trailing indentation...
+ (paredit-skip-whitespace t)
+ (and (not (eq (char-after) ?\; ))
+ (point)))
+ ;; ...or just use the point past the newline, if
+ ;; we encounter a comment.
+ (point-at-eol)))
+ (cond ((save-excursion (paredit-skip-whitespace nil (point-at-bol))
+ (bolp))
+ ;; Nothing but indentation before the point, so indent it.
+ (lisp-indent-line))
+ ((eobp) nil) ; Protect the CHAR-SYNTAX below against NIL.
+ ;; Insert a space to avoid invalid joining if necessary.
+ ((let ((syn-before (char-syntax (char-before)))
+ (syn-after (char-syntax (char-after))))
+ (or (and (eq syn-before ?\) ) ; Separate opposing
+ (eq syn-after ?\( )) ; parentheses,
+ (and (eq syn-before ?\" ) ; string delimiter
+ (eq syn-after ?\" )) ; pairs,
+ (and (memq syn-before '(?_ ?w)) ; or word or symbol
+ (memq syn-after '(?_ ?w))))) ; constituents.
+ (insert " "))))
+
+;;;;; Killing Words
+
+;;; This is tricky and asymmetrical because backward parsing is
+;;; extraordinarily difficult or impossible, so we have to implement
+;;; killing in both directions by parsing forward.
+
+(defun paredit-forward-kill-word ()
+ "Kill a word forward, skipping over intervening delimiters."
+ (interactive)
+ (let ((beginning (point)))
+ (skip-syntax-forward " -")
+ (let* ((parse-state (paredit-current-parse-state))
+ (state (paredit-kill-word-state parse-state 'char-after)))
+ (while (not (or (eobp)
+ (eq ?w (char-syntax (char-after)))))
+ (setq parse-state
+ (progn (forward-char 1) (paredit-current-parse-state))
+;; (parse-partial-sexp (point) (1+ (point))
+;; nil nil parse-state)
+ )
+ (let* ((old-state state)
+ (new-state
+ (paredit-kill-word-state parse-state 'char-after)))
+ (cond ((not (eq old-state new-state))
+ (setq parse-state
+ (paredit-kill-word-hack old-state
+ new-state
+ parse-state))
+ (setq state
+ (paredit-kill-word-state parse-state
+ 'char-after))
+ (setq beginning (point)))))))
+ (goto-char beginning)
+ (kill-word 1)))
+
+(defun paredit-backward-kill-word ()
+ "Kill a word backward, skipping over any intervening delimiters."
+ (interactive)
+ (if (not (or (bobp)
+ (eq (char-syntax (char-before)) ?w)))
+ (let ((end (point)))
+ (backward-word 1)
+ (forward-word 1)
+ (goto-char (min end (point)))
+ (let* ((parse-state (paredit-current-parse-state))
+ (state
+ (paredit-kill-word-state parse-state 'char-before)))
+ (while (and (< (point) end)
+ (progn
+ (setq parse-state
+ (parse-partial-sexp (point) (1+ (point))
+ nil nil parse-state))
+ (or (eq state
+ (paredit-kill-word-state parse-state
+ 'char-before))
+ (progn (backward-char 1) nil)))))
+ (if (and (eq state 'comment)
+ (eq ?\# (char-after (point)))
+ (eq ?\| (char-before (point))))
+ (backward-char 1)))))
+ (backward-kill-word 1))
+
+;;;;;; Word-Killing Auxiliaries
+
+(defun paredit-kill-word-state (parse-state adjacent-char-fn)
+ (cond ((paredit-in-comment-p parse-state) 'comment)
+ ((paredit-in-string-p parse-state) 'string)
+ ((memq (char-syntax (funcall adjacent-char-fn))
+ '(?\( ?\) ))
+ 'delimiter)
+ (t 'other)))
+
+;;; This optionally advances the point past any comment delimiters that
+;;; should probably not be touched, based on the last state change and
+;;; the characters around the point. It returns a new parse state,
+;;; starting from the PARSE-STATE parameter.
+
+(defun paredit-kill-word-hack (old-state new-state parse-state)
+ (cond ((and (not (eq old-state 'comment))
+ (not (eq new-state 'comment))
+ (not (paredit-in-string-escape-p))
+ (eq ?\# (char-before))
+ (eq ?\| (char-after)))
+ (forward-char 1)
+ (paredit-current-parse-state)
+;; (parse-partial-sexp (point) (1+ (point))
+;; nil nil parse-state)
+ )
+ ((and (not (eq old-state 'comment))
+ (eq new-state 'comment)
+ (eq ?\; (char-before)))
+ (skip-chars-forward ";")
+ (paredit-current-parse-state)
+;; (parse-partial-sexp (point) (save-excursion
+;; (skip-chars-forward ";"))
+;; nil nil parse-state)
+ )
+ (t parse-state)))
+
+(defun paredit-copy-as-kill ()
+ "Save in the kill ring the region that `paredit-kill' would kill."
+ (interactive)
+ (save-excursion
+ (if (paredit-in-char-p)
+ (backward-char 2))
+ (let ((beginning (point))
+ (eol (point-at-eol)))
+ (let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol)))
+ (if end-of-list-p (progn (up-list) (backward-char)))
+ (copy-region-as-kill beginning
+ (cond (kill-whole-line
+ (or (save-excursion
+ (paredit-skip-whitespace t)
+ (and (not (eq (char-after) ?\; ))
+ (point)))
+ (point-at-eol)))
+ ((and (not end-of-list-p)
+ (eq (point-at-eol) eol))
+ eol)
+ (t
+ (point))))))))
+
+;;;; Cursor and Screen Movement
+
+(eval-and-compile
+ (defmacro defun-saving-mark (name bvl doc &rest body)
+ `(defun ,name ,bvl
+ ,doc
+ ,(xcond ((paredit-xemacs-p)
+ '(interactive "_"))
+ ((paredit-gnu-emacs-p)
+ '(interactive)))
+ ,@body)))
+
+(defun-saving-mark paredit-forward ()
+ "Move forward an S-expression, or up an S-expression forward.
+If there are no more S-expressions in this one before the closing
+ delimiter, move past that closing delimiter; otherwise, move forward
+ past the S-expression following the point."
+ (paredit-handle-sexp-errors
+ (forward-sexp)
+ ;++ Is it necessary to use UP-LIST and not just FORWARD-CHAR?
+ (if (paredit-in-string-p) (forward-char) (up-list))))
+
+(defun-saving-mark paredit-backward ()
+ "Move backward an S-expression, or up an S-expression backward.
+If there are no more S-expressions in this one before the opening
+ delimiter, move past that opening delimiter backward; otherwise, move
+ move backward past the S-expression preceding the point."
+ (paredit-handle-sexp-errors
+ (backward-sexp)
+ (if (paredit-in-string-p) (backward-char) (backward-up-list))))
+
+;;; Why is this not in lisp.el?
+
+(defun backward-down-list (&optional arg)
+ "Move backward and descend into one level of parentheses.
+With ARG, do this that many times.
+A negative argument means move forward but still descend a level."
+ (interactive "p")
+ (down-list (- (or arg 1))))
+
+;;; Thanks to Marco Baringer for suggesting & writing this function.
+
+(defun paredit-recentre-on-sexp (&optional n)
+ "Recentre the screen on the S-expression following the point.
+With a prefix argument N, encompass all N S-expressions forward."
+ (interactive "P")
+ (save-excursion
+ (forward-sexp n)
+ (let ((end-point (point)))
+ (backward-sexp n)
+ (let* ((start-point (point))
+ (start-line (count-lines (point-min) (point)))
+ (lines-on-sexps (count-lines start-point end-point)))
+ (goto-line (+ start-line (/ lines-on-sexps 2)))
+ (recenter)))))
+
+(defun paredit-focus-on-defun ()
+ "Moves display to the top of the definition at point."
+ (interactive)
+ (beginning-of-defun)
+ (recenter 0))
+
+;;;; Depth-Changing Commands: Wrapping, Splicing, & Raising
+
+(defun paredit-wrap-sexp (&optional argument open close)
+ "Wrap the following S-expression.
+If a `C-u' prefix argument is given, wrap all S-expressions following
+ the point until the end of the buffer or of the enclosing list.
+If a numeric prefix argument N is given, wrap N S-expressions.
+Automatically indent the newly wrapped S-expression.
+As a special case, if the point is at the end of a list, simply insert
+ a parenthesis pair, rather than inserting a lone opening delimiter
+ and then signalling an error, in the interest of preserving
+ structure.
+By default OPEN and CLOSE are round delimiters."
+ (interactive "P")
+ (paredit-lose-if-not-in-sexp 'paredit-wrap-sexp)
+ (let ((open (or open ?\( ))
+ (close (or close ?\) )))
+ (paredit-handle-sexp-errors
+ ((lambda (n) (paredit-insert-pair n open close 'goto-char))
+ (cond ((integerp argument) argument)
+ ((consp argument) (paredit-count-sexps-forward))
+ ((paredit-region-active-p) nil)
+ (t 1)))
+ (insert close)
+ (backward-char)))
+ (save-excursion (backward-up-list) (indent-sexp)))
+
+(defun paredit-count-sexps-forward ()
+ (save-excursion
+ (let ((n 0))
+ (paredit-ignore-sexp-errors
+ (while (not (eobp))
+ (forward-sexp)
+ (setq n (+ n 1))))
+ n)))
+
+(defun paredit-yank-pop (&optional argument)
+ "Replace just-yanked text with the next item in the kill ring.
+If this command follows a `yank', just run `yank-pop'.
+If this command follows a `paredit-wrap-sexp', or any other paredit
+ wrapping command (see `paredit-wrap-commands'), run `yank' and
+ reindent the enclosing S-expression.
+If this command is repeated, run `yank-pop' and reindent the enclosing
+ S-expression.
+
+The argument is passed on to `yank' or `yank-pop'; see their
+ documentation for details."
+ (interactive "*p")
+ (cond ((eq last-command 'yank)
+ (yank-pop argument))
+ ((memq last-command paredit-wrap-commands)
+ (yank argument)
+ ;; `yank' futzes with `this-command'.
+ (setq this-command 'paredit-yank-pop)
+ (save-excursion (backward-up-list) (indent-sexp)))
+ ((eq last-command 'paredit-yank-pop)
+ ;; Pretend we just did a `yank', so that we can use
+ ;; `yank-pop' without duplicating its definition.
+ (setq last-command 'yank)
+ (yank-pop argument)
+ ;; Return to our original state.
+ (setq last-command 'paredit-yank-pop)
+ (setq this-command 'paredit-yank-pop)
+ (save-excursion (backward-up-list) (indent-sexp)))
+ (t (error "Last command was not a yank or a wrap: %s" last-command))))
+
+;;; Thanks to Marco Baringer for the suggestion of a prefix argument
+;;; for PAREDIT-SPLICE-SEXP. (I, Taylor R. Campbell, however, still
+;;; implemented it, in case any of you lawyer-folk get confused by the
+;;; remark in the top of the file about explicitly noting code written
+;;; by other people.)
+
+(defun paredit-splice-sexp (&optional argument)
+ "Splice the list that the point is on by removing its delimiters.
+With a prefix argument as in `C-u', kill all S-expressions backward in
+ the current list before splicing all S-expressions forward into the
+ enclosing list.
+With two prefix arguments as in `C-u C-u', kill all S-expressions
+ forward in the current list before splicing all S-expressions
+ backward into the enclosing list.
+With a numerical prefix argument N, kill N S-expressions backward in
+ the current list before splicing the remaining S-expressions into the
+ enclosing list. If N is negative, kill forward.
+Inside a string, unescape all backslashes, or signal an error if doing
+ so would invalidate the buffer's structure."
+ (interactive "P")
+ (if (paredit-in-string-p)
+ (paredit-splice-string argument)
+ (save-excursion
+ (paredit-kill-surrounding-sexps-for-splice argument)
+ (backward-up-list) ; Go up to the beginning...
+ (save-excursion
+ (forward-sexp) ; Go forward an expression, to
+ (backward-delete-char 1)) ; delete the end delimiter.
+ (delete-char 1) ; ...to delete the open char.
+ (paredit-ignore-sexp-errors
+ (backward-up-list) ; Reindent, now that the
+ (indent-sexp))))) ; structure has changed.
+
+(defun paredit-kill-surrounding-sexps-for-splice (argument)
+ (cond ((or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (error "Invalid context for splicing S-expressions."))
+ ((or (not argument) (eq argument 0)) nil)
+ ((or (numberp argument) (eq argument '-))
+ ;; Kill S-expressions before/after the point by saving the
+ ;; point, moving across them, and killing the region.
+ (let* ((argument (if (eq argument '-) -1 argument))
+ (saved (paredit-point-at-sexp-boundary (- argument))))
+ (goto-char saved)
+ (paredit-ignore-sexp-errors (backward-sexp argument))
+ (paredit-hack-kill-region saved (point))))
+ ((consp argument)
+ (let ((v (car argument)))
+ (if (= v 4) ;One `C-u'.
+ ;; Move backward until we hit the open paren; then
+ ;; kill that selected region.
+ (let ((end (point)))
+ (paredit-ignore-sexp-errors
+ (while (not (bobp))
+ (backward-sexp)))
+ (paredit-hack-kill-region (point) end))
+ ;; Move forward until we hit the close paren; then
+ ;; kill that selected region.
+ (let ((beginning (point)))
+ (paredit-ignore-sexp-errors
+ (while (not (eobp))
+ (forward-sexp)))
+ (paredit-hack-kill-region beginning (point))))))
+ (t (error "Bizarre prefix argument `%s'." argument))))
+
+(defun paredit-splice-sexp-killing-backward (&optional n)
+ "Splice the list the point is on by removing its delimiters, and
+ also kill all S-expressions before the point in the current list.
+With a prefix argument N, kill only the preceding N S-expressions."
+ (interactive "P")
+ (paredit-splice-sexp (if n
+ (prefix-numeric-value n)
+ '(4))))
+
+(defun paredit-splice-sexp-killing-forward (&optional n)
+ "Splice the list the point is on by removing its delimiters, and
+ also kill all S-expressions after the point in the current list.
+With a prefix argument N, kill only the following N S-expressions."
+ (interactive "P")
+ (paredit-splice-sexp (if n
+ (- (prefix-numeric-value n))
+ '(16))))
+
+(defun paredit-raise-sexp (&optional n)
+ "Raise the following S-expression in a tree, deleting its siblings.
+With a prefix argument N, raise the following N S-expressions. If N
+ is negative, raise the preceding N S-expressions.
+If the point is on an S-expression, such as a string or a symbol, not
+ between them, that S-expression is considered to follow the point."
+ (interactive "p")
+ (save-excursion
+ (cond ((paredit-in-string-p)
+ (goto-char (car (paredit-string-start+end-points))))
+ ((paredit-in-char-p)
+ (backward-sexp))
+ ((paredit-in-comment-p)
+ (error "No S-expression to raise in comment.")))
+ ;; Select the S-expressions we want to raise in a buffer substring.
+ (let* ((bound (save-excursion (forward-sexp n) (point)))
+ (sexps (if (and n (< n 0))
+ (buffer-substring bound
+ (paredit-point-at-sexp-end))
+ (buffer-substring (paredit-point-at-sexp-start)