666 lines
23 KiB
EmacsLisp
666 lines
23 KiB
EmacsLisp
;;; vim-motions.el - Implementation of VIM motions.
|
|
|
|
;; Copyright (C) 2009, 2010 Frank Fischer
|
|
|
|
;; Author: Frank Fischer <frank.fischer@mathematik.tu-chemnitz.de>,
|
|
;;
|
|
;; This file is not part of GNU Emacs.
|
|
|
|
;;; Commentary:
|
|
|
|
;; Motions describe functions moving the cursor or representing an
|
|
;; argument for an operator. There are three types of motions:
|
|
;; character-wise, line-wise and block-wise. Usually only the first
|
|
;; two types are represented by motion-commands while the last one is
|
|
;; implicitly used by visual-block-mode.
|
|
;;
|
|
;; A motion is defined using the macro 'vim:defmotion' which has the
|
|
;; following form:
|
|
;;
|
|
;; (vim:defmotion name (count
|
|
;; argument[:{char}]
|
|
;; {inclusive,exclusive,linewise,block})
|
|
;; body...)
|
|
;;
|
|
;; Each of the arguments is optional. The names of the arguments must
|
|
;; be exactly as in the definition above (but see 'Argument-renaming'
|
|
;; below).
|
|
;;
|
|
;; The COUNT argument (if given) takes the count of the motion which
|
|
;; is usually the number how often the motion should be repeated. This
|
|
;; argument may be nil if no count is given.
|
|
;;
|
|
;; The ARGUMENT argument is an aditional text-argument to be given and
|
|
;; may be nil, too. If it is specified as ARGUMENT:CHAR, the argument
|
|
;; is a one-character argument (see `vim:motion-find' usually bound to
|
|
;; 'f' for an example), otherwise it's a string-argument. Currently all
|
|
;; motions taking an argument take a character-argument.
|
|
;;
|
|
;; One if the pseudo-arguments INCLUSIVE, EXCLUSIVE, LINEWISE and BLOCK
|
|
;; must be given and specifies the type of the motion. See the Vim-manual
|
|
;; for an explanation of motion-types.
|
|
;;
|
|
;; If you do not like the default argument names, they may be renamed by using
|
|
;; (ARG NEWNAME) instead of ARG, e.g.
|
|
;;
|
|
;; (vim:defmotion vim:motion-find (inclusive count (argument:char arg))
|
|
;;
|
|
;; defines an inclusive motion with a count-argument but renames the
|
|
;; character-argument to ARG.
|
|
;;
|
|
;; Each motion should return an object of type `vim:motion'. This may happen
|
|
;; in one of two ways: explicit or implicit.
|
|
;;
|
|
;; Explicit: The function creates an object of type `vim:motion' using
|
|
;; `vim:make-motion' specifing the begin position, the end position
|
|
;; and the type of the motion (overriding the motion-type specified in
|
|
;; the argument list). If the motion is a usual motion, the vim:motion
|
|
;; parameter :has-begin should be nil, if it's a text-objects it
|
|
;; should be t. The difference is that text-objects actively define a
|
|
;; range from the begin-position to the end-position, while
|
|
;; conventional motions define only the end-position placing begin at
|
|
;; (point). The motion should also change (point) usually to the
|
|
;; end-position of the returned motion.
|
|
;;
|
|
;; Implicit: Creating an explicit `vim:motion' object is overkill for
|
|
;; most simple motions. If the motion does not return a `vim:motion'
|
|
;; object, its created implicitly with the following rules:
|
|
;; - the begin-position is set to (point) before the execution of motion's
|
|
;; body
|
|
;; - the end-position is set to (point) after the execution of motion's
|
|
;; body
|
|
;; - :has-begin is nil
|
|
;; - the type is the type defined in the motion's argument list
|
|
;; Almost all motions defined in this file are implicit.
|
|
;;
|
|
;; Note that, independently on whether the motion is defined
|
|
;; implicitly or explicitly, calling a motion always returns a
|
|
;; `vim:motion' object, i.e. (vim:motion-p (vim:motion-left)) would
|
|
;; return t.
|
|
;;
|
|
;; Motions can be bound to some key-sequence as any other interactive
|
|
;; Emacs function, but they work only in vim-mode. Ususally motions
|
|
;; are bound to the operator-pending-mode keymap using `vim:omap'.
|
|
|
|
;;; Code:
|
|
|
|
(vim:deflocalvar vim:this-column nil
|
|
"The resulting column of the current motion.")
|
|
|
|
(vim:deflocalvar vim:last-column nil
|
|
"The resulting column of the previous motion.")
|
|
|
|
(vim:deflocalvar vim:last-find nil
|
|
"The previous find command (command . arg).")
|
|
|
|
(defcustom vim:word "[:word:]"
|
|
"Regexp-set matching a word."
|
|
:type 'string
|
|
:group 'vim-mode)
|
|
|
|
(defcustom vim:whitespace " \t\r\n"
|
|
"Regexp-set matching a whitespace."
|
|
:type 'string
|
|
:group 'vim-mode)
|
|
|
|
(defun vim:adjust-point ()
|
|
"Adjust the pointer after a command."
|
|
;; TODO: should we check modes directly?
|
|
(when (and (not (vim:insert-mode-p))
|
|
) ;(not vim:replace-mode))
|
|
|
|
(when vim:this-column
|
|
(move-to-column vim:this-column))
|
|
;; always stop at the last character (not the newline)
|
|
(when (and (not (vim:visual-mode-p))
|
|
(eolp) (not (bolp)))
|
|
(backward-char)))
|
|
|
|
(setq vim:last-column (or vim:this-column
|
|
(current-column)))
|
|
(setq vim:this-column nil))
|
|
|
|
|
|
(defun vim:use-last-column ()
|
|
"This function should by called by a motion not changing the column."
|
|
(setq vim:this-column vim:last-column))
|
|
|
|
|
|
;; This structure is passed to operators taking a motion.
|
|
;; It should *not* be returned by motions.
|
|
(defstruct (vim:motion
|
|
(:constructor vim:make-motion-struct))
|
|
has-begin ; t iff the motion defined an explicit begin
|
|
begin ; first point in this motion
|
|
end ; last point in this motion
|
|
type ; 'inclusive, 'exclusive, 'linewise
|
|
)
|
|
|
|
(defun* vim:make-motion (&key
|
|
has-begin
|
|
(begin (point))
|
|
(end (point))
|
|
type)
|
|
"Creates a new motion with `begin' and `end' always
|
|
positions within (point-min) and (point-max) and not at
|
|
(line-end-position) (if possible)."
|
|
(unless type
|
|
(setq type (if (<= begin end) 'inclusive 'exclusive)))
|
|
|
|
(labels
|
|
((shrink-to (pos lower upper)
|
|
(max lower (min upper pos)))
|
|
|
|
(normalize-pos (pos)
|
|
(let ((pos (shrink-to pos (point-min) (point-max))))
|
|
(shrink-to pos
|
|
(save-excursion
|
|
(goto-char pos)
|
|
(line-beginning-position))
|
|
(save-excursion
|
|
(goto-char pos)
|
|
(- (line-end-position)
|
|
(if (eq type 'inclusive) 1 0)))))))
|
|
|
|
(vim:make-motion-struct :has-begin has-begin
|
|
:begin (normalize-pos begin)
|
|
:end (normalize-pos end)
|
|
:type type)))
|
|
|
|
|
|
(defun vim:motion-line-count (motion)
|
|
"Returns the number of lines the `motion' covers."
|
|
(1+ (- (vim:motion-last-line motion)
|
|
(vim:motion-first-line motion))))
|
|
|
|
(defun vim:motion-first-line (motion)
|
|
"Returns the first line covered by `motion'."
|
|
(min (line-number-at-pos (vim:motion-begin motion))
|
|
(line-number-at-pos (vim:motion-end motion))))
|
|
|
|
(defun vim:motion-last-line (motion)
|
|
"Returns the last line covered by `motion'."
|
|
(max (line-number-at-pos (vim:motion-begin motion))
|
|
(line-number-at-pos (vim:motion-end motion))))
|
|
|
|
(defun vim:motion-first-col (motion)
|
|
"Returns the first column covered by `motion'."
|
|
(min (save-excursion
|
|
(goto-char (vim:motion-begin motion))
|
|
(current-column))
|
|
(save-excursion
|
|
(goto-char (vim:motion-end motion))
|
|
(current-column))))
|
|
|
|
(defun vim:motion-last-col (motion)
|
|
"Returns the last column covered by `motion'."
|
|
(max (save-excursion
|
|
(goto-char (vim:motion-begin motion))
|
|
(current-column))
|
|
(save-excursion
|
|
(goto-char (vim:motion-end motion))
|
|
(current-column))))
|
|
|
|
(defun vim:motion-begin-pos (motion)
|
|
"Returns the smaller position covered by `motion'.
|
|
The result is modified depending on the motion type to
|
|
return the correct start-position of emacs-ranges, i.e.
|
|
- if motion is inclusive or exclusive, nothing is changed
|
|
- if motion is line-wise, is always bol of the first line in the motion,
|
|
- if motion is block 1 is added if and only if the begin column
|
|
is larget than the end column."
|
|
(case (vim:motion-type motion)
|
|
(linewise
|
|
(save-excursion
|
|
(goto-line (vim:motion-first-line motion))
|
|
(line-beginning-position)))
|
|
('block
|
|
(let ((b (min (vim:motion-begin motion) (vim:motion-end motion)))
|
|
(e (max (vim:motion-begin motion) (vim:motion-end motion))))
|
|
(if (> (save-excursion (goto-char b) (current-column))
|
|
(save-excursion (goto-char e) (current-column)))
|
|
(1+ b)
|
|
b)))
|
|
(t (min (vim:motion-begin motion) (vim:motion-end motion)))))
|
|
|
|
|
|
(defun vim:motion-end-pos (motion)
|
|
"Returns the larger position covered by `motion'.
|
|
The result is modified depending on the motion type to
|
|
return the correct end-position of emacs-ranges, i.e.
|
|
- if motion is inclusive, 1 is added,
|
|
- if motion is exclusive, nothing is change,
|
|
- if motion is line-wise, is always eol of the last line in the motion,
|
|
- if motion is block 1 is added if and only if the end column
|
|
is larger than or equal to the begin column."
|
|
(case (vim:motion-type motion)
|
|
(linewise
|
|
(save-excursion
|
|
(goto-line (vim:motion-last-line motion))
|
|
(line-end-position)))
|
|
('block
|
|
(let ((b (min (vim:motion-begin motion) (vim:motion-end motion)))
|
|
(e (max (vim:motion-begin motion) (vim:motion-end motion))))
|
|
(if (>= (save-excursion (goto-char e) (current-column))
|
|
(save-excursion (goto-char b) (current-column)))
|
|
(1+ e)
|
|
e)))
|
|
(inclusive
|
|
(1+ (max (vim:motion-begin motion) (vim:motion-end motion))))
|
|
(t (max (vim:motion-begin motion) (vim:motion-end motion)))))
|
|
|
|
|
|
(defmacro vim:do-motion (type expression)
|
|
"Executes a motion body, ensuring the return of a valid vim:motion object."
|
|
(let ((current-pos (gensym))
|
|
(motion (gensym)))
|
|
`(let* ((,current-pos (point))
|
|
(,motion ,expression))
|
|
(if (vim:motion-p ,motion)
|
|
,motion
|
|
(vim:make-motion :has-begin nil
|
|
:begin ,current-pos
|
|
:end (point)
|
|
:type ,type)))))
|
|
(font-lock-add-keywords 'emacs-lisp-mode '("vim:do-motion"))
|
|
|
|
|
|
(vim:deflocalvar vim:local-marks-alist nil
|
|
"Local marks for this buffer.")
|
|
|
|
(defvar vim:global-marks-alist nil
|
|
"Global marks.")
|
|
|
|
(defun vim:local-mark-p (mark-char)
|
|
"Returns t if `mark-char' is a local mark."
|
|
(or (and (>= mark-char ?a) (<= mark-char ?z))
|
|
(member mark-char '(?^ ?. ?< ?>))))
|
|
|
|
(defun vim:global-mark-p (mark-char)
|
|
"Returns t if `mark-char' is a global mark."
|
|
(and (>= mark-char ?A) (<= mark-char ?z)))
|
|
|
|
(defun vim:set-mark (mark-char &optional pos)
|
|
"Sets the mark `mark-char' to `pos' or (point)."
|
|
(let (m)
|
|
(cond
|
|
((vim:local-mark-p mark-char)
|
|
(setq m (or (cdr-safe (assoc mark-char vim:local-marks-alist))))
|
|
(unless m
|
|
(setq m (make-marker))
|
|
(push (cons mark-char m) vim:local-marks-alist)))
|
|
|
|
((vim:global-mark-p mark-char)
|
|
(setq m (or (cdr-safe (assoc mark-char vim:global-marks-alist))))
|
|
(unless m
|
|
(setq m (make-marker))
|
|
(push (cons mark-char m) vim:global-marks-alist)))
|
|
(t (error "Unknown mark '%c'" mark-char)))
|
|
(set-marker m (or pos (point)))))
|
|
|
|
(defun vim:get-local-mark (mark-char)
|
|
"Returns the marker of `mark-char' if it's in the current buffer."
|
|
(cond
|
|
((vim:local-mark-p mark-char)
|
|
(let ((m (cdr-safe (assoc mark-char vim:local-marks-alist))))
|
|
(if m m
|
|
(error "No mark '%c' defined." mark-char))))
|
|
((vim:global-mark-p mark-char)
|
|
(let ((m (cdr-safe (assoc mark-char vim:global-marks-alist))))
|
|
(if m
|
|
(if (eq (marker-buffer m) (current-buffer))
|
|
m
|
|
(error "Global mark '%c' not in current buffer." mark-char))
|
|
(error "No mark '%c' defined." mark-char))))
|
|
(t
|
|
(error "Unknown mark: '%c'" mark-char))))
|
|
|
|
(add-hook 'before-change-functions 'vim:set-change-mark)
|
|
(defun vim:set-change-mark (beg end)
|
|
"Sets the change mark . to `beg'."
|
|
(vim:set-mark ?. beg))
|
|
|
|
(defun vim:adjust-end-of-line-position (pos)
|
|
"If pos is an end-of-line returns pos - 1 and pos otherwise."
|
|
(save-excursion
|
|
(goto-char pos)
|
|
(max (line-beginning-position)
|
|
(min (1- (line-end-position)) pos))))
|
|
|
|
(vim:defmotion vim:motion-left (exclusive count)
|
|
"Move the cursor count characters left."
|
|
(goto-char (max (line-beginning-position)
|
|
(- (point) (or count 1)))))
|
|
|
|
(vim:defmotion vim:motion-right (exclusive count)
|
|
"Move the cursor count characters right."
|
|
(goto-char
|
|
(min (line-end-position)
|
|
(+ (point) (or count 1)))))
|
|
|
|
(vim:defmotion vim:motion-up (linewise count)
|
|
"Move the cursor count lines up."
|
|
(vim:use-last-column)
|
|
(forward-line (- (or count 1))))
|
|
|
|
(vim:defmotion vim:motion-down (linewise count)
|
|
"Move the cursor count lines down."
|
|
(vim:use-last-column)
|
|
(forward-line (or count 1)))
|
|
|
|
(vim:defmotion vim:motion-lines (linewise count)
|
|
"Moves count - 1 lines down."
|
|
(vim:use-last-column)
|
|
(forward-line (1- (or count 1))))
|
|
|
|
|
|
(defun vim:motion-beginning-of-line-or-digit-argument ()
|
|
"Feeds a 0 count or moves the cursor to the beginning of the line."
|
|
(interactive)
|
|
(if (and current-prefix-arg
|
|
(not (zerop (prefix-numeric-value current-prefix-arg))))
|
|
(call-interactively 'digit-argument)
|
|
(call-interactively 'vim:motion-beginning-of-line)))
|
|
|
|
|
|
(vim:defmotion vim:motion-beginning-of-line (exclusive)
|
|
"Move the cursor to the beginning of the current line."
|
|
(beginning-of-line))
|
|
|
|
(vim:defmotion vim:motion-first-non-blank (exclusive)
|
|
"Move the cursor to the first non-blank character of the current line."
|
|
(back-to-indentation))
|
|
|
|
(vim:defmotion vim:motion-end-of-line (inclusive count)
|
|
"Move the cursor to the end of the current line."
|
|
(end-of-line count))
|
|
|
|
(vim:defmotion vim:motion-last-non-blank (inclusive count)
|
|
"Move the cursor to the last non-blank charactor of the current line."
|
|
(goto-char
|
|
(save-excursion
|
|
(beginning-of-line count)
|
|
(re-search-forward "[ \t]*$")
|
|
(max (line-beginning-position)
|
|
(1- (match-beginning 0))))))
|
|
|
|
(vim:defmotion vim:motion-go-to-first-non-blank-beg (linewise count)
|
|
"Moves the cursor to the first non-blank charactor of line count."
|
|
(if count
|
|
(goto-line count)
|
|
(goto-char (point-min)))
|
|
(vim:motion-first-non-blank))
|
|
|
|
(vim:defmotion vim:motion-go-to-first-non-blank-end (linewise count)
|
|
"Moves the cursor to the first non-blank charactor of line count."
|
|
(if count
|
|
(goto-line count)
|
|
(goto-char (point-max)))
|
|
(vim:motion-first-non-blank))
|
|
|
|
|
|
(vim:defmotion vim:motion-fwd-word (exclusive count)
|
|
"Moves the cursor beginning of the next word."
|
|
(let ((word (concat "[" vim:word "]"))
|
|
(noword (concat "[^" vim:word "]"))
|
|
(nonword (concat "[^" vim:whitespace vim:word "]"))
|
|
(nononword (concat "[" vim:whitespace vim:word "]")))
|
|
(dotimes (i (or count 1))
|
|
(forward-char)
|
|
(while
|
|
(not
|
|
(or (and (looking-back noword) (looking-at word))
|
|
(and (looking-back nononword) (looking-at nonword))
|
|
(and (bolp) (eolp))
|
|
(eobp)))
|
|
(forward-char))))
|
|
|
|
;; in operator-pending mode, if we reached the beginning of a new
|
|
;; line, go back to the end of the previous line
|
|
(when (and (vim:operator-pending-mode-p)
|
|
(vim:looking-back "^[ \t]*")
|
|
(not (save-excursion
|
|
(forward-line -1)
|
|
(and (bolp) (eolp)))))
|
|
(forward-line -1)
|
|
(end-of-line)))
|
|
|
|
|
|
(vim:defmotion vim:motion-bwd-word (exclusive count)
|
|
"Moves the cursor beginning of the previous word."
|
|
(let ((word (concat "[" vim:word "]"))
|
|
(noword (concat "[^" vim:word "]"))
|
|
(nonword (concat "[^" vim:whitespace vim:word "]"))
|
|
(nononword (concat "[" vim:whitespace vim:word "]")))
|
|
(dotimes (i (or count 1))
|
|
(backward-char)
|
|
(while
|
|
(not
|
|
(or (and (looking-back noword) (looking-at word))
|
|
(and (looking-back nononword) (looking-at nonword))
|
|
(and (bolp) (eolp))
|
|
(eobp)))
|
|
(backward-char)))))
|
|
|
|
|
|
(vim:defmotion vim:motion-fwd-word-end (inclusive count)
|
|
"Moves the cursor to the end of the next word."
|
|
(let ((wordend (concat "[" vim:word "][^" vim:word "]"))
|
|
(nowordend (concat "[^" vim:whitespace vim:word "][" vim:whitespace vim:word "]")))
|
|
(forward-char)
|
|
(re-search-forward (concat wordend "\\|" nowordend "\\|\\'") nil nil (or count 1))
|
|
(goto-char (match-beginning 0))))
|
|
|
|
|
|
(vim:defmotion vim:motion-bwd-word-end (inclusive count)
|
|
"Moves the cursor to the end of the previous word."
|
|
(let ((wordend (concat "[" vim:word "][^" vim:word "]"))
|
|
(nowordend (concat "[^" vim:whitespace vim:word "][" vim:whitespace vim:word "]")))
|
|
(unless (eobp) (forward-char))
|
|
(re-search-backward (concat wordend "\\|" nowordend "\\|\\`") nil nil (or count 1))
|
|
(goto-char (match-beginning 0))))
|
|
|
|
|
|
(vim:defmotion vim:motion-fwd-WORD (exclusive count)
|
|
"Moves the cursor to beginning of the next WORD."
|
|
(let ((WORD (concat "[^" vim:whitespace "]"))
|
|
(noWORD (concat "[" vim:whitespace "]")))
|
|
(dotimes (i (or count 1))
|
|
(forward-char)
|
|
(while
|
|
(not
|
|
(or (and (looking-back noWORD) (looking-at WORD))
|
|
(and (bolp) (eolp))
|
|
(eobp)))
|
|
(forward-char))))
|
|
|
|
;; in operator-pending mode, if we reached the beginning of a new
|
|
;; line, go back to the end of the previous line
|
|
(when (and (vim:operator-pending-mode-p)
|
|
(vim:looking-back "^[ \t]*")
|
|
(not (save-excursion
|
|
(forward-line -1)
|
|
(and (bolp) (eolp)))))
|
|
(forward-line -1)
|
|
(end-of-line)))
|
|
|
|
|
|
(vim:defmotion vim:motion-bwd-WORD (exclusive count)
|
|
"Moves the cursor to beginning of the previous WORD."
|
|
(let ((WORD (concat "[^" vim:whitespace "]"))
|
|
(noWORD (concat "[" vim:whitespace "]")))
|
|
(dotimes (i (or count 1))
|
|
(backward-char)
|
|
(while
|
|
(not
|
|
(or (and (looking-back noWORD) (looking-at WORD))
|
|
(and (bolp) (eolp))
|
|
(eobp)))
|
|
(backward-char)))))
|
|
|
|
|
|
(vim:defmotion vim:motion-fwd-WORD-end (inclusive count)
|
|
"Moves the cursor to the end of the next WORD."
|
|
(let ((WORDend (concat "[^" vim:whitespace "][" vim:whitespace "]")))
|
|
(forward-char)
|
|
(re-search-forward (concat WORDend "\\|\\'") nil nil (or count 1))
|
|
(goto-char (match-beginning 0))))
|
|
|
|
|
|
(vim:defmotion vim:motion-bwd-WORD-end (inclusive count)
|
|
"Moves the cursor to the end of the next WORD."
|
|
(let ((WORDend (concat "[^" vim:whitespace "][" vim:whitespace "]")))
|
|
(unless (eobp) (forward-char))
|
|
(re-search-backward (concat WORDend "\\|\\'") nil nil (or count 1))
|
|
(goto-char (match-beginning 0))))
|
|
|
|
|
|
(vim:defmotion vim:motion-fwd-sentence (exclusive count)
|
|
"Move the cursor `count' sentences forward."
|
|
(dotimes (i (or count 1))
|
|
(let ((par-end (save-excursion
|
|
(forward-paragraph)
|
|
(point))))
|
|
(unless (re-search-forward (sentence-end) par-end t)
|
|
(goto-char par-end)))))
|
|
|
|
|
|
(vim:defmotion vim:motion-bwd-sentence (exclusive count)
|
|
"Move the cursor `count' sentences backward."
|
|
(dotimes (i (or count 1))
|
|
(let ((par-beg (save-excursion
|
|
(backward-paragraph)
|
|
(point))))
|
|
(if (re-search-backward (concat (sentence-end) "[^ \t\n]")
|
|
par-beg t)
|
|
(goto-char (1- (match-end 0)))
|
|
(goto-char par-beg)))))
|
|
|
|
|
|
(vim:defmotion vim:motion-fwd-paragraph (exclusive count)
|
|
"Move the cursor `count' paragraphs forward."
|
|
(forward-paragraph (or count 1)))
|
|
|
|
|
|
(vim:defmotion vim:motion-bwd-paragraph (exclusive count)
|
|
"Move the cursor `count' paragraphs backward."
|
|
(backward-paragraph (or count 1)))
|
|
|
|
|
|
(vim:defmotion vim:motion-find (inclusive count (argument:char arg))
|
|
"Move the cursor to the next count'th occurrence of arg."
|
|
(forward-char)
|
|
(let ((case-fold-search nil))
|
|
(unless (search-forward (char-to-string arg)
|
|
nil t (or count 1))
|
|
(backward-char)
|
|
(error (format "Can't find %c" arg)))
|
|
(setq vim:last-find (cons 'vim:motion-find arg))
|
|
(backward-char)))
|
|
|
|
|
|
(vim:defmotion vim:motion-find-back (exclusive count (argument:char arg))
|
|
"Move the cursor to the previous count'th occurrence of arg."
|
|
(let ((case-fold-search nil))
|
|
(unless (search-backward (char-to-string arg)
|
|
nil t (or count 1))
|
|
(error (format "Can't find %c" arg)))
|
|
(setq vim:last-find (cons 'vim:motion-find-back arg))))
|
|
|
|
|
|
(vim:defmotion vim:motion-find-to (inclusive count (argument:char arg))
|
|
"Move the cursor to the character before the next count'th\
|
|
occurence of arg."
|
|
(vim:motion-find :count count :argument arg)
|
|
(backward-char)
|
|
(setq vim:last-find (cons 'vim:motion-find-to arg)))
|
|
|
|
|
|
(vim:defmotion vim:motion-find-back-to (exclusive count (argument:char arg))
|
|
"Move the cursor to the character after the previous count'th\
|
|
occurence of arg."
|
|
(vim:motion-find-back :count count :argument arg)
|
|
(forward-char)
|
|
(setq vim:last-find (cons 'vim:motion-find-to arg)))
|
|
|
|
|
|
(vim:defmotion vim:motion-repeat-last-find (inclusive count)
|
|
"Repeats the last find command."
|
|
(unless vim:last-find
|
|
(error "No previous find command."))
|
|
(funcall (car vim:last-find)
|
|
:count count
|
|
:argument (cdr vim:last-find)))
|
|
|
|
|
|
(vim:defmotion vim:motion-repeat-last-find-opposite (inclusive count)
|
|
"Repeats the last find command."
|
|
(unless vim:last-find
|
|
(error "No previous find command."))
|
|
(let ((func (case (car vim:last-find)
|
|
('vim:motion-find 'vim:motion-find-back)
|
|
('vim:motion-find-back 'vim:motion-find)
|
|
('vim:motion-find-to 'vim:motion-find-back-to)
|
|
('vim:motion-find-back-to 'vim:motion-find-to)
|
|
(t (error (format "Unexpected find command %s"
|
|
(car vim:last-find))))))
|
|
(arg (cdr vim:last-find)))
|
|
(let ((vim:last-find nil))
|
|
(funcall func :count count :argument arg))))
|
|
|
|
|
|
(vim:defmotion vim:motion-jump-item (inclusive)
|
|
"Find the next item in this line after or under the cursor and
|
|
jumps to the corresponding one."
|
|
(let ((next-open
|
|
(condition-case err
|
|
(1- (scan-lists (point) 1 -1))
|
|
(error
|
|
(point-max))))
|
|
(next-close
|
|
(condition-case nil
|
|
(1- (scan-lists (point) 1 +1))
|
|
(error (point-max)))))
|
|
(let ((pos (min next-open next-close)))
|
|
(when (>= pos (line-end-position))
|
|
(error "No matching item found on the current line."))
|
|
(if (= pos next-open)
|
|
(progn
|
|
(goto-char pos)
|
|
(forward-list)
|
|
(backward-char))
|
|
(progn
|
|
(goto-char (1+ pos))
|
|
(backward-list))))))
|
|
|
|
|
|
(vim:defmotion vim:motion-inner-word (inclusive count)
|
|
"Select `count' words."
|
|
(let ((beg (save-excursion
|
|
(forward-char)
|
|
(vim:motion-bwd-word)
|
|
(point)))
|
|
(end (save-excursion
|
|
(backward-char)
|
|
(vim:motion-fwd-word-end :count count)
|
|
(point))))
|
|
(goto-char end)
|
|
(vim:make-motion :has-begin t
|
|
:begin beg
|
|
:end end
|
|
:type 'inclusive)))
|
|
|
|
|
|
(vim:defmotion vim:motion-mark (exclusive (argument:char mark-char))
|
|
"Moves to the position of `mark-char'."
|
|
(goto-char (vim:get-local-mark mark-char)))
|
|
|
|
(vim:defmotion vim:motion-mark-line (linewise (argument:char mark-char))
|
|
"Moves to the first non-blank char in the line of `mark-char'."
|
|
(goto-char (vim:get-local-mark mark-char))
|
|
(vim:motion-first-non-blank)
|
|
t)
|
|
|
|
(provide 'vim-motions)
|
|
|
|
;;; vim-motions.el ends here
|