698 lines
25 KiB
EmacsLisp
698 lines
25 KiB
EmacsLisp
;;; vim-commands.el - Implementation of VIM commands.
|
|
|
|
;; Copyright (C) 2009, 2010 Frank Fischer
|
|
|
|
;; Author: Frank Fischer <frank.fischer@mathematik.tu-chemnitz.de>,
|
|
;;
|
|
;; This file is not part of GNU Emacs.
|
|
|
|
;;; Commentary:
|
|
|
|
;; In general there are two types of commands: those operating on a
|
|
;; motion and those not taking a motion. Examples of the first one are
|
|
;; the vim-commands c, d, y, =, examples of the second one are dd, D,
|
|
;; p, x.
|
|
;;
|
|
;; Commands are defined using the `vim:defcmd' macro and have the
|
|
;; following form:
|
|
;;
|
|
;; (vim:defcmd name (count
|
|
;; motion[:optional]
|
|
;; argument[:{char,file,buffer}]
|
|
;; [nonrepeatable]
|
|
;; [keep-visual])
|
|
;; 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 command which
|
|
;; is usually the number how often the command should be repeated.
|
|
;; This argument may be nil if no count is given. If the command takes
|
|
;; a MOTION argument, no COUNT argument is allowed (will always be
|
|
;; nil).
|
|
;;
|
|
;; The MOTION argument defines the range where the command should work
|
|
;; on. It's always of type `vim:motion'. Usually, a command should
|
|
;; respect the of the motion, i.e. charwise, linewise or block, but
|
|
;; there are commands that behave indepently of the motion type (e.g.
|
|
;; `vim:cmd-shift-left' always works linewise). If the MOTION
|
|
;; parameter has the form motion:optional, the MOTION parameter may be
|
|
;; nil, which can only happen if the command is bound in ex-mode (e.g.
|
|
;; the command `vim:cmd-substitute' is bound to :s may be called
|
|
;; without a motion, in which case it works only on the current line).
|
|
;; If the command is bound in normal-mode, the MOTION argument will
|
|
;; usually be created by some motion-command bound in
|
|
;; operator-pending-mode.
|
|
;;
|
|
;; 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:cmd-replace-char' usually
|
|
;; bound to 'r' for an example). If it specified as ARGUMENT:FILE it
|
|
;; takes a file-name as argument, ARGUMENT:BUFFER takes a buffer-name
|
|
;; as argument and a single ARGUMENT takes a string as argument. Only
|
|
;; the type ARGUMENT:CHAR has an effect in normal-mode, the others are
|
|
;; only important if bound in ex-mode. In this case the type of the
|
|
;; argument determines how minibuffer-completion is done. The argument
|
|
;; may be nil in which case the command should have a default
|
|
;; behaviour (e.g. the command `vim:cmd-write' bound to :write takes
|
|
;; an ARGUMENT:FILE argument and saves the current buffer to the given
|
|
;; file or to the buffer's own file if ARGUMENT is nil).
|
|
;;
|
|
;; The pseudo-argument NONREPEATABLE means, the command will not be
|
|
;; recorded to the repeat command (usually bound to '.'). This is
|
|
;; useful for non-editing commands, e.g. all window and scrolling
|
|
;; commands have this behaviour.
|
|
;;
|
|
;; The pseudo-argument KEEP-VISUAL means the command should not exit
|
|
;; visual-mode and go back to normal-mode when called in visual-mode.
|
|
;; This is useful for scrolling-commands which stay in visual-mode but
|
|
;; are no regular motions (scrolling commands move the (point) but are
|
|
;; no real motions since they can't in operating-pending mode), or
|
|
;; some visual-mode specific command like
|
|
;; `vim:visual-exchange-point-and-mark', usually bound to 'o').
|
|
;;
|
|
;; If you do not like the default argument names, they may be renamed
|
|
;; by using (ARG NEWNAME) instead of ARG, e.g.
|
|
;;
|
|
;; (vim:defcmd vim:cmd-replace-char (count (argument:char arg))
|
|
;;
|
|
;; defines a simple command with a COUNT argument but renames the
|
|
;; character-argument to ARG.
|
|
;;
|
|
;; Each command should place (point) at the correct position after the
|
|
;; operation.
|
|
;;
|
|
|
|
;;; Code:
|
|
|
|
(defcustom vim:shift-width 8
|
|
"The number of columns for shifting commands like < or >."
|
|
:type 'integer
|
|
:group 'vim-mode)
|
|
|
|
(vim:defcmd vim:cmd-insert (count)
|
|
"Switches to insert-mode before point."
|
|
(vim:activate-insert-mode))
|
|
|
|
(vim:defcmd vim:cmd-append (count)
|
|
"Switches to insert-mode after point."
|
|
(unless (eolp) (forward-char))
|
|
(vim:activate-insert-mode))
|
|
|
|
(vim:defcmd vim:cmd-Insert (count)
|
|
"Moves the cursor to the beginning of the current line
|
|
and switches to insert-mode."
|
|
(vim:motion-first-non-blank)
|
|
(vim:cmd-insert :count count))
|
|
|
|
(vim:defcmd vim:cmd-Append (count)
|
|
"Moves the cursor to the end of the current line
|
|
and switches to insert-mode."
|
|
(end-of-line)
|
|
(vim:cmd-append :count count))
|
|
|
|
(vim:defcmd vim:cmd-insert-line-above (count)
|
|
"Inserts a new line above the current one and goes to insert mode."
|
|
(vim:motion-beginning-of-line)
|
|
(newline)
|
|
(forward-line -1)
|
|
(indent-according-to-mode)
|
|
(vim:cmd-Insert))
|
|
|
|
(vim:defcmd vim:cmd-insert-line-below (count)
|
|
"Inserts a new line below the current one and goes to insert mode."
|
|
(vim:motion-end-of-line)
|
|
(newline)
|
|
(indent-according-to-mode)
|
|
(vim:cmd-insert))
|
|
|
|
(vim:defcmd vim:cmd-replace (count)
|
|
"Goes to replace-mode."
|
|
(vim:activate-insert-mode)
|
|
(vim:insert-mode-toggle-replace))
|
|
|
|
(vim:defcmd vim:insert-mode-exit (nonrepeatable)
|
|
"Deactivates insert-mode, returning to normal-mode."
|
|
(vim:activate-normal-mode)
|
|
(goto-char (max (line-beginning-position) (1- (point)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-delete-line (count register)
|
|
"Deletes the next count lines."
|
|
(vim:cmd-yank-line :count count :register register)
|
|
(let ((beg (line-beginning-position))
|
|
(end (save-excursion
|
|
(forward-line (1- (or count 1)))
|
|
(line-end-position))))
|
|
(if (= beg (point-min))
|
|
(if (= end (point-max))
|
|
(erase-buffer)
|
|
(delete-region beg (save-excursion
|
|
(goto-char end)
|
|
(forward-line)
|
|
(line-beginning-position))))
|
|
(delete-region (save-excursion
|
|
(goto-char beg)
|
|
(forward-line -1)
|
|
(line-end-position))
|
|
end))
|
|
(goto-char beg)
|
|
(vim:motion-first-non-blank)))
|
|
|
|
|
|
(vim:defcmd vim:cmd-delete (motion register)
|
|
"Deletes the characters defined by motion."
|
|
(case (vim:motion-type motion)
|
|
('linewise
|
|
(goto-line (vim:motion-first-line motion))
|
|
(vim:cmd-delete-line :count (vim:motion-line-count motion)
|
|
:register register))
|
|
|
|
('block
|
|
(vim:cmd-yank :motion motion :register register)
|
|
(delete-rectangle (vim:motion-begin-pos motion)
|
|
(vim:motion-end-pos motion)))
|
|
|
|
(t
|
|
(vim:cmd-yank :motion motion :register register)
|
|
(delete-region (vim:motion-begin-pos motion) (vim:motion-end-pos motion))
|
|
(goto-char (vim:motion-begin-pos motion)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-delete-char (count register)
|
|
"Deletes the next count characters."
|
|
(vim:cmd-delete :motion (vim:motion-right :count (or count 1))
|
|
:register register))
|
|
|
|
|
|
(vim:defcmd vim:cmd-change (motion register)
|
|
"Deletes the characters defined by motion and goes to insert mode."
|
|
(case (vim:motion-type motion)
|
|
('linewise
|
|
(goto-line (vim:motion-first-line motion))
|
|
(vim:cmd-change-line :count (vim:motion-line-count motion)
|
|
:register register))
|
|
|
|
('block
|
|
(let ((insert-info (vim:make-visual-insert-info :first-line (vim:motion-first-line motion)
|
|
:last-line (vim:motion-last-line motion)
|
|
:column (vim:motion-first-col motion))))
|
|
(vim:cmd-delete :motion motion :register register)
|
|
(vim:visual-start-insert insert-info)))
|
|
|
|
(t
|
|
;; deal with cw and cW
|
|
(when (and vim:current-motion
|
|
(not (member (char-after) '(? ?\r ?\n ?\t))))
|
|
(cond
|
|
((eq vim:current-motion 'vim:motion-fwd-word)
|
|
(let* ((cnt (* (or vim:current-cmd-count 1)
|
|
(or vim:current-motion-count 1)))
|
|
(pos
|
|
(save-excursion
|
|
(dotimes (i cnt)
|
|
(while
|
|
(not
|
|
(or (and (looking-at (concat "[^ \t\r\n]"
|
|
"[ \t\r\n]")))
|
|
(and (looking-at (concat "[" vim:word "]"
|
|
"[^ \t\r\n" vim:word "]")))
|
|
(and (looking-at (concat "[^ \t\r\n" vim:word "]"
|
|
"[" vim:word "]")))))
|
|
(forward-char))
|
|
(when (< i (1- cnt))
|
|
(forward-char)))
|
|
(point))))
|
|
(setq motion (vim:make-motion :begin (point) :end pos :type 'inclusive))))
|
|
|
|
((eq vim:current-motion 'vim:motion-fwd-WORD)
|
|
(let* ((cnt (* (or vim:current-cmd-count 1)
|
|
(or vim:current-motion-count 1)))
|
|
(pos
|
|
(save-excursion
|
|
(dotimes (i cnt)
|
|
(while
|
|
(not (looking-at (concat "[^ \t\r\n]"
|
|
"[ \t\r\n]")))
|
|
(forward-char))
|
|
(when (< i (1- cnt))
|
|
(forward-char)))
|
|
(point))))
|
|
(setq motion (vim:make-motion :begin (point) :end pos :type 'inclusive))))))
|
|
|
|
(vim:cmd-delete :motion motion :register register)
|
|
(if (eolp)
|
|
(vim:cmd-append :count 1)
|
|
(vim:cmd-insert :count 1)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-change-line (count register)
|
|
"Deletes count lines and goes to insert mode."
|
|
(let ((pos (line-beginning-position)))
|
|
(vim:cmd-delete-line :count count :register register)
|
|
(if (< (point) pos)
|
|
(progn
|
|
(end-of-line)
|
|
(newline))
|
|
(progn
|
|
(beginning-of-line)
|
|
(newline)
|
|
(forward-line -1)))
|
|
(indent-according-to-mode)
|
|
(if (eolp)
|
|
(vim:cmd-append :count 1)
|
|
(vim:cmd-insert :count 1))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-change-rest-of-line (register)
|
|
"Deletes the rest of the current line."
|
|
(vim:cmd-delete :motion (vim:make-motion :begin (point)
|
|
:end (1- (line-end-position))
|
|
:type 'inclusive)
|
|
:register register)
|
|
(vim:cmd-append :count 1))
|
|
|
|
|
|
|
|
(vim:defcmd vim:cmd-change-char (count register)
|
|
"Deletes the next count characters and goes to insert mode."
|
|
(let ((pos (point)))
|
|
(vim:cmd-delete-char :count count :register register)
|
|
(if (< (point) pos)
|
|
(vim:cmd-append)
|
|
(vim:cmd-insert))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-replace-char (count (argument:char arg))
|
|
"Replaces the next count characters with arg."
|
|
(unless (vim:char-p arg)
|
|
(error "Expected a character."))
|
|
(when (< (- (line-end-position) (point))
|
|
(or count 1))
|
|
(error "Too few characters to end of line."))
|
|
(delete-region (point) (+ (point) (or count 1)))
|
|
(insert-char arg (or count 1))
|
|
(backward-char))
|
|
|
|
|
|
(vim:defcmd vim:cmd-replace-region (motion (argument:char arg))
|
|
"Replace the complete region with `arg'"
|
|
(case (vim:motion-type motion)
|
|
('block
|
|
;; replace in block
|
|
(let ((begrow (vim:motion-first-line motion))
|
|
(begcol (vim:motion-first-col motion))
|
|
(endrow (vim:motion-last-line motion))
|
|
(endcol (1+ (vim:motion-last-col motion))))
|
|
(goto-line begrow)
|
|
(dotimes (i (1+ (- endrow begrow)))
|
|
;; TODO does it work with \r\n at the end?
|
|
(let ((maxcol (save-excursion
|
|
(end-of-line)
|
|
(current-column))))
|
|
(when (> maxcol begcol)
|
|
(delete-region (save-excursion
|
|
(move-to-column begcol t)
|
|
(point))
|
|
(save-excursion
|
|
(move-to-column (min endcol maxcol) t)
|
|
(point)))
|
|
(move-to-column begcol t)
|
|
(insert-char arg (- (min endcol maxcol) begcol))))
|
|
(forward-line 1))
|
|
(goto-line begrow)
|
|
(move-to-column begcol)))
|
|
|
|
(t ;; replace in linewise and normal
|
|
(let ((begrow (vim:motion-first-line motion))
|
|
(endrow (vim:motion-last-line motion)))
|
|
(goto-line begrow)
|
|
(do ((r begrow (1+ r)))
|
|
((> r endrow))
|
|
(goto-line r)
|
|
(let ((begcol
|
|
(if (and (= r begrow)
|
|
(not (eq (vim:motion-type motion) 'linewise)))
|
|
(save-excursion
|
|
(goto-char (vim:motion-begin-pos motion))
|
|
(current-column))
|
|
0))
|
|
(endcol
|
|
(if (and (= r endrow)
|
|
(not (eq (vim:motion-type motion) 'linewise)))
|
|
(save-excursion
|
|
(goto-char (vim:motion-end-pos motion))
|
|
(current-column))
|
|
;; TODO does it work with \r\n at the end?
|
|
(save-excursion
|
|
(end-of-line)
|
|
(current-column)))))
|
|
|
|
(delete-region (save-excursion
|
|
(move-to-column begcol t)
|
|
(point))
|
|
(save-excursion
|
|
(move-to-column endcol t)
|
|
(point)))
|
|
(move-to-column begcol t)
|
|
(insert-char arg (- endcol begcol)))))
|
|
|
|
(goto-char (vim:motion-begin-pos motion)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-yank (motion register nonrepeatable)
|
|
"Saves the characters in motion into the kill-ring."
|
|
(case (vim:motion-type motion)
|
|
('block (vim:cmd-yank-rectangle :motion motion :register register))
|
|
('linewise (goto-line (vim:motion-first-line motion))
|
|
(vim:cmd-yank-line :count (vim:motion-line-count motion)
|
|
:register register))
|
|
(t
|
|
(let ((text (buffer-substring
|
|
(vim:motion-begin-pos motion)
|
|
(vim:motion-end-pos motion))))
|
|
(if register
|
|
(set-register register text)
|
|
(kill-new text))))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-yank-line (count register nonrepeatable)
|
|
"Saves the next count lines into the kill-ring."
|
|
(let (lines
|
|
(linenr (line-number-at-pos (point))))
|
|
(setq count (or count 1))
|
|
(save-excursion
|
|
(while (> count 0)
|
|
(push (buffer-substring (line-beginning-position) (line-end-position)) lines)
|
|
(forward-line)
|
|
(decf count)
|
|
(incf linenr)
|
|
(when (> linenr (line-number-at-pos (point)))
|
|
(setq count 0))))
|
|
(if register
|
|
(let ((txt (make-string 1 ? )))
|
|
(put-text-property 0 1 'yank-handler
|
|
(list 'vim:yank-line-handler (reverse lines))
|
|
txt)
|
|
(set-register register txt))
|
|
(kill-new " " nil (list 'vim:yank-line-handler (reverse lines))))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-yank-rectangle (motion register nonrepeatable)
|
|
"Stores the rectangle defined by motion into the kill-ring."
|
|
(unless (eq (vim:motion-type motion) 'block)
|
|
(error "Motion must be of type block"))
|
|
;; TODO: yanking should not insert spaces or expand tabs.
|
|
(let ((begrow (vim:motion-first-line motion))
|
|
(begcol (vim:motion-first-col motion))
|
|
(endrow (vim:motion-last-line motion))
|
|
(endcol (vim:motion-last-col motion))
|
|
(parts nil))
|
|
(goto-line endrow)
|
|
(dotimes (i (1+ (- endrow begrow)))
|
|
(let ((beg (save-excursion (move-to-column begcol) (point)))
|
|
(end (save-excursion (move-to-column (1+ endcol)) (point))))
|
|
(push (cons (save-excursion (goto-char beg)
|
|
(- (current-column) begcol))
|
|
(buffer-substring beg end))
|
|
parts)
|
|
(forward-line -1)))
|
|
(if register
|
|
(let ((txt (make-string 1 ? )))
|
|
(put-text-property 0 1
|
|
'yank-handler
|
|
(list 'vim:yank-block-handler
|
|
(cons (- endcol begcol -1) parts))
|
|
txt)
|
|
(set-register register txt))
|
|
(kill-new " " nil (list 'vim:yank-block-handler
|
|
(cons (- endcol begcol -1) parts))))
|
|
(goto-line begrow)
|
|
(move-to-column begcol)))
|
|
|
|
(defun vim:yank-line-handler (text)
|
|
"Inserts the current text linewise."
|
|
(beginning-of-line)
|
|
(dolist (line text)
|
|
(insert line)
|
|
(newline)))
|
|
|
|
(defun vim:yank-block-handler (text)
|
|
"Inserts the current text as block."
|
|
(let ((ncols (car text))
|
|
(parts (cdr text))
|
|
(col (current-column))
|
|
(current-line (line-number-at-pos (point))))
|
|
|
|
(dolist (part parts)
|
|
|
|
(let* ((offset (car part))
|
|
(txt (cdr part))
|
|
(len (length txt)))
|
|
|
|
;; maybe we have to insert a new line at eob
|
|
(when (< (line-number-at-pos (point))
|
|
current-line)
|
|
(goto-char (point-max))
|
|
(newline))
|
|
(incf current-line)
|
|
|
|
(unless (and (< (current-column) col) ; nothing in this line
|
|
(<= offset 0) (zerop len)) ; and nothing to insert
|
|
(move-to-column (+ col (max 0 offset)) t)
|
|
(insert txt)
|
|
(unless (eolp)
|
|
;; text follows, so we have to insert spaces
|
|
(insert (make-string (- ncols len) ? ))))
|
|
(forward-line 1)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-paste-before (count register)
|
|
"Pastes the latest yanked text before the cursor position."
|
|
(unless (or kill-ring-yank-pointer register)
|
|
(error "kill-ring empty"))
|
|
|
|
(dotimes (i (or count 1))
|
|
(save-excursion
|
|
(if register
|
|
(insert-for-yank (vim:get-register register))
|
|
(yank)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-paste-behind (count register)
|
|
"Pastes the latest yanked text behind point."
|
|
(unless (or kill-ring-yank-pointer register)
|
|
(error "kill-ring empty"))
|
|
|
|
(let* ((txt (if register
|
|
(vim:get-register register)
|
|
(car kill-ring-yank-pointer)))
|
|
(yhandler (get-text-property 0 'yank-handler txt)))
|
|
(case (car-safe yhandler)
|
|
(vim:yank-line-handler
|
|
(end-of-line)
|
|
(if (eobp)
|
|
(progn
|
|
(newline)
|
|
(save-excursion
|
|
(vim:cmd-paste-before :count count :register register)
|
|
(goto-char (point-max))
|
|
(delete-backward-char 1)))
|
|
(forward-line)
|
|
(vim:cmd-paste-before :count count :register register))
|
|
(vim:motion-first-non-blank))
|
|
|
|
(vim:yank-block-handler
|
|
(forward-char)
|
|
(vim:cmd-paste-before :count count :register register))
|
|
|
|
(t
|
|
(forward-char)
|
|
(dotimes (i (or count 1))
|
|
(if register
|
|
(insert-for-yank (vim:get-register register))
|
|
(yank)))
|
|
(backward-char)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-join-lines (count)
|
|
"Join `count' lines with a minimum of two lines."
|
|
(dotimes (i (max 1 (1- (or count 1))))
|
|
(when (re-search-forward "\\(\\s-*\\)\\(\n\\s-*\\)\\()?\\)")
|
|
(delete-region (match-beginning 2)
|
|
(match-end 2))
|
|
(when (and (= (match-beginning 1) (match-end 1))
|
|
(= (match-beginning 3) (match-end 3)))
|
|
(insert-char ? 1))
|
|
(backward-char))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-join (motion)
|
|
"Join the lines covered by `motion'."
|
|
(goto-line (vim:motion-first-line motion))
|
|
(vim:cmd-join-lines :count (vim:motion-line-count motion)))
|
|
|
|
|
|
(vim:defcmd vim:cmd-indent (motion)
|
|
"Reindent the lines covered by `motion'."
|
|
(goto-line (vim:motion-first-line motion))
|
|
(indent-region (line-beginning-position)
|
|
(line-end-position (vim:motion-line-count motion))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-shift-left (motion)
|
|
"Shift the lines covered by `motion' leftwards."
|
|
(goto-line (vim:motion-first-line motion))
|
|
(indent-rigidly (line-beginning-position)
|
|
(line-end-position (vim:motion-line-count motion))
|
|
(- vim:shift-width)))
|
|
|
|
|
|
(vim:defcmd vim:cmd-shift-right (motion)
|
|
"Shift the lines covered by `motion' rightwards."
|
|
(goto-line (vim:motion-first-line motion))
|
|
(indent-rigidly (line-beginning-position)
|
|
(line-end-position (vim:motion-line-count motion))
|
|
vim:shift-width))
|
|
|
|
|
|
(vim:defcmd vim:cmd-toggle-case (motion)
|
|
"Toggles the case of all characters defined by `motion'."
|
|
(vim:change-case motion
|
|
#'(lambda (beg end)
|
|
(save-excursion
|
|
(goto-char beg)
|
|
(while (< beg end)
|
|
(let ((c (following-char)))
|
|
(delete-char 1 nil)
|
|
(insert-char (if (eq c (upcase c)) (downcase c) (upcase c)) 1)
|
|
(setq beg (1+ beg))))))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-make-upcase (motion)
|
|
"Upcases all characters defined by `motion'."
|
|
(vim:change-case motion #'upcase-region))
|
|
|
|
|
|
(vim:defcmd vim:cmd-make-downcase (motion)
|
|
"Downcases all characters defined by `motion'."
|
|
(vim:change-case motion #'downcase-region))
|
|
|
|
|
|
(defun vim:change-case (motion case-func)
|
|
(case (vim:motion-type motion)
|
|
('block
|
|
(do ((l (vim:motion-first-line motion) (1+ l)))
|
|
((> l (vim:motion-last-line motion)))
|
|
(funcall case-func
|
|
(save-excursion
|
|
(goto-line l)
|
|
(move-to-column (vim:motion-first-col motion))
|
|
(point))
|
|
(save-excursion
|
|
(goto-line l)
|
|
(move-to-column (vim:motion-last-col motion))
|
|
(1+ (point))))))
|
|
('linewise
|
|
(save-excursion
|
|
(funcall case-func (vim:motion-begin-pos motion) (vim:motion-end-pos motion))))
|
|
(t
|
|
(funcall case-func (vim:motion-begin-pos motion) (vim:motion-end-pos motion))
|
|
(goto-char (vim:motion-end-pos motion)))))
|
|
|
|
|
|
(vim:defcmd vim:cmd-repeat (nonrepeatable)
|
|
"Repeats the last command."
|
|
(unless vim:repeat-events
|
|
(error "Nothing to repeat"))
|
|
(vim:reset-key-state)
|
|
;;(dotimes (i (or count 1))
|
|
(let ((repeat-events vim:repeat-events)
|
|
(vim:repeat-events nil))
|
|
(execute-kbd-macro repeat-events)))
|
|
|
|
|
|
(vim:defcmd vim:cmd-emacs (nonrepeatable)
|
|
"Switches to Emacs for the next command."
|
|
(let (message-log-max) (message "Switch to Emacs for the next command."))
|
|
(vim:escape-to-emacs nil))
|
|
|
|
(vim:defcmd vim:cmd-write-and-close (nonrepeatable)
|
|
"Saves the current buffer and closes the window."
|
|
(save-buffer)
|
|
(condition-case nil
|
|
(delete-window)
|
|
(error (condition-case nil
|
|
(delete-frame)
|
|
(error (save-buffers-kill-emacs))))))
|
|
|
|
(vim:defcmd vim:cmd-set-mark ((argument:char mark-char) nonrepeatable)
|
|
"Sets the mark `mark-char' at point."
|
|
(vim:set-mark mark-char))
|
|
|
|
(vim:defcmd vim:cmd-show-marks (nonrepeatable (argument marks))
|
|
"Shows all currently defined marks."
|
|
(let ((all-marks (append vim:local-marks-alist vim:global-marks-alist)))
|
|
(when marks
|
|
(setq all-marks (remove-if-not #'(lambda (x) (find (car x) marks)) all-marks)))
|
|
|
|
(setq all-marks (sort all-marks #'(lambda (x y) (< (car x) (car y)))))
|
|
(setq all-marks (apply #'concat
|
|
(mapcar
|
|
#'(lambda (m)
|
|
(format "%3c %5d %3d %s\n"
|
|
(car m)
|
|
(line-number-at-pos (cdr m))
|
|
(save-excursion
|
|
(goto-char (cdr m))
|
|
(current-column))
|
|
(buffer-substring-no-properties
|
|
(save-excursion
|
|
(goto-char (cdr m))
|
|
(line-beginning-position))
|
|
(+ 20
|
|
(save-excursion
|
|
(goto-char (cdr m))
|
|
(line-beginning-position))))))
|
|
all-marks)))
|
|
(let (message-truncate-lines message-log-max)
|
|
(message "%4s %5s %3s %s\n%s" "Mark" "Line" "Col" "File/Text"
|
|
all-marks))))
|
|
|
|
(vim:deflocalvar vim:current-macro nil
|
|
"The name of the currently recorded macro.")
|
|
|
|
(vim:defcmd vim:cmd-toggle-macro-recording ((argument:char reg) nonrepeatable)
|
|
"Toggles recording of a keyboard macro."
|
|
(if reg
|
|
(progn
|
|
(put 'vim:cmd-toggle-macro-recording 'argument nil)
|
|
(vim:cmd-start-macro reg))
|
|
(progn
|
|
(put 'vim:cmd-toggle-macro-recording 'argument 'char)
|
|
(vim:cmd-stop-macro))))
|
|
|
|
(defun vim:cmd-start-macro (reg)
|
|
"Starts recording a macro in register `reg'."
|
|
(setq vim:current-macro reg)
|
|
(start-kbd-macro nil)
|
|
(let (message-log-max)
|
|
(message "Start recording keyboard macro in register '%c'" reg)))
|
|
|
|
(defun vim:cmd-stop-macro ()
|
|
"Stops recording of a macro."
|
|
(end-kbd-macro)
|
|
(let (message-log-max)
|
|
(message "Stop recording keyboard macro in register '%c'" vim:current-macro))
|
|
(set-register vim:current-macro last-kbd-macro))
|
|
|
|
(vim:defcmd vim:cmd-execute-macro (count nonrepeatable (argument:char reg))
|
|
"Executes the keyboard-macro in register `reg.'"
|
|
(vim:reset-key-state)
|
|
(execute-kbd-macro (vim:get-register reg) count))
|
|
|
|
(provide 'vim-commands)
|
|
|
|
;;; vim-commands.el ends here
|