[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC] The best way to choose an "action" at point: context-menu-mode, tr
From: |
Ihor Radchenko |
Subject: |
[RFC] The best way to choose an "action" at point: context-menu-mode, transient, which-key or embark? (was: Fwd: Org-cite: Replace basic follow-processor with transient menu?) |
Date: |
Fri, 13 Dec 2024 18:41:14 +0000 |
TL;DR: We are in the process of designing a more unified selection
interface for Org mode and want to see if there is some way to unify
context-menu-mode, transient, which-key and embark together. The idea is
to (1) avoid too many customizations; (2) allow users to decide how to
choose between multiple options - by mouse, keyboard, and using
customizable UIs.
Dear all,
I have raised the topic of refactoring Org mode menu systems during
EmacsConf in https://emacsconf.org/2024/talks/org-update/
The initial idea was replacing the self-written menu code in Org with
built-in transient.el.
Later, during OrgMeetup a number of people raised concerns that
transient may sometimes be an overkill, and that some people may prefer
alternative UIs. In particular, embark and context-menu-mode were
mentioned.
(I am CCing the discussion participants and potentially interested
maintainers)
In Org mode (although not only in Org mode, looking at the success of
embark.el), we often have a UI model where users call an "action"
command (like org-ctrl-c-ctrl-c or org-open-at-point) followed by
interactive selection among multiple actions.
For example, org-open-at-point on a heading with multiple links inside
will raise a section buffer listing all the links - pressing a number
will open the corresponding link.
Another example (see the example patch below), which is a
work-in-progress patch for Org citation system, is "following" a
citation. To "follow" citation may mean multiple things including but
not limited to: (1) going to citation record in the bibliography file;
(2) following URL; (3) downloading .pdf file for a citation; etc.
**The list of "follow" actions may be customized by users**
The general UI flow in these scenarios will be:
1. User calls "action" with cursor at certain syntax element
2. Action menu is displayed, showing the available actions/targets (dynamically
built)
3. User selects the action/target
This UI flow can be implemented using context menus, which-key popups,
transient menus, and also using embark (where the way menu is displayed
can be customized).
All the 4 approaches represent different UI models with various
strengths and weaknesses:
- transient has a very flexible layout builder where the menu items can
be arranged granularly, but intercepts the main loop disrupting
certain keyboard-based workflows
- which-key does not stand on the way and integrates well into Emacs'
key binding model, but provides little flexibility for menu layout
- embark stays in the middle between which-key and transient, making use
of transient keymaps and allowing a custom menu renderer
- context-menu-mode provides mouse experience
I am wondering if we can work out some universal API to plug the
described action->menu->selection model into the UI that user prefers.
Tentatively, I am thinking about the following:
For a given Emacs "prefix" command (e.g. org-open-at-point), we define a
set of customizations:
1. List of possible actions: ((name1 . action1 props) (name2 . action2 ...) ...)
PROPS is a plist defining extra properties like key-binding, display
string, maybe something else to be used in the future.
2. Menu interface to use (transient, context-menu, embark, which-key)
3. Layout settings for the specific interfaces. For example, transient
layout definition.
WDYT?
Best,
Ihor
Tor-björn Claesson <tclaesson@gmail.com> writes:
> From 7e9e0c64fbda2dcb67d8c8421d1c9923ca93e8b4 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?Tor-bj=C3=B6rn=20Claesson?= <torb@barbar.claesson.fi>
> Date: Tue, 12 Nov 2024 11:09:16 +0200
> Subject: [PATCH] lisp/oc-basic.el: Transient menu for following citations
>
> * lisp/oc-basic.el (require 'transient): Pull in transient.
> (require 'org-element): Pull in org-element.
> (org-cite-basic-follow-ask): New customization option. should
> `org-cite-basic-follow' prompt the user for an action?
> (org-cite-basic-follow-actions): New customization option, that
> specifies the contents of the transient menu.
> (org-cite-basic--get-key): New function. Get citation key from
> citation or citation reference.
> (org-cite-basic-follow): New function. Displays a menu asking how to
> follow a citation if `org-cite-basic-follow-ask' is
> non-nil. Otherwise, it retains the default behaviour of opening the
> bibliography entry. This can be inversed with a negative prefix argument.
> (org-cite-basic-follow--parse-suffix-specification and
> org-cite-basic-follow--setup): Helper functions for
> `org-cite-basic-follow'.
> (org-cite-register-processor 'basic): Update the basic citation
> processor to follow citations using `org-cite-basic-follow'.
>
> * etc/ORG_NEWS (Menu for choosing how to follow citations): Describe
> the new feature
> (New option ~org-cite-basic-follow-ask~): Describe this new
> customization option.
> (New option ~org-cite-basic-follow-actions~): Describe this new
> customization option, which specifies the layout of the
> `org-cite-basic-follow' transient menu.
>
> This change was co-authored with much support from Ihor Radchenko and
> Jonas Bernoulli, thanks!
> ---
> etc/ORG-NEWS | 22 +++++++++
> lisp/oc-basic.el | 115 +++++++++++++++++++++++++++++++++++++++++++----
> 2 files changed, 128 insertions(+), 9 deletions(-)
>
> diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
> index de4f11b25..bacc38be2 100644
> --- a/etc/ORG-NEWS
> +++ b/etc/ORG-NEWS
> @@ -114,6 +114,15 @@ The keybindings in the repeat-maps can be changed by
> customizing
>
> See the new [[info:org#Repeating commands]["Repeating commands"]] section in
> Org mode manual.
>
> +*** New transient menu when following citations
> +
> +Following citations with the org-cite-basic citation backend can now present
> a
> +transient menu. To show this menu, set ~org-cite-basic-follow-ask~ to
> non-nil.
> +This behaviour can be reversed with a -4 prefix.
> +
> +The contents of this menu can be customized in
> +~org-cite-basic-follow-actions~.
> +
> ** New and changed options
>
> # Chanes deadling with changing default values of customizations,
> @@ -158,6 +167,19 @@ English. The default value is ~t~ as the CSL standard
> assumes that
> English titles are specified in sentence-case but the bibtex
> bibliography format requires them to be written in title-case.
>
> +*** New option ~org-cite-basic-follow-ask~
> +
> +When this option is non-nil, following a citation with the basic citation
> +backend will present a transient menu with choices for how to follow the
> +citation.
> +If nil, following a citation will open its bibliography entry.
> +
> +This behaviour can be reversed with a -4 prefix argument.
> +
> +*** New option ~org-cite-basic-follow-actions~
> +
> +This option specifies the options presented by ~org-cite-basic-follow~.
> +
> ** New functions and changes in function arguments
>
> # This also includes changes in function behavior from Elisp perspective.
> diff --git a/lisp/oc-basic.el b/lisp/oc-basic.el
> index e207a1997..fc55d3a32 100644
> --- a/lisp/oc-basic.el
> +++ b/lisp/oc-basic.el
> @@ -74,6 +74,8 @@
> (require 'map)
> (require 'oc)
> (require 'seq)
> +(require 'transient)
> +(require 'org-element)
>
> (declare-function org-open-at-point "org" (&optional arg))
> (declare-function org-open-file "org" (path &optional in-emacs line search))
> @@ -140,6 +142,39 @@
> :type 'face
> :safe #'facep)
>
> +(defcustom org-cite-basic-follow-ask nil
> + "Should `org-cite-basic' ask how to follow citations?
> +
> +When this option is nil, `org-cite-basic-follow' opens the bibliography
> entry.
> +Otherwise, `org-cite-basic-follow' will display a transient menu prompting
> the
> +user for an action. The contents of this menu can be customized in
> +`org-cite-basic-follow-actions'."
> + :group 'org-cite
> + :package-version '(Org . "9.8")
> + :type 'boolean)
> +
> +(defcustom org-cite-basic-follow-actions
> + '[["Open"
> + ("b" "bibliography entry" (org-cite-basic-goto !citation !prefix))]
> + ["Copy"
> + ("d" "DOI"
> + (kill-new
> + (org-cite-basic--get-field
> + 'doi
> + (org-cite-basic--get-key !citation))))]]
> + "Actions in the `org-cite-basic-follow' transient menu.
> +
> +This option uses the same syntax as `transient-define-prefix', see Info node
> +`(transient)Binding Suffix and Infix Commands'. In addition, it is possible
> +to specify a function call for the COMMAND part, where !citation (the
> citation
> +object to be followed) and !prefix (any prefix argument to the follower) can
> be
> +used to access those values. For example:
> +(org-cite-basic-goto !citation !prefix) or
> +(lambda () (message (org-element-property :key !citation)))"
> + :group 'org-cite
> + :package-version '(Org . "9.8")
> + :type 'sexp)
> +
>
> ;;; Internal variables
> (defvar org-cite-basic--bibliography-cache nil
> @@ -326,6 +361,16 @@ INFO is the export state, as a property list."
> (map-keys entries))
> (org-cite-basic--parse-bibliography)))
>
> +(defun org-cite-basic--get-key (citation-or-citation-reference)
> + "Return citation key for CITATION."
> + (if (org-element-type-p citation-or-citation-reference 'citation-reference)
> + (org-element-property :key citation-or-citation-reference)
> + (pcase (org-cite-get-references citation-or-citation-reference t)
> + (`(,key) key)
> + (keys
> + (or (completing-read "Select citation key: " keys nil t)
> + (user-error "Aborted"))))))
> +
> (defun org-cite-basic--get-entry (key &optional info)
> "Return BibTeX entry for KEY, as an association list.
> When non-nil, INFO is the export state, as a property list."
> @@ -805,14 +850,7 @@ export state, as a property list."
> When DATUM is a citation reference, open bibliography entry referencing
> the citation key. Otherwise, select which key to follow among all keys
> present in the citation."
> - (let* ((key
> - (if (org-element-type-p datum 'citation-reference)
> - (org-element-property :key datum)
> - (pcase (org-cite-get-references datum t)
> - (`(,key) key)
> - (keys
> - (or (completing-read "Select citation key: " keys nil t)
> - (user-error "Aborted"))))))
> + (let* ((key (org-cite-basic--get-key datum))
> (file
> (pcase (seq-find (pcase-lambda (`(,_ . ,entries))
> (gethash key entries))
> @@ -832,6 +870,65 @@ present in the citation."
> (bibtex-set-dialect)
> (bibtex-search-entry key)))))
>
> +(transient-define-prefix org-cite-basic-follow (citation-object &optional
> prefix)
> + "Follow citation.
> +
> +If `org-cite-basic-follow-ask' is non-nil, this transient will present
> +a menu prompting the user for an action.
> +Otherwise, it will open the bibliography entry for the citation at point.
> +This behaviour is inverted when the transient is called with a -4 prefix
> +argument.
> +
> +The contents of the menu are defined in the variable
> +`org-cite-basic-follow-actions'."
> + [:class transient-columns
> + :setup-children org-cite-basic-follow--setup
> + :pad-keys t]
> + (interactive
> + (list (let ((obj (org-element-context)))
> + (pcase (org-element-type obj)
> + ((or 'citation 'citation-reference) obj)
> + (_ (user-error "No citation at point"))))))
> + (if (xor org-cite-basic-follow-ask
> + (equal prefix '(-4)))
> + (transient-setup 'org-cite-basic-follow nil nil
> + :scope (list :citation citation-object :prefix
> prefix))
> + (org-cite-basic-goto citation-object prefix)))
> +
> +(defun org-cite-basic-follow--parse-suffix-specification (specification)
> + "Handle special syntax for `org-cite-basic-follow-actions'."
> + (pcase specification
> + (`(,key ,desc (lambda ,args . ,fn-args) . ,other)
> + `(,key ,desc
> + (lambda ,args
> + ,(unless (and (listp (car fn-args))
> + (equal (caar fn-args)
> + 'interactive))
> + '(interactive))
> + (let ((!citation (plist-get (transient-scope) :citation))
> + (!prefix (plist-get (transient-scope) :prefix)))
> + ,@fn-args))
> + ,@other))
> + (`(,key ,desc (,fn . ,fn-args) . ,other)
> + `(,key ,desc
> + (lambda ()
> + (interactive)
> + (let ((!citation (plist-get (transient-scope) :citation))
> + (!prefix (plist-get (transient-scope) :prefix)))
> + (,fn ,@fn-args)))
> + ,@other))
> + (other other)))
> +
> +(defun org-cite-basic-follow--setup (_)
> + "Update `org-cite-basic-follow' when `org-cite-basic-follow-actions'
> changes."
> + (transient-parse-suffixes
> + 'org-cite-basic-follow
> + (cl-map 'vector
> + (lambda (group)
> + (cl-map 'vector
> #'org-cite-basic-follow--parse-suffix-specification
> + group))
> + org-cite-basic-follow-actions)))
> +
>
> ;;; "Insert" capability
> (defun org-cite-basic--complete-style (_)
> @@ -920,7 +1017,7 @@ Raise an error when no bibliography is set in the
> buffer."
> :activate #'org-cite-basic-activate
> :export-citation #'org-cite-basic-export-citation
> :export-bibliography #'org-cite-basic-export-bibliography
> - :follow #'org-cite-basic-goto
> + :follow #'org-cite-basic-follow
> :insert (org-cite-make-insert-processor #'org-cite-basic--complete-key
> #'org-cite-basic--complete-style)
> :cite-styles
> --
> 2.46.0
>
--
Ihor Radchenko // yantar92,
Org mode maintainer,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
- [RFC] The best way to choose an "action" at point: context-menu-mode, transient, which-key or embark? (was: Fwd: Org-cite: Replace basic follow-processor with transient menu?),
Ihor Radchenko <=
- Re: [RFC] The best way to choose an "action" at point: context-menu-mode, transient, which-key or embark?, Gabriel Santos, 2024/12/13
- Re: [RFC] The best way to choose an "action" at point: context-menu-mode, transient, which-key or embark? (was: Fwd: Org-cite: Replace basic follow-processor with transient menu?), Panayotis Manganaris, 2024/12/14
- Re: [RFC] The best way to choose an "action" at point: context-menu-mode, transient, which-key or embark?, Suhail Singh, 2024/12/14
- Re: [RFC] The best way to choose an "action" at point: context-menu-mode, transient, which-key or embark?, indieterminacy, 2024/12/14