From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0.migadu.com ([2001:41d0:403:58f0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id UPPYLgaAXGczoQAAqHPOHw:P1 (envelope-from ) for ; Fri, 13 Dec 2024 18:42:14 +0000 Received: from aspmx1.migadu.com ([2001:41d0:403:58f0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0.migadu.com with LMTPS id UPPYLgaAXGczoQAAqHPOHw (envelope-from ) for ; Fri, 13 Dec 2024 19:42:14 +0100 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=posteo.net header.s=2017 header.b=NZ9+0aFC; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=posteo.net ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1734115334; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post:dkim-signature; bh=3AFlf2iTroHTCgexHteEsXYmzIJIGNDucW4nITKaS/A=; b=qxt+sdIi4hDXCxOl2fUdlLcZ5aHvadNdoHSYFw5IQ60SCzW+EWHN1Vd0grr/MvtRI+VAYm QnM0pYgfGXmMinulwsMeoTr+Zd658Gaag2mbkGhNc9yf6kJ++z2TtlkqxYhrxkyPKsdih2 ZE9Z+UiuLjTuWgLYeORySitrILMkjJX0rHZ/BDrXmhY6ueVNFXOyvxa1wGRAe9IoJgRAz5 qLuawO7L3v9idpwXziF3MxM3h/Mlr8FpahoH8CcYRDSuj+SgM4BoIGFLsEPzQ/N/zP9d5v XUM5/9ccuspIKXrfUp0d7z5TOdcfRHszDDvWm6S0jWCaME4pwKgyZOU1oYXoYw== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=posteo.net header.s=2017 header.b=NZ9+0aFC; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=posteo.net ARC-Seal: i=1; s=key1; d=yhetil.org; t=1734115334; a=rsa-sha256; cv=none; b=ClIhTLIB3N+a64T46Z/sU96kcCtnNLKBVRDvKbCO6E6viQ3G2IbraI5fd9CUFTYDLdcRJS OR9SS1BBYW5vuZEnbrTR20LmgC+6eDN2gzkTZhImi4VRW7tA8NjkYcN6rB7eiQDKIX98eF gYecbYUBxAiqnpLA56f8zQBIaYkRzFIuysjzASfOvl5681/Q6y1vEyxhz7qmDC6lBHpQ8S Al+LeRAVHc2IcfRmmkizyBfgzOl1BBpIvVROsQaXPoa/Bj9yJL1Kc127uHUcVlIk6nDL/E qW0QzOwSrV5nLF+HVbkaVky0WD+v936INYWPdx/NGMX6rVQDjErGlt8we45zaA== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id D3B1694096 for ; Fri, 13 Dec 2024 19:42:13 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tMAbX-0003qL-Ad; Fri, 13 Dec 2024 13:41:35 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tMAZy-0002wF-N4 for emacs-orgmode@gnu.org; Fri, 13 Dec 2024 13:39:58 -0500 Received: from mout01.posteo.de ([185.67.36.65]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tMAZs-0007df-CD for emacs-orgmode@gnu.org; Fri, 13 Dec 2024 13:39:58 -0500 Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id 6633E240029 for ; Fri, 13 Dec 2024 19:39:48 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1734115188; bh=l5PmT5Jgv5d/X6tyPm1Z5vAareEvkTAFiS+Uw93VPJ0=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type: Content-Transfer-Encoding:From; b=NZ9+0aFC8gApyLp8vNbAVnE2ia+/LWCO/bx+HIc9Ur0xw0Ph3SkzA8ku33/59iPW8 Gz4C1U8zibtCUi88obJIDIoIMShul3+e2Yc7pfelM1jZcW2OJf+hLn1V91DPsS1KjB PHkZ2VF8OlwoSDGdQcBiN8/Kv7NS3ODcQFBAD5vrIPzFEHj2UGpVxbDxMYZCUrw3JE Ku1a8KGKO+uvQ3I2FZZh6fDpHclr7Nt2s0pFX7KOAEaF4wQPABHFkBGbWsyN0sQ90H U8M2zoqOXm0XQ07UIoZiqsloDLpZ8lHhNyFAsF/xtkt2BjL4j5XmuZcSi0SK6AaPwa U6036GNtGenDQ== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4Y8ymx5yjrz6trs; Fri, 13 Dec 2024 19:39:45 +0100 (CET) From: Ihor Radchenko To: =?utf-8?Q?Tor-bj=C3=B6rn?= Claesson , emacs-devel@gnu.org Cc: emacs-orgmode@gnu.org, Philip Kaludercic , Omar =?utf-8?Q?Antol=C3=ADn?= Camarena , Jonas Bernoulli , Juri Linkov , karthikchikmagalur@gmail.com, Visuwesh , charles.choi@yummymelon.com, Justin Burkett 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?) In-Reply-To: <87wmg6edr0.fsf@gmail.com> References: <8734m28l9a.fsf@gmail.com> <87wmhlmp83.fsf@gmail.com> <871pzte929.fsf@localhost> <87v7x548ri.fsf@gmail.com> <87y120daue.fsf@localhost> <874j4m9ep6.fsf@gmail.com> <87h68gfqj1.fsf@localhost> <87pln3f3cc.fsf@localhost> <87jzd9ojj0.fsf@localhost> <87cyj0ajm9.fsf@gmail.com> <87zfm4s50x.fsf@localhost> <87wmh8s358.fsf@localhost> <87y11nwp9z.fsf@gmail.com> <87v7wd9a2h.fsf@localhost> <878qt7fbki.fsf@gmail.com> <87o71jwdxz.fsf@localhost> <87wmg6edr0.fsf@gmail.com> Date: Fri, 13 Dec 2024 18:41:14 +0000 Message-ID: <87msgzh1dh.fsf@localhost> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Received-SPF: pass client-ip=185.67.36.65; envelope-from=yantar92@posteo.net; helo=mout01.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_PASS=-0.001, T_SPF_HELO_TEMPERROR=0.01 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Fri, 13 Dec 2024 13:41:33 -0500 X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-Migadu-Scanner: mx11.migadu.com X-Migadu-Spam-Score: -4.90 X-Spam-Score: -4.90 X-Migadu-Queue-Id: D3B1694096 X-TUID: So5RvS8bvQZY 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 (dynamic= ally 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=C3=B6rn Claesson writes: > From 7e9e0c64fbda2dcb67d8c8421d1c9923ca93e8b4 Mon Sep 17 00:00:00 2001 > From: =3D?UTF-8?q?Tor-bj=3DC3=3DB6rn=3D20Claesson?=3D > 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 >=20=20 > See the new [[info:org#Repeating commands]["Repeating commands"]] sectio= n in Org mode manual. >=20=20 > +*** New transient menu when following citations > + > +Following citations with the org-cite-basic citation backend can now pre= sent a > +transient menu. To show this menu, set ~org-cite-basic-follow-ask~ to no= n-nil.=20 > +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 >=20=20 > # Chanes deadling with changing default values of customizations, > @@ -158,6 +167,19 @@ English. The default value is ~t~ as the CSL standa= rd assumes that > English titles are specified in sentence-case but the bibtex > bibliography format requires them to be written in title-case. >=20=20 > +*** 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 >=20=20 > # 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) >=20=20 > (declare-function org-open-at-point "org" (&optional arg)) > (declare-function org-open-file "org" (path &optional in-emacs line sear= ch)) > @@ -140,6 +142,39 @@ > :type 'face > :safe #'facep) >=20=20 > +(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.=20 > +Otherwise, `org-cite-basic-follow' will display a transient menu prompti= ng the=20 > +user for an action. The contents of this menu can be customized in=20 > +`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 poss= ible=20 > +to specify a function call for the COMMAND part, where !citation (the ci= tation > +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))) >=20=20 > +(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-refer= ence) > + (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))))) >=20=20 > +(transient-define-prefix org-cite-basic-follow (citation-object &optiona= l prefix) > + "Follow citation. > + > +If `org-cite-basic-follow-ask' is non-nil, this transient will present > +a menu prompting the user for an action.=20 > +Otherwise, it will open the bibliography entry for the citation at point= .=20=20 > +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 pr= efix)) > + (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' c= hanges." > + (transient-parse-suffixes > + 'org-cite-basic-follow > + (cl-map 'vector > + (lambda (group) > + (cl-map 'vector #'org-cite-basic-follow--parse-suffix-speci= fication > + 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 bu= ffer." > :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-sty= le) > :cite-styles > --=20 > 2.46.0 > --=20 Ihor Radchenko // yantar92, Org mode maintainer, Learn more about Org mode at . Support Org development at , or support my work at