Download
(defgroup iy-go-to-char nil
"go to char like f in vim."
:link '(emacs-commentary-link "iy-go-to-char")
:prefix "iy-go-to-char-"
:group 'matching)
(defcustom iy-go-to-char-key-forward ?\
"Default key used to go to next occurence of the char."
:type 'character
:group 'iy-go-to-char)
(defcustom iy-go-to-char-key-backward ?\,
"Default key used to go to previous occurence of the char."
:type 'character
:group 'iy-go-to-char)
(defvar iy-go-to-char-start-pos nil
"Position where go to char mode is enabled.")
(defvar iy-go-to-char-start-dir 1
"Jump start direction.")
(defvar iy-go-to-char-last-char nil
"Last char used in iy-go-to-char.")
(defvar iy-go-to-char-last-step 1
"Last jump step used in iy-go-to-char.")
(defvar iy-go-to-char-keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-s") 'iy-go-to-char-isearch)
(define-key map (kbd "C-r") 'iy-go-to-char-isearch-backward)
(define-key map (kbd "C-w") 'iy-go-to-char-kill-region)
(define-key map (kbd "M-w") 'iy-go-to-char-kill-ring-save)
(define-key map (kbd "C-g") 'iy-go-to-char-quit)
map)
"Keymap used when iy-go-to-char is ongoing.")
(defun iy-go-to-char--set-mc-command (command)
"Set COMMAND as multiple cursors this command."
(when (and (boundp 'multiple-cursors-mode) multiple-cursors-mode (boundp 'mc--this-command))
(setq mc--this-command command)))
(defun iy-go-to-char--set-mc-specific-vars ()
"Add `iy-go-to-char-start-pos' to `mc/cursor-specific-vars'."
(when (boundp 'mc/cursor-specific-vars)
(add-to-list 'mc/cursor-specific-vars 'iy-go-to-char-start-pos))
(remove-hook 'multiple-cursors-mode-hook 'iy-go-to-char--set-mc-specific-vars))
(add-hook 'multiple-cursors-mode-hook 'iy-go-to-char--set-mc-specific-vars)
(defun iy-go-to-char--isearch-setup ()
"Setup jump char as initial string for isearch."
(remove-hook 'isearch-mode-hook 'iy-go-to-char--isearch-setup)
(setq isearch-string (if iy-go-to-char-last-char (string iy-go-to-char-last-char) ""))
(isearch-search-and-update))
(defun iy-go-to-char--override-local-map (char)
"Override the local key map for jump char CHAR."
(setq overriding-local-map
(let ((map (copy-keymap iy-go-to-char-keymap)))
(define-key map (string char) 'iy-go-to-char-continue)
(define-key map (string iy-go-to-char-key-forward) 'iy-go-to-char-continue)
(define-key map (string iy-go-to-char-key-backward) 'iy-go-to-char-continue-backward)
(define-key map [t] 'iy-go-to-char-pass-through)
map)))
(defun iy-go-to-char-done ()
"Finish iy-go-to-char-mode."
(interactive)
(push-mark iy-go-to-char-start-pos t)
(setq iy-go-to-char-start-pos nil)
(setq overriding-local-map nil))
(defun iy-go-to-char-quit ()
"Quit iy-go-to-char-mode."
(interactive)
(goto-char iy-go-to-char-start-pos)
(setq iy-go-to-char-start-pos nil)
(setq overriding-local-map nil))
(defun iy-go-to-char-pass-through ()
"Finish iy-go-to-char-mode and invoke the corresponding command."
(interactive)
(iy-go-to-char-done)
(let* ((keys (progn
(setq unread-command-events
(append (this-single-command-raw-keys)
unread-command-events))
(read-key-sequence-vector "")))
(command (and keys (key-binding keys))))
(when (commandp command)
(setq this-command command
this-original-command command)
(iy-go-to-char--set-mc-command `(lambda ()
(interactive)
(push-mark iy-go-to-char-start-pos t)
(setq iy-go-to-char-start-pos nil)
(call-interactively ',command)))
(call-interactively command))))
(defun iy-go-to-char-isearch ()
"Start isearch using the char."
(interactive)
(iy-go-to-char-done)
(add-hook 'isearch-mode-hook 'iy-go-to-char--isearch-setup)
(isearch-forward))
(defun iy-go-to-char-isearch-backward ()
"Start isearch backward using the char."
(interactive)
(iy-go-to-char-done)
(add-hook 'isearch-mode-hook 'iy-go-to-char--isearch-setup)
(isearch-backward))
(defun iy-go-to-char-kill-region ()
"Kill region between jump start position and current position."
(interactive)
(iy-go-to-char-done)
(iy-go-to-char--set-mc-command (lambda ()
(interactive)
(kill-region (point) iy-go-to-char-start-pos)
(setq iy-go-to-char-start-pos nil)))
(kill-region (point) (mark)))
(defun iy-go-to-char-kill-ring-save ()
"Save region between jump start position and current position."
(interactive)
(iy-go-to-char-done)
(iy-go-to-char--set-mc-command (lambda ()
(interactive)
(kill-ring-save (point) iy-go-to-char-start-pos)
(setq iy-go-to-char-start-pos nil)))
(kill-ring-save (point) (mark)))
(defun iy-go-to-char--command ()
"Repeatable command to really move cursor."
(interactive)
(setq iy-go-to-char-start-pos (or iy-go-to-char-start-pos (point)))
(let ((n (if (< iy-go-to-char-start-dir 0)
(- iy-go-to-char-last-step)
iy-go-to-char-last-step)))
(search-forward (string iy-go-to-char-last-char) nil nil n)))
(defun iy-go-to-char--internal (n char)
"Store jump step N and jump CHAR for `iy-go-to-char--command'."
(interactive "p\ncGo to char: ")
(setq iy-go-to-char-last-step n)
(setq iy-go-to-char-last-char char)
(unless iy-go-to-char-start-pos
(setq iy-go-to-char-start-pos (point))
(iy-go-to-char--override-local-map char))
(setq this-original-command 'iy-go-to-char--command
this-command 'iy-go-to-char--command)
(iy-go-to-char--set-mc-command 'iy-go-to-char--command)
(call-interactively 'iy-go-to-char--command))
(defun iy-go-to-char (n char)
"Move forward to N occurrences of CHAR.
\\<iy-go-to-char-keymap>
Typing key of CHAR will move to the next occurence of CHAR.
Typing `iy-go-to-char-key-forward' will move to the next occurence of CHAR.
Typing `iy-go-to-char-key-backward', will move to the previous occurence of CHAR.
Typing \\[iy-go-to-char-quit] will quit and return to the original point.
Typing \\[iy-go-to-char-isearch] or \\[iy-go-to-char-isearch-backward]] will start `isearch` using CHAR.
Typing \\[iy-go-to-char-kill-region] or \\[iy-go-to-char-kill-ring-save] will kill/copy between current point and the start point.
Unless quit using \\[iy-go-to-char-quit] or the region is activated before searching,
the start point is set as mark."
(interactive "p\ncGo to char: ")
(setq iy-go-to-char-start-dir (if (< n 0) -1 1))
(iy-go-to-char--internal n char))
(defun iy-go-to-char-backward (n char)
"Move backward to N occurence of CHAR.
\\<iy-go-to-char-keymap>
Typing key of CHAR will move to the previous occurence of CHAR.
Typing `iy-go-to-char-key-forward' will move to the next occurence of CHAR.
Typing `iy-go-to-char-key-backward', will move to the previous occurence of CHAR.
Typing \\[iy-go-to-char-quit] will quit and return to the original point.
nTyping \\[iy-go-to-char-isearch] or \\[iy-go-to-char-isearch-backward]] will start `isearch` using CHAR."
(interactive "p\ncGo to char: ")
(setq iy-go-to-char-start-dir (if (< n 0) 1 -1))
(iy-go-to-char--internal n char))
(defun iy-go-to-char-continue (n)
"Continue last `iy-go-to-char' or `iy-go-to-char-backward'."
(interactive "p")
(when iy-go-to-char-last-char
(iy-go-to-char--internal n iy-go-to-char-last-char)))
(defun iy-go-to-char-continue-backward (n)
"Continue last `iy-go-to-char' or `iy-go-to-char-backward'."
(interactive "p")
(when iy-go-to-char-last-char
(iy-go-to-char--internal (- n) iy-go-to-char-last-char)))
(provide 'iy-go-to-char)