I'm exporting sub-trees as pdf files for some classes I'm taking: # -*- org-use-property-inheritance: t; -*- * Class A :PROPERTIES: :EXPORT_FILE_NAME: support/Class A :END: ** Assignment 1 :PROPERTIES: :EXPORT_FILE_NAME+: /assignment-1 :END: Some assignment for Class A ** Assignment 2 :PROPERTIES: :EXPORT_FILE_NAME+: /assignment-2 :END: Some other assignment for Class A * Class B :PROPERTIES :EXPORT_FILE_NAME: support/Class B :END: ** Assignment 1 :PROPERTIES: :EXPORT_FILE_NAME+: /assignment-1 :END: Some assignment for Class B ** Assignment 2 :PROPERTIES: :EXPORT_FILE_NAME+: /assignment-2 :END: Some other assignment for Class B And this works great, except there's always a space between 'support/Class A' and '/assignment-1.pdf'. Is there any way to concatenate the two properties rather than join them with spaces? Best, Tyler
[-- Attachment #1: Type: text/plain, Size: 1523 bytes --] I believe this is hard coded in org-entry-get-with-inheritance. The fastest option would be an override advice with your own function that replaces (and value " ") with (and value ""), and maybe the two other " " with "". John ----------------------------------- Professor John Kitchin (he/him/his) Doherty Hall A207F Department of Chemical Engineering Carnegie Mellon University Pittsburgh, PA 15213 412-268-7803 @johnkitchin http://kitchingroup.cheme.cmu.edu On Thu, May 5, 2022 at 6:38 PM Tyler Grinn <tylergrinn@gmail.com> wrote: > > I'm exporting sub-trees as pdf files for some classes I'm taking: > > # -*- org-use-property-inheritance: t; -*- > > * Class A > :PROPERTIES: > :EXPORT_FILE_NAME: support/Class A > :END: > ** Assignment 1 > :PROPERTIES: > :EXPORT_FILE_NAME+: /assignment-1 > :END: > Some assignment for Class A > ** Assignment 2 > :PROPERTIES: > :EXPORT_FILE_NAME+: /assignment-2 > :END: > Some other assignment for Class A > * Class B > :PROPERTIES > :EXPORT_FILE_NAME: support/Class B > :END: > ** Assignment 1 > :PROPERTIES: > :EXPORT_FILE_NAME+: /assignment-1 > :END: > Some assignment for Class B > ** Assignment 2 > :PROPERTIES: > :EXPORT_FILE_NAME+: /assignment-2 > :END: > Some other assignment for Class B > > And this works great, except there's always a space between > 'support/Class A' and '/assignment-1.pdf'. Is there any way to > concatenate the two properties rather than join them with spaces? > > Best, > > Tyler > > [-- Attachment #2: Type: text/html, Size: 2335 bytes --]
John Kitchin <jkitchin@andrew.cmu.edu> writes:
> I believe this is hard coded in org-entry-get-with-inheritance. The
> fastest option would be an override advice with your own function that
> replaces (and value " ") with (and value ""), and maybe the two other
> " " with "".
>
> John
>
> -----------------------------------
> Professor John Kitchin (he/him/his)
> Doherty Hall A207F
> Department of Chemical Engineering
> Carnegie Mellon University
> Pittsburgh, PA 15213
> 412-268-7803
> @johnkitchin
> http://kitchingroup.cheme.cmu.edu
>
Thanks for the advice. I've added the caret symbol (:EXPORT_FILE_NAME^:
assignment-1) to mean 'concatenate' as opposed to + for 'joining'. If
this is something the community would want I will clean it up and send
in a patch.
Tyler Grinn <tylergrinn@gmail.com> writes:
> Thanks for the advice. I've added the caret symbol (:EXPORT_FILE_NAME^:
> assignment-1) to mean 'concatenate' as opposed to + for 'joining'. If
> this is something the community would want I will clean it up and send
> in a patch.
Hmm. I am not sure if it going to be a straightforward patch. You may
need to update org-element parser accordingly.
Also, I do not like the idea of extending org syntax with :PROPERTY^:.
It may be easier to introduce a variable similar to
org-use-property-inheritance where the user can customise how to
accumulate parent properties.
Best,
Ihor
[-- Attachment #1: Type: text/plain, Size: 1307 bytes --] I like the variable idea. I would make it a concatenation string for joining. That way “” would concatenate the way Tyler wants, “ “ would preserve current behavior, and “,” could lead to a comma separated list for example. Other things like “\n” might lead to a column, etc. On Sat, May 7, 2022 at 1:32 AM Ihor Radchenko <yantar92@gmail.com> wrote: > Tyler Grinn <tylergrinn@gmail.com> writes: > > > Thanks for the advice. I've added the caret symbol (:EXPORT_FILE_NAME^: > > assignment-1) to mean 'concatenate' as opposed to + for 'joining'. If > > this is something the community would want I will clean it up and send > > in a patch. > > Hmm. I am not sure if it going to be a straightforward patch. You may > need to update org-element parser accordingly. > > Also, I do not like the idea of extending org syntax with :PROPERTY^:. > It may be easier to introduce a variable similar to > org-use-property-inheritance where the user can customise how to > accumulate parent properties. > > Best, > Ihor > -- John ----------------------------------- Professor John Kitchin (he/him/his) Doherty Hall A207F Department of Chemical Engineering Carnegie Mellon University Pittsburgh, PA 15213 412-268-7803 @johnkitchin http://kitchingroup.cheme.cmu.edu [-- Attachment #2: Type: text/html, Size: 2005 bytes --]
John Kitchin <jkitchin@andrew.cmu.edu> writes:
> I like the variable idea. I would make it a concatenation string for
> joining. That way “” would concatenate the way Tyler wants, “ “ would
> preserve current behavior, and “,” could lead to a comma separated
> list for example. Other things like “\n” might lead to a column, etc.
I'm not a huge fan of the variable idea because it would make it
impossible to include both behaviors in a single file, whereas extending
the syntax maintains any existing properties that used '+'.
[-- Attachment #1: Type: text/plain, Size: 1161 bytes --] Where did you add that? John ----------------------------------- Professor John Kitchin (he/him/his) Doherty Hall A207F Department of Chemical Engineering Carnegie Mellon University Pittsburgh, PA 15213 412-268-7803 @johnkitchin http://kitchingroup.cheme.cmu.edu On Fri, May 6, 2022 at 11:40 AM Tyler Grinn <tylergrinn@gmail.com> wrote: > John Kitchin <jkitchin@andrew.cmu.edu> writes: > > > I believe this is hard coded in org-entry-get-with-inheritance. The > > fastest option would be an override advice with your own function that > > replaces (and value " ") with (and value ""), and maybe the two other > > " " with "". > > > > John > > > > ----------------------------------- > > Professor John Kitchin (he/him/his) > > Doherty Hall A207F > > Department of Chemical Engineering > > Carnegie Mellon University > > Pittsburgh, PA 15213 > > 412-268-7803 > > @johnkitchin > > http://kitchingroup.cheme.cmu.edu > > > > Thanks for the advice. I've added the caret symbol (:EXPORT_FILE_NAME^: > assignment-1) to mean 'concatenate' as opposed to + for 'joining'. If > this is something the community would want I will clean it up and send > in a patch. > [-- Attachment #2: Type: text/html, Size: 2064 bytes --]
John Kitchin <jkitchin@andrew.cmu.edu> writes:
> Where did you add that?
>
> John
>
> -----------------------------------
> Professor John Kitchin (he/him/his)
> Doherty Hall A207F
> Department of Chemical Engineering
> Carnegie Mellon University
> Pittsburgh, PA 15213
> 412-268-7803
> @johnkitchin
> http://kitchingroup.cheme.cmu.edu
>
> On Fri, May 6, 2022 at 11:40 AM Tyler Grinn <tylergrinn@gmail.com>
> wrote:
>
> Thanks for the advice. I've added the caret symbol
> (:EXPORT_FILE_NAME^:
> assignment-1) to mean 'concatenate' as opposed to + for 'joining'.
> If
> this is something the community would want I will clean it up and
> send
> in a patch.
I modified 'org--property-local-values' to return a cons list with the
car being the value of the property and the cdr being either 'concat or
'join.
;; Find additional values.
(let* ((property-extension (org-re-property
(concat (regexp-quote property)
"[+^]")
t t)))
(while (re-search-forward property-extension end t)
(push (cons (match-string-no-properties 3)
(if (string-match-p "\\^\\'" (match-string-no-properties 2))
'concat
'join))
value)))
I used a single re-search in order to maintain the ordering of
properties, ie:
:PROP: he
:PROP^: llo
:PROP+: world
produces 'hello world'
I then updated both 'org-entry-get' and
'org-entry-get-with-inheritance' to 'cl-reduce' the cons list into the
final string value.
Tyler Grinn <tylergrinn@gmail.com> writes: > John Kitchin <jkitchin@andrew.cmu.edu> writes: > >> I like the variable idea. I would make it a concatenation string for >> joining. That way “” would concatenate the way Tyler wants, “ “ would >> preserve current behavior, and “,” could lead to a comma separated >> list for example. Other things like “\n” might lead to a column, etc. > > I'm not a huge fan of the variable idea because it would make it > impossible to include both behaviors in a single file, whereas extending > the syntax maintains any existing properties that used '+'. I think I need to elaborate what I meant by "similar to org-use-property-inheritance". org-use-property-inheritance docstring: >>> When nil, only the properties directly given in the current entry >>> count. When t, every property is inherited. The value may also be a >>> list of properties that should have inheritance, or a regular >>> expression matching properties that should be inherited. Similarly, concatenation of PROPERTY+ can be controlled on per-property basis. Best, Ihor
Ihor Radchenko <yantar92@gmail.com> writes:
> Tyler Grinn <tylergrinn@gmail.com> writes:
>
>> John Kitchin <jkitchin@andrew.cmu.edu> writes:
>>
>>> I like the variable idea. I would make it a concatenation string for
>>> joining. That way “” would concatenate the way Tyler wants, “ “ would
>>> preserve current behavior, and “,” could lead to a comma separated
>>> list for example. Other things like “\n” might lead to a column, etc.
>>
>> I'm not a huge fan of the variable idea because it would make it
>> impossible to include both behaviors in a single file, whereas extending
>> the syntax maintains any existing properties that used '+'.
>
> I think I need to elaborate what I meant by "similar to
> org-use-property-inheritance".
>
> org-use-property-inheritance docstring:
>
>>>> When nil, only the properties directly given in the current entry
>>>> count. When t, every property is inherited. The value may also be a
>>>> list of properties that should have inheritance, or a regular
>>>> expression matching properties that should be inherited.
>
> Similarly, concatenation of PROPERTY+ can be controlled on per-property
> basis.
>
> Best,
> Ihor
Could you provide an example of what the value of that variable would be
if, for instance, I wanted PROP_A and PROP_B to be joined with a single
space and PROP_C and PROP_D to be concatenated? Or better yet, have the
default be to join with a single space for any property and have only
PROP_C and PROP_D concatenated?
Tyler Grinn <tylergrinn@gmail.com> writes:
>
> Could you provide an example of what the value of that variable would be
> if, for instance, I wanted PROP_A and PROP_B to be joined with a single
> space and PROP_C and PROP_D to be concatenated? Or better yet, have the
> default be to join with a single space for any property and have only
> PROP_C and PROP_D concatenated?
Say,
'(("PROP_[CD]" . ""))
or
'(("PROP_[AB]" . "/") ("PROP_[CD]" . "") (".+" . "∿"))
Best,
Ihor
Ihor Radchenko <yantar92@gmail.com> writes:
> Tyler Grinn <tylergrinn@gmail.com> writes:
>
>>
>> Could you provide an example of what the value of that variable would be
>> if, for instance, I wanted PROP_A and PROP_B to be joined with a single
>> space and PROP_C and PROP_D to be concatenated? Or better yet, have the
>> default be to join with a single space for any property and have only
>> PROP_C and PROP_D concatenated?
>
> Say,
>
> '(("PROP_[CD]" . ""))
>
> or
>
> '(("PROP_[AB]" . "/") ("PROP_[CD]" . "") (".+" . "∿"))
>
> Best,
> Ihor
I'm not sure forcing regular expressions for all use cases is ideal. Is
there a way to allow the option (I'm calling it org-property-separators)
to allow either regular expressions or exact matches, like
org-use-property-inheritance does?
Other than that I'm mostly ready to send in my first patch. Should I
attach it in this thread or start a new one?
Best,
Tyler
Tyler Grinn <tylergrinn@gmail.com> writes: > Ihor Radchenko <yantar92@gmail.com> writes: > >> Tyler Grinn <tylergrinn@gmail.com> writes: >> >>> >>> Could you provide an example of what the value of that variable would be >>> if, for instance, I wanted PROP_A and PROP_B to be joined with a single >>> space and PROP_C and PROP_D to be concatenated? Or better yet, have the >>> default be to join with a single space for any property and have only >>> PROP_C and PROP_D concatenated? >> >> Say, >> >> '(("PROP_[CD]" . "")) >> >> or >> >> '(("PROP_[AB]" . "/") ("PROP_[CD]" . "") (".+" . "∿")) >> >> Best, >> Ihor > > I'm not sure forcing regular expressions for all use cases is ideal. Is > there a way to allow the option (I'm calling it org-property-separators) > to allow either regular expressions or exact matches, like > org-use-property-inheritance does? It's up to you. I merely suggested that the new option should work similarly to org-use-property-inheritance. It will not be the exact match since we also need to provide the separator string. > Other than that I'm mostly ready to send in my first patch. Should I > attach it in this thread or start a new one? Keeping the patch within this thread will be more reasonable for future readers. Just remember to prefix the subject line with [PATCH]. Best, Ihor
[-- Attachment #1: Type: text/plain, Size: 1670 bytes --] Ihor Radchenko <yantar92@gmail.com> writes: > Tyler Grinn <tylergrinn@gmail.com> writes: > >> Ihor Radchenko <yantar92@gmail.com> writes: >> >>> Tyler Grinn <tylergrinn@gmail.com> writes: >>> >>>> >>>> Could you provide an example of what the value of that variable would be >>>> if, for instance, I wanted PROP_A and PROP_B to be joined with a single >>>> space and PROP_C and PROP_D to be concatenated? Or better yet, have the >>>> default be to join with a single space for any property and have only >>>> PROP_C and PROP_D concatenated? >>> >>> Say, >>> >>> '(("PROP_[CD]" . "")) >>> >>> or >>> >>> '(("PROP_[AB]" . "/") ("PROP_[CD]" . "") (".+" . "∿")) >>> >>> Best, >>> Ihor >> >> I'm not sure forcing regular expressions for all use cases is ideal. Is >> there a way to allow the option (I'm calling it org-property-separators) >> to allow either regular expressions or exact matches, like >> org-use-property-inheritance does? > > It's up to you. I merely suggested that the new option should work > similarly to org-use-property-inheritance. It will not be the exact > match since we also need to provide the separator string. > >> Other than that I'm mostly ready to send in my first patch. Should I >> attach it in this thread or start a new one? > > Keeping the patch within this thread will be more reasonable for future > readers. Just remember to prefix the subject line with [PATCH]. > > Best, > Ihor OK, what I have now is that if the car of an alist item is a list, exact matching will be done for each list item, but if it is a string, it will be matched as a regular expression. Best, Tyler [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: Add org-property-separators option --] [-- Type: text/x-patch, Size: 8332 bytes --] From c23b45dd5d0bc5a049b916a4b4003c6f55ce4b51 Mon Sep 17 00:00:00 2001 From: Tyler Grinn <tylergrinn@gmail.com> Date: Mon, 9 May 2022 15:52:58 -0400 Subject: [PATCH] lisp/org.el: Add org-property-separators option * lisp/org.el (org-property-separators, org-property-get-separator): Created. (org-entry-get, org-entry-get-with-inheritance): Use new org-property-get-separator function. * testing/lisp/test-org.el (test-org/entry-get): Added tests for combining properties with custom separators org-property-separators is a customization option that allows for properties to be combined using a separator other than the default (a single space). It is an alist with the car of each element being a list of property names or regular expression and the cdr being the separator string, like '((("EXPORT_FILE_NAME") . "/")). --- etc/ORG-NEWS | 21 ++++++++++++++++++++ lisp/org.el | 43 ++++++++++++++++++++++++++++++++-------- testing/lisp/test-org.el | 30 +++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 27de6da62..0297a9d49 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -141,6 +141,27 @@ discouraged when working with Org files. ** New features +*** New customization option =org-property-separators= +A new alist variable to control how properties are combined. + +If a property is specified multiple times with a =+=, like +=:EXPORT_FILE_NAME+:=, the old behavior was to always combine them +with a single space. For the new variable, the car of each item in the +alist should be either a list of property names or a regular +expression, while the cdr should be the separator to use when +combining that property. + +The default value for the separator is a single space, if none of the +provided items in the alist match a given property. + +For example, in order to combine =EXPORT_FILE_NAME= properties with a +forward slash =/=, one can use + +#+begin_src emacs-lisp +(setq org-use-property-inheritance '("EXPORT_FILE_NAME") + org-property-separators '((("EXPORT_FILE_NAME") . "/"))) +#+end_src + *** New library =org-persist.el= implements variable persistence across Emacs sessions The library stores variable data in ~org-persist-directory~ (set to XDG diff --git a/lisp/org.el b/lisp/org.el index cab59b87c..a53505752 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -2850,6 +2850,30 @@ in this variable)." (member-ignore-case property org-use-property-inheritance)) (t (error "Invalid setting of `org-use-property-inheritance'")))) +(defcustom org-property-separators nil + "An alist to control how properties are combined. + +The car of each item should be either a list of property names or +a regular expression, while the cdr should be the separator to +use when combining that property. + +If an alist item cannot be found that matches a given property, a +single space will be used as the separator." + :group 'org-properties + :type '(alist :key-type string :value-type sexp)) + +(defun org-property-get-separator (property) + "Get the separator to use for combining PROPERTY." + (or + (catch 'separator + (dolist (spec org-property-separators) + (if (listp (car spec)) + (if (member property (car spec)) + (throw 'separator (cdr spec))) + (if (string-match-p (car spec) property) + (throw 'separator (cdr spec)))))) + " ")) + (defcustom org-columns-default-format "%25ITEM %TODO %3PRIORITY %TAGS" "The default column format, if no other format has been defined. This variable can be set on the per-file basis by inserting a line @@ -12355,7 +12379,9 @@ value higher up the hierarchy." (org-entry-get-with-inheritance property literal-nil)) (t (let* ((local (org--property-local-values property literal-nil)) - (value (and local (mapconcat #'identity (delq nil local) " ")))) + (value (and local (mapconcat #'identity + (delq nil local) + (org-property-get-separator property))))) (if literal-nil value (org-not-nil value))))))) (defun org-property-or-variable-value (var &optional inherit) @@ -12464,7 +12490,8 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (catch 'exit (let ((element (or element (and (org-element--cache-active-p) - (org-element-at-point nil 'cached))))) + (org-element-at-point nil 'cached)))) + (separator (org-property-get-separator property))) (if element (let ((element (org-element-lineage element '(headline org-data inlinetask) 'with-self))) (while t @@ -12472,8 +12499,8 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (v (if (listp v) v (list v)))) (when v (setq value - (concat (mapconcat #'identity (delq nil v) " ") - (and value " ") + (concat (mapconcat #'identity (delq nil v) separator) + (and value separator) value))) (cond ((car v) @@ -12484,15 +12511,15 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (t (let ((global (org--property-global-or-keyword-value property literal-nil))) (cond ((not global)) - (value (setq value (concat global " " value))) + (value (setq value (concat global separator value))) (t (setq value global)))) (throw 'exit nil)))))) (while t (let ((v (org--property-local-values property literal-nil))) (when v (setq value - (concat (mapconcat #'identity (delq nil v) " ") - (and value " ") + (concat (mapconcat #'identity (delq nil v) separator) + (and value separator) value))) (cond ((car v) @@ -12513,7 +12540,7 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (t (let ((global (org--property-global-or-keyword-value property literal-nil))) (cond ((not global)) - (value (setq value (concat global " " value))) + (value (setq value (concat global separator value))) (t (setq value global)))) (throw 'exit nil)))))))) (if literal-nil value (org-not-nil value))))) diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 0cee31d4e..c0c13f586 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -5997,7 +5997,35 @@ Paragraph<point>" (org-test-with-temp-text ":PROPERTIES:\n:A: 0\n:END:\n#+PROPERTY: A 1\n* H\n:PROPERTIES:\n:A+: 2\n:END:" (org-mode-restart) - (org-entry-get (point-max) "A" t))))) + (org-entry-get (point-max) "A" t)))) + ;; Use alternate separators + (should + (equal "0~2" + (org-test-with-temp-text + ":PROPERTIES:\n:A: 0\n:A+: 2\n:END:" + (let ((org-property-separators '((("A") . "~")))) + (org-entry-get (point) "A"))))) + ;; Default separator is single space + (should + (equal "0 2" + (org-test-with-temp-text + ":PROPERTIES:\n:A: 0\n:B: 1\n:A+: 2\n:B+: 3\n:END:" + (let ((org-property-separators '((("B") . "~")))) + (org-entry-get (point) "A"))))) + ;; Regular expression matching for separator + (should + (equal "0/2" + (org-test-with-temp-text + ":PROPERTIES:\n:A: 0\n:A+: 2\n:END:" + (let ((org-property-separators '((("B") . "~") ("[AC]" . "/")))) + (org-entry-get (point) "A"))))) + ;; Separator works with inheritance + (should + (equal "1~2" + (org-test-with-temp-text + "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2\n:PROPERTIES:\n:A+: 2\n:END:" + (let ((org-property-separators '((("A") . "~")))) + (org-entry-get (point-max) "A" t)))))) (ert-deftest test-org/entry-properties () "Test `org-entry-properties' specifications." -- 2.35.3
Tyler Grinn <tylergrinn@gmail.com> writes: > OK, what I have now is that if the car of an alist item is a list, exact > matching will be done for each list item, but if it is a string, it will > be matched as a regular expression. Sounds reasonable. Note that your patch is >15LOC long and you need to sign the copyright agreement with FSF in order to contribute. See https://orgmode.org/worg/org-contribute.html#copyright Some comments on the patch: > * lisp/org.el (org-property-separators, org-property-get-separator): > Created. I'd make the function private: org--property-get-separator. It is not intended as an API function. > (org-entry-get, org-entry-get-with-inheritance): Use new > org-property-get-separator function. > org-property-separators is a customization option that allows for Please quote the function name as `org-property-get-separator'. > +If a property is specified multiple times with a =+=, like > +=:EXPORT_FILE_NAME+:=, the old behavior was to always combine them > +with a single space. For the new variable, the car of each item in > the Please, use double space " " to separate sentences. Also, see doc/Documentation_Standards.org > +For example, in order to combine =EXPORT_FILE_NAME= properties with a > +forward slash =/=, one can use > + > +#+begin_src emacs-lisp > +(setq org-use-property-inheritance '("EXPORT_FILE_NAME") > + org-property-separators '((("EXPORT_FILE_NAME") . "/"))) > +#+end_src This example is a bit confusing because it is unclear what you want to achieve and why you also need to set inheritance. > +(defcustom org-property-separators nil > ... > + :group 'org-properties > + :type '(alist :key-type string :value-type sexp)) This defcustom type does not match what you described in the docstring. You need something like :type '(alist :key-type (choice string (repeat string)) :value-type string) Best, Ihor
[-- Attachment #1: Type: text/plain, Size: 2419 bytes --] Ihor Radchenko <yantar92@gmail.com> writes: > Note that your patch is >15LOC long and you need to sign the copyright > agreement with FSF in order to contribute. See > https://orgmode.org/worg/org-contribute.html#copyright I've already submitted a copyright assignment to the FSF in order to publish on ELPA. Do I need one specific to org-mode? > Some comments on the patch: > >> * lisp/org.el (org-property-separators, org-property-get-separator): >> Created. > > I'd make the function private: org--property-get-separator. It is not > intended as an API function. I agree, that was an oversight. > >> (org-entry-get, org-entry-get-with-inheritance): Use new >> org-property-get-separator function. > >> org-property-separators is a customization option that allows for > > Please quote the function name as `org-property-get-separator'. > No problem. >> +If a property is specified multiple times with a =+=, like >> +=:EXPORT_FILE_NAME+:=, the old behavior was to always combine them >> +with a single space. For the new variable, the car of each item in >> the > > Please, use double space " " to separate sentences. Also, see > doc/Documentation_Standards.org No problem. > >> +For example, in order to combine =EXPORT_FILE_NAME= properties with a >> +forward slash =/=, one can use >> + >> +#+begin_src emacs-lisp >> +(setq org-use-property-inheritance '("EXPORT_FILE_NAME") >> + org-property-separators '((("EXPORT_FILE_NAME") . "/"))) >> +#+end_src > > This example is a bit confusing because it is unclear what you want to > achieve and why you also need to set inheritance. Inheritance is the most likely scenario one would need to use the '+' property syntax, but I do agree it's kinda distracting and not absolutely necessary in order to get the correct behavior. > >> +(defcustom org-property-separators nil >> ... >> + :group 'org-properties >> + :type '(alist :key-type string :value-type sexp)) > > This defcustom type does not match what you described in the docstring. > You need something like :type '(alist :key-type (choice string (repeat string)) :value-type string) > > Best, > Ihor Setting ':value-type string' is confusing, in my opinion, because the default single space looks like: in the customization buffer, which is indistinguishable from no space: . I just found out about the restricted-sexp type, which I think makes the customization buffer more user-friendly. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: Add org-property-separators option --] [-- Type: text/x-patch, Size: 8763 bytes --] From f474cb25840fdc6b24618b1452cb7fdd32545092 Mon Sep 17 00:00:00 2001 From: Tyler Grinn <tylergrinn@gmail.com> Date: Mon, 9 May 2022 15:52:58 -0400 Subject: [PATCH] lisp/org.el: Add org-property-separators option * lisp/org.el (org-property-separators, org-property-get-separator): Created. (org-entry-get, org-entry-get-with-inheritance): Use new `org-property-get-separator' function. * testing/lisp/test-org.el (test-org/entry-get): Added tests for combining properties with custom separators `org-property-separators' is a customization option that allows for properties to be combined using a separator other than the default (a single space). It is an alist with the car of each element being a list of property names or regular expression and the cdr being the separator string, like '((("EXPORT_FILE_NAME") . "/")). --- etc/ORG-NEWS | 31 ++++++++++++++++++++++++++ lisp/org.el | 47 +++++++++++++++++++++++++++++++++------- testing/lisp/test-org.el | 30 ++++++++++++++++++++++++- 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 27de6da62..9d1d2cdcf 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -141,6 +141,37 @@ discouraged when working with Org files. ** New features +*** New customization option =org-property-separators= +A new alist variable to control how properties are combined. + +If a property is specified multiple times with a =+=, like + +#+begin_src org +:PROPERTIES: +:EXPORT_FILE_NAME: some/path +:EXPORT_FILE_NAME+: to/file +:END: +#+end_src + +the old behavior was to always combine them with a single space +(=some/path to/file=). For the new variable, the car of each item in +the alist should be either a list of property names or a regular +expression, while the cdr should be the separator to use when +combining that property. + +The default value for the separator is a single space, if none of the +provided items in the alist match a given property. + +For example, in order to combine =EXPORT_FILE_NAME= properties with a +forward slash =/=, one can use + +#+begin_src emacs-lisp +(setq org-property-separators '((("EXPORT_FILE_NAME") . "/"))) +#+end_src + +The example above would then produce the property value +=some/path/to/file=. + *** New library =org-persist.el= implements variable persistence across Emacs sessions The library stores variable data in ~org-persist-directory~ (set to XDG diff --git a/lisp/org.el b/lisp/org.el index cab59b87c..643fd6b73 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -2850,6 +2850,34 @@ in this variable)." (member-ignore-case property org-use-property-inheritance)) (t (error "Invalid setting of `org-use-property-inheritance'")))) +(defcustom org-property-separators nil + "An alist to control how properties are combined. + +The car of each item should be either a list of property names or +a regular expression, while the cdr should be the separator to +use when combining that property. + +If an alist item cannot be found that matches a given property, a +single space will be used as the separator." + :group 'org-properties + :type '(alist :key-type (choice (repeat :tag "Properties" string) + (string :tag "Regular Expression")) + :value-type (restricted-sexp :tag "Separator" + :match-alternatives (stringp) + :value " "))) + +(defun org--property-get-separator (property) + "Get the separator to use for combining PROPERTY." + (or + (catch 'separator + (dolist (spec org-property-separators) + (if (listp (car spec)) + (if (member property (car spec)) + (throw 'separator (cdr spec))) + (if (string-match-p (car spec) property) + (throw 'separator (cdr spec)))))) + " ")) + (defcustom org-columns-default-format "%25ITEM %TODO %3PRIORITY %TAGS" "The default column format, if no other format has been defined. This variable can be set on the per-file basis by inserting a line @@ -12355,7 +12383,9 @@ value higher up the hierarchy." (org-entry-get-with-inheritance property literal-nil)) (t (let* ((local (org--property-local-values property literal-nil)) - (value (and local (mapconcat #'identity (delq nil local) " ")))) + (value (and local (mapconcat #'identity + (delq nil local) + (org--property-get-separator property))))) (if literal-nil value (org-not-nil value))))))) (defun org-property-or-variable-value (var &optional inherit) @@ -12464,7 +12494,8 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (catch 'exit (let ((element (or element (and (org-element--cache-active-p) - (org-element-at-point nil 'cached))))) + (org-element-at-point nil 'cached)))) + (separator (org--property-get-separator property))) (if element (let ((element (org-element-lineage element '(headline org-data inlinetask) 'with-self))) (while t @@ -12472,8 +12503,8 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (v (if (listp v) v (list v)))) (when v (setq value - (concat (mapconcat #'identity (delq nil v) " ") - (and value " ") + (concat (mapconcat #'identity (delq nil v) separator) + (and value separator) value))) (cond ((car v) @@ -12484,15 +12515,15 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (t (let ((global (org--property-global-or-keyword-value property literal-nil))) (cond ((not global)) - (value (setq value (concat global " " value))) + (value (setq value (concat global separator value))) (t (setq value global)))) (throw 'exit nil)))))) (while t (let ((v (org--property-local-values property literal-nil))) (when v (setq value - (concat (mapconcat #'identity (delq nil v) " ") - (and value " ") + (concat (mapconcat #'identity (delq nil v) separator) + (and value separator) value))) (cond ((car v) @@ -12513,7 +12544,7 @@ However, if LITERAL-NIL is set, return the string value \"nil\" instead." (t (let ((global (org--property-global-or-keyword-value property literal-nil))) (cond ((not global)) - (value (setq value (concat global " " value))) + (value (setq value (concat global separator value))) (t (setq value global)))) (throw 'exit nil)))))))) (if literal-nil value (org-not-nil value))))) diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 0cee31d4e..c0c13f586 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -5997,7 +5997,35 @@ Paragraph<point>" (org-test-with-temp-text ":PROPERTIES:\n:A: 0\n:END:\n#+PROPERTY: A 1\n* H\n:PROPERTIES:\n:A+: 2\n:END:" (org-mode-restart) - (org-entry-get (point-max) "A" t))))) + (org-entry-get (point-max) "A" t)))) + ;; Use alternate separators + (should + (equal "0~2" + (org-test-with-temp-text + ":PROPERTIES:\n:A: 0\n:A+: 2\n:END:" + (let ((org-property-separators '((("A") . "~")))) + (org-entry-get (point) "A"))))) + ;; Default separator is single space + (should + (equal "0 2" + (org-test-with-temp-text + ":PROPERTIES:\n:A: 0\n:B: 1\n:A+: 2\n:B+: 3\n:END:" + (let ((org-property-separators '((("B") . "~")))) + (org-entry-get (point) "A"))))) + ;; Regular expression matching for separator + (should + (equal "0/2" + (org-test-with-temp-text + ":PROPERTIES:\n:A: 0\n:A+: 2\n:END:" + (let ((org-property-separators '((("B") . "~") ("[AC]" . "/")))) + (org-entry-get (point) "A"))))) + ;; Separator works with inheritance + (should + (equal "1~2" + (org-test-with-temp-text + "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2\n:PROPERTIES:\n:A+: 2\n:END:" + (let ((org-property-separators '((("A") . "~")))) + (org-entry-get (point-max) "A" t)))))) (ert-deftest test-org/entry-properties () "Test `org-entry-properties' specifications." -- 2.35.3
Tyler Grinn <tylergrinn@gmail.com> writes: > Ihor Radchenko <yantar92@gmail.com> writes: > >> Note that your patch is >15LOC long and you need to sign the copyright >> agreement with FSF in order to contribute. See >> https://orgmode.org/worg/org-contribute.html#copyright > > I've already submitted a copyright assignment to the FSF in order to > publish on ELPA. Do I need one specific to org-mode? Nope. The ELPA one is good enough. Basically, Org has the same requirement as Emacs (Org is a part of Emacs). >>> +(defcustom org-property-separators nil >>> ... >>> + :group 'org-properties >>> + :type '(alist :key-type string :value-type sexp)) >> >> This defcustom type does not match what you described in the docstring. >> You need something like :type '(alist :key-type (choice string (repeat string)) :value-type string) > > Setting ':value-type string' is confusing, in my opinion, because the > default single space looks like: in the customization buffer, which is > indistinguishable from no space: . I just found out about the restricted-sexp > type, which I think makes the customization buffer more user-friendly. restricted-sexp is fine. Though you could also do something like (choice (const :tag "Single space" " ") (string :tag "Other string")) > From f474cb25840fdc6b24618b1452cb7fdd32545092 Mon Sep 17 00:00:00 2001 > From: Tyler Grinn <tylergrinn@gmail.com> > Date: Mon, 9 May 2022 15:52:58 -0400 > Subject: [PATCH] lisp/org.el: Add org-property-separators option LGTM! I will leave this for another week to give other people a chance to comment. For reference, this patch might be useful in https://orgmode.org/list/87o821dv7o.fsf@localhost Best, Ihor
Ihor Radchenko <yantar92@gmail.com> writes:
>> From f474cb25840fdc6b24618b1452cb7fdd32545092 Mon Sep 17 00:00:00 2001
>> From: Tyler Grinn <tylergrinn@gmail.com>
>> Date: Mon, 9 May 2022 15:52:58 -0400
>> Subject: [PATCH] lisp/org.el: Add org-property-separators option
>
> LGTM!
>
> I will leave this for another week to give other people a chance to
> comment.
>
> For reference, this patch might be useful in https://orgmode.org/list/87o821dv7o.fsf@localhost
>
> Best,
> Ihor
Great; it's exciting to have my first patch accepted and I look forward
to future development.
Best,
Tyler
Ihor Radchenko <yantar92@gmail.com> writes:
> I will leave this for another week to give other people a chance to
> comment.
Applied upstream onto main via e268e4797.
Best,
Ihor