Function: evil-define-text-object

evil-define-text-object is a macro defined in evil-macros.el.

Signature

(evil-define-text-object OBJECT (COUNT) DOC [[KEY VALUE]...] BODY...)

Documentation

Define a text object command OBJECT.

BODY should return a range (BEG END) to the right of point if COUNT is positive, and to the left of it if negative.

Optional keyword arguments:
- :type - determines how the range applies after an operator
  (inclusive, line, block, and exclusive, or a self-defined
  motion type).
- :extend-selection - if non-nil (default), the text object always
  enlarges the current selection. Otherwise, it replaces the current
  selection.

Source Code

;; Defined in ~/.emacs.d/elpa/evil-20251108.138/evil-macros.el
(defmacro evil-define-text-object (object args &rest body)
  "Define a text object command OBJECT.
BODY should return a range (BEG END) to the right of point
if COUNT is positive, and to the left of it if negative.

Optional keyword arguments:
- `:type' - determines how the range applies after an operator
  (`inclusive', `line', `block', and `exclusive', or a self-defined
  motion type).
- `:extend-selection' - if non-nil (default), the text object always
  enlarges the current selection.  Otherwise, it replaces the current
  selection.

\(fn OBJECT (COUNT) DOC [[KEY VALUE]...] BODY...)"
  (declare (indent defun)
           (doc-string 3)
           (debug (&define name lambda-list
                           [&optional stringp]
                           [&rest keywordp sexp]
                           def-body)))
  (let* ((args (delq '&optional args))
         (count (or (pop args) 'count))
         (args (when args `(&optional ,@args)))
         (interactive '(interactive "<c><v>"))
         doc keys)
    ;; collect docstring
    (when (stringp (car body))
      (setq doc (pop body)))
    ;; collect keywords
    (setq keys (plist-put keys :extend-selection t))
    (while (keywordp (car body))
      (setq keys (plist-put keys (pop body) (pop body))))
    ;; interactive
    (when (eq (car-safe (car body)) 'interactive)
      (setq interactive (pop body)))
    ;; macro expansion
    `(evil-define-motion ,object (,count ,@args)
       ,@(when doc `(,doc))
       ,@keys
       ,interactive
       (setq ,count (or ,count 1))
       (when (/= ,count 0)
         ;; FIXME: These let-bindings shadow variables in args
         (let ((type (evil-type ',object evil-visual-char))
               (extend (and (evil-visual-state-p)
                            (evil-get-command-property
                             ',object :extend-selection
                             ',(plist-get keys :extend-selection))))
               (dir evil-visual-direction)
               mark point range selection)
           (cond
            ;; Visual state: extend the current selection
            ((and (evil-visual-state-p)
                  (called-interactively-p 'any))
             ;; if we are at the beginning of the Visual selection,
             ;; go to the left (negative COUNT); if at the end,
             ;; go to the right (positive COUNT)
             (setq dir evil-visual-direction
                   ,count (* ,count dir))
             (setq range (progn ,@body))
             (when (evil-range-p range)
               (setq range (evil-expand-range range))
               (evil-set-type range (evil-type range type))
               (setq range (evil-contract-range range))
               ;; the beginning is mark and the end is point
               ;; unless the selection goes the other way
               (setq mark  (evil-range-beginning range)
                     point (evil-range-end range)
                     type  (evil-type
                            (if evil-text-object-change-visual-type
                                range
                              (evil-visual-range))))
               (when (and (eq type 'line)
                          (not (eq type (evil-type range))))
                 (let ((newrange (evil-text-object-make-linewise range)))
                   (setq mark (evil-range-beginning newrange)
                         point (evil-range-end newrange))))
               (when (< dir 0)
                 (evil-swap mark point))
               ;; select the union
               (evil-visual-make-selection mark point type)))
            ;; not Visual state: return a pair of buffer positions
            (t
             (setq range (progn ,@body))
             (unless (evil-range-p range)
               (setq ,count (- ,count)
                     range (progn ,@body)))
             (when (evil-range-p range)
               (setq selection (evil-range (point) (point) type))
               (if extend
                   (setq range (evil-range-union range selection))
                 (evil-set-type range (evil-type range type)))
               ;; possibly convert to linewise
               (when (eq evil-this-type-modified 'line)
                 (setq range (evil-text-object-make-linewise range)))
               (evil-set-range-properties range nil)
               range))))))))