(in-package :editor) ;; Modes (defmode "Vim Command" :setup-function 'setup-vim-command) (defmode "Vim Insert") (defmode "Vim Operator Pending") (defun setup-vim-command (buffer) (use-buffer buffer (setf *vim-pending-action* (constantly nil) *vim-last-action* #'identity *vim-repeat-multiplier* nil))) (defun mode-p (mode) (and (current-buffer) (find mode (buffer-mode-names (current-buffer)) :test #'string=))) (defun vim-command-mode-p () (mode-p "Vim Command")) (defun vim-operator-pending-mode-p () (mode-p "Vim Operator Pending")) (defcommand "All Vim" (p) "Put all buffers in Vim Command mode." "Put all buffers in Vim Command mode." (declare (ignore p)) (dolist (b *buffer-list*) (when (buffer-pathname b) (use-buffer b (vim-command-mode-command t))))) (defcommand "Vim Command Mode" (p) "Start Vim Command mode" "Start Vim Command mode" (declare (ignore p)) (setf (buffer-minor-mode (current-buffer) "Vim Insert") nil) (setf (buffer-minor-mode (current-buffer) "Vim Operator Pending") nil) (setf (buffer-minor-mode (current-buffer) "Vim Command") t)) (defcommand "Vim Mode" (p) "" "" (vim-command-mode-command p)) (defcommand "Vim Insert Mode" (p) "" "" (declare (ignore p)) (setf (buffer-minor-mode (current-buffer) "Vim Insert") t) (setf (buffer-minor-mode (current-buffer) "Vim Command") nil)) (defcommand "Exit Vim Mode" (p) "Exit Vim Command Mode" "Exit Vim Command Mode" (declare (ignore p)) (dolist (mode '("Vim Command" "Vim Operator Pending" "Vim Insert")) (setf (buffer-minor-mode (current-buffer) mode) nil))) (defcommand "Vim Scroll Window Down" (p) "" "" (scroll-window-down-command (or p 1))) (defcommand "Vim Scroll Window Up" (p) "" "" (scroll-window-up-command (or p 1))) ; Modified from Line to Top of Window from filecoms.lisp in the editor sources. (defcommand "Vim Scroll Line to Top of Window" (p) "" "" (scroll-line-to-place p :top)) (defcommand "Vim Scroll Line to Middle of Window" (p) "" "" (scroll-line-to-place p :middle)) (defcommand "Vim Scroll Line to Bottom of Window" (p) "" "" (scroll-line-to-place p :bottom)) (defcommand "Vim Repeat" (p) "" "" (funcall *vim-last-action* p)) (defcommand "Vim Append Text at End of Line" (p) "" "" (declare (ignore p)) (end-of-line-command nil) (vim-insert-mode-command nil)) (defcommand "Vim Insert Text at Beginning of Line" (p) "" "" (declare (ignore p)) (back-to-indentation-command nil) (vim-insert-mode-command nil)) (defcommand "Vim Open Line Down" (p) "" "" (declare (ignore p)) (line-end (current-point)) (new-line-command nil) (vim-insert-mode-command nil)) (defcommand "Vim Open Line Up" (p) "" "" (declare (ignore p)) (line-start (current-point)) (open-line-command nil) (vim-insert-mode-command nil)) (defcommand "Vim Save All Files" (p) "" "" (declare (ignore p)) (save-all-files-command t)) (defcommand "Vim Save All Files and Exit" (p) "" "" (declare (ignore p)) (save-all-files-command t) (lispworks-tools::confirm-quit-lispworks)) ;; This isn't really the right way (i.e. the Vim way) to do a ;; setting like this. Oh well. #+nil (defcommand "Vim Toggle hlsearch" (p) "" "" (setq v+hlsearch (cond ((not p) (not v+hlsearch)) ((plusp p) t) (t nil))) (if v+hlsearch (highlight-search) (font-lock-fontify-buffer-command nil))) ; (defcommand "Vim Jump To Match" (p) "Currently only works on ()" "" ; (declare (ignore p)) (def-vim-move "Vim Next Line" (p) :linewise :inclusive "" "" (next-line-command p)) (def-vim-move "Vim Next Screen Line" (p) nil :inclusive "" "" (next-line-command p)) (def-vim-move "Vim Previous Line" (p) :linewise :inclusive "" "" (previous-line-command p)) (def-vim-move "Vim Previous Screen Line" (p) nil :inclusive "" "" (previous-line-command p)) (def-vim-move "Vim Backward Character" (p) nil :exclusive "" "" (backward-character-command p)) (def-vim-move "Vim Forward Character" (p) nil :exclusive "" "" (forward-character-command p)) (def-vim-move "Vim Forward Word" (p) nil :exclusive "" "" (forward-word-command (+ (or p 1) 1)) (backward-word-command 1)) (def-vim-move "Vim Backward Word" (p) nil :exclusive "" "" (backward-word-command p)) (def-vim-move "Vim Goto Line or End of File" (p) :linewise :inclusive "" "" (if p (editor::goto-line-command p) (editor::end-of-buffer-command nil)) (back-to-indentation-command p)) (def-vim-change "Vim Delete Next Character" (p) "" "" (delete-next-character-command p)) (def-vim-change "Vim Delete Previous Character" (p) "" "" (delete-previous-character-command p)) (def-vim-move "Vim Top of Window" (p) :linewise :inclusive "" "" (top-of-window-command p)) (def-vim-move "Vim Bottom of Window" (p) :linewise :inclusive "" "" (bottom-of-window-command p)) (def-vim-move "Vim Move to Window Line" (p) :linewise :inclusive "" "" (move-to-window-line-command p)) (def-vim-move "Vim Forward Form" (p) nil :exclusive "" "" (forward-form-command p)) (def-vim-move "Vim Backward Form" (p) nil :exclusive "" "" (backward-form-command p)) (def-vim-move "Vim Forward List" (p) nil :exclusive "" "" (forward-list-command p)) (def-vim-move "Vim Backward List" (p) nil :exclusive "" "" (backward-list-command p)) (def-vim-move "Vim Beginning of Line" (p) nil :exclusive "" "" (beginning-of-line-command p)) (def-vim-move "Vim End of Line" (p) nil :exclusive "" "" (end-of-line-command p)) (def-vim-move "Vim Goto Line or Top of Buffer" (p) :linewise :exclusive "" "" (if p (goto-line-command p) (beginning-of-buffer-command p)) (back-to-indentation-command p)) (def-vim-move "Vim Move Over Whole Line" (p) :linewise :exclusive "" "" (line-start b-vim-point-before-movement) (when (and p (> p 1)) (line-offset (current-point) (1- p))) (line-end (current-point))) (defcommand "Vim Revert Buffer" (p) "" "" (revert-buffer-command p) (clear-undo-command p)) ; Note: Emacs's kill-region-command does not include the character at the "end" mark. I ; think this just reflects a difference in approach. To Emacs a point lies *between* ; two characters, so if you have 123^456^789 it deletes the "456". To Vim a point lies *on* ; a character, so if you have ; 123456789 ; ^ ^ ; an "exclusive" motion d2l deletes the "45". (def-vim-movement-pending "Vim Delete Motion" (begin end) "" "" ; (format t "~&starting Vim Delete Motion: begin is ~S, end is ~S~%" begin end) (when (point< end begin) (rotatef end begin)) ; (format t "begin is ~S, end is ~S~%" begin end) (when (and (not (linewise)) (not (same-line-p begin end)) (blanks-before begin) (blanks-after end)) (setf (linewise) t)) (when (linewise) (line-start begin) (line-end end) (character-offset end 1)) ; (format t "begin is ~S, end is ~S~%" begin end) (with-point ((after-end end :after-insert)) (unless (linewise) (character-offset after-end 1)) (move-point (current-point) begin) (set-current-mark end) (kill-region-command nil) (move-point (current-point) begin))) (defcommand "Vim Argument Digit" (p) "" "" (setf w-vim-collecting-count t) (argument-digit-command p)) (defcommand "Vim Beginning of Line or Collect Count" (p) "" "" (if w-vim-collecting-count (prompt-for-prefix 1 (* p 10)) (vim-beginning-of-line-command p))) (def-vim-change "Vim Replace Character" (p) "" "" (let ((ch (gesture-to-simple-char (prompt-for-character* "Character: " t)))) (with-point ((point (current-point)) (end (current-point))) ;; Only proceed if there *are* p more characters in the buffer. (if (character-offset end (or p 1)) (progn (collect-undo (current-buffer) (loop for n below (or p 1) do (setf (next-character point) ch) (character-offset point 1))) (character-offset point -1)) (editor-error)))))