diff --git a/extensions/vi-mode/binds.lisp b/extensions/vi-mode/binds.lisp index ed29260c9..8976ed99f 100644 --- a/extensions/vi-mode/binds.lisp +++ b/extensions/vi-mode/binds.lisp @@ -70,9 +70,16 @@ (define-key *motion-keymap* "T" 'vi-find-char-backward-after) (define-key *motion-keymap* ";" 'vi-find-char-repeat) (define-key *motion-keymap* "," 'vi-find-char-repeat-backward) -(define-key *motion-keymap* "z z" 'recenter) -(define-key *motion-keymap* "Z Z" 'vi-write-quit) -(define-key *motion-keymap* "Z Q" 'vi-quit) +(define-key *normal-keymap* "z z" 'vi-scroll-line-to-center) +(define-key *normal-keymap* "z ." 'vi-scroll-line-to-center-back-to-indentation) +(define-key *normal-keymap* "z t" 'vi-scroll-line-to-top) +(define-key *normal-keymap* "z Return" 'vi-scroll-line-to-top-back-to-indentation) +(define-key *normal-keymap* "z b" 'vi-scroll-line-to-bottom) +(define-key *normal-keymap* "z -" 'vi-scroll-line-to-bottom-back-to-indentation) +(define-key *normal-keymap* "z +" 'vi-scroll-bottom-line-to-top) +(define-key *normal-keymap* "z ^" 'vi-scroll-top-line-to-bottom) +(define-key *normal-keymap* "Z Z" 'vi-write-quit) +(define-key *normal-keymap* "Z Q" 'vi-quit) (define-key *motion-keymap* "C-w s" 'split-active-window-vertically) (define-key *motion-keymap* "C-w C-s" 'split-active-window-vertically) (define-key *motion-keymap* "C-w w" 'next-window) diff --git a/extensions/vi-mode/commands.lisp b/extensions/vi-mode/commands.lisp index e803cbdb4..e55041149 100644 --- a/extensions/vi-mode/commands.lisp +++ b/extensions/vi-mode/commands.lisp @@ -22,7 +22,10 @@ (:import-from :lem-vi-mode/window :move-to-window-top :move-to-window-middle - :move-to-window-bottom) + :move-to-window-bottom + :move-to-window-bottom-below + :move-to-window-top-above + :scroll-line) (:import-from :lem-vi-mode/utils :kill-region-without-appending) (:import-from :lem/isearch @@ -53,6 +56,14 @@ :vi-move-to-window-top :vi-move-to-window-middle :vi-move-to-window-bottom + :vi-scroll-line-to-center + :vi-scroll-line-to-center-back-to-indentation + :vi-scroll-line-to-top + :vi-scroll-line-to-top-back-to-indentation + :vi-scroll-line-to-bottom + :vi-scroll-line-to-bottom-back-to-indentation + :vi-scroll-bottom-line-to-top + :vi-scroll-top-line-to-bottom :vi-back-to-indentation :vi-indent :vi-substitute @@ -273,6 +284,49 @@ :jump t) (move-to-window-bottom)) +(define-command vi-scroll-line-to-center (&optional n) ("P") + "Scroll line number N (or the current line) to the center of the screen." + (scroll-line n :center)) + +(define-command vi-scroll-line-to-center-back-to-indentation (&optional n) ("P") + "Scroll line number N (or the current line) to the center of the screen. +Move the cursor to the first non-blank character of the line." + (scroll-line n :center) + (vi-back-to-indentation)) + +(define-command vi-scroll-line-to-top (&optional n) ("P") + "Scroll line number N (or the current line) to the top of the screen." + (scroll-line n :top)) + +(define-command vi-scroll-line-to-top-back-to-indentation (&optional n) ("P") + "Scroll line number N (or the current line) to the top of the screen. +Move the cursor to the first non-blank character of the line." + (scroll-line n :top) + (vi-back-to-indentation)) + +(define-command vi-scroll-line-to-bottom (&optional n) ("P") + "Scroll line number N (or the current line) to the bottom of the screen." + (scroll-line n :bottom)) + +(define-command vi-scroll-line-to-bottom-back-to-indentation (&optional n) ("P") + "Scroll line number N (or the current line) to the bottom of the screen. +Move the cursor to the first non-blank character of the line." + (scroll-line n :bottom) + (vi-back-to-indentation)) + +(define-command vi-scroll-bottom-line-to-top (&optional n) ("P") + (unless n + (move-to-window-bottom-below)) + (vi-scroll-line-to-top-back-to-indentation n)) + +(define-command vi-scroll-top-line-to-bottom (&optional n) ("P") + (if n + (progn + (vi-scroll-line-to-bottom n) + (vi-move-to-window-top)) + (move-to-window-top-above)) + (vi-scroll-line-to-bottom-back-to-indentation nil)) + (define-command vi-back-to-indentation () () (vi-move-to-beginning-of-line) (skip-whitespace-forward (current-point) t)) diff --git a/extensions/vi-mode/utils.lisp b/extensions/vi-mode/utils.lisp index 38a13f082..face11d38 100644 --- a/extensions/vi-mode/utils.lisp +++ b/extensions/vi-mode/utils.lisp @@ -4,7 +4,8 @@ (:import-from :cl-ppcre) (:export :change-directory :expand-filename-modifiers - :kill-region-without-appending)) + :kill-region-without-appending + :save-column)) (in-package :lem-vi-mode/utils) (defvar *previous-cwd* nil) @@ -65,3 +66,9 @@ (rotatef start end)) (let ((killed-string (delete-character start (count-characters start end)))) (copy-to-clipboard-with-killring killed-string))) + +(defmacro save-column (&body body) + (let ((column (gensym "COLUMN"))) + `(let ((,column (point-column (current-point)))) + (unwind-protect (progn ,@body) + (move-to-column (current-point) ,column))))) diff --git a/extensions/vi-mode/window.lisp b/extensions/vi-mode/window.lisp index da6e35d74..c5995e5d2 100644 --- a/extensions/vi-mode/window.lisp +++ b/extensions/vi-mode/window.lisp @@ -3,11 +3,16 @@ :lem) (:import-from :lem-vi-mode/options :option-value) + (:import-from :lem-vi-mode/utils + :save-column) (:import-from :lem-core :window-height-without-modeline) (:export :move-to-window-top :move-to-window-middle :move-to-window-bottom + :move-to-window-bottom-below + :move-to-window-top-above + :scroll-line :adjust-window-scroll)) (in-package :lem-vi-mode/window) @@ -55,6 +60,36 @@ (when (window-has-following-lines-p window) (previous-line (scroll-offset))))) +(defun move-to-window-bottom-below () + (let ((window (current-window))) + (move-point (current-point) (window-end-point window)) + (when (window-has-following-lines-p window) + (next-line 1)))) + +(defun move-to-window-top-above () + (let ((window (current-window))) + (move-point (current-point) (window-start-point window)) + (when (window-has-leading-lines-p window) + (previous-line 1)))) + +(defun scroll-line (line-number scroll-position) + "Scroll the line LINE-NUMBER to SCROLL-POSITION of the screen. +If LINE-NUMBER is NIL, scroll the current line. +SCROLL-POSITION can be one of :TOP :CENTER :BOTTOM. +The SCROLL-POSITION is influenced by the scrolloff option." + (clear-screens-of-window-list) + (when line-number + (save-column + (goto-line line-number))) + (let* ((window (current-window)) + (scrolloff (scroll-offset window))) + (case scroll-position + (:top (window-recenter window :line scrolloff :from-bottom nil)) + (:center (window-recenter window :line nil :from-bottom nil)) + (:bottom (window-recenter window :line scrolloff :from-bottom t)))) + (redraw-display) + t) + (defun adjust-window-scroll () (let* ((window (current-window)) (window-height (window-height* window))