1.1--- a/.bashrc Tue May 14 14:47:50 2024 -0400
1.2+++ b/.bashrc Sat Jun 01 23:43:29 2024 -0400
1.3@@ -5,46 +5,38 @@
1.4 export LISP='sbcl'
1.5 export ESHELL='/usr/bin/bash'
1.6 export ORGANIZATION='The Compiler Company'
1.7-export TERM='xterm-256color'
1.8 export MANPATH="/usr/local/man:$MANPATH"
1.9 export LANG=en_US.UTF-8
1.10 export ALTERNATE_EDITOR=''
1.11-export EDITOR='emacsclient -t -a='
1.12-export VISUAL='emacsclient -c'
1.13+export EDITOR='emacsclient -a='
1.14 # sudo pacman -Sy seahorse libgnome-keyring libsecret
1.15 #export SSH_ASKPASS=/usr/lib/seahorse/ssh-askpass
1.16 # git config --global credential.helper /usr/lib/git-core/git-credential-libsecret
1.17 export XDG_CONFIG_HOME=$HOME/.config
1.18 export XDG_CACHE_HOME=$HOME/.cache
1.19-export XDG_DATA_HOME=$HOME/.data
1.20-export XDG_STATE_HOME=$HOME/.state
1.21-export XDG_STATE_HOME=$HOME/.state
1.22-export XDG_DESKTOP_DIR=$HOME/Desktop
1.23-export XDG_DOCUMENTS_DIR=$HOME/stash/docs
1.24-export XDG_DOWNLOAD_DIR=$HOME/stash/dl
1.25-export XDG_MUSIC_DIR=$HOME/media/music
1.26-export XDG_PICTURES_DIR=$HOME/media/pictures
1.27-export XDG_PUBLICSHARE_DIR=$HOME/stash/public
1.28-export XDG_TEMPLATES_DIR=$HOME/stash/templates
1.29-export XDG_VIDEOS_DIR=$HOME/media/videos
1.30-# custom configs
1.31-export FREESOUND_CONFIG="~/.config/freesound.json"
1.32-export SSH_AUTH_SOCK="${XDG_RUNTIME_DIR}/ssh-agent.socket"
1.33+
1.34 # aliases
1.35 eman() {
1.36- emacsclient -c -e "(man \"$1\")"
1.37+ emacsclient -t -e "(man \"$1\")" -a=
1.38 }
1.39
1.40+eww() {
1.41+ emacsclient -t -e '(eww-browse-url "'"$1"'")' -a=
1.42+}
1.43+alias em='emacsclient -a='
1.44 alias ec='emacsclient -c -a='
1.45 alias et='emacsclient -t -a='
1.46-alias lr='rlwrap sbcl'
1.47+alias skm='skel make'
1.48+alias hmi='homer install'
1.49+alias lisp='rlwrap sbcl'
1.50 alias hgpu='hg pull -u'
1.51 alias hgc='hg ci -m'
1.52 alias hgp='hg push'
1.53-alias hgfe='hg-fast-export.sh'
1.54+
1.55 # VCS
1.56 alias hgsub='find . -name ".hg" -type d | grep -v "\./\.hg" | xargs -n1 dirname | xargs -iREPO hg -R REPO'
1.57
1.58 alias q='QHOME=~/q rlwrap -r ~/q/l64/q'
1.59 alias ..='cd ..'
1.60+
1.61 complete -c man which
2.1--- a/.config/systemd/user/default.target.wants/emacs.service Tue May 14 14:47:50 2024 -0400
2.2+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2.3@@ -1,19 +0,0 @@
2.4-[Unit]
2.5-Description=Emacs text editor
2.6-Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
2.7-
2.8-[Service]
2.9-Type=notify
2.10-ExecStart=/usr/local/bin/emacs --fg-daemon
2.11-
2.12-# Emacs will exit with status 15 after having received SIGTERM, which
2.13-# is the default "KillSignal" value systemd uses to stop services.
2.14-SuccessExitStatus=15
2.15-
2.16-# The location of the SSH auth socket varies by distribution, and some
2.17-# set it from PAM, so don't override by default.
2.18-# Environment=SSH_AUTH_SOCK=%t/keyring/ssh
2.19-Restart=on-failure
2.20-
2.21-[Install]
2.22-WantedBy=default.target
3.1--- a/.emacs.d/ellis.el Tue May 14 14:47:50 2024 -0400
3.2+++ b/.emacs.d/ellis.el Sat Jun 01 23:43:29 2024 -0400
3.3@@ -25,14 +25,14 @@
3.4 ;;; Code:
3.5 (require 'inbox)
3.6 (require 'sk)
3.7-(require 'slime-cape)
3.8+;; (require 'slime-cape)
3.9 (require 'sxp)
3.10 (require 'ulang)
3.11
3.12 (defalias 'make #'compile)
3.13
3.14 (setopt default-theme 'modus-vivendi-tritanopia
3.15- user-lab-directory (join-paths user-home-directory "dev")
3.16+ user-lab-directory (join-paths user-home-directory "lab")
3.17 company-source-directory (join-paths user-lab-directory "comp"))
3.18
3.19 (unless (display-graphic-p) (setq default-theme 'wheatgrass))
3.20@@ -53,8 +53,9 @@
3.21 (keymap-set emacs-lisp-mode-map "C-c C-l" #'load-file)
3.22 (keymap-set emacs-lisp-mode-map "C-c M-k" #'elisp-byte-compile-file)
3.23
3.24-;; (add-hook 'common-lisp-mode-hook #'enable-paredit-mode)
3.25-;; (add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode)
3.26+(require 'paredit)
3.27+(add-hook 'common-lisp-mode-hook #'enable-paredit-mode)
3.28+(add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode)
3.29
3.30 (repeat-mode)
3.31
3.32@@ -273,14 +274,17 @@
3.33 (async-shell-command
3.34 "etags ./*.el \\
3.35 ./lib/*.el \\
3.36-~/dev/comp/org/*.el \\
3.37-~/dev/comp/core/emacs/*.el \\
3.38-~/dev/comp/core/emacs/lib/*.el \\
3.39+~/comp/org/*.el \\
3.40+~/comp/core/emacs/*.el \\
3.41+~/comp/core/emacs/lib/*.el \\
3.42 -o TAGS")))
3.43
3.44 (unless (string-equal "hyde" system-name)
3.45 (add-hook 'dired-mode-hook #'all-the-icons-dired-mode)
3.46 (add-hook 'ibuffer-mode-hook #'all-the-icons-ibuffer-mode))
3.47
3.48+;; strangerdanger
3.49+(setq slime-enable-evaluate-in-emacs t)
3.50+
3.51 (provide 'ellis)
3.52 ;;; ellis.el ends here
4.1--- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2+++ b/.emacs.d/lib/paredit.el Sat Jun 01 23:43:29 2024 -0400
4.3@@ -0,0 +1,3098 @@
4.4+;;; paredit.el --- minor mode for editing parentheses -*- Mode: Emacs-Lisp -*-
4.5+
4.6+;; Copyright (C) 2005--2023 Taylor R. Campbell
4.7+
4.8+;; Author: Taylor R. Campbell <campbell@paredit.org>
4.9+;; Version: 27beta
4.10+;; Created: 2005-07-31
4.11+;; Keywords: lisp
4.12+;; URL: https://paredit.org
4.13+
4.14+;; Paredit is free software: you can redistribute it and/or modify it
4.15+;; under the terms of the GNU General Public License as published by
4.16+;; the Free Software Foundation, either version 3 of the License, or
4.17+;; (at your option) any later version.
4.18+;;
4.19+;; Paredit is distributed in the hope that it will be useful, but
4.20+;; WITHOUT ANY WARRANTY; without even the implied warranty of
4.21+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.22+;; GNU General Public License for more details.
4.23+;;
4.24+;; You should have received a copy of the GNU General Public License
4.25+;; along with paredit. If not, see <http://www.gnu.org/licenses/>.
4.26+
4.27+;;; Paredit - https://paredit.org
4.28+;;;
4.29+;;; Latest release: https://paredit.org/paredit.el
4.30+;;; Current development version: https://paredit.org/paredit-beta.el
4.31+;;; Release notes: https://paredit.org/NEWS
4.32+
4.33+;;; Commentary:
4.34+
4.35+;; Paredit keeps your parentheses balanced while editing. Paredit Mode
4.36+;; binds keys like `(', `)', and `"' to insert or delete parentheses
4.37+;; and string quotes in balanced pairs as you're editing without
4.38+;; getting in your way, augments editing keys like `C-k' to handle
4.39+;; balanced expressions, and provides advanced commands for editing
4.40+;; balanced expressions like splicing and joining while judiciously
4.41+;; keeping the code you're working on indented.
4.42+
4.43+;;; Install paredit by placing `paredit.el' in `/path/to/elisp', a
4.44+;;; directory of your choice, and adding to your .emacs file:
4.45+;;;
4.46+;;; (add-to-list 'load-path "/path/to/elisp")
4.47+;;; (autoload 'enable-paredit-mode "paredit"
4.48+;;; "Turn on pseudo-structural editing of Lisp code."
4.49+;;; t)
4.50+;;;
4.51+;;; Start Paredit Mode on the fly with `M-x enable-paredit-mode RET',
4.52+;;; or always enable it in a major mode `M' (e.g., `lisp') with:
4.53+;;;
4.54+;;; (add-hook 'M-mode-hook 'enable-paredit-mode)
4.55+;;;
4.56+;;; Customize paredit using `eval-after-load':
4.57+;;;
4.58+;;; (eval-after-load 'paredit
4.59+;;; '(progn
4.60+;;; (define-key paredit-mode-map (kbd "ESC M-A-C-s-)")
4.61+;;; 'paredit-dwim)))
4.62+;;;
4.63+;;; Send questions, bug reports, comments, feature suggestions, &c.,
4.64+;;; via email to the author's surname at paredit.org.
4.65+;;;
4.66+;;; Paredit should run in GNU Emacs 21 or later and XEmacs 21.5.28 or
4.67+;;; later.
4.68+
4.69+;;; The paredit minor mode, Paredit Mode, binds common character keys,
4.70+;;; such as `(', `)', `"', and `\', to commands that carefully insert
4.71+;;; S-expression structures in the buffer:
4.72+;;;
4.73+;;; ( inserts `()', leaving the point in the middle;
4.74+;;; ) moves the point over the next closing delimiter;
4.75+;;; " inserts `""' if outside a string, or inserts an escaped
4.76+;;; double-quote if in the middle of a string, or moves over the
4.77+;;; closing double-quote if at the end of a string; and
4.78+;;; \ prompts for the character to escape, to avoid inserting lone
4.79+;;; backslashes that may break structure.
4.80+;;;
4.81+;;; In comments, these keys insert themselves. If necessary, you can
4.82+;;; insert these characters literally outside comments by pressing
4.83+;;; `C-q' before these keys, in case a mistake has broken the
4.84+;;; structure.
4.85+;;;
4.86+;;; These key bindings are designed so that when typing new code in
4.87+;;; Paredit Mode, you can generally type exactly the same sequence of
4.88+;;; keys you would have typed without Paredit Mode.
4.89+;;;
4.90+;;; Paredit Mode also binds common editing keys, such as `DEL', `C-d',
4.91+;;; and `C-k', to commands that respect S-expression structures in the
4.92+;;; buffer:
4.93+;;;
4.94+;;; DEL deletes the previous character, unless it is a delimiter: DEL
4.95+;;; will move the point backward over a closing delimiter, and
4.96+;;; will delete a delimiter pair together if between an open and
4.97+;;; closing delimiter;
4.98+;;;
4.99+;;; C-d deletes the next character in much the same manner; and
4.100+;;;
4.101+;;; C-k kills all S-expressions that begin anywhere between the point
4.102+;;; and the end of the line or the closing delimiter of the
4.103+;;; enclosing list, whichever is first.
4.104+;;;
4.105+;;; If necessary, you can delete a character, kill a line, &c.,
4.106+;;; irrespective of S-expression structure, by pressing `C-u' before
4.107+;;; these keys, in case a mistake has broken the structure.
4.108+;;;
4.109+;;; Finally, Paredit Mode binds some keys to complex S-expression
4.110+;;; editing operations. For example, `C-<right>' makes the enclosing
4.111+;;; list slurp up an S-expression to its right (here `|' denotes the
4.112+;;; point):
4.113+;;;
4.114+;;; (foo (bar | baz) quux) C-<right> (foo (bar | baz quux))
4.115+;;;
4.116+;;; Note: Paredit Mode is not compatible with Electric Indent Mode.
4.117+;;; Use one or the other, not both. If you want RET to auto-indent and
4.118+;;; C-j to just insert newline in Paredit Mode, simply rebind the keys
4.119+;;; with the following fragment in your .emacs file:
4.120+;;;
4.121+;;; (eval-after-load 'paredit
4.122+;;; '(progn
4.123+;;; (define-key paredit-mode-map (kbd "RET") 'paredit-newline)
4.124+;;; (define-key paredit-mode-map (kbd "C-j") nil)))
4.125+;;;
4.126+;;; Some paredit commands automatically reindent code. When they do,
4.127+;;; they try to indent as locally as possible, to avoid interfering
4.128+;;; with any indentation you might have manually written. Only the
4.129+;;; advanced S-expression manipulation commands automatically reindent,
4.130+;;; and only the forms that they immediately operated upon (and their
4.131+;;; subforms).
4.132+;;;
4.133+;;; This code is written for clarity, not efficiency. It frequently
4.134+;;; walks over S-expressions redundantly. If you have problems with
4.135+;;; the time it takes to execute some of the commands, let me know.
4.136+
4.137+;;; This assumes Unix-style LF line endings.
4.138+
4.139+(defconst paredit-version 27)
4.140+(defconst paredit-beta-p t)
4.141+
4.142+(eval-and-compile
4.143+
4.144+ (defun paredit-xemacs-p ()
4.145+ ;; No idea where I got this definition from. Edward O'Connor
4.146+ ;; (hober in #emacs) suggested the current definition.
4.147+ ;; (and (boundp 'running-xemacs)
4.148+ ;; running-xemacs)
4.149+ (featurep 'xemacs))
4.150+
4.151+ (defun paredit-gnu-emacs-p ()
4.152+ ;++ This could probably be improved.
4.153+ (not (paredit-xemacs-p)))
4.154+
4.155+ (defmacro xcond (&rest clauses)
4.156+ "Exhaustive COND.
4.157+Signal an error if no clause matches."
4.158+ `(cond ,@clauses
4.159+ (t (error "XCOND lost."))))
4.160+
4.161+ (defalias 'paredit-warn (if (fboundp 'warn) 'warn 'message))
4.162+
4.163+ (defvar paredit-sexp-error-type
4.164+ (with-temp-buffer
4.165+ (insert "(")
4.166+ (condition-case condition
4.167+ (backward-sexp)
4.168+ (error (if (eq (car condition) 'error)
4.169+ (paredit-warn "%s%s%s%s%s"
4.170+ "Paredit is unable to discriminate"
4.171+ " S-expression parse errors from"
4.172+ " other errors. "
4.173+ " This may cause obscure problems. "
4.174+ " Please upgrade Emacs."))
4.175+ (car condition)))))
4.176+
4.177+ (defmacro paredit-handle-sexp-errors (body &rest handler)
4.178+ `(condition-case ()
4.179+ ,body
4.180+ (,paredit-sexp-error-type ,@handler)))
4.181+
4.182+ (put 'paredit-handle-sexp-errors 'lisp-indent-function 1)
4.183+
4.184+ (defmacro paredit-ignore-sexp-errors (&rest body)
4.185+ `(paredit-handle-sexp-errors (progn ,@body)
4.186+ nil))
4.187+
4.188+ (put 'paredit-ignore-sexp-errors 'lisp-indent-function 0)
4.189+
4.190+ (defmacro paredit-preserving-column (&rest body)
4.191+ "Evaluate BODY and restore point to former column, relative to code.
4.192+Assumes BODY will change only indentation.
4.193+If point was on code, it moves with the code.
4.194+If point was on indentation, it stays in indentation."
4.195+ (let ((column (make-symbol "column"))
4.196+ (indentation (make-symbol "indentation")))
4.197+ `(let ((,column (paredit-current-column))
4.198+ (,indentation (paredit-current-indentation)))
4.199+ (let ((value (progn ,@body)))
4.200+ (paredit-restore-column ,column ,indentation)
4.201+ value))))
4.202+
4.203+ (put 'paredit-preserving-column 'lisp-indent-function 0)
4.204+
4.205+ nil)
4.206+
4.207+;;;; Minor Mode Definition
4.208+
4.209+(defvar paredit-lighter " Paredit"
4.210+ "Mode line lighter Paredit Mode.")
4.211+
4.212+(defvar paredit-mode-map (make-sparse-keymap)
4.213+ "Keymap for the paredit minor mode.")
4.214+
4.215+(defvar paredit-override-check-parens-function
4.216+ (lambda (condition) (declare ignore condition) nil)
4.217+ "Function to tell whether unbalanced text should inhibit Paredit Mode.")
4.218+
4.219+;;;###autoload
4.220+(define-minor-mode paredit-mode
4.221+ "Minor mode for pseudo-structurally editing Lisp code.
4.222+With a prefix argument, enable Paredit Mode even if there are
4.223+ unbalanced parentheses in the buffer.
4.224+Paredit behaves badly if parentheses are unbalanced, so exercise
4.225+ caution when forcing Paredit Mode to be enabled, and consider
4.226+ fixing unbalanced parentheses instead.
4.227+\\<paredit-mode-map>"
4.228+ :lighter paredit-lighter
4.229+ ;; Setting `paredit-mode' to false here aborts enabling Paredit Mode.
4.230+ (if (and paredit-mode
4.231+ (not current-prefix-arg))
4.232+ (condition-case condition
4.233+ (check-parens)
4.234+ (error
4.235+ (if (not (funcall paredit-override-check-parens-function condition))
4.236+ (progn (setq paredit-mode nil)
4.237+ (signal (car condition) (cdr condition))))))))
4.238+
4.239+(defun paredit-override-check-parens-interactively (condition)
4.240+ (y-or-n-p (format "Enable Paredit Mode despite condition %S? " condition)))
4.241+
4.242+;;;###autoload
4.243+(defun enable-paredit-mode ()
4.244+ "Turn on pseudo-structural editing of Lisp code."
4.245+ (interactive)
4.246+ (paredit-mode +1))
4.247+
4.248+(defun disable-paredit-mode ()
4.249+ "Turn off pseudo-structural editing of Lisp code."
4.250+ (interactive)
4.251+ (paredit-mode -1))
4.252+
4.253+(defvar paredit-backward-delete-key
4.254+ (xcond ((paredit-xemacs-p) "BS")
4.255+ ((paredit-gnu-emacs-p) "DEL")))
4.256+
4.257+(defvar paredit-forward-delete-keys
4.258+ (xcond ((paredit-xemacs-p) '("DEL"))
4.259+ ((paredit-gnu-emacs-p) '("<delete>" "<deletechar>"))))
4.260+
4.261+;;;; Paredit Keys
4.262+
4.263+;;; Separating the definition and initialization of this variable
4.264+;;; simplifies the development of paredit, since re-evaluating DEFVAR
4.265+;;; forms doesn't actually do anything.
4.266+
4.267+(defvar paredit-commands nil
4.268+ "List of paredit commands with their keys and examples.")
4.269+
4.270+;;; Each specifier is of the form:
4.271+;;; (key[s] function (example-input example-output) ...)
4.272+;;; where key[s] is either a single string suitable for passing to KBD
4.273+;;; or a list of such strings. Entries in this list may also just be
4.274+;;; strings, in which case they are headings for the next entries.
4.275+
4.276+(progn (setq paredit-commands
4.277+ `(
4.278+ "Basic Insertion Commands"
4.279+ ("(" paredit-open-round
4.280+ ("(a b |c d)"
4.281+ "(a b (|) c d)")
4.282+ ("(foo \"bar |baz\" quux)"
4.283+ "(foo \"bar (|baz\" quux)"))
4.284+ (")" paredit-close-round
4.285+ ("(a b |c )" "(a b c)|")
4.286+ ("; Hello,| world!"
4.287+ "; Hello,)| world!"))
4.288+ ("M-)" paredit-close-round-and-newline
4.289+ ("(defun f (x| ))"
4.290+ "(defun f (x)\n |)")
4.291+ ("; (Foo.|"
4.292+ "; (Foo.)|"))
4.293+ ("[" paredit-open-square
4.294+ ("(a b |c d)"
4.295+ "(a b [|] c d)")
4.296+ ("(foo \"bar |baz\" quux)"
4.297+ "(foo \"bar [|baz\" quux)"))
4.298+ ("]" paredit-close-square
4.299+ ("(define-key keymap [frob| ] 'frobnicate)"
4.300+ "(define-key keymap [frob]| 'frobnicate)")
4.301+ ("; [Bar.|"
4.302+ "; [Bar.]|"))
4.303+
4.304+ ("\"" paredit-doublequote
4.305+ ("(frob grovel |full lexical)"
4.306+ "(frob grovel \"|\" full lexical)"
4.307+ "(frob grovel \"\"| full lexical)")
4.308+ ("(foo \"bar |baz\" quux)"
4.309+ "(foo \"bar \\\"|baz\" quux)")
4.310+ ("(frob grovel) ; full |lexical"
4.311+ "(frob grovel) ; full \"|lexical"))
4.312+ ("M-\"" paredit-meta-doublequote
4.313+ ("(foo \"bar |baz\" quux)"
4.314+ "(foo \"bar baz\"| quux)")
4.315+ ("(foo |(bar #\\x \"baz \\\\ quux\") zot)"
4.316+ ,(concat "(foo \"|(bar #\\\\x \\\"baz \\\\"
4.317+ "\\\\ quux\\\")\" zot)")))
4.318+ ("\\" paredit-backslash
4.319+ ("(string #|)\n ; Character to escape: x"
4.320+ "(string #\\x|)")
4.321+ ("\"foo|bar\"\n ; Character to escape: \""
4.322+ "\"foo\\\"|bar\""))
4.323+ (";" paredit-semicolon
4.324+ ("|(frob grovel)"
4.325+ ";|(frob grovel)")
4.326+ ("(frob |grovel)"
4.327+ "(frob ;|grovel\n )")
4.328+ ("(frob |grovel (bloit\n zargh))"
4.329+ "(frob ;|grovel\n (bloit\n zargh))")
4.330+ ("(frob grovel) |"
4.331+ "(frob grovel) ;|"))
4.332+ ("M-;" paredit-comment-dwim
4.333+ ("(foo |bar) ; baz"
4.334+ "(foo bar) ; |baz")
4.335+ ("(frob grovel)|"
4.336+ "(frob grovel) ;|")
4.337+ ("(zot (foo bar)\n|\n (baz quux))"
4.338+ "(zot (foo bar)\n ;; |\n (baz quux))")
4.339+ ("(zot (foo bar) |(baz quux))"
4.340+ "(zot (foo bar)\n ;; |\n (baz quux))")
4.341+ ("|(defun hello-world ...)"
4.342+ ";;; |\n(defun hello-world ...)"))
4.343+
4.344+ (() paredit-newline
4.345+ ("(let ((n (frobbotz))) |(display (+ n 1)\nport))"
4.346+ ,(concat "(let ((n (frobbotz)))"
4.347+ "\n |(display (+ n 1)"
4.348+ "\n port))")))
4.349+ ("RET" paredit-RET)
4.350+ ("C-j" paredit-C-j)
4.351+
4.352+ "Deleting & Killing"
4.353+ (,paredit-forward-delete-keys
4.354+ paredit-forward-delete
4.355+ ("(quu|x \"zot\")" "(quu| \"zot\")")
4.356+ ("(quux |\"zot\")"
4.357+ "(quux \"|zot\")"
4.358+ "(quux \"|ot\")")
4.359+ ("(foo (|) bar)" "(foo | bar)")
4.360+ ("|(foo bar)" "(|foo bar)"))
4.361+ (,paredit-backward-delete-key
4.362+ paredit-backward-delete
4.363+ ("(\"zot\" q|uux)" "(\"zot\" |uux)")
4.364+ ("(\"zot\"| quux)"
4.365+ "(\"zot|\" quux)"
4.366+ "(\"zo|\" quux)")
4.367+ ("(foo (|) bar)" "(foo | bar)")
4.368+ ("(foo bar)|" "(foo bar|)"))
4.369+ ("C-d" paredit-delete-char
4.370+ ("(quu|x \"zot\")" "(quu| \"zot\")")
4.371+ ("(quux |\"zot\")"
4.372+ "(quux \"|zot\")"
4.373+ "(quux \"|ot\")")
4.374+ ("(foo (|) bar)" "(foo | bar)")
4.375+ ("|(foo bar)" "(|foo bar)"))
4.376+ ("C-k" paredit-kill
4.377+ ("(foo bar)| ; Useless comment!"
4.378+ "(foo bar)|")
4.379+ ("(|foo bar) ; Useful comment!"
4.380+ "(|) ; Useful comment!")
4.381+ ("|(foo bar) ; Useless line!"
4.382+ "|")
4.383+ ("(foo \"|bar baz\"\n quux)"
4.384+ "(foo \"|\"\n quux)"))
4.385+ ("M-d" paredit-forward-kill-word
4.386+ ("|(foo bar) ; baz"
4.387+ "(| bar) ; baz"
4.388+ "(|) ; baz"
4.389+ "() ;|")
4.390+ (";;;| Frobnicate\n(defun frobnicate ...)"
4.391+ ";;;|\n(defun frobnicate ...)"
4.392+ ";;;\n(| frobnicate ...)"))
4.393+ (,(concat "M-" paredit-backward-delete-key)
4.394+ paredit-backward-kill-word
4.395+ ("(foo bar) ; baz\n(quux)|"
4.396+ "(foo bar) ; baz\n(|)"
4.397+ "(foo bar) ; |\n()"
4.398+ "(foo |) ; \n()"
4.399+ "(|) ; \n()"))
4.400+
4.401+ "Movement & Navigation"
4.402+ ("C-M-f" paredit-forward
4.403+ ("(foo |(bar baz) quux)"
4.404+ "(foo (bar baz)| quux)")
4.405+ ("(foo (bar)|)"
4.406+ "(foo (bar))|"))
4.407+ ("C-M-b" paredit-backward
4.408+ ("(foo (bar baz)| quux)"
4.409+ "(foo |(bar baz) quux)")
4.410+ ("(|(foo) bar)"
4.411+ "|((foo) bar)"))
4.412+ ("C-M-u" paredit-backward-up)
4.413+ ("C-M-d" paredit-forward-down)
4.414+ ("C-M-p" paredit-backward-down) ; Built-in, these are FORWARD-
4.415+ ("C-M-n" paredit-forward-up) ; & BACKWARD-LIST, which have
4.416+ ; no need given C-M-f & C-M-b.
4.417+
4.418+ "Depth-Changing Commands"
4.419+ ("M-(" paredit-wrap-round
4.420+ ("(foo |bar baz)"
4.421+ "(foo (|bar) baz)"))
4.422+ ("M-s" paredit-splice-sexp
4.423+ ("(foo (bar| baz) quux)"
4.424+ "(foo bar| baz quux)"))
4.425+ (("M-<up>" "ESC <up>")
4.426+ paredit-splice-sexp-killing-backward
4.427+ ("(foo (let ((x 5)) |(sqrt n)) bar)"
4.428+ "(foo |(sqrt n) bar)"))
4.429+ (("M-<down>" "ESC <down>")
4.430+ paredit-splice-sexp-killing-forward
4.431+ ("(a (b c| d e) f)"
4.432+ "(a b c| f)"))
4.433+ ("M-r" paredit-raise-sexp
4.434+ ("(dynamic-wind in (lambda () |body) out)"
4.435+ "(dynamic-wind in |body out)"
4.436+ "|body"))
4.437+ ("M-?" paredit-convolute-sexp
4.438+ ("(let ((x 5) (y 3)) (frob |(zwonk)) (wibblethwop))"
4.439+ "(frob |(let ((x 5) (y 3)) (zwonk) (wibblethwop)))"))
4.440+
4.441+ "Barfage & Slurpage"
4.442+ (("C-)" "C-<right>")
4.443+ paredit-forward-slurp-sexp
4.444+ ("(foo (bar |baz) quux zot)"
4.445+ "(foo (bar |baz quux) zot)")
4.446+ ("(a b ((c| d)) e f)"
4.447+ "(a b ((c| d) e) f)"))
4.448+ (("C-}" "C-<left>")
4.449+ paredit-forward-barf-sexp
4.450+ ("(foo (bar |baz quux) zot)"
4.451+ "(foo (bar |baz) quux zot)"))
4.452+ (("C-(" "C-M-<left>" "ESC C-<left>")
4.453+ paredit-backward-slurp-sexp
4.454+ ("(foo bar (baz| quux) zot)"
4.455+ "(foo (bar baz| quux) zot)")
4.456+ ("(a b ((c| d)) e f)"
4.457+ "(a (b (c| d)) e f)"))
4.458+ (("C-{" "C-M-<right>" "ESC C-<right>")
4.459+ paredit-backward-barf-sexp
4.460+ ("(foo (bar baz |quux) zot)"
4.461+ "(foo bar (baz |quux) zot)"))
4.462+
4.463+ "Miscellaneous Commands"
4.464+ ("M-S" paredit-split-sexp
4.465+ ("(hello| world)"
4.466+ "(hello)| (world)")
4.467+ ("\"Hello, |world!\""
4.468+ "\"Hello, \"| \"world!\""))
4.469+ ("M-J" paredit-join-sexps
4.470+ ("(hello)| (world)"
4.471+ "(hello| world)")
4.472+ ("\"Hello, \"| \"world!\""
4.473+ "\"Hello, |world!\"")
4.474+ ("hello-\n| world"
4.475+ "hello-|world"))
4.476+ ("C-c C-M-l" paredit-recenter-on-sexp)
4.477+ ("M-q" paredit-reindent-defun)
4.478+ ))
4.479+ nil) ; end of PROGN
4.480+
4.481+;;;;; Command Examples
4.482+
4.483+(eval-and-compile
4.484+ (defmacro paredit-do-commands (vars string-case &rest body)
4.485+ (let ((spec (nth 0 vars))
4.486+ (keys (nth 1 vars))
4.487+ (fn (nth 2 vars))
4.488+ (examples (nth 3 vars)))
4.489+ `(dolist (,spec paredit-commands)
4.490+ (if (stringp ,spec)
4.491+ ,string-case
4.492+ (let ((,keys (let ((k (car ,spec)))
4.493+ (cond ((stringp k) (list k))
4.494+ ((listp k) k)
4.495+ (t (error "Invalid paredit command %s."
4.496+ ,spec)))))
4.497+ (,fn (cadr ,spec))
4.498+ (,examples (cddr ,spec)))
4.499+ ,@body)))))
4.500+
4.501+ (put 'paredit-do-commands 'lisp-indent-function 2))
4.502+
4.503+(defun paredit-define-keys ()
4.504+ (paredit-do-commands (spec keys fn examples)
4.505+ nil ; string case
4.506+ (dolist (key keys)
4.507+ (define-key paredit-mode-map (read-kbd-macro key) fn))))
4.508+
4.509+(defun paredit-function-documentation (fn)
4.510+ (let ((original-doc (get fn 'paredit-original-documentation))
4.511+ (doc (documentation fn 'function-documentation)))
4.512+ (or original-doc
4.513+ (progn (put fn 'paredit-original-documentation doc)
4.514+ doc))))
4.515+
4.516+(defun paredit-annotate-mode-with-examples ()
4.517+ (let ((contents
4.518+ (list (paredit-function-documentation 'paredit-mode))))
4.519+ (paredit-do-commands (spec keys fn examples)
4.520+ (push (concat "\n\n" spec "\n")
4.521+ contents)
4.522+ (let ((name (symbol-name fn)))
4.523+ (if (string-match (symbol-name 'paredit-) name)
4.524+ (push (concat "\n\n\\[" name "]\t" name
4.525+ (if examples
4.526+ (mapconcat (lambda (example)
4.527+ (concat
4.528+ "\n"
4.529+ (mapconcat 'identity
4.530+ example
4.531+ "\n --->\n")
4.532+ "\n"))
4.533+ examples
4.534+ "")
4.535+ "\n (no examples)\n"))
4.536+ contents))))
4.537+ (put 'paredit-mode 'function-documentation
4.538+ (apply 'concat (reverse contents))))
4.539+ ;; PUT returns the huge string we just constructed, which we don't
4.540+ ;; want it to return.
4.541+ nil)
4.542+
4.543+(defun paredit-annotate-functions-with-examples ()
4.544+ (paredit-do-commands (spec keys fn examples)
4.545+ nil ; string case
4.546+ (put fn 'function-documentation
4.547+ (concat (paredit-function-documentation fn)
4.548+ "\n\n\\<paredit-mode-map>\\[" (symbol-name fn) "]\n"
4.549+ (mapconcat (lambda (example)
4.550+ (concat "\n"
4.551+ (mapconcat 'identity
4.552+ example
4.553+ "\n ->\n")
4.554+ "\n"))
4.555+ examples
4.556+ "")))))
4.557+
4.558+;;;;; HTML Examples
4.559+
4.560+(defun paredit-insert-html-examples ()
4.561+ "Insert HTML for a paredit quick reference table."
4.562+ (interactive)
4.563+ (let ((insert-lines
4.564+ (lambda (&rest lines) (dolist (line lines) (insert line) (newline))))
4.565+ (initp nil))
4.566+ (paredit-do-commands (spec keys fn examples)
4.567+ (progn (if initp
4.568+ (funcall insert-lines "</table>")
4.569+ (setq initp t))
4.570+ (funcall insert-lines (concat "<h3>" spec "</h3>"))
4.571+ (funcall insert-lines "<table>"))
4.572+ (let ((name (symbol-name fn))
4.573+ (keys
4.574+ (mapconcat (lambda (key)
4.575+ (concat "<tt>" (paredit-html-quote key) "</tt>"))
4.576+ keys
4.577+ ", ")))
4.578+ (funcall insert-lines "<tr>")
4.579+ (funcall insert-lines (concat " <th align=\"left\">" keys "</th>"))
4.580+ (funcall insert-lines (concat " <th align=\"left\">" name "</th>"))
4.581+ (funcall insert-lines "</tr>")
4.582+ (funcall insert-lines
4.583+ "<tr><td colspan=\"2\"><table cellpadding=\"5\"><tr>")
4.584+ (dolist (example examples)
4.585+ (let ((prefix "<td><table border=\"1\"><tr><td><table><tr><td><pre>")
4.586+ (examples
4.587+ (mapconcat 'paredit-html-quote
4.588+ example
4.589+ (concat "</pre></td></tr>"
4.590+ "<tr><th>↓</th></tr>"
4.591+ "<tr><td><pre>")))
4.592+ (suffix "</pre></td></tr></table></td></tr></table></td>"))
4.593+ (funcall insert-lines (concat prefix examples suffix))))
4.594+ (funcall insert-lines "</tr></table></td></tr>")))
4.595+ (funcall insert-lines "</table>")))
4.596+
4.597+(defun paredit-html-quote (string)
4.598+ (with-temp-buffer
4.599+ (dotimes (i (length string))
4.600+ (insert (let ((c (elt string i)))
4.601+ (cond ((eq c ?\<) "<")
4.602+ ((eq c ?\>) ">")
4.603+ ((eq c ?\&) "&")
4.604+ ((eq c ?\') "'")
4.605+ ((eq c ?\") """)
4.606+ (t c)))))
4.607+ (buffer-string)))
4.608+
4.609+;;;; Delimiter Insertion
4.610+
4.611+(eval-and-compile
4.612+ (defun paredit-conc-name (&rest strings)
4.613+ (intern (apply 'concat strings)))
4.614+
4.615+ (defmacro define-paredit-pair (open close name)
4.616+ `(progn
4.617+ (defun ,(paredit-conc-name "paredit-open-" name) (&optional n)
4.618+ ,(concat "Insert a balanced " name " pair.
4.619+With a prefix argument N, put the closing " name " after N
4.620+ S-expressions forward.
4.621+If the region is active, `transient-mark-mode' is enabled, and the
4.622+ region's start and end fall in the same parenthesis depth, insert a
4.623+ " name " pair around the region.
4.624+If in a string or a comment, insert a single " name ".
4.625+If in a character literal, do nothing. This prevents changing what was
4.626+ in the character literal to a meaningful delimiter unintentionally.")
4.627+ (interactive "P")
4.628+ (cond ((or (paredit-in-string-p)
4.629+ (paredit-in-comment-p))
4.630+ (insert ,open))
4.631+ ((not (paredit-in-char-p))
4.632+ (paredit-insert-pair n ,open ,close 'goto-char)
4.633+ (save-excursion (backward-up-list) (indent-sexp)))))
4.634+ (defun ,(paredit-conc-name "paredit-close-" name) ()
4.635+ ,(concat "Move past one closing delimiter and reindent.
4.636+\(Agnostic to the specific closing delimiter.)
4.637+If in a string or comment, insert a single closing " name ".
4.638+If in a character literal, do nothing. This prevents changing what was
4.639+ in the character literal to a meaningful delimiter unintentionally.")
4.640+ (interactive)
4.641+ (paredit-move-past-close ,close))
4.642+ (defun ,(paredit-conc-name "paredit-close-" name "-and-newline") ()
4.643+ ,(concat "Move past one closing delimiter, add a newline,"
4.644+ " and reindent.
4.645+If there was a margin comment after the closing delimiter, preserve it
4.646+ on the same line.")
4.647+ (interactive)
4.648+ (paredit-move-past-close-and-newline ,close))
4.649+ (defun ,(paredit-conc-name "paredit-wrap-" name)
4.650+ (&optional argument)
4.651+ ,(concat "Wrap the following S-expression.
4.652+See `paredit-wrap-sexp' for more details.")
4.653+ (interactive "P")
4.654+ (paredit-wrap-sexp argument ,open ,close))
4.655+ (add-to-list 'paredit-wrap-commands
4.656+ ',(paredit-conc-name "paredit-wrap-" name)))))
4.657+
4.658+(defvar paredit-wrap-commands '(paredit-wrap-sexp)
4.659+ "List of paredit commands that wrap S-expressions.
4.660+Used by `paredit-yank-pop'; for internal paredit use only.")
4.661+
4.662+(define-paredit-pair ?\( ?\) "round")
4.663+(define-paredit-pair ?\[ ?\] "square")
4.664+(define-paredit-pair ?\{ ?\} "curly")
4.665+(define-paredit-pair ?\< ?\> "angled")
4.666+
4.667+;;; Aliases for the old names.
4.668+
4.669+(defalias 'paredit-open-parenthesis 'paredit-open-round)
4.670+(defalias 'paredit-close-parenthesis 'paredit-close-round)
4.671+(defalias 'paredit-close-parenthesis-and-newline
4.672+ 'paredit-close-round-and-newline)
4.673+
4.674+(defalias 'paredit-open-bracket 'paredit-open-square)
4.675+(defalias 'paredit-close-bracket 'paredit-close-square)
4.676+(defalias 'paredit-close-bracket-and-newline
4.677+ 'paredit-close-square-and-newline)
4.678+
4.679+(defun paredit-move-past-close (close)
4.680+ (paredit-move-past-close-and close
4.681+ (lambda ()
4.682+ (paredit-blink-paren-match nil))))
4.683+
4.684+(defun paredit-move-past-close-and-newline (close)
4.685+ (paredit-move-past-close-and close
4.686+ (lambda ()
4.687+ (let ((comment.point (paredit-find-comment-on-line)))
4.688+ (newline)
4.689+ (if comment.point
4.690+ (save-excursion
4.691+ (forward-line -1)
4.692+ (end-of-line)
4.693+ (indent-to (cdr comment.point))
4.694+ (insert (car comment.point)))))
4.695+ (lisp-indent-line)
4.696+ (paredit-ignore-sexp-errors (indent-sexp))
4.697+ (paredit-blink-paren-match t))))
4.698+
4.699+(defun paredit-move-past-close-and (close if-moved)
4.700+ (if (or (paredit-in-string-p)
4.701+ (paredit-in-comment-p))
4.702+ (insert close)
4.703+ (if (paredit-in-char-p) (forward-char))
4.704+ (paredit-move-past-close-and-reindent close)
4.705+ (funcall if-moved)))
4.706+
4.707+(defun paredit-find-comment-on-line ()
4.708+ "Find a margin comment on the current line.
4.709+Return nil if there is no such comment or if there is anything but
4.710+ whitespace until such a comment.
4.711+If such a comment exists, delete the comment (including all leading
4.712+ whitespace) and return a cons whose car is the comment as a string
4.713+ and whose cdr is the point of the comment's initial semicolon,
4.714+ relative to the start of the line."
4.715+ (save-excursion
4.716+ (paredit-skip-whitespace t (point-at-eol))
4.717+ (and (eq ?\; (char-after))
4.718+ (not (eq ?\; (char-after (1+ (point)))))
4.719+ (not (or (paredit-in-string-p)
4.720+ (paredit-in-char-p)))
4.721+ (let* ((start ;Move to before the semicolon.
4.722+ (progn (backward-char) (point)))
4.723+ (comment
4.724+ (buffer-substring start (point-at-eol))))
4.725+ (paredit-skip-whitespace nil (point-at-bol))
4.726+ (delete-region (point) (point-at-eol))
4.727+ (cons comment (- start (point-at-bol)))))))
4.728+
4.729+(defun paredit-insert-pair (n open close forward)
4.730+ (let* ((regionp
4.731+ (and (paredit-region-active-p)
4.732+ (paredit-region-safe-for-insert-p)))
4.733+ (end
4.734+ (and regionp
4.735+ (not n)
4.736+ (prog1 (region-end) (goto-char (region-beginning))))))
4.737+ (let ((spacep (paredit-space-for-delimiter-p nil open)))
4.738+ (if spacep (insert " "))
4.739+ (insert open)
4.740+ (save-excursion
4.741+ ;; Move past the desired region.
4.742+ (cond (n
4.743+ (funcall forward
4.744+ (paredit-scan-sexps-hack (point)
4.745+ (prefix-numeric-value n))))
4.746+ (regionp
4.747+ (funcall forward (+ end (if spacep 2 1)))))
4.748+ ;; The string case can happen if we are inserting string
4.749+ ;; delimiters. The comment case may happen by moving to the
4.750+ ;; end of a buffer that has a comment with no trailing newline.
4.751+ (if (and (not (paredit-in-string-p))
4.752+ (paredit-in-comment-p))
4.753+ (newline))
4.754+ (insert close)
4.755+ (if (paredit-space-for-delimiter-p t close)
4.756+ (insert " "))))))
4.757+
4.758+;++ This needs a better name...
4.759+
4.760+(defun paredit-scan-sexps-hack (point n)
4.761+ (save-excursion
4.762+ (goto-char point)
4.763+ (let ((direction (if (< 0 n) +1 -1))
4.764+ (magnitude (abs n))
4.765+ (count 0))
4.766+ (catch 'exit
4.767+ (while (< count magnitude)
4.768+ (let ((p
4.769+ (paredit-handle-sexp-errors (scan-sexps (point) direction)
4.770+ nil)))
4.771+ (if (not p) (throw 'exit nil))
4.772+ (goto-char p))
4.773+ (setq count (+ count 1)))))
4.774+ (point)))
4.775+
4.776+(defun paredit-region-safe-for-insert-p ()
4.777+ (save-excursion
4.778+ (let ((beginning (region-beginning))
4.779+ (end (region-end)))
4.780+ (goto-char beginning)
4.781+ (let* ((beginning-state (paredit-current-parse-state))
4.782+ (end-state
4.783+ (parse-partial-sexp beginning end nil nil beginning-state)))
4.784+ (and (= (nth 0 beginning-state) ; 0. depth in parens
4.785+ (nth 0 end-state))
4.786+ (eq (nth 3 beginning-state) ; 3. non-nil if inside a
4.787+ (nth 3 end-state)) ; string
4.788+ (eq (nth 4 beginning-state) ; 4. comment status, yada
4.789+ (nth 4 end-state))
4.790+ (eq (nth 5 beginning-state) ; 5. t if following char
4.791+ (nth 5 end-state))))))) ; quote
4.792+
4.793+(defvar paredit-space-for-delimiter-predicates nil
4.794+ "List of predicates for whether to put space by delimiter at point.
4.795+Each predicate is a function that is is applied to two arguments, ENDP
4.796+ and DELIMITER, and that returns a boolean saying whether to put a
4.797+ space next to the delimiter -- before/after the delimiter if ENDP is
4.798+ false/true, respectively.
4.799+If any predicate returns false, no space is inserted: every predicate
4.800+ has veto power.
4.801+Each predicate may assume that the point is not at the beginning/end of
4.802+ the buffer, and that the point is preceded/followed by a word
4.803+ constituent, symbol constituent, string quote, or delimiter matching
4.804+ DELIMITER, if ENDP is false/true, respectively.
4.805+Each predicate should examine only text before/after the point if ENDP is
4.806+ false/true, respectively.")
4.807+
4.808+(defun paredit-space-for-delimiter-p (endp delimiter)
4.809+ ;; If at the buffer limit, don't insert a space. If there is a word,
4.810+ ;; symbol, other quote, or non-matching parenthesis delimiter (i.e. a
4.811+ ;; close when want an open the string or an open when we want to
4.812+ ;; close the string), do insert a space.
4.813+ (and (not (if endp (eobp) (bobp)))
4.814+ (memq (char-syntax (if endp (char-after) (char-before)))
4.815+ (list ?w ?_ ?\"
4.816+ (let ((matching (matching-paren delimiter)))
4.817+ (and matching (char-syntax matching)))
4.818+ (and (not endp)
4.819+ (eq ?\" (char-syntax delimiter))
4.820+ ?\) )))
4.821+ (catch 'exit
4.822+ (dolist (predicate paredit-space-for-delimiter-predicates)
4.823+ (if (not (funcall predicate endp delimiter))
4.824+ (throw 'exit nil)))
4.825+ t)))
4.826+
4.827+(defun paredit-move-past-close-and-reindent (close)
4.828+ (let ((open (paredit-missing-close)))
4.829+ (if open
4.830+ (if (eq close (matching-paren open))
4.831+ (save-excursion
4.832+ (message "Missing closing delimiter: %c" close)
4.833+ (insert close))
4.834+ (error "Mismatched missing closing delimiter: %c ... %c"
4.835+ open close))))
4.836+ (up-list)
4.837+ (if (catch 'return ; This CATCH returns T if it
4.838+ (while t ; should delete leading spaces
4.839+ (save-excursion ; and NIL if not.
4.840+ (let ((before-paren (1- (point))))
4.841+ (back-to-indentation)
4.842+ (cond ((not (eq (point) before-paren))
4.843+ ;; Can't call PAREDIT-DELETE-LEADING-WHITESPACE
4.844+ ;; here -- we must return from SAVE-EXCURSION
4.845+ ;; first.
4.846+ (throw 'return t))
4.847+ ((save-excursion (forward-line -1)
4.848+ (end-of-line)
4.849+ (paredit-in-comment-p))
4.850+ ;; Moving the closing delimiter any further
4.851+ ;; would put it into a comment, so we just
4.852+ ;; indent the closing delimiter where it is and
4.853+ ;; abort the loop, telling its continuation that
4.854+ ;; no leading whitespace should be deleted.
4.855+ (lisp-indent-line)
4.856+ (throw 'return nil))
4.857+ (t (delete-indentation)))))))
4.858+ (paredit-delete-leading-whitespace)))
4.859+
4.860+(defun paredit-missing-close ()
4.861+ (save-excursion
4.862+ (paredit-handle-sexp-errors (backward-up-list)
4.863+ (error "Not inside a list."))
4.864+ (let ((open (char-after)))
4.865+ (paredit-handle-sexp-errors (progn (forward-sexp) nil)
4.866+ open))))
4.867+
4.868+(defun paredit-delete-leading-whitespace ()
4.869+ ;; This assumes that we're on the closing delimiter already.
4.870+ (save-excursion
4.871+ (backward-char)
4.872+ (while (let ((syn (char-syntax (char-before))))
4.873+ (and (or (eq syn ?\ ) (eq syn ?-)) ; whitespace syntax
4.874+ ;; The above line is a perfect example of why the
4.875+ ;; following test is necessary.
4.876+ (not (paredit-in-char-p (1- (point))))))
4.877+ (delete-char -1))))
4.878+
4.879+(defun paredit-blink-paren-match (another-line-p)
4.880+ (if (and blink-matching-paren
4.881+ (or (not show-paren-mode) another-line-p))
4.882+ (paredit-ignore-sexp-errors
4.883+ (save-excursion
4.884+ (backward-sexp)
4.885+ (forward-sexp)
4.886+ ;; SHOW-PAREN-MODE inhibits any blinking, so we disable it
4.887+ ;; locally here.
4.888+ (let ((show-paren-mode nil))
4.889+ (blink-matching-open))))))
4.890+
4.891+(defun paredit-doublequote (&optional n)
4.892+ "Insert a pair of double-quotes.
4.893+With a prefix argument N, wrap the following N S-expressions in
4.894+ double-quotes, escaping intermediate characters if necessary.
4.895+If the region is active, `transient-mark-mode' is enabled, and the
4.896+ region's start and end fall in the same parenthesis depth, insert a
4.897+ pair of double-quotes around the region, again escaping intermediate
4.898+ characters if necessary.
4.899+Inside a comment, insert a literal double-quote.
4.900+At the end of a string, move past the closing double-quote.
4.901+In the middle of a string, insert a backslash-escaped double-quote.
4.902+If in a character literal, do nothing. This prevents accidentally
4.903+ changing a what was in the character literal to become a meaningful
4.904+ delimiter unintentionally."
4.905+ (interactive "P")
4.906+ (cond ((paredit-in-string-p)
4.907+ (if (eq (point) (- (paredit-enclosing-string-end) 1))
4.908+ (forward-char) ; Just move past the closing quote.
4.909+ ;; Don't split a \x into an escaped backslash and a string end.
4.910+ (if (paredit-in-string-escape-p) (forward-char))
4.911+ (insert ?\\ ?\" )))
4.912+ ((paredit-in-comment-p)
4.913+ (insert ?\" ))
4.914+ ((not (paredit-in-char-p))
4.915+ (paredit-insert-pair n ?\" ?\" 'paredit-forward-for-quote))))
4.916+
4.917+(defun paredit-meta-doublequote (&optional n)
4.918+ "Move to the end of the string.
4.919+If not in a string, act as `paredit-doublequote'; if no prefix argument
4.920+ is specified and the region is not active or `transient-mark-mode' is
4.921+ disabled, the default is to wrap one S-expression, however, not zero."
4.922+ (interactive "P")
4.923+ (if (not (paredit-in-string-p))
4.924+ (paredit-doublequote (or n (and (not (paredit-region-active-p)) 1)))
4.925+ (goto-char (paredit-enclosing-string-end))))
4.926+
4.927+(defun paredit-meta-doublequote-and-newline (&optional n)
4.928+ "Move to the end of the string, insert a newline, and indent.
4.929+If not in a string, act as `paredit-doublequote'; if no prefix argument
4.930+ is specified and the region is not active or `transient-mark-mode' is
4.931+ disabled, the default is to wrap one S-expression, however, not zero."
4.932+ (interactive "P")
4.933+ (if (not (paredit-in-string-p))
4.934+ (paredit-doublequote (or n (and (not (paredit-region-active-p)) 1)))
4.935+ (progn (goto-char (paredit-enclosing-string-end))
4.936+ (newline)
4.937+ (lisp-indent-line)
4.938+ (paredit-ignore-sexp-errors (indent-sexp)))))
4.939+
4.940+(defun paredit-forward-for-quote (end)
4.941+ (let ((state (paredit-current-parse-state)))
4.942+ (while (< (point) end)
4.943+ (let ((new-state (parse-partial-sexp (point) (1+ (point))
4.944+ nil nil state)))
4.945+ (if (paredit-in-string-p new-state)
4.946+ (if (not (paredit-in-string-escape-p))
4.947+ (setq state new-state)
4.948+ ;; Escape character: turn it into an escaped escape
4.949+ ;; character by appending another backslash.
4.950+ (insert ?\\ )
4.951+ ;; Now the point is after both escapes, and we want to
4.952+ ;; rescan from before the first one to after the second
4.953+ ;; one.
4.954+ (setq state
4.955+ (parse-partial-sexp (- (point) 2) (point)
4.956+ nil nil state))
4.957+ ;; Advance the end point, since we just inserted a new
4.958+ ;; character.
4.959+ (setq end (1+ end)))
4.960+ ;; String: escape by inserting a backslash before the quote.
4.961+ (backward-char)
4.962+ (insert ?\\ )
4.963+ ;; The point is now between the escape and the quote, and we
4.964+ ;; want to rescan from before the escape to after the quote.
4.965+ (setq state
4.966+ (parse-partial-sexp (1- (point)) (1+ (point))
4.967+ nil nil state))
4.968+ ;; Advance the end point for the same reason as above.
4.969+ (setq end (1+ end)))))))
4.970+
4.971+;;;; Escape Insertion
4.972+
4.973+(defun paredit-backslash ()
4.974+ "Insert a backslash followed by a character to escape."
4.975+ (interactive)
4.976+ (cond ((paredit-in-string-p) (paredit-backslash-interactive))
4.977+ ((paredit-in-comment-p) (insert ?\\))
4.978+ ((paredit-in-char-p) (forward-char) (paredit-backslash-interactive))
4.979+ (t (paredit-backslash-interactive))))
4.980+
4.981+(defun paredit-backslash-interactive ()
4.982+ (insert ?\\ )
4.983+ ;; Read a character to insert after the backslash. If anything
4.984+ ;; goes wrong -- the user hits delete (entering the rubout
4.985+ ;; `character'), aborts with C-g, or enters non-character input
4.986+ ;; -- then delete the backslash to avoid a dangling escape.
4.987+ (let ((delete-p t))
4.988+ (unwind-protect
4.989+ (let ((char (read-char "Character to escape: " t)))
4.990+ (if (not (eq char ?\^?))
4.991+ (progn (message "Character to escape: %c" char)
4.992+ (insert char)
4.993+ (setq delete-p nil))))
4.994+ (if delete-p
4.995+ (progn (message "Deleting escape.")
4.996+ (delete-char -1))))))
4.997+
4.998+(defun paredit-newline ()
4.999+ "Insert a newline and indent it.
4.1000+This is like `newline-and-indent', but it not only indents the line
4.1001+ that the point is on but also the S-expression following the point,
4.1002+ if there is one.
4.1003+Move forward one character first if on an escaped character.
4.1004+If in a string, just insert a literal newline.
4.1005+If in a comment and if followed by invalid structure, call
4.1006+ `indent-new-comment-line' to keep the invalid structure in a
4.1007+ comment."
4.1008+ (interactive)
4.1009+ (cond ((paredit-in-string-p)
4.1010+ (newline))
4.1011+ ((paredit-in-comment-p)
4.1012+ (if (paredit-region-ok-p (point) (point-at-eol))
4.1013+ (progn (newline-and-indent)
4.1014+ (paredit-ignore-sexp-errors (indent-sexp)))
4.1015+ (indent-new-comment-line)))
4.1016+ (t
4.1017+ (if (paredit-in-char-p)
4.1018+ (forward-char))
4.1019+ (newline-and-indent)
4.1020+ ;; Indent the following S-expression, but don't signal an
4.1021+ ;; error if there's only a closing delimiter after the point.
4.1022+ (paredit-ignore-sexp-errors (indent-sexp)))))
4.1023+
4.1024+(defun paredit-electric-indent-mode-p ()
4.1025+ "True if Electric Indent Mode is on, false if not.
4.1026+Electric Indent Mode is generally not compatible with paredit and
4.1027+ users are advised to disable it, since paredit does essentially
4.1028+ everything it tries to do better.
4.1029+However, to mitigate the negative user experience of combining
4.1030+ Electric Indent Mode with paredit, the default key bindings for
4.1031+ RET and C-j in paredit are exchanged depending on whether
4.1032+ Electric Indent Mode is enabled."
4.1033+ (and (boundp 'electric-indent-mode)
4.1034+ electric-indent-mode))
4.1035+
4.1036+(defun paredit-RET ()
4.1037+ "Default key binding for RET in Paredit Mode.
4.1038+Normally, inserts a newline, like traditional Emacs RET.
4.1039+With Electric Indent Mode enabled, inserts a newline and indents
4.1040+ the new line, as well as any subexpressions of it on subsequent
4.1041+ lines; see `paredit-newline' for details and examples."
4.1042+ (interactive)
4.1043+ (if (paredit-electric-indent-mode-p)
4.1044+ (let ((electric-indent-mode nil))
4.1045+ (paredit-newline))
4.1046+ (newline)))
4.1047+
4.1048+(defun paredit-C-j ()
4.1049+ "Default key binding for C-j in Paredit Mode.
4.1050+Normally, inserts a newline and indents
4.1051+ the new line, as well as any subexpressions of it on subsequent
4.1052+ lines; see `paredit-newline' for details and examples.
4.1053+With Electric Indent Mode enabled, inserts a newline, like
4.1054+ traditional Emacs RET."
4.1055+ (interactive)
4.1056+ (if (paredit-electric-indent-mode-p)
4.1057+ (let ((electric-indent-mode nil))
4.1058+ (newline))
4.1059+ (paredit-newline)))
4.1060+
4.1061+(defun paredit-reindent-defun (&optional argument)
4.1062+ "Reindent the definition that the point is on.
4.1063+If the point is in a string or a comment, fill the paragraph instead,
4.1064+ and with a prefix argument, justify as well."
4.1065+ (interactive "P")
4.1066+ (if (or (paredit-in-string-p)
4.1067+ (paredit-in-comment-p))
4.1068+ (if (memq fill-paragraph-function '(t nil))
4.1069+ (lisp-fill-paragraph argument)
4.1070+ (funcall fill-paragraph-function argument))
4.1071+ (paredit-preserving-column
4.1072+ (save-excursion
4.1073+ (end-of-defun)
4.1074+ (beginning-of-defun)
4.1075+ (indent-sexp)))))
4.1076+
4.1077+;;;; Comment Insertion
4.1078+
4.1079+(defun paredit-semicolon (&optional n)
4.1080+ "Insert a semicolon.
4.1081+With a prefix argument N, insert N semicolons.
4.1082+If in a string, do just that and nothing else.
4.1083+If in a character literal, move to the beginning of the character
4.1084+ literal before inserting the semicolon.
4.1085+If the enclosing list ends on the line after the point, break the line
4.1086+ after the last S-expression following the point.
4.1087+If a list begins on the line after the point but ends on a different
4.1088+ line, break the line after the last S-expression following the point
4.1089+ before the list."
4.1090+ (interactive "p")
4.1091+ (if (or (paredit-in-string-p) (paredit-in-comment-p))
4.1092+ (insert (make-string (or n 1) ?\; ))
4.1093+ (if (paredit-in-char-p)
4.1094+ (backward-char 2))
4.1095+ (let ((line-break-point (paredit-semicolon-find-line-break-point)))
4.1096+ (if line-break-point
4.1097+ (paredit-semicolon-with-line-break line-break-point (or n 1))
4.1098+ (insert (make-string (or n 1) ?\; ))))))
4.1099+
4.1100+(defun paredit-semicolon-find-line-break-point ()
4.1101+ (and (not (eolp)) ;Implies (not (eobp)).
4.1102+ (let ((eol (point-at-eol)))
4.1103+ (save-excursion
4.1104+ (catch 'exit
4.1105+ (while t
4.1106+ (let ((line-break-point (point)))
4.1107+ (cond ((paredit-handle-sexp-errors (progn (forward-sexp) t)
4.1108+ nil)
4.1109+ ;; Successfully advanced by an S-expression.
4.1110+ ;; If that S-expression started on this line
4.1111+ ;; and ended on another one, break here.
4.1112+ (cond ((not (eq eol (point-at-eol)))
4.1113+ (throw 'exit
4.1114+ (and (save-excursion
4.1115+ (backward-sexp)
4.1116+ (eq eol (point-at-eol)))
4.1117+ line-break-point)))
4.1118+ ((eobp)
4.1119+ (throw 'exit nil))))
4.1120+ ((save-excursion
4.1121+ (paredit-skip-whitespace t (point-at-eol))
4.1122+ (or (eolp) (eobp) (eq (char-after) ?\;)))
4.1123+ ;; Can't move further, but there's no closing
4.1124+ ;; delimiter we're about to clobber -- either
4.1125+ ;; it's on the next line or we're at the end of
4.1126+ ;; the buffer. Don't break the line.
4.1127+ (throw 'exit nil))
4.1128+ (t
4.1129+ ;; Can't move because we hit a delimiter at the
4.1130+ ;; end of this line. Break here.
4.1131+ (throw 'exit line-break-point))))))))))
4.1132+
4.1133+(defun paredit-semicolon-with-line-break (line-break-point n)
4.1134+ (let ((line-break-marker (make-marker)))
4.1135+ (set-marker line-break-marker line-break-point)
4.1136+ (set-marker-insertion-type line-break-marker t)
4.1137+ (insert (make-string (or n 1) ?\; ))
4.1138+ (save-excursion
4.1139+ (goto-char line-break-marker)
4.1140+ (set-marker line-break-marker nil)
4.1141+ (newline)
4.1142+ (lisp-indent-line)
4.1143+ ;; This step is redundant if we are inside a list, but even if we
4.1144+ ;; are at the top level, we want at least to indent whatever we
4.1145+ ;; bumped off the line.
4.1146+ (paredit-ignore-sexp-errors (indent-sexp))
4.1147+ (paredit-indent-sexps))))
4.1148+
4.1149+;;; This is all a horrible, horrible hack, primarily for GNU Emacs 21,
4.1150+;;; in which there is no `comment-or-uncomment-region'.
4.1151+
4.1152+(autoload 'comment-forward "newcomment")
4.1153+(autoload 'comment-normalize-vars "newcomment")
4.1154+(autoload 'comment-region "newcomment")
4.1155+(autoload 'comment-search-forward "newcomment")
4.1156+(autoload 'uncomment-region "newcomment")
4.1157+
4.1158+(defun paredit-initialize-comment-dwim ()
4.1159+ (require 'newcomment)
4.1160+ (if (not (fboundp 'comment-or-uncomment-region))
4.1161+ (defalias 'comment-or-uncomment-region
4.1162+ (lambda (beginning end &optional argument)
4.1163+ (interactive "*r\nP")
4.1164+ (if (save-excursion (goto-char beginning)
4.1165+ (comment-forward (point-max))
4.1166+ (<= end (point)))
4.1167+ (uncomment-region beginning end argument)
4.1168+ (comment-region beginning end argument)))))
4.1169+ (defalias 'paredit-initialize-comment-dwim 'comment-normalize-vars)
4.1170+ (comment-normalize-vars))
4.1171+
4.1172+(defvar paredit-comment-prefix-toplevel ";;; "
4.1173+ "String of prefix for top-level comments aligned at the left margin.")
4.1174+
4.1175+(defvar paredit-comment-prefix-code ";; "
4.1176+ "String of prefix for comments indented at the same depth as code.")
4.1177+
4.1178+(defvar paredit-comment-prefix-margin ";"
4.1179+ "String of prefix for comments on the same line as code in the margin.")
4.1180+
4.1181+(defun paredit-comment-dwim (&optional argument)
4.1182+ "Call the Lisp comment command you want (Do What I Mean).
4.1183+This is like `comment-dwim', but it is specialized for Lisp editing.
4.1184+If transient mark mode is enabled and the mark is active, comment or
4.1185+ uncomment the selected region, depending on whether it was entirely
4.1186+ commented not not already.
4.1187+If there is already a comment on the current line, with no prefix
4.1188+ argument, indent to that comment; with a prefix argument, kill that
4.1189+ comment.
4.1190+Otherwise, insert a comment appropriate for the context and ensure that
4.1191+ any code following the comment is moved to the next line.
4.1192+At the top level, where indentation is calculated to be at column 0,
4.1193+ insert a triple-semicolon comment; within code, where the indentation
4.1194+ is calculated to be non-zero, and on the line there is either no code
4.1195+ at all or code after the point, insert a double-semicolon comment;
4.1196+ and if the point is after all code on the line, insert a single-
4.1197+ semicolon margin comment at `comment-column'."
4.1198+ (interactive "*P")
4.1199+ (paredit-initialize-comment-dwim)
4.1200+ (cond ((paredit-region-active-p)
4.1201+ (comment-or-uncomment-region (region-beginning)
4.1202+ (region-end)
4.1203+ argument))
4.1204+ ((paredit-comment-on-line-p)
4.1205+ (if argument
4.1206+ (comment-kill (if (integerp argument) argument nil))
4.1207+ (comment-indent)))
4.1208+ (t (paredit-insert-comment))))
4.1209+
4.1210+(defun paredit-comment-on-line-p ()
4.1211+ "True if there is a comment on the line following point.
4.1212+This is expected to be called only in `paredit-comment-dwim'; do not
4.1213+ call it elsewhere."
4.1214+ (save-excursion
4.1215+ (beginning-of-line)
4.1216+ (let ((comment-p nil))
4.1217+ ;; Search forward for a comment beginning. If there is one, set
4.1218+ ;; COMMENT-P to true; if not, it will be nil.
4.1219+ (while (progn
4.1220+ (setq comment-p ;t -> no error
4.1221+ (comment-search-forward (point-at-eol) t))
4.1222+ (and comment-p
4.1223+ (or (paredit-in-string-p)
4.1224+ (paredit-in-char-p (1- (point))))))
4.1225+ (forward-char))
4.1226+ comment-p)))
4.1227+
4.1228+(defun paredit-insert-comment ()
4.1229+ (let ((code-after-p
4.1230+ (save-excursion (paredit-skip-whitespace t (point-at-eol))
4.1231+ (not (eolp))))
4.1232+ (code-before-p
4.1233+ (save-excursion (paredit-skip-whitespace nil (point-at-bol))
4.1234+ (not (bolp)))))
4.1235+ (cond ((and (bolp)
4.1236+ (let ((indent
4.1237+ (let ((indent (calculate-lisp-indent)))
4.1238+ (if (consp indent) (car indent) indent))))
4.1239+ (and indent (zerop indent))))
4.1240+ ;; Top-level comment
4.1241+ (if code-after-p (save-excursion (newline)))
4.1242+ (insert paredit-comment-prefix-toplevel))
4.1243+ ((or code-after-p (not code-before-p))
4.1244+ ;; Code comment
4.1245+ (if code-before-p
4.1246+ (newline-and-indent)
4.1247+ (lisp-indent-line))
4.1248+ (insert paredit-comment-prefix-code)
4.1249+ (if code-after-p
4.1250+ (save-excursion
4.1251+ (newline)
4.1252+ (lisp-indent-line)
4.1253+ (paredit-indent-sexps))))
4.1254+ (t
4.1255+ ;; Margin comment
4.1256+ (indent-to comment-column 1) ; 1 -> force one leading space
4.1257+ (insert paredit-comment-prefix-margin)))))
4.1258+
4.1259+;;;; Character Deletion
4.1260+
4.1261+(defun paredit-delete-char (&optional argument)
4.1262+ "Delete a character forward or move forward over a delimiter.
4.1263+If on an opening S-expression delimiter, move forward into the
4.1264+ S-expression.
4.1265+If on a closing S-expression delimiter, refuse to delete unless the
4.1266+ S-expression is empty, in which case delete the whole S-expression.
4.1267+With a numeric prefix argument N, delete N characters forward.
4.1268+With a `C-u' prefix argument, simply delete a character forward,
4.1269+ without regard for delimiter balancing.
4.1270+
4.1271+Like `delete-char', ignores `delete-active-region'."
4.1272+ (interactive "P")
4.1273+ (let ((delete-active-region nil))
4.1274+ (paredit-forward-delete argument)))
4.1275+
4.1276+(defun paredit-delete-active-region-p ()
4.1277+ "True if the region is active and to be deleted."
4.1278+ (and (paredit-region-active-p)
4.1279+ (boundp 'delete-active-region)
4.1280+ (eq delete-active-region t)))
4.1281+
4.1282+(defun paredit-kill-active-region-p ()
4.1283+ "True if the region is active and to be killed."
4.1284+ (and (paredit-region-active-p)
4.1285+ (boundp 'delete-active-region)
4.1286+ (eq delete-active-region 'kill)))
4.1287+
4.1288+(defun paredit-forward-delete (&optional argument)
4.1289+ "Delete a character forward or move forward over a delimiter.
4.1290+If on an opening S-expression delimiter, move forward into the
4.1291+ S-expression.
4.1292+If on a closing S-expression delimiter, refuse to delete unless the
4.1293+ S-expression is empty, in which case delete the whole S-expression.
4.1294+With a numeric prefix argument N, delete N characters forward.
4.1295+With a `C-u' prefix argument, simply delete a character forward,
4.1296+ without regard for delimiter balancing.
4.1297+
4.1298+If `delete-active-region' is enabled and the mark is active and
4.1299+ no prefix argument is specified, act as `paredit-delete-region'
4.1300+ or `paredit-kill-region' as appropriate instead."
4.1301+ (interactive "P")
4.1302+ (cond ((consp argument)
4.1303+ (delete-char +1))
4.1304+ ((integerp argument)
4.1305+ (let ((delete-active-region nil))
4.1306+ (if (< argument 0)
4.1307+ (paredit-backward-delete argument)
4.1308+ (while (> argument 0)
4.1309+ (paredit-forward-delete)
4.1310+ (setq argument (- argument 1))))))
4.1311+ ((paredit-delete-active-region-p)
4.1312+ (paredit-delete-region (region-beginning) (region-end)))
4.1313+ ((paredit-kill-active-region-p)
4.1314+ (paredit-kill-region (region-beginning) (region-end)))
4.1315+ ((eobp)
4.1316+ (delete-char +1))
4.1317+ ((paredit-in-string-p)
4.1318+ (paredit-forward-delete-in-string))
4.1319+ ((paredit-in-comment-p)
4.1320+ (paredit-forward-delete-in-comment))
4.1321+ ((paredit-in-char-p) ; Escape -- delete both chars.
4.1322+ (delete-char -1)
4.1323+ (delete-char +1))
4.1324+ ((eq (char-after) ?\\ ) ; ditto
4.1325+ (delete-char +2))
4.1326+ ((let ((syn (char-syntax (char-after))))
4.1327+ (or (eq syn ?\( )
4.1328+ (eq syn ?\" )))
4.1329+ (if (save-excursion
4.1330+ (paredit-handle-sexp-errors (progn (forward-sexp) t)
4.1331+ nil))
4.1332+ (forward-char)
4.1333+ (message "Deleting spurious opening delimiter.")
4.1334+ (delete-char +1)))
4.1335+ ((and (not (paredit-in-char-p (1- (point))))
4.1336+ (eq (char-syntax (char-after)) ?\) )
4.1337+ (eq (char-before) (matching-paren (char-after))))
4.1338+ (delete-char -1) ; Empty list -- delete both
4.1339+ (delete-char +1)) ; delimiters.
4.1340+ ((eq ?\; (char-after))
4.1341+ (paredit-forward-delete-comment-start))
4.1342+ ((eq (char-syntax (char-after)) ?\) )
4.1343+ (if (paredit-handle-sexp-errors
4.1344+ (save-excursion (forward-char) (backward-sexp) t)
4.1345+ nil)
4.1346+ (message "End of list!")
4.1347+ (progn
4.1348+ (message "Deleting spurious closing delimiter.")
4.1349+ (delete-char +1))))
4.1350+ ;; Just delete a single character, if it's not a closing
4.1351+ ;; delimiter. (The character literal case is already handled
4.1352+ ;; by now.)
4.1353+ (t (delete-char +1))))
4.1354+
4.1355+(defun paredit-forward-delete-in-string ()
4.1356+ (let ((start+end (paredit-string-start+end-points)))
4.1357+ (cond ((not (eq (point) (cdr start+end)))
4.1358+ ;; If it's not the close-quote, it's safe to delete. But
4.1359+ ;; first handle the case that we're in a string escape.
4.1360+ (cond ((paredit-in-string-escape-p)
4.1361+ ;; We're right after the backslash, so backward
4.1362+ ;; delete it before deleting the escaped character.
4.1363+ (delete-char -1))
4.1364+ ((eq (char-after) ?\\ )
4.1365+ ;; If we're not in a string escape, but we are on a
4.1366+ ;; backslash, it must start the escape for the next
4.1367+ ;; character, so delete the backslash before deleting
4.1368+ ;; the next character.
4.1369+ (delete-char +1)))
4.1370+ (delete-char +1))
4.1371+ ((eq (1- (point)) (car start+end))
4.1372+ ;; If it is the close-quote, delete only if we're also right
4.1373+ ;; past the open-quote (i.e. it's empty), and then delete
4.1374+ ;; both quotes. Otherwise we refuse to delete it.
4.1375+ (delete-char -1)
4.1376+ (delete-char +1)))))
4.1377+
4.1378+(defun paredit-check-forward-delete-in-comment ()
4.1379+ ;; Point is in a comment, possibly at eol. We are about to delete
4.1380+ ;; some characters forward; if we are at eol, we are about to delete
4.1381+ ;; the line break. Refuse to do so if if moving the next line into
4.1382+ ;; the comment would break structure.
4.1383+ (if (eolp)
4.1384+ (let ((next-line-start (point-at-bol 2))
4.1385+ (next-line-end (point-at-eol 2)))
4.1386+ (paredit-check-region next-line-start next-line-end))))
4.1387+
4.1388+(defun paredit-forward-delete-in-comment ()
4.1389+ (paredit-check-forward-delete-in-comment)
4.1390+ (delete-char +1))
4.1391+
4.1392+(defun paredit-forward-delete-comment-start ()
4.1393+ ;; Point precedes a comment start (not at eol). Refuse to delete a
4.1394+ ;; comment start if the comment contains unbalanced junk.
4.1395+ (paredit-check-region (+ (point) 1) (point-at-eol))
4.1396+ (delete-char +1))
4.1397+
4.1398+(defun paredit-backward-delete (&optional argument)
4.1399+ "Delete a character backward or move backward over a delimiter.
4.1400+If on a closing S-expression delimiter, move backward into the
4.1401+ S-expression.
4.1402+If on an opening S-expression delimiter, refuse to delete unless the
4.1403+ S-expression is empty, in which case delete the whole S-expression.
4.1404+With a numeric prefix argument N, delete N characters backward.
4.1405+With a `C-u' prefix argument, simply delete a character backward,
4.1406+ without regard for delimiter balancing.
4.1407+
4.1408+If `delete-active-region' is enabled and the mark is active and
4.1409+ no prefix argument is specified, act as `paredit-delete-region'
4.1410+ or `paredit-kill-region' as appropriate instead."
4.1411+ (interactive "P")
4.1412+ (cond ((consp argument)
4.1413+ ;++ Should this untabify?
4.1414+ (delete-char -1))
4.1415+ ((integerp argument)
4.1416+ (let ((delete-active-region nil))
4.1417+ (if (< argument 0)
4.1418+ (paredit-forward-delete (- 0 argument))
4.1419+ (while (> argument 0)
4.1420+ (paredit-backward-delete)
4.1421+ (setq argument (- argument 1))))))
4.1422+ ((paredit-delete-active-region-p)
4.1423+ (paredit-delete-region (region-beginning) (region-end)))
4.1424+ ((paredit-kill-active-region-p)
4.1425+ (paredit-kill-region (region-beginning) (region-end)))
4.1426+ ((bobp)
4.1427+ (delete-char -1))
4.1428+ ((paredit-in-string-p)
4.1429+ (paredit-backward-delete-in-string))
4.1430+ ((paredit-in-comment-p)
4.1431+ (paredit-backward-delete-in-comment))
4.1432+ ((paredit-in-char-p) ; Escape -- delete both chars.
4.1433+ (delete-char -1)
4.1434+ (delete-char +1))
4.1435+ ((paredit-in-char-p (1- (point)))
4.1436+ (delete-char -2)) ; ditto
4.1437+ ((let ((syn (char-syntax (char-before))))
4.1438+ (or (eq syn ?\) )
4.1439+ (eq syn ?\" )))
4.1440+ (if (save-excursion
4.1441+ (paredit-handle-sexp-errors (progn (backward-sexp) t)
4.1442+ nil))
4.1443+ (backward-char)
4.1444+ (message "Deleting spurious closing delimiter.")
4.1445+ (delete-char -1)))
4.1446+ ((and (eq (char-syntax (char-before)) ?\( )
4.1447+ (eq (char-after) (matching-paren (char-before))))
4.1448+ (delete-char -1) ; Empty list -- delete both
4.1449+ (delete-char +1)) ; delimiters.
4.1450+ ((bolp)
4.1451+ (paredit-backward-delete-maybe-comment-end))
4.1452+ ((eq (char-syntax (char-before)) ?\( )
4.1453+ (if (paredit-handle-sexp-errors
4.1454+ (save-excursion (backward-char) (forward-sexp) t)
4.1455+ nil)
4.1456+ (message "Beginning of list!")
4.1457+ (progn
4.1458+ (message "Deleting spurious closing delimiter.")
4.1459+ (delete-char -1))))
4.1460+ ;; Delete it, unless it's an opening delimiter. The case of
4.1461+ ;; character literals is already handled by now.
4.1462+ (t
4.1463+ ;; Turn off the @#&*&!^&(%^ botch in GNU Emacs 24 that changed
4.1464+ ;; `backward-delete-char' and `backward-delete-char-untabify'
4.1465+ ;; semantically so that they delete the region in transient
4.1466+ ;; mark mode.
4.1467+ (let ((delete-active-region nil))
4.1468+ (backward-delete-char-untabify +1)))))
4.1469+
4.1470+(defun paredit-backward-delete-in-string ()
4.1471+ (let ((start+end (paredit-string-start+end-points)))
4.1472+ (cond ((not (eq (1- (point)) (car start+end)))
4.1473+ ;; If it's not the open-quote, it's safe to delete.
4.1474+ (if (paredit-in-string-escape-p)
4.1475+ ;; If we're on a string escape, since we're about to
4.1476+ ;; delete the backslash, we must first delete the
4.1477+ ;; escaped char.
4.1478+ (delete-char +1))
4.1479+ (delete-char -1)
4.1480+ (if (paredit-in-string-escape-p)
4.1481+ ;; If, after deleting a character, we find ourselves in
4.1482+ ;; a string escape, we must have deleted the escaped
4.1483+ ;; character, and the backslash is behind the point, so
4.1484+ ;; backward delete it.
4.1485+ (delete-char -1)))
4.1486+ ((eq (point) (cdr start+end))
4.1487+ ;; If it is the open-quote, delete only if we're also right
4.1488+ ;; past the close-quote (i.e. it's empty), and then delete
4.1489+ ;; both quotes. Otherwise we refuse to delete it.
4.1490+ (delete-char -1)
4.1491+ (delete-char +1)))))
4.1492+
4.1493+(defun paredit-backward-delete-in-comment ()
4.1494+ ;; Point is in a comment, possibly just after the comment start.
4.1495+ ;; Refuse to delete a comment start if the comment contains
4.1496+ ;; unbalanced junk.
4.1497+ (if (save-excursion
4.1498+ (backward-char)
4.1499+ ;; Must call `paredit-in-string-p' before
4.1500+ ;; `paredit-in-comment-p'.
4.1501+ (not (or (paredit-in-string-p) (paredit-in-comment-p))))
4.1502+ (paredit-check-region (point) (point-at-eol)))
4.1503+ (backward-delete-char-untabify +1))
4.1504+
4.1505+(defun paredit-backward-delete-maybe-comment-end ()
4.1506+ ;; Point is at bol, possibly just after a comment end (i.e., the
4.1507+ ;; previous line may have had a line comment). Refuse to delete a
4.1508+ ;; comment end if moving the current line into the previous line's
4.1509+ ;; comment would break structure.
4.1510+ (if (save-excursion
4.1511+ (backward-char)
4.1512+ (and (not (paredit-in-string-p)) (paredit-in-comment-p)))
4.1513+ (paredit-check-region (point-at-eol) (point-at-bol)))
4.1514+ (delete-char -1))
4.1515+
4.1516+;;;; Killing
4.1517+
4.1518+(defun paredit-kill (&optional argument)
4.1519+ "Kill a line as if with `kill-line', but respecting delimiters.
4.1520+In a string, act exactly as `kill-line' but do not kill past the
4.1521+ closing string delimiter.
4.1522+On a line with no S-expressions on it starting after the point or
4.1523+ within a comment, act exactly as `kill-line'.
4.1524+Otherwise, kill all S-expressions that start after the point.
4.1525+With a `C-u' prefix argument, just do the standard `kill-line'.
4.1526+With a numeric prefix argument N, do `kill-line' that many times.
4.1527+
4.1528+If `kill-whole-line' is true, kills the newline character and
4.1529+ indentation on the next line as well.
4.1530+In that case, ensure there is at least one space between the
4.1531+ preceding S-expression and whatever follows on the next line."
4.1532+ (interactive "P")
4.1533+ (cond (argument
4.1534+ (kill-line (if (integerp argument) argument 1)))
4.1535+ ((paredit-in-string-p)
4.1536+ (paredit-kill-line-in-string))
4.1537+ ((paredit-in-comment-p)
4.1538+ (paredit-kill-line-in-comment))
4.1539+ ((save-excursion (paredit-skip-whitespace t (point-at-eol))
4.1540+ (or (eolp) (eq (char-after) ?\; )))
4.1541+ ;** Be careful about trailing backslashes.
4.1542+ (if (paredit-in-char-p)
4.1543+ (backward-char))
4.1544+ (kill-line))
4.1545+ (t (paredit-kill-sexps-on-line))))
4.1546+
4.1547+(defun paredit-kill-line-in-string ()
4.1548+ (if (save-excursion (paredit-skip-whitespace t (point-at-eol))
4.1549+ (eolp))
4.1550+ (kill-line)
4.1551+ (save-excursion
4.1552+ ;; Be careful not to split an escape sequence.
4.1553+ (if (paredit-in-string-escape-p)
4.1554+ (backward-char))
4.1555+ (kill-region (point)
4.1556+ (min (point-at-eol)
4.1557+ (cdr (paredit-string-start+end-points)))))))
4.1558+
4.1559+(defun paredit-kill-line-in-comment ()
4.1560+ ;; The variable `kill-whole-line' is not relevant: the point is in a
4.1561+ ;; comment, and hence not at the beginning of the line.
4.1562+ (paredit-check-forward-delete-in-comment)
4.1563+ (kill-line))
4.1564+
4.1565+(defun paredit-kill-sexps-on-line ()
4.1566+ (if (paredit-in-char-p) ; Move past the \ and prefix.
4.1567+ (backward-char 2)) ; (# in Scheme/CL, ? in elisp)
4.1568+ (let ((beginning (point))
4.1569+ (eol (point-at-eol)))
4.1570+ (let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol)))
4.1571+ ;; If we got to the end of the list and it's on the same line,
4.1572+ ;; move backward past the closing delimiter before killing. (This
4.1573+ ;; allows something like killing the whitespace in ( ).)
4.1574+ (if end-of-list-p (progn (up-list) (backward-char)))
4.1575+ (if kill-whole-line
4.1576+ (paredit-kill-sexps-on-whole-line beginning)
4.1577+ (kill-region beginning
4.1578+ ;; If all of the S-expressions were on one line,
4.1579+ ;; i.e. we're still on that line after moving past
4.1580+ ;; the last one, kill the whole line, including
4.1581+ ;; any comments; otherwise just kill to the end of
4.1582+ ;; the last S-expression we found. Be sure,
4.1583+ ;; though, not to kill any closing parentheses.
4.1584+ (if (and (not end-of-list-p)
4.1585+ (eq (point-at-eol) eol))
4.1586+ eol
4.1587+ (point)))))))
4.1588+
4.1589+;;; Move to the end of the last S-expression that started on this line,
4.1590+;;; or to the closing delimiter if the last S-expression in this list
4.1591+;;; and the closing delimiter both lie on this line. Return true if
4.1592+;;; the closing delimiter of this list is on this line, false if not.
4.1593+;;;
4.1594+;;; beginning is (point), and eol is (point-at-eol). Handling of
4.1595+;;; `kill-whole-line' is trick, and probably kind of broken.
4.1596+
4.1597+(defun paredit-forward-sexps-to-kill (beginning eol)
4.1598+ (let ((end-of-list-p nil) ;Have we hit a closing delimiter on this line?
4.1599+ (firstp t)) ;Is this still the first line?
4.1600+ (catch 'return
4.1601+ (while t
4.1602+ ;; This and the `kill-whole-line' business below fix a bug that
4.1603+ ;; inhibited any S-expression at the very end of the buffer
4.1604+ ;; (with no trailing newline) from being deleted. It's a
4.1605+ ;; bizarre fix that I ought to document at some point, but I am
4.1606+ ;; too busy at the moment to do so.
4.1607+ (if (and kill-whole-line (eobp)) (throw 'return nil))
4.1608+ ;; See if we can move forward, and stay on an S-expression that
4.1609+ ;; started on this line.
4.1610+ (save-excursion
4.1611+ (paredit-handle-sexp-errors (forward-sexp)
4.1612+ ;; Can't move forward -- we must have hit the end of a
4.1613+ ;; list. Stop here, but record whether the closing
4.1614+ ;; delimiter occurred on the starting line.
4.1615+ (up-list)
4.1616+ (setq end-of-list-p (eq (point-at-eol) eol))
4.1617+ (throw 'return nil))
4.1618+ ;; We can move forward. Where did we move to? Stop if:
4.1619+ ;;
4.1620+ ;; (a) we hit the end of the buffer in certain circumstances
4.1621+ ;; (XXX why are these circumstances? necessary according
4.1622+ ;; to tests, need explanation), because forward-sexp
4.1623+ ;; didn't/won't make any progress and we'll get stuck in
4.1624+ ;; a loop; or
4.1625+ ;;
4.1626+ ;; (b) the S-expression we moved to the end to actually
4.1627+ ;; started on line after where we started so it's not
4.1628+ ;; under our jurisdiction.
4.1629+ (if (or (and (not firstp) ;(a)
4.1630+ (not kill-whole-line)
4.1631+ (eobp))
4.1632+ (paredit-handle-sexp-errors ;(b)
4.1633+ (progn (backward-sexp) nil)
4.1634+ t)
4.1635+ (not (eq (point-at-eol) eol)))
4.1636+ (throw 'return nil)))
4.1637+ ;; Determined we can and should move forward. Do so.
4.1638+ (forward-sexp)
4.1639+ ;; In certain other circumstances (XXX need explanation), if we
4.1640+ ;; hit the end of the buffer, stop here; otherwise the next
4.1641+ ;; forward-sexp will fail to make progress and we might get
4.1642+ ;; stuck in a loop.
4.1643+ (if (and firstp
4.1644+ (not kill-whole-line)
4.1645+ (eobp))
4.1646+ (throw 'return nil))
4.1647+ ;; We have made it past one S-expression.
4.1648+ (setq firstp nil)))
4.1649+ end-of-list-p))
4.1650+
4.1651+;;; Handle the actual kill when `kill-whole-line' is enabled.
4.1652+;;;
4.1653+;;; XXX This has various broken edge cases (see the xfails in test.el)
4.1654+;;; and it doesn't make paredit-kill/yank a noop on round-trip, in an
4.1655+;;; attempt to avoid inadvertently joining S-expressions when it
4.1656+;;; deletes the newline. It could use some input and logic from a user
4.1657+;;; who relies on `kill-whole-line' and has a better sense of
4.1658+;;; expectations.
4.1659+
4.1660+(defun paredit-kill-sexps-on-whole-line (beginning)
4.1661+ (kill-region beginning
4.1662+ (or (save-excursion ; Delete trailing indentation...
4.1663+ (paredit-skip-whitespace t)
4.1664+ (and (not (eq (char-after) ?\; ))
4.1665+ (point)))
4.1666+ ;; ...or just use the point past the newline, if
4.1667+ ;; we encounter a comment.
4.1668+ (point-at-eol)))
4.1669+ (cond ((save-excursion (paredit-skip-whitespace nil (point-at-bol))
4.1670+ (bolp))
4.1671+ ;; Nothing but indentation before the point, so indent it.
4.1672+ (lisp-indent-line))
4.1673+ ((eobp) nil) ; Protect the CHAR-SYNTAX below against NIL.
4.1674+ ;; Insert a space to avoid invalid joining if necessary.
4.1675+ ((let ((syn-before (char-syntax (char-before)))
4.1676+ (syn-after (char-syntax (char-after))))
4.1677+ (and (memq syn-before '(?\) ?\" ?_ ?w))
4.1678+ (memq syn-after '(?\( ?\" ?_ ?w))))
4.1679+ (save-excursion (insert " ")))))
4.1680+
4.1681+;;;;; Killing Words
4.1682+
4.1683+;;; This is tricky and asymmetrical because backward parsing is
4.1684+;;; extraordinarily difficult or impossible, so we have to implement
4.1685+;;; killing in both directions by parsing forward.
4.1686+
4.1687+(defun paredit-forward-kill-word (&optional argument)
4.1688+ "Kill a word forward, skipping over intervening delimiters."
4.1689+ (interactive "p")
4.1690+ (let ((argument (or argument 1)))
4.1691+ (if (< argument 0)
4.1692+ (paredit-backward-kill-word (- argument))
4.1693+ (dotimes (i argument)
4.1694+ (let ((beginning (point)))
4.1695+ (skip-syntax-forward " -")
4.1696+ (let* ((parse-state (paredit-current-parse-state))
4.1697+ (state (paredit-kill-word-state parse-state 'char-after)))
4.1698+ (while (not (or (eobp)
4.1699+ (eq ?w (char-syntax (char-after)))))
4.1700+ (setq parse-state
4.1701+ (progn (forward-char 1) (paredit-current-parse-state))
4.1702+ ;; XXX Why did I comment this out?
4.1703+ ;; (parse-partial-sexp (point) (1+ (point))
4.1704+ ;; nil nil parse-state)
4.1705+ )
4.1706+ (let* ((old-state state)
4.1707+ (new-state
4.1708+ (paredit-kill-word-state parse-state 'char-after)))
4.1709+ (cond ((not (eq old-state new-state))
4.1710+ (setq parse-state
4.1711+ (paredit-kill-word-hack old-state
4.1712+ new-state
4.1713+ parse-state))
4.1714+ (setq state
4.1715+ (paredit-kill-word-state parse-state
4.1716+ 'char-after))
4.1717+ (setq beginning (point)))))))
4.1718+ (goto-char beginning)
4.1719+ (kill-word 1))))))
4.1720+
4.1721+(defun paredit-backward-kill-word (&optional argument)
4.1722+ "Kill a word backward, skipping over any intervening delimiters."
4.1723+ (interactive "p")
4.1724+ (let ((argument (or argument 1)))
4.1725+ (if (< argument 0)
4.1726+ (paredit-forward-kill-word (- argument))
4.1727+ (dotimes (i argument)
4.1728+ (if (not (or (bobp)
4.1729+ (eq (char-syntax (char-before)) ?w)))
4.1730+ (let ((end (point)))
4.1731+ (backward-word 1)
4.1732+ (forward-word 1)
4.1733+ (goto-char (min end (point)))
4.1734+ (let* ((parse-state (paredit-current-parse-state))
4.1735+ (state
4.1736+ (paredit-kill-word-state parse-state 'char-before)))
4.1737+ (while (and (< (point) end)
4.1738+ (progn
4.1739+ (setq parse-state
4.1740+ (parse-partial-sexp (point) (1+ (point))
4.1741+ nil nil parse-state))
4.1742+ (or (eq state
4.1743+ (paredit-kill-word-state parse-state
4.1744+ 'char-before))
4.1745+ (progn (backward-char 1) nil)))))
4.1746+ (if (and (eq state 'comment)
4.1747+ (eq ?\# (char-after (point)))
4.1748+ (eq ?\| (char-before (point))))
4.1749+ (backward-char 1)))))
4.1750+ (backward-kill-word 1)))))
4.1751+
4.1752+;;;;;; Word-Killing Auxiliaries
4.1753+
4.1754+(defun paredit-kill-word-state (parse-state adjacent-char-fn)
4.1755+ (cond ((paredit-in-comment-p parse-state) 'comment)
4.1756+ ((paredit-in-string-p parse-state) 'string)
4.1757+ ((memq (char-syntax (funcall adjacent-char-fn))
4.1758+ '(?\( ?\) ))
4.1759+ 'delimiter)
4.1760+ (t 'other)))
4.1761+
4.1762+;;; This optionally advances the point past any comment delimiters that
4.1763+;;; should probably not be touched, based on the last state change and
4.1764+;;; the characters around the point. It returns a new parse state,
4.1765+;;; starting from the PARSE-STATE parameter.
4.1766+
4.1767+(defun paredit-kill-word-hack (old-state new-state parse-state)
4.1768+ (cond ((and (not (eq old-state 'comment))
4.1769+ (not (eq new-state 'comment))
4.1770+ (not (paredit-in-string-escape-p))
4.1771+ (eq ?\# (char-before))
4.1772+ (eq ?\| (char-after)))
4.1773+ (forward-char 1)
4.1774+ (paredit-current-parse-state)
4.1775+;; (parse-partial-sexp (point) (1+ (point))
4.1776+;; nil nil parse-state)
4.1777+ )
4.1778+ ((and (not (eq old-state 'comment))
4.1779+ (eq new-state 'comment)
4.1780+ (eq ?\; (char-before)))
4.1781+ (skip-chars-forward ";")
4.1782+ (paredit-current-parse-state)
4.1783+;; (parse-partial-sexp (point) (save-excursion
4.1784+;; (skip-chars-forward ";"))
4.1785+;; nil nil parse-state)
4.1786+ )
4.1787+ (t parse-state)))
4.1788+
4.1789+(defun paredit-copy-as-kill ()
4.1790+ "Save in the kill ring the region that `paredit-kill' would kill."
4.1791+ (interactive)
4.1792+ (cond ((paredit-in-string-p)
4.1793+ (paredit-copy-as-kill-in-string))
4.1794+ ((paredit-in-comment-p)
4.1795+ (copy-region-as-kill (point) (point-at-eol)))
4.1796+ ((save-excursion (paredit-skip-whitespace t (point-at-eol))
4.1797+ (or (eolp) (eq (char-after) ?\; )))
4.1798+ ;** Be careful about trailing backslashes.
4.1799+ (save-excursion
4.1800+ (if (paredit-in-char-p)
4.1801+ (backward-char))
4.1802+ (copy-region-as-kill (point) (point-at-eol))))
4.1803+ (t (paredit-copy-sexps-as-kill))))
4.1804+
4.1805+(defun paredit-copy-as-kill-in-string ()
4.1806+ (save-excursion
4.1807+ (if (paredit-in-string-escape-p)
4.1808+ (backward-char))
4.1809+ (copy-region-as-kill (point)
4.1810+ (min (point-at-eol)
4.1811+ (cdr (paredit-string-start+end-points))))))
4.1812+
4.1813+(defun paredit-copy-sexps-as-kill ()
4.1814+ (save-excursion
4.1815+ (if (paredit-in-char-p)
4.1816+ (backward-char 2))
4.1817+ (let ((beginning (point))
4.1818+ (eol (point-at-eol)))
4.1819+ (let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol)))
4.1820+ (if end-of-list-p (progn (up-list) (backward-char)))
4.1821+ (copy-region-as-kill beginning
4.1822+ (cond (kill-whole-line
4.1823+ (or (save-excursion
4.1824+ (paredit-skip-whitespace t)
4.1825+ (and (not (eq (char-after) ?\; ))
4.1826+ (point)))
4.1827+ (point-at-eol)))
4.1828+ ((and (not end-of-list-p)
4.1829+ (eq (point-at-eol) eol))
4.1830+ eol)
4.1831+ (t
4.1832+ (point))))))))
4.1833+
4.1834+;;;; Deleting Regions
4.1835+
4.1836+(defun paredit-delete-region (start end)
4.1837+ "Delete the text between point and mark, like `delete-region'.
4.1838+If that text is unbalanced, signal an error instead.
4.1839+With a prefix argument, skip the balance check."
4.1840+ (interactive "r")
4.1841+ (if (and start end (not current-prefix-arg))
4.1842+ (paredit-check-region-for-delete start end))
4.1843+ (setq this-command 'delete-region)
4.1844+ (delete-region start end))
4.1845+
4.1846+(defun paredit-kill-region (start end)
4.1847+ "Kill the text between point and mark, like `kill-region'.
4.1848+If that text is unbalanced, signal an error instead.
4.1849+With a prefix argument, skip the balance check."
4.1850+ (interactive "r")
4.1851+ (if (and start end (not current-prefix-arg))
4.1852+ (paredit-check-region-for-delete start end))
4.1853+ (setq this-command 'kill-region)
4.1854+ (kill-region start end))
4.1855+
4.1856+(defun paredit-check-region-for-delete (start end)
4.1857+ "Signal an error deleting text between START and END is unsafe."
4.1858+ (save-excursion
4.1859+ (goto-char start)
4.1860+ (let* ((start-state (paredit-current-parse-state))
4.1861+ (end-state (parse-partial-sexp start end nil nil start-state)))
4.1862+ (paredit-check-region-for-delete:depth start start-state end end-state)
4.1863+ (paredit-check-region-for-delete:string start start-state end end-state)
4.1864+ (paredit-check-region-for-delete:comment start start-state end end-state)
4.1865+ (paredit-check-region-for-delete:char-quote start start-state
4.1866+ end end-state))))
4.1867+
4.1868+(defun paredit-check-region-for-delete:depth (start start-state end end-state)
4.1869+ (let ((start-depth (nth 0 start-state))
4.1870+ (end-depth (nth 0 end-state)))
4.1871+ (if (not (= start-depth end-depth))
4.1872+ (error "Mismatched parenthesis depth: %S at start, %S at end."
4.1873+ start-depth
4.1874+ end-depth))))
4.1875+
4.1876+(defun paredit-check-region-for-delete:string (start start-state end end-state)
4.1877+ (let ((start-string-p (nth 3 start-state))
4.1878+ (end-string-p (nth 3 end-state)))
4.1879+ (if (not (eq start-string-p end-string-p))
4.1880+ (error "Mismatched string state: start %sin string, end %sin string."
4.1881+ (if start-string-p "" "not ")
4.1882+ (if end-string-p "" "not ")))))
4.1883+
4.1884+(defun paredit-check-region-for-delete:comment
4.1885+ (start start-state end end-state)
4.1886+ (let ((start-comment-state (nth 4 start-state))
4.1887+ (end-comment-state (nth 4 end-state)))
4.1888+ (if (not (or (eq start-comment-state end-comment-state)
4.1889+ ;; If we are moving text into or out of a line
4.1890+ ;; comment, make sure that the text is balanced. (The
4.1891+ ;; comment state may be a number, not t or nil at all,
4.1892+ ;; for nestable comments, which are not handled by
4.1893+ ;; this heuristic (or any of paredit, really).)
4.1894+ (and (or (and (eq start-comment-state nil)
4.1895+ (eq end-comment-state t))
4.1896+ (and (eq start-comment-state t)
4.1897+ (eq end-comment-state nil)))
4.1898+ (save-excursion
4.1899+ (goto-char end)
4.1900+ (paredit-region-ok-p (point) (point-at-eol))))))
4.1901+ (error "Mismatched comment state: %s"
4.1902+ (cond ((and (integerp start-comment-state)
4.1903+ (integerp end-comment-state))
4.1904+ (format "depth %S at start, depth %S at end."
4.1905+ start-comment-state
4.1906+ end-comment-state))
4.1907+ ((integerp start-comment-state)
4.1908+ "start in nested comment, end otherwise.")
4.1909+ ((integerp end-comment-state)
4.1910+ "end in nested comment, start otherwise.")
4.1911+ (start-comment-state
4.1912+ "start in comment, end not in comment.")
4.1913+ (end-comment-state
4.1914+ "end in comment, start not in comment.")
4.1915+ (t
4.1916+ (format "start %S, end %S."
4.1917+ start-comment-state
4.1918+ end-comment-state)))))))
4.1919+
4.1920+(defun paredit-check-region-for-delete:char-quote
4.1921+ (start start-state end end-state)
4.1922+ (let ((start-char-quote (nth 5 start-state))
4.1923+ (end-char-quote (nth 5 end-state)))
4.1924+ (if (not (eq start-char-quote end-char-quote))
4.1925+ (let ((phrase "character quotation"))
4.1926+ (error "Mismatched %s: start %sin %s, end %sin %s."
4.1927+ phrase
4.1928+ (if start-char-quote "" "not ")
4.1929+ phrase
4.1930+ (if end-char-quote "" "not ")
4.1931+ phrase)))))
4.1932+
4.1933+;;;; Point Motion
4.1934+
4.1935+(eval-and-compile
4.1936+ (defmacro defun-motion (name bvl doc &rest body)
4.1937+ `(defun ,name ,bvl
4.1938+ ,doc
4.1939+ ,(xcond ((paredit-xemacs-p)
4.1940+ '(interactive "_"))
4.1941+ ((paredit-gnu-emacs-p)
4.1942+ ;++ Not sure this is sufficient for the `^'.
4.1943+ (if (fboundp 'handle-shift-selection)
4.1944+ '(interactive "^p")
4.1945+ '(interactive "p"))))
4.1946+ ,@body)))
4.1947+
4.1948+(defun-motion paredit-forward (&optional arg)
4.1949+ "Move forward an S-expression, or up an S-expression forward.
4.1950+If there are no more S-expressions in this one before the closing
4.1951+ delimiter, move past that closing delimiter; otherwise, move forward
4.1952+ past the S-expression following the point."
4.1953+ (let ((n (or arg 1)))
4.1954+ (cond ((< 0 n) (dotimes (i n) (paredit-move-forward)))
4.1955+ ((< n 0) (dotimes (i (- n)) (paredit-move-backward))))))
4.1956+
4.1957+(defun-motion paredit-backward (&optional arg)
4.1958+ "Move backward an S-expression, or up an S-expression backward.
4.1959+If there are no more S-expressions in this one before the opening
4.1960+ delimiter, move past that opening delimiter backward; otherwise,
4.1961+ move backward past the S-expression preceding the point."
4.1962+ (let ((n (or arg 1)))
4.1963+ (cond ((< 0 n) (dotimes (i n) (paredit-move-backward)))
4.1964+ ((< n 0) (dotimes (i (- n)) (paredit-move-forward))))))
4.1965+
4.1966+(defun paredit-move-forward ()
4.1967+ (cond ((paredit-in-string-p)
4.1968+ (let ((end (paredit-enclosing-string-end)))
4.1969+ ;; `forward-sexp' and `up-list' may move into the next string
4.1970+ ;; in the buffer. Don't do that; move out of the current one.
4.1971+ (if (paredit-handle-sexp-errors
4.1972+ (progn (paredit-handle-sexp-errors (forward-sexp)
4.1973+ (up-list))
4.1974+ (<= end (point)))
4.1975+ t)
4.1976+ (goto-char end))))
4.1977+ ((paredit-in-char-p)
4.1978+ (forward-char))
4.1979+ (t
4.1980+ (paredit-handle-sexp-errors (forward-sexp)
4.1981+ (up-list)))))
4.1982+
4.1983+(defun paredit-move-backward ()
4.1984+ (cond ((paredit-in-string-p)
4.1985+ (let ((start (paredit-enclosing-string-start)))
4.1986+ (if (paredit-handle-sexp-errors
4.1987+ (progn (paredit-handle-sexp-errors (backward-sexp)
4.1988+ (backward-up-list))
4.1989+ (<= (point) start))
4.1990+ t)
4.1991+ (goto-char start))))
4.1992+ ((paredit-in-char-p)
4.1993+ ;++ Corner case: a buffer of `\|x'. What to do?
4.1994+ (backward-char 2))
4.1995+ (t
4.1996+ (paredit-handle-sexp-errors (backward-sexp)
4.1997+ (backward-up-list)))))
4.1998+
4.1999+;;;; Window Positioning
4.2000+
4.2001+(defalias 'paredit-recentre-on-sexp 'paredit-recenter-on-sexp)
4.2002+
4.2003+(defun paredit-recenter-on-sexp (&optional n)
4.2004+ "Recenter the screen on the S-expression following the point.
4.2005+With a prefix argument N, encompass all N S-expressions forward."
4.2006+ (interactive "P")
4.2007+ (let* ((p (point))
4.2008+ (end-point (progn (forward-sexp n) (point)))
4.2009+ (start-point (progn (goto-char end-point) (backward-sexp n) (point))))
4.2010+ ;; Point is at beginning of first S-expression.
4.2011+ (let ((p-visible nil) (start-visible nil))
4.2012+ (save-excursion
4.2013+ (forward-line (/ (count-lines start-point end-point) 2))
4.2014+ (recenter)
4.2015+ (setq p-visible (pos-visible-in-window-p p))
4.2016+ (setq start-visible (pos-visible-in-window-p start-point)))
4.2017+ (cond ((not start-visible)
4.2018+ ;; Implies (not p-visible). Put the start at the top of
4.2019+ ;; the screen.
4.2020+ (recenter 0))
4.2021+ (p-visible
4.2022+ ;; Go back to p if we can.
4.2023+ (goto-char p))))))
4.2024+
4.2025+(defun paredit-recenter-on-defun ()
4.2026+ "Recenter the screen on the definition at point."
4.2027+ (interactive)
4.2028+ (save-excursion
4.2029+ (beginning-of-defun)
4.2030+ (paredit-recenter-on-sexp)))
4.2031+
4.2032+(defun paredit-focus-on-defun ()
4.2033+ "Moves display to the top of the definition at point."
4.2034+ (interactive)
4.2035+ (beginning-of-defun)
4.2036+ (recenter 0))
4.2037+
4.2038+;;;; Generalized Upward/Downward Motion
4.2039+
4.2040+(defun paredit-up/down (n vertical-direction)
4.2041+ (let ((horizontal-direction (if (< 0 n) +1 -1)))
4.2042+ (while (/= n 0)
4.2043+ (goto-char
4.2044+ (paredit-next-up/down-point horizontal-direction vertical-direction))
4.2045+ (setq n (- n horizontal-direction)))))
4.2046+
4.2047+(defun paredit-next-up/down-point (horizontal-direction vertical-direction)
4.2048+ (let ((state (paredit-current-parse-state))
4.2049+ (scan-lists
4.2050+ (lambda ()
4.2051+ (scan-lists (point) horizontal-direction vertical-direction))))
4.2052+ (cond ((paredit-in-string-p state)
4.2053+ (let ((start+end (paredit-string-start+end-points state)))
4.2054+ (if (< 0 vertical-direction)
4.2055+ (if (< 0 horizontal-direction)
4.2056+ (+ 1 (cdr start+end))
4.2057+ (car start+end))
4.2058+ ;; We could let the user try to descend into lists
4.2059+ ;; within the string, but that would be asymmetric
4.2060+ ;; with the up case, which rises out of the whole
4.2061+ ;; string and not just out of a list within the
4.2062+ ;; string, so this case will just be an error.
4.2063+ (error "Can't descend further into string."))))
4.2064+ ((< 0 vertical-direction)
4.2065+ ;; When moving up, just try to rise up out of the list.
4.2066+ (or (funcall scan-lists)
4.2067+ (buffer-end horizontal-direction)))
4.2068+ ((< vertical-direction 0)
4.2069+ ;; When moving down, look for a string closer than a list,
4.2070+ ;; and use that if we find it.
4.2071+ (let* ((list-start
4.2072+ (paredit-handle-sexp-errors (funcall scan-lists) nil))
4.2073+ (string-start
4.2074+ (paredit-find-next-string-start horizontal-direction
4.2075+ list-start)))
4.2076+ (if (and string-start list-start)
4.2077+ (if (< 0 horizontal-direction)
4.2078+ (min string-start list-start)
4.2079+ (max string-start list-start))
4.2080+ (or string-start
4.2081+ ;; Scan again: this is a kludgey way to report the
4.2082+ ;; error if there really was one.
4.2083+ (funcall scan-lists)
4.2084+ (buffer-end horizontal-direction)))))
4.2085+ (t
4.2086+ (error "Vertical direction must be nonzero in `%s'."
4.2087+ 'paredit-up/down)))))
4.2088+
4.2089+(defun paredit-find-next-string-start (horizontal-direction limit)
4.2090+ (let ((buffer-limit-p (if (< 0 horizontal-direction) 'eobp 'bobp))
4.2091+ (next-char (if (< 0 horizontal-direction) 'char-after 'char-before))
4.2092+ (pastp (if (< 0 horizontal-direction) '> '<)))
4.2093+ (paredit-handle-sexp-errors
4.2094+ (save-excursion
4.2095+ (catch 'exit
4.2096+ (while t
4.2097+ (if (or (funcall buffer-limit-p)
4.2098+ (and limit (funcall pastp (point) limit)))
4.2099+ (throw 'exit nil))
4.2100+ (forward-sexp horizontal-direction)
4.2101+ (save-excursion
4.2102+ (backward-sexp horizontal-direction)
4.2103+ (if (eq ?\" (char-syntax (funcall next-char)))
4.2104+ (throw 'exit (+ (point) horizontal-direction)))))))
4.2105+ nil)))
4.2106+
4.2107+(defun-motion paredit-forward-down (&optional argument)
4.2108+ "Move forward down into a list.
4.2109+With a positive argument, move forward down that many levels.
4.2110+With a negative argument, move backward down that many levels."
4.2111+ (paredit-up/down (or argument +1) -1))
4.2112+
4.2113+(defun-motion paredit-backward-up (&optional argument)
4.2114+ "Move backward up out of the enclosing list.
4.2115+With a positive argument, move backward up that many levels.
4.2116+With a negative argument, move forward up that many levels.
4.2117+If in a string initially, that counts as one level."
4.2118+ (paredit-up/down (- 0 (or argument +1)) +1))
4.2119+
4.2120+(defun-motion paredit-forward-up (&optional argument)
4.2121+ "Move forward up out of the enclosing list.
4.2122+With a positive argument, move forward up that many levels.
4.2123+With a negative argument, move backward up that many levels.
4.2124+If in a string initially, that counts as one level."
4.2125+ (paredit-up/down (or argument +1) +1))
4.2126+
4.2127+(defun-motion paredit-backward-down (&optional argument)
4.2128+ "Move backward down into a list.
4.2129+With a positive argument, move backward down that many levels.
4.2130+With a negative argument, move forward down that many levels."
4.2131+ (paredit-up/down (- 0 (or argument +1)) -1))
4.2132+
4.2133+;;;; Depth-Changing Commands: Wrapping, Splicing, & Raising
4.2134+
4.2135+(defun paredit-wrap-sexp (&optional argument open close)
4.2136+ "Wrap the following S-expression.
4.2137+If a `C-u' prefix argument is given, wrap all S-expressions following
4.2138+ the point until the end of the buffer or of the enclosing list.
4.2139+If a numeric prefix argument N is given, wrap N S-expressions.
4.2140+Automatically indent the newly wrapped S-expression.
4.2141+As a special case, if the point is at the end of a list, simply insert
4.2142+ a parenthesis pair, rather than inserting a lone opening delimiter
4.2143+ and then signalling an error, in the interest of preserving
4.2144+ structure.
4.2145+By default OPEN and CLOSE are round delimiters."
4.2146+ (interactive "P")
4.2147+ (paredit-lose-if-not-in-sexp 'paredit-wrap-sexp)
4.2148+ (let ((open (or open ?\( ))
4.2149+ (close (or close ?\) )))
4.2150+ (paredit-handle-sexp-errors
4.2151+ ((lambda (n) (paredit-insert-pair n open close 'goto-char))
4.2152+ (cond ((integerp argument) argument)
4.2153+ ((consp argument) (paredit-count-sexps-forward))
4.2154+ ((paredit-region-active-p) nil)
4.2155+ (t 1)))
4.2156+ (insert close)
4.2157+ (backward-char)))
4.2158+ (save-excursion (backward-up-list) (indent-sexp)))
4.2159+
4.2160+(defun paredit-yank-pop (&optional argument)
4.2161+ "Replace just-yanked text with the next item in the kill ring.
4.2162+If this command follows a `yank', just run `yank-pop'.
4.2163+If this command follows a `paredit-wrap-sexp', or any other paredit
4.2164+ wrapping command (see `paredit-wrap-commands'), run `yank' and
4.2165+ reindent the enclosing S-expression.
4.2166+If this command is repeated, run `yank-pop' and reindent the enclosing
4.2167+ S-expression.
4.2168+
4.2169+The argument is passed on to `yank' or `yank-pop'; see their
4.2170+ documentation for details."
4.2171+ (interactive "*p")
4.2172+ (cond ((eq last-command 'yank)
4.2173+ (yank-pop argument))
4.2174+ ((memq last-command paredit-wrap-commands)
4.2175+ (yank argument)
4.2176+ ;; `yank' futzes with `this-command'.
4.2177+ (setq this-command 'paredit-yank-pop)
4.2178+ (save-excursion (backward-up-list) (indent-sexp)))
4.2179+ ((eq last-command 'paredit-yank-pop)
4.2180+ ;; Pretend we just did a `yank', so that we can use
4.2181+ ;; `yank-pop' without duplicating its definition.
4.2182+ (setq last-command 'yank)
4.2183+ (yank-pop argument)
4.2184+ ;; Return to our original state.
4.2185+ (setq last-command 'paredit-yank-pop)
4.2186+ (setq this-command 'paredit-yank-pop)
4.2187+ (save-excursion (backward-up-list) (indent-sexp)))
4.2188+ (t (error "Last command was not a yank or a wrap: %s" last-command))))
4.2189+
4.2190+(defun paredit-splice-sexp (&optional argument)
4.2191+ "Splice the list that the point is on by removing its delimiters.
4.2192+With a prefix argument as in `C-u', kill all S-expressions backward in
4.2193+ the current list before splicing all S-expressions forward into the
4.2194+ enclosing list.
4.2195+With two prefix arguments as in `C-u C-u', kill all S-expressions
4.2196+ forward in the current list before splicing all S-expressions
4.2197+ backward into the enclosing list.
4.2198+With a numerical prefix argument N, kill N S-expressions backward in
4.2199+ the current list before splicing the remaining S-expressions into the
4.2200+ enclosing list. If N is negative, kill forward.
4.2201+Inside a string, unescape all backslashes, or signal an error if doing
4.2202+ so would invalidate the buffer's structure."
4.2203+ (interactive "P")
4.2204+ (if (paredit-in-string-p)
4.2205+ (paredit-splice-string argument)
4.2206+ (if (paredit-in-comment-p)
4.2207+ (error "Can't splice comment."))
4.2208+ (paredit-handle-sexp-errors (paredit-enclosing-list-start)
4.2209+ (error "Can't splice top level."))
4.2210+ (paredit-kill-surrounding-sexps-for-splice argument)
4.2211+ (let ((delete-start (paredit-enclosing-list-start))
4.2212+ (delete-end
4.2213+ (let ((limit
4.2214+ (save-excursion
4.2215+ (paredit-ignore-sexp-errors (forward-sexp) (backward-sexp))
4.2216+ (point))))
4.2217+ (save-excursion
4.2218+ (backward-up-list)
4.2219+ (forward-char +1)
4.2220+ (paredit-skip-whitespace t limit)
4.2221+ (point)))))
4.2222+ (let ((end-marker (make-marker)))
4.2223+ (save-excursion
4.2224+ (up-list)
4.2225+ (delete-char -1)
4.2226+ (set-marker end-marker (point)))
4.2227+ (delete-region delete-start delete-end)
4.2228+ (paredit-splice-reindent delete-start (marker-position end-marker))))))
4.2229+
4.2230+(defun paredit-splice-reindent (start end)
4.2231+ (paredit-preserving-column
4.2232+ ;; If we changed the first subform of the enclosing list, we must
4.2233+ ;; reindent the whole enclosing list.
4.2234+ (if (paredit-handle-sexp-errors
4.2235+ (save-excursion
4.2236+ (backward-up-list)
4.2237+ (down-list)
4.2238+ (paredit-ignore-sexp-errors (forward-sexp))
4.2239+ (< start (point)))
4.2240+ nil)
4.2241+ (save-excursion (backward-up-list) (indent-sexp))
4.2242+ (paredit-indent-region start end))))
4.2243+
4.2244+(defun paredit-kill-surrounding-sexps-for-splice (argument)
4.2245+ (cond ((or (paredit-in-string-p)
4.2246+ (paredit-in-comment-p))
4.2247+ (error "Invalid context for splicing S-expressions."))
4.2248+ ((or (not argument) (eq argument 0)) nil)
4.2249+ ((or (numberp argument) (eq argument '-))
4.2250+ ;; Kill S-expressions before/after the point by saving the
4.2251+ ;; point, moving across them, and killing the region.
4.2252+ (let* ((argument (if (eq argument '-) -1 argument))
4.2253+ (saved (paredit-point-at-sexp-boundary (- argument))))
4.2254+ (goto-char saved)
4.2255+ (paredit-ignore-sexp-errors (backward-sexp argument))
4.2256+ (paredit-hack-kill-region saved (point))))
4.2257+ ((consp argument)
4.2258+ (let ((v (car argument)))
4.2259+ (if (= v 4) ;One `C-u'.
4.2260+ ;; Move backward until we hit the open paren; then
4.2261+ ;; kill that selected region.
4.2262+ (let ((end (point)))
4.2263+ (paredit-ignore-sexp-errors
4.2264+ (while (not (bobp))
4.2265+ (backward-sexp)))
4.2266+ (paredit-hack-kill-region (point) end))
4.2267+ ;; Move forward until we hit the close paren; then
4.2268+ ;; kill that selected region.
4.2269+ (let ((beginning (point)))
4.2270+ (paredit-ignore-sexp-errors
4.2271+ (while (not (eobp))
4.2272+ (forward-sexp)))
4.2273+ (paredit-hack-kill-region beginning (point))))))
4.2274+ (t (error "Bizarre prefix argument `%s'." argument))))
4.2275+
4.2276+(defun paredit-splice-sexp-killing-backward (&optional n)
4.2277+ "Splice the list the point is on by removing its delimiters, and
4.2278+ also kill all S-expressions before the point in the current list.
4.2279+With a prefix argument N, kill only the preceding N S-expressions."
4.2280+ (interactive "P")
4.2281+ (paredit-splice-sexp (if n
4.2282+ (prefix-numeric-value n)
4.2283+ '(4))))
4.2284+
4.2285+(defun paredit-splice-sexp-killing-forward (&optional n)
4.2286+ "Splice the list the point is on by removing its delimiters, and
4.2287+ also kill all S-expressions after the point in the current list.
4.2288+With a prefix argument N, kill only the following N S-expressions."
4.2289+ (interactive "P")
4.2290+ (paredit-splice-sexp (if n
4.2291+ (- (prefix-numeric-value n))
4.2292+ '(16))))
4.2293+
4.2294+(defun paredit-raise-sexp (&optional argument)
4.2295+ "Raise the following S-expression in a tree, deleting its siblings.
4.2296+With a prefix argument N, raise the following N S-expressions. If N
4.2297+ is negative, raise the preceding N S-expressions.
4.2298+If the point is on an S-expression, such as a string or a symbol, not
4.2299+ between them, that S-expression is considered to follow the point."
4.2300+ (interactive "P")
4.2301+ (save-excursion
4.2302+ ;; Select the S-expressions we want to raise in a buffer substring.
4.2303+ (let* ((bound
4.2304+ (if (and (not argument) (paredit-region-active-p))
4.2305+ (progn (if (< (mark) (point))
4.2306+ (paredit-check-region (mark) (point))
4.2307+ (paredit-check-region (point) (mark)))
4.2308+ (mark))
4.2309+ (cond ((paredit-in-string-p)
4.2310+ (goto-char (car (paredit-string-start+end-points))))
4.2311+ ((paredit-in-char-p)
4.2312+ (backward-sexp))
4.2313+ ((paredit-in-comment-p)
4.2314+ (error "No S-expression to raise in comment.")))
4.2315+ (scan-sexps (point) (prefix-numeric-value argument))))
4.2316+ (sexps
4.2317+ (if (< bound (point))
4.2318+ (buffer-substring bound (paredit-point-at-sexp-end))
4.2319+ (buffer-substring (paredit-point-at-sexp-start) bound))))
4.2320+ ;; Move up to the list we're raising those S-expressions out of and
4.2321+ ;; delete it.
4.2322+ (backward-up-list)
4.2323+ (delete-region (point) (scan-sexps (point) 1))
4.2324+ (let* ((indent-start (point))
4.2325+ (indent-end (save-excursion (insert sexps) (point))))
4.2326+ ;; If the expression spans multiple lines, its indentation is
4.2327+ ;; probably broken, so reindent it -- but don't reindent
4.2328+ ;; anything that we didn't touch outside the expression.
4.2329+ ;;
4.2330+ ;; XXX What if the *column* of the starting point was preserved
4.2331+ ;; too? Should we avoid reindenting in that case?
4.2332+ (if (not (eq (save-excursion (goto-char indent-start) (point-at-eol))
4.2333+ (save-excursion (goto-char indent-end) (point-at-eol))))
4.2334+ (indent-region indent-start indent-end nil))))))
4.2335+
4.2336+;;; The effects of convolution on the surrounding whitespace are pretty
4.2337+;;; random. If you have better suggestions, please let me know.
4.2338+
4.2339+(defun paredit-convolute-sexp (&optional n)
4.2340+ "Convolute S-expressions.
4.2341+Save the S-expressions preceding point and delete them.
4.2342+Splice the S-expressions following point.
4.2343+Wrap the enclosing list in a new list prefixed by the saved text.
4.2344+With a prefix argument N, move up N lists before wrapping."
4.2345+ (interactive "p")
4.2346+ (paredit-lose-if-not-in-sexp 'paredit-convolute-sexp)
4.2347+ ;; Make sure we can move up before destroying anything.
4.2348+ (save-excursion (backward-up-list n) (backward-up-list))
4.2349+ (let (open close) ;++ Is this a good idea?
4.2350+ (let ((prefix
4.2351+ (let ((end (point)))
4.2352+ (paredit-ignore-sexp-errors
4.2353+ (while (not (bobp)) (backward-sexp)))
4.2354+ (prog1 (buffer-substring (point) end)
4.2355+ (backward-up-list)
4.2356+ (save-excursion (forward-sexp)
4.2357+ (setq close (char-before))
4.2358+ (delete-char -1))
4.2359+ (setq open (char-after))
4.2360+ (delete-region (point) end)
4.2361+ ;; I'm not sure this makes sense...
4.2362+ (if (not (eolp)) (just-one-space))))))
4.2363+ (backward-up-list n)
4.2364+ (paredit-insert-pair 1 open close 'goto-char)
4.2365+ (insert prefix)
4.2366+ ;; I'm not sure this makes sense either...
4.2367+ (if (not (eolp)) (just-one-space))
4.2368+ (save-excursion
4.2369+ (backward-up-list)
4.2370+ (paredit-ignore-sexp-errors (indent-sexp))))))
4.2371+
4.2372+(defun paredit-splice-string (argument)
4.2373+ (let ((original-point (point))
4.2374+ (start+end (paredit-string-start+end-points)))
4.2375+ (let ((start (car start+end))
4.2376+ (end (cdr start+end)))
4.2377+ ;; START and END both lie before the respective quote
4.2378+ ;; characters, which we want to delete; thus we increment START
4.2379+ ;; by one to extract the string, and we increment END by one to
4.2380+ ;; delete the string.
4.2381+ (let* ((escaped-string
4.2382+ (cond ((not (consp argument))
4.2383+ (buffer-substring (1+ start) end))
4.2384+ ((= 4 (car argument))
4.2385+ (buffer-substring original-point end))
4.2386+ (t
4.2387+ (buffer-substring (1+ start) original-point))))
4.2388+ (unescaped-string
4.2389+ (paredit-unescape-string escaped-string)))
4.2390+ (if (not unescaped-string)
4.2391+ (error "Unspliceable string.")
4.2392+ (save-excursion
4.2393+ (goto-char start)
4.2394+ (delete-region start (1+ end))
4.2395+ (insert unescaped-string))
4.2396+ (if (not (and (consp argument)
4.2397+ (= 4 (car argument))))
4.2398+ (goto-char (- original-point 1))))))))
4.2399+
4.2400+(defun paredit-unescape-string (string)
4.2401+ (with-temp-buffer
4.2402+ (insert string)
4.2403+ (goto-char (point-min))
4.2404+ (while (and (not (eobp))
4.2405+ ;; nil -> no bound; t -> no errors.
4.2406+ (search-forward "\\" nil t))
4.2407+ (delete-char -1)
4.2408+ (forward-char))
4.2409+ (paredit-handle-sexp-errors
4.2410+ (progn (scan-sexps (point-min) (point-max))
4.2411+ (buffer-string))
4.2412+ nil)))
4.2413+
4.2414+;;;; Slurpage & Barfage
4.2415+
4.2416+(defun paredit-forward-slurp-sexp (&optional argument)
4.2417+ "Add the S-expression following the current list into that list
4.2418+ by moving the closing delimiter.
4.2419+Automatically reindent the newly slurped S-expression with respect to
4.2420+ its new enclosing form.
4.2421+If in a string, move the opening double-quote forward by one
4.2422+ S-expression and escape any intervening characters as necessary,
4.2423+ without altering any indentation or formatting."
4.2424+ (interactive "P")
4.2425+ (save-excursion
4.2426+ (cond ((paredit-in-comment-p)
4.2427+ (error "Invalid context for slurping S-expressions."))
4.2428+ ((numberp argument)
4.2429+ (if (< argument 0)
4.2430+ (paredit-forward-barf-sexp (- 0 argument))
4.2431+ (while (< 0 argument)
4.2432+ (paredit-forward-slurp-sexp)
4.2433+ (setq argument (- argument 1)))))
4.2434+ ((paredit-in-string-p)
4.2435+ ;; If there is anything to slurp into the string, take that.
4.2436+ ;; Otherwise, try to slurp into the enclosing list.
4.2437+ (if (save-excursion
4.2438+ (goto-char (paredit-enclosing-string-end))
4.2439+ (paredit-handle-sexp-errors (progn (forward-sexp) nil)
4.2440+ t))
4.2441+ (progn
4.2442+ (goto-char (paredit-enclosing-string-end))
4.2443+ (paredit-forward-slurp-into-list argument))
4.2444+ (paredit-forward-slurp-into-string argument)))
4.2445+ (t
4.2446+ (paredit-forward-slurp-into-list argument)))))
4.2447+
4.2448+(defun paredit-forward-slurp-into-list (&optional argument)
4.2449+ (let ((nestedp nil))
4.2450+ (save-excursion
4.2451+ (up-list) ; Up to the end of the list to
4.2452+ (let ((close (char-before))) ; save and delete the closing
4.2453+ (delete-char -1) ; delimiter.
4.2454+ (let ((start (point)))
4.2455+ (catch 'return ; Go to the end of the desired
4.2456+ (while t ; S-expression, going up a
4.2457+ (paredit-handle-sexp-errors ; list if it's not in this,
4.2458+ (progn (forward-sexp)
4.2459+ (if argument
4.2460+ (paredit-ignore-sexp-errors
4.2461+ (while (not (eobp))
4.2462+ (forward-sexp))))
4.2463+ (throw 'return nil))
4.2464+ (setq nestedp t)
4.2465+ (up-list)
4.2466+ (setq close ; adjusting for mixed
4.2467+ (prog1 (char-before) ; delimiters as necessary,
4.2468+ (delete-char -1)
4.2469+ (insert close))))))
4.2470+ (insert close) ; to insert that delimiter.
4.2471+ (indent-region start (point) nil))))
4.2472+ (if (and (not nestedp)
4.2473+ (eq (save-excursion (paredit-skip-whitespace nil) (point))
4.2474+ (save-excursion (backward-up-list) (forward-char) (point)))
4.2475+ (eq (save-excursion (forward-sexp) (backward-sexp) (point))
4.2476+ (save-excursion (paredit-skip-whitespace t) (point))))
4.2477+ (delete-region (save-excursion (paredit-skip-whitespace nil) (point))
4.2478+ (save-excursion (paredit-skip-whitespace t) (point))))))
4.2479+
4.2480+(defun paredit-forward-slurp-into-string (&optional argument)
4.2481+ (let ((start (paredit-enclosing-string-start))
4.2482+ (end (paredit-enclosing-string-end)))
4.2483+ (goto-char end)
4.2484+ ;; Signal any errors that we might get first, before mucking with
4.2485+ ;; the buffer's contents.
4.2486+ (save-excursion (forward-sexp))
4.2487+ (let ((close (char-before)))
4.2488+ ;; Skip intervening whitespace if we're slurping into an empty
4.2489+ ;; string. XXX What about nonempty strings?
4.2490+ (if (and (= (+ start 2) end)
4.2491+ (eq (save-excursion (paredit-skip-whitespace t) (point))
4.2492+ (save-excursion (forward-sexp) (backward-sexp) (point))))
4.2493+ (delete-region (- (point) 1)
4.2494+ (save-excursion (paredit-skip-whitespace t) (point)))
4.2495+ (delete-char -1))
4.2496+ (paredit-forward-for-quote
4.2497+ (save-excursion
4.2498+ (forward-sexp)
4.2499+ (if argument
4.2500+ (while (paredit-handle-sexp-errors (progn (forward-sexp) t) nil)))
4.2501+ (point)))
4.2502+ (insert close))))
4.2503+
4.2504+(defun paredit-forward-barf-sexp (&optional argument)
4.2505+ "Remove the last S-expression in the current list from that list
4.2506+ by moving the closing delimiter.
4.2507+Automatically reindent the newly barfed S-expression with respect to
4.2508+ its new enclosing form."
4.2509+ (interactive "P")
4.2510+ (paredit-lose-if-not-in-sexp 'paredit-forward-barf-sexp)
4.2511+ (if (and (numberp argument) (< argument 0))
4.2512+ (paredit-forward-slurp-sexp (- 0 argument))
4.2513+ (let ((start (point)) (end nil))
4.2514+ (save-excursion
4.2515+ (up-list) ; Up to the end of the list to
4.2516+ (let ((close (char-before))) ; save and delete the closing
4.2517+ (delete-char -1) ; delimiter.
4.2518+ (setq end (point))
4.2519+ (paredit-ignore-sexp-errors ; Go back to where we want to
4.2520+ (if (or (not argument) ; insert the delimiter.
4.2521+ (numberp argument))
4.2522+ (backward-sexp argument)
4.2523+ (while (paredit-handle-sexp-errors
4.2524+ (save-excursion (backward-sexp) (<= start (point)))
4.2525+ nil)
4.2526+ (backward-sexp))))
4.2527+ (paredit-skip-whitespace nil) ; Skip leading whitespace.
4.2528+ (cond ((bobp)
4.2529+ ;++ We'll have deleted the close, but there's no open.
4.2530+ ;++ Is that OK?
4.2531+ (error "Barfing all subexpressions with no open-paren?"))
4.2532+ ((paredit-in-comment-p) ; Don't put the close-paren in
4.2533+ (newline))) ; a comment.
4.2534+ (insert close))
4.2535+ ;; Reindent all of the newly barfed S-expressions. Start at the
4.2536+ ;; start of the first barfed S-expression, not at the close we
4.2537+ ;; just inserted.
4.2538+ (forward-sexp)
4.2539+ (backward-sexp)
4.2540+ (if (or (not argument) (numberp argument))
4.2541+ (paredit-forward-and-indent argument)
4.2542+ (indent-region (point) end))))))
4.2543+
4.2544+(defun paredit-backward-slurp-sexp (&optional argument)
4.2545+ "Add the S-expression preceding the current list into that list
4.2546+ by moving the closing delimiter.
4.2547+Automatically reindent the whole form into which new S-expression was
4.2548+ slurped.
4.2549+If in a string, move the opening double-quote backward by one
4.2550+ S-expression and escape any intervening characters as necessary,
4.2551+ without altering any indentation or formatting."
4.2552+ (interactive "P")
4.2553+ (save-excursion
4.2554+ (cond ((paredit-in-comment-p)
4.2555+ (error "Invalid context for slurping S-expressions."))
4.2556+ ((numberp argument)
4.2557+ (if (< argument 0)
4.2558+ (paredit-backward-barf-sexp (- 0 argument))
4.2559+ (while (< 0 argument)
4.2560+ (paredit-backward-slurp-sexp)
4.2561+ (setq argument (- argument 1)))))
4.2562+ ((paredit-in-string-p)
4.2563+ ;; If there is anything to slurp into the string, take that.
4.2564+ ;; Otherwise, try to slurp into the enclosing list.
4.2565+ (if (save-excursion
4.2566+ (goto-char (paredit-enclosing-string-start))
4.2567+ (paredit-handle-sexp-errors (progn (backward-sexp) nil)
4.2568+ t))
4.2569+ (progn
4.2570+ (goto-char (paredit-enclosing-string-start))
4.2571+ (paredit-backward-slurp-into-list argument))
4.2572+ (paredit-backward-slurp-into-string argument)))
4.2573+ (t
4.2574+ (paredit-backward-slurp-into-list argument)))))
4.2575+
4.2576+(defun paredit-backward-slurp-into-list (&optional argument)
4.2577+ (let ((nestedp nil))
4.2578+ (save-excursion
4.2579+ (backward-up-list)
4.2580+ (let ((open (char-after)))
4.2581+ (delete-char +1)
4.2582+ (catch 'return
4.2583+ (while t
4.2584+ (paredit-handle-sexp-errors
4.2585+ (progn (backward-sexp)
4.2586+ (if argument
4.2587+ (paredit-ignore-sexp-errors
4.2588+ (while (not (bobp))
4.2589+ (backward-sexp))))
4.2590+ (throw 'return nil))
4.2591+ (setq nestedp t)
4.2592+ (backward-up-list)
4.2593+ (setq open
4.2594+ (prog1 (char-after)
4.2595+ (save-excursion (insert open) (delete-char +1)))))))
4.2596+ (insert open))
4.2597+ ;; Reindent the line at the beginning of wherever we inserted the
4.2598+ ;; opening delimiter, and then indent the whole S-expression.
4.2599+ (backward-up-list)
4.2600+ (lisp-indent-line)
4.2601+ (indent-sexp))
4.2602+ ;; If we slurped into an empty list, don't leave dangling space:
4.2603+ ;; (foo |).
4.2604+ (if (and (not nestedp)
4.2605+ (eq (save-excursion (paredit-skip-whitespace nil) (point))
4.2606+ (save-excursion (backward-sexp) (forward-sexp) (point)))
4.2607+ (eq (save-excursion (up-list) (backward-char) (point))
4.2608+ (save-excursion (paredit-skip-whitespace t) (point))))
4.2609+ (delete-region (save-excursion (paredit-skip-whitespace nil) (point))
4.2610+ (save-excursion (paredit-skip-whitespace t) (point))))))
4.2611+
4.2612+(defun paredit-backward-slurp-into-string (&optional argument)
4.2613+ (let ((start (paredit-enclosing-string-start))
4.2614+ (end (paredit-enclosing-string-end)))
4.2615+ (goto-char start)
4.2616+ ;; Signal any errors that we might get first, before mucking with
4.2617+ ;; the buffer's contents.
4.2618+ (save-excursion (backward-sexp))
4.2619+ (let ((open (char-after))
4.2620+ (target (point)))
4.2621+ ;; Skip intervening whitespace if we're slurping into an empty
4.2622+ ;; string. XXX What about nonempty strings?
4.2623+ (if (and (= (+ start 2) end)
4.2624+ (eq (save-excursion (paredit-skip-whitespace nil) (point))
4.2625+ (save-excursion (backward-sexp) (forward-sexp) (point))))
4.2626+ (delete-region (save-excursion (paredit-skip-whitespace nil) (point))
4.2627+ (+ (point) 1))
4.2628+ (delete-char +1))
4.2629+ (backward-sexp)
4.2630+ (if argument
4.2631+ (paredit-ignore-sexp-errors
4.2632+ (while (not (bobp))
4.2633+ (backward-sexp))))
4.2634+ (insert open)
4.2635+ (paredit-forward-for-quote target))))
4.2636+
4.2637+(defun paredit-backward-barf-sexp (&optional argument)
4.2638+ "Remove the first S-expression in the current list from that list
4.2639+ by moving the closing delimiter.
4.2640+Automatically reindent the barfed S-expression and the form from which
4.2641+ it was barfed."
4.2642+ (interactive "P")
4.2643+ (paredit-lose-if-not-in-sexp 'paredit-backward-barf-sexp)
4.2644+ (if (and (numberp argument) (< argument 0))
4.2645+ (paredit-backward-slurp-sexp (- 0 argument))
4.2646+ (let ((end (make-marker)))
4.2647+ (set-marker end (point))
4.2648+ (save-excursion
4.2649+ (backward-up-list)
4.2650+ (let ((open (char-after)))
4.2651+ (delete-char +1)
4.2652+ (paredit-ignore-sexp-errors
4.2653+ (paredit-forward-and-indent
4.2654+ (if (or (not argument) (numberp argument))
4.2655+ argument
4.2656+ (let ((n 0))
4.2657+ (save-excursion
4.2658+ (while (paredit-handle-sexp-errors
4.2659+ (save-excursion
4.2660+ (forward-sexp)
4.2661+ (<= (point) end))
4.2662+ nil)
4.2663+ (forward-sexp)
4.2664+ (setq n (+ n 1))))
4.2665+ n))))
4.2666+ (while (progn (paredit-skip-whitespace t) (eq (char-after) ?\; ))
4.2667+ (forward-line 1))
4.2668+ (if (eobp)
4.2669+ ;++ We'll have deleted the close, but there's no open.
4.2670+ ;++ Is that OK?
4.2671+ (error "Barfing all subexpressions with no close-paren?"))
4.2672+ ;** Don't use `insert' here. Consider, e.g., barfing from
4.2673+ ;** (foo|)
4.2674+ ;** and how `save-excursion' works.
4.2675+ (insert-before-markers open))
4.2676+ (backward-up-list)
4.2677+ (lisp-indent-line)
4.2678+ (indent-sexp)))))
4.2679+
4.2680+;;;; Splitting & Joining
4.2681+
4.2682+(defun paredit-split-sexp ()
4.2683+ "Split the list or string the point is on into two."
4.2684+ (interactive)
4.2685+ (cond ((paredit-in-string-p)
4.2686+ (insert "\"")
4.2687+ (save-excursion (insert " \"")))
4.2688+ ((or (paredit-in-comment-p)
4.2689+ (paredit-in-char-p))
4.2690+ (error "Invalid context for splitting S-expression."))
4.2691+ (t
4.2692+ (let ((open (save-excursion (backward-up-list) (char-after)))
4.2693+ (close (save-excursion (up-list) (char-before))))
4.2694+ (delete-horizontal-space)
4.2695+ (insert close)
4.2696+ (save-excursion
4.2697+ (insert ?\ )
4.2698+ (insert open)
4.2699+ (backward-char)
4.2700+ (indent-sexp))))))
4.2701+
4.2702+(defun paredit-join-sexps ()
4.2703+ "Join the S-expressions adjacent on either side of the point.
4.2704+Both must be lists, strings, or atoms; error if there is a mismatch."
4.2705+ (interactive)
4.2706+ (cond ((paredit-in-comment-p) (error "Can't join S-expressions in comment."))
4.2707+ ((paredit-in-string-p) (error "Nothing to join in a string."))
4.2708+ ((paredit-in-char-p) (error "Can't join characters.")))
4.2709+ (let ((left-point (paredit-point-at-sexp-end))
4.2710+ (right-point (paredit-point-at-sexp-start)))
4.2711+ (let ((left-char (char-before left-point))
4.2712+ (right-char (char-after right-point)))
4.2713+ (let ((left-syntax (char-syntax left-char))
4.2714+ (right-syntax (char-syntax right-char)))
4.2715+ (cond ((< right-point left-point)
4.2716+ (error "Can't join a datum with itself."))
4.2717+ ((and (eq left-syntax ?\) )
4.2718+ (eq right-syntax ?\( )
4.2719+ (eq left-char (matching-paren right-char))
4.2720+ (eq right-char (matching-paren left-char)))
4.2721+ (paredit-join-lists-internal left-point right-point)
4.2722+ (paredit-preserving-column
4.2723+ (save-excursion
4.2724+ (backward-up-list)
4.2725+ (indent-sexp))))
4.2726+ ((and (eq left-syntax ?\" )
4.2727+ (eq right-syntax ?\" ))
4.2728+ ;; Delete any intermediate formatting.
4.2729+ (delete-region (1- left-point) (1+ right-point)))
4.2730+ ((and (memq left-syntax '(?w ?_)) ; Word or symbol
4.2731+ (memq right-syntax '(?w ?_)))
4.2732+ (delete-region left-point right-point))
4.2733+ (t (error "Mismatched S-expressions to join.")))))))
4.2734+
4.2735+(defun paredit-join-lists-internal (left-point right-point)
4.2736+ (save-excursion
4.2737+ ;; Leave intermediate formatting alone.
4.2738+ (goto-char right-point)
4.2739+ (delete-char +1)
4.2740+ (goto-char left-point)
4.2741+ (delete-char -1)
4.2742+ ;; Kludge: Add an extra space in several conditions.
4.2743+ (if (or
4.2744+ ;; (foo)| ;x\n(bar) => (foo | ;x\nbar), not (foo| ;x\nbar).
4.2745+ (and (not (eolp))
4.2746+ (save-excursion
4.2747+ (paredit-skip-whitespace t (point-at-eol))
4.2748+ (eq (char-after) ?\;)))
4.2749+ ;; (foo)|(bar) => (foo| bar), not (foo|bar).
4.2750+ (and (= left-point right-point)
4.2751+ (not (or (eq ?\ (char-syntax (char-before)))
4.2752+ (eq ?\ (char-syntax (char-after)))))))
4.2753+ (insert ?\ ))))
4.2754+
4.2755+;++ How ought paredit-join to handle comments intervening symbols or strings?
4.2756+;++ Idea:
4.2757+;++
4.2758+;++ "foo" | ;bar
4.2759+;++ "baz" ;quux
4.2760+;++
4.2761+;++ =>
4.2762+;++
4.2763+;++ "foo|baz" ;bar
4.2764+;++ ;quux
4.2765+;++
4.2766+;++ The point should stay where it is relative to the comments, and the
4.2767+;++ the comments' columns should all be preserved, perhaps. Hmmmm...
4.2768+;++ What about this?
4.2769+;++
4.2770+;++ "foo" ;bar
4.2771+;++ | ;baz
4.2772+;++ "quux" ;zot
4.2773+
4.2774+;++ Should rename:
4.2775+;++ paredit-point-at-sexp-start -> paredit-start-of-sexp-after-point
4.2776+;++ paredit-point-at-sexp-end -> paredit-end-of-sexp-before-point
4.2777+
4.2778+;;;; Variations on the Lurid Theme
4.2779+
4.2780+;;; I haven't the imagination to concoct clever names for these.
4.2781+
4.2782+(defun paredit-add-to-previous-list ()
4.2783+ "Add the S-expression following point to the list preceding point."
4.2784+ (interactive)
4.2785+ (paredit-lose-if-not-in-sexp 'paredit-add-to-previous-list)
4.2786+ (save-excursion
4.2787+ (down-list -1) ;++ backward-down-list...
4.2788+ (paredit-forward-slurp-sexp)))
4.2789+
4.2790+(defun paredit-add-to-next-list ()
4.2791+ "Add the S-expression preceding point to the list following point.
4.2792+If no S-expression precedes point, move up the tree until one does."
4.2793+ (interactive)
4.2794+ (paredit-lose-if-not-in-sexp 'paredit-add-to-next-list)
4.2795+ (save-excursion
4.2796+ (down-list)
4.2797+ (paredit-backward-slurp-sexp)))
4.2798+
4.2799+(defun paredit-join-with-previous-list ()
4.2800+ "Join the list the point is on with the previous list in the buffer."
4.2801+ (interactive)
4.2802+ (paredit-lose-if-not-in-sexp 'paredit-join-with-previous-list)
4.2803+ (save-excursion
4.2804+ (while (paredit-handle-sexp-errors (save-excursion (backward-sexp) nil)
4.2805+ (backward-up-list)
4.2806+ t))
4.2807+ (paredit-join-sexps)))
4.2808+
4.2809+(defun paredit-join-with-next-list ()
4.2810+ "Join the list the point is on with the next list in the buffer."
4.2811+ (interactive)
4.2812+ (paredit-lose-if-not-in-sexp 'paredit-join-with-next-list)
4.2813+ (save-excursion
4.2814+ (while (paredit-handle-sexp-errors (save-excursion (forward-sexp) nil)
4.2815+ (up-list)
4.2816+ t))
4.2817+ (paredit-join-sexps)))
4.2818+
4.2819+;;;; Utilities
4.2820+
4.2821+(defun paredit-in-string-escape-p ()
4.2822+ "True if the point is on a character escape of a string.
4.2823+This is true only if the character is preceded by an odd number of
4.2824+ backslashes.
4.2825+This assumes that `paredit-in-string-p' has already returned true."
4.2826+ (let ((oddp nil))
4.2827+ (save-excursion
4.2828+ (while (eq (char-before) ?\\ )
4.2829+ (setq oddp (not oddp))
4.2830+ (backward-char)))
4.2831+ oddp))
4.2832+
4.2833+(defun paredit-in-char-p (&optional position)
4.2834+ "True if point is on a character escape outside a string."
4.2835+ (save-excursion
4.2836+ (goto-char (or position (point)))
4.2837+ (paredit-in-string-escape-p)))
4.2838+
4.2839+(defun paredit-skip-whitespace (trailing-p &optional limit)
4.2840+ "Skip past any whitespace, or until the point LIMIT is reached.
4.2841+If TRAILING-P is nil, skip leading whitespace; otherwise, skip trailing
4.2842+ whitespace."
4.2843+ (funcall (if trailing-p 'skip-chars-forward 'skip-chars-backward)
4.2844+ " \t\n" ; This should skip using the syntax table, but LF
4.2845+ limit)) ; is a comment end, not newline, in Lisp mode.
4.2846+
4.2847+(defalias 'paredit-region-active-p
4.2848+ (xcond ((paredit-xemacs-p) 'region-active-p)
4.2849+ ((paredit-gnu-emacs-p)
4.2850+ (lambda ()
4.2851+ (and mark-active transient-mark-mode)))))
4.2852+
4.2853+(defun paredit-hack-kill-region (start end)
4.2854+ "Kill the region between START and END.
4.2855+Do not append to any current kill, and
4.2856+ do not let the next kill append to this one."
4.2857+ (interactive "r") ;Eh, why not?
4.2858+ ;; KILL-REGION sets THIS-COMMAND to tell the next kill that the last
4.2859+ ;; command was a kill. It also checks LAST-COMMAND to see whether it
4.2860+ ;; should append. If we bind these locally, any modifications to
4.2861+ ;; THIS-COMMAND will be masked, and it will not see LAST-COMMAND to
4.2862+ ;; indicate that it should append.
4.2863+ (let ((this-command nil)
4.2864+ (last-command nil))
4.2865+ (kill-region start end)))
4.2866+
4.2867+;;;;; Reindentation utilities
4.2868+
4.2869+;++ Should `paredit-indent-sexps' and `paredit-forward-and-indent' use
4.2870+;++ `paredit-indent-region' rather than `indent-region'?
4.2871+
4.2872+(defun paredit-indent-sexps ()
4.2873+ "If in a list, indent all following S-expressions in the list."
4.2874+ (let* ((start (point))
4.2875+ (end (paredit-handle-sexp-errors (progn (up-list) (point)) nil)))
4.2876+ (if end
4.2877+ (indent-region start end nil))))
4.2878+
4.2879+(defun paredit-forward-and-indent (&optional n)
4.2880+ "Move forward by N S-expressions, indenting them with `indent-region'."
4.2881+ (let ((start (point)))
4.2882+ (forward-sexp n)
4.2883+ (indent-region start (point) nil)))
4.2884+
4.2885+(defun paredit-indent-region (start end)
4.2886+ "Indent the region from START to END.
4.2887+Don't reindent the line starting at START, however."
4.2888+ (if (not (<= start end))
4.2889+ (error "Incorrectly related points: %S, %S" start end))
4.2890+ (save-excursion
4.2891+ (goto-char start)
4.2892+ (let ((bol (point-at-bol)))
4.2893+ ;; Skip all S-expressions that end on the starting line, but
4.2894+ ;; don't go past `end'.
4.2895+ (if (and (save-excursion (goto-char end) (not (eq bol (point-at-bol))))
4.2896+ (paredit-handle-sexp-errors
4.2897+ (catch 'exit
4.2898+ (while t
4.2899+ (save-excursion
4.2900+ (forward-sexp)
4.2901+ (if (not (eq bol (point-at-bol)))
4.2902+ (throw 'exit t))
4.2903+ (if (not (< (point) end))
4.2904+ (throw 'exit nil)))
4.2905+ (forward-sexp)))
4.2906+ nil))
4.2907+ (progn
4.2908+ ;; Point is still on the same line, but precedes an
4.2909+ ;; S-expression that ends on a different line.
4.2910+ (if (not (eq bol (point-at-bol)))
4.2911+ (error "Internal error -- we moved forward a line!"))
4.2912+ (goto-char (+ 1 (point-at-eol)))
4.2913+ (if (not (<= (point) end))
4.2914+ (error "Internal error -- we frobnitzed the garfnut!"))
4.2915+ (indent-region (point) end nil))))))
4.2916+
4.2917+;;;;; S-expression Parsing Utilities
4.2918+
4.2919+;++ These routines redundantly traverse S-expressions a great deal.
4.2920+;++ If performance issues arise, this whole section will probably have
4.2921+;++ to be refactored to preserve the state longer, like paredit.scm
4.2922+;++ does, rather than to traverse the definition N times for every key
4.2923+;++ stroke as it presently does.
4.2924+
4.2925+(defun paredit-current-parse-state ()
4.2926+ "Return parse state of point from beginning of defun."
4.2927+ (let ((point (point)))
4.2928+ (beginning-of-defun)
4.2929+ ;; Calling PARSE-PARTIAL-SEXP will advance the point to its second
4.2930+ ;; argument (unless parsing stops due to an error, but we assume it
4.2931+ ;; won't in paredit-mode).
4.2932+ (parse-partial-sexp (point) point)))
4.2933+
4.2934+(defun paredit-in-string-p (&optional state)
4.2935+ "True if the parse state is within a double-quote-delimited string.
4.2936+If no parse state is supplied, compute one from the beginning of the
4.2937+ defun to the point."
4.2938+ ;; 3. non-nil if inside a string (the terminator character, really)
4.2939+ (and (nth 3 (or state (paredit-current-parse-state)))
4.2940+ t))
4.2941+
4.2942+(defun paredit-string-start+end-points (&optional state)
4.2943+ "Return a cons of the points of open and close quotes of the string.
4.2944+The string is determined from the parse state STATE, or the parse state
4.2945+ from the beginning of the defun to the point.
4.2946+This assumes that `paredit-in-string-p' has already returned true, i.e.
4.2947+ that the point is already within a string."
4.2948+ (save-excursion
4.2949+ ;; 8. character address of start of comment or string; nil if not
4.2950+ ;; in one
4.2951+ (let ((start (nth 8 (or state (paredit-current-parse-state)))))
4.2952+ (goto-char start)
4.2953+ (forward-sexp 1)
4.2954+ (cons start (1- (point))))))
4.2955+
4.2956+(defun paredit-enclosing-string-start ()
4.2957+ (car (paredit-string-start+end-points)))
4.2958+
4.2959+(defun paredit-enclosing-string-end ()
4.2960+ (+ 1 (cdr (paredit-string-start+end-points))))
4.2961+
4.2962+(defun paredit-enclosing-list-start ()
4.2963+ (save-excursion
4.2964+ (backward-up-list)
4.2965+ (point)))
4.2966+
4.2967+(defun paredit-enclosing-list-end ()
4.2968+ (save-excursion
4.2969+ (up-list)
4.2970+ (point)))
4.2971+
4.2972+(defun paredit-in-comment-p (&optional state)
4.2973+ "True if parse state STATE is within a comment.
4.2974+If no parse state is supplied, compute one from the beginning of the
4.2975+ defun to the point."
4.2976+ ;; 4. nil if outside a comment, t if inside a non-nestable comment,
4.2977+ ;; else an integer (the current comment nesting)
4.2978+ (and (nth 4 (or state (paredit-current-parse-state)))
4.2979+ t))
4.2980+
4.2981+(defun paredit-prefix-numeric-value (argument)
4.2982+ ;++ Kludgerific.
4.2983+ (cond ((integerp argument) argument)
4.2984+ ((eq argument '-) -1)
4.2985+ ((consp argument)
4.2986+ (cond ((equal argument '(4)) (paredit-count-sexps-forward)) ;C-u
4.2987+ ((equal argument '(16)) (paredit-count-sexps-backward)) ;C-u C-u
4.2988+ (t (error "Invalid prefix argument: %S" argument))))
4.2989+ ((paredit-region-active-p)
4.2990+ (save-excursion
4.2991+ (save-restriction
4.2992+ (narrow-to-region (region-beginning) (region-end))
4.2993+ (cond ((= (point) (point-min)) (paredit-count-sexps-forward))
4.2994+ ((= (point) (point-max)) (paredit-count-sexps-backward))
4.2995+ (t
4.2996+ (error "Point %S is not start or end of region: %S..%S"
4.2997+ (point) (region-beginning) (region-end)))))))
4.2998+ (t 1)))
4.2999+
4.3000+(defun paredit-count-sexps-forward ()
4.3001+ (save-excursion
4.3002+ (let ((n 0) (p nil)) ;hurk
4.3003+ (paredit-ignore-sexp-errors
4.3004+ (while (setq p (scan-sexps (point) +1))
4.3005+ (goto-char p)
4.3006+ (setq n (+ n 1))))
4.3007+ n)))
4.3008+
4.3009+(defun paredit-count-sexps-backward ()
4.3010+ (save-excursion
4.3011+ (let ((n 0) (p nil)) ;hurk
4.3012+ (paredit-ignore-sexp-errors
4.3013+ (while (setq p (scan-sexps (point) -1))
4.3014+ (goto-char p)
4.3015+ (setq n (+ n 1))))
4.3016+ n)))
4.3017+
4.3018+(defun paredit-point-at-sexp-boundary (n)
4.3019+ (cond ((< n 0) (paredit-point-at-sexp-start))
4.3020+ ((= n 0) (point))
4.3021+ ((> n 0) (paredit-point-at-sexp-end))))
4.3022+
4.3023+(defun paredit-point-at-sexp-start ()
4.3024+ (save-excursion
4.3025+ (forward-sexp)
4.3026+ (backward-sexp)
4.3027+ (point)))
4.3028+
4.3029+(defun paredit-point-at-sexp-end ()
4.3030+ (save-excursion
4.3031+ (backward-sexp)
4.3032+ (forward-sexp)
4.3033+ (point)))
4.3034+
4.3035+(defun paredit-lose-if-not-in-sexp (command)
4.3036+ (if (or (paredit-in-string-p)
4.3037+ (paredit-in-comment-p)
4.3038+ (paredit-in-char-p))
4.3039+ (error "Invalid context for command `%s'." command)))
4.3040+
4.3041+(defun paredit-check-region (start end)
4.3042+ "Signal an error if text between `start' and `end' is unbalanced."
4.3043+ ;; `narrow-to-region' will move the point, so avoid calling it if we
4.3044+ ;; don't need to. We don't want to use `save-excursion' because we
4.3045+ ;; want the point to move if `check-parens' reports an error.
4.3046+ (if (not (paredit-region-ok-p start end))
4.3047+ (save-restriction
4.3048+ (narrow-to-region start end)
4.3049+ (check-parens))))
4.3050+
4.3051+(defun paredit-region-ok-p (start end)
4.3052+ "Return true iff the region between `start' and `end' is balanced.
4.3053+This is independent of context -- it doesn't check what state the
4.3054+ text at `start' is in."
4.3055+ (save-excursion
4.3056+ (paredit-handle-sexp-errors
4.3057+ (progn
4.3058+ (save-restriction
4.3059+ (narrow-to-region start end)
4.3060+ (scan-sexps (point-min) (point-max)))
4.3061+ t)
4.3062+ nil)))
4.3063+
4.3064+(defun paredit-current-column ()
4.3065+ ;; Like current-column, but respects field boundaries in interactive
4.3066+ ;; modes like ielm. For use only with paredit-restore-column, which
4.3067+ ;; works relative to point-at-bol.
4.3068+ (- (point) (point-at-bol)))
4.3069+
4.3070+(defun paredit-current-indentation ()
4.3071+ (save-excursion
4.3072+ (back-to-indentation)
4.3073+ (paredit-current-column)))
4.3074+
4.3075+(defun paredit-restore-column (column indentation)
4.3076+ ;; Preserve the point's position either in the indentation or in the
4.3077+ ;; code: if on code, move with the code; if in indentation, leave it
4.3078+ ;; in the indentation, either where it was (if still on indentation)
4.3079+ ;; or at the end of the indentation (if the code moved far enough
4.3080+ ;; left).
4.3081+ (let ((indentation* (paredit-current-indentation)))
4.3082+ (goto-char
4.3083+ (+ (point-at-bol)
4.3084+ (cond ((not (< column indentation))
4.3085+ (+ column (- indentation* indentation)))
4.3086+ ((<= indentation* column) indentation*)
4.3087+ (t column))))))
4.3088+
4.3089+;;;; Initialization
4.3090+
4.3091+(paredit-define-keys)
4.3092+(paredit-annotate-mode-with-examples)
4.3093+(paredit-annotate-functions-with-examples)
4.3094+
4.3095+(provide 'paredit)
4.3096+
4.3097+;;; Local Variables:
4.3098+;;; outline-regexp: "\n;;;;+"
4.3099+;;; End:
4.3100+
4.3101+;;; paredit.el ends here
5.1--- a/.homerc Tue May 14 14:47:50 2024 -0400
5.2+++ b/.homerc Sat Jun 01 23:43:29 2024 -0400
5.3@@ -1,8 +1,8 @@
5.4 ;;; -*- mode:skel; -*-
5.5 :user ellis
5.6-:packy "~/.packy"
5.7+:packy "~/.stash/packy"
5.8 :wm :stumpwm
5.9 :shell :bash
5.10-:browser "/usr/bin/chromium"
5.11+:browser "eww"
5.12 :editor :emacs
5.13-:src "~/dev/comp/home/"
5.14+:src "~/comp/home/"
6.1--- a/.sbclrc Tue May 14 14:47:50 2024 -0400
6.2+++ b/.sbclrc Sat Jun 01 23:43:29 2024 -0400
6.3@@ -26,8 +26,8 @@
6.4
6.5 (mapc #'include-projects-from
6.6 (mapcar (lambda (x) (merge-pathnames x (user-homedir-pathname)))
6.7- (list "dev/comp/core/lisp/"
6.8- "dev/comp/demo/"
6.9- "dev/comp/scratch/")))
6.10+ (list "comp/core/lisp/"
6.11+ "comp/demo/"
6.12+ "comp/scratch/")))
6.13
6.14 ;; (require :sb-aclrepl)