spacemacs-elpa/26.3/develop/evil-cleverparens-20170718.413/evil-cleverparens.el
Yann Esposito (Yogsototh) 863ed55051
snapshot
2020-03-17 23:22:53 +01:00

2160 lines
78 KiB
EmacsLisp

;;; evil-cleverparens.el --- Evil friendly minor-mode for editing lisp.
;;
;; Copyright (C) 2015 Olli Piepponen
;;
;; Author: Olli Piepponen <opieppo@gmail.com>
;; URL: https://github.com/luxbock/evil-cleverparens
;; Keywords: cleverparens, parentheses, evil, paredit, smartparens
;; Version: 0.1.0
;; Package-Requires: ((evil "1.0") (paredit "1") (smartparens "1.6.1") (emacs "24.4") (dash "2.12.0"))
;;
;; This file is NOT part of GNU Emacs.
;;
;; This file is free software (MIT License)
;;; Commentary:
;; Use Vim/evil like modal editing with lisp without screwing up the structure
;; of your code. Tries to offer useful alternatives for behavior which would
;; otherwise be destructive.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Code:
(require 'cl-lib)
(require 'dash)
(require 'evil)
(require 'paredit)
(require 'smartparens)
(require 'subr-x)
(require 'evil-cleverparens-text-objects)
(require 'evil-cleverparens-util)
;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defgroup evil-cleverparens nil
"`evil-mode' for handling your parentheses with a mix of `smartparens' and `paredit'"
:group 'smartparens)
(defcustom evil-cleverparens-threshold 1500
"If the region being operated on is larger than this we cop out.
Quite a bit of work gets done to ensure the region being worked
is in an safe state, so this lets us sarifice safety for a snappy
editor on slower computers.
Even on a large computer you shouldn't set this too high or your
computer will freeze when copying large files out of Emacs.
This is a feature copied from `evil-smartparens'."
:group 'evil-cleverparens
:type 'number)
(defcustom evil-cleverparens-complete-parens-in-yanked-region nil
"Determines how to handle yanking a region containing
unbalanced expressions. If this value is non-nil, a yanked
region containing missing parentheses will include the missing
parens appended to the end."
:group 'evil-cleverparens
:type 'boolean)
(defcustom evil-cleverparens-move-skip-delimiters t
"Determines whether parentheses and other delimiters are
considered symbols or not. The effect this has is that when
enabled (default), the by-symbol navigation commands happily
travel through these delimiters. This can be handy if you are
already used to using evil-cleverparenses parentheses navigation
commands."
:group 'evil-cleverparens
:type 'boolean)
(defcustom evil-cleverparens-swap-move-by-word-and-symbol nil
"If true, the keys w, e, b, and ge will be bound to the
evil-cleverparens movement by symbol commands, and the regular
evil move by word commands will be bound to W, E, B and gE respectively."
:group 'evil-cleverparens
:type 'boolean)
(defcustom evil-cleverparens-drag-ignore-lines nil
"Controls whether top-level sexps should be swapped with other
sexps or should lines be considered as well."
:group 'evil-cleverparens
:type 'boolean)
(defcustom evil-cleverparens-drag-comment-blocks t
"Controls whether to drag by top-level comment block or a
single line when point is inside a multi-line top-level command
block."
:group 'evil-cleverparens
:type 'boolean)
(defcustom evil-cleverparens-indent-afterwards t
"Controls whether to automatically indent when performing
commands that alter the structure of the surrounding code.
Enabled by default."
:group 'evil-cleverparens
:type 'boolean)
(defcustom evil-cleverparens-use-regular-insert nil
"Determines whether to use `evil-insert' or `evil-cp-insert'."
:group 'evil-cleverparens
:type 'boolean)
(defvar evil-cp--override nil
"Should the next command skip region checks?")
(defvar evil-cp--inserted-space-after-round-open nil
"Helper var for `evil-cp-insert'.")
(defcustom evil-cleverparens-use-additional-bindings t
"Should additional bindings be enabled."
:type 'boolean
:group 'evil-cleverparens)
(defcustom evil-cleverparens-use-additional-movement-keys t
"Should additional movement keys, mostly those related to
parentheses navigation, be enabled."
:type 'boolean
:group 'evil-cleverparens)
(defcustom evil-cleverparens-use-s-and-S t
"Should we bind s and S, thus shadowing any existing bindings
to these keys."
:type 'boolean
:group 'evil-cleverparens)
(defcustom evil-cleverparens-enabled-hook nil
"Called after `evil-cleverparens-mode' is turned on."
:type 'hook
:group 'evil-cleverparens)
(defcustom evil-cleverparens-disabled-hook nil
"Called after `evil-cleverparens-mode' is turned off."
:type 'hook
:group 'evil-cleverparens)
;;; Overriding ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun evil-cp--override ()
"Can be used as a predicate to determine if the next operation
should default to using regular evil functions. Resets the
`evil-cp--override' variable back to nil."
(prog1 (or evil-cp--override
(evil-cp--region-too-expensive-to-check))
(setq evil-cp--override nil)))
(defun evil-cp-override ()
"Calling this function will have evil-cleverparens default to
the regular evil equivalent of the next command that gets called."
(interactive)
(setq evil-cp--override t))
(defun evil-cp--region-too-expensive-to-check ()
"When it takes prohobitively long to check region we cop out.
This is a feature copied from `evil-smartparens'."
(when (region-active-p)
(> (abs (- (region-beginning) (region-end)))
evil-cleverparens-threshold)))
(defun evil-cp--fail ()
"Error out with a friendly message."
(error "Can't find a safe region to act on."))
;;; Evil Operators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun evil-cp--splice-form ()
"Evil friendly version of splice that takes care of the
location of point, which also works for strings. Unlike
`sp-splice-sexp', this one doesn't perform clean-up for empty
forms."
(evil-cp--guard-point
(-when-let (ok (sp-get-enclosing-sexp 1))
(if (equal ";" (sp-get ok :prefix))
(sp-get ok
(goto-char :beg)
(-when-let (enc (sp-get-enclosing-sexp 1))
(sp--unwrap-sexp enc t)))
(sp--unwrap-sexp ok t)))))
(defun evil-cp-delete-or-splice-region (beg end)
"Deletes a region of text by splicing parens and deleting other
chars. Does not splice strings unless the whole expression is
contained within the region."
(let ((chars-left (- end beg)))
(while (> chars-left 0)
(cond
((evil-cp--point-in-string-or-comment)
(let* ((ending (cdr (or (sp-get-comment-bounds)
(sp-get-quoted-string-bounds))))
(diff (- (min end ending) (point))))
(delete-char diff)
(setq chars-left (- chars-left diff))))
((evil-cp--looking-at-opening-p)
(let ((other-paren (evil-cp--matching-paren-pos)))
(if (< (point) other-paren end)
(let ((char-count (sp-get (sp-get-sexp) (- :end :beg))))
(delete-char char-count)
(setq chars-left (- chars-left char-count)))
(evil-cp--splice-form)
(cl-decf chars-left))))
((evil-cp--looking-at-string-opening-p)
(let ((other-quote (evil-cp--matching-paren-pos)))
(if (< (point) other-quote end)
(let ((char-count (sp-get (sp-get-string) (- :end :beg))))
(delete-char char-count)
(setq chars-left (- chars-left char-count)))
(forward-char)
(cl-decf chars-left))))
((evil-cp--looking-at-paren-p)
(evil-cp--splice-form)
(cl-decf chars-left))
((evil-cp--looking-at-string-delimiter-p)
(forward-char)
(cl-decf chars-left))
(t
(delete-char 1)
(cl-decf chars-left))))))
(evil-define-operator evil-cp-delete-char-or-splice (beg end type register yank-handler)
"Deletes the region by splicing parentheses / string delimiters
and deleting other characters. Can be overriden by
`evil-cp-override' in visual-mode."
:motion evil-forward-char
(interactive "<R><x>")
(cond ((or (evil-cp--balanced-block-p beg end)
(evil-cp--override))
(evil-delete-char beg end type register))
((eq type 'block)
(evil-cp--yank-rectangle beg end register yank-handler)
(evil-apply-on-block #'evil-cp-delete-or-splice-region beg end nil))
(t
(if evil-cleverparens-complete-parens-in-yanked-region
(evil-cp-yank beg end type register yank-handler)
(evil-yank beg end type register yank-handler))
(evil-cp-delete-or-splice-region beg end))))
(evil-define-operator evil-cp-delete-char-or-splice-backwards
(beg end type register yank-handler)
"Just like `evil-cp-delete-char-or-splice' but acts on the preceding character."
:motion evil-backward-char
(interactive "<R><x>")
(evil-cp-delete-char-or-splice beg end type register yank-handler))
(evil-define-command evil-cp-delete-backward-word ()
"Like `evil-delete-backward-word' with added behavior when
called in front of an opening delimiter, in which case the
enclosing form gets deleted."
(if (evil-cp--looking-at-any-opening-p (1- (point)))
(let ((enc (evil-cp--get-enclosing-bounds)))
(delete-region (car enc) (cdr enc)))
(evil-delete-backward-word)))
;; Originally from:
;; http://emacs.stackexchange.com/questions/777/closing-all-pending-parenthesis
(defun evil-cp--insert-missing-parentheses (backp)
"Calling this function in a buffer with unbalanced parentheses
will have the missing parentheses be inserted at the end of the
buffer if BACKP is nil and at the beginning if it is true."
(let ((closing nil))
;; fix for the degenerate case of nothing but closing parens
(when backp (save-excursion (insert " ")))
(save-excursion
(while (condition-case nil
(progn
(if (not backp)
(backward-up-list)
(forward-char)
(up-list)
(backward-char))
(let* ((syntax (syntax-after (point)))
(head (car syntax)))
(cond
((eq head (if backp 5 4))
(setq closing (cons (cdr syntax) closing)))
((member head '(7 8))
(setq closing (cons (char-after (point)) closing)))))
t)
((scan-error) nil))))
(when backp (goto-char (point-min)))
(apply #'insert (nreverse closing))
;; see above
(when backp (delete-char 1))))
(defun evil-cp--close-missing-parens (text)
"Takes a text object and inserts missing parentheses first at
the end of the text, and then, if the expression is still
unbalanced, will insert the rest of the missing parens at the
beginning."
(with-temp-buffer
(insert text)
(goto-char (point-max))
(evil-cp--insert-missing-parentheses nil)
(when (not (sp-region-ok-p (point-min) (point-max)))
(goto-char (point-min))
(evil-cp--insert-missing-parentheses t))
(buffer-string)))
(defun evil-cp--region-has-unbalanced-string-p (beg end)
"Predicate for checking if a region contains an unbalanced string."
(not (cl-evenp (count-matches (sp--get-stringlike-regexp) beg end))))
(defun evil-cp--yank-characters
(beg end &optional register yank-handler add-parens-p)
"Yank characters while keeping parentheses balanced. The
optional ADD-PARENS-P arg determines how to handle the missing
parentheses: if nil, the non-balanced parens are
ignored. Otherwise they are added to the start/beginning of the
region."
(unless (evil-cp--region-has-unbalanced-string-p beg end)
(let ((text (string-trim (filter-buffer-substring beg end))))
(when (not (evil-cp--text-balanced-p text))
(setq text (evil-cp--close-missing-parens text)))
(when yank-handler
(setq text (propertize text 'yank-handler (list yank-handler))))
(when register
(evil-set-register register text))
(when evil-was-yanked-without-register
(evil-set-register ?0 text)) ; "0 register contains last yanked text
(unless (eq register ?_)
(kill-new text)))))
(defun evil-cp--yank-rectangle (beg end &optional register yank-handler)
"Saves the rectangle defined by BEG and END into the kill-ring,
while keeping the parentheses of the region balanced."
(let ((lines (list nil)))
(evil-apply-on-rectangle #'extract-rectangle-line beg end lines)
(setq lines (nreverse (cdr lines)))
(let* ((yank-handler (list (or yank-handler #'evil-yank-block-handler)
lines
nil
'evil-delete-yanked-rectangle))
(joined (mapconcat #'identity lines "\n"))
(text (if (evil-cp--text-balanced-p joined)
joined
(evil-cp--close-missing-parens joined)))
(text (propertize text 'yank-handler yank-handler)))
(when register (evil-set-register register text))
(when evil-was-yanked-without-register (evil-set-register ?0 text))
(unless (eq register ?_)
(kill-new text)))))
(defun evil-cp--swap-regions (r1 r2)
"Transposes the regions where R1 and R2 are cons-pairs where
the car is the start and the cdr is the end of each respective
region."
(when (and r1 r2)
(transpose-regions (cdr-safe r1)
(car-safe r1)
(cdr-safe r2)
(car-safe r2))))
(defun evil-cp--new-beginning (beg end &optional shrink)
"Return a new value for BEG if POINT is inside an empty sexp.
If SHRINK is t we try to shrink the region until it is balanced
by decrementing BEG.
Copied from `evil-smartparens'."
(if (not shrink)
(min beg
(if (sp-point-in-empty-sexp)
(evil-cp--point-after
(sp-backward-up-sexp))
(point-max)))
(let ((region (string-trim (buffer-substring-no-properties beg end))))
(unless (string-blank-p region)
(cond ((sp-point-in-empty-sexp)
;; expand region if we're in an empty sexp
(setf end (save-excursion (sp-backward-up-sexp) (point))))
;; reduce region if it's unbalanced due to selecting too much
(t (while (not (or (sp-region-ok-p beg end)
(= beg end)))
(cl-incf beg)))))
(when (= beg end)
(evil-cp--fail)))
beg))
(defun evil-cp--new-ending (beg end &optional no-error)
"Find the largest safe region delimited by BEG END.
Copied from `evil-smartparens'."
(let ((region (string-trim (buffer-substring-no-properties beg end))))
(unless (string-blank-p region)
(cond ((sp-point-in-empty-sexp)
;; expand region if we're in an empty sexp
(setf end (save-excursion (sp-up-sexp) (point))))
;; reduce region if it's unbalanced due to selecting too much
(t (while (not (or (sp-region-ok-p beg end)
(= beg end)))
(cl-decf end))))))
(if (and (not no-error) (= beg end))
(evil-cp--fail)
end))
(defun evil-cp--ignoring-yank (beg end type register yank-handler)
"This is a version of yank ignores unbalanced parentheses to
keep the region safe.
Copied from `evil-smartparens'."
(save-excursion
(condition-case nil
(let ((new-beg (evil-cp--new-beginning beg end))
(new-end (evil-cp--new-ending beg end)))
(if (and (= new-end end)
(= new-beg beg))
(evil-yank beg end type register yank-handler)
(evil-yank new-beg new-end type register yank-handler)))
(error (let* ((beg (evil-cp--new-beginning beg end :shrink))
(end (evil-cp--new-ending beg end)))
(evil-yank beg end type register yank-handler))))))
(defun evil-cp-yank-form-handler (text)
"Copy of `evil-yank-line-handler' that handles the lack of
newlines at the end of forms."
(let* ((pcount (or evil-paste-count 1))
(text (apply #'concat
(cdr (-interleave
(make-list pcount "\n")
(make-list pcount (string-trim text))))))
(opoint (point)))
(remove-list-of-text-properties
0 (length text) yank-excluded-properties text)
(cond
((eq this-command 'evil-paste-before)
(evil-move-beginning-of-line)
(evil-move-mark (point))
(insert text "\n")
(setq evil-last-paste
(list 'evil-paste-before
evil-paste-count
opoint
(mark t)
(point)))
(evil-set-marker ?\[ (mark))
(evil-set-marker ?\] (1- (point)))
(evil-exchange-point-and-mark)
(back-to-indentation))
((eq this-command 'evil-paste-after)
(evil-move-end-of-line)
(evil-move-mark (point))
(insert "\n")
(insert text)
(evil-set-marker ?\[ (1+ (mark)))
(evil-set-marker ?\] (1- (point)))
(setq evil-last-paste
(list 'evil-paste-after
evil-paste-count
opoint
(mark t)
(point)))
(evil-move-mark (1+ (mark t)))
(evil-exchange-point-and-mark)
(back-to-indentation))
(t
(insert text)))))
(evil-define-operator evil-cp-yank (beg end type register yank-handler)
"Saves the characters in motion into the kill-ring while
respecting parentheses."
:move-point nil
:repeat nil
(interactive "<R><x><y>")
(let ((evil-was-yanked-without-register
(and evil-was-yanked-without-register (not register)))
(safep (sp-region-ok-p beg end)))
(cond
;; block which is balanced
((and (eq type 'block) (evil-cp--balanced-block-p beg end))
(evil-yank-rectangle beg end register yank-handler))
;; unbalanced block, add parens
((and (eq type 'block)
evil-cleverparens-complete-parens-in-yanked-region)
(evil-cp--yank-rectangle beg end register yank-handler))
;; unbalanced block, ignoring parens
((eq type 'block)
(evil-cp--fail))
;; balanced region, or override
((or safep (evil-cp--override))
(evil-yank beg end type register yank-handler))
;; unbalanced line, fill parens
((and (eq type 'line)
evil-cleverparens-complete-parens-in-yanked-region)
(evil-cp--yank-characters beg end register
'evil-cp-yank-form-handler))
((eq type 'line)
(evil-cp--ignoring-yank beg end type register
'evil-yank-line-handler))
;; unbalanced, fill missing
(evil-cleverparens-complete-parens-in-yanked-region
(evil-cp--yank-characters beg end register yank-handler))
;; unbalanced, ignore extra
(t
(evil-cp--ignoring-yank beg end type register yank-handler)))))
(evil-define-operator evil-cp-yank-line (beg end type register)
"Acts like `paredit-copy-sexp-as-kill'."
:motion evil-line
:move-point nil
(interactive "<R><x>")
(cond
((evil-visual-state-p)
(let ((beg (point-at-bol))
(end (if (eq type 'line)
(1- end)
(save-excursion
(goto-char end)
(point-at-eol)))))
(evil-exit-visual-state)
(evil-cp--yank-characters beg end type register)))
;; Copied and modified from paredit.el
((paredit-in-string-p)
(save-excursion
(when (paredit-in-string-escape-p)
(backward-char))
(evil-yank-characters (point)
(min (point-at-eol)
(cdr (paredit-string-start+end-points)))
register)))
((paredit-in-comment-p)
(evil-yank-characters (point) (point-at-eol) register))
((save-excursion (paredit-skip-whitespace t (point-at-eol))
(or (eolp) (eq (char-after) ?\; )))
(save-excursion
(if (paredit-in-char-p)
(backward-char)))
(evil-yank-characters (point) (point-at-eol) register))
(t
(save-excursion
(if (paredit-in-char-p)
(backward-char 2))
(let ((beginning (point))
(eol (point-at-eol)))
(let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol)))
(when end-of-list-p
(up-list)
(backward-char))
(evil-yank-characters
beginning
(cond
(kill-whole-line
(or (save-excursion
(paredit-skip-whitespace t)
(and (not (eq (char-after) ?\; ))
(point)))
(point-at-eol)))
((and (not end-of-list-p)
(eq (point-at-eol) eol))
eol)
(t
(point)))
register)))))))
(defun evil-cp--delete-characters (beg end)
"Deletes everything except unbalanced parentheses / string
delimiters in the region defined by BEG and END."
(let ((chars-left (- end beg)))
(goto-char beg)
(while (> chars-left 0)
(cond
((evil-cp--point-in-comment)
(let* ((ending (cdr (or (sp-get-comment-bounds)
(sp-get-quoted-string-bounds))))
(diff (- (min end ending) (point))))
(delete-char diff)
(setq chars-left (- chars-left diff))))
((evil-cp--looking-at-any-opening-p)
(let ((other-end (evil-cp--matching-paren-pos)))
;; matching paren is in the range of the command
(if (<= (point) other-end end)
(let ((char-count
(evil-cp--guard-point
(sp-get (sp-get-enclosing-sexp)
(- :end :beg)))))
(delete-char char-count)
(setq chars-left (- chars-left char-count)))
(forward-char)
(cl-decf chars-left))))
((and (evil-cp--looking-at-any-closing-p) (= chars-left 1))
(cl-decf chars-left))
((evil-cp--looking-at-any-closing-p)
(forward-char 1)
(cl-decf chars-left))
(t
(delete-char 1)
(cl-decf chars-left))))))
(defun evil-cp-first-non-blank-non-opening ()
"Like `evil-first-non-blank' but also skips opening parentheses
and string delimiters."
(interactive)
(evil-first-non-blank)
(while (and (evil-cp--looking-at-any-opening-p)
(<= (point) (point-at-eol)))
(forward-char)))
(evil-define-operator evil-cp-delete (beg end type register yank-handler)
"A version of `evil-delete' that attempts to leave the region
its acting on with balanced parentheses. The behavior of
kill-ring is determined by the
`evil-cleverparens-complete-parens-in-yanked-region' variable."
:move-point nil
(interactive "<R><x><y>")
(let ((safep (evil-cp-region-ok-p beg end)))
(evil-cp-yank beg end type register yank-handler)
(cond ((or (= beg end)
(evil-cp--override)
(and (eq type 'block) (evil-cp--balanced-block-p beg end))
(and safep (not (eq type 'block))))
(evil-delete beg end type register yank-handler))
((eq type 'line)
(goto-char beg)
(save-excursion
(evil-cp--delete-characters
(+ beg
(save-excursion ; skip preceding whitespace
(beginning-of-line)
(sp-forward-whitespace t)))
(1- end)))
(when (and evil-cleverparens-indent-afterwards
(evil-cp--inside-any-form-p))
(save-excursion
(evil-cp--backward-up-list)
(indent-sexp)))
(evil-cp-first-non-blank-non-opening)
(if (evil-cp--looking-at-any-closing-p)
(when (save-excursion
(forward-line -1)
(not (evil-cp--comment-block?)))
(forward-line -1)
(join-line 1)
(forward-line 1))
(join-line 1)))
(t (evil-cp--delete-characters beg end)))))
(evil-define-operator evil-cp-delete-line (beg end type register yank-handler)
"Kills the balanced expressions on the line until the eol."
:motion nil
:keep-visual t
:move-point nil
(interactive "<R><x>")
(cond ((evil-visual-state-p)
;; Not sure what this should do in visual-state
(let ((safep (sp-region-ok-p beg end)))
(if (not safep)
(evil-cp--fail)
(evil-delete-line beg end type register yank-handler))))
((paredit-in-string-p)
(save-excursion
(when (paredit-in-string-escape-p)
(backward-char))
(let ((beg (point))
(end (min (point-at-eol)
(cdr (paredit-string-start+end-points)))))
(evil-yank-characters beg end register)
(delete-region beg end))))
((paredit-in-comment-p)
(let ((beg (point))
(end (point-at-eol)))
(evil-yank-characters beg end register)
(delete-region beg end)))
((save-excursion (paredit-skip-whitespace t (point-at-eol))
(or (eolp) (eq (char-after) ?\; )))
(when (paredit-in-char-p) (backward-char))
;; `kill-line' inlined from from `simple.el'
;; this works but is not very evilidiomatic
(kill-region
(point)
(progn
(when (eobp) (evil-signal-at-eob))
(let ((end (save-excursion (end-of-visible-line) (point))))
(if (or (save-excursion
;; If trailing whitespace is visible,
;; don't treat it as nothing.
(unless show-trailing-whitespace
(skip-chars-forward " \t" end))
(= (point) end))
(and kill-whole-line (bolp)))
(forward-visible-line 1)
(goto-char end)))
(point))))
(t
(save-excursion
(when (paredit-in-char-p) (backward-char 2))
(let* ((beg (point))
(eol (point-at-eol))
(end-of-list-p (paredit-forward-sexps-to-kill beg eol)))
(when end-of-list-p (up-list) (backward-char))
(let ((end (cond
(kill-whole-line
(or (save-excursion
(paredit-skip-whitespace t)
(and (not (eq (char-after) ?\; ))
(point)))
(point-at-eol)))
((and (not end-of-list-p)
(eq (point-at-eol) eol))
eol)
(t
(point)))))
(evil-yank-characters beg end register)
(delete-region beg end)))))))
(evil-define-operator evil-cp-change (beg end type register yank-handler delete-func)
"Call `evil-change' while keeping parentheses balanced."
:move-point nil
(interactive "<R><x><y>")
(if (or (= beg end)
(evil-cp--override)
(and (eq type 'block) (evil-cp--balanced-block-p beg end))
(and (sp-region-ok-p beg end) (not (eq type 'block))))
(evil-change beg end type register yank-handler delete-func)
(let ((delete-func (or delete-func #'evil-cp-delete))
(nlines (1+ (- (line-number-at-pos end)
(line-number-at-pos beg))))
(opoint (save-excursion
(goto-char beg)
(line-beginning-position))))
(cond ((eq type 'line)
(save-excursion
(evil-cp--delete-characters
(+ beg
(save-excursion
(beginning-of-line)
(sp-forward-whitespace t)))
(1- end)))
(evil-cp-first-non-blank-non-opening)
(indent-according-to-mode)
(evil-insert 1))
((eq type 'block)
(evil-cp-delete beg end type register yank-handler)
(evil-insert 1 nlines))
(t
(funcall delete-func beg end type register yank-handler)
(evil-insert 1))))))
(evil-define-operator evil-cp-change-line (beg end type register yank-handler)
"Change to end of line while respecting parentheses."
:motion evil-end-of-line
(interactive "<R><x><y>")
(evil-cp-change beg end type register yank-handler #'evil-cp-delete-line))
(evil-define-operator evil-cp-change-whole-line (beg end type register yank-handler)
"Change whole line while respecting parentheses."
:motion evil-line
(interactive "<R><x>")
(if (sp-region-ok-p beg end)
(evil-change beg end type register yank-handler)
(evil-cp-first-non-blank-non-opening)
(evil-cp-change beg end type register yank-handler #'evil-cp-delete-line)))
;;; Movement ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(evil-define-motion evil-cp-forward-sexp (count)
"Motion for moving forward by a sexp via `sp-forward-sexp'."
:type exclusive
(let ((count (or count 1)))
(sp-forward-sexp count)))
(evil-define-motion evil-cp-backward-sexp (count)
"Motion for moving backwward by a sexp via `sp-backward-sexp'."
:type exclusive
(let ((count (or count 1)))
(sp-backward-sexp count)))
(evil-define-motion evil-cp-beginning-of-defun (count)
"Motion for moving to the beginning of a defun (i.e. a top
level sexp)."
:type inclusive
(let ((count (or count 1)))
(beginning-of-defun count)))
(evil-define-motion evil-cp-end-of-defun (count)
"Motion for moving to the end of a defun (i.e. a top level sexp)."
:type inclusive
(let ((count (or count 1)))
(if (evil-cp--looking-at-closing-p)
(forward-char 2))
(end-of-defun count)
(backward-char (if (eobp) 1 2))))
;; TODO: this looks ugly
(defun evil-cp--paren-navigation-helper (move-dir paren-side)
(let ((move-fn (cl-case move-dir
(:next 'forward-char)
(:previous 'backward-char)))
(the-end (cl-case move-dir
(:next 'point-max)
(:previous 'point-min)))
(paren-p (cl-case paren-side
(:opening 'evil-cp--looking-at-any-opening-p)
(:closing 'evil-cp--looking-at-any-closing-p)))
(pt-orig (point))
done-p)
(when (funcall paren-p) (funcall move-fn))
(while (not done-p)
(cond
((= (point) (funcall the-end))
(setq done-p t)
(goto-char pt-orig))
((funcall paren-p)
(setq done-p t))
(t
(funcall move-fn))))))
(evil-define-motion evil-cp-next-opening (count)
"Motion for moving to the next open parentheses."
:move-point nil
:type inclusive
(let ((count (or count 1)))
(dotimes (i count)
(evil-cp--paren-navigation-helper :next :opening))))
(evil-define-motion evil-cp-previous-opening (count)
"Motion for moving to the previous open parentheses."
:move-point nil
:type inclusive
(let ((count (or count 1)))
(dotimes (i count)
(evil-cp--paren-navigation-helper :previous :opening))))
(evil-define-motion evil-cp-next-closing (count)
"Motion for moving to the next closing parentheses."
:move-point nil
:type inclusive
(let ((count (or count 1)))
(dotimes (i count)
(evil-cp--paren-navigation-helper :next :closing))))
(evil-define-motion evil-cp-previous-closing (count)
"Motion for moving to the previous closing parentheses."
:move-point nil
:type inclusive
(let ((count (or count 1)))
(dotimes (i count)
(evil-cp--paren-navigation-helper :previous :closing))))
(evil-define-motion evil-cp-backward-up-sexp (count)
"Motion for moving backward up to the previous level of
parentheses. Basically just wraps `sp-backward-up-sexp' as an
evil-motion."
:move-point nil
:type inclusive
(let ((count (or count 1)))
;; for some reason calling `sp-backward-up-sexp' with a large `count'
;; doesn't move the point at all
(dotimes (i count)
(sp-backward-up-sexp))))
(evil-define-motion evil-cp-up-sexp (count)
"Motion for moving up to the previous level of parenteheses.
The same as `sp-up-sexp', but leaves the point on top of the
closing paren."
:move-point nil
:type inclusive
(let ((count (or count 1)))
(when (evil-cp--looking-at-closing-p) (forward-char))
(dotimes (i count)
(sp-up-sexp))
(backward-char)))
(defun forward-evil-cp-symbol (&optional count)
"Move forward COUNT \"WORDS\".
Moves point COUNT WORDS forward or (- COUNT) WORDS backward if
COUNT is negative. Point is placed after the end of the WORD (if
forward) or at the first character of the WORD (if backward). A
WORD is a sequence of non-whitespace characters
'[^\\n\\r\\t\\f ]', or an empty line matching ^$."
(evil-forward-nearest
count
#'(lambda (&optional cnt)
(let ((pnt (point)))
(forward-symbol cnt)
(if (= pnt (point)) cnt 0)))
#'forward-evil-empty-line))
;; incomplete
(defun forward-evil-cp-word (&optional count)
"TODO: This is exactly the same as `forward-evil-word' and not used
for anything right now. It would be nice to skip delimiters on
the small-word movements as well but I couldn't figure out how to
change this to make it work. Pull requests welcome."
(evil-forward-nearest
count
#'(lambda (&optional cnt)
(let ((word-separating-categories evil-cjk-word-separating-categories)
(word-combining-categories evil-cjk-word-combining-categories)
(pnt (point)))
(forward-word cnt)
(if (= pnt (point)) cnt 0)))
#'(lambda (&optional cnt)
(evil-forward-chars "^[:word:]\n\r\t\f " cnt))
#'forward-evil-empty-line))
(defun evil-cp--forward-X-begin (thing count)
"TODO: see `forward-evil-cp-word' which is currently not
working. Could be used to implement a future
`evil-cp-forward-word-begin' the same way that
`evil-cp-forward-symbol-begin' is defined."
(let ((orig (point)))
(evil-signal-at-bob-or-eob count)
(cond ((not (evil-operator-state-p))
(evil-forward-beginning thing count))
((and evil-want-change-word-to-end
(eq evil-this-operator #'evil-change)
(< orig (or (cdr-safe (bounds-of-thing-at-point thing)) orig)))
(forward-thing thing count))
(t (prog1 (evil-forward-beginning thing count)
(when (and (> (line-beginning-position) orig)
(looking-back "^[[:space:]]*" (line-beginning-position)))
(evil-move-end-of-line 0)
(while (and (looking-back "^[[:space:]]+$" (line-beginning-position))
(not (<= (line-beginning-position) orig)))
(evil-move-end-of-line 0))
(when (bolp) (forward-char))))))))
(evil-define-motion evil-cp-forward-symbol-begin (count)
:type exclusive
(evil-cp--forward-X-begin (if evil-cleverparens-move-skip-delimiters
'evil-cp-symbol
'evil-symbol)
(or count 1)))
(defun evil-cp--forward-X-end (thing count)
(evil-signal-at-bob-or-eob count)
(unless (and (evil-operator-state-p)
(= 1 count)
(let ((bnd (bounds-of-thing-at-point thing)))
(and bnd
(= (car bnd) (point))
(= (cdr bnd) (1+ (point)))))
(looking-at "[[:word:]]"))
(evil-forward-end thing count)))
(evil-define-motion evil-cp-forward-symbol-end (count)
"Copy of `evil-forward-word-end' using 'evil-symbol for the
movement."
:type inclusive
(evil-cp--forward-X-end (if evil-cleverparens-move-skip-delimiters
'evil-cp-symbol
'evil-symbol)
(or count 1)))
(evil-define-motion evil-cp-backward-symbol-begin (count)
"Copy of `evil-backward-word-begin' using 'evil-symbol for the
movement."
:type exclusive
(let ((thing (if evil-cleverparens-move-skip-delimiters
'evil-cp-symbol
'evil-symbol)))
(evil-signal-at-bob-or-eob (- (or count 1)))
(evil-backward-beginning thing count)))
(evil-define-motion evil-cp-backward-symbol-begin (count)
"Copy of `evil-backward-word-begin' using 'evil-symbol for the
movement."
:type exclusive
(let ((thing (if evil-cleverparens-move-skip-delimiters
'evil-cp-symbol
'evil-symbol)))
(evil-signal-at-bob-or-eob (- (or count 1)))
(evil-backward-beginning thing count)))
(evil-define-motion evil-cp-backward-symbol-end (count)
"Copy of `evil-backward-word-end' using 'evil-symbol for the
movement."
:type inclusive
(let ((thing (if evil-cleverparens-move-skip-delimiters
'evil-cp-symbol
'evil-symbol)))
(evil-signal-at-bob-or-eob (- (or count 1)))
(evil-backward-end thing count)))
;;; Additional Operations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; This is a lot slower than the approach below, but should behave correctly
;; with modes that use `sp-sexp-prefix'. Might be worth re-writing in the
;; future.
(defun evil-cp--slurp-backward-from-opening ()
(catch 'abort
(let ((depth 1))
(save-excursion
(while (evil-cp--first-form-of-form-p)
(evil-cp--backward-up-list)
(cl-incf depth))
(when (and (= (evil-cp--point-after (backward-sexp)) 1)
(evil-cp--looking-at-whitespace-p))
(throw 'abort nil)))
(forward-char)
(dotimes (_ depth)
(sp-backward-slurp-sexp))
(while (looking-back " ")
(backward-delete-char 1))
(insert " ")
(backward-sexp)
(backward-char)
(indent-sexp))))
(defun evil-cp--slurp-forward-from-closing ()
(let* ((pt (point))
(next-thing
(save-excursion
(while (evil-cp--last-form-of-form-p)
(evil-cp--up-list))
(evil-cp--up-list)
(when (not (eq :eobp (evil-cp--skip-whitespace-and-comments)))
(evil-cp--movement-bounds (forward-sexp)))))
delims)
(when next-thing
(save-excursion
(goto-char (1- (car next-thing)))
(while (<= pt (point))
(cond
((and (evil-cp--looking-at-any-closing-p)
(not (evil-cp--point-in-string-or-comment)))
(push (following-char) delims)
(delete-char 1))
((and (looking-at-p " ")
(not (evil-cp--point-in-string-or-comment)))
(delete-char 1)))
(backward-char)))
(if (looking-at-p evil-cp--ws-regexp)
(evil-cp--skip-whitespace-and-comments)
(insert " "))
(forward-sexp)
(insert (apply #'string delims))
(backward-char (length delims))
(save-excursion
(goto-char pt)
(evil-cp--backward-up-list)
(indent-sexp)))))
(defun evil-cp--singleton-list-p ()
"Checks that the list the point is in contains more than one
sexp inside it. Should not be called in an empty list."
(when (evil-cp--inside-any-form-p)
(save-excursion
(goto-char (1+ (car (evil-cp--get-enclosing-bounds))))
(forward-sexp)
(evil-cp--cant-forward-sexp-p))))
(evil-define-command evil-cp-< (count)
"Slurping/barfing operation that acts differently based on the
points location in the form.
When point is on the opening delimiter of the form boundary, it
will slurp the next element backwards while maintaining the
location of the point in the original delimiter.
foo [(]bar baz) -> [(]foo bar baz)
When point is on the closing delimiter, it will barf the
rightmost element of the form forward, again maintaining the
location of the point in the original delimiter. Only works if
the form has more than one element.
(bar baz foo[)] -> (bar baz[)] foo
When point is in the middle of a form, it will act as like a
regular forward-barf."
(interactive "<c>")
(setq count (or count 1))
(when (evil-cp--inside-any-form-p)
(dotimes (_ count)
(cond
((evil-cp--looking-at-any-opening-p)
(evil-cp--slurp-backward-from-opening))
((evil-cp--looking-at-any-closing-p)
(when (not (sp-point-in-empty-sexp))
(when (not (evil-cp--singleton-list-p))
(sp-forward-barf-sexp)
(sp-backward-sexp)
(evil-cp-previous-closing))))
((evil-cp--singleton-list-p)
(save-excursion
(while (and (evil-cp--singleton-list-p)
(not (evil-cp--outside-form-p)))
(evil-cp--backward-up-list))
(when (not (evil-cp--singleton-list-p))
(forward-char)
(sp-forward-barf-sexp))))
(t
(sp-forward-barf-sexp))))))
(evil-define-command evil-cp-> (count)
"Slurping/barfing operation that acts differently based on the
points location in the form.
When cursor is on the opening delimiter of the form boundary, it
will barf the first element of the form while maintaining the
location of the cursor on the original delimiter.
[(]foo bar baz) -> foo [(]bar baz)
When the cursor is on a closing delimiter, it will slurp the
first sexp after the last closing delimiter from point moving it
inside the form of which the cursor is currently on top of while
maintaining the location of the cursor on the current closing
delimiter.
(((bar baz[)])) foo -> (((bar baz foo[)]))
When point is in the middle of a form, it will act as like a
regular forward-slurp."
(interactive "<c>")
(setq count (or count 1))
(when (evil-cp--inside-any-form-p)
(dotimes (_ count)
(cond
((evil-cp--looking-at-any-closing-p)
(evil-cp--slurp-forward-from-closing))
((evil-cp--looking-at-any-opening-p)
(when (not (or (evil-cp--looking-at-empty-form)
(evil-cp--singleton-list-p)))
(forward-char)
(sp-backward-barf-sexp)
(sp-forward-sexp)
(evil-cp-next-opening)))
(t
(sp-forward-slurp-sexp))))))
(defun evil-cp--symbol-bounds ()
(save-excursion
(if (looking-at-p "\\_<")
(evil-cp--sp-obj-bounds (sp-get-symbol))
(when (sp-point-in-symbol)
(evil-cp--sp-obj-bounds (sp-get-symbol))))))
(defun evil-cp--cant-forward-sexp-p ()
(save-excursion
(condition-case nil
(forward-sexp)
(error t))))
(defun evil-cp--cant-backward-sexp-p ()
(save-excursion
(condition-case nil
(backward-sexp)
(error t))))
(defun evil-cp--last-symbol-of-form-p ()
(save-excursion
(-when-let (end (cdr (evil-cp--symbol-bounds)))
(goto-char end)
(evil-cp--cant-forward-sexp-p))))
(defun evil-cp--first-symbol-of-form-p ()
(save-excursion
(-when-let (beg (car (evil-cp--symbol-bounds)))
(goto-char beg)
(evil-cp--cant-backward-sexp-p))))
(defun evil-cp--first-form-of-form-p ()
(when (evil-cp--inside-any-form-p)
(save-excursion
(evil-cp--guard-point
(evil-cp-backward-up-sexp)
(evil-cp--cant-backward-sexp-p)))))
(defun evil-cp--last-form-of-form-p ()
(when (evil-cp--inside-any-form-p)
(save-excursion
(evil-cp--guard-point
(evil-cp--up-list)
(evil-cp--cant-forward-sexp-p)))))
(defun evil-cp--next-thing-bounds* (by-line-p)
"Fetches the bounds for the next \"thing\" from the location of
point. The thing may be a symbol, a form, a line or a comment
block. When BY-LINE-P is true, defaults to swapping by line and
only swaps by form if the next line is not safe. May move the
point. Acts as a helper for `evil-cp--next-thing-bounds'."
(let* ((next-form
(save-excursion
(evil-cp--skip-whitespace-and-comments)
(evil-cp--movement-bounds (forward-sexp))))
(next-line
(or (when (not (= (point-at-eol) (point-max)))
(save-excursion
(forward-line)
(evil-cp--comment-block-bounds)))
(evil-cp--safe-line-bounds (forward-line)))))
(cond
(by-line-p (or next-line next-form))
(evil-cleverparens-drag-ignore-lines next-form)
((and next-line next-form
(<= (cdr next-form) (cdr next-line)))
next-form)
(t (or next-line next-form)))))
(defun evil-cp--previous-thing-bounds* (by-line-p)
"Fetches the bounds for the previous \"thing\" from the
location of point. The thing may be a symbol, a form, a line or
a comment block. When BY-LINE-P is true, defaults to swapping
by line and only swaps by form if the next line is not safe.
May move the point. Acts as a helper for
`evil-cp--previous-thing-bounds'."
(let ((prev-form
(save-excursion
(backward-sexp)
(when (not (bobp))
(evil-cp--next-sexp-bounds))))
(prev-line
(when (not (= (point-at-bol) (point-min)))
(or (save-excursion
(forward-line -1)
(evil-cp--comment-block-bounds))
(evil-cp--safe-line-bounds (forward-line -1))))))
(cond
(by-line-p (or prev-line prev-form))
(evil-cleverparens-drag-ignore-lines prev-form)
((and prev-line prev-form
(>= (cdr prev-form) (cdr prev-line)))
prev-form)
(t (or prev-line prev-form)))))
(defun evil-cp--swap-with-next (this-bounds &optional by-line-p)
"Swaps the region defined by THIS-BOUNDS with the next thing
from the end of THIS-BOUNDS."
(when this-bounds
(let ((that-bounds (evil-cp--next-thing-bounds
(goto-char (cdr this-bounds))
by-line-p)))
(if (not (= (point) (point-at-eol)))
(evil-cp--swap-regions this-bounds that-bounds)
(progn
(backward-char)
(evil-cp--swap-regions this-bounds that-bounds)
(forward-char)))
(when evil-cleverparens-indent-afterwards
(save-excursion
(goto-char (car this-bounds))
(evil-cp--backward-up-list t)
(indent-sexp))))))
(defun evil-cp--swap-with-previous (this-bounds &optional by-line-p)
"Swaps the region defined by THIS-BOUNDS with the previous
thing from the beginning of of THIS-BOUNDS."
(when this-bounds
(let ((that-bounds (evil-cp--previous-thing-bounds
(goto-char (car this-bounds))
by-line-p)))
(when that-bounds
(if (not (= (point) (point-at-eol)))
(evil-cp--swap-regions this-bounds that-bounds)
(backward-char)
(evil-cp--swap-regions this-bounds that-bounds)
(forward-char))
(when evil-cleverparens-indent-afterwards
(save-excursion
(goto-char (car that-bounds))
(evil-cp--backward-up-list t)
(indent-sexp)))))))
(defun evil-cp--comment-block? ()
"Checks whether point is on a line that starts with whitespace
or a comment."
(save-excursion
(evil-first-non-blank)
(looking-at-p sp-comment-char)))
(defun evil-cp--comment-block-bounds (&optional pos)
"Gets the bounds for a comment block (i.e. a group of lines
that start with whitespace or a comment)."
(save-excursion
(when pos (goto-char pos))
(when (evil-cp--comment-block?)
(let (beg end)
(save-excursion
(beginning-of-line)
(setq beg (point))
(while (and (not (bobp)) (evil-cp--comment-block?))
(forward-line -1)
(when (evil-cp--comment-block?) (setq beg (point)))))
(save-excursion
(end-of-line)
(setq end (point))
(while (and (not (eobp)) (evil-cp--comment-block?))
(forward-line 1)
(end-of-line)
(when (evil-cp--comment-block?) (setq end (point)))))
(cons beg end)))))
(evil-define-command evil-cp-drag-forward (count)
"Drags the thing under point forward. The thing can be either a
symbol, a form, a line or a comment block. Does not mess with the
depth of expressions by slurping or barfing. If the thing under
point is at the end of a form then tries to drag its \"parent\"
thing forward instead. Maintains the location of point relative
to the thing being dragged."
(interactive "<c>")
(if (evil-visual-state-p)
(let* ((v-range (evil-visual-range))
(linep (eq 'line (evil-visual-type)))
(beg (car v-range))
(end (if linep (1- (cadr v-range)) (cadr v-range))))
(when (sp-region-ok-p beg end)
(evil-cp--swap-with-next (cons beg end) linep)))
(let (drag-by-line-p)
(evil-cp--swap-with-next
(let ((sym-bounds (evil-cp--symbol-bounds))
(in-form-p (evil-cp--inside-any-form-p)))
(cond
((and sym-bounds
(not (evil-cp--point-in-string-or-comment))
(not (evil-cp--last-symbol-of-form-p)))
sym-bounds)
((and in-form-p (evil-cp--point-in-comment))
(save-excursion
(evil-cp--backward-up-list)
(evil-cp--next-sexp-bounds)))
(in-form-p
(save-excursion
(while (evil-cp--last-form-of-form-p)
(when (evil-cp--looking-at-any-opening-p)
(forward-char))
(evil-cp--up-list))
(evil-cp--get-enclosing-bounds t)))
(t
(setq drag-by-line-p t)
(if (and evil-cleverparens-drag-comment-blocks
(evil-cp--comment-block?))
(evil-cp--comment-block-bounds)
(evil-cp--safe-line-bounds)))))
drag-by-line-p))))
(evil-define-command evil-cp-drag-backward (count)
"Drags the thing under point backward. The thing can be either
a symbol, a form, a line or a comment block. Does not mess with
the depth of expressions by slurping or barfing. If the thing
under point is at the beginning of a form then tries to drag its
\"parent\" thing backward instead. Maintains the location of
point relative to the thing being dragged."
(interactive "<c>")
(if (evil-visual-state-p)
(let* ((v-range (evil-visual-range))
(linep (eq 'line (evil-visual-type)))
(beg (car v-range))
(end (if linep (1- (cadr v-range)) (cadr v-range))))
(when (sp-region-ok-p beg end)
(evil-cp--swap-with-previous (cons beg end) linep)))
(let (drag-by-line-p)
(evil-cp--swap-with-previous
(let ((sym-bounds (evil-cp--symbol-bounds))
(in-form-p (evil-cp--inside-any-form-p)))
(cond
((and sym-bounds
(not (evil-cp--point-in-string-or-comment))
(not (evil-cp--first-symbol-of-form-p)))
sym-bounds)
((and in-form-p (evil-cp--point-in-comment))
(save-excursion
(evil-cp--backward-up-list)
(evil-cp--next-sexp-bounds)))
(in-form-p
(save-excursion
(when (not (evil-cp--looking-at-any-opening-p))
(evil-cp--backward-up-list))
(while (evil-cp--first-form-of-form-p)
(evil-cp--backward-up-list))
(evil-cp--get-enclosing-bounds t)))
(t
(setq drag-by-line-p t)
(if (and evil-cleverparens-drag-comment-blocks
(evil-cp--comment-block?))
(evil-cp--comment-block-bounds)
(evil-cp--safe-line-bounds)))))
drag-by-line-p))))
(evil-define-operator evil-cp-substitute (beg end type register)
"Parentheses safe version of `evil-substitute'."
:motion evil-forward-char
(interactive "<R><x>")
(cond
((evil-cp--looking-at-any-opening-p)
(evil-append 1))
((evil-cp--looking-at-any-closing-p)
(evil-insert 1))
(t (evil-substitute beg end type register))))
(evil-define-command evil-cp-insert-at-end-of-form (count)
"Move point COUNT times with `sp-forward-sexp' and enter insert
mode at the end of form. Using an arbitrarily large COUNT is
guaranteened to take the point to the beginning of the top level
form."
(interactive "<c>")
(let ((line-end (point-at-eol)))
(when (or (when (sp-up-sexp count) (backward-char) t)
(-when-let (enc-end (cdr (evil-cp--top-level-bounds)))
(goto-char (1- enc-end))))
(if (<= (point) line-end)
(evil-insert 1)
(insert "\n")
(indent-according-to-mode)
(evil-insert 1)))))
(evil-define-command evil-cp-insert-at-beginning-of-form (count)
"Move point COUNT times with `sp-backward-up-sexp' and enter
insert mode at the beginning of the form. Using an arbitrarily
large COUNT is guaranteened to take the point to the beginning
of the top level form."
(interactive "<c>")
(when (or (when (sp-backward-up-sexp count) (forward-char) t)
(-when-let (enc-beg (car (evil-cp--top-level-bounds)))
(goto-char (1+ enc-beg))))
(if evil-cleverparens-use-regular-insert
(evil-insert 1)
(evil-cp-insert 1))))
(evil-define-command evil-cp-copy-paste-form (count)
"Copies the surrounding form and inserts it below itself. If
called with a single prefix-argument, will copy the top-level
sexp regardless of what level the point is currently at."
(interactive "<c>")
(setq count (or count 1))
(if (evil-cp--inside-any-form-p)
(let* ((prefixp (consp current-prefix-arg))
(bounds
(if prefixp
(evil-cp--top-level-bounds)
(evil-cp--get-enclosing-bounds t)))
(beg (car bounds))
(end (cdr bounds))
(offset (- end (point)))
(text (buffer-substring-no-properties beg end)))
(when (and beg end)
(if prefixp
(progn
(end-of-defun)
(insert "\n" text "\n"))
(when (evil-cp--looking-at-any-opening-p)
(forward-char))
(sp-up-sexp)
(dotimes (_ count)
(insert "\n")
(indent-according-to-mode)
(insert text)
(when (or (looking-at "\\b")
(evil-cp--looking-at-any-opening-p))
(insert " ")
(cl-incf offset))))
(backward-char offset)))
(when (not (eobp))
(let* ((col-pos (current-column))
(end (point-at-eol))
(line (buffer-substring-no-properties (point-at-bol) end)))
(goto-char end)
(insert "\n" line)
(beginning-of-line)
(forward-char col-pos)))))
(evil-define-command evil-cp-open-below-form (count)
"Move COUNT levels up from the current form and enter
insert-state. If the last form is a top-level sexp then two
newlines are inserted instead. If point is not inside a form,
inserts two newlines and enters insert-state between them.
Note thath the COUNT parameter doesn't affect the amount of
times that the inserted text gets output into the buffer, unlike
in `evil-open-below'."
(interactive "<c>")
(setq count (or count 1))
(if (not (evil-cp--inside-any-form-p))
(progn
(insert "\n\n")
(forward-line -1)
(evil-insert-state))
(sp-up-sexp count)
(if (save-excursion
(backward-char)
(evil-cp--top-level-form-p))
(insert "\n\n")
(insert "\n"))
(indent-according-to-mode)
(evil-insert-state)))
(evil-define-command evil-cp-open-above-form (count)
"Move COUNT levels backwards up from the current form and
enter insert-state. If the form is a top-level sexp then two
newlines are inserted instead.
Note thath the COUNT parameter doesn't affect the amount of
times that the inserted text gets output into the buffer, unlike
in `evil-open-below'."
(interactive "<c>")
(setq count (or count 1))
(sp-backward-up-sexp count)
(save-excursion
(if (evil-cp--top-level-form-p)
(insert "\n\n")
(insert "\n"))
(indent-according-to-mode)
(evil-insert 1)))
(defun evil-cp--kill-sexp-range (count)
(save-excursion
(when (not (evil-cp--inside-any-form-p))
(sp-forward-whitespace))
(let* ((beg (point))
(end (point))
(enc-range (evil-cp--get-enclosing-bounds))
(e-beg (or (car enc-range) (point)))
(e-end (or (cdr enc-range) (point)))
(n (or count 1))
(ok t))
(while (and (> n 0) ok)
(setq ok (sp-forward-sexp 1))
(when ok
(sp-get ok
(when (> :end end) (setq end :end))
(when (and (>= :end e-end) (> beg :beg))
(setq beg :beg))))
(setq n (1- n)))
(cons beg end))))
(defun evil-cp--del-characters (beg end &optional register yank-handler)
(evil-yank-characters beg end register yank-handler)
(delete-region beg end))
(evil-define-command evil-cp-yank-sexp (count &optional register)
"Copies COUNT many sexps from point to the kill-ring.
Essentially a less greedy version of `evil-cp-yank-line'. When
called with \\[universal-argument], copies everything from point
to the end of the the current form."
(interactive "<c><x>")
(if (and (consp current-prefix-arg)
(evil-cp--inside-any-form-p))
(sp-get (sp-get-enclosing-sexp)
(evil-yank-characters (point) (1- :end)))
(let* ((range (evil-cp--kill-sexp-range count))
(beg (car range))
(end (cdr range)))
(evil-yank-characters (car range) (cdr range) register))))
(evil-define-command evil-cp-delete-sexp (count &optional register)
"Kills COUNT many sexps from point. Essentially a less greedy
version of `evil-cp-delete-line'. When called with
\\[universal-argument], deletes everything from point to the end
of the the current form."
(interactive "<c><x>")
(if (and (consp current-prefix-arg)
(evil-cp--inside-any-form-p))
(sp-get (sp-get-enclosing-sexp)
(evil-cp--del-characters (point) (1- :end)))
(let* ((range (evil-cp--kill-sexp-range count))
(beg (car range))
(end (cdr range)))
(evil-cp--del-characters beg end register))))
(evil-define-command evil-cp-change-sexp (count &optional register)
"Like `evil-cp-delete-sexp' but enters insert mode after the
operation. When called with \\[universal-argument], deletes
everything from point to the end of the the current form before
entering insert-state."
(interactive "<c><x>")
(evil-cp-delete-sexp count register)
(evil-insert-state))
(defun evil-cp-top-level-yank-handler (text)
(insert (concat "\n" (string-trim text) "\n"))
(backward-char 1))
(evil-define-command evil-cp-yank-enclosing (count &optional register)
"Copies the enclosing form to kill-ring. With COUNT, yanks the
nth form upwards instead. When called with a raw prefix argument,
yanks the top-level form and deletes the leftover whitespace,
while adding a yank-handler that inserts two newlines at the end
and beginning of the copied top-level form."
(interactive "<c><x>")
(when (evil-cp--inside-any-form-p)
(save-excursion
(if (consp current-prefix-arg)
(progn
(beginning-of-defun)
(sp-get (sp-get-sexp)
(evil-yank-characters
:beg
:end
register
#'evil-cp-top-level-yank-handler)))
(evil-cp-backward-up-sexp count)
(evil-cp--guard-point
(sp-get (sp-get-enclosing-sexp)
(evil-yank-characters :beg :end register)))))))
(defun evil-cp--should-join-line-p ()
(save-excursion
(beginning-of-line)
(skip-chars-forward "\s")
(or (evil-cp--looking-at-any-closing-p)
(= (point) (point-at-eol)))))
(evil-define-command evil-cp-delete-enclosing (count &optional register)
"Kills the enclosing form. With COUNT, kills the nth form
upwards instead. When called with a raw prefix argument, kills
the top-level form and deletes the extra whitespace."
(interactive "<c><x>")
(when (evil-cp--inside-any-form-p)
(if (consp current-prefix-arg)
(progn
(beginning-of-defun)
(sp-get (sp-get-sexp)
(evil-cp--del-characters
:beg
:end
register
#'evil-cp-top-level-yank-handler))
(sp-backward-whitespace)
(let ((del-me (save-excursion (sp-forward-whitespace t))))
(delete-char (- del-me 2))
(forward-char)))
(evil-cp-backward-up-sexp count)
(evil-cp--guard-point
(sp-get (sp-get-enclosing-sexp)
(evil-cp--del-characters :beg :end register)))
(when (evil-cp--should-join-line-p)
(forward-line -1)
(join-line 1)
(end-of-line))
(when (looking-back "\s")
(delete-backward-char 1)))))
(evil-define-command evil-cp-change-enclosing (count &optional register)
"Calls `evil-cp-delete-enclosing' and enters insert-state."
(interactive "<c><x>")
(when (evil-cp--inside-any-form-p)
(evil-cp-delete-enclosing count register)
(when (consp current-prefix-arg)
(insert "\n\n")
(backward-char 1))
(evil-insert-state)))
(defun evil-cp-raise-form (&optional count)
"Raises the form under point COUNT many times."
(interactive "P")
(let ((count (or count 1)))
(dotimes (_ count)
(when (evil-cp--inside-any-form-p)
(let ((orig (point)) offset)
(evil-cp--guard-point
(sp-beginning-of-sexp))
(backward-char)
(setq offset (- orig (point)))
(sp-raise-sexp)
(forward-char offset))))))
(defun evil-cp--wrap-region-with-pair (pair start end)
"Helper that inserts a pair as indicated by PAIR at positions
START and END. Performs no safety checks on the regions."
(-when-let (this-pair (evil-cp-pair-for pair))
(let ((open (car this-pair))
(close (cdr this-pair)))
(save-excursion
(goto-char start)
(insert open)
(goto-char end)
(insert close)))))
(defun evil-cp--wrap-next (pair count)
(let ((pt-orig (point))
(this-pair (evil-cp-pair-for pair)))
(when (sp-point-in-symbol)
(sp-get (sp-get-symbol)
(goto-char :beg)))
(let ((start (point))
(open (car this-pair))
(close (cdr this-pair))
(enc-end (sp-get (sp-get-enclosing-sexp)
(when :end (1- :end)))))
(sp-forward-sexp count)
(setq end (if enc-end (min (point) enc-end) (point)))
(if (= end pt-orig)
(goto-char pt-orig)
(goto-char start)
(insert open)
(goto-char (1+ end))
(insert close)
(goto-char start)
(indent-region start end)
(forward-char (1+ (- pt-orig start)))))))
(defun evil-cp--wrap-previous (pair count)
(let ((pt-orig (point))
(this-pair (evil-cp-pair-for pair)))
(when (and (sp-point-in-symbol) (not (eobp))) ; bug in `sp-point-in-symbol'?
(sp-get (sp-get-symbol)
(goto-char :end)))
(let ((start (point))
(open (car this-pair))
(close (cdr this-pair))
(enc-beg (sp-get (sp-get-enclosing-sexp)
(when :beg (1+ :beg)))))
(sp-backward-sexp count)
(setq beg (if enc-beg (max (point) enc-beg) (point)))
(when (not (= pt-orig beg))
(goto-char beg)
(insert open)
(goto-char (1+ start))
(insert close)
(goto-char start)
(indent-region beg start)
(backward-char (- (point) pt-orig 1))))))
(defun evil-cp-universal-invoke-arg-count ()
"Gets the count for how many times the universal prefix
argument was invoked, i.e. for \\[universal-argument]
\\[universal-argument] it would return 2."
(when (consp current-prefix-arg)
(truncate (log (car current-prefix-arg) 4))))
(evil-define-command evil-cp-wrap-next-round (count)
"Wraps the next COUNT sexps inside parentheses. If the point is
inside a symbol, that symbol is treated as the first sexp to
wrap.
When called with \\[universal-argument], wraps the current
enclosing form and the next N forms, where N is the count for how
many times the \\[universal-argument] was invoked."
(interactive "<c>")
(setq count (or count 1))
(if (consp current-prefix-arg)
(let ((count (evil-cp-universal-invoke-arg-count)))
(save-excursion
(sp-backward-up-sexp)
(evil-cp--wrap-next "(" count))
(evil-cp--backward-up-list))
(evil-cp--wrap-next "(" count)))
(evil-define-command evil-cp-wrap-previous-round (count)
"Wraps the previous COUNT sexps inside parentheses. If the point is
inside a symbol, that symbol is treated as the first sexp to
wrap.
When called with \\[universal-argument], wraps the current
enclosing form and the previous N forms, where N is the count for how
many times the \\[universal-argument] was invoked."
(interactive "<c>")
(setq count (or count 1))
(if (consp current-prefix-arg)
(let ((count (evil-cp-universal-invoke-arg-count)))
(save-excursion
(sp-up-sexp)
(evil-cp--wrap-previous "(" count))
(evil-cp--up-list))
(evil-cp--wrap-previous "(" count)))
(evil-define-command evil-cp-wrap-next-square (count)
"Wraps the next COUNT sexps inside square braces. If the point
is inside a symbol, that symbol is treated as the first sexp to
wrap.
When called with \\[universal-argument], wraps the current
enclosing form and the next N forms, where N is the count for how
many times the \\[universal-argument] was invoked."
(interactive "<c>")
(setq count (or count 1))
(if (consp current-prefix-arg)
(let ((count (evil-cp-universal-invoke-arg-count)))
(save-excursion
(sp-backward-up-sexp)
(evil-cp--wrap-next "[" count))
(evil-cp--backward-up-list))
(evil-cp--wrap-next "[" count)))
(evil-define-command evil-cp-wrap-previous-square (count)
"Wraps the previous COUNT sexps inside square braces. If the
point is inside a symbol, that symbol is treated as the first
sexp to wrap.
When called with \\[universal-argument], wraps the current
enclosing form and the previous N forms, where N is the count for how
many times the \\[universal-argument] was invoked."
(interactive "<c>")
(setq count (or count 1))
(if (consp current-prefix-arg)
(let ((count (evil-cp-universal-invoke-arg-count)))
(save-excursion
(sp-up-sexp)
(evil-cp--wrap-previous "[" count))
(evil-cp--up-list))
(evil-cp--wrap-previous "[" count)))
(evil-define-command evil-cp-wrap-next-curly (count)
"Wraps the next COUNT sexps inside curly braces. If the point
is inside a symbol, that symbol is treated as the first sexp to
wrap.
When called with \\[universal-argument], wraps the current
enclosing form and the next N forms, where N is the count for how
many times the \\[universal-argument] was invoked."
(interactive "<c>")
(setq count (or count 1))
(if (consp current-prefix-arg)
(let ((count (evil-cp-universal-invoke-arg-count)))
(save-excursion
(sp-backward-up-sexp)
(evil-cp--wrap-next "{" count))
(evil-cp--backward-up-list))
(evil-cp--wrap-next "{" count)))
(evil-define-command evil-cp-wrap-previous-curly (count)
"Wraps the previous COUNT sexps inside curly braces. If the point is
inside a symbol, that symbol is treated as the first sexp to
wrap.
When called with \\[universal-argument], wraps the current
enclosing form and the previous N forms, where N is the count for
how many times the \\[universal-argument] was invoked."
(interactive "<c>")
(setq count (or count 1))
(if (consp current-prefix-arg)
(let ((count (evil-cp-universal-invoke-arg-count)))
(save-excursion
(sp-up-sexp)
(evil-cp--wrap-previous "{" count))
(evil-cp--up-list))
(evil-cp--wrap-previous "{" count)))
(defun evil-cp-insert (count &optional vcount skip-empty-lines)
"Like `evil-insert', but tries to be helpful by automatically
inserting a space in situations where the need for it is highly
likely, and cleaning after itself in the case where the space
wasn't actually needed:
- When point is at an open parentheses (but not in an empty
list), a space gets inserted but the point still remains where
it used to.
Can be disabled by setting `evil-cleverparens-use-regular-insert'
to true."
(interactive "p")
(cond
((or (evil-visual-state-p)
(looking-at-p "[\n\t ]+")
(bobp)
(eobp)
(evil-cp--point-in-string-or-comment)
(evil-cp--looking-at-empty-form))
(call-interactively 'evil-insert))
((and (looking-back "(")
;; should I use `sp-sexp-prefix' here?
(not (looking-back "'("))
(not (looking-back "#(")))
(setq evil-cp--inserted-space-after-round-open t)
(insert " ")
(backward-char)
(evil-insert count))
(t
(call-interactively 'evil-insert))))
(defun evil-cp-append (count &optional vcount skip-empty-lines)
"Like `evil-append', but tries to be helpful by automatically
inserting a space in situations where the need for it is highly
likely, and cleaning after itself in the case where the space
wasn't actually needed:
- When point is before an open parentheses (but not in an empty
list), a space gets inserted but the point still remains where
it used to.
Can be disabled by setting `evil-cleverparens-use-regular-insert'
to true."
(interactive "p")
(cond
((or (evil-visual-state-p)
(looking-at-p "[\n\t ]+")
(bobp)
(eobp)
(evil-cp--point-in-string-or-comment)
(evil-cp--looking-at-empty-form))
(call-interactively 'evil-append))
((and (or (looking-at-p "(\\b")
(evil-cp--looking-at-any-opening-p))
(not (looking-back "'"))
(not (looking-back "#")))
(setq evil-cp--inserted-space-after-round-open t)
(forward-char)
(insert " ")
(backward-char)
(evil-insert count))
(t
(call-interactively 'evil-append))))
(defun evil-cp-insert-exit-hook ()
"Deletes the extra space left by `evil-cp-insert' if nothing was inserted."
(if (and evil-cp--inserted-space-after-round-open
(looking-at-p "[[:space:]]"))
(when (or (not evil-current-insertion)
(= (car evil-current-insertion)
(cdr evil-current-insertion)))
(delete-char 1)))
(setq evil-cp--inserted-space-after-round-open nil))
(defun evil-cp-toggle-balanced-yank (&optional forcep)
"Toggles the setting `evil-cleverparens-complete-parens-in-yanked-region',
which determines whether or not an incomplete yanked region
should be supplemented with the missing parentheses at the end
and/or beginning."
(interactive)
(cond
(forcep
(message "Turned yank auto-balancing ON.")
(setq evil-cleverparens-complete-parens-in-yanked-region t))
((not evil-cleverparens-complete-parens-in-yanked-region)
(message "Turned yank auto-balancing ON.")
(setq evil-cleverparens-complete-parens-in-yanked-region t))
(t
(message "Turned yank auto-balancing OFF.")
(setq evil-cleverparens-complete-parens-in-yanked-region nil))))
;;; Keys ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar evil-cp-regular-movement-keys
'(("w" . evil-forward-word-begin)
("e" . evil-forward-word-end)
("b" . evil-backward-word-begin)
("ge" . evil-backward-word-end)
("W" . evil-cp-forward-symbol-begin)
("E" . evil-cp-forward-symbol-end)
("B" . evil-cp-backward-symbol-begin)
("gE" . evil-cp-backward-symbol-end)))
(defvar evil-cp-swapped-movement-keys
'(("w" . evil-cp-forward-symbol-begin)
("e" . evil-cp-forward-symbol-end)
("b" . evil-cp-backward-symbol-begin)
("ge" . evil-cp-backward-symbol-end)
("W" . evil-forward-word-begin)
("E" . evil-forward-word-end)
("B" . evil-backward-word-begin)
("gE" . evil-backward-word-end)))
(defvar evil-cp-additional-movement-keys
'(("L" . evil-cp-forward-sexp)
("H" . evil-cp-backward-sexp)
("M-l" . evil-cp-end-of-defun)
("M-h" . evil-cp-beginning-of-defun)
("[" . evil-cp-previous-opening)
("]" . evil-cp-next-closing)
("{" . evil-cp-next-opening)
("}" . evil-cp-previous-closing)
("(" . evil-cp-backward-up-sexp)
(")" . evil-cp-up-sexp)))
(defvar evil-cp-regular-bindings
'(("d" . evil-cp-delete)
("c" . evil-cp-change)
("y" . evil-cp-yank)
("D" . evil-cp-delete-line)
("C" . evil-cp-change-line)
("Y" . evil-cp-yank-line)
("x" . evil-cp-delete-char-or-splice)
("X" . evil-cp-delete-char-or-splice-backwards)
(">" . evil-cp->)
("<" . evil-cp-<)
("_" . evil-cp-first-non-blank-non-opening)
("M-T" . evil-cp-toggle-balanced-yank)
("M-z" . evil-cp-override))
"Alist containing the regular evil-cleverparens bindings that
override evil's bindings in normal mode.")
(defvar evil-cp-additional-bindings
'(("M-t" . sp-transpose-sexp)
("M-k" . evil-cp-drag-backward)
("M-j" . evil-cp-drag-forward)
("M-J" . sp-join-sexp)
("M-s" . sp-splice-sexp)
("M-S" . sp-split-sexp)
("M-R" . evil-cp-raise-form)
("M-r" . sp-raise-sexp)
("M-a" . evil-cp-insert-at-end-of-form)
("M-i" . evil-cp-insert-at-beginning-of-form)
("M-w" . evil-cp-copy-paste-form)
("M-y" . evil-cp-yank-sexp)
("M-d" . evil-cp-delete-sexp)
("M-c" . evil-cp-change-sexp)
("M-Y" . evil-cp-yank-enclosing)
("M-D" . evil-cp-delete-enclosing)
("M-C" . evil-cp-change-enclosing)
("M-q" . sp-indent-defun)
("M-o" . evil-cp-open-below-form)
("M-O" . evil-cp-open-above-form)
("M-v" . sp-convolute-sexp)
("M-(" . evil-cp-wrap-next-round)
("M-)" . evil-cp-wrap-previous-round)
("M-[" . evil-cp-wrap-next-square)
("M-]" . evil-cp-wrap-previous-square)
("M-{" . evil-cp-wrap-next-curly)
("M-}" . evil-cp-wrap-previous-curly))
"Alist containing additional functionality for
evil-cleverparens via a modifier key (using the meta-key by
default). Only enabled in evil's normal mode.")
(defun evil-cp--populate-mode-bindings-for-state (bindings state addp)
"Helper function that defines BINDINGS for the evil-state
STATE when ADDP is true. If ADDP is false, then the keys in
BINDINGS are set to nil instead, effectively disabling the keys
in question."
(--each bindings
(evil-define-key state evil-cleverparens-mode-map
(read-kbd-macro (car it))
(if addp (cdr it) nil))))
;;;###autoload
(defun evil-cp-set-movement-keys ()
"Sets the movement keys in
`evil-cleverparens-regular-movement-keys' or
`evil-cp-swapped-movement-keys' based on the value of
`evil-cleverparens-swap-move-by-word-and-symbol'."
(interactive)
(let ((keys (if evil-cleverparens-swap-move-by-word-and-symbol
evil-cp-swapped-movement-keys
evil-cp-regular-movement-keys)))
(evil-cp--populate-mode-bindings-for-state keys 'normal t)))
(defun evil-cp--enable-regular-bindings ()
"Enables the regular evil-cleverparens bindings based on
`evil-cp-regular-bindings'."
(dolist (state '(normal visual))
(evil-cp--populate-mode-bindings-for-state
evil-cp-regular-bindings
state
t))
(if evil-cleverparens-use-regular-insert
;; in case we change our mind
(progn
(evil-define-key 'normal evil-cleverparens-mode-map
"i" 'evil-insert)
(remove-hook 'evil-insert-state-exit-hook
'evil-cp-insert-exit-hook))
(evil-define-key 'normal evil-cleverparens-mode-map
"i" 'evil-cp-insert
"a" 'evil-cp-append)
(add-hook 'evil-insert-state-exit-hook
'evil-cp-insert-exit-hook))
;; If evil-snipe is not present or does not want to use s and S bindings,
;; then we can use them. To take effect, evil-snipe must be loaded before us.
(when (and evil-cleverparens-use-s-and-S
(not (bound-and-true-p evil-snipe-auto-disable-substitute)))
(evil-define-key 'normal evil-cleverparens-mode-map
"s" 'evil-cp-substitute
"S" 'evil-cp-change-whole-line)))
;;;###autoload
(defun evil-cp-set-additional-movement-keys ()
"Sets the movement keys is `evil-cp-additional-movement-keys'
for normal, visual and operator states if
`evil-cleverparens-use-additional-movement-keys' is true."
(interactive)
(dolist (state '(normal visual operator))
(evil-cp--populate-mode-bindings-for-state
evil-cp-additional-movement-keys
state
evil-cleverparens-use-additional-movement-keys)))
;;;###autoload
(defun evil-cp-set-additional-bindings ()
"Sets the movement keys is `evil-cp-additional-bindings' for
normal-state if `evil-cleverparens-use-additional-bindings' is
true."
(interactive)
(evil-cp--populate-mode-bindings-for-state
evil-cp-additional-bindings
'normal
evil-cleverparens-use-additional-bindings))
(defun evil-cp--enable-C-w-delete ()
(when evil-want-C-w-delete
(evil-define-key 'insert evil-cleverparens-mode-map
"\C-w" 'evil-cp-delete-backward-word)))
(defun evil-cp--enable-text-objects ()
"Enables text-objects defined in evil-cleverparens."
(define-key evil-outer-text-objects-map "f" #'evil-cp-a-form)
(define-key evil-inner-text-objects-map "f" #'evil-cp-inner-form)
(define-key evil-outer-text-objects-map "c" #'evil-cp-a-comment)
(define-key evil-inner-text-objects-map "c" #'evil-cp-inner-comment)
(define-key evil-outer-text-objects-map "d" #'evil-cp-a-defun)
(define-key evil-inner-text-objects-map "d" #'evil-cp-inner-defun))
(defun evil-cp--enable-surround-operators ()
"Enables the use of `evil-cp-delete' and `evil-cp-change' with
`evil-surround-mode'"
(add-to-list 'evil-surround-operator-alist '(evil-cp-delete . delete))
(add-to-list 'evil-surround-operator-alist '(evil-cp-change . change)))
;; Setup keymap
(defvar evil-cleverparens-mode-map (make-sparse-keymap))
(evil-cp-set-movement-keys)
(evil-cp--enable-regular-bindings)
(evil-cp-set-additional-bindings)
(evil-cp-set-additional-movement-keys)
(evil-cp--enable-C-w-delete)
(evil-cp--enable-text-objects)
;;;###autoload
(define-minor-mode evil-cleverparens-mode
"Minor mode for setting up evil with smartparens and paredit
for an advanced modal structural editing experience."
:group 'evil-cleverparens
:keymap evil-cleverparens-mode-map
:lighter (" ecp"
(:eval (if evil-cleverparens-complete-parens-in-yanked-region
"/b" "/i")))
:init-value nil
(if evil-cleverparens-mode
(progn
(if (bound-and-true-p evil-surround-mode)
(evil-cp--enable-surround-operators)
(add-hook 'evil-surround-mode-hook
'evil-cp--enable-surround-operators))
(run-hooks 'evil-cleverparens-enabled-hook))
(run-hooks 'evil-cleverparens-disabled-hook)))
(provide 'evil-cleverparens)
;;; evil-cleverparens.el ends here