diff --git a/.gitignore b/.gitignore index 571ede8c966..6bb154c614d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,10 @@ Nyxt.app _build/submodules/system-index.txt # Ignore PNG and XCF -*.png *.xcf +*.png +# Except for this foler +!libraries/prompter/*.png # Ignore compiled lisp files *.FASL diff --git a/libraries/prompter/README.org b/libraries/prompter/README.org index 550bb3e9fee..2be5e855cef 100644 --- a/libraries/prompter/README.org +++ b/libraries/prompter/README.org @@ -1,6 +1,37 @@ -This prompter library is heavily inspired by Emacs' minibuffer and [[https://emacs-helm.github.io/helm/][Helm]]. +* Class overview -It only deals with the backend side of things, it does not handle any display. +The key objects are =prompters=, =sources= and =suggestions=. + +A =prompt= is an interface for user interactions that holds one or more +=sources=, and each of those are populated by =suggestions=. + +Other central concepts include: + +- =prompt= + + =selection= :: A single =suggestion=, in any. Think of it as the currently + selected =suggestion=, belonging to a =source=. +- =source= + + =marks= :: A list of =suggestions=, when =multi-selection-p= is non-nil. + + =actions= :: A list of functions that run on =suggestions=. + - =return-actions= :: General purpose. + - =marks-actions= :: On =marks= change (event-driven). + - =selection-actions= :: On =selection= change (event-driven). + +Example: Find below a graphical visualization of a single prompt with sources 1 +and 2, and suggestions A, B, C and D. The =marks= is the list composed by +Suggestions A and C. The =selection= is Suggestion B. + +[[file:example.png]] + +Remarks: + +A =prompt= always has a single =selection=, whereas a =source= has either a +single =selection= or none (when =prompt= has multiple =sources=). + +=marks= is a concept related to =source= not =prompt=, unlike that of +=selection=. + +* Features Non-exhaustive list of features: @@ -17,3 +48,6 @@ Non-exhaustive list of features: - Selection actions (automatically run persistent actions on selection change). - Marks actions (automatically run persistent actions on marks change). - Automatically return the prompt when narrowed down to a single suggestion. + +This library is heavily inspired by Emacs' minibuffer and [[https://emacs-helm.github.io/helm/][Helm]]. It only deals +with the backend side of things, it does not handle any display. diff --git a/libraries/prompter/example.png b/libraries/prompter/example.png new file mode 100644 index 00000000000..5b3dce2a63a Binary files /dev/null and b/libraries/prompter/example.png differ diff --git a/libraries/prompter/prompter-source.lisp b/libraries/prompter/prompter-source.lisp index 10d49c90415..799cf2bc495 100644 --- a/libraries/prompter/prompter-source.lisp +++ b/libraries/prompter/prompter-source.lisp @@ -208,13 +208,17 @@ recompute the match-data for instance.") The predicate works the same as the `sort' predicate.") (return-actions - '(identity) - :type list + #'identity + :type (or null + (or function function-symbol) + (cons (or function function-symbol) *)) :accessor nil :export nil :documentation "List of funcallables that can be run on `suggestion's of this source. This is the low-level implementation, see the `return-actions' -function for the public interface.") +function for the public interface. +For convenience, it may be initialized with a single function or symbol, in +which case it will be automatically turned into a list.") (update-notifier (make-channel) @@ -275,7 +279,7 @@ to compute asynchronously.") (multi-selection-p nil :type boolean - :documentation "Whether multiple candidates can be marked.") + :documentation "Whether multiple `suggestion's can be marked.") (resumer nil @@ -310,7 +314,7 @@ Also see `selection-actions-enabled-p'.")) (:accessor-name-transformer (class*:make-name-transformer name)) (:documentation "A prompter source instance is meant to be used by a `prompter' object. See its `sources' slot. A source is a consistent collection -of suggestions, filters, return-actions. +of suggestions, filters and actions. When a `prompter' `input' is set, the `update' function is called over all sources. This function pipelines `initial-suggestions' through @@ -331,7 +335,7 @@ call.")) (run-thread "Prompter mark action thread" (funcall action (marks source))))) (defmethod default-selection-action ((source prompter:source)) - (first (slot-value source 'selection-actions))) + (first (selection-actions source))) (export-always 'object-attributes) (defgeneric object-attributes (object source) @@ -626,6 +630,8 @@ If you are looking for a source that just returns its plain suggestions, use `so (calispel:? wait-channel)) (setf (selection-actions source) (uiop:ensure-list (selection-actions source))) (setf (marks-actions source) (uiop:ensure-list (marks-actions source))) + (setf (slot-value source 'return-actions) + (uiop:ensure-list (slot-value source 'return-actions))) source) (export-always 'attributes-keys-non-default) diff --git a/libraries/prompter/prompter.lisp b/libraries/prompter/prompter.lisp index 47c8444b31c..17005d61c61 100644 --- a/libraries/prompter/prompter.lisp +++ b/libraries/prompter/prompter.lisp @@ -32,7 +32,7 @@ A new object is created on every new input.")) (the (values cl-containers:ring-buffer-reverse &optional) (containers:make-ring-buffer size :last-in-first-out))) -;; Same as `source' as to why we wrap in `eval-always'. +;; Eval at read-time because `make' is generated using the class' initargs. (sera:eval-always (define-class prompter () ((input @@ -97,8 +97,7 @@ when the suggestions are narrowed down to just one item.") (history (make-history) :type (or containers:ring-buffer-reverse null) - :documentation - "History of inputs for the prompter. + :documentation "History of inputs for the prompter. If nil, no history is used.") (result-channel @@ -127,16 +126,16 @@ See also `result-channel'.") (:export-accessor-names-p t) (:accessor-name-transformer (class*:make-name-transformer name)) (:documentation "The prompter is an interface for user interactions. -A prompter object holds multiple sources (of type `source') which -contain a list of `suggestion's. +A prompter object holds multiple `source's which contain a list of +`suggestion's. -You can call `destroy' to call the registered termination functions of the -prompter and its sources. +Call `destroy' to the register termination functions of the prompter and its +sources. -Suggestions are computed asynchronously when `input' is updated. -Use `all-ready-p' and `next-ready-p' to know when the prompter is ready. -Sources suggestions can be retrieved, possibly partially, even when the -compution is not finished."))) +`suggestion's are computed asynchronously when `input' is updated. +Use `all-ready-p' and `next-ready-p' to access whether the prompter is ready. +Sources' suggestions can be retrieved, possibly partially, even when the +computation is not finished."))) (defun update-sources (prompter &optional (text "")) (setf (sync-queue prompter) (make-instance 'sync-queue)) @@ -367,34 +366,33 @@ Empty sources are skipped." (union marks suggestion-values) (intersection marks suggestion-values)))))))))) -(defun resolve-selection (prompter) ; TODO: Write tests for this! - "Return the list of selected values. -If there is no marks, the current selection value is returned as a list of one element. -For instance, if the selected element value is NIL, this returns '(NIL). -If there is no element, NIL is returned." +(defun resolve-marks (prompter) ; TODO: Write tests for this! + "Return the list of marked `suggestion's. +When `marks' is nil, the current selection value is returned as a list of one +element. +For instance, if the selected element value is NIL, this returns '(NIL). If +there is no element, NIL is returned." (or (all-marks prompter) (mapcar #'value (uiop:ensure-list (selected-suggestion prompter))))) (export-always 'return-actions) (defun return-actions (prompter) - "Return the list of contextual return-actions. -Without marks, it's the list of return-actions for the current source. -With marks, it's the intersection of the return-actions of the sources that contain the -marked elements." - (alex:if-let ((marked-sources - (remove-if (complement #'marks) (sources prompter)))) - (reduce #'intersection (mapcar (lambda (source) - (slot-value source 'return-actions)) - marked-sources)) + "Return the list of contextual `return-actions'. + +When `marks' is non-nil, return the list of `return-actions' shared by every +marked element; otherwise return the list of `return-actions' for the current +`source'." + (alex:if-let ((marked-sources (remove-if (complement #'marks) (sources prompter)))) + (reduce #'intersection + (mapcar (lambda (source) (slot-value source 'return-actions)) + marked-sources)) (slot-value (selected-source prompter) 'return-actions))) (defun history-pushnew (history element &key (test #'equal) ) - (alex:when-let ((previous-element-index (containers:element-position - history - element - :test test))) - (containers:delete-item-at history - previous-element-index)) + (alex:when-let ((previous-element-index (containers:element-position history + element + :test test))) + (containers:delete-item-at history previous-element-index)) (containers:insert-item history element)) @@ -409,17 +407,14 @@ If input is already in history, move to first position." (export-always 'return-selection) (defun return-selection (prompter &optional (return-action (default-return-action prompter))) - "Call RETURN-ACTION over selection and send the results to PROMPTER's `result-channel'. -The selection is the collection of marked suggestions across all sources. -If there is no marked suggestion, send the currently selected suggestion -instead." - (unless return-action - (setf return-action #'identity)) + "Call RETURN-ACTION over `marks' and send the results to PROMPTER's `result-channel'. +See `resolve-marks' for a reference on how `marks' are handled." + (unless return-action (setf return-action #'identity)) (setf (returned-p prompter) t) (add-input-to-history prompter) - (alex:when-let ((selection-values (resolve-selection prompter))) - (let ((return-action-result (funcall return-action selection-values))) - (calispel:! (result-channel prompter) return-action-result))) + (alex:when-let ((marks (resolve-marks prompter))) + (calispel:! (result-channel prompter) + (funcall return-action marks))) (destroy prompter)) (export-always 'toggle-selection-actions-enabled) @@ -429,14 +424,11 @@ instead." (setf (selection-actions-enabled-p source) (not (selection-actions-enabled-p source)))) (export-always 'next-ready-p) -(defun next-ready-p (prompter &optional timeout) +(defun next-ready-p (prompter) "Block and return next PROMPTER ready source. It's the next source that's done updating. If all sources are done, return T. -This is unblocked when the PROMPTER is `destroy'ed. - -TIMEOUT is deprecated." - (declare (ignore timeout)) ; Deprecated. +This is unblocked when the PROMPTER is `destroy'ed." (when prompter ;; We let-bind `sync-queue' here so that it remains the same object throughout ;; this function, since the slot is subject to be changed concurrently when @@ -445,46 +437,41 @@ TIMEOUT is deprecated." (if (= (length (ready-sources sync-queue)) (length (sources prompter))) t - (progn - (calispel:fair-alt - ((calispel:? (ready-channel sync-queue) next-source) - (cond - ((null next-source) - nil) - (t - (push next-source (ready-sources sync-queue)) - ;; Update selection when update is done: - (select-first prompter) - next-source))) - ((calispel:? (sync-interrupt-channel sync-queue)) - nil)))) + (calispel:fair-alt + ((calispel:? (ready-channel sync-queue) next-source) + (cond + ((null next-source) + nil) + (t + (push next-source (ready-sources sync-queue)) + ;; Update selection when update is done: + (select-first prompter) + next-source))) + ((calispel:? (sync-interrupt-channel sync-queue)) + nil))) ;; No sync-queue if no input was ever set. t))) (export-always 'all-ready-p) -(defun all-ready-p (prompter &optional timeout) - "Return non-nil when all prompter sources are ready. -After timeout has elapsed for one source, return nil." - (sera:nlet check ((next-source (next-ready-p prompter timeout))) - (cond - ((eq t next-source) - t) - ((null next-source) - nil) - (t - (check (next-ready-p prompter timeout)))))) +(defun all-ready-p (prompter) + "Return non-nil when all PROMPTER sources are ready." + (sera:nlet check ((next-source (next-ready-p prompter))) + (if (typep next-source 'boolean) + next-source + (check (next-ready-p prompter))))) (export-always 'make) (define-function make (append '(&rest args) `(&key sources ,@(public-initargs 'prompter))) - "Return prompter object. + "Return `prompter' object. The arguments are the initargs of the `prompter' class. As a special case, the `:sources' keyword argument not only accepts `source' -objects but also symbols. Example: +objects but also symbols. - (prompter:make :sources 'prompter:raw-source)" +Example: +(prompter:make :sources 'prompter:raw-source)" (apply #'make-instance 'prompter args)) (export-always 'selected-source) @@ -493,25 +480,28 @@ objects but also symbols. Example: (export-always 'selected-suggestion) (defun selected-suggestion (prompter) - "Return selected prompt-buffer suggestion. + "Return selected PROMPTER `suggestion'. Return source as second value." - (let* ((source (first (selection prompter)))) - (values (nth (second (selection prompter)) (suggestions source)) source))) + (let ((source (first (selection prompter)))) + (values (nth (second (selection prompter)) (suggestions source)) + source))) (export-always 'selected-suggestion-position) (defun selected-suggestion-position (prompter) - "Return selected prompt-buffer suggestion position among current source + "Return selected PROMPTER `suggestion' position among current `source' suggestions." (second (selection prompter))) (export-always 'all-marks) (defun all-marks (prompter) - "Return the list of the marked suggestions in the prompter." + "Return the list of `prompter''s `marks'. +Note that `marks' is a slot of `source', and `prompter' may have multiple +sources." (alex:mappend #'marks (sources prompter))) (export-always 'all-suggestions) (defun all-suggestions (prompter) - "Return the list of the suggestions in the prompter." + "Return the list of PROMPTER's `suggestion's." (alex:mappend #'suggestions (sources prompter))) (export-always 'default-return-action) @@ -520,8 +510,7 @@ suggestions." (export-always 'resume) (defun resume (prompter) - "Calls each source `resumer' function over the source. -This is meant to be called when a prompter is resumed." - (mapc (lambda (source) - (maybe-funcall (resumer source) source)) + "Call each source `resumer' function over the source. +Meant to be called when PROMPTER is resumed." + (mapc (lambda (source) (maybe-funcall (resumer source) source)) (sources prompter))) diff --git a/source/auto-rules.lisp b/source/auto-rules.lisp index e49be9d21e9..e23cca92a2b 100644 --- a/source/auto-rules.lisp +++ b/source/auto-rules.lisp @@ -290,7 +290,7 @@ For the storage format see the comment in the header of your `auto-rules-file'." (make-instance 'prompter:raw-source :name "New URL") (make-instance 'global-history-source - :return-actions '()))))) + :return-actions #'identity)))) (when (typep url 'nyxt::history-entry) (setf url (url url))) (add-modes-to-auto-rules @@ -322,7 +322,7 @@ For the storage format see the comment in the header of your `auto-rules-file'." (make-instance 'prompter:raw-source :name "New URL") (make-instance 'global-history-source - :return-actions '()))))) + :return-actions #'identity))))) (setf url (url url)) (add-modes-to-auto-rules (url-infer-match url) :include (rememberable-of (modes (current-buffer))) diff --git a/source/browser.lisp b/source/browser.lisp index 883229b01ff..8f8fcf6927e 100644 --- a/source/browser.lisp +++ b/source/browser.lisp @@ -573,7 +573,7 @@ set of useful URLs or preparing a list to send to a someone else." :sources (make-instance 'buffer-source :constructor (remove-if #'internal-url-p (buffer-list) :key #'url) - :return-actions '(identity) + :return-actions #'identity :multi-selection-p t)))) (unwind-protect (spinneret:with-html-string diff --git a/source/buffer.lisp b/source/buffer.lisp index 03e9e49753f..da5d6f5a75f 100644 --- a/source/buffer.lisp +++ b/source/buffer.lisp @@ -1230,7 +1230,8 @@ proceeding." 'reload-buffers)) (prompter:selection-actions-enabled-p t) (prompter:selection-actions-delay 0.1) - (prompter:selection-actions (lambda (buffer) + (prompter:selection-actions (lambda-command set-current-buffer* (buffer) + "Set current BUFFER for the active window." (set-current-buffer buffer :focus nil))) (prompter:destructor (let ((buffer (current-buffer))) (lambda (prompter source) @@ -1389,7 +1390,7 @@ URL-DESIGNATOR is then transformed by BUFFER's `buffer-load-hook'." (history-initial-suggestions))) (prompter:multi-selection-p t) (prompter:filter-preprocessor nil) ; Don't remove non-exact results. - (prompter:return-actions '(buffer-load))) + (prompter:return-actions #'buffer-load)) (:export-class-name-p t) (:metaclass user-class)) @@ -1545,7 +1546,7 @@ Finally, if nothing else, set the `engine' to the `default-search-engine'.")) (input->queries input :check-dns-p t :engine-completion-p t))) - (prompter:return-actions '(buffer-load))) + (prompter:return-actions #'buffer-load)) (:export-class-name-p t) (:documentation "This prompter source tries to \"do the right thing\" to generate a new URL query from user input. @@ -1619,11 +1620,11 @@ any.") (define-command set-url-new-buffer (&key (prefill-current-url-p t)) "Prompt for a URL and set it in a new focused buffer." (let ((history (set-url-history *browser*)) - (return-actions (list (lambda-command new-buffer-load (suggestion-values) - "Load URL(s) in new buffer(s)" - (mapc (lambda (suggestion) (make-buffer :url (url suggestion))) - (rest suggestion-values)) - (make-buffer-focus :url (url (first suggestion-values))))))) + (return-actions (lambda-command new-buffer-load (suggestion-values) + "Load URL(s) in new buffer(s)" + (mapc (lambda (suggestion) (make-buffer :url (url suggestion))) + (rest suggestion-values)) + (make-buffer-focus :url (url (first suggestion-values)))))) (pushnew-url-history history (url (current-buffer))) (prompt :prompt "Open URL in new buffer" @@ -1636,12 +1637,12 @@ any.") (define-command set-url-new-nosave-buffer (&key (prefill-current-url-p t)) "Prompt for a URL and set it in a new focused nosave buffer." (let ((return-actions - (list (lambda-command new-nosave-buffer-load (suggestion-values) - "Load URL(s) in new nosave buffer(s)" - (mapc (lambda (suggestion) (make-nosave-buffer :url (url suggestion))) - (rest suggestion-values)) - (make-buffer-focus :url (url (first suggestion-values)) - :nosave-buffer-p t))))) + (lambda-command new-nosave-buffer-load (suggestion-values) + "Load URL(s) in new nosave buffer(s)" + (mapc (lambda (suggestion) (make-nosave-buffer :url (url suggestion))) + (rest suggestion-values)) + (make-buffer-focus :url (url (first suggestion-values)) + :nosave-buffer-p t)))) (prompt :prompt "Open URL in new nosave buffer" :input (if prefill-current-url-p diff --git a/source/command-commands.lisp b/source/command-commands.lisp index e4b7fcb9d7e..e204f739dc2 100644 --- a/source/command-commands.lisp +++ b/source/command-commands.lisp @@ -140,28 +140,27 @@ together with the arglists and documentations of the functions typed in." :sources (list (make-instance 'command-source :return-actions - (list (lambda-command run-command* (commands) - "Run the chosen command." - (let ((command (first commands))) - (setf (last-access command) (time:now)) - (run-async command))))) + (lambda-command run-command* (commands) + "Run the chosen command." + (let ((command (first commands))) + (setf (last-access command) (local-time:now)) + (run-async command)))) (make-instance 'extended-command-source :return-actions - (list - (lambda-command evaluate-lisp-expression* (exprs) - "Evaluate the inputted Lisp expression." - (run-thread "evaluator" - (let ((*interactive-p* t)) - (echo "~s" (eval (first exprs)))))))) + (lambda-command evaluate-lisp-expression* (exprs) + "Evaluate the inputted Lisp expression." + (run-thread "evaluator" + (let ((*interactive-p* t)) + (echo "~s" (eval (first exprs))))))) (make-instance 'predicted-command-source :return-actions - (list (lambda-command run-command* (commands) - "Run the chosen command." - (let ((command (first commands))) - (setf (last-access command) (time:now)) - (run-async command)))))) + (lambda-command run-command* (commands) + "Run the chosen command." + (let ((command (first commands))) + (setf (last-access command) (time:now)) + (run-async command))))) :hide-suggestion-count-p t))) (defun parse-function-lambda-list-types (fn) diff --git a/source/configuration-commands.lisp b/source/configuration-commands.lisp index 9ec24fe18d3..b316cd35225 100644 --- a/source/configuration-commands.lisp +++ b/source/configuration-commands.lisp @@ -71,9 +71,9 @@ On error, return the condition as a first value and the backtrace as second valu :sources (make-instance 'nyxt/file-manager-mode:file-source :extensions '("lisp") - :return-actions (list (lambda-command load-file* (files) - (dolist (file files) - (load-lisp file))))))) + :return-actions (lambda-command load-file* (files) + (dolist (file files) + (load-lisp file)))))) (define-command clean-configuration () "Clean all the user configuration created with `define-configuration' or `customize-instance'." diff --git a/source/describe.lisp b/source/describe.lisp index a94371d6118..cbdbf8bc3fa 100644 --- a/source/describe.lisp +++ b/source/describe.lisp @@ -172,71 +172,71 @@ Otherwise prompt for matches." (sources (list (make-instance 'variable-source - :return-actions (list (lambda-command describe-variable* (variables) - (describe-variable :variable (first variables)))) + :return-actions (lambda-command describe-variable* (variables) + (describe-variable :variable (first variables))) :filter-preprocessor preprocessor) (make-instance 'variable-non-nyxt-source - :return-actions (list (lambda-command describe-variable* (variables) - (describe-variable :variable (first variables)))) + :return-actions (lambda-command describe-variable* (variables) + (describe-variable :variable (first variables))) :filter-preprocessor preprocessor) (make-instance 'variable-internal-source - :return-actions (list (lambda-command describe-variable* (variables) - (describe-variable :variable (first variables)))) + :return-actions (lambda-command describe-variable* (variables) + (describe-variable :variable (first variables))) :filter-preprocessor preprocessor) (make-instance 'function-source - :return-actions (list (lambda-command describe-function* (functions) - (describe-function :fn (first functions)))) + :return-actions (lambda-command describe-function* (functions) + (describe-function :fn (first functions))) :filter-preprocessor preprocessor) (make-instance 'function-non-nyxt-source - :return-actions (list (lambda-command describe-function* (functions) - (describe-function :fn (first functions)))) + :return-actions (lambda-command describe-function* (functions) + (describe-function :fn (first functions))) :filter-preprocessor preprocessor) (make-instance 'function-internal-source - :return-actions (list (lambda-command describe-function* (functions) - (describe-function :fn (first functions)))) + :return-actions (lambda-command describe-function* (functions) + (describe-function :fn (first functions))) :filter-preprocessor preprocessor) (make-instance 'command-source - :return-actions (list (lambda-command describe-command* (commands) - (describe-command :command (name (first commands))))) + :return-actions (lambda-command describe-command* (commands) + (describe-command :command (name (first commands)))) :filter-preprocessor preprocessor) (make-instance 'class-source - :return-actions (list (lambda-command describe-class* (classes) - (describe-class :class (first classes)))) + :return-actions (lambda-command describe-class* (classes) + (describe-class :class (first classes))) :filter-preprocessor preprocessor) (make-instance 'class-non-nyxt-source - :return-actions (list (lambda-command describe-class* (classes) - (describe-class :class (first classes)))) + :return-actions (lambda-command describe-class* (classes) + (describe-class :class (first classes))) :filter-preprocessor preprocessor) (make-instance 'class-internal-source - :return-actions (list (lambda-command describe-class* (classes) - (describe-class :class (first classes)))) + :return-actions (lambda-command describe-class* (classes) + (describe-class :class (first classes))) :filter-preprocessor preprocessor) (make-instance 'slot-source - :return-actions (list (lambda-command describe-slot** (slots) - (describe-slot :class (class-sym (first slots)) - :name (name (first slots))))) + :return-actions (lambda-command describe-slot** (slots) + (describe-slot :class (class-sym (first slots)) + :name (name (first slots)))) :filter-preprocessor preprocessor) (make-instance 'slot-non-nyxt-source - :return-actions (list (lambda-command describe-slot** (slots) - (describe-slot :class (class-sym (first slots)) - :name (name (first slots))))) + :return-actions (lambda-command describe-slot** (slots) + (describe-slot :class (class-sym (first slots)) + :name (name (first slots)))) :filter-preprocessor preprocessor) (make-instance 'slot-internal-source - :return-actions (list (lambda-command describe-slot** (slots) - (describe-slot :class (class-sym (first slots)) - :name (name (first slots))))) + :return-actions (lambda-command describe-slot** (slots) + (describe-slot :class (class-sym (first slots)) + :name (name (first slots)))) :filter-preprocessor preprocessor)))) (let ((suggestion+action-pairs (and input diff --git a/source/history.lisp b/source/history.lisp index d5b10a1d2ab..d326549865d 100644 --- a/source/history.lisp +++ b/source/history.lisp @@ -122,7 +122,7 @@ then become available for deletion with `delete-history-entry'." (let ((buffers (or (alex:ensure-list buffer) (prompt :prompt "Reset histories of buffer(s)" :sources (make-instance 'buffer-source - :return-actions '()))))) + :return-actions #'identity))))) (files:with-file-content (history (history-file (current-buffer))) (dolist (buffer buffers) (htree:reset-owner history (id buffer)))))) diff --git a/source/lisp-system.lisp b/source/lisp-system.lisp index 193befec3cf..31fb06045a3 100644 --- a/source/lisp-system.lisp +++ b/source/lisp-system.lisp @@ -27,8 +27,8 @@ (define-class quicklisp-source (prompter:source) ((prompter:name "Quicklisp systems") (prompter:constructor (mapcar #'ql-dist:short-description (ql:system-list))) - (prompter:return-actions (list (lambda-command quickload* (systems) - (ql:quickload (first systems))))))) + (prompter:return-actions (lambda-command quickload* (systems) + (ql:quickload (first systems)))))) (define-command load-system () "Load a system from Quicklisp." diff --git a/source/mode/annotate.lisp b/source/mode/annotate.lisp index f53d0bccd86..0064ff20c6b 100644 --- a/source/mode/annotate.lisp +++ b/source/mode/annotate.lisp @@ -180,7 +180,7 @@ make-instance." (prompt :prompt "Show annotation(s)" :sources (make-instance 'annotation-source - :return-actions nil)))) + :return-actions #'identity)))) (render-annotations selected-annotations))) (define-internal-page-command-global show-annotations () diff --git a/source/mode/autofill.lisp b/source/mode/autofill.lisp index c9854ee1cde..0393827a62b 100644 --- a/source/mode/autofill.lisp +++ b/source/mode/autofill.lisp @@ -63,8 +63,9 @@ it will be in conflict with common-lisp:fill.")) ((prompter:name "Autofills") (prompter:constructor (autofills (find-submode 'autofill-mode))) (prompter:return-actions - (list (lambda-command autofill* (autofills) - (ffi-buffer-paste (current-buffer) (funcall (first autofills))))))) + (lambda-command autofill* (autofills) + (ffi-buffer-paste (current-buffer) + (funcall (first autofills)))))) (:export-class-name-p t) (:metaclass user-class)) diff --git a/source/mode/bookmark.lisp b/source/mode/bookmark.lisp index 87ed4774727..aec5114c038 100644 --- a/source/mode/bookmark.lisp +++ b/source/mode/bookmark.lisp @@ -250,7 +250,7 @@ In particular, we ignore the protocol (e.g. HTTP or HTTPS does not matter)." :prompt "Bookmark URL from buffer(s)" :sources (make-instance 'buffer-source :multi-selection-p t - :return-actions (list (lambda-mapped-command bookmark-current-url))))) + :return-actions (lambda-mapped-command bookmark-current-url)))) (define-command bookmark-url (&key (url (ignore-errors (quri:uri (prompt1 diff --git a/source/mode/diff.lisp b/source/mode/diff.lisp index 0e72d0f9e22..77a07de8f68 100644 --- a/source/mode/diff.lisp +++ b/source/mode/diff.lisp @@ -30,10 +30,10 @@ the highest standard on accessibility.")) (define-internal-page-command-global diff (&key (old-buffer-id (id (prompt1 :prompt "Old buffer" :sources (make-instance 'buffer-source - :return-actions nil)))) + :return-actions #'identity)))) (new-buffer-id (id (prompt1 :prompt "New buffer" :sources (make-instance 'buffer-source - :return-actions nil))))) + :return-actions #'identity))))) (diff-buffer "*diff*" 'diff-mode) "Show difference between two buffers" (let ((old-html (ffi-buffer-get-document (nyxt::buffers-get old-buffer-id))) diff --git a/source/mode/document.lisp b/source/mode/document.lisp index 1215b23200f..53f6fe264c9 100644 --- a/source/mode/document.lisp +++ b/source/mode/document.lisp @@ -183,9 +183,8 @@ It does not assume being online.")) (prompter:constructor (lambda (source) (containers:container->list (ring source)))) - (prompter:return-actions - (list (lambda-command paste* (ring-items) - (ffi-buffer-paste (current-buffer) (first ring-items)))))) + (prompter:return-actions (lambda-command paste* (ring-items) + (ffi-buffer-paste (current-buffer) (first ring-items))))) (:export-class-name-p t) (:metaclass user-class)) @@ -471,10 +470,12 @@ ID is a buffer `id'." ((prompter:name "Headings") (buffer :accessor buffer :initarg :buffer) (prompter:selection-actions-enabled-p t) - (prompter:selection-actions 'scroll-page-to-heading) + (prompter:selection-actions (lambda-command scroll-page-to-heading* (heading) + "Scroll to heading." + (scroll-page-to-heading heading))) (prompter:constructor (lambda (source) (get-headings :buffer (buffer source)))) - (prompter:return-actions (list (lambda-unmapped-command scroll-page-to-heading))))) + (prompter:return-actions (lambda-unmapped-command scroll-page-to-heading)))) (defmethod prompter:object-attributes ((heading heading) (source heading-source)) (declare (ignore source)) @@ -503,7 +504,7 @@ of buffers." :prompt "Select headings from buffers" :sources (make-instance 'buffer-source :multi-selection-p t - :return-actions nil)))) + :return-actions #'identity)))) (prompt :prompt "Jump to heading" :sources (loop for buffer in buffers @@ -720,10 +721,9 @@ of buffers." 'frame-source :buffer buffer :multi-selection-p t - :return-actions (list (lambda-command open-new-buffers (urls) - (mapcar (lambda (i) - (make-buffer :url (quri:uri i))) - urls)))) + :return-actions (lambda-command open-new-buffers (urls) + (mapcar (lambda (i) (make-buffer :url (quri:uri i))) + urls))) :after-destructor (lambda () (with-current-buffer buffer (frame-element-clear))))) diff --git a/source/mode/editor.lisp b/source/mode/editor.lisp index 9883ce9eafd..2a930267c53 100644 --- a/source/mode/editor.lisp +++ b/source/mode/editor.lisp @@ -125,7 +125,7 @@ contains an `nyxt/editor-mode:editor-mode' instance (or a subclass thereof).")) :sources (list (make-instance 'nyxt/file-manager-mode:file-source :name "Existing file" - :return-actions '(identity)) + :return-actions #'identity) (make-instance 'prompter:raw-source :name "Create new file")))))) diff --git a/source/mode/hint.lisp b/source/mode/hint.lisp index d96491d414e..2e04772c741 100644 --- a/source/mode/hint.lisp +++ b/source/mode/hint.lisp @@ -243,7 +243,8 @@ For instance, to include images: (append matching-hints other-hints)))) (prompter:selection-actions (unless (fit-to-prompt-p (find-submode 'hint-mode)) - (lambda (suggestion) + (lambda-command highlight-selected-hint* (suggestion) + "Highlight hint." (highlight-selected-hint :element suggestion :scroll nil)))) (prompter:marks-actions diff --git a/source/mode/no-procrastinate.lisp b/source/mode/no-procrastinate.lisp index a4cedb284e8..21aa36ad9cb 100644 --- a/source/mode/no-procrastinate.lisp +++ b/source/mode/no-procrastinate.lisp @@ -202,7 +202,7 @@ page(s) in the active buffer." :prompt "Avoid procrastination on HOSTS from buffer(s)" :sources (make-instance 'buffer-source :multi-selection-p t - :return-actions (list (lambda-mapped-command no-procrastinate-current-host))))) + :return-actions (lambda-mapped-command no-procrastinate-current-host)))) diff --git a/source/mode/prompt-buffer.lisp b/source/mode/prompt-buffer.lisp index bdf9edf7018..16a02e1506c 100644 --- a/source/mode/prompt-buffer.lisp +++ b/source/mode/prompt-buffer.lisp @@ -16,9 +16,9 @@ The prompt buffer can have multiple `prompter:source's of suggestions. Each source has its own properties, such as the ability to mark multiple suggestions. The same source can be used by different prompt buffers. -Each source offers a set of `return-actions' for its selection(s). These can be -listed and run with `return-selection-over-action' (bound to \"M-return\" by -default)." +Each source offers a set of `return-actions' for marked items. These can be +listed and chosen from with the command `return-marks-action' (bound to +\"M-return\" by default)." ((visible-in-status-p nil) (keyscheme-map (define-keyscheme-map "prompt-buffer-mode" () @@ -248,7 +248,7 @@ If N is negative, go to next pages instead." ((prompter:name "List of prompter attributes") (prompter:multi-selection-p t) (prompter:suggestion-maker 'make-attribute-suggestion) - (prompter:return-actions '(return-marks-only)))) + (prompter:return-actions #'return-marks-only))) (defun return-marks-only (suggestion-values) "Return marked suggestions only. @@ -362,8 +362,10 @@ current unmarked selection." (prompter:destroy prompt-buffer)) (define-command-prompt toggle-selection-actions-enabled (prompt-buffer) - "Close the PROMPT-BUFFER without further action." - (prompter:toggle-selection-actions-enabled prompt-buffer)) + "Toggle whether `prompter:selection-actions' are enabled for PROMPT-BUFFER." + (prompter:toggle-selection-actions-enabled prompt-buffer) + (echo "Selection actions: ~:[dis~;en~]abled." + (prompter:selection-actions-enabled-p (current-source prompt-buffer)))) (define-command-prompt toggle-mark (prompt-buffer &key (direction :forward)) "Mark selection. diff --git a/source/mode/record-input-field.lisp b/source/mode/record-input-field.lisp index b6a5c4dc363..d058f92975b 100644 --- a/source/mode/record-input-field.lisp +++ b/source/mode/record-input-field.lisp @@ -134,9 +134,9 @@ See also `set-input-data-from-saved-domain'." :return-actions return-actions))) (define-command set-input-data-from-saved-domain - (&key (return-actions (list (lambda-command buffer-load* (suggestion-values) - "Load selected input-entry in current buffer's input fields." - (ps-write-input-data (input-data (first suggestion-values))))))) + (&key (return-actions (lambda-command buffer-load* (suggestion-values) + "Load selected input-entry in current buffer's input fields." + (ps-write-input-data (input-data (first suggestion-values)))))) "Set the input data from a list of saved data filtered by current domain into the current buffer. diff --git a/source/mode/remembrance.lisp b/source/mode/remembrance.lisp index bd5263a042c..6ee94dd11f7 100644 --- a/source/mode/remembrance.lisp +++ b/source/mode/remembrance.lisp @@ -373,7 +373,7 @@ See also `auto-cache-on-load-p' in `remembrance-mode'." (let ((buffer (or buffer (prompt1 :prompt "View changes for buffer" :sources (make-instance 'buffer-source - :return-actions '())))) + :return-actions #'identity)))) (mode (find-submode 'remembrance-mode))) (alex:when-let ((doc (find-url (url buffer) mode))) ;; TODO: Display in internal page. @@ -388,7 +388,7 @@ BUFFER can be a list of buffers." (let ((buffers (or (alex:ensure-list buffer) (prompt :prompt "Cache content of buffers" :sources (make-instance 'buffer-source - :return-actions '()))))) + :return-actions #'identity))))) (dolist (buffer buffers) (buffer->cache buffer (find-submode 'remembrance-mode))))) diff --git a/source/mode/repl.lisp b/source/mode/repl.lisp index c54c4185255..0f323d9c5af 100644 --- a/source/mode/repl.lisp +++ b/source/mode/repl.lisp @@ -539,7 +539,7 @@ Follows what the compiler finds aesthetically pleasing." (let ((functions (prompt :prompt "Function to edit" :sources (make-instance 'nyxt::function-source - :return-actions '(identity))))) + :return-actions #'identity)))) (setf (evaluations repl-mode) (append (evaluations repl-mode) diff --git a/source/mode/search-buffer.lisp b/source/mode/search-buffer.lisp index 42a27811e5f..32eb9918c9d 100644 --- a/source/mode/search-buffer.lisp +++ b/source/mode/search-buffer.lisp @@ -219,7 +219,8 @@ (progn (remove-search-hints) '())))) - (prompter:selection-actions (lambda (suggestion) + (prompter:selection-actions (lambda-command highlight-match (suggestion) + "Scroll to search match." ;; TODO: rewrite prompt-buffer-selection-highlight-hint (set-current-buffer (buffer suggestion) :focus nil) (prompt-buffer-selection-highlight-hint :scroll t))) @@ -252,16 +253,16 @@ Example: :sources (make-instance 'search-buffer-source :case-sensitive-p case-sensitive-p :return-actions - (list (lambda (search-match) - (unless (keep-search-hints-p (current-buffer)) - (remove-search-hints)) - search-match))))) + (lambda (search-match) + (unless (keep-search-hints-p (current-buffer)) + (remove-search-hints)) + search-match)))) (define-command search-buffers (&key case-sensitive-p) "Search multiple buffers." (let ((buffers (prompt :prompt "Search buffer(s)" :sources (make-instance 'buffer-source ; TODO: Define class? - :return-actions '() + :return-actions #'identity :multi-selection-p t)))) (prompt :prompt "Search text" diff --git a/source/window.lisp b/source/window.lisp index b118950632c..c2ef527ade7 100644 --- a/source/window.lisp +++ b/source/window.lisp @@ -181,7 +181,7 @@ not try to quit the browser." ((prompter:name "Windows") (prompter:multi-selection-p t) (prompter:constructor (window-list)) - (prompter:return-actions (list (lambda-mapped-command window-delete))))) + (prompter:return-actions (lambda-mapped-command window-delete)))) (defmethod prompter:object-attributes ((window window) (source window-source)) (declare (ignore source))