summaryrefslogtreecommitdiff
path: root/elpa/evil-20220510.2302/evil-jumps.el
diff options
context:
space:
mode:
Diffstat (limited to 'elpa/evil-20220510.2302/evil-jumps.el')
-rw-r--r--elpa/evil-20220510.2302/evil-jumps.el354
1 files changed, 354 insertions, 0 deletions
diff --git a/elpa/evil-20220510.2302/evil-jumps.el b/elpa/evil-20220510.2302/evil-jumps.el
new file mode 100644
index 0000000..fea2e43
--- /dev/null
+++ b/elpa/evil-20220510.2302/evil-jumps.el
@@ -0,0 +1,354 @@
+;;; evil-jumps.el --- Jump list implementation -*- lexical-binding: t -*-
+
+;; Author: Bailey Ling <bling at live.ca>
+
+;; Version: 1.15.0
+
+;;
+;; This file is NOT part of GNU Emacs.
+
+;;; License:
+
+;; This file is part of Evil.
+;;
+;; Evil is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; Evil is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with Evil. If not, see <http://www.gnu.org/licenses/>.
+
+(require 'cl-lib)
+(require 'evil-core)
+(require 'evil-states)
+
+;;; Code:
+
+(defgroup evil-jumps nil
+ "Evil jump list configuration options."
+ :prefix "evil-jumps"
+ :group 'evil)
+
+(defcustom evil-jumps-cross-buffers t
+ "When non-nil, the jump commands can cross borders between buffers, otherwise the jump commands act only within the current buffer."
+ :type 'boolean
+ :group 'evil-jumps)
+
+(defcustom evil-jumps-max-length 100
+ "The maximum number of jumps to keep track of."
+ :type 'integer
+ :group 'evil-jumps)
+
+(defcustom evil-jumps-pre-jump-hook nil
+ "Hooks to run just before jumping to a location in the jump list."
+ :type 'hook
+ :group 'evil-jumps)
+
+(defcustom evil-jumps-post-jump-hook nil
+ "Hooks to run just after jumping to a location in the jump list."
+ :type 'hook
+ :group 'evil-jumps)
+
+(defcustom evil-jumps-ignored-file-patterns '("COMMIT_EDITMSG$" "TAGS$")
+ "A list of pattern regexps to match on the file path to exclude from being included in the jump list."
+ :type '(repeat string)
+ :group 'evil-jumps)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar savehist-additional-variables)
+
+(defvar evil--jumps-jumping nil)
+
+(defvar evil--jumps-jumping-backward nil
+ "Set by `evil--jump-backward', used and cleared in the
+`post-command-hook' by `evil--jump-handle-buffer-crossing'")
+
+(eval-when-compile (defvar evil--jumps-debug nil))
+
+(defvar evil--jumps-buffer-targets "\\*\\(new\\|scratch\\)\\*"
+ "Regexp to match against `buffer-name' to determine whether it's a valid jump target.")
+
+(defvar evil--jumps-window-jumps (make-hash-table)
+ "Hashtable which stores all jumps on a per window basis.")
+
+(defvar evil-jumps-history nil
+ "History of `evil-mode' jumps that are persisted with `savehist'.")
+
+(cl-defstruct evil-jumps-struct
+ ring
+ (idx -1)
+ previous-pos)
+
+;; Is inlining this really worth it?
+(defsubst evil--jumps-message (format &rest args)
+ (when (eval-when-compile evil--jumps-debug)
+ (with-current-buffer (get-buffer-create "*evil-jumps*")
+ (goto-char (point-max))
+ (insert (apply #'format format args) "\n"))))
+
+(defun evil--jumps-get-current (&optional window)
+ (unless window
+ (setq window (frame-selected-window)))
+ (let* ((jump-struct (gethash window evil--jumps-window-jumps)))
+ (unless jump-struct
+ (setq jump-struct (make-evil-jumps-struct))
+ (puthash window jump-struct evil--jumps-window-jumps))
+ jump-struct))
+
+(defun evil--jumps-get-jumps (struct)
+ (let ((ring (evil-jumps-struct-ring struct)))
+ (unless ring
+ (setq ring (make-ring evil-jumps-max-length))
+ (setf (evil-jumps-struct-ring struct) ring))
+ ring))
+
+(defun evil--jumps-get-window-jump-list ()
+ (let ((struct (evil--jumps-get-current)))
+ (evil--jumps-get-jumps struct)))
+
+(defun evil--jumps-savehist-load ()
+ (add-to-list 'savehist-additional-variables 'evil-jumps-history)
+ (let ((ring (make-ring evil-jumps-max-length)))
+ (cl-loop for jump in (reverse evil-jumps-history)
+ do (ring-insert ring jump))
+ (setf (evil-jumps-struct-ring (evil--jumps-get-current)) ring))
+ (add-hook 'savehist-save-hook #'evil--jumps-savehist-sync)
+ (remove-hook 'savehist-mode-hook #'evil--jumps-savehist-load))
+
+(defun evil--jumps-savehist-sync ()
+ "Updates the printable value of window jumps for `savehist'."
+ (setq evil-jumps-history
+ (delq nil (mapcar #'(lambda (jump)
+ (let* ((mark (car jump))
+ (pos (if (markerp mark)
+ (marker-position mark)
+ mark))
+ (file-name (cadr jump)))
+ (when (and (not (file-remote-p file-name))
+ (file-exists-p file-name)
+ pos)
+ (list pos file-name))))
+ (ring-elements (evil--jumps-get-window-jump-list))))))
+
+(defun evil--jumps-jump (idx shift)
+ (let ((target-list (evil--jumps-get-window-jump-list)))
+ (evil--jumps-message "jumping from %s by %s" idx shift)
+ (evil--jumps-message "target list = %s" target-list)
+ (setq idx (+ idx shift))
+ (let* ((current-file-name (or (buffer-file-name) (buffer-name)))
+ (size (ring-length target-list)))
+ (unless evil-jumps-cross-buffers
+ ;; skip jump marks pointing to other buffers
+ (while (and (< idx size) (>= idx 0)
+ (not (string= current-file-name (cadr (ring-ref target-list idx)))))
+ (setq idx (+ idx shift))))
+ (when (and (< idx size) (>= idx 0))
+ ;; actual jump
+ (run-hooks 'evil-jumps-pre-jump-hook)
+ (let* ((place (ring-ref target-list idx))
+ (pos (car place))
+ (file-name (cadr place)))
+ (setq evil--jumps-jumping t)
+ (unless (string= current-file-name file-name)
+ (if (string-match-p evil--jumps-buffer-targets file-name)
+ (switch-to-buffer file-name)
+ (find-file file-name)))
+ (setq evil--jumps-jumping nil)
+ (goto-char pos)
+ (setf (evil-jumps-struct-idx (evil--jumps-get-current)) idx)
+ (run-hooks 'evil-jumps-post-jump-hook))))))
+
+(defun evil--jumps-push ()
+ "Pushes the current cursor/file position to the jump list."
+ (let ((target-list (evil--jumps-get-window-jump-list)))
+ (let ((file-name (buffer-file-name))
+ (buffer-name (buffer-name))
+ (current-pos (point-marker))
+ (first-pos nil)
+ (first-file-name nil)
+ (excluded nil))
+ (when (and (not file-name)
+ (string-match-p evil--jumps-buffer-targets buffer-name))
+ (setq file-name buffer-name))
+ (when file-name
+ (dolist (pattern evil-jumps-ignored-file-patterns)
+ (when (string-match-p pattern file-name)
+ (setq excluded t)))
+ (unless excluded
+ (unless (ring-empty-p target-list)
+ (setq first-pos (car (ring-ref target-list 0)))
+ (setq first-file-name (car (cdr (ring-ref target-list 0)))))
+ (unless (and (equal first-pos current-pos)
+ (equal first-file-name file-name))
+ (evil--jumps-message "pushing %s on %s" current-pos file-name)
+ (ring-insert target-list `(,current-pos ,file-name))))))
+ (evil--jumps-message "%s %s"
+ (selected-window)
+ (and (not (ring-empty-p target-list))
+ (ring-ref target-list 0)))))
+
+(evil-define-command evil-show-jumps ()
+ "Display the contents of the jump list."
+ :repeat nil
+ (evil-with-view-list
+ :name "evil-jumps"
+ :mode "Evil Jump List"
+ :format [("Jump" 5 nil)
+ ("Marker" 8 nil)
+ ("File/text" 1000 t)]
+ :entries (let* ((jumps (evil--jumps-savehist-sync))
+ (count 0))
+ (cl-loop for jump in jumps
+ collect `(nil [,(number-to-string (cl-incf count))
+ ,(number-to-string (car jump))
+ (,(cadr jump))])))
+ :select-action #'evil--show-jumps-select-action))
+
+(defun evil--show-jumps-select-action (jump)
+ (let ((position (string-to-number (elt jump 1)))
+ (file (car (elt jump 2))))
+ (kill-buffer)
+ (switch-to-buffer (find-file file))
+ (goto-char position)))
+
+(defun evil-set-jump (&optional pos)
+ "Set jump point at POS.
+POS defaults to point."
+ (save-excursion
+ (when (markerp pos)
+ (set-buffer (marker-buffer pos)))
+
+ (unless (or (region-active-p) (evil-visual-state-p))
+ (push-mark pos t))
+
+ (unless evil--jumps-jumping
+ ;; clear out intermediary jumps when a new one is set
+ (let* ((struct (evil--jumps-get-current))
+ (target-list (evil--jumps-get-jumps struct))
+ (idx (evil-jumps-struct-idx struct)))
+ (cl-loop repeat idx
+ do (ring-remove target-list))
+ (setf (evil-jumps-struct-idx struct) -1))
+ (when pos
+ (goto-char pos))
+ (evil--jumps-push))))
+
+(defun evil--jump-backward (count)
+ (setq evil--jumps-jumping-backward t)
+ (let ((count (or count 1)))
+ (evil-motion-loop (nil count)
+ (let* ((struct (evil--jumps-get-current))
+ (idx (evil-jumps-struct-idx struct)))
+ (evil--jumps-message "jumping back %s" idx)
+ (when (= idx -1)
+ (setq idx 0)
+ (setf (evil-jumps-struct-idx struct) 0)
+ (evil--jumps-push))
+ (evil--jumps-jump idx 1)))))
+
+(defun evil--jump-forward (count)
+ (let ((count (or count 1)))
+ (evil-motion-loop (nil count)
+ (let* ((struct (evil--jumps-get-current))
+ (idx (evil-jumps-struct-idx struct)))
+ (when (= idx -1)
+ (setq idx 0)
+ (setf (evil-jumps-struct-idx struct) 0)
+ (evil--jumps-push))
+ (evil--jumps-jump idx -1)))))
+
+(defun evil--jumps-window-configuration-hook (&rest _args)
+ (let* ((window-list (window-list-1 nil nil t))
+ (existing-window (selected-window))
+ (new-window (previous-window)))
+ (when (and (not (eq existing-window new-window))
+ (> (length window-list) 1))
+ (let* ((target-jump-struct (evil--jumps-get-current new-window)))
+ (if (not (ring-empty-p (evil--jumps-get-jumps target-jump-struct)))
+ (evil--jumps-message "target window %s already has %s jumps" new-window
+ (ring-length (evil--jumps-get-jumps target-jump-struct)))
+ (evil--jumps-message "new target window detected; copying %s to %s" existing-window new-window)
+ (let* ((source-jump-struct (evil--jumps-get-current existing-window))
+ (source-list (evil--jumps-get-jumps source-jump-struct)))
+ (when (= (ring-length (evil--jumps-get-jumps target-jump-struct)) 0)
+ (setf (evil-jumps-struct-previous-pos target-jump-struct) (evil-jumps-struct-previous-pos source-jump-struct))
+ (setf (evil-jumps-struct-idx target-jump-struct) (evil-jumps-struct-idx source-jump-struct))
+ (setf (evil-jumps-struct-ring target-jump-struct) (ring-copy source-list)))))))
+ ;; delete obsolete windows
+ (maphash (lambda (key _val)
+ (unless (member key window-list)
+ (evil--jumps-message "removing %s" key)
+ (remhash key evil--jumps-window-jumps)))
+ evil--jumps-window-jumps)))
+
+(defun evil--jump-hook (&optional command)
+ "`pre-command-hook' for evil-jumps.
+Set jump point if COMMAND has a non-nil `:jump' property. Otherwise,
+save the current position in case the command being executed will
+change the current buffer."
+ (setq command (or command this-command))
+ (if (evil-get-command-property command :jump)
+ (evil-set-jump)
+ (setf (evil-jumps-struct-previous-pos (evil--jumps-get-current))
+ (point-marker))))
+
+(defun evil--jump-handle-buffer-crossing ()
+ (let ((jumping-backward evil--jumps-jumping-backward))
+ (setq evil--jumps-jumping-backward nil)
+ (dolist (frame (frame-list))
+ (dolist (window (window-list frame))
+ (let* ((struct (evil--jumps-get-current window))
+ (previous-pos (evil-jumps-struct-previous-pos struct)))
+ (when previous-pos
+ (setf (evil-jumps-struct-previous-pos struct) nil)
+ (if (and
+ ;; `evil-jump-backward' (and other backward jumping
+ ;; commands) needs to be handled specially. When
+ ;; jumping backward multiple times, calling
+ ;; `evil-set-jump' is always wrong: If you jump back
+ ;; twice and we call `evil-set-jump' after the second
+ ;; time, we clear the forward jump list and
+ ;; `evil--jump-forward' won't work.
+
+ ;; The first time you jump backward, setting a jump
+ ;; point is sometimes correct. But we don't do it
+ ;; here because this function is called after
+ ;; `evil--jump-backward' has updated our position in
+ ;; the jump list so, again, `evil-set-jump' would
+ ;; break `evil--jump-forward'.
+ (not jumping-backward)
+ (let ((previous-buffer (marker-buffer previous-pos)))
+ (and previous-buffer
+ (not (eq previous-buffer (window-buffer window))))))
+ (evil-set-jump previous-pos)
+ (set-marker previous-pos nil))))))))
+
+(if (bound-and-true-p savehist-loaded)
+ (evil--jumps-savehist-load)
+ (add-hook 'savehist-mode-hook #'evil--jumps-savehist-load))
+
+(defun evil--jumps-install-or-uninstall ()
+ (if evil-local-mode
+ (progn
+ (add-hook 'pre-command-hook #'evil--jump-hook nil t)
+ (add-hook 'post-command-hook #'evil--jump-handle-buffer-crossing nil t)
+ (add-hook 'next-error-hook #'evil-set-jump nil t)
+ (add-hook 'window-configuration-change-hook #'evil--jumps-window-configuration-hook nil t))
+ (remove-hook 'pre-command-hook #'evil--jump-hook t)
+ (remove-hook 'post-command-hook #'evil--jump-handle-buffer-crossing t)
+ (remove-hook 'next-error-hook #'evil-set-jump t)
+ (remove-hook 'window-configuration-change-hook #'evil--jumps-window-configuration-hook t)
+ (evil--jump-handle-buffer-crossing)))
+
+(add-hook 'evil-local-mode-hook #'evil--jumps-install-or-uninstall)
+
+(provide 'evil-jumps)
+
+;;; evil-jumps.el ends here