multiple-cursors.el/mc-cycle-cursors.el
Marco Baringer e4adefc04e Fix issues with mc/cycle-(backward|forward) where the cycling was dependent on not having an active mark.
This patch also adds two utility functions mc/first-cursor-after and mc/last-cursor-before.
2012-10-10 13:39:28 +02:00

121 lines
4.0 KiB
EmacsLisp

;;; mc-cycle-cursors.el
;; Copyright (C) 2012 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Keywords: editing cursors
;; This program 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.
;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This scrolls the buffer to center each cursor in turn.
;; Scroll down with C-v, scroll up with M-v
;; This is nice when you have cursors that's outside of your view.
;;; Code:
(require 'multiple-cursors-core)
(eval-when-compile (require 'cl))
(defun mc/next-cursor-after-point ()
(let ((pos (point))
(next-pos (point-max))
next)
(mc/for-each-fake-cursor
(let ((cursor-pos (overlay-get cursor 'point)))
(when (and (< pos cursor-pos)
(< cursor-pos next-pos))
(setq next-pos cursor-pos)
(setq next cursor))))
next))
(defun mc/prev-cursor-before-point ()
(let ((pos (point))
(prev-pos (point-min))
prev)
(mc/for-each-fake-cursor
(let ((cursor-pos (overlay-get cursor 'point)))
(when (and (> pos cursor-pos)
(> cursor-pos prev-pos))
(setq prev-pos cursor-pos)
(setq prev cursor))))
prev))
(defcustom mc/cycle-looping-behaviour 'continue
"What to do if asked to cycle beyond the last cursor or before the first cursor."
:type '(radio (const :tag "Loop around to beginning/end of document." continue)
(const :tag "Warn and then loop around." warn)
(const :tag "Signal an error." error)))
(defun mc/handle-loop-condition (error-message)
(ecase mc/cycle-looping-behaviour
(error (error error-message))
(warn (message error-message))
(continue nil)))
(defun mc/cycle (next-cursor fallback-cursor loop-message)
(when (null next-cursor)
(mc/handle-loop-condition loop-message)
(setf next-cursor fallback-cursor))
(mc/create-fake-cursor-at-point)
(mc/pop-state-from-overlay next-cursor)
(recenter))
(defun extreme (sequence predicate &optional key)
"Returns the most predicate-y element of sequence; equivalent
to (first (sort sequence text)). The extreme of the empty list is
always nil."
(let ((extreme (first sequence)))
(dolist (i (rest sequence))
(when (funcall predicate
(funcall (or key 'identity) i)
(funcall (or key 'identity) extreme))
(setf extreme i)))
extreme))
(defun mc/first-cursor-after (point)
"Very similar to mc/furthest-cursor-before-point, but ignores (mark) and (point)."
(extreme (remove-if (lambda (cursor)
(< (mc/cursor-beg cursor) point))
(mc/all-fake-cursors))
'< 'mc/cursor-beg))
(defun mc/last-cursor-before (point)
"Very similar to mc/furthest-cursor-before-point, but ignores (mark) and (point)."
(extreme (remove-if (lambda (cursor)
(> (mc/cursor-end cursor) point))
(mc/all-fake-cursors))
'> 'mc/cursor-end))
(defun mc/cycle-forward ()
(interactive)
(mc/cycle (mc/next-cursor-after-point)
(mc/first-cursor-after (point-min))
"We're already at the last cursor."))
(defun mc/cycle-backward ()
(interactive)
(mc/cycle (mc/prev-cursor-before-point)
(mc/last-cursor-before (point-max))
"We're already at the last cursor"))
(define-key mc/keymap (kbd "C-v") 'mc/cycle-forward)
(define-key mc/keymap (kbd "M-v") 'mc/cycle-backward)
(provide 'mc-cycle-cursors)
;;; mc-cycle-cursors.el ends here