diff --git a/README.org b/README.org index 660b5841..8cecc85a 100644 --- a/README.org +++ b/README.org @@ -216,6 +216,8 @@ You then have two ways to access these strings from the completion prompt: 1. by using =M-n= from the prompt, which will cycle through the strings 2. by calling =citar-insert-preset= with a keybinding, and then selecting the string +If you want to select multiple candidates using the same search term =M-n= also recalls the previous input, so you can use =TAB= to select one candidate then press =M-n= to quickly insert the previous search term again (see notes below for some caveats). + =citar= also preserves the history of your selections (see caveat below about multiple candidate selection though), which are also accessible in your completion UI, but by using =M-p=. You can save this history across sessions by adding =citar-history= to =savehist-additional-variables=. @@ -238,6 +240,14 @@ The ~citar-notes-sources~ variable configures note backends, and ~citar-notes-so A backend primarily specifies functions to update the Citar display, to create the completion candidates, and to open existing and new notes. See the ~citar-notes-sources~ docstring for details, and the =citar-register-notes-source= and =citar-remove-notes-source= convenience functions. +Using =M-n= to recall the previous input only works when selecting multiple candidates during a single =citar-command=. If you use =vertico=, =vertico-repeat= provides similar functionality for rerunning the commands with the last input. + +By default =Emacs= leaves the point at the beginning of input if you press =M-n= with minibuffer empty. If you want to change this behavior and instead leave the point at the end of input use, + +#+BEGIN_SRC emacs-lisp +(advice-add 'goto-history-element :after (defun my-end-of-buffer (&rest _) (goto-char (point-max)))) +#+END_SRC + ** Files, file association and file-field parsing If you have ~citar-library-paths~ set, the relevant open commands will look in those directories for file names of =CITEKEY.EXTENSION=. diff --git a/citar.el b/citar.el index f26eb9af..2fb59ae5 100644 --- a/citar.el +++ b/citar.el @@ -584,10 +584,17 @@ to filter them." SEL is the key which should be used for selection. EXIT is the key which is used for exiting the minibuffer during completing read.") +(defvar citar--multiple-last-input nil + "Variable used to track the input so that it can be restored subsequently.") + (defun citar--multiple-exit () "Exit with the currently selected candidates." (interactive) - (setq unread-command-events (listify-key-sequence (kbd (car citar--multiple-setup))))) + (funcall-interactively (key-binding (kbd (car citar--multiple-setup))))) + +(defun citar--multiple-record-input () + "Record the current minibuffer input." + (setq citar--multiple-last-input (minibuffer-contents-no-properties))) (defun citar--setup-multiple-keymap () "Make a keymap suitable for `citar--select-multiple'." @@ -596,29 +603,34 @@ is used for exiting the minibuffer during completing read.") (kbdexit (kbd (cdr citar--multiple-setup)))) (define-key keymap kbdselect (lookup-key keymap kbdexit)) (define-key keymap kbdexit #'citar--multiple-exit) - (use-local-map keymap))) + (use-local-map keymap)) + (add-hook 'post-command-hook #'citar--multiple-record-input nil t)) (defun citar--select-multiple (prompt candidates &optional filter history def) "Select multiple CANDIDATES with PROMPT. HISTORY is the `completing-read' history argument." ;; Because completing-read-multiple just does not work for long candidate ;; strings, and IMO is a poor UI. - (let* ((selected-hash (make-hash-table :test 'equal))) - (while (let ((initial-history (symbol-value history)) - (item (minibuffer-with-setup-hook #'citar--setup-multiple-keymap - (completing-read - (format "%s (%s/%s): " prompt - (hash-table-count selected-hash) - (hash-table-count candidates)) - (citar--multiple-completion-table selected-hash candidates filter) - nil t nil history `("" . ,def))))) - (unless (string-empty-p item) + (let* ((selected-hash (make-hash-table :test 'equal)) + (command this-command)) + (push (setq citar--multiple-last-input "") def) + (while (let* ((initial-history (symbol-value history)) + (item (minibuffer-with-setup-hook #'citar--setup-multiple-keymap + (completing-read + (format "%s (%s/%s): " prompt + (hash-table-count selected-hash) + (hash-table-count candidates)) + (citar--multiple-completion-table selected-hash candidates filter) + nil t nil history def)))) + (push citar--multiple-last-input def) + (unless (string-blank-p citar--multiple-last-input) (if (not (gethash item selected-hash)) (puthash item t selected-hash) (remhash item selected-hash) (set history initial-history))) - (not (or (eq last-command #'citar--multiple-exit) - (string-empty-p item))))) + (not (or (eq this-command #'citar--multiple-exit) + (string-blank-p citar--multiple-last-input)))) + (setq this-command command)) (hash-table-keys selected-hash))) (cl-defun citar--get-resource-candidates (citekeys &key files links notes create-notes)