diff --git a/rectangular-region-mode.el b/rectangular-region-mode.el new file mode 100644 index 0000000..7773c78 --- /dev/null +++ b/rectangular-region-mode.el @@ -0,0 +1,70 @@ +(require 'multiple-cursors-core) + +(defvar rrm/anchor (make-marker) + "The position in the buffer that anchors the rectangular region.") + +(defvar rectangular-region-mode-map (make-sparse-keymap) + "Keymap for rectangular region is mainly for rebinding C-g") + +(define-key rectangular-region-mode-map (kbd "C-g") 'rrm/keyboard-quit) + +(defun rrm/keyboard-quit () + (interactive) + (rectangular-region-mode 0) + (rrm/remove-rectangular-region-overlays) + (deactivate-mark)) + +;; Bind this to a key (for instance H-SPC) to start rectangular-region-mode +(defun set-rectangular-region-anchor () + (interactive) + (set-marker rrm/anchor (point)) + (push-mark (point)) + (rectangular-region-mode 1)) + +(defun rrm/remove-rectangular-region-overlays () + (mc/remove-fake-cursors) + (mapc #'(lambda (o) + (when (eq (overlay-get o 'type) 'additional-region) + (delete-overlay o))) + (overlays-in (point-min) (point-max)))) + +(defun rrm/repaint () + (rrm/remove-rectangular-region-overlays) + (let* ((annoying-arrows-mode nil) + (point-column (current-column)) + (point-line (line-number-at-pos)) + (anchor-column (save-excursion (goto-char rrm/anchor) (current-column))) + (anchor-line (save-excursion (goto-char rrm/anchor) (line-number-at-pos))) + (left-column (if (< point-column anchor-column) point-column anchor-column)) + (right-column (if (> point-column anchor-column) point-column anchor-column)) + (num-mirrors (abs (- point-line anchor-line))) + (num-chars (- right-column left-column)) + (navigation-func (if (< point-line anchor-line) 'next-line 'previous-line))) + (move-to-column anchor-column t) + (set-mark (point)) + (move-to-column point-column t) + (mc/save-excursion + (dotimes (i num-mirrors) + (funcall navigation-func) + (move-to-column right-column t) + (move-to-column anchor-column t) + (set-mark (point)) + (move-to-column point-column t) + (mc/create-fake-cursor-at-point))))) + +(defun rrm/execute-change (&rest forms) + (rectangular-region-mode 0) + (multiple-cursors-mode 1)) + +(define-minor-mode rectangular-region-mode + "A mode for creating a rectangular region to edit" + nil " rr" rectangular-region-mode-map + (if rectangular-region-mode + (progn + (add-hook 'after-change-functions 'rrm/execute-change t t) + (add-hook 'post-command-hook 'rrm/repaint t t)) + (remove-hook 'after-change-functions 'rrm/execute-change t) + (remove-hook 'post-command-hook 'rrm/repaint t) + (set-marker rrm/anchor nil))) + +(provide 'rectangular-region-mode) diff --git a/todo.org b/todo.org index 45c8eca..0814503 100644 --- a/todo.org +++ b/todo.org @@ -1,21 +1,36 @@ -* TODO: [4/11] -** DONE (set-marker MARKER nil) for performance -** DONE C-g deactivates regions first, before disabling multiple-cursors -** DONE more state to save per cursor: er/history -** DONE refactor: an object with all the current state, used for both overlays and current +* Ideas [1/5] +** DONE rectangular-region-mode + Switch to a specialized multiple-cursors mode, anchoring it to the current + cursor position. Moving around increases/decreases the size/number of cursors, + to simulate vertical editing in textmate. +** TODO Cycle through cursors + To scroll the buffer and watch all your cursors - make a keybinding for + promoting the next/previous fake cursor. +** TODO Create replacements for common non-working commands + Possible to do C-s ... how about M-y ? + Any way to make M-z just prompt for letter once? +** TODO mark-multiple-mode + mark-multiple has some advantages + -> simpler visuals, no extra cursors (matter of taste?) + -> moving out of the marked area quits the mode + -> yank-pop actually works ** TODO unknown command: Do for (a)ll, (o)nce or (i)gnore -> (did that work ok? (k)eep doing that or (d)on't) unknown-command ran once - what now? (o)nce is enough, repeat for (a)ll, (u)ndo - that did NOT work. unknown-command ran for all - did that work as expected? (y)es, (n)o what to do about unknown-command in the future? Run it (o)nce, or (s)top it from being run. +* BUGS: [0/4] +** TODO with multiple regions active, C-d deletes only a char in the fake ones +** TODO there's something going wrong with last-command/this-command? + expand-region works nicely, even collapse when we save er/history, but last collapse puts all cursors in same spot +** TODO clean up overlays when reverting buffer +** TODO rectangular-region-mode fails when lines of the buffer have been hidden (ref collapse in org-mode) +* TODO: [4/6] +** DONE (set-marker MARKER nil) for performance +** DONE C-g deactivates regions first, before disabling multiple-cursors +** DONE more state to save per cursor: er/history +** DONE refactor: an object with all the current state, used for both overlays and current ** TODO separate mark-multiple and multiple-cursors mark-multiple goes back to being the util? or just dies? given the problem with extract-var and undo, may just kill mark-multiple. problem with that is: C-g when doing rename-tag shouldn't go to multiple-cursors. -** TODO clean up overlays when reverting buffer ** TODO add tests -** TODO collapse cursors at same point (?) -** TODO there's something going wrong with last-command/this-command? - expand-region works nicely, even collapse when we save er/history, but last collapse puts all cursors in same spot - -** TODO Create replacements for some common non-working commands - Possible to do C-s ... how about M-y ?