Subject: Re: #22873 (multiple fake cursors)

From: Keith David Bershatsky <[email-masked]>
Date: Wed, 27 Dec 2017 17:20:46 -0800

John:  Here is an example modification to the most recent version Magnar's multiple-cursors package to make use of #22873 with the patch.diff from earlier today for Emacs 26.  I like yellow for even numbered columns and red for odd numbered columns, and I like the vertical bar fake cursor, so that is what is used in this example.

(require 'multiple-cursors-core)
(require 'cl) ;; for oddp and evenp

(add-hook 'multiple-cursors-mode-disabled-hook
          (lambda () (kill-local-variable 'mc-glyph-list)))

(defvar mc/use-built-in-cursors t
"Whether to use the built-in fake cursors.")

(defun mc/create-fake-cursor-at-point (&optional id)
  "Add a fake cursor and possibly a fake active region overlay based on point and mark.
Saves the current state in the overlay to be restored later."
  (unless mc--max-cursors-original
    (setq mc--max-cursors-original mc/max-cursors))
  (when mc/max-cursors
    (unless (< (mc/num-cursors) mc/max-cursors)
      (if (yes-or-no-p (format "%d active cursors. Continue? " (mc/num-cursors)))
          (setq mc/max-cursors (read-number "Enter a new, temporary maximum: "))
        (mc/remove-fake-cursors)
        (error "Aborted: too many cursors"))))
  (let ((overlay
          (if mc/use-built-in-cursors
            (make-overlay (point) (point))
            (mc/make-cursor-overlay-at-point))))
    (overlay-put overlay 'mc-id (or id (mc/create-cursor-id)))
    (overlay-put overlay 'type 'fake-cursor)
    (overlay-put overlay 'priority 100)
    (mc/store-current-state-in-overlay overlay)
    (when (use-region-p)
      (overlay-put overlay 'region-overlay
                   (mc/make-region-overlay-between-point-and-mark)))
    overlay))

(defun mc/execute-this-command-for-all-cursors-1 ()
  "Used with post-command-hook to execute supported commands for all cursors.
  It uses two lists of commands to know what to do: the run-once
list and the run-for-all list. If a command is in neither of these lists,
it will prompt for the proper action and then save that preference.
  Some commands are so unsupported that they are even prevented for
the original cursor, to inform about the lack of support."
  (unless mc--executing-command-for-fake-cursor
    (if (eq 1 (mc/num-cursors)) ;; no fake cursors? disable mc/mode
        (mc/mode 0)
      (when this-original-command
        (let ((original-command (or mc--this-command
                                    (command-remapping this-original-command)
                                    this-original-command)))
          ;; skip keyboard macros, since they will generate actual commands that are
          ;; also run in the command loop - we'll handle those later instead.
          (when (functionp original-command)
            ;; if it's a lambda, we can't know if it's supported or not
            ;; - so go ahead and assume it's ok, because we're just optimistic like that
            (if (or (not (symbolp original-command))
                    ;; lambda registered by smartrep
                    (string-prefix-p "(" (symbol-name original-command)))
                (mc/execute-command-for-all-fake-cursors original-command)
              ;; smartrep `intern's commands into own obarray to help
              ;; `describe-bindings'.  So, let's re-`intern' here to
              ;; make the command comparable by `eq'.
              (setq original-command (intern (symbol-name original-command)))
              ;; otherwise it's a symbol, and we can be more thorough
              (if (get original-command 'mc--unsupported)
                  (message "%S is not supported with multiple cursors%s"
                           original-command
                           (get original-command 'mc--unsupported))
                (when (and original-command
                           (not (memq original-command mc--default-cmds-to-run-once))
                           (not (memq original-command mc/cmds-to-run-once))
                           (or (memq original-command mc--default-cmds-to-run-for-all)
                               (memq original-command mc/cmds-to-run-for-all)
                               (mc/prompt-for-inclusion-in-whitelist original-command)))
                  (mc/execute-command-for-all-fake-cursors original-command))))
            (when mc/use-built-in-cursors
              (let* ((win (selected-window))
                     (window-start (window-start win))
                     (window-end (window-end win))
                     (overlays (mc/all-fake-cursors))
                     (lst
                       (mapcar
                         (lambda (x)
                           (let* ((pos (overlay-start x))
                                  (current-column
                                    (if (and (>= pos window-start)
                                             (<= pos window-end))
                                      (save-excursion
                                        (goto-char pos)
                                        (current-column))
                                      0)))
                             (cond
                               ((oddp current-column)
                                 (list pos "bar" "#FF0000")) ;; red or [1.0 0.0 0.0]
                               ((evenp current-column)
                                 (list pos "bar" "yellow"))))) ;; [1.0 1.0 0.0]
                         overlays)))
                (setq mc-glyph-list lst)))))))))

(defun mc/execute-this-command-for-all-cursors-1 ()
  "Used with post-command-hook to execute supported commands for all cursors.

It uses two lists of commands to know what to do: the run-once
list and the run-for-all list. If a command is in neither of these lists,
it will prompt for the proper action and then save that preference.

Some commands are so unsupported that they are even prevented for
the original cursor, to inform about the lack of support."
  (unless mc--executing-command-for-fake-cursor

    (if (eq 1 (mc/num-cursors)) ;; no fake cursors? disable mc-mode
        (multiple-cursors-mode 0)
      (when this-original-command
        (let ((original-command (or mc--this-command
                                    (command-remapping this-original-command)
                                    this-original-command)))

          ;; skip keyboard macros, since they will generate actual commands that are
          ;; also run in the command loop - we'll handle those later instead.
          (when (functionp original-command)

            ;; if it's a lambda, we can't know if it's supported or not
            ;; - so go ahead and assume it's ok, because we're just optimistic like that
            (if (or (not (symbolp original-command))
                    ;; lambda registered by smartrep
                    (string-prefix-p "(" (symbol-name original-command)))
                (mc/execute-command-for-all-fake-cursors original-command)

              ;; smartrep `intern's commands into own obarray to help
              ;; `describe-bindings'.  So, let's re-`intern' here to
              ;; make the command comparable by `eq'.
              (setq original-command (intern (symbol-name original-command)))

              ;; otherwise it's a symbol, and we can be more thorough
              (if (get original-command 'mc--unsupported)
                  (message "%S is not supported with multiple cursors%s"
                           original-command
                           (get original-command 'mc--unsupported))
                (when (and original-command
                           (not (memq original-command mc--default-cmds-to-run-once))
                           (not (memq original-command mc/cmds-to-run-once))
                           (or mc/always-run-for-all
                               (memq original-command mc--default-cmds-to-run-for-all)
                               (memq original-command mc/cmds-to-run-for-all)
                               (mc/prompt-for-inclusion-in-whitelist original-command)))
                  (mc/execute-command-for-all-fake-cursors original-command))))))

            (when mc/use-built-in-cursors
              (let* ((win (selected-window))
                     (window-start (window-start win))
                     (window-end (window-end win))
                     (overlays (mc/all-fake-cursors))
                     (lst
                       (mapcar
                         (lambda (x)
                           (let* ((pos (overlay-start x))
                                  (current-column
                                    (if (and (>= pos window-start)
                                             (<= pos window-end))
                                      (save-excursion
                                        (goto-char pos)
                                        (current-column))
                                      0)))
                             (cond
                               ((oddp current-column)
                                 (list pos "bar" "#FF0000")) ;; red or [1.0 0.0 0.0]
                               ((evenp current-column)
                                 (list pos "bar" "yellow"))))) ;; [1.0 1.0 0.0]
                         overlays)))
                (setq mc-glyph-list lst))) ))))