Function: combobulate-proffer-choices

combobulate-proffer-choices is a natively compiled function defined in combobulate-manipulation.el.

Signature

(combobulate-proffer-choices NODES ACTION-FN &key (FIRST-CHOICE nil) (RESET-POINT-ON-ABORT t) (RESET-POINT-ON-ACCEPT nil) (PROMPT-DESCRIPTION nil) (EXTRA-MAP nil) (FLASH-NODE nil) (QUIET nil) (ACCEPT-ACTION 'rollback) (CANCEL-ACTION 'commit) (SWITCH-ACTION 'rollback) (RECENTER nil) (ALLOW-NUMERIC-SELECTION nil) (SIGNAL-ON-ABORT nil) (START-INDEX 0) (BEFORE-SWITCH-FN nil) (AFTER-SWITCH-FN nil) (UNIQUE-ONLY t))

Documentation

Interactively browse NODES one at a time with ACTION-FN applied to it.

Interactively let the user select one of the nodes in NODES and preview the transformation Combobulate would apply if they accept that choice. The active node is rendered with ACTION-FN to allow the user to see what they are selecting and the possible transformation that will take place if they accept the choice.

The user can then select the node with RET, or abort the selection with C-g. The user can also cycle through the nodes with TAB and S-TAB. See combobulate-proffer-map.

If there is exactly one node in NODES, then it is automatically selected and no user interaction is required.

ACTION-FN must be a function that takes four arguments:

   (INDEX CURRENT-NODE PROXY-NODES REFACTOR-ID)

Where INDEX is the current index of the node in PROXY-NODES, which is the same as CURRENT-NODE. PROXY-NODES is a list of proxy nodes available to the user to choose from. REFACTOR-ID is a unique identifier for the current refactoring operation. Use combobulate-refactor with the REFACTOR-ID to manipulate the state of the refactoring operation.

Setting :first-choice to non-nil prevents the system from proffering choices at all; instead, the first choice is automatically picked, if there is a choice to make.

When :reset-point-on-abort or :reset-point-on-accept is non-nil, the point is reset to where it was when the proffer was first started depending on the outcome of the proffer.

If :flash-node is non-nil, then display a node tree in the echo area alongside the status message.

If non-nil, :unique-only filters out duplicate nodes *and* nodes that share the same range extent. I.e., a block and a statement node that effectively encompass the same range in the buffer.

:allow-numeric-selection is a boolean that determines whether
the user can select a node by typing its index. If non-nil, then the user can type a number to select the node at that index from
1 through to 9.

:extra-map is a list of cons cells consisting of (KEY
. COMMAND). The extra keys are mapped into the proffer map,
combobulate-proffer-map.

:accept-action is a symbol that determines what happens when
the user accepts a choice. The following symbols are supported:

  rollback - Rollback the refactoring operation.
  commit - Commit the refactoring operation.

:cancel-action and :switch-action is the same as
:accept-action, but for when the user cancels the choice or
interactively switches to a different node.

:signal-on-abort is a symbol that determines what happens when
the user aborts the choice. The following symbols are supported:

  error - Signal an error.
  message - Display a message in the echo area.

:prompt-description is a string that is displayed in the prompt.

:start-index is an integer that determines the starting index
of the node in the list of nodes. This is useful when you want to skip over the first few nodes in the list.

:recenter is a boolean that determines whether to recenter the
buffer when the user switches to a different node.

:quiet is a boolean that determines whether to suppress the
status message when the user switches to a different node, accepts or cancels the proffer.

Source Code

;; Defined in /nix/store/b5fvrwzi3zvkabyzx0in6gw1cj963z46-emacs-packages-deps/share/emacs/site-lisp/combobulate-manipulation.el
(cl-defun combobulate-proffer-choices (nodes action-fn &key
                                             (first-choice nil)
                                             (reset-point-on-abort t) (reset-point-on-accept nil)
                                             (prompt-description nil)
                                             (extra-map nil)
                                             (flash-node nil)
                                             (quiet nil)
                                             (accept-action 'rollback)
                                             (cancel-action 'commit)
                                             (switch-action 'rollback)
                                             (recenter nil)
                                             (allow-numeric-selection nil)
                                             (signal-on-abort nil)
                                             (start-index 0)
                                             (before-switch-fn nil)
                                             (after-switch-fn nil)
                                             (unique-only t))
  "Interactively browse NODES one at a time with ACTION-FN applied to it.

Interactively let the user select one of the nodes in NODES and
preview the transformation Combobulate would apply if they accept
that choice. The active node is rendered with ACTION-FN to allow
the user to see what they are selecting and the possible
transformation that will take place if they accept the choice.

The user can then select the node with RET, or abort the
selection with C-g. The user can also cycle through the nodes
with TAB and S-TAB. See `combobulate-proffer-map'.

If there is exactly one node in NODES, then it is automatically
selected and no user interaction is required.

ACTION-FN must be a function that takes four arguments:

   (INDEX CURRENT-NODE PROXY-NODES REFACTOR-ID)

Where INDEX is the current index of the node in PROXY-NODES,
which is the same as CURRENT-NODE. PROXY-NODES is a list of proxy
nodes available to the user to choose from. REFACTOR-ID is a
unique identifier for the current refactoring operation. Use
`combobulate-refactor' with the REFACTOR-ID to manipulate the
state of the refactoring operation.

Setting `:first-choice' to non-nil prevents the system from
proffering choices at all; instead, the first choice is
automatically picked, if there is a choice to make.

When `:reset-point-on-abort' or `:reset-point-on-accept' is
non-nil, the point is reset to where it was when the proffer was
first started depending on the outcome of the proffer.

If `:flash-node' is non-nil, then display a node tree in the echo
area alongside the status message.

If non-nil, `:unique-only' filters out duplicate nodes *and*
nodes that share the same range extent. I.e., a `block' and a
`statement' node that effectively encompass the same range in the
buffer.

`:allow-numeric-selection' is a boolean that determines whether
the user can select a node by typing its index. If non-nil, then
the user can type a number to select the node at that index from
1 through to 9.

`:extra-map' is a list of cons cells consisting of (KEY
. COMMAND). The extra keys are mapped into the proffer map,
`combobulate-proffer-map'.

`:accept-action' is a symbol that determines what happens when
the user accepts a choice. The following symbols are supported:

  `rollback' - Rollback the refactoring operation.
  `commit' - Commit the refactoring operation.

`:cancel-action' and `:switch-action' is the same as
`:accept-action', but for when the user cancels the choice or
interactively switches to a different node.

`:signal-on-abort' is a symbol that determines what happens when
the user aborts the choice. The following symbols are supported:

  `error' - Signal an error.
  `message' - Display a message in the echo area.

`:prompt-description' is a string that is displayed in the prompt.

`:start-index' is an integer that determines the starting index
of the node in the list of nodes. This is useful when you want to
skip over the first few nodes in the list.

`:recenter' is a boolean that determines whether to recenter the
buffer when the user switches to a different node.

`:quiet' is a boolean that determines whether to suppress the
status message when the user switches to a different node,
accepts or cancels the proffer. "
  (setq allow-numeric-selection
	;; numeric selection uses `C-1' through to `C-9' which cannot
	;; always be typed on a terminal.
	(and allow-numeric-selection
             (display-graphic-p)
             combobulate-proffer-allow-numeric-selection))
  (let ((proxy-nodes
         (and nodes
              (funcall #'combobulate-proxy-node-make-from-nodes
                       ;; strip out duplicate nodes. That
                       ;; includes nodes that are duplicates
                       ;; of one another; however, we also
                       ;; strip out nodes that share the
                       ;; same range extent.
                       (if unique-only
                           (seq-uniq nodes (lambda (a b)
                                             (or (equal a b)
                                                 (equal (combobulate-node-range a)
                                                        (combobulate-node-range b)))))
                         nodes))))
        (result) (state 'continue) (current-node)
        (index start-index) (pt (point)) (raw-event)
        (refactor-id (combobulate-refactor-setup))
        (change-group)
        (prompt)
        (map (let ((map (make-sparse-keymap)))
               (set-keymap-parent map combobulate-proffer-map)
               ;; do ont bind the same key twice; this could override
               ;; important keys like RET.
               (unless (lookup-key map (this-command-keys))
                 (define-key map (this-command-keys) 'next))
               (when extra-map
                 (mapc (lambda (k) (define-key map (car k) (cdr k))) extra-map))
               (when allow-numeric-selection
                 (dotimes (i 9)
                   (define-key map (kbd (format "C-%d" (1+ i))) (1+ i))))
               map)))
    (unless proxy-nodes (error "There are no choices to make"))
    (cl-assert (< start-index (length proxy-nodes)) nil
               "Start index %d is greater than the number of nodes %d"
               start-index (length proxy-nodes))
    (condition-case err
        (with-undo-amalgamate
          (catch 'exit
            (while (eq state 'continue)
              (unless change-group
                (setq change-group (prepare-change-group)))
              (catch 'next
                (setq current-node (nth index proxy-nodes))
                (combobulate-refactor (:id refactor-id)
                  (let ((proffer-action
                         (combobulate-proffer-action-create
                          :index index
                          :current-node current-node
                          :proxy-nodes proxy-nodes
                          :refactor-id refactor-id
                          :prompt-description
                          (or prompt-description "")
                          :extra-map extra-map
                          :display-indicator (combobulate-display-indicator index (length proxy-nodes)))))
                    (funcall action-fn proffer-action)
                    (with-slots (display-indicator prompt-description) proffer-action
                      (setq prompt
                            (substitute-command-keys
                             (format "%s %s`%s': `%s' or \\`S-TAB' to cycle%s; \\`C-g' quits; rest accepts.%s"
                                     display-indicator
                                     (concat (cond ((stringp prompt-description) prompt-description)
                                                   ((functionp prompt-description) (funcall prompt-description proffer-action))
                                                   (t ""))
                                             " ")
                                     ;; (propertize " → " 'face 'shadow)
                                     (propertize (combobulate-pretty-print-node current-node) 'face
                                                 'combobulate-tree-highlighted-node-face)
                                     (mapconcat (lambda (k)
                                                  (propertize (key-description k) 'face 'help-key-binding))
                                                ;; messy; is this really the best way?
                                                (where-is-internal 'next map)
                                                ", ")
                                     (if allow-numeric-selection (concat "; \\`C-1' to \\`C-9' to select") "")
                                     (if (and flash-node combobulate-flash-node)
                                         (concat "\n"
                                                 (or (combobulate-display-draw-node-tree
                                                      (combobulate-proxy-node-to-real-node current-node))
                                                     ""))
                                       "")))))
                    (cl-flet ((refactor-action (action)
                                (cond ((eq action 'commit)
                                       (commit))
                                      ((eq action 'rollback)
                                       (rollback))
                                      (t (error "Unknown action: %s" action)))))
                      (and before-switch-fn (funcall before-switch-fn))
                      (when recenter (ignore-errors (recenter)))
                      ;; if we have just one item, or if
                      ;; `:first-choice' is non-nil, we pick the first
                      ;; item in `proxy-nodes'
                      (if (or (= (length proxy-nodes) 1) first-choice)
                          (progn (refactor-action accept-action)
                                 (setq state 'accept))
                        (run-hooks 'combobulate-proffer-after-action-hook)
                        (setq result
                              (condition-case nil
                                  (lookup-key
                                   map
                                   ;; we need to preserve the raw
                                   ;; event so we can put it back on
                                   ;; the unread event loop later if
                                   ;; the key is not recognised.
                                   (setq raw-event
                                         ;; hooooo boy; so the
                                         ;; terminal does not like
                                         ;; certain keys (or key
                                         ;; sequences) and misbehaves
                                         ;; especially if something
                                         ;; like TAB is used. On a
                                         ;; console, we'll use the one
                                         ;; way I know that does work
                                         ;; (albeit imperfectly as it
                                         ;; swallows whole complete
                                         ;; key sequences not bound in
                                         ;; this key map, and it also
                                         ;; echoes the key which is
                                         ;; annoying). On a graphical
                                         ;; display, we'll use the
                                         ;; normal way.
                                         (if (display-graphic-p)
                                             (vector (read-key prompt))
                                           (read-key-sequence-vector prompt))))
                                ;; if `condition-case' traps a quit
                                ;; error, then map it into the symbol
                                ;; `cancel', which corresponds to the
                                ;; equivalent event in the state
                                ;; machine below.
                                (quit 'cancel)
                                (t (rollback) 'cancel)))
                        (run-hooks 'combobulate-proffer-after-action-hook)
                        (and after-switch-fn (funcall after-switch-fn))
                        (pcase result
                          ('prev
                           (refactor-action switch-action)
                           (setq index (mod (1- index) (length proxy-nodes)))
                           (throw 'next nil))
                          ('next
                           (refactor-action switch-action)
                           (setq index (mod (1+ index) (length proxy-nodes)))
                           (throw 'next nil))
                          ('done
                           (refactor-action accept-action)
                           (unless quiet (combobulate-message "Committing" current-node))
                           (setq state 'accept))
                          ((pred functionp)
                           (funcall result)
                           (refactor-action accept-action)
                           (throw 'next nil))
                          ('cancel
                           (unless quiet (combobulate-message "Cancelling..."))
                           (setq state 'abort)
                           (refactor-action cancel-action)
                           (keyboard-quit))
                          ;; handle numeric selection `1' to `9'
                          ((and (pred (numberp))
                                (pred (lambda (n) (and (>= n 1)
                                                  (<= n 9)
                                                  (<= n (length proxy-nodes)))))
                                n)
                           (refactor-action switch-action)
                           (setq index (1- n))
                           (throw 'next nil))
                          (_
                           (unless quiet (combobulate-message "Committing..."))
                           ;; pushing `raw-event' to
                           ;; `unread-command-events' allows for a
                           ;; seamless exit out of the proffer
                           ;; prompt by preserving the the last,
                           ;; unhandled event the user inputted.
                           (when (length> raw-event 0)
                             (push (aref raw-event 0) unread-command-events))
                           (refactor-action accept-action)
                           (setq state 'accept)))
                        (run-hooks 'combobulate-proffer-after-action-hook)))))
                (when change-group
                  (cancel-change-group change-group)))
              (activate-change-group change-group))))
      (quit (when signal-on-abort
              (signal (car err) (cdr err)))))
    ;; Determine where point is placed on exit and whether we return
    ;; the current node or not.
    (cond
     ((and (eq state 'abort))
      (when reset-point-on-abort (goto-char pt))
      nil)
     ((and (eq state 'accept))
      (when reset-point-on-accept
        (goto-char pt))
      current-node)
     (t (error "Unknown termination state `%s'" state)))))