Skip to content

Instantly share code, notes, and snippets.

@hchbaw
Created November 9, 2012 06:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hchbaw/4044079 to your computer and use it in GitHub Desktop.
Save hchbaw/4044079 to your computer and use it in GitHub Desktop.
;; evil.el version of textobj-motionmotion.vim
;; https://github.com/hchbaw/textobj-motionmotion.vim
;; Thanks very much the authors of evil.el -- extensible vi layer!
;; TODO:
;; - Am I totally doing wrong?
;; - arrgh! exclusive/inclusive in general
(defvar *evil-motionmotion-point* nil
"Saved value of `point' result before invoking the
`evil-textobj-motionmotion-operator'.")
(defmacro evil-with-motionmotion-point (&rest body)
(declare (indent 0))
`(let ((*evil-motionmotion-point* (point)))
,@body))
(defun evil-motionmotion-text-object-outer-motion-p (motion)
(string-match (rx string-start "evil-" (or "outer" "a") "-")
(format "%s" motion)))
(defun evil-motionmotion-text-object-inner-motion-p (motion)
(string-match (rx string-start "evil-" (or "inner" "i") "-")
(format "%s" motion)))
(evil-define-operator evil-motionmotion-operator (beg end type)
(interactive "<R>")
(cond ((evil-motionmotion-text-object-inner-motion-p evil-this-motion)
(cons beg (1- end)))
((evil-motionmotion-text-object-outer-motion-p evil-this-motion)
(cons beg end))
((= *evil-motionmotion-point* end) ;; left
(goto-char beg)
(cons (point) (point)))
((= *evil-motionmotion-point* beg) ;; right
(goto-char end)
(when (eq type 'inclusive)
(goto-char (1- (point))))
(cons (point) (point)))
(t (cons beg end))))
(defun evil-motionmotion-range (interim)
(macrolet ((save-this-command-keys (&rest body)
;; While an operator is in effect, subsequent operator
;; invocation may break the `evil-operator-range'.
;; For example, issuing "dimfa" will result that "d" as the
;; operator and the result of `this-command-keys'.
;; Then (note the operator "d" is in effect) "imfa" part will
;; try to invoke the operator `evil-motionmotion-operator',
;; so `evil-operator-range' will get called. At this point,
;; `this-command-keys' will result "dim",
;; `evil-extract-count' will complain "Key sequence contains
;; no complete binding".
;; So for now, while invoking an operator, hold the result of
;; `this-command-keys' and use it on subsequent calls.
;; fixes - "dimfi" -> insert mode.
`(letf* (((symbol-function 'this-command-keys-orig)
(symbol-function 'this-command-keys))
((symbol-function 'this-command-keys)
(apply-partially (lambda (keys)
(let ((len (length keys)))
(substring keys 0 (1- len))))
(this-command-keys-orig))))
(progn ,@body))))
(let ((pos (point)))
(pcase-let
((`((,b . _) . (_ . ,e))
(save-this-command-keys
(cons
#1=(evil-with-motionmotion-point
(call-interactively 'evil-motionmotion-operator))
(progn
(funcall interim pos)
#1#)))))
(let ((range (evil-range b (goto-char e))))
;; XXX: textobj-motionmotion.vim tends to be inclusive
(evil-normalize-range range)
(evil-set-range-type range 'inclusive)
(evil-expand-range range))))))
(evil-define-text-object evil-a-motionmotion (count &optional beg end type)
(evil-motionmotion-range 'ignore))
(evil-define-text-object evil-i-motionmotion (count &optional beg end type)
(evil-motionmotion-range 'goto-char))
(define-key evil-outer-text-objects-map "m" 'evil-a-motionmotion)
(define-key evil-inner-text-objects-map "m" 'evil-i-motionmotion)
(dont-compile
(when (fboundp 'expectations)
(require 'evil-tests)
(expectations
(expect (no-error)
(evil-test-buffer
"This[ ]test hl"
("dimhl")
"Thiest hl")
(evil-test-buffer
"This[ ]test lh"
("dimlh")
"Thiest lh")
))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment