* [BUG] Numeric priorities only partially supported
@ 2024-10-13 16:02 Tommy Jollyboat
2024-10-13 16:11 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Tommy Jollyboat @ 2024-10-13 16:02 UTC (permalink / raw)
To: emacs-orgmode
(tested with Org 9.7.11, I've done my best to search archives and the
bug-tracker)
Back in 2020, commit #4f98694bf introduced numeric priorities,
allowing [#1] to [#64] as an alternative to [#A] to [#Z]. This is also
documented in the manual.
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=4f98694bf
https://orgmode.org/manual/Priorities.html
It introduced the new function ~org-priority-to-value~, which replaces
~string-to-char~ for parsing priority strings. It parses numeric prios
as ints (1..64) and alphabetic prios as chars (?A..?Z, which is
65-90).
* PROBLEM
However, various functions within org-mode are still using
~string-to-char~ and giving inconsistent results, because numerical
priorities are parsed wrongly, or sometimes return nil (or #10 to
#64).
* EXAMPLES
With the following heading tree:
* test tree
** [#10] ten
** [#2] two
You can get the correct values 10 and 2 with: (org-priority-to-value
(thing-at-point 'line))
You can get incorrect values nil and ?2 with: (org-element-property
:priority (org-element-at-point)) or (nth 3 (org-heading-components))
And if you sort the test tree by priority (with org-sort-entries,
"p"), it won't change, because [#10] is wrongly interpreted as if it
were [#1].
* SOLUTION
If we agree that numeric priorities should be supported everywhere,
various functions need updating:
Functions that use string-to-char to parse:
- org-entry-put
- org-mobile-edit (in org-mobile.el)
- org-agenda-fontify-priorities (org-agenda.el)
- org-mouse-context-menu (org-mouse.el)
I've confirmed that org-entry-put exhibits wrong behaviour (parsing
1-9 as ?1-?9 and 10-64 as nil). I'm not familiar with the others, as
they're in modules I don't use.
The Org Element API also needs updating:
- ~org-element--headline-parse-title~ implements its own
org-priority-regexp, which doesn't account for multiple characters
- ~org-entry-put~ has bugs, but I don't understand the code well enough
- A couple of functions format priorities as "[#%c]", and should
include a %s case for values under 65.
These should all be fairly safe/uncontroversial changes to make, as
any documents that are impacted were relying on undefined behavior
(such as using chars outside of the range A-Z as priorities).
Hope that's all clear! Sorry it's a bit of an essay.
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2024-10-13 16:02 [BUG] Numeric priorities only partially supported Tommy Jollyboat
@ 2024-10-13 16:11 ` Ihor Radchenko
2025-06-08 14:18 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2024-10-13 16:11 UTC (permalink / raw)
To: Tommy Jollyboat; +Cc: emacs-orgmode
Tommy Jollyboat <[email protected]> writes:
> However, various functions within org-mode are still using
> ~string-to-char~ and giving inconsistent results, because numerical
> priorities are parsed wrongly, or sometimes return nil (or #10 to
> #64).
Confirmed.
> * SOLUTION
>
> If we agree that numeric priorities should be supported everywhere,
> various functions need updating:
> ...
Yes.
> These should all be fairly safe/uncontroversial changes to make, as
> any documents that are impacted were relying on undefined behavior
> (such as using chars outside of the range A-Z as priorities).
Not so safe. I tried to fix this specific issue in the past and changing
the org-element parser to handle non-single char priorities broke
tests (either because I did something stupid or because the tests make
incorrect assumptions).
It would be really nice if someone worked on this issue and resolved all
the possible corner cases in the tests and elsewhere.
We must eventually support numeric properties, as documented in the manual.
--
Ihor Radchenko // yantar92,
Org mode contributor,
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2024-10-13 16:11 ` Ihor Radchenko
@ 2025-06-08 14:18 ` Derek Chen-Becker
2025-06-08 16:03 ` Ihor Radchenko
2025-06-08 20:42 ` Jacob S. Gordon
0 siblings, 2 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-06-08 14:18 UTC (permalink / raw)
To: emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1312 bytes --]
On Sun, Oct 13, 2024 at 04:11:01PM +0000, Ihor Radchenko wrote:
> Not so safe. I tried to fix this specific issue in the past and changing
> the org-element parser to handle non-single char priorities broke
> tests (either because I did something stupid or because the tests make
> incorrect assumptions).
>
> It would be really nice if someone worked on this issue and resolved all
> the possible corner cases in the tests and elsewhere.
>
> We must eventually support numeric properties, as documented in the manual.
This is actually something I've wanted for a while and never got
around to looking at it. I'm happy to start digging into this if it's
still valid. I can change this to handled for Woof to track if that
helps.
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| http://chen-becker.org |
| |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-06-08 14:18 ` Derek Chen-Becker
@ 2025-06-08 16:03 ` Ihor Radchenko
2025-06-17 12:29 ` Derek Chen-Becker
2025-06-08 20:42 ` Jacob S. Gordon
1 sibling, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-06-08 16:03 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
>> We must eventually support numeric properties, as documented in the manual.
>
> This is actually something I've wanted for a while and never got
> around to looking at it. I'm happy to start digging into this if it's
> still valid.
Yes, this bug still needs to be fixed.
> ... I can change this to handled for Woof to track if that
> helps.
You can use Handled. keyword as described in https://tracker.orgmode.org/howto
It is optional though as the tracker is not very stable (and thus not
very useful) for the time being.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-06-08 14:18 ` Derek Chen-Becker
2025-06-08 16:03 ` Ihor Radchenko
@ 2025-06-08 20:42 ` Jacob S. Gordon
1 sibling, 0 replies; 81+ messages in thread
From: Jacob S. Gordon @ 2025-06-08 20:42 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Hey Derek,
Just tying this together with a recent thread where numeric default
priorities are interpreted as characters:
https://list.orgmode.org/orgmode/[email protected]/
Thanks,
--
Jacob S. Gordon
[email protected]
======================
Please avoid sending me HTML emails and MS Office documents.
https://useplaintext.email/#etiquette
https://www.gnu.org/philosophy/no-word-attachments.html
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-06-08 16:03 ` Ihor Radchenko
@ 2025-06-17 12:29 ` Derek Chen-Becker
2025-07-10 13:19 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-06-17 12:29 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1395 bytes --]
Sounds good!
Handled.
On Sun, Jun 8, 2025 at 10:04 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> >> We must eventually support numeric properties, as documented in the
> manual.
> >
> > This is actually something I've wanted for a while and never got
> > around to looking at it. I'm happy to start digging into this if it's
> > still valid.
>
> Yes, this bug still needs to be fixed.
>
> > ... I can change this to handled for Woof to track if that
> > helps.
>
> You can use Handled. keyword as described in
> https://tracker.orgmode.org/howto
> It is optional though as the tracker is not very stable (and thus not
> very useful) for the time being.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3235 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-06-17 12:29 ` Derek Chen-Becker
@ 2025-07-10 13:19 ` Derek Chen-Becker
2025-07-12 16:11 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-07-10 13:19 UTC (permalink / raw)
To: emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 2188 bytes --]
Summer schedules got the better of me, but here's the first of hopefully
several patches to address this is small pieces rather than trying to
commit to one large chunk of work. Tests pass locally and I added some new
ones as well.
On Tue, Jun 17, 2025 at 6:29 AM Derek Chen-Becker <[email protected]>
wrote:
> Sounds good!
>
> Handled.
>
> On Sun, Jun 8, 2025 at 10:04 AM Ihor Radchenko <[email protected]>
> wrote:
>
>> Derek Chen-Becker <[email protected]> writes:
>>
>> >> We must eventually support numeric properties, as documented in the
>> manual.
>> >
>> > This is actually something I've wanted for a while and never got
>> > around to looking at it. I'm happy to start digging into this if it's
>> > still valid.
>>
>> Yes, this bug still needs to be fixed.
>>
>> > ... I can change this to handled for Woof to track if that
>> > helps.
>>
>> You can use Handled. keyword as described in
>> https://tracker.orgmode.org/howto
>> It is optional though as the tracker is not very stable (and thus not
>> very useful) for the time being.
>>
>> --
>> 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>
>>
>
>
> --
> +---------------------------------------------------------------+
> | Derek Chen-Becker |
> | GPG Key available at https://keybase.io/dchenbecker and |
> | https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
> | Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
> +---------------------------------------------------------------+
>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 5148 bytes --]
[-- Attachment #2: 0001-lisp-org.el-Handle-numeric-priorities-in-org-entry-p.patch --]
[-- Type: application/octet-stream, Size: 3174 bytes --]
From 42b39b1a08299e3157c74bd80e9741a27f31c6d6 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 8 Jul 2025 07:21:22 -0600
Subject: [PATCH] lisp/org.el: Handle numeric priorities in `org-entry-put'
* lisp/org.el (org-entry-put): Utilize `org-priority-to-value' to parse
priority values so that numeric priorites are handled properly.
* testing/lisp/test-org.el (test-org/entry-put): Add unit tests to cover
reported error cases, including a valid numeric priority, a two-digit
numeric priority, and a numeric priority outside of the defined range.
Reported-by: "Tommy Jollyboat" <[email protected]>
Link: https://list.orgmode.org/orgmode/CANDZv_N394TYMy30-KuEQV+eZ0FEkm4GSjTrkUtDViK_-DkX-Q@mail.gmail.com/
---
lisp/org.el | 2 +-
testing/lisp/test-org.el | 23 +++++++++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/lisp/org.el b/lisp/org.el
index 0a406d7cc..00d6db073 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -13376,7 +13376,7 @@ decreases scheduled or deadline date by one day."
(org-todo value)
(when org-auto-align-tags (org-align-tags)))
((equal property "PRIORITY")
- (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s))
+ (org-priority (if (org-string-nw-p value) (org-priority-to-value value) ?\s))
(when org-auto-align-tags (org-align-tags)))
((equal property "SCHEDULED")
(forward-line)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 36dea35b7..51c3ec477 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -7355,6 +7355,29 @@ Paragraph<point>"
(org-test-with-temp-text "* [#B] H"
(org-entry-put (point) "PRIORITY" nil)
(buffer-string))))
+ (should ;; Set a numeric priority
+ (equal "* [#3] H"
+ (org-test-with-temp-text "* H"
+ (let ((org-priority-lowest 3)
+ (org-priority-highest 1)
+ (org-priority-default 2))
+ (org-entry-put (point) "PRIORITY" "3")
+ (buffer-string)))))
+ (should ;; Set a numeric priority with double digits
+ (equal "* [#20] H"
+ (org-test-with-temp-text "* H"
+ (let ((org-priority-lowest 20)
+ (org-priority-highest 1)
+ (org-priority-default 2))
+ (org-entry-put (point) "PRIORITY" "20")
+ (buffer-string)))))
+ (should-error ;; Attempt to set a priority outside of the highest/lowest range
+ (org-test-with-temp-text "* H"
+ (let ((org-priority-lowest 3)
+ (org-priority-highest 1)
+ (org-priority-default 2))
+ (org-entry-put (point) "PRIORITY" "42")
+ (buffer-string))))
;; Set "SCHEDULED" property.
(should
(string-match "* H\n *SCHEDULED: <2014-03-04 .*?>"
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-10 13:19 ` Derek Chen-Becker
@ 2025-07-12 16:11 ` Ihor Radchenko
2025-07-12 19:27 ` Derek Chen-Becker
2025-07-19 12:56 ` Derek Chen-Becker
0 siblings, 2 replies; 81+ messages in thread
From: Ihor Radchenko @ 2025-07-12 16:11 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Summer schedules got the better of me, but here's the first of hopefully
> several patches to address this is small pieces rather than trying to
> commit to one large chunk of work. Tests pass locally and I added some new
> ones as well.
Thanks!
> - (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s))
> + (org-priority (if (org-string-nw-p value) (org-priority-to-value value) ?\s))
I am not sure if it is the cleanest approach.
Maybe we should instead modify `org-priority' itself to handle string
and non-string properties? For now, your patch is basically violating
the docstring of `org-priority' by feeding a number instead of
character.
> + (should ;; Set a numeric priority with double digits
> + (equal "* [#20] H"
> + (org-test-with-temp-text "* H"
> + (let ((org-priority-lowest 20)
20 - lowest?
More importantly, try the same test but with priority = 97
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-12 16:11 ` Ihor Radchenko
@ 2025-07-12 19:27 ` Derek Chen-Becker
2025-07-19 12:56 ` Derek Chen-Becker
1 sibling, 0 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-07-12 19:27 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1902 bytes --]
Thanks for the feedback. Let me rework it per your suggestions.
Cheers,
Derek
On Sat, Jul 12, 2025 at 10:11 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Summer schedules got the better of me, but here's the first of hopefully
> > several patches to address this is small pieces rather than trying to
> > commit to one large chunk of work. Tests pass locally and I added some
> new
> > ones as well.
>
> Thanks!
>
> > - (org-priority (if (org-string-nw-p value) (string-to-char value)
> ?\s))
> > + (org-priority (if (org-string-nw-p value) (org-priority-to-value
> value) ?\s))
>
> I am not sure if it is the cleanest approach.
> Maybe we should instead modify `org-priority' itself to handle string
> and non-string properties? For now, your patch is basically violating
> the docstring of `org-priority' by feeding a number instead of
> character.
>
> > + (should ;; Set a numeric priority with double digits
> > + (equal "* [#20] H"
> > + (org-test-with-temp-text "* H"
> > + (let ((org-priority-lowest 20)
>
> 20 - lowest?
> More importantly, try the same test but with priority = 97
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3928 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-12 16:11 ` Ihor Radchenko
2025-07-12 19:27 ` Derek Chen-Becker
@ 2025-07-19 12:56 ` Derek Chen-Becker
2025-07-19 13:22 ` Ihor Radchenko
1 sibling, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-07-19 12:56 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2291 bytes --]
Just double-checking my understanding of things, but it looks like
`org-priority' already handles number inputs, despite the docstring:
((or (eq action 'set)
(integerp action))
(if (not (eq action 'set))
(setq new action)
My read of that is that if the argument to `org-priority` is an actual
integer (not an integer string), then it uses that integer as the new
value. Am I missing something?
Thanks,
Derek
On Sat, Jul 12, 2025 at 10:11 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Summer schedules got the better of me, but here's the first of hopefully
> > several patches to address this is small pieces rather than trying to
> > commit to one large chunk of work. Tests pass locally and I added some
> new
> > ones as well.
>
> Thanks!
>
> > - (org-priority (if (org-string-nw-p value) (string-to-char value)
> ?\s))
> > + (org-priority (if (org-string-nw-p value) (org-priority-to-value
> value) ?\s))
>
> I am not sure if it is the cleanest approach.
> Maybe we should instead modify `org-priority' itself to handle string
> and non-string properties? For now, your patch is basically violating
> the docstring of `org-priority' by feeding a number instead of
> character.
>
> > + (should ;; Set a numeric priority with double digits
> > + (equal "* [#20] H"
> > + (org-test-with-temp-text "* H"
> > + (let ((org-priority-lowest 20)
>
> 20 - lowest?
> More importantly, try the same test but with priority = 97
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 4740 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-19 12:56 ` Derek Chen-Becker
@ 2025-07-19 13:22 ` Ihor Radchenko
2025-07-19 13:58 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-07-19 13:22 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Just double-checking my understanding of things, but it looks like
> `org-priority' already handles number inputs, despite the docstring:
>
> ((or (eq action 'set)
> (integerp action))
> (if (not (eq action 'set))
> (setq new action)
> My read of that is that if the argument to `org-priority` is an actual
> integer (not an integer string), then it uses that integer as the new
> value. Am I missing something?
1. (integerp ?a) ; => t and (characterp 30) ;=> t, so you can never tell
2. The code has
(and (= (upcase org-priority-highest) org-priority-highest)
(= (upcase org-priority-lowest) org-priority-lowest))
and, more importantly
((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
(user-error
(if nump
"Priority must be between `%s' and `%s'"
"Priority must be between `%c' and `%c'")
org-priority-highest org-priority-lowest))
- those only work reliably for priority being not too large number
(up to 64). (upcase 1) ; => 1, but (upcase 98) ; => 66. So, handling
of numbers is not reliable at best:
;; Numerical priorities are limited to 64, beyond that number,
;; assume the priority cookie is a character.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-19 13:22 ` Ihor Radchenko
@ 2025-07-19 13:58 ` Derek Chen-Becker
2025-07-19 14:07 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-07-19 13:58 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2895 bytes --]
Sure, but there's the check in the let to set `nump' based on the
highest/lowest priority. I could add that as a condition to the upcase
transform:
(when (and (= (upcase org-priority-highest) org-priority-highest)
(= (upcase org-priority-lowest) org-priority-lowest)
(not nump))
(setq new (upcase new)))
I'm also not clear on why we upcase again when checking the bounds:
((or (< (upcase new) org-priority-highest) (> (upcase new)
org-priority-lowest))
Perhaps I can break out validation of the range into its own function
(easier to test) to avoid conflation with the logic to set/modify.
Cheers,
Derek
On Sat, Jul 19, 2025 at 7:22 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Just double-checking my understanding of things, but it looks like
> > `org-priority' already handles number inputs, despite the docstring:
> >
> > ((or (eq action 'set)
> > (integerp action))
> > (if (not (eq action 'set))
> > (setq new action)
>
> > My read of that is that if the argument to `org-priority` is an actual
> > integer (not an integer string), then it uses that integer as the new
> > value. Am I missing something?
>
> 1. (integerp ?a) ; => t and (characterp 30) ;=> t, so you can never tell
> 2. The code has
> (and (= (upcase org-priority-highest) org-priority-highest)
> (= (upcase org-priority-lowest) org-priority-lowest))
> and, more importantly
> ((or (< (upcase new) org-priority-highest) (> (upcase new)
> org-priority-lowest))
> (user-error
> (if nump
> "Priority must be between `%s' and `%s'"
> "Priority must be between `%c' and `%c'")
> org-priority-highest org-priority-lowest))
> - those only work reliably for priority being not too large number
> (up to 64). (upcase 1) ; => 1, but (upcase 98) ; => 66. So, handling
> of numbers is not reliable at best:
> ;; Numerical priorities are limited to 64, beyond that number,
> ;; assume the priority cookie is a character.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 5947 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-19 13:58 ` Derek Chen-Becker
@ 2025-07-19 14:07 ` Ihor Radchenko
2025-07-19 16:04 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-07-19 14:07 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I'm also not clear on why we upcase again when checking the bounds:
>
> ((or (< (upcase new) org-priority-highest) (> (upcase new)
> org-priority-lowest))
That's a historical code. It has been there before the earlier upcase
was introduced.
Here is the initial version of that code:
((eq action 'set)
(message "Priority A-%c, SPC to remove: " org-lowest-priority)
(setq new (read-char-exclusive))
(cond ((equal new ?\ ) (setq remove t))
((or (< (upcase new) ?A) (> (upcase new) org-lowest-priority))
(error "Priority must be between `%c' and `%c'"
?A org-lowest-priority))))
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-19 14:07 ` Ihor Radchenko
@ 2025-07-19 16:04 ` Derek Chen-Becker
2025-07-19 16:29 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-07-19 16:04 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2158 bytes --]
I guess looking at https://orgmode.org/manual/Priorities.html, it's not
clearly defined what valid ranges are. Are upper and lower cases considered
equivalent? Is it strictly A-Z, or a-z, or 1-64? If it's not numeric, do
we just allow any character whose value is >= 65? For example, 一,二, 三, 四?
Technically, is 1 the highest possible numeric priority or would zero (or a
negative number) be valid? I feel like maybe we should update the manual at
the same time, and precisely specify what the allowed values are. I can
sort of reverse engineer from the code, but is this documented anywhere
other than the manual?
Thanks,
Derek
On Sat, Jul 19, 2025 at 8:07 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > I'm also not clear on why we upcase again when checking the bounds:
> >
> > ((or (< (upcase new) org-priority-highest) (> (upcase new)
> > org-priority-lowest))
>
> That's a historical code. It has been there before the earlier upcase
> was introduced.
>
> Here is the initial version of that code:
> ((eq action 'set)
> (message "Priority A-%c, SPC to remove: " org-lowest-priority)
> (setq new (read-char-exclusive))
> (cond ((equal new ?\ ) (setq remove t))
> ((or (< (upcase new) ?A) (> (upcase new)
> org-lowest-priority))
> (error "Priority must be between `%c' and `%c'"
> ?A org-lowest-priority))))
>
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 4258 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-19 16:04 ` Derek Chen-Becker
@ 2025-07-19 16:29 ` Ihor Radchenko
2025-07-20 23:32 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-07-19 16:29 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I guess looking at https://orgmode.org/manual/Priorities.html, it's not
> clearly defined what valid ranges are. Are upper and lower cases considered
> equivalent? Is it strictly A-Z, or a-z, or 1-64? If it's not numeric, do
> we just allow any character whose value is >= 65? For example, 一,二, 三, 四?
> Technically, is 1 the highest possible numeric priority or would zero (or a
> negative number) be valid? I feel like maybe we should update the manual at
> the same time, and precisely specify what the allowed values are. I can
> sort of reverse engineer from the code, but is this documented anywhere
> other than the manual?
Well. You cannot reverse engineer.
1. org-priority-regexp is ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
2. org-element.el is
(priority (and (looking-at "\\[#.\\][ \t]*")
(progn (goto-char (match-end 0))
(aref (match-string 0) 2))))
3. https://orgmode.org/worg/org-syntax.html (following the parser)
PRIORITY (optional)
A single alphanumeric character preceded by a hash sign # and
enclosed within square brackets (e.g. [#A] or [#1]). This is called a
“priority cookie”.
4. 5.4 Priorities in the manual says, "A", "B", "C", "integers, which
must all be strictly inferior to 65", "earlier in alphabet" (implying
some kind of ordered alphabet, but not CJK)
5. org-priority-highest
If you set org-priority-highest to a numeric value inferior to
65, Org assumes you want to use digits for the priority cookie.
If you set it to >=65, Org assumes you want to use alphabetical
characters.
6. org-priority-get-priority-function is a defcustom, implying that
priority might be less restrictive
7. (org-element-property :priority (org-element-at-point)) always
returns a number/character (and must be interpreted back by
org-element-interpret-data)
8. Some places in the code
(`org-latex-format-headline-default-function') plainly assume that
priority is formattable with (format "%c" ...)
I'd say that the simplest approach will be using the most restrictive
definition of priority: either a number between 0-64 or a single
alphabetic character (with code >=65).
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-19 16:29 ` Ihor Radchenko
@ 2025-07-20 23:32 ` Derek Chen-Becker
2025-07-21 16:53 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-07-20 23:32 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 3343 bytes --]
Sounds good. I'm happy to update the documentation, the regexp, and
other mentioned locations to match one of A-Z or 0-64. Are we OK
restricting it to uppercase only?
Thanks!
Derek
On Sat, Jul 19, 2025 at 10:29 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > I guess looking at https://orgmode.org/manual/Priorities.html, it's not
> > clearly defined what valid ranges are. Are upper and lower cases
> considered
> > equivalent? Is it strictly A-Z, or a-z, or 1-64? If it's not numeric, do
> > we just allow any character whose value is >= 65? For example, 一,二, 三, 四?
> > Technically, is 1 the highest possible numeric priority or would zero
> (or a
> > negative number) be valid? I feel like maybe we should update the manual
> at
> > the same time, and precisely specify what the allowed values are. I can
> > sort of reverse engineer from the code, but is this documented anywhere
> > other than the manual?
>
> Well. You cannot reverse engineer.
>
> 1. org-priority-regexp is ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
> 2. org-element.el is
> (priority (and (looking-at "\\[#.\\][ \t]*")
> (progn (goto-char (match-end 0))
> (aref (match-string 0) 2))))
> 3. https://orgmode.org/worg/org-syntax.html (following the parser)
> PRIORITY (optional)
> A single alphanumeric character preceded by a hash sign # and
> enclosed within square brackets (e.g. [#A] or [#1]). This is called a
> “priority cookie”.
>
> 4. 5.4 Priorities in the manual says, "A", "B", "C", "integers, which
> must all be strictly inferior to 65", "earlier in alphabet" (implying
> some kind of ordered alphabet, but not CJK)
>
> 5. org-priority-highest
> If you set org-priority-highest to a numeric value inferior to
> 65, Org assumes you want to use digits for the priority cookie.
> If you set it to >=65, Org assumes you want to use alphabetical
> characters.
>
> 6. org-priority-get-priority-function is a defcustom, implying that
> priority might be less restrictive
>
> 7. (org-element-property :priority (org-element-at-point)) always
> returns a number/character (and must be interpreted back by
> org-element-interpret-data)
>
> 8. Some places in the code
> (`org-latex-format-headline-default-function') plainly assume that
> priority is formattable with (format "%c" ...)
>
> I'd say that the simplest approach will be using the most restrictive
> definition of priority: either a number between 0-64 or a single
> alphabetic character (with code >=65).
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 5703 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-20 23:32 ` Derek Chen-Becker
@ 2025-07-21 16:53 ` Ihor Radchenko
2025-09-13 11:36 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-07-21 16:53 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Sounds good. I'm happy to update the documentation, the regexp, and
> other mentioned locations to match one of A-Z or 0-64. Are we OK
> restricting it to uppercase only?
Yes, let's go for it.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-07-21 16:53 ` Ihor Radchenko
@ 2025-09-13 11:36 ` Derek Chen-Becker
2025-09-13 14:39 ` Rudolf Adamkovič
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-09-13 11:36 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1429 bytes --]
I ended up going down a couple of paths before settling on a general
refactor of the code to introduce some helper functions and more unit
tests. There are still several things I need to fix, but I've attached the
patches related to `org-priority' at least. If this looks reasonable I'll
start working on the other parts of the codebase that deal with priorities,
using the same helper functions.
Thanks,
Derek
On Mon, Jul 21, 2025 at 10:53 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Sounds good. I'm happy to update the documentation, the regexp, and
> > other mentioned locations to match one of A-Z or 0-64. Are we OK
> > restricting it to uppercase only?
>
> Yes, let's go for it.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 3355 bytes --]
[-- Attachment #2: 0002-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: text/x-patch, Size: 16054 bytes --]
From c256a75956c3ebf374b9e11366af926b56dbdb05 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Sat, 6 Sep 2025 07:07:34 -0600
Subject: [PATCH 2/2] lisp/org.el: Add proper support for numeric priorities
* doc/org-manual.org: Update the manual to clarify the allowed values for
priorities.
* lisp/org.el (org-priority-regexp): Update the priority cookie regular
expression to fully validate numeric values, and update the documentation
to match.
(org-priority-valid-cookiep, org-priority-valid-valuep,
org-priority-value-in-rangep, org-priority-numberp): Add validation
predicates to test priority cookies and priority values.
(org-priority-to-string): Add a function to provide a consistent string
value from a given priority value.
(org-priority): Refactor to apply more validation and use the new
predicates and functions defined in this commit to simplify logic and make
things more consistent, particularly around double-digit numeric values.
* testing/lisp/test-org.el: Add unit tests for the new predicats and format
functions related to priority.
---
doc/org-manual.org | 8 +-
lisp/org.el | 165 +++++++++++++++++++++++++++------------
testing/lisp/test-org.el | 66 +++++++++++++++-
3 files changed, 185 insertions(+), 54 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 8c6962d4b..26a5cbd57 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4626,7 +4626,7 @@ You can also use numeric values for priorities, such as
When using numeric priorities, you need to set ~org-priority-highest~,
~org-priority-lowest~ and ~org-priority-default~ to integers, which
-must all be strictly inferior to 65.
+must all be a positive integer between 0 and 64, inclusive.
Priorities can be attached to any heading; they do not need to be
TODO items.
@@ -4661,8 +4661,10 @@ TODO items.
#+vindex: org-priority-default
You can change the range of allowed priorities by setting the
variables ~org-priority-highest~, ~org-priority-lowest~, and
-~org-priority-default~. For an individual buffer, you may set these
-values (highest, lowest, default) like this (please make sure that the
+~org-priority-default~. Valid priority values are single uppercase
+Latin alphabetical characters A-Z, and positive integers in between 0
+and 64, inclusive. For an individual buffer, you may set these values
+(highest, lowest, default) like this (please make sure that the
highest priority is earlier in the alphabet than the lowest priority):
#+cindex: @samp{PRIORITIES}, keyword
diff --git a/lisp/org.el b/lisp/org.el
index 602fea261..d2aec1b33 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11296,14 +11296,66 @@ from the `before-change-functions' in the current buffer."
;;;; Priorities
-(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
+(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z]\\|[0-9]\\|[1-5][0-9]\\|6[0-4]\\)\\] ?\\)"
"Regular expression matching the priority indicator.
A priority indicator can be e.g. [#A] or [#1].
+The value of the priority cookie must be a capital latin
+alphabetic character, A-Z, or can be an integer value in
+the range 0-64.
This regular expression matches these groups:
0 : the whole match, e.g. \"TODO [#A] Hack\"
1 : the priority cookie, e.g. \"[#A]\"
2 : the value of the priority cookie, e.g. \"A\".")
+(defun org-priority-valid-cookiep (priority)
+ "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
+ (interactive "P")
+ (cond
+ ((stringp priority)
+ (let ((case-fold-search nil)) ;; Force case-sensitive match
+ ;; (and ... t) to force explicit t/nil
+ (and (string-match-p org-priority-regexp priority) t)))))
+
+(defun org-priority-valid-valuep (priority)
+ "Return t if the PRIORITY is a valid priority value (0-64, A-Z), nil otherwise."
+ (interactive "P")
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (integerp priority)
+ (or (and (>= priority 0)
+ (<= priority 64))
+ (and (>= priority ?A)
+ (<= priority ?Z)))))
+
+(defun org-priority-value-in-rangep (priority)
+ "Return t if the PRIORITY is a valid priority value that is in the range of
+ org-priority-lowest >= PRIORITY >= org-priority-highest."
+ (interactive "P")
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (org-priority-valid-valuep priority)
+ (<= priority org-priority-lowest)
+ (>= priority org-priority-highest)))
+
+(defun org-priority-numberp (priority)
+ "Return t if the PRIORITY is a number between 0 and 64, inclusive, return
+ nil otherwise."
+ (interactive "P")
+ (and (integerp priority)
+ (>= priority 0)
+ (<= priority 64)))
+
+(defun org-priority-to-string (priority)
+ "Returns a string form of PRIORITY based on whether it's numeric or alphabetic."
+ (interactive "P")
+ (if (org-priority-valid-valuep priority)
+ (if (org-priority-numberp priority)
+ (number-to-string priority)
+ (format "%c" priority))
+ (user-error "Invalid priority value `%s'" priority)))
+
(defun org-priority-up ()
"Increase the priority of the current item."
(interactive)
@@ -11320,57 +11372,72 @@ This regular expression matches these groups:
When called interactively with a `\\[universal-argument]' prefix,
show the priority in the minibuffer instead of changing it.
-When called programmatically, ACTION can be `set', `up', `down',
-or a character."
+When called programmatically, ACTION can be `set', `up', `down', an
+uppercase alphabetic character (A-Z), or an integer between [0,64],
+inclusive."
(interactive "P")
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
(user-error "Priority commands are disabled"))
+ ;; validation checks on our current range of priority values
+ (unless (org-priority-valid-valuep org-priority-lowest)
+ (user-error "Invalid org-priority-lowest: %s" org-priority-lowest))
+ (unless (org-priority-valid-valuep org-priority-highest)
+ (user-error "Invalid org-priority-highest: %s" org-priority-highest))
+ (unless (or (and (org-priority-numberp org-priority-highest)
+ (org-priority-numberp org-priority-lowest))
+ (and (not (org-priority-numberp org-priority-highest))
+ (not (org-priority-numberp org-priority-lowest))))
+ (user-error "Priority range highest/lowest must both be numeric or both be alphabetic: %s-%s"
+ org-priority-highest
+ org-priority-lowest))
+ ;; If action was not provided, default to "set", which will prompt the user
(setq action (or action 'set))
- (let ((nump (< org-priority-lowest 65))
- current new news have remove)
+ (let ((is_numeric_priority (org-priority-numberp org-priority-lowest))
+ current_value new_value new_value_string has_existing_cookie remove)
(save-excursion
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
- (setq current (org-priority-to-value ms)
- have t)))
+ (setq current_value (org-priority-to-value ms)
+ has_existing_cookie t)))
(cond
((eq action 'remove)
- (setq remove t new ?\ ))
+ (setq remove t new_value ?\ ))
+ ;; set and a value are treated similarly, but only if the
+ ;; value is a valid priority
((or (eq action 'set)
- (integerp action))
+ (org-priority-valid-valuep action))
(if (not (eq action 'set))
- (setq new action)
+ (setq new_value action)
(setq
- new
- (if nump
- (let* ((msg (format "Priority %s-%s, SPC to remove: "
- (number-to-string org-priority-highest)
- (number-to-string org-priority-lowest)))
- (s (if (< 9 org-priority-lowest)
+ new_value
+ (let* ((msg (format "Priority %s-%s, SPC to remove: "
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ (s (if (and is_numeric_priority
+ (< 9 org-priority-lowest))
(read-string msg)
- (message msg)
- (char-to-string (read-char-exclusive)))))
- (if (equal s " ") ?\s (string-to-number s)))
- (progn (message "Priority %c-%c, SPC to remove: "
- org-priority-highest org-priority-lowest)
- (save-match-data
- (setq new (read-char-exclusive)))))))
- (when (and (= (upcase org-priority-highest) org-priority-highest)
- (= (upcase org-priority-lowest) org-priority-lowest))
- (setq new (upcase new)))
- (cond ((equal new ?\s) (setq remove t))
- ((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
- (user-error
- (if nump
- "Priority must be between `%s' and `%s'"
- "Priority must be between `%c' and `%c'")
- org-priority-highest org-priority-lowest))))
+ (char-to-string (read-char-exclusive msg)))))
+ (if (equal s " ") ?\s (string-to-number s)))))
+ ;; The user might have given us garbage
+ (unless (org-priority-valid-valuep new_value)
+ (user-error "Invalid priority: `%s'" new_value))
+ ;; If we're using alphabetical priorities, we will force uppercase letters
+ (when (not is_numeric_priority)
+ (setq new_value (upcase new_value)))
+ ;; After reading interactive input, set removal flag if needed and
+ ;; perform validation on the new value
+ (cond
+ ((equal new_value ?\s) (setq remove t))
+ ((not (org-priority-value-in-rangep new_value))
+ (user-error "Priority must be between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))))
((eq action 'up)
- (setq new (if have
- (1- current) ; normal cycling
+ (setq new_value (if has_existing_cookie
+ (1- current_value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-lowest ; wrap around empty to lowest
@@ -11379,8 +11446,8 @@ or a character."
org-priority-default
(1- org-priority-default))))))
((eq action 'down)
- (setq new (if have
- (1+ current) ; normal cycling
+ (setq new_value (if has_existing_cookie
+ (1+ current_value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-highest ; wrap around empty to highest
@@ -11389,36 +11456,34 @@ or a character."
org-priority-default
(1+ org-priority-default))))))
(t (user-error "Invalid action")))
- (when (or (< (upcase new) org-priority-highest)
- (> (upcase new) org-priority-lowest))
+ ;; Check if we need to wrap
+ (when (not (org-priority-valid-valuep new_value))
(if (and (memq action '(up down))
- (not have) (not (eq last-command this-command)))
- ;; `new' is from default priority
+ (not has_existing_cookie) (not (eq last-command this-command)))
+ ;; `new_value' is from default priority
(error
"The default can not be set, see `org-priority-default' why")
- ;; normal cycling: `new' is beyond highest/lowest priority
+ ;; normal cycling: `new_value' is beyond highest/lowest priority
;; and is wrapped around to the empty priority
(setq remove t)))
- ;; Numerical priorities are limited to 64, beyond that number,
- ;; assume the priority cookie is a character.
- (setq news (if (> new 64) (format "%c" new) (format "%s" new)))
- (if have
+ (setq new_value_string (org-priority-to-string new_value))
+ (if has_existing_cookie
(if remove
(replace-match "" t t nil 1)
- (replace-match news t t nil 2))
+ (replace-match new_value_string t t nil 2))
(if remove
(user-error "No priority cookie found in line")
(let ((case-fold-search nil)) (looking-at org-todo-line-regexp))
(if (match-end 2)
(progn
(goto-char (match-end 2))
- (insert " [#" news "]"))
+ (insert " [#" new_value_string "]"))
(goto-char (match-beginning 3))
- (insert "[#" news "] "))))
+ (insert "[#" new_value_string "] "))))
(when org-auto-align-tags (org-align-tags)))
(if remove
(message "Priority removed")
- (message "Priority of current item set to %s" news)))))
+ (message "Priority of current item set to %s" new_value_string)))))
(defalias 'org-show-priority 'org-priority-show)
(defun org-priority-show ()
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 36dea35b7..0ec8fc2ff 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -1,4 +1,4 @@
-;;; test-org.el --- tests for org.el -*- lexical-binding: t -*-
+ ;;; test-org.el --- tests for org.el -*- lexical-binding: t -*-
;; Copyright (c) David Maus
;; Authors: David Maus
@@ -9847,6 +9847,70 @@ two
(test-org/extract-mathml-math
(org-create-math-formula "quote\" ; |"))))))
+;;; Priority validation and handling
+(ert-deftest test-org/priority-validation ()
+ "Test validation of priority cookies."
+ ;; Simple bounds checks on single alphabetic characters
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (org-priority-valid-cookiep cookie)))
+ (number-sequence ?A ?Z)))
+ ;; Test all valid numbers
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%d]" p)))
+ (org-priority-valid-cookiep cookie)))
+ (number-sequence 0 64)))
+ ;; Invalid characters (not exhaustive)
+ (should
+ (not (org-priority-valid-cookiep "[#$]")))
+ ;; Don't accept lower-case
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (not (org-priority-valid-cookiep cookie))))
+ (number-sequence ?a ?z)))
+ ;; Invalid numberic values (< 0 or > 64)
+ (should
+ (not (org-priority-valid-cookiep "[#-1]")))
+ (should
+ (not (org-priority-valid-cookiep "[#65]")))
+ ;; Value tests (as opposed to cookie tests)
+ ;;
+ ;; Numeric, full range
+ (should
+ (let ((org-priority-highest 0)
+ (org-priority-lowest 64))
+ (seq-every-p (lambda (pv) (and (org-priority-valid-valuep pv)
+ (org-priority-value-in-rangep pv)))
+ (number-sequence 0 64))))
+ ;; Numeric, valid value, but out of range
+ (should
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv)
+ (and (org-priority-valid-valuep pv)
+ (not (org-priority-value-in-rangep pv)))
+ )
+ '(0 5 9 21 42 64))))
+ ;; ;; Alphabetic, full range
+ (should
+ (let ((org-priority-highest ?A)
+ (org-priority-lowest ?Z))
+ (seq-every-p (lambda (pv) (and (org-priority-valid-valuep pv)
+ (org-priority-value-in-rangep pv)))
+ (number-sequence ?A ?Z))))
+ ;; Alphabetic, valid value, but out of range
+ (should
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv)
+ (and (org-priority-valid-valuep pv)
+ (not (org-priority-value-in-rangep pv))))
+ '(?A ?L ?N ?Z))))
+ )
+
(provide 'test-org)
;;; test-org.el ends here
--
2.43.0
[-- Attachment #3: 0001-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: text/x-patch, Size: 1349 bytes --]
From 5c6c1dd28b6447d24918412c1568a8317ac38c6c Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 29 Jul 2025 06:19:32 -0600
Subject: [PATCH 1/2] lisp/org.el: Remove deprecated show command
* org.el (org-priority): Remove the deprecated show command now that we're
several versions beyond when the deprecation was introduced.
---
lisp/org.el | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/lisp/org.el b/lisp/org.el
index 68ecb95f2..602fea261 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11314,7 +11314,7 @@ This regular expression matches these groups:
(interactive)
(org-priority 'down))
-(defun org-priority (&optional action show)
+(defun org-priority (&optional action)
"Change the priority of an item.
When called interactively with a `\\[universal-argument]' prefix,
@@ -11323,10 +11323,6 @@ show the priority in the minibuffer instead of changing it.
When called programmatically, ACTION can be `set', `up', `down',
or a character."
(interactive "P")
- (when show
- ;; Deprecation warning inserted for Org 9.2; once enough time has
- ;; passed the SHOW argument should be removed.
- (warn "`org-priority' called with deprecated SHOW argument"))
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-13 11:36 ` Derek Chen-Becker
@ 2025-09-13 14:39 ` Rudolf Adamkovič
2025-09-14 12:43 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Rudolf Adamkovič @ 2025-09-13 14:39 UTC (permalink / raw)
To: Derek Chen-Becker, Ihor Radchenko; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> If this looks reasonable I'll start working on the other parts of the
> codebase that deal with priorities, using the same helper functions.
Below are some comments from me.
> +must all be a positive integer between 0 and 64, inclusive.
"Positive integer between 0 and 64, inclusive" makes zero sense (pun
intended). I think you meant to write a non-negative integer. Zero is
not positive, yet it is listed and the sentence says "inclusive".
> +Latin alphabetical characters A-Z, and positive integers in between 0
> +and 64, inclusive.
Ditto.
> +The value of the priority cookie must be a capital latin
^^^^^
Latin
> +alphabetic character, A-Z, or can be an integer value in
> +the range 0-64.
^^^^
0 through 64 (perhaps)
"through" implies "inclusive", unlike "to"
> +(defun org-priority-valid-cookiep (priority)
^^^^^^^
cookie-p
> +(defun org-priority-valid-valuep (priority)
^^^^^^
value-p
> +(defun org-priority-value-in-rangep (priority)
^^^^^^
range-p
> + "Return t if the PRIORITY is a valid priority value that is in the
^^^^^^^
useless words
> + org-priority-lowest >= PRIORITY >= org-priority-highest."
^^ ^^
<= <=
> + (<= priority org-priority-lowest)
> + (>= priority org-priority-highest)))
(<= org-priority-lowest priority org-priority-highest)
> +(defun org-priority-numberp (priority)
^^^^^^^
number-p
> + "Return t if the PRIORITY is a number between 0 and 64, inclusive,
^^^^^^
integer
> + (>= priority 0)
> + (<= priority 64)))
(<= 0 priority 64)
> +uppercase alphabetic character (A-Z), or an integer between [0,64],
^^^^^^^
in
> + (let ((is_numeric_priority (org-priority-numberp
^^^^^^^^^^^^^^^^^^^
is-numeric-priority
> + (setq remove t new_value ?\ ))
^^^^^^^^^
new-value
> + (setq new_value_string (org-priority-to-string new_value))
^^^^^^^^^^^^^^^^
new-value-string
> + (if has_existing_cookie
^^^^^^^^^^^^^^^^^^^
has-existing-cookie
> + )
^
lonely parens (in the tests)
Rudy
--
"If you're thinking without writing, you only think you're thinking."
--- Leslie Lamport
Rudolf Adamkovič <[email protected]> [he/him]
http://adamkovic.org
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-13 14:39 ` Rudolf Adamkovič
@ 2025-09-14 12:43 ` Derek Chen-Becker
2025-09-15 7:12 ` Rudolf Adamkovič
2025-09-27 13:24 ` Ihor Radchenko
0 siblings, 2 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-09-14 12:43 UTC (permalink / raw)
To: Rudolf Adamkovič; +Cc: Ihor Radchenko, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 4232 bytes --]
Thanks for the feedback! I've attached the updated patches. One note on the
comments:
> + (<= priority org-priority-lowest)
> + (>= priority org-priority-highest)))
(<= org-priority-lowest priority org-priority-highest)
needs to be
(>= org-priority-lowest priority org-priority-highest)
because org-priority-lowest > org-priority-highest.
Cheers,
Derek
On Sat, Sep 13, 2025 at 8:39 AM Rudolf Adamkovič <[email protected]>
wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > If this looks reasonable I'll start working on the other parts of the
> > codebase that deal with priorities, using the same helper functions.
>
> Below are some comments from me.
>
> > +must all be a positive integer between 0 and 64, inclusive.
>
> "Positive integer between 0 and 64, inclusive" makes zero sense (pun
> intended). I think you meant to write a non-negative integer. Zero is
> not positive, yet it is listed and the sentence says "inclusive".
>
> > +Latin alphabetical characters A-Z, and positive integers in between 0
> > +and 64, inclusive.
>
> Ditto.
>
> > +The value of the priority cookie must be a capital latin
> ^^^^^
> Latin
>
> > +alphabetic character, A-Z, or can be an integer value in
> > +the range 0-64.
> ^^^^
> 0 through 64 (perhaps)
> "through" implies "inclusive", unlike "to"
>
> > +(defun org-priority-valid-cookiep (priority)
> ^^^^^^^
> cookie-p
>
> > +(defun org-priority-valid-valuep (priority)
> ^^^^^^
> value-p
>
> > +(defun org-priority-value-in-rangep (priority)
> ^^^^^^
> range-p
>
> > + "Return t if the PRIORITY is a valid priority value that is in the
> ^^^^^^^
> useless words
>
> > + org-priority-lowest >= PRIORITY >= org-priority-highest."
> ^^ ^^
> <= <=
>
> > + (<= priority org-priority-lowest)
> > + (>= priority org-priority-highest)))
>
> (<= org-priority-lowest priority org-priority-highest)
>
> > +(defun org-priority-numberp (priority)
> ^^^^^^^
> number-p
>
> > + "Return t if the PRIORITY is a number between 0 and 64, inclusive,
> ^^^^^^
> integer
>
> > + (>= priority 0)
> > + (<= priority 64)))
>
> (<= 0 priority 64)
>
> > +uppercase alphabetic character (A-Z), or an integer between [0,64],
> ^^^^^^^
> in
>
> > + (let ((is_numeric_priority (org-priority-numberp
> ^^^^^^^^^^^^^^^^^^^
> is-numeric-priority
>
> > + (setq remove t new_value ?\ ))
> ^^^^^^^^^
> new-value
>
> > + (setq new_value_string (org-priority-to-string new_value))
> ^^^^^^^^^^^^^^^^
> new-value-string
>
> > + (if has_existing_cookie
> ^^^^^^^^^^^^^^^^^^^
> has-existing-cookie
> > + )
> ^
> lonely parens (in the tests)
>
> Rudy
> --
> "If you're thinking without writing, you only think you're thinking."
> --- Leslie Lamport
>
> Rudolf Adamkovič <[email protected]> [he/him]
> http://adamkovic.org
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 8428 bytes --]
[-- Attachment #2: 0002-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: text/x-patch, Size: 16051 bytes --]
From d73323e2cd1a8592f185ba2da845882fd8985378 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Sat, 6 Sep 2025 07:07:34 -0600
Subject: [PATCH 2/2] lisp/org.el: Add proper support for numeric priorities
* doc/org-manual.org: Update the manual to clarify the allowed values for
priorities.
* lisp/org.el (org-priority-regexp): Update the priority cookie regular
expression to fully validate numeric values, and update the documentation
to match.
(org-priority-valid-cookiep, org-priority-valid-valuep,
org-priority-value-in-rangep, org-priority-numberp): Add validation
predicates to test priority cookies and priority values.
(org-priority-to-string): Add a function to provide a consistent string
value from a given priority value.
(org-priority): Refactor to apply more validation and use the new
predicates and functions defined in this commit to simplify logic and make
things more consistent, particularly around double-digit numeric values.
* testing/lisp/test-org.el: Add unit tests for the new predicats and format
functions related to priority.
---
doc/org-manual.org | 8 +-
lisp/org.el | 163 +++++++++++++++++++++++++++------------
testing/lisp/test-org.el | 66 +++++++++++++++-
3 files changed, 183 insertions(+), 54 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 8c6962d4b..a86024cd9 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4626,7 +4626,7 @@ You can also use numeric values for priorities, such as
When using numeric priorities, you need to set ~org-priority-highest~,
~org-priority-lowest~ and ~org-priority-default~ to integers, which
-must all be strictly inferior to 65.
+must all be a non-negative integer between 0 and 64, inclusive.
Priorities can be attached to any heading; they do not need to be
TODO items.
@@ -4661,8 +4661,10 @@ TODO items.
#+vindex: org-priority-default
You can change the range of allowed priorities by setting the
variables ~org-priority-highest~, ~org-priority-lowest~, and
-~org-priority-default~. For an individual buffer, you may set these
-values (highest, lowest, default) like this (please make sure that the
+~org-priority-default~. Valid priority values are single uppercase
+Latin alphabetical characters A-Z, and non-negative integers in between 0
+and 64, inclusive. For an individual buffer, you may set these values
+(highest, lowest, default) like this (please make sure that the
highest priority is earlier in the alphabet than the lowest priority):
#+cindex: @samp{PRIORITIES}, keyword
diff --git a/lisp/org.el b/lisp/org.el
index 602fea261..694454978 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11296,14 +11296,64 @@ from the `before-change-functions' in the current buffer."
;;;; Priorities
-(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
+(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z]\\|[0-9]\\|[1-5][0-9]\\|6[0-4]\\)\\] ?\\)"
"Regular expression matching the priority indicator.
A priority indicator can be e.g. [#A] or [#1].
+The value of the priority cookie must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in
+the range 0 through 64.
This regular expression matches these groups:
0 : the whole match, e.g. \"TODO [#A] Hack\"
1 : the priority cookie, e.g. \"[#A]\"
2 : the value of the priority cookie, e.g. \"A\".")
+(defun org-priority-valid-cookie-p (priority)
+ "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
+ (interactive "P")
+ (cond
+ ((stringp priority)
+ (let ((case-fold-search nil)) ;; Force case-sensitive match
+ ;; (and ... t) to force explicit t/nil
+ (and (string-match-p org-priority-regexp priority) t)))))
+
+(defun org-priority-valid-value-p (priority)
+ "Return t if the PRIORITY is a valid priority value (0-64, A-Z), nil otherwise."
+ (interactive "P")
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (integerp priority)
+ (or (and (>= priority 0)
+ (<= priority 64))
+ (and (>= priority ?A)
+ (<= priority ?Z)))))
+
+(defun org-priority-value-in-range-p (priority)
+ "Return t if the PRIORITY is a value that is in the range of
+ org-priority-lowest <= PRIORITY <= org-priority-highest."
+ (interactive "P")
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (org-priority-valid-value-p priority)
+ (>= org-priority-lowest priority org-priority-highest)))
+
+(defun org-priority-number-p (priority)
+ "Return t if the PRIORITY is an integer 0 through 64, inclusive, return
+ nil otherwise."
+ (interactive "P")
+ (and (integerp priority)
+ (<= 0 priority 64)))
+
+(defun org-priority-to-string (priority)
+ "Returns a string form of PRIORITY based on whether it's numeric or alphabetic."
+ (interactive "P")
+ (if (org-priority-valid-value-p priority)
+ (if (org-priority-number-p priority)
+ (number-to-string priority)
+ (format "%c" priority))
+ (user-error "Invalid priority value `%s'" priority)))
+
(defun org-priority-up ()
"Increase the priority of the current item."
(interactive)
@@ -11320,57 +11370,72 @@ This regular expression matches these groups:
When called interactively with a `\\[universal-argument]' prefix,
show the priority in the minibuffer instead of changing it.
-When called programmatically, ACTION can be `set', `up', `down',
-or a character."
+When called programmatically, ACTION can be `set', `up', `down', an
+uppercase alphabetic character A through Z, or an integer 0 through 64,
+inclusive."
(interactive "P")
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
(user-error "Priority commands are disabled"))
+ ;; validation checks on our current range of priority values
+ (unless (org-priority-valid-value-p org-priority-lowest)
+ (user-error "Invalid org-priority-lowest: %s" org-priority-lowest))
+ (unless (org-priority-valid-value-p org-priority-highest)
+ (user-error "Invalid org-priority-highest: %s" org-priority-highest))
+ (unless (or (and (org-priority-number-p org-priority-highest)
+ (org-priority-number-p org-priority-lowest))
+ (and (not (org-priority-number-p org-priority-highest))
+ (not (org-priority-number-p org-priority-lowest))))
+ (user-error "Priority range highest/lowest must both be numeric or both be alphabetic: %s-%s"
+ org-priority-highest
+ org-priority-lowest))
+ ;; If action was not provided, default to "set", which will prompt the user
(setq action (or action 'set))
- (let ((nump (< org-priority-lowest 65))
- current new news have remove)
+ (let ((is-numeric-priority (org-priority-number-p org-priority-lowest))
+ current-value new-value new-value-string has-existing-cookie remove)
(save-excursion
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
- (setq current (org-priority-to-value ms)
- have t)))
+ (setq current-value (org-priority-to-value ms)
+ has-existing-cookie t)))
(cond
((eq action 'remove)
- (setq remove t new ?\ ))
+ (setq remove t new-value ?\ ))
+ ;; set and a value are treated similarly, but only if the
+ ;; value is a valid priority
((or (eq action 'set)
- (integerp action))
+ (org-priority-valid-value-p action))
(if (not (eq action 'set))
- (setq new action)
+ (setq new-value action)
(setq
- new
- (if nump
- (let* ((msg (format "Priority %s-%s, SPC to remove: "
- (number-to-string org-priority-highest)
- (number-to-string org-priority-lowest)))
- (s (if (< 9 org-priority-lowest)
+ new-value
+ (let* ((msg (format "Priority %s-%s, SPC to remove: "
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ (s (if (and is-numeric-priority
+ (< 9 org-priority-lowest))
(read-string msg)
- (message msg)
- (char-to-string (read-char-exclusive)))))
- (if (equal s " ") ?\s (string-to-number s)))
- (progn (message "Priority %c-%c, SPC to remove: "
- org-priority-highest org-priority-lowest)
- (save-match-data
- (setq new (read-char-exclusive)))))))
- (when (and (= (upcase org-priority-highest) org-priority-highest)
- (= (upcase org-priority-lowest) org-priority-lowest))
- (setq new (upcase new)))
- (cond ((equal new ?\s) (setq remove t))
- ((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
- (user-error
- (if nump
- "Priority must be between `%s' and `%s'"
- "Priority must be between `%c' and `%c'")
- org-priority-highest org-priority-lowest))))
+ (char-to-string (read-char-exclusive msg)))))
+ (if (equal s " ") ?\s (string-to-number s)))))
+ ;; The user might have given us garbage
+ (unless (org-priority-valid-value-p new-value)
+ (user-error "Invalid priority: `%s'" new-value))
+ ;; If we're using alphabetical priorities, we will force uppercase letters
+ (when (not is-numeric-priority)
+ (setq new-value (upcase new-value)))
+ ;; After reading interactive input, set removal flag if needed and
+ ;; perform validation on the new value
+ (cond
+ ((equal new-value ?\s) (setq remove t))
+ ((not (org-priority-value-in-range-p new-value))
+ (user-error "Priority must be between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))))
((eq action 'up)
- (setq new (if have
- (1- current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1- current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-lowest ; wrap around empty to lowest
@@ -11379,8 +11444,8 @@ or a character."
org-priority-default
(1- org-priority-default))))))
((eq action 'down)
- (setq new (if have
- (1+ current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1+ current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-highest ; wrap around empty to highest
@@ -11389,36 +11454,34 @@ or a character."
org-priority-default
(1+ org-priority-default))))))
(t (user-error "Invalid action")))
- (when (or (< (upcase new) org-priority-highest)
- (> (upcase new) org-priority-lowest))
+ ;; Check if we need to wrap
+ (when (not (org-priority-valid-value-p new-value))
(if (and (memq action '(up down))
- (not have) (not (eq last-command this-command)))
- ;; `new' is from default priority
+ (not has-existing-cookie) (not (eq last-command this-command)))
+ ;; `new-value' is from default priority
(error
"The default can not be set, see `org-priority-default' why")
- ;; normal cycling: `new' is beyond highest/lowest priority
+ ;; normal cycling: `new-value' is beyond highest/lowest priority
;; and is wrapped around to the empty priority
(setq remove t)))
- ;; Numerical priorities are limited to 64, beyond that number,
- ;; assume the priority cookie is a character.
- (setq news (if (> new 64) (format "%c" new) (format "%s" new)))
- (if have
+ (setq new-value-string (org-priority-to-string new-value))
+ (if has-existing-cookie
(if remove
(replace-match "" t t nil 1)
- (replace-match news t t nil 2))
+ (replace-match new-value-string t t nil 2))
(if remove
(user-error "No priority cookie found in line")
(let ((case-fold-search nil)) (looking-at org-todo-line-regexp))
(if (match-end 2)
(progn
(goto-char (match-end 2))
- (insert " [#" news "]"))
+ (insert " [#" new-value-string "]"))
(goto-char (match-beginning 3))
- (insert "[#" news "] "))))
+ (insert "[#" new-value-string "] "))))
(when org-auto-align-tags (org-align-tags)))
(if remove
(message "Priority removed")
- (message "Priority of current item set to %s" news)))))
+ (message "Priority of current item set to %s" new-value-string)))))
(defalias 'org-show-priority 'org-priority-show)
(defun org-priority-show ()
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 36dea35b7..014e7593c 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -1,4 +1,4 @@
-;;; test-org.el --- tests for org.el -*- lexical-binding: t -*-
+ ;;; test-org.el --- tests for org.el -*- lexical-binding: t -*-
;; Copyright (c) David Maus
;; Authors: David Maus
@@ -9847,6 +9847,70 @@ two
(test-org/extract-mathml-math
(org-create-math-formula "quote\" ; |"))))))
+;;; Priority validation and handling
+(ert-deftest test-org/priority-validation ()
+ "Test validation of priority cookies."
+ ;; Simple bounds checks on single alphabetic characters
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (org-priority-valid-cookie-p cookie)))
+ (number-sequence ?A ?Z)))
+ ;; Test all valid numbers
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%d]" p)))
+ (org-priority-valid-cookie-p cookie)))
+ (number-sequence 0 64)))
+ ;; Invalid characters (not exhaustive)
+ (should
+ (not (org-priority-valid-cookie-p "[#$]")))
+ ;; Don't accept lower-case
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (not (org-priority-valid-cookie-p cookie))))
+ (number-sequence ?a ?z)))
+ ;; Invalid numberic values (< 0 or > 64)
+ (should
+ (not (org-priority-valid-cookie-p "[#-1]")))
+ (should
+ (not (org-priority-valid-cookie-p "[#65]")))
+ ;; Value tests (as opposed to cookie tests)
+ ;;
+ ;; Numeric, full range
+ (should
+ (let ((org-priority-highest 0)
+ (org-priority-lowest 64))
+ (seq-every-p (lambda (pv) (and (org-priority-valid-value-p pv)
+ (org-priority-value-in-range-p pv)))
+ (number-sequence 0 64))))
+ ;; Numeric, valid value, but out of range
+ (should
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv)
+ (and (org-priority-valid-value-p pv)
+ (not (org-priority-value-in-range-p pv)))
+ )
+ '(0 5 9 21 42 64))))
+ ;; ;; Alphabetic, full range
+ (should
+ (let ((org-priority-highest ?A)
+ (org-priority-lowest ?Z))
+ (seq-every-p (lambda (pv) (and (org-priority-valid-value-p pv)
+ (org-priority-value-in-range-p pv)))
+ (number-sequence ?A ?Z))))
+ ;; Alphabetic, valid value, but out of range
+ (should
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv)
+ (and (org-priority-valid-value-p pv)
+ (not (org-priority-value-in-range-p pv))))
+ '(?A ?L ?N ?Z)))))
+
(provide 'test-org)
;;; test-org.el ends here
+
--
2.43.0
[-- Attachment #3: 0001-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: text/x-patch, Size: 1349 bytes --]
From 5c6c1dd28b6447d24918412c1568a8317ac38c6c Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 29 Jul 2025 06:19:32 -0600
Subject: [PATCH 1/2] lisp/org.el: Remove deprecated show command
* org.el (org-priority): Remove the deprecated show command now that we're
several versions beyond when the deprecation was introduced.
---
lisp/org.el | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/lisp/org.el b/lisp/org.el
index 68ecb95f2..602fea261 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11314,7 +11314,7 @@ This regular expression matches these groups:
(interactive)
(org-priority 'down))
-(defun org-priority (&optional action show)
+(defun org-priority (&optional action)
"Change the priority of an item.
When called interactively with a `\\[universal-argument]' prefix,
@@ -11323,10 +11323,6 @@ show the priority in the minibuffer instead of changing it.
When called programmatically, ACTION can be `set', `up', `down',
or a character."
(interactive "P")
- (when show
- ;; Deprecation warning inserted for Org 9.2; once enough time has
- ;; passed the SHOW argument should be removed.
- (warn "`org-priority' called with deprecated SHOW argument"))
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-14 12:43 ` Derek Chen-Becker
@ 2025-09-15 7:12 ` Rudolf Adamkovič
2025-09-27 13:24 ` Ihor Radchenko
1 sibling, 0 replies; 81+ messages in thread
From: Rudolf Adamkovič @ 2025-09-15 7:12 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Ihor Radchenko, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> [...] because org-priority-lowest > org-priority-highest.
Just checked the docs, and you are right! What a confusing API, mixing
ordinals with characters ... mathematics be damned. Ugh!
Rudy
--
"It is far better to have a question that can't be answered than an
answer that can't be questioned." --- Carl Sagan
Rudolf Adamkovič <[email protected]> [he/him]
http://adamkovic.org
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-14 12:43 ` Derek Chen-Becker
2025-09-15 7:12 ` Rudolf Adamkovič
@ 2025-09-27 13:24 ` Ihor Radchenko
2025-09-28 14:20 ` Derek Chen-Becker
1 sibling, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-09-27 13:24 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> (org-priority): Refactor to apply more validation and use the new
> predicates and functions defined in this commit to simplify logic and make
> things more consistent, particularly around double-digit numeric values.
It would be helpful if you listed what exactly changed compared to
previous situation.
> +(defun org-priority-valid-cookie-p (priority)
> + "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
If I just look at the functions names `org-priority-valid-cookie-p' and
`org-priority-valid-value-p', I cannot easily tell how "cookie" is
different from "value". At least, the function names should be more
intuitive. Or maybe we can even merge the two functions into 1.
> + (interactive "P")
Why is the function interactive? I do not see how it can be used by
users in any useful way. Same for all other predicate functions you added.
> +(defun org-priority-value-in-range-p (priority)
> + "Return t if the PRIORITY is a value that is in the range of
> + org-priority-lowest <= PRIORITY <= org-priority-highest."
Note that this docstring does not follow the conventions: (1) First line
should be a complete sentence; (2) Symbols inside docstring should be `quoted'.
Also, the function name reads weird. Usually, when I see "in-range", I
expect the range to be passed as argument. More importantly, I am not
sure why this should be distinct from `org-priority-valid-value-p'. If
the value is outside the lowest/highest priority, is it valid?
> + ;; validation checks on our current range of priority values
> + (unless (org-priority-valid-value-p org-priority-lowest)
> + (user-error "Invalid org-priority-lowest: %s" org-priority-lowest))
> + (unless (org-priority-valid-value-p org-priority-highest)
> + (user-error "Invalid org-priority-highest: %s" org-priority-highest))
> + (unless (or (and (org-priority-number-p org-priority-highest)
> + (org-priority-number-p org-priority-lowest))
> + (and (not (org-priority-number-p org-priority-highest))
> + (not (org-priority-number-p org-priority-lowest))))
> + (user-error "Priority range highest/lowest must both be numeric or both be alphabetic: %s-%s"
> + org-priority-highest
> + org-priority-lowest))
It is not a job for runtime to check user customization. I think that it
will be better to write a proper defcustom type specification, so that
Emacs automatically warn users about invalid customization value.
> + (s (if (and is-numeric-priority
> + (< 9 org-priority-lowest))
> (read-string msg)
> - (message msg)
Any reason you remove this message?
> + ;; The user might have given us garbage
> + (unless (org-priority-valid-value-p new-value)
> + (user-error "Invalid priority: `%s'" new-value))
> + ;; If we're using alphabetical priorities, we will force uppercase letters
> + (when (not is-numeric-priority)
> + (setq new-value (upcase new-value)))
This reads slightly confusing as it is a hidden feature where passing ?a
when priority range is, say ?A..?C will automatically convert it to ?A.
We should probably explicitly say this in the docstring.
> + ;; Check if we need to wrap
> + (when (not (org-priority-valid-value-p new-value))
But does it check for wrapping?
`org-priority-valid-value-p' checks for the whole 0..64,?A..?Z range,
not for wrapping around the allowed range.
> --- a/testing/lisp/test-org.el
> +++ b/testing/lisp/test-org.el
> @@ -1,4 +1,4 @@
> -;;; test-org.el --- tests for org.el -*- lexical-binding: t -*-
> + ;;; test-org.el --- tests for org.el -*- lexical-binding: t -*-
Stray whitespace change.
> From 5c6c1dd28b6447d24918412c1568a8317ac38c6c Mon Sep 17 00:00:00 2001
> From: Derek Chen-Becker <[email protected]>
> Date: Tue, 29 Jul 2025 06:19:32 -0600
> Subject: [PATCH 1/2] lisp/org.el: Remove deprecated show command
>
> * org.el (org-priority): Remove the deprecated show command now that we're
> several versions beyond when the deprecation was introduced.
> ---
> lisp/org.el | 6 +-----
> 1 file changed, 1 insertion(+), 5 deletions(-)
When we remove deprecated command, we should announce it in the news.
It is a breaking change (even if it has been announced in the past).
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-27 13:24 ` Ihor Radchenko
@ 2025-09-28 14:20 ` Derek Chen-Becker
2025-09-28 16:12 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-09-28 14:20 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 5094 bytes --]
On Sat, Sep 27, 2025 at 7:24 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > (org-priority): Refactor to apply more validation and use the new
> > predicates and functions defined in this commit to simplify logic and
> make
> > things more consistent, particularly around double-digit numeric values.
>
> It would be helpful if you listed what exactly changed compared to
> previous situation.
>
OK, let me try a different approach to how I detail the changes.
> If I just look at the functions names `org-priority-valid-cookie-p' and
> `org-priority-valid-value-p', I cannot easily tell how "cookie" is
> different from "value". At least, the function names should be more
> intuitive. Or maybe we can even merge the two functions into 1.
>
"Cookie" is the term used throughout the rest of the codebase for the
string with open/close square brackets (e.g. "[#A]"), as opposed to "value"
which is the part inside the brackets following the hash). I was trying to
be consistent with existing naming. I'm open to suggestions for alternative
nomenclature, but it seemed like a well-established pattern.
> Why is the function interactive? I do not see how it can be used by
> users in any useful way. Same for all other predicate functions you added.
>
Mostly for testing on my part, I can remove this. For my own knowledge, is
there a performance or other drawback for functions that are marked
"interactive" over ones that aren't, or is this just a stylistic concern?
> Also, the function name reads weird. Usually, when I see "in-range", I
> expect the range to be passed as argument. More importantly, I am not
> sure why this should be distinct from `org-priority-valid-value-p'. If
> the value is outside the lowest/highest priority, is it valid?
>
This was actually needed for a later part of the code that you commented
on, so I'll continue that discussion around the wrapping check.
> It is not a job for runtime to check user customization. I think that it
> will be better to write a proper defcustom type specification, so that
> Emacs automatically warn users about invalid customization value.
>
Do you have an example of a defcustom that does that? I tried looking at
the docs for defcustom and didn't see an obvious way to do that. I also
couldn't find an example in the codebase that seemed to do any validation.
> > + (s (if (and is-numeric-priority
> > + (< 9 org-priority-lowest))
> > (read-string msg)
> > - (message msg)
>
> Any reason you remove this message?
>
It looked like a leftover debug statement added in
https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/lisp/org.el?id=b91e934a59f0da839c7fc5680d45ce1ddc1b718a.
There's no comment explaining why we would echo back what a user just
entered to them. and the message was not there prior to the change where we
ask for a single character rather than a string. Is Bastien still around so
we can ask him?
> This reads slightly confusing as it is a hidden feature where passing ?a
> when priority range is, say ?A..?C will automatically convert it to ?A.
> We should probably explicitly say this in the docstring.
>
This is only intended to apply to a value where the user has interactively
provided a new value via the interactive read of a string, and it matches
what we were doing already in this code, which is not commented but
effectively was checking to see if we're using alphabetic priorities, and
if so converts to upper case:
(when (and (= (upcase org-priority-highest) org-priority-highest)
(= (upcase org-priority-lowest) org-priority-lowest))
(setq new (upcase new)))
This should not have any effect on a priority value passed to the function.
>
> > + ;; Check if we need to wrap
> > + (when (not (org-priority-valid-value-p new-value))
>
> But does it check for wrapping?
> `org-priority-valid-value-p' checks for the whole 0..64,?A..?Z range,
> not for wrapping around the allowed range.
>
Good call, this is where I would need the predicate that checks for a value
within the valid range. Do you have a suggestion for a different name for
that predicate than org-priority-value-in-range-p?
> When we remove deprecated command, we should announce it in the news.
> It is a breaking change (even if it has been announced in the past).
>
OK, how do I make that announcement? It don't see a NEWS file.
Thanks for the feedback!
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 9975 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-28 14:20 ` Derek Chen-Becker
@ 2025-09-28 16:12 ` Ihor Radchenko
2025-09-28 16:53 ` Derek Chen-Becker
` (2 more replies)
0 siblings, 3 replies; 81+ messages in thread
From: Ihor Radchenko @ 2025-09-28 16:12 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
>> If I just look at the functions names `org-priority-valid-cookie-p' and
>> `org-priority-valid-value-p', I cannot easily tell how "cookie" is
>> different from "value". At least, the function names should be more
>> intuitive. Or maybe we can even merge the two functions into 1.
>
> "Cookie" is the term used throughout the rest of the codebase for the
> string with open/close square brackets (e.g. "[#A]"), as opposed to "value"
> which is the part inside the brackets following the hash). I was trying to
> be consistent with existing naming. I'm open to suggestions for alternative
> nomenclature, but it seemed like a well-established pattern.
Fair. Still, I think highlighting that cookie is a string would be
helpful: `org-priority-valid-cookie-string-p' or so.
>> Why is the function interactive? I do not see how it can be used by
>> users in any useful way. Same for all other predicate functions you added.
>
> Mostly for testing on my part, I can remove this. For my own knowledge, is
> there a performance or other drawback for functions that are marked
> "interactive" over ones that aren't, or is this just a stylistic concern?
The main drawback is that interactive functions will show up in M-x
completions and may confuse users. Emacs generally moves towards making
M-x list more specific, only listing the immediately useful commands in
current context (see the new `execute-extended-command-for-buffer'). So,
adding interactive functions that will not be useful is not a good idea
IMHO.
>> It is not a job for runtime to check user customization. I think that it
>> will be better to write a proper defcustom type specification, so that
>> Emacs automatically warn users about invalid customization value.
>
> Do you have an example of a defcustom that does that? I tried looking at
> the docs for defcustom and didn't see an obvious way to do that. I also
> couldn't find an example in the codebase that seemed to do any validation.
`org-property-separators'. Also see restricted-sexp customization type
described in 15.4.2 Composite Types section of Elisp manual.
>> > + (s (if (and is-numeric-priority
>> > + (< 9 org-priority-lowest))
>> > (read-string msg)
>> > - (message msg)
>>
>> Any reason you remove this message?
>
> It looked like a leftover debug statement added in
> https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/lisp/org.el?id=b91e934a59f0da839c7fc5680d45ce1ddc1b718a.
> There's no comment explaining why we would echo back what a user just
> entered to them. and the message was not there prior to the change where we
> ask for a single character rather than a string. Is Bastien still around so
> we can ask him?
There is no echoing back. What is done in first branch of the if is
`read-string' that (1) displays MSG; (2) reads user input.
The other branch (that you modified) (1) displays MSG; (2) reads user
input as character. We display a message to tell user what to
input. After your change, "Priority A-C, SPC to remove: " message will
not be shown.
That's why I ask to list every significant change in the patch. Spelling
it out (1) helps yourself to formulate why the change was made; (2)
helps other to understand why it was made.
>> This reads slightly confusing as it is a hidden feature where passing ?a
>> when priority range is, say ?A..?C will automatically convert it to ?A.
>> We should probably explicitly say this in the docstring.
>
> This is only intended to apply to a value where the user has interactively
> provided a new value via the interactive read of a string
> ...
> This should not have any effect on a priority value passed to the function.
But it will, won't it? It is in the same branch of code that assigns the
value of ACTION to NEW-VALUE.
> ... , and it matches
> what we were doing already in this code, which is not commented but
> effectively was checking to see if we're using alphabetic priorities, and
> if so converts to upper case:
>
> (when (and (= (upcase org-priority-highest) org-priority-highest)
> (= (upcase org-priority-lowest) org-priority-lowest))
> (setq new (upcase new)))
Sure. I do not oppose this. Just wanted you to document this behavior in
the docstring explicitly.
>>
>> > + ;; Check if we need to wrap
>> > + (when (not (org-priority-valid-value-p new-value))
>>
>> But does it check for wrapping?
>> `org-priority-valid-value-p' checks for the whole 0..64,?A..?Z range,
>> not for wrapping around the allowed range.
>
> Good call, this is where I would need the predicate that checks for a value
> within the valid range. Do you have a suggestion for a different name for
> that predicate than org-priority-value-in-range-p?
What about making `org-priority-valid-value-p' check the range as well?
If an option to ignore the range is necessary, it can be activated by
passing an optional argument.
>> When we remove deprecated command, we should announce it in the news.
>> It is a breaking change (even if it has been announced in the past).
>>
> OK, how do I make that announcement? It don't see a NEWS file.
/etc/ORG-NEWS
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-28 16:12 ` Ihor Radchenko
@ 2025-09-28 16:53 ` Derek Chen-Becker
2025-09-28 18:46 ` Derek Chen-Becker
2025-10-06 4:01 ` Derek Chen-Becker
2 siblings, 0 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-09-28 16:53 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2274 bytes --]
On Sun, Sep 28, 2025 at 10:12 AM Ihor Radchenko <[email protected]> wrote:
>
> Fair. Still, I think highlighting that cookie is a string would be
> helpful: `org-priority-valid-cookie-string-p' or so.
>
Agreed, that makes sense. I'll change the name.
> The main drawback is that interactive functions will show up in M-x
> completions and may confuse users. Emacs generally moves towards making
> M-x list more specific, only listing the immediately useful commands in
> current context (see the new `execute-extended-command-for-buffer'). So,
> adding interactive functions that will not be useful is not a good idea
> IMHO.
>
OK, that makes sense, I didn't realize it. I'll remove the `interactive'
from all of the functions that don't actually need to be interactive.
>
> `org-property-separators'. Also see restricted-sexp customization type
> described in 15.4.2 Composite Types section of Elisp manual.
>
Cool, thanks for the pointers. I'll take a look and see what I can do.
>
> There is no echoing back. What is done in first branch of the if is
> `read-string' that (1) displays MSG; (2) reads user input.
> The other branch (that you modified) (1) displays MSG; (2) reads user
> input as character. We display a message to tell user what to
> input. After your change, "Priority A-C, SPC to remove: " message will
> not be shown.
>
Got it, thanks.
>
> Sure. I do not oppose this. Just wanted you to document this behavior in
> the docstring explicitly.
>
Makes sense, I'll update the docstring.
> What about making `org-priority-valid-value-p' check the range as well?
> If an option to ignore the range is necessary, it can be activated by
> passing an optional argument.
>
OK, I can make that change.
>
> /etc/ORG-NEWS
>
Thanks,I'll update that.
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 5861 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-28 16:12 ` Ihor Radchenko
2025-09-28 16:53 ` Derek Chen-Becker
@ 2025-09-28 18:46 ` Derek Chen-Becker
2025-10-06 4:01 ` Derek Chen-Becker
2 siblings, 0 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-09-28 18:46 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]
On Sun, Sep 28, 2025 at 10:12 AM Ihor Radchenko <[email protected]> wrote:
> There is no echoing back. What is done in first branch of the if is
> `read-string' that (1) displays MSG; (2) reads user input.
> The other branch (that you modified) (1) displays MSG; (2) reads user
> input as character. We display a message to tell user what to
> input. After your change, "Priority A-C, SPC to remove: " message will
> not be shown.
>
Actually, I remembered why I had removed the `message' call. Both
`read-string' and `read-char-exclusive' are given the `msg' value as their
prompt argument, so they already both emit the prompt. I agree that I
should have included this in the changelog.
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 2752 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-09-28 16:12 ` Ihor Radchenko
2025-09-28 16:53 ` Derek Chen-Becker
2025-09-28 18:46 ` Derek Chen-Becker
@ 2025-10-06 4:01 ` Derek Chen-Becker
2025-10-11 12:51 ` Ihor Radchenko
2 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-10-06 4:01 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2734 bytes --]
On Sun, Sep 28, 2025 at 10:12 AM Ihor Radchenko <[email protected]> wrote:
> > Do you have an example of a defcustom that does that? I tried looking at
> > the docs for defcustom and didn't see an obvious way to do that. I also
> > couldn't find an example in the codebase that seemed to do any
> validation.
>
> `org-property-separators'. Also see restricted-sexp customization type
> described in 15.4.2 Composite Types section of Elisp manual.
>
I think I have most of the patch fixed up based on the other feedback, but
I'm having some difficulty with customization. I think I have the
customization correct, but I can't figure out how to test that validation
operates on any values. I don't see anything in the current unit tests for
`org-property-separators' to reject bad values or otherwise validate
values. Here's what I currently have for `org-priority-highest`:
(defcustom org-priority-highest ?A
"The highest priority of TODO items.
A character like ?A, ?B, etc., or a numeric value like 1, 2, etc.
The default is the character ?A, which is 65 as a numeric value.
If you set `org-priority-highest' to a numeric value inferior to
65, Org assumes you want to use digits for the priority cookie.
If you set it to >=65, Org assumes you want to use alphabetical
characters.
In both cases, the value of `org-priority-highest' must be
smaller than `org-priority-lowest': for example, if \"A\" is the
highest priority, it is smaller than the lowest \"C\" priority:
65 < 67."
:group 'org-priorities
:type '(restricted-sexp
:match-alternatives (;; Choice 1: Integer from 0 to 65
(lambda (val)
(and (integerp val)
(<= 0 val 64)))
;; Choice 2: Uppercase character A-Z (which
is also an integer)
(lambda (val)
(and (integerp val)
(<= ?A val ?Z))))))
This does validate things if I use the customize interface, but is there a
way to do it programmatically so that I can unit test it? I found
`customize-set-value' and `customize-set-variable' but neither of those
seem like the right function.
Thanks,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 5164 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-10-06 4:01 ` Derek Chen-Becker
@ 2025-10-11 12:51 ` Ihor Radchenko
2025-10-28 16:21 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-10-11 12:51 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I think I have most of the patch fixed up based on the other feedback, but
> I'm having some difficulty with customization. I think I have the
> customization correct, but I can't figure out how to test that validation
> operates on any values. I don't see anything in the current unit tests for
> `org-property-separators' to reject bad values or otherwise validate
> values. Here's what I currently have for `org-priority-highest`:
> ...
> This does validate things if I use the customize interface, but is there a
> way to do it programmatically so that I can unit test it? I found
> `customize-set-value' and `customize-set-variable' but neither of those
> seem like the right function.
See how it is done in `setopt--set'.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-10-11 12:51 ` Ihor Radchenko
@ 2025-10-28 16:21 ` Derek Chen-Becker
2025-10-29 11:25 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-10-28 16:21 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1766 bytes --]
Still working through some testing and improvements on the main commit
message, but I'm attaching the patch to remove the deprecated `show'
parameter for `org-priority'.
Thanks,
Derek
On Sat, Oct 11, 2025 at 6:51 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > I think I have most of the patch fixed up based on the other feedback,
> but
> > I'm having some difficulty with customization. I think I have the
> > customization correct, but I can't figure out how to test that validation
> > operates on any values. I don't see anything in the current unit tests
> for
> > `org-property-separators' to reject bad values or otherwise validate
> > values. Here's what I currently have for `org-priority-highest`:
> > ...
> > This does validate things if I use the customize interface, but is there
> a
> > way to do it programmatically so that I can unit test it? I found
> > `customize-set-value' and `customize-set-variable' but neither of those
> > seem like the right function.
>
> See how it is done in `setopt--set'.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 3743 bytes --]
[-- Attachment #2: 0001-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: application/x-patch, Size: 2162 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-10-28 16:21 ` Derek Chen-Becker
@ 2025-10-29 11:25 ` Derek Chen-Becker
2025-11-02 13:08 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-10-29 11:25 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 2534 bytes --]
Here's the full set of patches for removing the deprecated `show' parameter
as well as updating the handling of priorities in `org-priority'. Looking
forward to your feedback.
Cheers,
Derek
On Tue, Oct 28, 2025 at 10:21 AM Derek Chen-Becker <[email protected]>
wrote:
> Still working through some testing and improvements on the main commit
> message, but I'm attaching the patch to remove the deprecated `show'
> parameter for `org-priority'.
>
> Thanks,
>
> Derek
>
> On Sat, Oct 11, 2025 at 6:51 AM Ihor Radchenko <[email protected]>
> wrote:
>
>> Derek Chen-Becker <[email protected]> writes:
>>
>> > I think I have most of the patch fixed up based on the other feedback,
>> but
>> > I'm having some difficulty with customization. I think I have the
>> > customization correct, but I can't figure out how to test that
>> validation
>> > operates on any values. I don't see anything in the current unit tests
>> for
>> > `org-property-separators' to reject bad values or otherwise validate
>> > values. Here's what I currently have for `org-priority-highest`:
>> > ...
>> > This does validate things if I use the customize interface, but is
>> there a
>> > way to do it programmatically so that I can unit test it? I found
>> > `customize-set-value' and `customize-set-variable' but neither of those
>> > seem like the right function.
>>
>> See how it is done in `setopt--set'.
>>
>> --
>> 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>
>>
>
>
> --
> +---------------------------------------------------------------+
> | Derek Chen-Becker |
> | GPG Key available at https://keybase.io/dchenbecker and |
> | https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
> | Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
> +---------------------------------------------------------------+
>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 6013 bytes --]
[-- Attachment #2: 0001-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: application/x-patch, Size: 2166 bytes --]
[-- Attachment #3: 0002-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: application/x-patch, Size: 21326 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-10-29 11:25 ` Derek Chen-Becker
@ 2025-11-02 13:08 ` Ihor Radchenko
2025-11-03 13:59 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-02 13:08 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Here's the full set of patches for removing the deprecated `show' parameter
> as well as updating the handling of priorities in `org-priority'. Looking
> forward to your feedback.
Thanks!
Please use double space between sentences in the commit message.
> + (user-error "Invalid priority value `%s'" priority)))
Just `error' I think. `user-error' is used when the requested user
command cannot be performed. Here, it is just a function that is called
with invalid value.
> When called programmatically, ACTION can be `set', `up', `down', an
> uppercase alphabetic character A through Z, or an integer 0 through 64,
> inclusive. If a lower-case character is passed as ACTION or entered via
> interactive prompt, it will automatically be converted to uppercase."
What about 'remove?
> ;; Validate the input string to ensure it's a valid value because
> ;; string-to-number returns zero for a non-numeric string, which could
> ;; be a valid priority
> (unless (string-match-p "[0-9]\\|[1-5][0-9]\\|6[0-4]" s)
> (user-error "Priority must be a number between `%s' and `%s'"
> (org-priority-to-string org-priority-highest)
> (org-priority-to-string org-priority-lowest)))
Why not using `org-priority-valid-cookie-string-p'?
> + (let ((validatef (lambda (variable value)
> + (widget-apply (widget-convert (get variable 'custom-type)) :match value))))
> + ;; Valid numeric values for high/low/default
> + (seq-every-p (lambda (var)
> + (should (funcall validatef var 0))
`cl-flet' would lead to slightly shorter code here.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-02 13:08 ` Ihor Radchenko
@ 2025-11-03 13:59 ` Derek Chen-Becker
2025-11-05 18:52 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-03 13:59 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 2006 bytes --]
On Sun, Nov 2, 2025 at 6:08 AM Ihor Radchenko <[email protected]> wrote:
> Please use double space between sentences in the commit message.
>
...
> Just `error' I think. `user-error' is used when the requested user
> command cannot be performed. Here, it is just a function that is called
> with invalid value.
>
...
> What about 'remove?
>
All fixed in the latest patch. Is there an existing pre-commit script hook
that can be used to validate formatting of commit messages? I feel like I'm
going to forget the double-space again at some point. Happy to add one that
folks could (optionally) use.
> Why not using `org-priority-valid-cookie-string-p'?
>
I actually started thinking about that, but the difference here is that the
existing `org-priority-regexp' is for a cookie (including the square
brackets, e.g. "[#...]") whereas this match is for the raw value. I was
considering adding a new `defvar' for `org-priority-value-regexp' that I
could use with a new `org-priority-valid-value-string-p' predicate, as well
as constructing `org-priority-regexp', but that felt a little overly
complicated. I guess I could also just format the value read from the user
appropriately and then call `org-priority-valid-cookie-string-p'. I'm open
to suggestions.
`cl-flet' would lead to slightly shorter code here.
>
Thanks! I wasn't aware of `cl-flet' but I changed the code to use it and I
like it better :)
The latest patches are attached, but I'm happy to make changes for the
regexp (or any other improvements) if you want.
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 4929 bytes --]
[-- Attachment #2: 0002-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: application/octet-stream, Size: 21260 bytes --]
From 6ec1a651122811bbfc971e52a0880ca9b71c9e7d Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Sat, 6 Sep 2025 07:07:34 -0600
Subject: [PATCH 2/2] lisp/org.el: Add proper support for numeric priorities
* doc/org-manual.org: Update the manual to clarify the allowed values for
priorities.
* lisp/org.el (org-priority-highest, org-priority-lowest,
org-priority-default): Update the `defcustom' types so that they perform
validation on the values entered.
(org-priority-regexp): Update the priority cookie regular expression to
fully validate numeric values, and update the documentation to match.
(org-priority-valid-cookie-string-p): Add a validation predicate to test
priority cookie strings against the updated `org-priority-regexp' regex.
(org-priority-valid-value-p): Add a validation predicate to test priority
values against allowed values and the currently defined high/low range.
Optionally ignore the defined range in the test so that the predicate can
be used in defcustom validation.
(org-priority-number-p): Add a validation predicate to test if a priority
is in the numeric range (0-64).
(org-priority-to-string): Add a function to provide a consistent string
value from a given priority value.
(org-priority): Update the docstring to clarify acceptable values as well
as the implicit conversion from lower-case to upper-case for alphabetic
values. Refactor to use the new predicate functions defined in this commit
for validation of values, particularly for double-digit numeric values.
Rename variables used in the function to clarify intent and improve
readability. Replace bare space characters ("?\ ") with explicit
escapes ("?\s") to improve readability. Refactor code for interactively
reading new priorities from the user to utilize the predicate functions
defined in this commit and perform additional validation. Utilize the new
predicate functions to clarify the wrapping logic for `up' and `down'
actions.
(org-entry-put): Replace bare space escape for removal with the `remove'
symbol for consistency.
* testing/lisp/test-org.el: Add unit tests for the new predicates and
format functions related to priority. Add unit tests for the new
validations for `org-priority-highest', `org-priority-lowest', and
`org-priority-default'.
---
doc/org-manual.org | 8 +-
lisp/org.el | 177 +++++++++++++++++++++++++--------------
testing/lisp/test-org.el | 97 +++++++++++++++++++++
3 files changed, 217 insertions(+), 65 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index ff5a569d4..574af5533 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4627,7 +4627,7 @@ You can also use numeric values for priorities, such as
When using numeric priorities, you need to set ~org-priority-highest~,
~org-priority-lowest~ and ~org-priority-default~ to integers, which
-must all be strictly inferior to 65.
+must all be a non-negative integer between 0 and 64, inclusive.
Priorities can be attached to any heading; they do not need to be
TODO items.
@@ -4662,8 +4662,10 @@ TODO items.
#+vindex: org-priority-default
You can change the range of allowed priorities by setting the
variables ~org-priority-highest~, ~org-priority-lowest~, and
-~org-priority-default~. For an individual buffer, you may set these
-values (highest, lowest, default) like this (please make sure that the
+~org-priority-default~. Valid priority values are single uppercase
+Latin alphabetical characters A-Z, and non-negative integers in between 0
+and 64, inclusive. For an individual buffer, you may set these values
+(highest, lowest, default) like this (please make sure that the
highest priority is earlier in the alphabet than the lowest priority):
#+cindex: @samp{PRIORITIES}, keyword
diff --git a/lisp/org.el b/lisp/org.el
index 6c6a0058d..94b1bd104 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -2436,7 +2436,6 @@ set a priority."
:type 'boolean)
(defvaralias 'org-highest-priority 'org-priority-highest)
-
(defcustom org-priority-highest ?A
"The highest priority of TODO items.
@@ -2454,9 +2453,8 @@ smaller than `org-priority-lowest': for example, if \"A\" is the
highest priority, it is smaller than the lowest \"C\" priority:
65 < 67."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-lowest-priority 'org-priority-lowest)
(defcustom org-priority-lowest ?C
@@ -2476,9 +2474,8 @@ than `org-priority-highest': for example, if \"C\" is the lowest
priority, it is greater than the highest \"A\" priority: 67 >
65."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-default-priority 'org-priority-default)
(defcustom org-priority-default ?B
@@ -2492,9 +2489,8 @@ in this range exclusive or inclusive to the range boundaries. Else the
first step refuses to set the default and the second will fall back on
\(depending on the command used) the highest or lowest priority."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defcustom org-priority-start-cycle-with-default t
"Non-nil means start with default priority when starting to cycle.
@@ -11303,14 +11299,56 @@ from the `before-change-functions' in the current buffer."
;;;; Priorities
-(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
+(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z]\\|[0-9]\\|[1-5][0-9]\\|6[0-4]\\)\\] ?\\)"
"Regular expression matching the priority indicator.
A priority indicator can be e.g. [#A] or [#1].
+The value of the priority cookie must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in
+the range 0 through 64.
This regular expression matches these groups:
0 : the whole match, e.g. \"TODO [#A] Hack\"
1 : the priority cookie, e.g. \"[#A]\"
2 : the value of the priority cookie, e.g. \"A\".")
+(defun org-priority-valid-cookie-string-p (priority)
+ "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
+ (cond
+ ((stringp priority)
+ (let ((case-fold-search nil)) ;; Force case-sensitive match
+ ;; (and ... t) to force explicit t/nil
+ (and (string-match-p org-priority-regexp priority) t)))))
+
+(defun org-priority-valid-value-p (priority &optional ignore-user-bounds)
+ "Return t if the PRIORITY is a valid priority value (0-64, A-Z), nil otherwise.
+Also validate that the priority value is within the current bounds of
+ `org-priority-lowest' and `org-priority-highest' unless IGNORE-USER-BOUNDS is
+non-nil."
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (integerp priority)
+ ;; validate that the value is within valid ranges
+ (or (<= 0 priority 64)
+ (<= ?A priority ?Z))
+ ;; more specifically, validate within the current high/low ranges
+ ;; unless the caller is ignoring the range
+ (or ignore-user-bounds
+ (>= org-priority-lowest priority org-priority-highest))))
+
+(defun org-priority-number-p (priority)
+ "Return t if the PRIORITY is an integer 0 through 64, inclusive, nil otherwise."
+ (and (integerp priority)
+ (<= 0 priority 64)))
+
+(defun org-priority-to-string (priority)
+ "Return a string form of PRIORITY based on whether it's numeric or alphabetic."
+ ;; we check whether this is even a valid value, ignoring the current high/low bounds
+ (if (org-priority-valid-value-p priority t)
+ (if (org-priority-number-p priority)
+ (number-to-string priority)
+ (format "%c" priority))
+ (error "Invalid priority value `%s'" priority)))
+
(defun org-priority-up ()
"Increase the priority of the current item."
(interactive)
@@ -11327,57 +11365,74 @@ This regular expression matches these groups:
When called interactively with a `\\[universal-argument]' prefix,
show the priority in the minibuffer instead of changing it.
-When called programmatically, ACTION can be `set', `up', `down',
-or a character."
+When called programmatically, ACTION can be `set', `up', `down', `remove', an
+uppercase alphabetic character A through Z, or an integer 0 through 64,
+inclusive. If a lower-case character is passed as ACTION or entered via
+interactive prompt, it will automatically be converted to uppercase."
(interactive "P")
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
(user-error "Priority commands are disabled"))
+ ;; If action was not provided, default to "set", which will prompt the user
(setq action (or action 'set))
- (let ((nump (< org-priority-lowest 65))
- current new news have remove)
+ (let ((is-numeric-priority (org-priority-number-p org-priority-lowest))
+ current-value new-value new-value-string has-existing-cookie remove)
(save-excursion
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
- (setq current (org-priority-to-value ms)
- have t)))
+ (setq current-value (org-priority-to-value ms)
+ has-existing-cookie t)))
(cond
((eq action 'remove)
- (setq remove t new ?\ ))
+ (setq remove t new-value ?\s))
+ ;; set and a value are treated similarly, but only if the
+ ;; value is a valid priority
((or (eq action 'set)
- (integerp action))
+ (org-priority-valid-value-p action))
(if (not (eq action 'set))
- (setq new action)
+ (setq new-value action)
(setq
- new
- (if nump
- (let* ((msg (format "Priority %s-%s, SPC to remove: "
- (number-to-string org-priority-highest)
- (number-to-string org-priority-lowest)))
- (s (if (< 9 org-priority-lowest)
- (read-string msg)
- (message msg)
- (char-to-string (read-char-exclusive)))))
- (if (equal s " ") ?\s (string-to-number s)))
- (progn (message "Priority %c-%c, SPC to remove: "
- org-priority-highest org-priority-lowest)
- (save-match-data
- (setq new (read-char-exclusive)))))))
- (when (and (= (upcase org-priority-highest) org-priority-highest)
- (= (upcase org-priority-lowest) org-priority-lowest))
- (setq new (upcase new)))
- (cond ((equal new ?\s) (setq remove t))
- ((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
- (user-error
- (if nump
- "Priority must be between `%s' and `%s'"
- "Priority must be between `%c' and `%c'")
- org-priority-highest org-priority-lowest))))
+ new-value
+ (let* ((msg (format "Priority %s-%s, SPC to remove: "
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ ;; Read input as a string if the current high/low range could
+ ;; be two digits. Otherwise, we can just read a single
+ ;; character and save the user from pressing <enter>
+ (s (if (and is-numeric-priority
+ (>= org-priority-lowest 10))
+ (read-string msg)
+ (char-to-string (read-char-exclusive msg)))))
+ ;; Space is a special value indicating removal. For numeric
+ ;; priorities, parse the numeric value or ensure an upper case
+ ;; character (and convert back to a character for validation)
+ (if (string-equal s " ")
+ ?\s
+ (if is-numeric-priority
+ (progn
+ ;; Validate the input string to ensure it's a valid value because
+ ;; string-to-number returns zero for a non-numeric string, which could
+ ;; be a valid priority
+ (unless (string-match-p "[0-9]\\|[1-5][0-9]\\|6[0-4]" s)
+ (user-error "Priority must be a number between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ (string-to-number s))
+ ;; Validation of non-numerics is handled next
+ (string-to-char (upcase s)))))))
+ ;; After reading interactive input, set removal flag if needed and
+ ;; perform validation on the new value
+ (cond
+ ((equal new-value ?\s) (setq remove t))
+ ((not (org-priority-valid-value-p new-value))
+ (user-error "Priority must be between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))))
((eq action 'up)
- (setq new (if have
- (1- current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1- current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-lowest ; wrap around empty to lowest
@@ -11386,8 +11441,8 @@ or a character."
org-priority-default
(1- org-priority-default))))))
((eq action 'down)
- (setq new (if have
- (1+ current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1+ current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-highest ; wrap around empty to highest
@@ -11396,36 +11451,34 @@ or a character."
org-priority-default
(1+ org-priority-default))))))
(t (user-error "Invalid action")))
- (when (or (< (upcase new) org-priority-highest)
- (> (upcase new) org-priority-lowest))
+ ;; Check against the current high/low range if we need to wrap
+ (when (not (org-priority-valid-value-p new-value))
(if (and (memq action '(up down))
- (not have) (not (eq last-command this-command)))
- ;; `new' is from default priority
+ (not has-existing-cookie) (not (eq last-command this-command)))
+ ;; `new-value' is from default priority
(error
"The default can not be set, see `org-priority-default' why")
- ;; normal cycling: `new' is beyond highest/lowest priority
+ ;; normal cycling: `new-value' is beyond highest/lowest priority
;; and is wrapped around to the empty priority
(setq remove t)))
- ;; Numerical priorities are limited to 64, beyond that number,
- ;; assume the priority cookie is a character.
- (setq news (if (> new 64) (format "%c" new) (format "%s" new)))
- (if have
+ (setq new-value-string (org-priority-to-string new-value))
+ (if has-existing-cookie
(if remove
(replace-match "" t t nil 1)
- (replace-match news t t nil 2))
+ (replace-match new-value-string t t nil 2))
(if remove
(user-error "No priority cookie found in line")
(let ((case-fold-search nil)) (looking-at org-todo-line-regexp))
(if (match-end 2)
(progn
(goto-char (match-end 2))
- (insert " [#" news "]"))
+ (insert " [#" new-value-string "]"))
(goto-char (match-beginning 3))
- (insert "[#" news "] "))))
+ (insert "[#" new-value-string "] "))))
(when org-auto-align-tags (org-align-tags)))
(if remove
(message "Priority removed")
- (message "Priority of current item set to %s" news)))))
+ (message "Priority of current item set to %s" new-value-string)))))
(defalias 'org-show-priority 'org-priority-show)
(defun org-priority-show ()
@@ -13460,7 +13513,7 @@ decreases scheduled or deadline date by one day."
(org-todo value)
(when org-auto-align-tags (org-align-tags)))
((equal property "PRIORITY")
- (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s))
+ (org-priority (if (org-string-nw-p value) (string-to-char value) 'remove))
(when org-auto-align-tags (org-align-tags)))
((equal property "SCHEDULED")
(forward-line)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 88c083def..f6c949f04 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -9926,6 +9926,103 @@ two
(test-org/extract-mathml-math
(org-create-math-formula "quote\" ; |"))))))
+;;; Priority customization validation
+(ert-deftest test-org/priority-customization ()
+ "Test validation of priority customization."
+ (cl-flet ((validatef (lambda (variable value)
+ (widget-apply (widget-convert (get variable 'custom-type)) :match value))))
+ ;; Valid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var 0))
+ (should (validatef var 42))
+ (should (validatef var 64)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Valid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var ?A))
+ (should (validatef var ?M))
+ (should (validatef var ?Z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var -1))
+ (should-not (validatef var 200)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var ?a))
+ (should-not (validatef var ?z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))))
+
+;;; Priority validation and handling
+(ert-deftest test-org/priority-validation ()
+ "Test validation of priority cookies."
+ ;; Simple bounds checks on single alphabetic characters
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence ?A ?Z)))
+ ;; Test all valid numbers
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%d]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence 0 64)))
+ ;; Invalid characters (not exhaustive)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#$]")))
+ ;; Don't accept lower-case
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (not (org-priority-valid-cookie-string-p cookie))))
+ (number-sequence ?a ?z)))
+ ;; Invalid numberic values (< 0 or > 64)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#-1]")))
+ (should
+ (not (org-priority-valid-cookie-string-p "[#65]")))
+ ;; Value tests (as opposed to cookie tests)
+ ;;
+ ;; Numeric, full range
+ (should
+ (let ((org-priority-highest 0)
+ (org-priority-lowest 64))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence 0 64))))
+ ;; Numeric, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(0 5 9 21 42 64))))
+ ;; Numeric, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(0 5 9 21 42 64))))
+ ;; ;; Alphabetic, full range
+ (should
+ (let ((org-priority-highest ?A)
+ (org-priority-lowest ?Z))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence ?A ?Z))))
+ ;; Alphabetic, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(?A ?L ?N ?Z))))
+ ;; Alphabetic, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(?A ?L ?N ?Z)))))
+
(provide 'test-org)
;;; test-org.el ends here
+
--
2.43.0
[-- Attachment #3: 0001-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: application/octet-stream, Size: 2166 bytes --]
From 76dacea15031f202a75757bae86e5d9b5aac8018 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 29 Jul 2025 06:19:32 -0600
Subject: [PATCH 1/2] lisp/org.el: Remove deprecated show command
* org.el (org-priority): Remove the deprecated show command now that we're
several versions beyond when the deprecation was introduced.
* etc/ORG-NEWS: Add an announcement for the removal of the `show' parameter.
---
etc/ORG-NEWS | 6 ++++++
lisp/org.el | 6 +-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index e767bae00..a2d93a863 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -148,6 +148,12 @@ Previously, the whole contents of drawer, including blank lines at the beginning
parsed as paragraph. Now, the blank lines at the beginning are stored in ~:pre-blank~
property, just as in other greater elements.
+*** The deprecated =show= parameter to =org-priority= has been removed
+
+The =show= parameter for the =org-priority= function was deprecated in
+Org 9.2 (released in 2017). Sufficient time has passed and it is being
+removed as part of refactoring for numeric priorities.
+
** New features
# We list the most important features, and the features that may
diff --git a/lisp/org.el b/lisp/org.el
index 6b8d02b87..6c6a0058d 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11321,7 +11321,7 @@ This regular expression matches these groups:
(interactive)
(org-priority 'down))
-(defun org-priority (&optional action show)
+(defun org-priority (&optional action)
"Change the priority of an item.
When called interactively with a `\\[universal-argument]' prefix,
@@ -11330,10 +11330,6 @@ show the priority in the minibuffer instead of changing it.
When called programmatically, ACTION can be `set', `up', `down',
or a character."
(interactive "P")
- (when show
- ;; Deprecation warning inserted for Org 9.2; once enough time has
- ;; passed the SHOW argument should be removed.
- (warn "`org-priority' called with deprecated SHOW argument"))
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-03 13:59 ` Derek Chen-Becker
@ 2025-11-05 18:52 ` Ihor Radchenko
2025-11-05 19:15 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-05 18:52 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> All fixed in the latest patch. Is there an existing pre-commit script hook
> that can be used to validate formatting of commit messages? I feel like I'm
> going to forget the double-space again at some point. Happy to add one that
> folks could (optionally) use.
Not in Org repository, but we can borrow one from Emacs repo. Do you
want to prepare a patch for that? (I can do it myself, but want a second
pair of eyes to take a look what Emacs does and whether things will work
for Org as well)
>> Why not using `org-priority-valid-cookie-string-p'?
>
> I actually started thinking about that, but the difference here is that the
> existing `org-priority-regexp' is for a cookie (including the square
> brackets, e.g. "[#...]") whereas this match is for the raw value. I was
> considering adding a new `defvar' for `org-priority-value-regexp' that I
> could use with a new `org-priority-valid-value-string-p' predicate, as well
> as constructing `org-priority-regexp', but that felt a little overly
> complicated. I guess I could also just format the value read from the user
> appropriately and then call `org-priority-valid-cookie-string-p'. I'm open
> to suggestions.
I think that an additional defvar with regexp will be useful to have. I
do not like hard-coding regexps in the code.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-05 18:52 ` Ihor Radchenko
@ 2025-11-05 19:15 ` Derek Chen-Becker
2025-11-07 2:39 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-05 19:15 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1006 bytes --]
On Wed, Nov 5, 2025 at 11:52 AM Ihor Radchenko <[email protected]> wrote:
>
> Not in Org repository, but we can borrow one from Emacs repo. Do you
> want to prepare a patch for that? (I can do it myself, but want a second
> pair of eyes to take a look what Emacs does and whether things will work
> for Org as well)
>
Let me look at what Emacs is doing and add it as a patch along with these.
I think that an additional defvar with regexp will be useful to have. I
> do not like hard-coding regexps in the code.
Sounds good, let me make that change.
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3050 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-05 19:15 ` Derek Chen-Becker
@ 2025-11-07 2:39 ` Derek Chen-Becker
2025-11-07 5:01 ` Jacob S. Gordon
2025-11-08 11:03 ` Ihor Radchenko
0 siblings, 2 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-07 2:39 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 2068 bytes --]
I took a look at the Emacs commit hook scripts and I didn't see anything
that looked unusual, although I'm also not familiar enough to say for
certain. I went ahead and created an additional patch for those scripts
along with some minor tweaks to refer to the correct CONTRIBUTE.org file
and to add a check for two spaces after sentences. I also added a new
regexp for the numeric value check and to construct the cookie regexp for
the main patch. Looking forward to feedback.
Thanks,
Derek
On Wed, Nov 5, 2025 at 12:15 PM Derek Chen-Becker <[email protected]>
wrote:
>
>
> On Wed, Nov 5, 2025 at 11:52 AM Ihor Radchenko <[email protected]>
> wrote:
>
>>
>> Not in Org repository, but we can borrow one from Emacs repo. Do you
>> want to prepare a patch for that? (I can do it myself, but want a second
>> pair of eyes to take a look what Emacs does and whether things will work
>> for Org as well)
>>
>
> Let me look at what Emacs is doing and add it as a patch along with these.
>
> I think that an additional defvar with regexp will be useful to have. I
>> do not like hard-coding regexps in the code.
>
>
> Sounds good, let me make that change.
>
> Cheers,
>
> Derek
>
>
> --
> +---------------------------------------------------------------+
> | Derek Chen-Becker |
> | GPG Key available at https://keybase.io/dchenbecker and |
> | https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
> | Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
> +---------------------------------------------------------------+
>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 5817 bytes --]
[-- Attachment #2: 0003-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: application/octet-stream, Size: 21664 bytes --]
From ef70b637f6e9992d7b8f2c07d6f97c26ac51dafe Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Sat, 6 Sep 2025 07:07:34 -0600
Subject: [PATCH 3/3] lisp/org.el: Add proper support for numeric priorities
* doc/org-manual.org: Update the manual to clarify the allowed values for
priorities.
* lisp/org.el (org-priority-highest, org-priority-lowest,
org-priority-default): Update the `defcustom' types so that they perform
validation on the values entered.
(org-priority-numeric-value-regexp): Add a new regexp for valid numeric
priority values for use in input parsing.
(org-priority-regexp): Update the priority cookie regular expression to
fully validate numeric values by using the new
`org-priority-numeric-value-regexp', and update the documentation to match.
(org-priority-valid-cookie-string-p): Add a validation predicate to test
priority cookie strings against the updated `org-priority-regexp' regex.
(org-priority-valid-value-p): Add a validation predicate to test priority
values against allowed values and the currently defined high/low range.
Optionally ignore the defined range in the test so that the predicate can
be used in defcustom validation.
(org-priority-number-p): Add a validation predicate to test if a priority
is in the numeric range (0-64).
(org-priority-to-string): Add a function to provide a consistent string
value from a given priority value.
(org-priority): Update the docstring to clarify acceptable values as well
as the implicit conversion from lower-case to upper-case for alphabetic
values. Refactor to use the new predicate functions defined in this commit
for validation of values, particularly for double-digit numeric values.
Rename variables used in the function to clarify intent and improve
readability. Replace bare space characters ("?\ ") with explicit
escapes ("?\s") to improve readability. Refactor code for interactively
reading new priorities from the user to utilize the predicate functions
defined in this commit and perform additional validation. Utilize the new
predicate functions to clarify the wrapping logic for `up' and `down'
actions.
(org-entry-put): Replace bare space escape for removal with the `remove'
symbol for consistency.
* testing/lisp/test-org.el: Add unit tests for the new predicates and
format functions related to priority. Add unit tests for the new
validations for `org-priority-highest', `org-priority-lowest', and
`org-priority-default'.
---
doc/org-manual.org | 8 +-
lisp/org.el | 182 ++++++++++++++++++++++++++-------------
testing/lisp/test-org.el | 97 +++++++++++++++++++++
3 files changed, 222 insertions(+), 65 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 50359ef5b..af3c80d8c 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4627,7 +4627,7 @@ You can also use numeric values for priorities, such as
When using numeric priorities, you need to set ~org-priority-highest~,
~org-priority-lowest~ and ~org-priority-default~ to integers, which
-must all be strictly inferior to 65.
+must all be a non-negative integer between 0 and 64, inclusive.
Priorities can be attached to any heading; they do not need to be
TODO items.
@@ -4662,8 +4662,10 @@ TODO items.
#+vindex: org-priority-default
You can change the range of allowed priorities by setting the
variables ~org-priority-highest~, ~org-priority-lowest~, and
-~org-priority-default~. For an individual buffer, you may set these
-values (highest, lowest, default) like this (please make sure that the
+~org-priority-default~. Valid priority values are single uppercase
+Latin alphabetical characters A-Z, and non-negative integers in between 0
+and 64, inclusive. For an individual buffer, you may set these values
+(highest, lowest, default) like this (please make sure that the
highest priority is earlier in the alphabet than the lowest priority):
#+cindex: @samp{PRIORITIES}, keyword
diff --git a/lisp/org.el b/lisp/org.el
index 15fb9f3ec..b62b0b0f8 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -2437,7 +2437,6 @@ set a priority."
:type 'boolean)
(defvaralias 'org-highest-priority 'org-priority-highest)
-
(defcustom org-priority-highest ?A
"The highest priority of TODO items.
@@ -2455,9 +2454,8 @@ smaller than `org-priority-lowest': for example, if \"A\" is the
highest priority, it is smaller than the lowest \"C\" priority:
65 < 67."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-lowest-priority 'org-priority-lowest)
(defcustom org-priority-lowest ?C
@@ -2477,9 +2475,8 @@ than `org-priority-highest': for example, if \"C\" is the lowest
priority, it is greater than the highest \"A\" priority: 67 >
65."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-default-priority 'org-priority-default)
(defcustom org-priority-default ?B
@@ -2493,9 +2490,8 @@ in this range exclusive or inclusive to the range boundaries. Else the
first step refuses to set the default and the second will fall back on
\(depending on the command used) the highest or lowest priority."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defcustom org-priority-start-cycle-with-default t
"Non-nil means start with default priority when starting to cycle.
@@ -11316,14 +11312,61 @@ from the `before-change-functions' in the current buffer."
;;;; Priorities
-(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
+(defvar org-priority-numeric-value-regexp "[0-9]\\|[1-5][0-9]\\|6[0-4]"
+ "Regular expression matching valid numeric priority values.
+The priority value must be an integer value in the range 0 through 64.")
+
+(defvar org-priority-regexp
+ (format ".*?\\(\\[#\\([A-Z]\\|%s\\)\\] ?\\)" org-priority-numeric-value-regexp)
"Regular expression matching the priority indicator.
A priority indicator can be e.g. [#A] or [#1].
+The value of the priority cookie must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in
+the range 0 through 64.
This regular expression matches these groups:
0 : the whole match, e.g. \"TODO [#A] Hack\"
1 : the priority cookie, e.g. \"[#A]\"
2 : the value of the priority cookie, e.g. \"A\".")
+(defun org-priority-valid-cookie-string-p (priority)
+ "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
+ (cond
+ ((stringp priority)
+ (let ((case-fold-search nil)) ;; Force case-sensitive match
+ ;; (and ... t) to force explicit t/nil
+ (and (string-match-p org-priority-regexp priority) t)))))
+
+(defun org-priority-valid-value-p (priority &optional ignore-user-bounds)
+ "Return t if the PRIORITY is a valid priority value (0-64, A-Z), nil otherwise.
+Also validate that the priority value is within the current bounds of
+ `org-priority-lowest' and `org-priority-highest' unless IGNORE-USER-BOUNDS is
+non-nil."
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (integerp priority)
+ ;; validate that the value is within valid ranges
+ (or (<= 0 priority 64)
+ (<= ?A priority ?Z))
+ ;; more specifically, validate within the current high/low ranges
+ ;; unless the caller is ignoring the range
+ (or ignore-user-bounds
+ (>= org-priority-lowest priority org-priority-highest))))
+
+(defun org-priority-number-p (priority)
+ "Return t if the PRIORITY is an integer 0 through 64, inclusive, nil otherwise."
+ (and (integerp priority)
+ (<= 0 priority 64)))
+
+(defun org-priority-to-string (priority)
+ "Return a string form of PRIORITY based on whether it's numeric or alphabetic."
+ ;; we check whether this is even a valid value, ignoring the current high/low bounds
+ (if (org-priority-valid-value-p priority t)
+ (if (org-priority-number-p priority)
+ (number-to-string priority)
+ (format "%c" priority))
+ (error "Invalid priority value `%s'" priority)))
+
(defun org-priority-up ()
"Increase the priority of the current item."
(interactive)
@@ -11340,57 +11383,74 @@ This regular expression matches these groups:
When called interactively with a `\\[universal-argument]' prefix,
show the priority in the minibuffer instead of changing it.
-When called programmatically, ACTION can be `set', `up', `down',
-or a character."
+When called programmatically, ACTION can be `set', `up', `down', `remove', an
+uppercase alphabetic character A through Z, or an integer 0 through 64,
+inclusive. If a lower-case character is passed as ACTION or entered via
+interactive prompt, it will automatically be converted to uppercase."
(interactive "P")
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
(user-error "Priority commands are disabled"))
+ ;; If action was not provided, default to "set", which will prompt the user
(setq action (or action 'set))
- (let ((nump (< org-priority-lowest 65))
- current new news have remove)
+ (let ((is-numeric-priority (org-priority-number-p org-priority-lowest))
+ current-value new-value new-value-string has-existing-cookie remove)
(save-excursion
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
- (setq current (org-priority-to-value ms)
- have t)))
+ (setq current-value (org-priority-to-value ms)
+ has-existing-cookie t)))
(cond
((eq action 'remove)
- (setq remove t new ?\ ))
+ (setq remove t new-value ?\s))
+ ;; set and a value are treated similarly, but only if the
+ ;; value is a valid priority
((or (eq action 'set)
- (integerp action))
+ (org-priority-valid-value-p action))
(if (not (eq action 'set))
- (setq new action)
+ (setq new-value action)
(setq
- new
- (if nump
- (let* ((msg (format "Priority %s-%s, SPC to remove: "
- (number-to-string org-priority-highest)
- (number-to-string org-priority-lowest)))
- (s (if (< 9 org-priority-lowest)
- (read-string msg)
- (message msg)
- (char-to-string (read-char-exclusive)))))
- (if (equal s " ") ?\s (string-to-number s)))
- (progn (message "Priority %c-%c, SPC to remove: "
- org-priority-highest org-priority-lowest)
- (save-match-data
- (setq new (read-char-exclusive)))))))
- (when (and (= (upcase org-priority-highest) org-priority-highest)
- (= (upcase org-priority-lowest) org-priority-lowest))
- (setq new (upcase new)))
- (cond ((equal new ?\s) (setq remove t))
- ((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
- (user-error
- (if nump
- "Priority must be between `%s' and `%s'"
- "Priority must be between `%c' and `%c'")
- org-priority-highest org-priority-lowest))))
+ new-value
+ (let* ((msg (format "Priority %s-%s, SPC to remove: "
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ ;; Read input as a string if the current high/low range could
+ ;; be two digits. Otherwise, we can just read a single
+ ;; character and save the user from pressing <enter>
+ (s (if (and is-numeric-priority
+ (>= org-priority-lowest 10))
+ (read-string msg)
+ (char-to-string (read-char-exclusive msg)))))
+ ;; Space is a special value indicating removal. For numeric
+ ;; priorities, parse the numeric value or ensure an upper case
+ ;; character (and convert back to a character for validation)
+ (if (string-equal s " ")
+ ?\s
+ (if is-numeric-priority
+ (progn
+ ;; Validate the input string to ensure it's a valid value because
+ ;; string-to-number returns zero for a non-numeric string, which could
+ ;; be a valid priority
+ (unless (string-match-p org-priority-numeric-value-regexp s)
+ (user-error "Priority must be a number between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ (string-to-number s))
+ ;; Validation of non-numerics is handled next
+ (string-to-char (upcase s)))))))
+ ;; After reading interactive input, set removal flag if needed and
+ ;; perform validation on the new value
+ (cond
+ ((equal new-value ?\s) (setq remove t))
+ ((not (org-priority-valid-value-p new-value))
+ (user-error "Priority must be between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))))
((eq action 'up)
- (setq new (if have
- (1- current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1- current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-lowest ; wrap around empty to lowest
@@ -11399,8 +11459,8 @@ or a character."
org-priority-default
(1- org-priority-default))))))
((eq action 'down)
- (setq new (if have
- (1+ current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1+ current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-highest ; wrap around empty to highest
@@ -11409,36 +11469,34 @@ or a character."
org-priority-default
(1+ org-priority-default))))))
(t (user-error "Invalid action")))
- (when (or (< (upcase new) org-priority-highest)
- (> (upcase new) org-priority-lowest))
+ ;; Check against the current high/low range if we need to wrap
+ (when (not (org-priority-valid-value-p new-value))
(if (and (memq action '(up down))
- (not have) (not (eq last-command this-command)))
- ;; `new' is from default priority
+ (not has-existing-cookie) (not (eq last-command this-command)))
+ ;; `new-value' is from default priority
(error
"The default can not be set, see `org-priority-default' why")
- ;; normal cycling: `new' is beyond highest/lowest priority
+ ;; normal cycling: `new-value' is beyond highest/lowest priority
;; and is wrapped around to the empty priority
(setq remove t)))
- ;; Numerical priorities are limited to 64, beyond that number,
- ;; assume the priority cookie is a character.
- (setq news (if (> new 64) (format "%c" new) (format "%s" new)))
- (if have
+ (setq new-value-string (org-priority-to-string new-value))
+ (if has-existing-cookie
(if remove
(replace-match "" t t nil 1)
- (replace-match news t t nil 2))
+ (replace-match new-value-string t t nil 2))
(if remove
(user-error "No priority cookie found in line")
(let ((case-fold-search nil)) (looking-at org-todo-line-regexp))
(if (match-end 2)
(progn
(goto-char (match-end 2))
- (insert " [#" news "]"))
+ (insert " [#" new-value-string "]"))
(goto-char (match-beginning 3))
- (insert "[#" news "] "))))
+ (insert "[#" new-value-string "] "))))
(when org-auto-align-tags (org-align-tags)))
(if remove
(message "Priority removed")
- (message "Priority of current item set to %s" news)))))
+ (message "Priority of current item set to %s" new-value-string)))))
(defalias 'org-show-priority 'org-priority-show)
(defun org-priority-show ()
@@ -13475,7 +13533,7 @@ decreases scheduled or deadline date by one day."
(org-todo value)
(when org-auto-align-tags (org-align-tags)))
((equal property "PRIORITY")
- (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s))
+ (org-priority (if (org-string-nw-p value) (string-to-char value) 'remove))
(when org-auto-align-tags (org-align-tags)))
((equal property "SCHEDULED")
(forward-line)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 90e506f50..2efa66df3 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -9962,6 +9962,103 @@ two
(test-org/extract-mathml-math
(org-create-math-formula "quote\" ; |"))))))
+;;; Priority customization validation
+(ert-deftest test-org/priority-customization ()
+ "Test validation of priority customization."
+ (cl-flet ((validatef (lambda (variable value)
+ (widget-apply (widget-convert (get variable 'custom-type)) :match value))))
+ ;; Valid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var 0))
+ (should (validatef var 42))
+ (should (validatef var 64)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Valid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var ?A))
+ (should (validatef var ?M))
+ (should (validatef var ?Z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var -1))
+ (should-not (validatef var 200)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var ?a))
+ (should-not (validatef var ?z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))))
+
+;;; Priority validation and handling
+(ert-deftest test-org/priority-validation ()
+ "Test validation of priority cookies."
+ ;; Simple bounds checks on single alphabetic characters
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence ?A ?Z)))
+ ;; Test all valid numbers
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%d]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence 0 64)))
+ ;; Invalid characters (not exhaustive)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#$]")))
+ ;; Don't accept lower-case
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (not (org-priority-valid-cookie-string-p cookie))))
+ (number-sequence ?a ?z)))
+ ;; Invalid numberic values (< 0 or > 64)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#-1]")))
+ (should
+ (not (org-priority-valid-cookie-string-p "[#65]")))
+ ;; Value tests (as opposed to cookie tests)
+ ;;
+ ;; Numeric, full range
+ (should
+ (let ((org-priority-highest 0)
+ (org-priority-lowest 64))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence 0 64))))
+ ;; Numeric, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(0 5 9 21 42 64))))
+ ;; Numeric, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(0 5 9 21 42 64))))
+ ;; ;; Alphabetic, full range
+ (should
+ (let ((org-priority-highest ?A)
+ (org-priority-lowest ?Z))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence ?A ?Z))))
+ ;; Alphabetic, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(?A ?L ?N ?Z))))
+ ;; Alphabetic, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(?A ?L ?N ?Z)))))
+
(provide 'test-org)
;;; test-org.el ends here
+
--
2.43.0
[-- Attachment #3: 0002-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: application/octet-stream, Size: 2166 bytes --]
From 14effb82a618d8091938e7e78a33a395489d2ace Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 29 Jul 2025 06:19:32 -0600
Subject: [PATCH 2/3] lisp/org.el: Remove deprecated show command
* org.el (org-priority): Remove the deprecated show command now that we're
several versions beyond when the deprecation was introduced.
* etc/ORG-NEWS: Add an announcement for the removal of the `show' parameter.
---
etc/ORG-NEWS | 6 ++++++
lisp/org.el | 6 +-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 3cfc2b011..9df33b17e 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -161,6 +161,12 @@ Previously, the whole contents of drawer, including blank lines at the beginning
parsed as paragraph. Now, the blank lines at the beginning are stored in ~:pre-blank~
property, just as in other greater elements.
+*** The deprecated =show= parameter to =org-priority= has been removed
+
+The =show= parameter for the =org-priority= function was deprecated in
+Org 9.2 (released in 2017). Sufficient time has passed and it is being
+removed as part of refactoring for numeric priorities.
+
** New features
# We list the most important features, and the features that may
diff --git a/lisp/org.el b/lisp/org.el
index ed012d2e6..15fb9f3ec 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11334,7 +11334,7 @@ This regular expression matches these groups:
(interactive)
(org-priority 'down))
-(defun org-priority (&optional action show)
+(defun org-priority (&optional action)
"Change the priority of an item.
When called interactively with a `\\[universal-argument]' prefix,
@@ -11343,10 +11343,6 @@ show the priority in the minibuffer instead of changing it.
When called programmatically, ACTION can be `set', `up', `down',
or a character."
(interactive "P")
- (when show
- ;; Deprecation warning inserted for Org 9.2; once enough time has
- ;; passed the SHOW argument should be removed.
- (warn "`org-priority' called with deprecated SHOW argument"))
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
--
2.43.0
[-- Attachment #4: 0001-git-hooks-Add-git-hook-scripts-along-with-instructio.patch --]
[-- Type: application/octet-stream, Size: 22099 bytes --]
From c0f97f5a2de15bfc6fd869a01d4a2ee1dee3bc62 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 6 Nov 2025 18:52:04 -0700
Subject: [PATCH 1/3] git-hooks: Add git hook scripts along with instructions
for use
* git-hooks/commit-msg: Copy commit-msg hook script from the Emacs repo.
Add a check for two spaces between sentences. Update the abort message to
reference `CONTRIBUTE.org' instead of `CONTRIBUTE'.
* git-hooks/commit-msg-files.awk: Copy commit-msg-files.awk hook script
from the Emacs repo. Update the abort message to reference
`CONTRIBUTE.org' instead of `CONTRIBUTE'.
* git-hooks/post-commit: Copy post-commit hook script from the Emacs repo.
* git-hooks/pre-push: Copy pre-push hook script from the Emacs repo.
* git-hooks/prepare-commit-msg: Copy prepare-commit-msg hook script from
the Emacs repo. Update the abort message to reference `CONTRIBUTE.org'
instead of `CONTRIBUTE'.
* CONTRIBUTE.org: Add instructions for utilizing the git hook scripts.
---
CONTRIBUTE.org | 11 ++
git-hooks/commit-msg | 187 +++++++++++++++++++++++++++++++++
git-hooks/commit-msg-files.awk | 128 ++++++++++++++++++++++
git-hooks/post-commit | 47 +++++++++
git-hooks/pre-commit | 83 +++++++++++++++
git-hooks/pre-push | 88 ++++++++++++++++
git-hooks/prepare-commit-msg | 49 +++++++++
7 files changed, 593 insertions(+)
create mode 100755 git-hooks/commit-msg
create mode 100644 git-hooks/commit-msg-files.awk
create mode 100755 git-hooks/post-commit
create mode 100755 git-hooks/pre-commit
create mode 100755 git-hooks/pre-push
create mode 100755 git-hooks/prepare-commit-msg
diff --git a/CONTRIBUTE.org b/CONTRIBUTE.org
index 8f94e875b..a42190e8d 100644
--- a/CONTRIBUTE.org
+++ b/CONTRIBUTE.org
@@ -16,6 +16,17 @@ You can contribute with bug reports and patches.
See these [[https://orgmode.org/worg/org-contribute.html#org069b83a][directions]].
+** Git Hooks
+
+If you would like to utilize the git hook scripts to provide
+validation on commits, you can link the existing files under the
+~git-hooks~ directory. If you want to use all of the hooks, the simple
+way to do this is to run:
+
+#+begin_src shell
+ln -rs git-hooks/* .git/hooks
+#+end_src
+
* As an Org maintainer
We encourage you to volunteer to maintain one of the Org files.
diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg
new file mode 100755
index 000000000..3cd6d8af7
--- /dev/null
+++ b/git-hooks/commit-msg
@@ -0,0 +1,187 @@
+#!/bin/sh
+# Check the format of GNU Emacs change log entries.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+# Written by Paul Eggert.
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk=gawk
+else
+ awk=awk
+fi
+
+# Use a UTF-8 locale if available, so that the UTF-8 check works.
+# Use U+00A2 CENT SIGN to test whether the locale works.
+cent_sign_utf8_format='\302\242\n'
+cent_sign=`printf "$cent_sign_utf8_format"`
+replacement_character_utf8_format='\357\277\275\n'
+replacement_character=`printf "$replacement_character_utf8_format"`
+print_at_sign='BEGIN {print substr("'$cent_sign'@", 2)}'
+at_sign=`$awk "$print_at_sign" </dev/null 2>/dev/null`
+if test "$at_sign" != @; then
+ at_sign=`LC_ALL=en_US.UTF-8 $awk "$print_at_sign" </dev/null 2>/dev/null`
+ if test "$at_sign" = @; then
+ LC_ALL=en_US.UTF-8
+ else
+ LC_ALL=C
+ fi
+ export LC_ALL
+fi
+
+# Check the log entry.
+exec $awk \
+ -v at_sign="$at_sign" \
+ -v cent_sign="$cent_sign" \
+ -v file="$1" \
+ -v replacement_character="$replacement_character" \
+'
+ BEGIN {
+ # These regular expressions assume traditional Unix unibyte behavior.
+ # They are needed for old or broken versions of awk, e.g.,
+ # mawk 1.3.3 (1996), or gawk on MSYS (2015), and/or for systems that
+ # cannot use UTF-8 as the codeset for the locale.
+ space = "[ \f\n\r\t\v]"
+ non_space = "[^ \f\n\r\t\v]"
+ # The non_print below rejects control characters and surrogates
+ # UTF-8 for: 0x01-0x1f 0x7f 0x80-0x9f 0xd800-0xdbff 0xdc00-0xdfff
+ non_print = "[\1-\37\177]|\302[\200-\237]|\355[\240-\277][\200-\277]"
+
+ # Prefer POSIX regular expressions if available, as they do a
+ # better job of checking. Similarly, prefer POSIX negated
+ # expressions if UTF-8 also works.
+ if (" " ~ /[[:space:]]/) {
+ space = "[[:space:]]"
+ if (at_sign == "@" && cent_sign ~ /^[[:print:]]$/) {
+ non_space = "[^[:space:]]"
+ non_print = "[^[:print:]]"
+ }
+ }
+ c_lower = "abcdefghijklmnopqrstuvwxyz"
+ unsafe_gnu_url = "(http|ftp)://([" c_lower ".]*\\.)?(gnu|fsf)\\.org"
+ }
+
+ { input[NR] = $0 }
+
+ /^#/ {
+ # Ignore every line after a scissors line.
+ if (/^# *---* *(>[8%]|[8%]<) *---* *$/) { exit }
+
+ # Ignore comment lines.
+ next
+ }
+
+ !/^.*$/ {
+ print "Invalid character (not UTF-8) in commit message"
+ status = 1
+ }
+
+ /(^|[^\\])`[^'\''`]+`/ {
+ print "Markdown-style quotes in commit message"
+ status = 1
+ }
+
+ /\. [[:alnum:]]/ {
+ print "Two spaces required between sentences."
+ status = 1
+ }
+
+ nlines == 0 && $0 !~ non_space { next }
+
+ { nlines++ }
+
+ nlines == 1 {
+ # Ignore special markers used by "git rebase --autosquash".
+ if (! sub(/^fixup! /, ""))
+ sub(/^squash! /, "")
+
+ if ($0 ~ "^" space) {
+ print "White space at start of commit message'\''s first line"
+ status = 1
+ }
+ }
+
+ nlines == 2 && $0 ~ non_space {
+ print "Nonempty second line in commit message"
+ status = 1
+ }
+
+ {
+ # Expand tabs to spaces for length calculations etc.
+ while (match($0, /\t/)) {
+ before_tab = substr($0, 1, RSTART - 1)
+ after_tab = substr($0, RSTART + 1)
+ $0 = sprintf("%s%*s%s", before_tab, 8 - (RSTART - 1) % 8, "", after_tab)
+ }
+ }
+
+ 78 < length && $0 ~ space {
+ print "Line longer than 78 characters in commit message"
+ status = 1
+ }
+
+ 140 < length {
+ print "Word longer than 140 characters in commit message"
+ status = 1
+ }
+
+ /^Signed-off-by: / {
+ print "'\''Signed-off-by:'\'' in commit message"
+ status = 1
+ }
+
+ $0 ~ unsafe_gnu_url {
+ needs_rewriting = 1
+ }
+
+ $0 ~ non_print {
+ print "Unprintable character in commit message"
+ status = 1
+ }
+ $0 ~ replacement_character {
+ print "Replacement character in commit message"
+ status = 1
+ }
+
+ END {
+ if (nlines == 0) {
+ print "Empty commit message"
+ status = 1
+ }
+ if (status == 0 && needs_rewriting) {
+ for (i = 1; i <= NR; i++) {
+ line = input[i]
+ while (match(line, unsafe_gnu_url)) {
+ prefix = substr(line, 1, RSTART - 1)
+ suffix = substr(line, RSTART)
+ line = prefix "https:" substr(suffix, 5 + (suffix ~ /^http:/))
+ }
+ print line >file
+ }
+ if (close(file) != 0) {
+ print "Cannot rewrite: " file
+ status = 1
+ }
+ }
+ if (status != 0) {
+ print "Commit aborted; please see the file 'CONTRIBUTE.org'"
+ }
+ exit status
+ }
+' <"$1"
diff --git a/git-hooks/commit-msg-files.awk b/git-hooks/commit-msg-files.awk
new file mode 100644
index 000000000..693c6bd00
--- /dev/null
+++ b/git-hooks/commit-msg-files.awk
@@ -0,0 +1,128 @@
+# Check the file list of GNU Emacs change log entries for each commit SHA.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This script accepts a list of (unabbreviated) Git commit SHAs, and
+# will then iterate over them to check that any files mentioned in the
+# commit message are actually present in the commit's diff. If not,
+# it will print out the incorrect file names and return 1.
+
+# You can also pass "-v reason=pre-push", which will add more-verbose
+# output, indicating the abbreviated commit SHA and first line of the
+# commit message for any improper commits.
+
+### Code:
+
+function get_commit_changes(commit_sha, changes, cmd, i, j, len, \
+ bits, filename) {
+ # Collect all the files touched in the specified commit.
+ cmd = ("git show --name-status --first-parent --format= " commit_sha)
+ while ((cmd | getline) > 0) {
+ for (i = 2; i <= NF; i++) {
+ len = split($i, bits, "/")
+ for (j = 1; j <= len; j++) {
+ if (j == 1)
+ filename = bits[j]
+ else
+ filename = filename "/" bits[j]
+ changes[filename] = 1
+ }
+ }
+ }
+ close(cmd)
+}
+
+function check_commit_msg_files(commit_sha, verbose, changes, good, \
+ cmd, msg, filenames_str, filenames, i) {
+ get_commit_changes(commit_sha, changes)
+ good = 1
+
+ cmd = ("git log -1 --format=%B " commit_sha)
+ while ((cmd | getline) > 0) {
+ if (verbose && ! msg)
+ msg = $0
+
+ # Find file entries in the commit message. We look at any line
+ # starting with "*" (possibly prefixed by "; ") followed by a ":",
+ # possibly on a different line. If we encounter a blank line
+ # without seeing a ":", then we don't treat that as a file entry.
+
+ # Accumulate the contents of a (possible) file entry.
+ if (/^[ \t]*$/)
+ filenames_str = ""
+ else if (/^(; )?\*[ \t]+[[:alnum:]]/)
+ filenames_str = $0
+ else if (filenames_str)
+ filenames_str = (filenames_str $0)
+
+ # We have a file entry; analyze it.
+ if (filenames_str && /:/) {
+ # Delete the leading "*" and any trailing information.
+ sub(/^(; )?\*[ \t]+/, "", filenames_str)
+ sub(/[ \t]*[[(<:].*$/, "", filenames_str)
+
+ # There might be multiple files listed in this entry, separated
+ # by spaces (and possibly a comma). Iterate over each of them.
+ split(filenames_str, filenames, ",[ \t]+")
+ for (i in filenames) {
+ # Remove trailing slashes from any directory entries.
+ sub(/\/$/, "", filenames[i])
+
+ if (length(filenames[i]) && ! (filenames[i] in changes)) {
+ if (good) {
+ # Print a header describing the error.
+ if (verbose)
+ printf("In commit %s \"%s\"...\n", substr(commit_sha, 1, 10), msg)
+ printf("Files listed in commit message, but not in diff:\n")
+ }
+ printf(" %s\n", filenames[i])
+ good = 0
+ }
+ }
+
+ filenames_str = ""
+ }
+ }
+ close(cmd)
+
+ return good
+}
+
+BEGIN {
+ if (reason == "pre-push")
+ verbose = 1
+}
+
+/^[a-z0-9]{40}$/ {
+ if (! check_commit_msg_files($0, verbose)) {
+ status = 1
+ }
+}
+
+END {
+ if (status != 0) {
+ if (reason == "pre-push")
+ error_msg = "Push aborted"
+ else
+ error_msg = "Bad commit message"
+ printf("%s; please see the file 'CONTRIBUTE.org'\n", error_msg)
+ }
+ exit status
+}
diff --git a/git-hooks/post-commit b/git-hooks/post-commit
new file mode 100755
index 000000000..d5d7b585f
--- /dev/null
+++ b/git-hooks/post-commit
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Check the file list of GNU Emacs change log entries after committing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs after a commit is finalized and checks that the files
+# mentioned in the commit message match the diff. We perform this in
+# the post-commit phase so that we can be sure we properly detect all
+# the files in the diff (this is difficult during the commit-msg hook,
+# since there's no cross-platform way to detect when a commit is being
+# amended).
+
+# However, since this is a post-commit hook, it's too late to error
+# out and abort the commit: it's already done! As a result, this hook
+# is purely advisory, and instead we error out when trying to push
+# (see "pre-push" in this directory).
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+git rev-parse HEAD | $awk -v reason=post-commit \
+ -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit
new file mode 100755
index 000000000..b38c06175
--- /dev/null
+++ b/git-hooks/pre-commit
@@ -0,0 +1,83 @@
+#!/bin/sh
+# Check file names in git commits for GNU Emacs.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+LC_ALL=C
+export LC_ALL
+
+# If this is a system where /bin/sh isn't sufficient to
+# run git-sh-setup, use a working shell as a recourse.
+if test -x "/usr/xpg4/bin/sh" && test -z "$POSIX_SHELL"; then
+ POSIX_SHELL=1
+ export POSIX_SHELL
+ exec "/usr/xpg4/bin/sh" `dirname $0`/pre-commit
+fi
+
+exec >&2
+
+. git-sh-setup
+
+# When doing a two-way merge, ignore problems that came from the other
+# side of the merge.
+head=HEAD
+if test -r "$GIT_DIR"/MERGE_HEAD && test "$GIT_MERGE_CHECK_OTHER" != true; then
+ merge_heads=`cat "$GIT_DIR"/MERGE_HEAD` || exit
+ for merge_head in $merge_heads; do
+ case $head in
+ HEAD) head=$merge_head;;
+ # For multi-head merges, there's no easy way to ignore merged-in
+ # changes. But if you're doing multi-head merges, presumably
+ # you know how to handle any ensuing problems.
+ *) head=HEAD; break;;
+ esac
+ done
+fi
+
+git_diff='git diff --cached --name-only --diff-filter=A'
+
+# 'git diff' will backslash escape tabs and newlines, so we don't have
+# to worry about word splitting here.
+$git_diff $head |
+LC_ALL=C grep -E 'ChangeLog|^-|/-|[^-+./_0-9A-Z_a-z]' |
+while IFS= read -r new_name; do
+ case $new_name in
+ -* | */-*)
+ echo "$new_name: File name component begins with '-'."
+ exit 1;;
+ ChangeLog.android)
+ # This file is explicitly ok.
+ ;;
+ ChangeLog | */ChangeLog)
+ echo "$new_name: Please use git commit messages, not ChangeLog files."
+ exit 1;;
+ *)
+ echo "$new_name: File name does not consist of -+./_ or ASCII letters or digits."
+ exit 1;;
+ esac
+done
+
+# The '--check' option of git diff-index makes Git complain if changes
+# introduce whitespace errors. This can be a pain when editing test
+# files that deliberately contain lines with trailing whitespace.
+# To work around the problem you can run a command like 'git config
+# core.whitespace -trailing-space'. It may be better to revamp the
+# tests so that trailing spaces are generated on the fly rather than
+# being committed as source.
+
+exec git diff-index --check --cached $head --
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
new file mode 100755
index 000000000..b88a8ed85
--- /dev/null
+++ b/git-hooks/pre-push
@@ -0,0 +1,88 @@
+#!/bin/sh
+# Check the file list of GNU Emacs change log entries before pushing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs before pushing a series of commits and checks that
+# the files mentioned in each commit message match the diffs. This
+# helps ensure that the resulting change logs are correct, which
+# should prevent errors when generating etc/AUTHORS.
+
+# These checks also happen in the "post-commit" hook (which see), but
+# that hook can't abort a commit; it just advises the committer to fix
+# the commit so that this hook runs without errors.
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+# Standard input receives lines of the form:
+# <local ref> SP <local sha> SP <remote ref> SP <remote sha> LF
+$awk -v origin_name="$1" '
+ # If the local SHA is all zeroes, ignore it.
+ $2 ~ /^0{40}$/ {
+ next
+ }
+
+ # Check any lines with a valid local SHA and whose remote ref is
+ # master or an emacs-NN release branch. (We want to avoid checking
+ # feature or scratch branches here.)
+ $2 ~ /^[a-z0-9]{40}$/ && $3 ~ /^refs\/heads\/(master|emacs-[0-9]+)$/ {
+ newref = $2
+ # If the remote SHA is all zeroes, this is a new object to be
+ # pushed (likely a branch)...
+ if ($4 ~ /^0{40}$/) {
+ back = 0
+ # ... Go backwards until we find a SHA on an origin branch.
+ # Stop trying after 1000 commits, just in case...
+ for (back = 0; back < 1000; back++) {
+ cmd = ("git branch -r -l '\''" origin_name "/*'\''" \
+ " --contains " newref "~" back)
+ rv = (cmd | getline)
+ close(cmd)
+ if (rv > 0)
+ break;
+ }
+
+ cmd = ("git rev-parse " newref "~" back)
+ cmd | getline oldref
+ if (!(oldref ~ /^[a-z0-9]{40}$/)) {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+ close(cmd)
+ } else if ($4 ~ /^[a-z0-9]{40}$/) {
+ oldref = $4
+ } else {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+
+ # Print every SHA after oldref, up to (and including) newref.
+ system("git rev-list --first-parent --reverse " oldref ".." newref)
+ }
+' | $awk -v reason=pre-push -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/prepare-commit-msg b/git-hooks/prepare-commit-msg
new file mode 100755
index 000000000..a6fabdb3d
--- /dev/null
+++ b/git-hooks/prepare-commit-msg
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Check the format of GNU Emacs change log entries.
+
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+# Next use /usr/xpg4/bin/awk if available, since the script
+# doesn't support Unix awk.
+elif test -x /usr/xpg4/bin/awk; then
+ awk="/usr/xpg4/bin/awk"
+else
+ awk="awk"
+fi
+
+exec $awk "
+ # Catch the case when someone ran git-commit with -s option,
+ # which automatically adds Signed-off-by.
+ /^Signed-off-by: / {
+ print \"'Signed-off-by:' in commit message\"
+ status = 1
+ }
+ END {
+ if (status != 0) {
+ print \"Commit aborted; please see the file 'CONTRIBUTE.org'\"
+ }
+ exit status
+ }
+" <"$COMMIT_MSG_FILE"
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-07 2:39 ` Derek Chen-Becker
@ 2025-11-07 5:01 ` Jacob S. Gordon
2025-11-19 5:17 ` Derek Chen-Becker
2025-11-08 11:03 ` Ihor Radchenko
1 sibling, 1 reply; 81+ messages in thread
From: Jacob S. Gordon @ 2025-11-07 5:01 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Ihor Radchenko, Rudolf Adamkovič, emacs-orgmode
Hey Derek,
Nice work! A small bit of feedback tying back to this bug with default
priorities:
https://list.orgmode.org/orgmode/[email protected]/
In ‘org-entry-properties’ under lisp/org.el, ‘org-priority-default’ is
treated like a character:
(if (looking-at org-priority-regexp)
(match-string-no-properties 2)
(char-to-string org-priority-default))
so, e.g., column view on the heading will show ‘^G’ instead of 7:
#+PRIORITIES: 1 10 7
* Heading
I think something similar is happening in
‘org-property-get-allowed-values’, and in org-mouse (which does its
own thing for priorities).
Best,
--
Jacob S. Gordon
[email protected]
Please avoid sending me HTML emails and MS Office documents.
https://useplaintext.email/#etiquette
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-07 2:39 ` Derek Chen-Becker
2025-11-07 5:01 ` Jacob S. Gordon
@ 2025-11-08 11:03 ` Ihor Radchenko
2025-11-10 5:01 ` Derek Chen-Becker
1 sibling, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-08 11:03 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I took a look at the Emacs commit hook scripts and I didn't see anything
> that looked unusual, although I'm also not familiar enough to say for
> certain. I went ahead and created an additional patch for those scripts
> along with some minor tweaks to refer to the correct CONTRIBUTE.org file
> and to add a check for two spaces after sentences. I also added a new
> regexp for the numeric value check and to construct the cookie regexp for
> the main patch. Looking forward to feedback.
Thanks! Although I am not sure if asking people to install hooks
manually is going to work. In Emacs, hooks are installed as a part of
autoconf.sh. We may need to copy that logic into our makefiles. I doubt
that people will install the hooks in practice otherwise.
Also, we should not say that the new hooks are "a part of Emacs". They
are not. Only a subset of files in Org repository are actually a part of
Emacs.
> -(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
> +(defvar org-priority-numeric-value-regexp "[0-9]\\|[1-5][0-9]\\|6[0-4]"
I am not sure if this is the best. A more logical way would be defining
a full regexp matching both A-Z and numeric value.
> + "Regular expression matching valid numeric priority values.
> +The priority value must be an integer value in the range 0 through 64.")
> +
> +(defvar org-priority-regexp
> + (format ".*?\\(\\[#\\([A-Z]\\|%s\\)\\] ?\\)" org-priority-numeric-value-regexp)
> + (let* ((msg (format "Priority %s-%s, SPC to remove: "
> + (org-priority-to-string org-priority-highest)
> + (org-priority-to-string org-priority-lowest)))
> + ;; Read input as a string if the current high/low range could
> + ;; be two digits. Otherwise, we can just read a single
> + ;; character and save the user from pressing <enter>
> + (s (if (and is-numeric-priority
> + (>= org-priority-lowest 10))
> + (read-string msg)
> + (char-to-string (read-char-exclusive msg)))))
> + ;; Space is a special value indicating removal. For numeric
> + ;; priorities, parse the numeric value or ensure an upper case
> + ;; character (and convert back to a character for validation)
> + (if (string-equal s " ")
> + ?\s
> + (if is-numeric-priority
> + (progn
> + ;; Validate the input string to ensure it's a valid value because
> + ;; string-to-number returns zero for a non-numeric string, which could
> + ;; be a valid priority
> + (unless (string-match-p org-priority-numeric-value-regexp s)
> + (user-error "Priority must be a number between `%s' and `%s'"
> + (org-priority-to-string org-priority-highest)
> + (org-priority-to-string org-priority-lowest)))
> + (string-to-number s))
> + ;; Validation of non-numerics is handled next
> + (string-to-char (upcase s)))))))
Here, read-char-exclusive can yield something like ?ы, which may also
need to be captured. So, may as well do the check using the full regexp.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-08 11:03 ` Ihor Radchenko
@ 2025-11-10 5:01 ` Derek Chen-Becker
2025-11-10 17:32 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-10 5:01 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1412 bytes --]
On Sat, Nov 8, 2025 at 4:03 AM Ihor Radchenko <[email protected]> wrote:
>
> Thanks! Although I am not sure if asking people to install hooks
> manually is going to work. In Emacs, hooks are installed as a part of
> autoconf.sh. We may need to copy that logic into our makefiles. I doubt
> that people will install the hooks in practice otherwise.
>
OK, I'm assuming you mean the Makefile in the project root? Are you
thinking a new "git" target that does the setup, similar to how Emacs'
autogen.sh does git setup with an argument of "git"? Or do you mean
something else?
> Also, we should not say that the new hooks are "a part of Emacs". They
> are not. Only a subset of files in Org repository are actually a part of
> Emacs.
>
OK, I'll clean up the commit message for that.
> I am not sure if this is the best. A more logical way would be defining
> a full regexp matching both A-Z and numeric value.
>
OK, I'll update the code for that.
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3748 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-10 5:01 ` Derek Chen-Becker
@ 2025-11-10 17:32 ` Ihor Radchenko
2025-11-11 1:19 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-10 17:32 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
>> Thanks! Although I am not sure if asking people to install hooks
>> manually is going to work. In Emacs, hooks are installed as a part of
>> autoconf.sh. We may need to copy that logic into our makefiles. I doubt
>> that people will install the hooks in practice otherwise.
>
> OK, I'm assuming you mean the Makefile in the project root? Are you
> thinking a new "git" target that does the setup, similar to how Emacs'
> autogen.sh does git setup with an argument of "git"? Or do you mean
> something else?
I mean mk/targets.mk, which is included into Makefile in the project
root. A new "git" target which is added to "all" and (maybe) cleaned up
as a part of "cleanall".
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-10 17:32 ` Ihor Radchenko
@ 2025-11-11 1:19 ` Derek Chen-Becker
2025-11-15 18:13 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-11 1:19 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1702 bytes --]
Patches attached for the numeric priorities as well as the removal of the
"show" parameter and an updated `mk/targets.mk' to install and remove the
git hook scripts.
Cheers,
Derek
On Mon, Nov 10, 2025 at 10:32 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> >> Thanks! Although I am not sure if asking people to install hooks
> >> manually is going to work. In Emacs, hooks are installed as a part of
> >> autoconf.sh. We may need to copy that logic into our makefiles. I doubt
> >> that people will install the hooks in practice otherwise.
> >
> > OK, I'm assuming you mean the Makefile in the project root? Are you
> > thinking a new "git" target that does the setup, similar to how Emacs'
> > autogen.sh does git setup with an argument of "git"? Or do you mean
> > something else?
>
> I mean mk/targets.mk, which is included into Makefile in the project
> root. A new "git" target which is added to "all" and (maybe) cleaned up
> as a part of "cleanall".
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 3923 bytes --]
[-- Attachment #2: 0002-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: application/octet-stream, Size: 2166 bytes --]
From 69695b69d9c88e9926b482b48ffd6e4186d395db Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 29 Jul 2025 06:19:32 -0600
Subject: [PATCH 2/3] lisp/org.el: Remove deprecated show command
* org.el (org-priority): Remove the deprecated show command now that we're
several versions beyond when the deprecation was introduced.
* etc/ORG-NEWS: Add an announcement for the removal of the `show' parameter.
---
etc/ORG-NEWS | 6 ++++++
lisp/org.el | 6 +-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 3cfc2b011..9df33b17e 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -161,6 +161,12 @@ Previously, the whole contents of drawer, including blank lines at the beginning
parsed as paragraph. Now, the blank lines at the beginning are stored in ~:pre-blank~
property, just as in other greater elements.
+*** The deprecated =show= parameter to =org-priority= has been removed
+
+The =show= parameter for the =org-priority= function was deprecated in
+Org 9.2 (released in 2017). Sufficient time has passed and it is being
+removed as part of refactoring for numeric priorities.
+
** New features
# We list the most important features, and the features that may
diff --git a/lisp/org.el b/lisp/org.el
index 55351fe47..b4f5e9267 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11312,7 +11312,7 @@ This regular expression matches these groups:
(interactive)
(org-priority 'down))
-(defun org-priority (&optional action show)
+(defun org-priority (&optional action)
"Change the priority of an item.
When called interactively with a `\\[universal-argument]' prefix,
@@ -11321,10 +11321,6 @@ show the priority in the minibuffer instead of changing it.
When called programmatically, ACTION can be `set', `up', `down',
or a character."
(interactive "P")
- (when show
- ;; Deprecation warning inserted for Org 9.2; once enough time has
- ;; passed the SHOW argument should be removed.
- (warn "`org-priority' called with deprecated SHOW argument"))
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
--
2.43.0
[-- Attachment #3: 0001-git-hooks-Add-git-hook-scripts-along-with-instructio.patch --]
[-- Type: application/octet-stream, Size: 23622 bytes --]
From f412f9e745448a375aee4d15d9eba8d8c7054dfa Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 6 Nov 2025 18:52:04 -0700
Subject: [PATCH 1/3] git-hooks: Add git hook scripts along with instructions
for use
* git-hooks/commit-msg: Add commit-msg hook script. Add a check for two
spaces between sentences. Update the abort message to reference
`CONTRIBUTE.org' instead of `CONTRIBUTE'.
* git-hooks/commit-msg-files.awk: Add commit-msg-files.awk hook script.
Update the abort message to reference `CONTRIBUTE.org' instead of
`CONTRIBUTE'.
* git-hooks/post-commit: Add post-commit hook script.
* git-hooks/pre-push: Add pre-push hook script.
* git-hooks/prepare-commit-msg: Add prepare-commit-msg hook script. Update
the abort message to reference `CONTRIBUTE.org' instead of `CONTRIBUTE'.
* mk/targets.mk: Add a `git' and `cleangit' target to install and remove
the new git hook scripts, respectively. Also add `git' as a dependency for
`all' and `cleangit' as a dependency for `cleanall'.
* CONTRIBUTE.org: Add instructions for utilizing the git hook scripts.
---
CONTRIBUTE.org | 11 ++
git-hooks/commit-msg | 187 +++++++++++++++++++++++++++++++++
git-hooks/commit-msg-files.awk | 128 ++++++++++++++++++++++
git-hooks/post-commit | 47 +++++++++
git-hooks/pre-commit | 83 +++++++++++++++
git-hooks/pre-push | 88 ++++++++++++++++
git-hooks/prepare-commit-msg | 49 +++++++++
mk/targets.mk | 16 ++-
8 files changed, 607 insertions(+), 2 deletions(-)
create mode 100755 git-hooks/commit-msg
create mode 100644 git-hooks/commit-msg-files.awk
create mode 100755 git-hooks/post-commit
create mode 100755 git-hooks/pre-commit
create mode 100755 git-hooks/pre-push
create mode 100755 git-hooks/prepare-commit-msg
diff --git a/CONTRIBUTE.org b/CONTRIBUTE.org
index 8f94e875b..bdd19438c 100644
--- a/CONTRIBUTE.org
+++ b/CONTRIBUTE.org
@@ -16,6 +16,17 @@ You can contribute with bug reports and patches.
See these [[https://orgmode.org/worg/org-contribute.html#org069b83a][directions]].
+** Git Hooks
+
+If you would like to utilize the git hook scripts to provide
+validation on commits, you can link the existing files under the
+~git-hooks~ directory (this is done automatically with ~make~). If you
+want to use all of the hooks, the simple way to do this is to run:
+
+#+begin_src shell
+make git
+#+end_src
+
* As an Org maintainer
We encourage you to volunteer to maintain one of the Org files.
diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg
new file mode 100755
index 000000000..3cd6d8af7
--- /dev/null
+++ b/git-hooks/commit-msg
@@ -0,0 +1,187 @@
+#!/bin/sh
+# Check the format of GNU Emacs change log entries.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+# Written by Paul Eggert.
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk=gawk
+else
+ awk=awk
+fi
+
+# Use a UTF-8 locale if available, so that the UTF-8 check works.
+# Use U+00A2 CENT SIGN to test whether the locale works.
+cent_sign_utf8_format='\302\242\n'
+cent_sign=`printf "$cent_sign_utf8_format"`
+replacement_character_utf8_format='\357\277\275\n'
+replacement_character=`printf "$replacement_character_utf8_format"`
+print_at_sign='BEGIN {print substr("'$cent_sign'@", 2)}'
+at_sign=`$awk "$print_at_sign" </dev/null 2>/dev/null`
+if test "$at_sign" != @; then
+ at_sign=`LC_ALL=en_US.UTF-8 $awk "$print_at_sign" </dev/null 2>/dev/null`
+ if test "$at_sign" = @; then
+ LC_ALL=en_US.UTF-8
+ else
+ LC_ALL=C
+ fi
+ export LC_ALL
+fi
+
+# Check the log entry.
+exec $awk \
+ -v at_sign="$at_sign" \
+ -v cent_sign="$cent_sign" \
+ -v file="$1" \
+ -v replacement_character="$replacement_character" \
+'
+ BEGIN {
+ # These regular expressions assume traditional Unix unibyte behavior.
+ # They are needed for old or broken versions of awk, e.g.,
+ # mawk 1.3.3 (1996), or gawk on MSYS (2015), and/or for systems that
+ # cannot use UTF-8 as the codeset for the locale.
+ space = "[ \f\n\r\t\v]"
+ non_space = "[^ \f\n\r\t\v]"
+ # The non_print below rejects control characters and surrogates
+ # UTF-8 for: 0x01-0x1f 0x7f 0x80-0x9f 0xd800-0xdbff 0xdc00-0xdfff
+ non_print = "[\1-\37\177]|\302[\200-\237]|\355[\240-\277][\200-\277]"
+
+ # Prefer POSIX regular expressions if available, as they do a
+ # better job of checking. Similarly, prefer POSIX negated
+ # expressions if UTF-8 also works.
+ if (" " ~ /[[:space:]]/) {
+ space = "[[:space:]]"
+ if (at_sign == "@" && cent_sign ~ /^[[:print:]]$/) {
+ non_space = "[^[:space:]]"
+ non_print = "[^[:print:]]"
+ }
+ }
+ c_lower = "abcdefghijklmnopqrstuvwxyz"
+ unsafe_gnu_url = "(http|ftp)://([" c_lower ".]*\\.)?(gnu|fsf)\\.org"
+ }
+
+ { input[NR] = $0 }
+
+ /^#/ {
+ # Ignore every line after a scissors line.
+ if (/^# *---* *(>[8%]|[8%]<) *---* *$/) { exit }
+
+ # Ignore comment lines.
+ next
+ }
+
+ !/^.*$/ {
+ print "Invalid character (not UTF-8) in commit message"
+ status = 1
+ }
+
+ /(^|[^\\])`[^'\''`]+`/ {
+ print "Markdown-style quotes in commit message"
+ status = 1
+ }
+
+ /\. [[:alnum:]]/ {
+ print "Two spaces required between sentences."
+ status = 1
+ }
+
+ nlines == 0 && $0 !~ non_space { next }
+
+ { nlines++ }
+
+ nlines == 1 {
+ # Ignore special markers used by "git rebase --autosquash".
+ if (! sub(/^fixup! /, ""))
+ sub(/^squash! /, "")
+
+ if ($0 ~ "^" space) {
+ print "White space at start of commit message'\''s first line"
+ status = 1
+ }
+ }
+
+ nlines == 2 && $0 ~ non_space {
+ print "Nonempty second line in commit message"
+ status = 1
+ }
+
+ {
+ # Expand tabs to spaces for length calculations etc.
+ while (match($0, /\t/)) {
+ before_tab = substr($0, 1, RSTART - 1)
+ after_tab = substr($0, RSTART + 1)
+ $0 = sprintf("%s%*s%s", before_tab, 8 - (RSTART - 1) % 8, "", after_tab)
+ }
+ }
+
+ 78 < length && $0 ~ space {
+ print "Line longer than 78 characters in commit message"
+ status = 1
+ }
+
+ 140 < length {
+ print "Word longer than 140 characters in commit message"
+ status = 1
+ }
+
+ /^Signed-off-by: / {
+ print "'\''Signed-off-by:'\'' in commit message"
+ status = 1
+ }
+
+ $0 ~ unsafe_gnu_url {
+ needs_rewriting = 1
+ }
+
+ $0 ~ non_print {
+ print "Unprintable character in commit message"
+ status = 1
+ }
+ $0 ~ replacement_character {
+ print "Replacement character in commit message"
+ status = 1
+ }
+
+ END {
+ if (nlines == 0) {
+ print "Empty commit message"
+ status = 1
+ }
+ if (status == 0 && needs_rewriting) {
+ for (i = 1; i <= NR; i++) {
+ line = input[i]
+ while (match(line, unsafe_gnu_url)) {
+ prefix = substr(line, 1, RSTART - 1)
+ suffix = substr(line, RSTART)
+ line = prefix "https:" substr(suffix, 5 + (suffix ~ /^http:/))
+ }
+ print line >file
+ }
+ if (close(file) != 0) {
+ print "Cannot rewrite: " file
+ status = 1
+ }
+ }
+ if (status != 0) {
+ print "Commit aborted; please see the file 'CONTRIBUTE.org'"
+ }
+ exit status
+ }
+' <"$1"
diff --git a/git-hooks/commit-msg-files.awk b/git-hooks/commit-msg-files.awk
new file mode 100644
index 000000000..693c6bd00
--- /dev/null
+++ b/git-hooks/commit-msg-files.awk
@@ -0,0 +1,128 @@
+# Check the file list of GNU Emacs change log entries for each commit SHA.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This script accepts a list of (unabbreviated) Git commit SHAs, and
+# will then iterate over them to check that any files mentioned in the
+# commit message are actually present in the commit's diff. If not,
+# it will print out the incorrect file names and return 1.
+
+# You can also pass "-v reason=pre-push", which will add more-verbose
+# output, indicating the abbreviated commit SHA and first line of the
+# commit message for any improper commits.
+
+### Code:
+
+function get_commit_changes(commit_sha, changes, cmd, i, j, len, \
+ bits, filename) {
+ # Collect all the files touched in the specified commit.
+ cmd = ("git show --name-status --first-parent --format= " commit_sha)
+ while ((cmd | getline) > 0) {
+ for (i = 2; i <= NF; i++) {
+ len = split($i, bits, "/")
+ for (j = 1; j <= len; j++) {
+ if (j == 1)
+ filename = bits[j]
+ else
+ filename = filename "/" bits[j]
+ changes[filename] = 1
+ }
+ }
+ }
+ close(cmd)
+}
+
+function check_commit_msg_files(commit_sha, verbose, changes, good, \
+ cmd, msg, filenames_str, filenames, i) {
+ get_commit_changes(commit_sha, changes)
+ good = 1
+
+ cmd = ("git log -1 --format=%B " commit_sha)
+ while ((cmd | getline) > 0) {
+ if (verbose && ! msg)
+ msg = $0
+
+ # Find file entries in the commit message. We look at any line
+ # starting with "*" (possibly prefixed by "; ") followed by a ":",
+ # possibly on a different line. If we encounter a blank line
+ # without seeing a ":", then we don't treat that as a file entry.
+
+ # Accumulate the contents of a (possible) file entry.
+ if (/^[ \t]*$/)
+ filenames_str = ""
+ else if (/^(; )?\*[ \t]+[[:alnum:]]/)
+ filenames_str = $0
+ else if (filenames_str)
+ filenames_str = (filenames_str $0)
+
+ # We have a file entry; analyze it.
+ if (filenames_str && /:/) {
+ # Delete the leading "*" and any trailing information.
+ sub(/^(; )?\*[ \t]+/, "", filenames_str)
+ sub(/[ \t]*[[(<:].*$/, "", filenames_str)
+
+ # There might be multiple files listed in this entry, separated
+ # by spaces (and possibly a comma). Iterate over each of them.
+ split(filenames_str, filenames, ",[ \t]+")
+ for (i in filenames) {
+ # Remove trailing slashes from any directory entries.
+ sub(/\/$/, "", filenames[i])
+
+ if (length(filenames[i]) && ! (filenames[i] in changes)) {
+ if (good) {
+ # Print a header describing the error.
+ if (verbose)
+ printf("In commit %s \"%s\"...\n", substr(commit_sha, 1, 10), msg)
+ printf("Files listed in commit message, but not in diff:\n")
+ }
+ printf(" %s\n", filenames[i])
+ good = 0
+ }
+ }
+
+ filenames_str = ""
+ }
+ }
+ close(cmd)
+
+ return good
+}
+
+BEGIN {
+ if (reason == "pre-push")
+ verbose = 1
+}
+
+/^[a-z0-9]{40}$/ {
+ if (! check_commit_msg_files($0, verbose)) {
+ status = 1
+ }
+}
+
+END {
+ if (status != 0) {
+ if (reason == "pre-push")
+ error_msg = "Push aborted"
+ else
+ error_msg = "Bad commit message"
+ printf("%s; please see the file 'CONTRIBUTE.org'\n", error_msg)
+ }
+ exit status
+}
diff --git a/git-hooks/post-commit b/git-hooks/post-commit
new file mode 100755
index 000000000..d5d7b585f
--- /dev/null
+++ b/git-hooks/post-commit
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Check the file list of GNU Emacs change log entries after committing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs after a commit is finalized and checks that the files
+# mentioned in the commit message match the diff. We perform this in
+# the post-commit phase so that we can be sure we properly detect all
+# the files in the diff (this is difficult during the commit-msg hook,
+# since there's no cross-platform way to detect when a commit is being
+# amended).
+
+# However, since this is a post-commit hook, it's too late to error
+# out and abort the commit: it's already done! As a result, this hook
+# is purely advisory, and instead we error out when trying to push
+# (see "pre-push" in this directory).
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+git rev-parse HEAD | $awk -v reason=post-commit \
+ -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit
new file mode 100755
index 000000000..b38c06175
--- /dev/null
+++ b/git-hooks/pre-commit
@@ -0,0 +1,83 @@
+#!/bin/sh
+# Check file names in git commits for GNU Emacs.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+LC_ALL=C
+export LC_ALL
+
+# If this is a system where /bin/sh isn't sufficient to
+# run git-sh-setup, use a working shell as a recourse.
+if test -x "/usr/xpg4/bin/sh" && test -z "$POSIX_SHELL"; then
+ POSIX_SHELL=1
+ export POSIX_SHELL
+ exec "/usr/xpg4/bin/sh" `dirname $0`/pre-commit
+fi
+
+exec >&2
+
+. git-sh-setup
+
+# When doing a two-way merge, ignore problems that came from the other
+# side of the merge.
+head=HEAD
+if test -r "$GIT_DIR"/MERGE_HEAD && test "$GIT_MERGE_CHECK_OTHER" != true; then
+ merge_heads=`cat "$GIT_DIR"/MERGE_HEAD` || exit
+ for merge_head in $merge_heads; do
+ case $head in
+ HEAD) head=$merge_head;;
+ # For multi-head merges, there's no easy way to ignore merged-in
+ # changes. But if you're doing multi-head merges, presumably
+ # you know how to handle any ensuing problems.
+ *) head=HEAD; break;;
+ esac
+ done
+fi
+
+git_diff='git diff --cached --name-only --diff-filter=A'
+
+# 'git diff' will backslash escape tabs and newlines, so we don't have
+# to worry about word splitting here.
+$git_diff $head |
+LC_ALL=C grep -E 'ChangeLog|^-|/-|[^-+./_0-9A-Z_a-z]' |
+while IFS= read -r new_name; do
+ case $new_name in
+ -* | */-*)
+ echo "$new_name: File name component begins with '-'."
+ exit 1;;
+ ChangeLog.android)
+ # This file is explicitly ok.
+ ;;
+ ChangeLog | */ChangeLog)
+ echo "$new_name: Please use git commit messages, not ChangeLog files."
+ exit 1;;
+ *)
+ echo "$new_name: File name does not consist of -+./_ or ASCII letters or digits."
+ exit 1;;
+ esac
+done
+
+# The '--check' option of git diff-index makes Git complain if changes
+# introduce whitespace errors. This can be a pain when editing test
+# files that deliberately contain lines with trailing whitespace.
+# To work around the problem you can run a command like 'git config
+# core.whitespace -trailing-space'. It may be better to revamp the
+# tests so that trailing spaces are generated on the fly rather than
+# being committed as source.
+
+exec git diff-index --check --cached $head --
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
new file mode 100755
index 000000000..b88a8ed85
--- /dev/null
+++ b/git-hooks/pre-push
@@ -0,0 +1,88 @@
+#!/bin/sh
+# Check the file list of GNU Emacs change log entries before pushing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs before pushing a series of commits and checks that
+# the files mentioned in each commit message match the diffs. This
+# helps ensure that the resulting change logs are correct, which
+# should prevent errors when generating etc/AUTHORS.
+
+# These checks also happen in the "post-commit" hook (which see), but
+# that hook can't abort a commit; it just advises the committer to fix
+# the commit so that this hook runs without errors.
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+# Standard input receives lines of the form:
+# <local ref> SP <local sha> SP <remote ref> SP <remote sha> LF
+$awk -v origin_name="$1" '
+ # If the local SHA is all zeroes, ignore it.
+ $2 ~ /^0{40}$/ {
+ next
+ }
+
+ # Check any lines with a valid local SHA and whose remote ref is
+ # master or an emacs-NN release branch. (We want to avoid checking
+ # feature or scratch branches here.)
+ $2 ~ /^[a-z0-9]{40}$/ && $3 ~ /^refs\/heads\/(master|emacs-[0-9]+)$/ {
+ newref = $2
+ # If the remote SHA is all zeroes, this is a new object to be
+ # pushed (likely a branch)...
+ if ($4 ~ /^0{40}$/) {
+ back = 0
+ # ... Go backwards until we find a SHA on an origin branch.
+ # Stop trying after 1000 commits, just in case...
+ for (back = 0; back < 1000; back++) {
+ cmd = ("git branch -r -l '\''" origin_name "/*'\''" \
+ " --contains " newref "~" back)
+ rv = (cmd | getline)
+ close(cmd)
+ if (rv > 0)
+ break;
+ }
+
+ cmd = ("git rev-parse " newref "~" back)
+ cmd | getline oldref
+ if (!(oldref ~ /^[a-z0-9]{40}$/)) {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+ close(cmd)
+ } else if ($4 ~ /^[a-z0-9]{40}$/) {
+ oldref = $4
+ } else {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+
+ # Print every SHA after oldref, up to (and including) newref.
+ system("git rev-list --first-parent --reverse " oldref ".." newref)
+ }
+' | $awk -v reason=pre-push -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/prepare-commit-msg b/git-hooks/prepare-commit-msg
new file mode 100755
index 000000000..a6fabdb3d
--- /dev/null
+++ b/git-hooks/prepare-commit-msg
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Check the format of GNU Emacs change log entries.
+
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+# Next use /usr/xpg4/bin/awk if available, since the script
+# doesn't support Unix awk.
+elif test -x /usr/xpg4/bin/awk; then
+ awk="/usr/xpg4/bin/awk"
+else
+ awk="awk"
+fi
+
+exec $awk "
+ # Catch the case when someone ran git-commit with -s option,
+ # which automatically adds Signed-off-by.
+ /^Signed-off-by: / {
+ print \"'Signed-off-by:' in commit message\"
+ status = 1
+ }
+ END {
+ if (status != 0) {
+ print \"Commit aborted; please see the file 'CONTRIBUTE.org'\"
+ }
+ exit status
+ }
+" <"$COMMIT_MSG_FILE"
diff --git a/mk/targets.mk b/mk/targets.mk
index 20f2ae504..05503449b 100644
--- a/mk/targets.mk
+++ b/mk/targets.mk
@@ -9,6 +9,9 @@ SUBDIRS = $(OTHERDIRS) $(LISPDIRS)
INSTSUB = $(SUBDIRS:%=install-%)
ORG_MAKE_DOC ?= info html pdf
+GITDIR = .git/hooks
+GITHOOKS = commit-msg commit-msg-files.awk post-commit pre-commit prepare-commit-msg pre-push
+
ifneq ($(wildcard .git),)
# Use the org.el header.
ORGVERSION := $(patsubst %-dev,%,$(shell $(BATCH) --eval "(require 'lisp-mnt)" \
@@ -86,7 +89,7 @@ local.mk:
$(info ======================================================)
-@$(MAKE_LOCAL_MK)
-all compile::
+all compile:: git
$(foreach dir, doc lisp, $(MAKE) -C $(dir) clean;)
compile compile-dirty::
$(MAKE) -C lisp $@
@@ -130,12 +133,21 @@ autoloads: lisp
repro: cleanall autoloads
-@$(REPRO) &
+# Implicit rule to copy Git hooks in
+$(GITDIR)/%: git-hooks/%
+ cp -f $< $@
+
+git: $(addprefix $(GITDIR)/,$(GITHOOKS))
+
+cleangit:
+ $(RM) $(addprefix $(GITDIR)/,$(GITHOOKS))
+
cleandirs:
$(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) cleanall;)
clean: cleanlisp cleandoc
-cleanall: cleandirs cleantest
+cleanall: cleandirs cleantest cleangit
-$(FIND) . \( -name \*~ -o -name \*# -o -name .#\* \) -exec $(RM) {} +
-$(FIND) $(CLEANDIRS) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} +
--
2.43.0
[-- Attachment #4: 0003-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: application/octet-stream, Size: 21730 bytes --]
From 1a6051a20773f7a8ac8d99236e38e5e51d30f73b Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Sat, 6 Sep 2025 07:07:34 -0600
Subject: [PATCH 3/3] lisp/org.el: Add proper support for numeric priorities
* doc/org-manual.org: Update the manual to clarify the allowed values for
priorities.
* lisp/org.el (org-priority-highest, org-priority-lowest,
org-priority-default): Update the `defcustom' types so that they perform
validation on the values entered.
(org-priority-numeric-value-regexp): Add a new regexp for valid numeric
priority values for use in input parsing.
(org-priority-regexp): Update the priority cookie regular expression to
fully validate numeric values by using the new
`org-priority-numeric-value-regexp', and update the documentation to match.
(org-priority-valid-cookie-string-p): Add a validation predicate to test
priority cookie strings against the updated `org-priority-regexp' regex.
(org-priority-valid-value-p): Add a validation predicate to test priority
values against allowed values and the currently defined high/low range.
Optionally ignore the defined range in the test so that the predicate can
be used in defcustom validation.
(org-priority-number-p): Add a validation predicate to test if a priority
is in the numeric range (0-64).
(org-priority-to-string): Add a function to provide a consistent string
value from a given priority value.
(org-priority): Update the docstring to clarify acceptable values as well
as the implicit conversion from lower-case to upper-case for alphabetic
values. Refactor to use the new predicate functions defined in this commit
for validation of values, particularly for double-digit numeric values.
Rename variables used in the function to clarify intent and improve
readability. Replace bare space characters ("?\ ") with explicit
escapes ("?\s") to improve readability. Refactor code for interactively
reading new priorities from the user to utilize the predicate functions
defined in this commit and perform additional validation. Utilize the new
predicate functions to clarify the wrapping logic for `up' and `down'
actions.
(org-entry-put): Replace bare space escape for removal with the `remove'
symbol for consistency.
* testing/lisp/test-org.el: Add unit tests for the new predicates and
format functions related to priority. Add unit tests for the new
validations for `org-priority-highest', `org-priority-lowest', and
`org-priority-default'.
---
doc/org-manual.org | 8 +-
lisp/org.el | 184 ++++++++++++++++++++++++++-------------
testing/lisp/test-org.el | 97 +++++++++++++++++++++
3 files changed, 224 insertions(+), 65 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 50359ef5b..af3c80d8c 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4627,7 +4627,7 @@ You can also use numeric values for priorities, such as
When using numeric priorities, you need to set ~org-priority-highest~,
~org-priority-lowest~ and ~org-priority-default~ to integers, which
-must all be strictly inferior to 65.
+must all be a non-negative integer between 0 and 64, inclusive.
Priorities can be attached to any heading; they do not need to be
TODO items.
@@ -4662,8 +4662,10 @@ TODO items.
#+vindex: org-priority-default
You can change the range of allowed priorities by setting the
variables ~org-priority-highest~, ~org-priority-lowest~, and
-~org-priority-default~. For an individual buffer, you may set these
-values (highest, lowest, default) like this (please make sure that the
+~org-priority-default~. Valid priority values are single uppercase
+Latin alphabetical characters A-Z, and non-negative integers in between 0
+and 64, inclusive. For an individual buffer, you may set these values
+(highest, lowest, default) like this (please make sure that the
highest priority is earlier in the alphabet than the lowest priority):
#+cindex: @samp{PRIORITIES}, keyword
diff --git a/lisp/org.el b/lisp/org.el
index b4f5e9267..e261b9987 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -2427,7 +2427,6 @@ set a priority."
:type 'boolean)
(defvaralias 'org-highest-priority 'org-priority-highest)
-
(defcustom org-priority-highest ?A
"The highest priority of TODO items.
@@ -2445,9 +2444,8 @@ smaller than `org-priority-lowest': for example, if \"A\" is the
highest priority, it is smaller than the lowest \"C\" priority:
65 < 67."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-lowest-priority 'org-priority-lowest)
(defcustom org-priority-lowest ?C
@@ -2467,9 +2465,8 @@ than `org-priority-highest': for example, if \"C\" is the lowest
priority, it is greater than the highest \"A\" priority: 67 >
65."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-default-priority 'org-priority-default)
(defcustom org-priority-default ?B
@@ -2483,9 +2480,8 @@ in this range exclusive or inclusive to the range boundaries. Else the
first step refuses to set the default and the second will fall back on
\(depending on the command used) the highest or lowest priority."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defcustom org-priority-start-cycle-with-default t
"Non-nil means start with default priority when starting to cycle.
@@ -11294,14 +11290,63 @@ from the `before-change-functions' in the current buffer."
;;;; Priorities
-(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
+(defvar org-priority-value-regexp "[A-Z]\\|[0-9]\\|[1-5][0-9]\\|6[0-4]"
+ "Regular expression matching valid priority values.
+The priority value must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in the range 0
+through 64.")
+
+(defvar org-priority-regexp
+ (format ".*?\\(\\[#\\(%s\\)\\] ?\\)" org-priority-value-regexp)
"Regular expression matching the priority indicator.
A priority indicator can be e.g. [#A] or [#1].
+The value of the priority cookie must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in
+the range 0 through 64.
This regular expression matches these groups:
0 : the whole match, e.g. \"TODO [#A] Hack\"
1 : the priority cookie, e.g. \"[#A]\"
2 : the value of the priority cookie, e.g. \"A\".")
+(defun org-priority-valid-cookie-string-p (priority)
+ "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
+ (cond
+ ((stringp priority)
+ (let ((case-fold-search nil)) ;; Force case-sensitive match
+ ;; (and ... t) to force explicit t/nil
+ (and (string-match-p org-priority-regexp priority) t)))))
+
+(defun org-priority-valid-value-p (priority &optional ignore-user-bounds)
+ "Return t if the PRIORITY is a valid priority value (0-64, A-Z), nil otherwise.
+Also validate that the priority value is within the current bounds of
+ `org-priority-lowest' and `org-priority-highest' unless IGNORE-USER-BOUNDS is
+non-nil."
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (integerp priority)
+ ;; validate that the value is within valid ranges
+ (or (<= 0 priority 64)
+ (<= ?A priority ?Z))
+ ;; more specifically, validate within the current high/low ranges
+ ;; unless the caller is ignoring the range
+ (or ignore-user-bounds
+ (>= org-priority-lowest priority org-priority-highest))))
+
+(defun org-priority-number-p (priority)
+ "Return t if the PRIORITY is an integer 0 through 64, inclusive, nil otherwise."
+ (and (integerp priority)
+ (<= 0 priority 64)))
+
+(defun org-priority-to-string (priority)
+ "Return a string form of PRIORITY based on whether it's numeric or alphabetic."
+ ;; we check whether this is even a valid value, ignoring the current high/low bounds
+ (if (org-priority-valid-value-p priority t)
+ (if (org-priority-number-p priority)
+ (number-to-string priority)
+ (format "%c" priority))
+ (error "Invalid priority value `%s'" priority)))
+
(defun org-priority-up ()
"Increase the priority of the current item."
(interactive)
@@ -11318,57 +11363,74 @@ This regular expression matches these groups:
When called interactively with a `\\[universal-argument]' prefix,
show the priority in the minibuffer instead of changing it.
-When called programmatically, ACTION can be `set', `up', `down',
-or a character."
+When called programmatically, ACTION can be `set', `up', `down', `remove', an
+uppercase alphabetic character A through Z, or an integer 0 through 64,
+inclusive. If a lower-case character is passed as ACTION or entered via
+interactive prompt, it will automatically be converted to uppercase."
(interactive "P")
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
(user-error "Priority commands are disabled"))
+ ;; If action was not provided, default to "set", which will prompt the user
(setq action (or action 'set))
- (let ((nump (< org-priority-lowest 65))
- current new news have remove)
+ (let ((is-numeric-priority (org-priority-number-p org-priority-lowest))
+ current-value new-value new-value-string has-existing-cookie remove)
(save-excursion
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
- (setq current (org-priority-to-value ms)
- have t)))
+ (setq current-value (org-priority-to-value ms)
+ has-existing-cookie t)))
(cond
((eq action 'remove)
- (setq remove t new ?\ ))
+ (setq remove t new-value ?\s))
+ ;; set and a value are treated similarly, but only if the
+ ;; value is a valid priority
((or (eq action 'set)
- (integerp action))
+ (org-priority-valid-value-p action))
(if (not (eq action 'set))
- (setq new action)
+ (setq new-value action)
(setq
- new
- (if nump
- (let* ((msg (format "Priority %s-%s, SPC to remove: "
- (number-to-string org-priority-highest)
- (number-to-string org-priority-lowest)))
- (s (if (< 9 org-priority-lowest)
- (read-string msg)
- (message msg)
- (char-to-string (read-char-exclusive)))))
- (if (equal s " ") ?\s (string-to-number s)))
- (progn (message "Priority %c-%c, SPC to remove: "
- org-priority-highest org-priority-lowest)
- (save-match-data
- (setq new (read-char-exclusive)))))))
- (when (and (= (upcase org-priority-highest) org-priority-highest)
- (= (upcase org-priority-lowest) org-priority-lowest))
- (setq new (upcase new)))
- (cond ((equal new ?\s) (setq remove t))
- ((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
- (user-error
- (if nump
- "Priority must be between `%s' and `%s'"
- "Priority must be between `%c' and `%c'")
- org-priority-highest org-priority-lowest))))
+ new-value
+ (let* ((msg (format "Priority %s-%s, SPC to remove: "
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ ;; Read input as a string if the current high/low range could
+ ;; be two digits. Otherwise, we can just read a single
+ ;; character and save the user from pressing <enter>
+ (s (if (and is-numeric-priority
+ (>= org-priority-lowest 10))
+ (read-string msg)
+ (char-to-string (read-char-exclusive msg)))))
+ ;; Space is a special value indicating removal. For numeric
+ ;; priorities, parse the numeric value or ensure an upper case
+ ;; character (and convert back to a character for validation)
+ (if (string-equal s " ")
+ ?\s
+ (if is-numeric-priority
+ (progn
+ ;; Validate the input string to ensure it's a valid value because
+ ;; string-to-number returns zero for a non-numeric string, which could
+ ;; be a valid priority
+ (unless (string-match-p org-priority-value-regexp s)
+ (user-error "Priority must be a number between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ (string-to-number s))
+ ;; Validation of non-numerics is handled next
+ (string-to-char (upcase s)))))))
+ ;; After reading interactive input, set removal flag if needed and
+ ;; perform validation on the new value
+ (cond
+ ((equal new-value ?\s) (setq remove t))
+ ((not (org-priority-valid-value-p new-value))
+ (user-error "Priority must be between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))))
((eq action 'up)
- (setq new (if have
- (1- current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1- current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-lowest ; wrap around empty to lowest
@@ -11377,8 +11439,8 @@ or a character."
org-priority-default
(1- org-priority-default))))))
((eq action 'down)
- (setq new (if have
- (1+ current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1+ current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-highest ; wrap around empty to highest
@@ -11387,36 +11449,34 @@ or a character."
org-priority-default
(1+ org-priority-default))))))
(t (user-error "Invalid action")))
- (when (or (< (upcase new) org-priority-highest)
- (> (upcase new) org-priority-lowest))
+ ;; Check against the current high/low range if we need to wrap
+ (when (not (org-priority-valid-value-p new-value))
(if (and (memq action '(up down))
- (not have) (not (eq last-command this-command)))
- ;; `new' is from default priority
+ (not has-existing-cookie) (not (eq last-command this-command)))
+ ;; `new-value' is from default priority
(error
"The default can not be set, see `org-priority-default' why")
- ;; normal cycling: `new' is beyond highest/lowest priority
+ ;; normal cycling: `new-value' is beyond highest/lowest priority
;; and is wrapped around to the empty priority
(setq remove t)))
- ;; Numerical priorities are limited to 64, beyond that number,
- ;; assume the priority cookie is a character.
- (setq news (if (> new 64) (format "%c" new) (format "%s" new)))
- (if have
+ (setq new-value-string (org-priority-to-string new-value))
+ (if has-existing-cookie
(if remove
(replace-match "" t t nil 1)
- (replace-match news t t nil 2))
+ (replace-match new-value-string t t nil 2))
(if remove
(user-error "No priority cookie found in line")
(let ((case-fold-search nil)) (looking-at org-todo-line-regexp))
(if (match-end 2)
(progn
(goto-char (match-end 2))
- (insert " [#" news "]"))
+ (insert " [#" new-value-string "]"))
(goto-char (match-beginning 3))
- (insert "[#" news "] "))))
+ (insert "[#" new-value-string "] "))))
(when org-auto-align-tags (org-align-tags)))
(if remove
(message "Priority removed")
- (message "Priority of current item set to %s" news)))))
+ (message "Priority of current item set to %s" new-value-string)))))
(defalias 'org-show-priority 'org-priority-show)
(defun org-priority-show ()
@@ -13459,7 +13519,7 @@ decreases scheduled or deadline date by one day."
(org-todo value)
(when org-auto-align-tags (org-align-tags)))
((equal property "PRIORITY")
- (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s))
+ (org-priority (if (org-string-nw-p value) (string-to-char value) 'remove))
(when org-auto-align-tags (org-align-tags)))
((equal property "SCHEDULED")
(forward-line)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index c25ae52ee..1a28d01ed 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -10056,6 +10056,103 @@ two
(execute-kbd-macro (kbd "C-a C-f C-f C-f C-f TAB"))
(should (equal (minibuffer-contents) "test|uniq+test")))))))
+;;; Priority customization validation
+(ert-deftest test-org/priority-customization ()
+ "Test validation of priority customization."
+ (cl-flet ((validatef (lambda (variable value)
+ (widget-apply (widget-convert (get variable 'custom-type)) :match value))))
+ ;; Valid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var 0))
+ (should (validatef var 42))
+ (should (validatef var 64)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Valid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var ?A))
+ (should (validatef var ?M))
+ (should (validatef var ?Z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var -1))
+ (should-not (validatef var 200)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var ?a))
+ (should-not (validatef var ?z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))))
+
+;;; Priority validation and handling
+(ert-deftest test-org/priority-validation ()
+ "Test validation of priority cookies."
+ ;; Simple bounds checks on single alphabetic characters
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence ?A ?Z)))
+ ;; Test all valid numbers
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%d]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence 0 64)))
+ ;; Invalid characters (not exhaustive)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#$]")))
+ ;; Don't accept lower-case
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (not (org-priority-valid-cookie-string-p cookie))))
+ (number-sequence ?a ?z)))
+ ;; Invalid numberic values (< 0 or > 64)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#-1]")))
+ (should
+ (not (org-priority-valid-cookie-string-p "[#65]")))
+ ;; Value tests (as opposed to cookie tests)
+ ;;
+ ;; Numeric, full range
+ (should
+ (let ((org-priority-highest 0)
+ (org-priority-lowest 64))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence 0 64))))
+ ;; Numeric, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(0 5 9 21 42 64))))
+ ;; Numeric, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(0 5 9 21 42 64))))
+ ;; ;; Alphabetic, full range
+ (should
+ (let ((org-priority-highest ?A)
+ (org-priority-lowest ?Z))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence ?A ?Z))))
+ ;; Alphabetic, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(?A ?L ?N ?Z))))
+ ;; Alphabetic, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(?A ?L ?N ?Z)))))
+
(provide 'test-org)
;;; test-org.el ends here
+
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-11 1:19 ` Derek Chen-Becker
@ 2025-11-15 18:13 ` Ihor Radchenko
2025-11-15 21:40 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-15 18:13 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Patches attached for the numeric priorities as well as the removal of the
> "show" parameter and an updated `mk/targets.mk' to install and remove the
> git hook scripts.
Thanks! I have several comments on the hooks.
1. The hooks still have # This file is not part of GNU Emacs
Those lines should be removed. The file headers should also be
modified to refer to Org mode. GNU Emacs -> Org mode.
2. If you look at the rest of CONTRIBUTE.org, you will see that it
refers to WORG for basically everything. We should probably modify
WORG page and refer to it instead. (At some point, that page should
go back to the main repo, but that's a different story)
3. You should document how to disable the hooks for people who do not
want them.
4. You should document how to remove the installed hooks (make cleanall)
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-15 18:13 ` Ihor Radchenko
@ 2025-11-15 21:40 ` Derek Chen-Becker
2025-11-15 21:45 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-15 21:40 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2213 bytes --]
OK, I can go ahead and make the changes. Two questions:
1. For the WORGG change, that needs to be a separate patch because that's a
different repo. Should there be anything at all in CONTRIBUTE.org, or just
leave it in the original state?
2. Previously you said to add the git hooks target to `all', but I wonder
if this should be opt-in or opt-out. Instead of adding some sort of
configuration (e.g. environment variable, file settings, etc), maybe I can
just leave the git hooks target out of `all' . Thoughts?
Thanks,
Derek
On Sat, Nov 15, 2025 at 11:13 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Patches attached for the numeric priorities as well as the removal of the
> > "show" parameter and an updated `mk/targets.mk' to install and remove
> the
> > git hook scripts.
>
> Thanks! I have several comments on the hooks.
> 1. The hooks still have # This file is not part of GNU Emacs
> Those lines should be removed. The file headers should also be
> modified to refer to Org mode. GNU Emacs -> Org mode.
> 2. If you look at the rest of CONTRIBUTE.org, you will see that it
> refers to WORG for basically everything. We should probably modify
> WORG page and refer to it instead. (At some point, that page should
> go back to the main repo, but that's a different story)
> 3. You should document how to disable the hooks for people who do not
> want them.
> 4. You should document how to remove the installed hooks (make cleanall)
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 4534 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-15 21:40 ` Derek Chen-Becker
@ 2025-11-15 21:45 ` Ihor Radchenko
2025-11-15 22:51 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-15 21:45 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> 1. For the WORGG change, that needs to be a separate patch because that's a
> different repo. Should there be anything at all in CONTRIBUTE.org, or just
> leave it in the original state?
The hooks refer to CONTRIBUTE.org, so let's put a section in there,
linking to WORG website, just as the existing sections in CONTRIBUTE.org do.
> 2. Previously you said to add the git hooks target to `all', but I wonder
> if this should be opt-in or opt-out. Instead of adding some sort of
> configuration (e.g. environment variable, file settings, etc), maybe I can
> just leave the git hooks target out of `all' . Thoughts?
I guess the question is who is the target auditory for these hooks.
Should it be occasional contributors? Should it be regular contributors?
If the former, we should include the hooks as a part of make all.
If the latter, maybe not.
P.S. I tested the hooks a bit, and it looks like they have
false-positives when inserting long Urls - complain about "too long
word" and "too long line".
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-15 21:45 ` Ihor Radchenko
@ 2025-11-15 22:51 ` Derek Chen-Becker
2025-11-16 9:30 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-15 22:51 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1782 bytes --]
On Sat, Nov 15, 2025 at 2:45 PM Ihor Radchenko <[email protected]> wrote:
> The hooks refer to CONTRIBUTE.org, so let's put a section in there,
> linking to WORG website, just as the existing sections in CONTRIBUTE.org
> do.
>
OK, I can update the section in CONTRIBUTE.org and add a separate patch for
the WORK repo.
> I guess the question is who is the target auditory for these hooks.
> Should it be occasional contributors? Should it be regular contributors?
> If the former, we should include the hooks as a part of make all.
> If the latter, maybe not.
>
My humble opinion is to make it opt-in and give people instructions for
installation via a `githook' target instead of making it automatic for
everyone. I want these to be a useful tool for folks, but not necessarily a
requirement, since we validate the commit message as part of patch review
anyway (we can always mention them in reviews if we see problems with the
message)
> P.S. I tested the hooks a bit, and it looks like they have
> false-positives when inserting long Urls - complain about "too long
> word" and "too long line".
>
I can either disable those or we can try to tune them to better align with
Org's rules for line length. Are you saying that they're too short, or that
long URLs should be exempt from line length requirements?
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 4008 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-15 22:51 ` Derek Chen-Becker
@ 2025-11-16 9:30 ` Ihor Radchenko
2025-11-16 14:55 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-16 9:30 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
>> I guess the question is who is the target auditory for these hooks.
>> Should it be occasional contributors? Should it be regular contributors?
>> If the former, we should include the hooks as a part of make all.
>> If the latter, maybe not.
>
> My humble opinion is to make it opt-in and give people instructions for
> installation via a `githook' target instead of making it automatic for
> everyone. I want these to be a useful tool for folks, but not necessarily a
> requirement, since we validate the commit message as part of patch review
> anyway (we can always mention them in reviews if we see problems with the
> message)
Yeah. After thinking more about this, it is probably disruptive to force
occasional contributors run the hooks. At the end, the overall goal is
making contributions easier, not harder.
For people who contribute regularly, the hooks might be of use though.
So, opt-in sounds like a better idea.
However, if it is opt-in, the hooks should certainly be documented in
org-contribute.org on WORG. A simple dedicated target that needs to be
built manually will do.
>> P.S. I tested the hooks a bit, and it looks like they have
>> false-positives when inserting long Urls - complain about "too long
>> word" and "too long line".
>
> I can either disable those or we can try to tune them to better align with
> Org's rules for line length. Are you saying that they're too short, or that
> long URLs should be exempt from line length requirements?
Long Urls should be exempt. Example: https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=2732be62368f586b36fd9cb894d124ff521f64dd
You can see that message IDs can get pretty long and there is no
practical reason to reject commit messages like in this commit - such
long URL cannot be wrapped.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-16 9:30 ` Ihor Radchenko
@ 2025-11-16 14:55 ` Derek Chen-Becker
2025-11-16 22:26 ` Derek Chen-Becker
2025-11-16 23:18 ` Derek Chen-Becker
0 siblings, 2 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-16 14:55 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1360 bytes --]
On Sun, Nov 16, 2025 at 2:30 AM Ihor Radchenko <[email protected]> wrote:
> For people who contribute regularly, the hooks might be of use though.
> So, opt-in sounds like a better idea.
>
> However, if it is opt-in, the hooks should certainly be documented in
> org-contribute.org on WORG. A simple dedicated target that needs to be
> built manually will do.
>
Agreed, let me rename the target to `githooks' to make it more clear and
update the documentation in WORG. I'll keep a `cleangithooks' target for
cleanup for convenience.
> Long Urls should be exempt. Example:
> https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=2732be62368f586b36fd9cb894d124ff521f64dd
> You can see that message IDs can get pretty long and there is no
> practical reason to reject commit messages like in this commit - such
> long URL cannot be wrapped.
>
OK,. makes sense. Let me see what I can do.
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3548 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-16 14:55 ` Derek Chen-Becker
@ 2025-11-16 22:26 ` Derek Chen-Becker
2025-11-17 5:53 ` Ihor Radchenko
2025-11-16 23:18 ` Derek Chen-Becker
1 sibling, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-16 22:26 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2369 bytes --]
I'm writing the WORG patch and I've been looking through the Worg docs, but
I can't figure out how to get the link ID for a section. I want to provide
a link to the new Git hooks section in the org manual (for example, one of
the existing links in CONTRIBUTE.org is
https://orgmode.org/worg/org-contribute.html#org069b83a). Is there a way to
build the WORG sources into HTML so that I can look at what the section ID
is?
Thanks,
Derek
On Sun, Nov 16, 2025 at 7:55 AM Derek Chen-Becker <[email protected]>
wrote:
>
>
> On Sun, Nov 16, 2025 at 2:30 AM Ihor Radchenko <[email protected]>
> wrote:
>
>> For people who contribute regularly, the hooks might be of use though.
>> So, opt-in sounds like a better idea.
>>
>> However, if it is opt-in, the hooks should certainly be documented in
>> org-contribute.org on WORG. A simple dedicated target that needs to be
>> built manually will do.
>>
>
> Agreed, let me rename the target to `githooks' to make it more clear and
> update the documentation in WORG. I'll keep a `cleangithooks' target for
> cleanup for convenience.
>
>
>> Long Urls should be exempt. Example:
>> https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=2732be62368f586b36fd9cb894d124ff521f64dd
>> You can see that message IDs can get pretty long and there is no
>> practical reason to reject commit messages like in this commit - such
>> long URL cannot be wrapped.
>>
>
> OK,. makes sense. Let me see what I can do.
>
> Cheers,
>
> Derek
>
> --
> +---------------------------------------------------------------+
> | Derek Chen-Becker |
> | GPG Key available at https://keybase.io/dchenbecker and |
> | https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
> | Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
> +---------------------------------------------------------------+
>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 6134 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-16 14:55 ` Derek Chen-Becker
2025-11-16 22:26 ` Derek Chen-Becker
@ 2025-11-16 23:18 ` Derek Chen-Becker
2025-11-17 5:55 ` Ihor Radchenko
1 sibling, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-16 23:18 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 912 bytes --]
On the long URLs, are we OK enforcing that a really long URL needs to be by
itself on a line? For example, a commit message like:
This implements the function at
http://...URL of > 140 characters...
Or do we want to ignore URL length for the purpose of computing line
length, like:
"This implements the function at http://URL of > 140 characters..."
It's going to be simpler if we just treat a URL on its own differently than
trying to ignore long URLs in lines for the sake of line length.
Thanks,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3222 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-16 22:26 ` Derek Chen-Becker
@ 2025-11-17 5:53 ` Ihor Radchenko
0 siblings, 0 replies; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-17 5:53 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I'm writing the WORG patch and I've been looking through the Worg docs, but
> I can't figure out how to get the link ID for a section.
The most reliable way is putting a CUSTOM_ID there.
> ... I want to provide
> a link to the new Git hooks section in the org manual (for example, one of
> the existing links in CONTRIBUTE.org is
> https://orgmode.org/worg/org-contribute.html#org069b83a). Is there a way to
> build the WORG sources into HTML so that I can look at what the section ID
> is?
See https://orgmode.org/worg/worg-setup.html
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-16 23:18 ` Derek Chen-Becker
@ 2025-11-17 5:55 ` Ihor Radchenko
2025-11-19 5:15 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-17 5:55 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> On the long URLs, are we OK enforcing that a really long URL needs to be by
> itself on a line? For example, a commit message like:
>
> This implements the function at
> http://...URL of > 140 characters...
>
> Or do we want to ignore URL length for the purpose of computing line
> length, like:
>
> "This implements the function at http://URL of > 140 characters..."
>
> It's going to be simpler if we just treat a URL on its own differently than
> trying to ignore long URLs in lines for the sake of line length.
Ideally, just ignore lines with Urls. Think of
Link: <long url here>
It would be awkward if the committer has to
Link:
<long url here>
just to pacify the scripts.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-17 5:55 ` Ihor Radchenko
@ 2025-11-19 5:15 ` Derek Chen-Becker
2025-11-22 14:15 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-19 5:15 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1986 bytes --]
OK, here are the updated numeric priority patches, along with a patch to
add the git hook scripts (with updates based on your feedback) and a
corresponding WORG patch with instructions on running the targets to link
and clean up the scripts. I modified the commit test scripts to only
validate length of lines not containing a URL and replaced mentions of GNU
Emacs with Org mode. I'm looking forward to feedback.
Thanks,
Derek
On Sun, Nov 16, 2025 at 10:55 PM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > On the long URLs, are we OK enforcing that a really long URL needs to be
> by
> > itself on a line? For example, a commit message like:
> >
> > This implements the function at
> > http://...URL of > 140 characters...
> >
> > Or do we want to ignore URL length for the purpose of computing line
> > length, like:
> >
> > "This implements the function at http://URL of > 140 characters..."
> >
> > It's going to be simpler if we just treat a URL on its own differently
> than
> > trying to ignore long URLs in lines for the sake of line length.
>
> Ideally, just ignore lines with Urls. Think of
> Link: <long url here>
> It would be awkward if the committer has to
> Link:
> <long url here>
> just to pacify the scripts.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 4235 bytes --]
[-- Attachment #2: 0002-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: application/octet-stream, Size: 2166 bytes --]
From 6eaddeb07cc7cd31f2fa58cad0d207552fc85419 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 29 Jul 2025 06:19:32 -0600
Subject: [PATCH 2/3] lisp/org.el: Remove deprecated show command
* org.el (org-priority): Remove the deprecated show command now that we're
several versions beyond when the deprecation was introduced.
* etc/ORG-NEWS: Add an announcement for the removal of the `show' parameter.
---
etc/ORG-NEWS | 6 ++++++
lisp/org.el | 6 +-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 9113e68ea..6ee0a263f 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -161,6 +161,12 @@ Previously, the whole contents of drawer, including blank lines at the beginning
parsed as paragraph. Now, the blank lines at the beginning are stored in ~:pre-blank~
property, just as in other greater elements.
+*** The deprecated =show= parameter to =org-priority= has been removed
+
+The =show= parameter for the =org-priority= function was deprecated in
+Org 9.2 (released in 2017). Sufficient time has passed and it is being
+removed as part of refactoring for numeric priorities.
+
** New features
# We list the most important features, and the features that may
diff --git a/lisp/org.el b/lisp/org.el
index 5e9def3af..58b026594 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11313,7 +11313,7 @@ This regular expression matches these groups:
(interactive)
(org-priority 'down))
-(defun org-priority (&optional action show)
+(defun org-priority (&optional action)
"Change the priority of an item.
When called interactively with a `\\[universal-argument]' prefix,
@@ -11322,10 +11322,6 @@ show the priority in the minibuffer instead of changing it.
When called programmatically, ACTION can be `set', `up', `down',
or a character."
(interactive "P")
- (when show
- ;; Deprecation warning inserted for Org 9.2; once enough time has
- ;; passed the SHOW argument should be removed.
- (warn "`org-priority' called with deprecated SHOW argument"))
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
--
2.43.0
[-- Attachment #3: 0003-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: application/octet-stream, Size: 21730 bytes --]
From a8b73d18fc9ad11ff118414645a74f4f40b0de21 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Sat, 6 Sep 2025 07:07:34 -0600
Subject: [PATCH 3/3] lisp/org.el: Add proper support for numeric priorities
* doc/org-manual.org: Update the manual to clarify the allowed values for
priorities.
* lisp/org.el (org-priority-highest, org-priority-lowest,
org-priority-default): Update the `defcustom' types so that they perform
validation on the values entered.
(org-priority-numeric-value-regexp): Add a new regexp for valid numeric
priority values for use in input parsing.
(org-priority-regexp): Update the priority cookie regular expression to
fully validate numeric values by using the new
`org-priority-numeric-value-regexp', and update the documentation to match.
(org-priority-valid-cookie-string-p): Add a validation predicate to test
priority cookie strings against the updated `org-priority-regexp' regex.
(org-priority-valid-value-p): Add a validation predicate to test priority
values against allowed values and the currently defined high/low range.
Optionally ignore the defined range in the test so that the predicate can
be used in defcustom validation.
(org-priority-number-p): Add a validation predicate to test if a priority
is in the numeric range (0-64).
(org-priority-to-string): Add a function to provide a consistent string
value from a given priority value.
(org-priority): Update the docstring to clarify acceptable values as well
as the implicit conversion from lower-case to upper-case for alphabetic
values. Refactor to use the new predicate functions defined in this commit
for validation of values, particularly for double-digit numeric values.
Rename variables used in the function to clarify intent and improve
readability. Replace bare space characters ("?\ ") with explicit
escapes ("?\s") to improve readability. Refactor code for interactively
reading new priorities from the user to utilize the predicate functions
defined in this commit and perform additional validation. Utilize the new
predicate functions to clarify the wrapping logic for `up' and `down'
actions.
(org-entry-put): Replace bare space escape for removal with the `remove'
symbol for consistency.
* testing/lisp/test-org.el: Add unit tests for the new predicates and
format functions related to priority. Add unit tests for the new
validations for `org-priority-highest', `org-priority-lowest', and
`org-priority-default'.
---
doc/org-manual.org | 8 +-
lisp/org.el | 184 ++++++++++++++++++++++++++-------------
testing/lisp/test-org.el | 97 +++++++++++++++++++++
3 files changed, 224 insertions(+), 65 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 50359ef5b..af3c80d8c 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4627,7 +4627,7 @@ You can also use numeric values for priorities, such as
When using numeric priorities, you need to set ~org-priority-highest~,
~org-priority-lowest~ and ~org-priority-default~ to integers, which
-must all be strictly inferior to 65.
+must all be a non-negative integer between 0 and 64, inclusive.
Priorities can be attached to any heading; they do not need to be
TODO items.
@@ -4662,8 +4662,10 @@ TODO items.
#+vindex: org-priority-default
You can change the range of allowed priorities by setting the
variables ~org-priority-highest~, ~org-priority-lowest~, and
-~org-priority-default~. For an individual buffer, you may set these
-values (highest, lowest, default) like this (please make sure that the
+~org-priority-default~. Valid priority values are single uppercase
+Latin alphabetical characters A-Z, and non-negative integers in between 0
+and 64, inclusive. For an individual buffer, you may set these values
+(highest, lowest, default) like this (please make sure that the
highest priority is earlier in the alphabet than the lowest priority):
#+cindex: @samp{PRIORITIES}, keyword
diff --git a/lisp/org.el b/lisp/org.el
index 58b026594..fabbff077 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -2428,7 +2428,6 @@ set a priority."
:type 'boolean)
(defvaralias 'org-highest-priority 'org-priority-highest)
-
(defcustom org-priority-highest ?A
"The highest priority of TODO items.
@@ -2446,9 +2445,8 @@ smaller than `org-priority-lowest': for example, if \"A\" is the
highest priority, it is smaller than the lowest \"C\" priority:
65 < 67."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-lowest-priority 'org-priority-lowest)
(defcustom org-priority-lowest ?C
@@ -2468,9 +2466,8 @@ than `org-priority-highest': for example, if \"C\" is the lowest
priority, it is greater than the highest \"A\" priority: 67 >
65."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-default-priority 'org-priority-default)
(defcustom org-priority-default ?B
@@ -2484,9 +2481,8 @@ in this range exclusive or inclusive to the range boundaries. Else the
first step refuses to set the default and the second will fall back on
\(depending on the command used) the highest or lowest priority."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defcustom org-priority-start-cycle-with-default t
"Non-nil means start with default priority when starting to cycle.
@@ -11295,14 +11291,63 @@ from the `before-change-functions' in the current buffer."
;;;; Priorities
-(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
+(defvar org-priority-value-regexp "[A-Z]\\|[0-9]\\|[1-5][0-9]\\|6[0-4]"
+ "Regular expression matching valid priority values.
+The priority value must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in the range 0
+through 64.")
+
+(defvar org-priority-regexp
+ (format ".*?\\(\\[#\\(%s\\)\\] ?\\)" org-priority-value-regexp)
"Regular expression matching the priority indicator.
A priority indicator can be e.g. [#A] or [#1].
+The value of the priority cookie must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in
+the range 0 through 64.
This regular expression matches these groups:
0 : the whole match, e.g. \"TODO [#A] Hack\"
1 : the priority cookie, e.g. \"[#A]\"
2 : the value of the priority cookie, e.g. \"A\".")
+(defun org-priority-valid-cookie-string-p (priority)
+ "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
+ (cond
+ ((stringp priority)
+ (let ((case-fold-search nil)) ;; Force case-sensitive match
+ ;; (and ... t) to force explicit t/nil
+ (and (string-match-p org-priority-regexp priority) t)))))
+
+(defun org-priority-valid-value-p (priority &optional ignore-user-bounds)
+ "Return t if the PRIORITY is a valid priority value (0-64, A-Z), nil otherwise.
+Also validate that the priority value is within the current bounds of
+ `org-priority-lowest' and `org-priority-highest' unless IGNORE-USER-BOUNDS is
+non-nil."
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (integerp priority)
+ ;; validate that the value is within valid ranges
+ (or (<= 0 priority 64)
+ (<= ?A priority ?Z))
+ ;; more specifically, validate within the current high/low ranges
+ ;; unless the caller is ignoring the range
+ (or ignore-user-bounds
+ (>= org-priority-lowest priority org-priority-highest))))
+
+(defun org-priority-number-p (priority)
+ "Return t if the PRIORITY is an integer 0 through 64, inclusive, nil otherwise."
+ (and (integerp priority)
+ (<= 0 priority 64)))
+
+(defun org-priority-to-string (priority)
+ "Return a string form of PRIORITY based on whether it's numeric or alphabetic."
+ ;; we check whether this is even a valid value, ignoring the current high/low bounds
+ (if (org-priority-valid-value-p priority t)
+ (if (org-priority-number-p priority)
+ (number-to-string priority)
+ (format "%c" priority))
+ (error "Invalid priority value `%s'" priority)))
+
(defun org-priority-up ()
"Increase the priority of the current item."
(interactive)
@@ -11319,57 +11364,74 @@ This regular expression matches these groups:
When called interactively with a `\\[universal-argument]' prefix,
show the priority in the minibuffer instead of changing it.
-When called programmatically, ACTION can be `set', `up', `down',
-or a character."
+When called programmatically, ACTION can be `set', `up', `down', `remove', an
+uppercase alphabetic character A through Z, or an integer 0 through 64,
+inclusive. If a lower-case character is passed as ACTION or entered via
+interactive prompt, it will automatically be converted to uppercase."
(interactive "P")
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
(user-error "Priority commands are disabled"))
+ ;; If action was not provided, default to "set", which will prompt the user
(setq action (or action 'set))
- (let ((nump (< org-priority-lowest 65))
- current new news have remove)
+ (let ((is-numeric-priority (org-priority-number-p org-priority-lowest))
+ current-value new-value new-value-string has-existing-cookie remove)
(save-excursion
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
- (setq current (org-priority-to-value ms)
- have t)))
+ (setq current-value (org-priority-to-value ms)
+ has-existing-cookie t)))
(cond
((eq action 'remove)
- (setq remove t new ?\ ))
+ (setq remove t new-value ?\s))
+ ;; set and a value are treated similarly, but only if the
+ ;; value is a valid priority
((or (eq action 'set)
- (integerp action))
+ (org-priority-valid-value-p action))
(if (not (eq action 'set))
- (setq new action)
+ (setq new-value action)
(setq
- new
- (if nump
- (let* ((msg (format "Priority %s-%s, SPC to remove: "
- (number-to-string org-priority-highest)
- (number-to-string org-priority-lowest)))
- (s (if (< 9 org-priority-lowest)
- (read-string msg)
- (message msg)
- (char-to-string (read-char-exclusive)))))
- (if (equal s " ") ?\s (string-to-number s)))
- (progn (message "Priority %c-%c, SPC to remove: "
- org-priority-highest org-priority-lowest)
- (save-match-data
- (setq new (read-char-exclusive)))))))
- (when (and (= (upcase org-priority-highest) org-priority-highest)
- (= (upcase org-priority-lowest) org-priority-lowest))
- (setq new (upcase new)))
- (cond ((equal new ?\s) (setq remove t))
- ((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
- (user-error
- (if nump
- "Priority must be between `%s' and `%s'"
- "Priority must be between `%c' and `%c'")
- org-priority-highest org-priority-lowest))))
+ new-value
+ (let* ((msg (format "Priority %s-%s, SPC to remove: "
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ ;; Read input as a string if the current high/low range could
+ ;; be two digits. Otherwise, we can just read a single
+ ;; character and save the user from pressing <enter>
+ (s (if (and is-numeric-priority
+ (>= org-priority-lowest 10))
+ (read-string msg)
+ (char-to-string (read-char-exclusive msg)))))
+ ;; Space is a special value indicating removal. For numeric
+ ;; priorities, parse the numeric value or ensure an upper case
+ ;; character (and convert back to a character for validation)
+ (if (string-equal s " ")
+ ?\s
+ (if is-numeric-priority
+ (progn
+ ;; Validate the input string to ensure it's a valid value because
+ ;; string-to-number returns zero for a non-numeric string, which could
+ ;; be a valid priority
+ (unless (string-match-p org-priority-value-regexp s)
+ (user-error "Priority must be a number between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ (string-to-number s))
+ ;; Validation of non-numerics is handled next
+ (string-to-char (upcase s)))))))
+ ;; After reading interactive input, set removal flag if needed and
+ ;; perform validation on the new value
+ (cond
+ ((equal new-value ?\s) (setq remove t))
+ ((not (org-priority-valid-value-p new-value))
+ (user-error "Priority must be between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))))
((eq action 'up)
- (setq new (if have
- (1- current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1- current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-lowest ; wrap around empty to lowest
@@ -11378,8 +11440,8 @@ or a character."
org-priority-default
(1- org-priority-default))))))
((eq action 'down)
- (setq new (if have
- (1+ current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1+ current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-highest ; wrap around empty to highest
@@ -11388,36 +11450,34 @@ or a character."
org-priority-default
(1+ org-priority-default))))))
(t (user-error "Invalid action")))
- (when (or (< (upcase new) org-priority-highest)
- (> (upcase new) org-priority-lowest))
+ ;; Check against the current high/low range if we need to wrap
+ (when (not (org-priority-valid-value-p new-value))
(if (and (memq action '(up down))
- (not have) (not (eq last-command this-command)))
- ;; `new' is from default priority
+ (not has-existing-cookie) (not (eq last-command this-command)))
+ ;; `new-value' is from default priority
(error
"The default can not be set, see `org-priority-default' why")
- ;; normal cycling: `new' is beyond highest/lowest priority
+ ;; normal cycling: `new-value' is beyond highest/lowest priority
;; and is wrapped around to the empty priority
(setq remove t)))
- ;; Numerical priorities are limited to 64, beyond that number,
- ;; assume the priority cookie is a character.
- (setq news (if (> new 64) (format "%c" new) (format "%s" new)))
- (if have
+ (setq new-value-string (org-priority-to-string new-value))
+ (if has-existing-cookie
(if remove
(replace-match "" t t nil 1)
- (replace-match news t t nil 2))
+ (replace-match new-value-string t t nil 2))
(if remove
(user-error "No priority cookie found in line")
(let ((case-fold-search nil)) (looking-at org-todo-line-regexp))
(if (match-end 2)
(progn
(goto-char (match-end 2))
- (insert " [#" news "]"))
+ (insert " [#" new-value-string "]"))
(goto-char (match-beginning 3))
- (insert "[#" news "] "))))
+ (insert "[#" new-value-string "] "))))
(when org-auto-align-tags (org-align-tags)))
(if remove
(message "Priority removed")
- (message "Priority of current item set to %s" news)))))
+ (message "Priority of current item set to %s" new-value-string)))))
(defalias 'org-show-priority 'org-priority-show)
(defun org-priority-show ()
@@ -13460,7 +13520,7 @@ decreases scheduled or deadline date by one day."
(org-todo value)
(when org-auto-align-tags (org-align-tags)))
((equal property "PRIORITY")
- (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s))
+ (org-priority (if (org-string-nw-p value) (string-to-char value) 'remove))
(when org-auto-align-tags (org-align-tags)))
((equal property "SCHEDULED")
(forward-line)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index c25ae52ee..1a28d01ed 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -10056,6 +10056,103 @@ two
(execute-kbd-macro (kbd "C-a C-f C-f C-f C-f TAB"))
(should (equal (minibuffer-contents) "test|uniq+test")))))))
+;;; Priority customization validation
+(ert-deftest test-org/priority-customization ()
+ "Test validation of priority customization."
+ (cl-flet ((validatef (lambda (variable value)
+ (widget-apply (widget-convert (get variable 'custom-type)) :match value))))
+ ;; Valid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var 0))
+ (should (validatef var 42))
+ (should (validatef var 64)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Valid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var ?A))
+ (should (validatef var ?M))
+ (should (validatef var ?Z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var -1))
+ (should-not (validatef var 200)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var ?a))
+ (should-not (validatef var ?z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))))
+
+;;; Priority validation and handling
+(ert-deftest test-org/priority-validation ()
+ "Test validation of priority cookies."
+ ;; Simple bounds checks on single alphabetic characters
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence ?A ?Z)))
+ ;; Test all valid numbers
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%d]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence 0 64)))
+ ;; Invalid characters (not exhaustive)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#$]")))
+ ;; Don't accept lower-case
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (not (org-priority-valid-cookie-string-p cookie))))
+ (number-sequence ?a ?z)))
+ ;; Invalid numberic values (< 0 or > 64)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#-1]")))
+ (should
+ (not (org-priority-valid-cookie-string-p "[#65]")))
+ ;; Value tests (as opposed to cookie tests)
+ ;;
+ ;; Numeric, full range
+ (should
+ (let ((org-priority-highest 0)
+ (org-priority-lowest 64))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence 0 64))))
+ ;; Numeric, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(0 5 9 21 42 64))))
+ ;; Numeric, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(0 5 9 21 42 64))))
+ ;; ;; Alphabetic, full range
+ (should
+ (let ((org-priority-highest ?A)
+ (org-priority-lowest ?Z))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence ?A ?Z))))
+ ;; Alphabetic, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(?A ?L ?N ?Z))))
+ ;; Alphabetic, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(?A ?L ?N ?Z)))))
+
(provide 'test-org)
;;; test-org.el ends here
+
--
2.43.0
[-- Attachment #4: 0001-git-hooks-Add-git-hook-scripts-along-with-instructio.patch --]
[-- Type: application/octet-stream, Size: 23454 bytes --]
From 2f9abd8897232681345de79205f624f325df44fe Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 6 Nov 2025 18:52:04 -0700
Subject: [PATCH 1/3] git-hooks: Add git hook scripts along with instructions
for use
* git-hooks/commit-msg: Add commit-msg hook script. Add a check for two
spaces between sentences. Update the abort message to reference
`CONTRIBUTE.org' instead of `CONTRIBUTE'.
* git-hooks/commit-msg-files.awk: Add commit-msg-files.awk hook script.
Update the abort message to reference `CONTRIBUTE.org' instead of
`CONTRIBUTE'.
* git-hooks/post-commit: Add post-commit hook script.
* git-hooks/pre-push: Add pre-push hook script.
* git-hooks/prepare-commit-msg: Add prepare-commit-msg hook script. Update
the abort message to reference `CONTRIBUTE.org' instead of `CONTRIBUTE'.
* mk/targets.mk: Add a `git' and `cleangit' target to install and remove
the new git hook scripts, respectively. Also add `git' as a dependency for
`all' and `cleangit' as a dependency for `cleanall'.
* CONTRIBUTE.org: Add instructions for utilizing the git hook scripts.
---
CONTRIBUTE.org | 5 +
git-hooks/commit-msg | 192 +++++++++++++++++++++++++++++++++
git-hooks/commit-msg-files.awk | 128 ++++++++++++++++++++++
git-hooks/post-commit | 47 ++++++++
git-hooks/pre-commit | 83 ++++++++++++++
git-hooks/pre-push | 88 +++++++++++++++
git-hooks/prepare-commit-msg | 49 +++++++++
mk/targets.mk | 14 ++-
8 files changed, 605 insertions(+), 1 deletion(-)
create mode 100755 git-hooks/commit-msg
create mode 100644 git-hooks/commit-msg-files.awk
create mode 100755 git-hooks/post-commit
create mode 100755 git-hooks/pre-commit
create mode 100755 git-hooks/pre-push
create mode 100755 git-hooks/prepare-commit-msg
diff --git a/CONTRIBUTE.org b/CONTRIBUTE.org
index 8f94e875b..93afb08ef 100644
--- a/CONTRIBUTE.org
+++ b/CONTRIBUTE.org
@@ -16,6 +16,11 @@ You can contribute with bug reports and patches.
See these [[https://orgmode.org/worg/org-contribute.html#org069b83a][directions]].
+** Git Hooks
+
+We provide several git hook scripts to facilitate validation of commit
+messages and structure. See [[https://orgmode.org/worg/org-contribute.html#git-hooks][these directions]] for how to set them up.
+
* As an Org maintainer
We encourage you to volunteer to maintain one of the Org files.
diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg
new file mode 100755
index 000000000..bff210f14
--- /dev/null
+++ b/git-hooks/commit-msg
@@ -0,0 +1,192 @@
+#!/bin/sh
+# Check the format of Org mode change log entries.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+# Written by Paul Eggert.
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk=gawk
+else
+ awk=awk
+fi
+
+# Use a UTF-8 locale if available, so that the UTF-8 check works.
+# Use U+00A2 CENT SIGN to test whether the locale works.
+cent_sign_utf8_format='\302\242\n'
+cent_sign=`printf "$cent_sign_utf8_format"`
+replacement_character_utf8_format='\357\277\275\n'
+replacement_character=`printf "$replacement_character_utf8_format"`
+print_at_sign='BEGIN {print substr("'$cent_sign'@", 2)}'
+at_sign=`$awk "$print_at_sign" </dev/null 2>/dev/null`
+if test "$at_sign" != @; then
+ at_sign=`LC_ALL=en_US.UTF-8 $awk "$print_at_sign" </dev/null 2>/dev/null`
+ if test "$at_sign" = @; then
+ LC_ALL=en_US.UTF-8
+ else
+ LC_ALL=C
+ fi
+ export LC_ALL
+fi
+
+# Check the log entry.
+exec $awk \
+ -v at_sign="$at_sign" \
+ -v cent_sign="$cent_sign" \
+ -v file="$1" \
+ -v replacement_character="$replacement_character" \
+'
+ BEGIN {
+ # These regular expressions assume traditional Unix unibyte behavior.
+ # They are needed for old or broken versions of awk, e.g.,
+ # mawk 1.3.3 (1996), or gawk on MSYS (2015), and/or for systems that
+ # cannot use UTF-8 as the codeset for the locale.
+ space = "[ \f\n\r\t\v]"
+ non_space = "[^ \f\n\r\t\v]"
+
+ # The non_print below rejects control characters and surrogates
+ # UTF-8 for: 0x01-0x1f 0x7f 0x80-0x9f 0xd800-0xdbff 0xdc00-0xdfff
+ non_print = "[\1-\37\177]|\302[\200-\237]|\355[\240-\277][\200-\277]"
+
+ # Match HTTP URLs so that we can ignore line length with long URLs
+ http_url = "http[s]?:"
+
+ # Prefer POSIX regular expressions if available, as they do a
+ # better job of checking. Similarly, prefer POSIX negated
+ # expressions if UTF-8 also works.
+ if (" " ~ /[[:space:]]/) {
+ space = "[[:space:]]"
+ if (at_sign == "@" && cent_sign ~ /^[[:print:]]$/) {
+ non_space = "[^[:space:]]"
+ non_print = "[^[:print:]]"
+ }
+ }
+ c_lower = "abcdefghijklmnopqrstuvwxyz"
+ unsafe_gnu_url = "(http|ftp)://([" c_lower ".]*\\.)?(gnu|fsf)\\.org"
+ }
+
+ { input[NR] = $0 }
+
+ /^#/ {
+ # Ignore every line after a scissors line.
+ if (/^# *---* *(>[8%]|[8%]<) *---* *$/) { exit }
+
+ # Ignore comment lines.
+ next
+ }
+
+ !/^.*$/ {
+ print "Invalid character (not UTF-8) in commit message"
+ status = 1
+ }
+
+ /(^|[^\\])`[^'\''`]+`/ {
+ print "Markdown-style quotes in commit message"
+ status = 1
+ }
+
+ /\. [[:alnum:]]/ {
+ print "Two spaces required between sentences."
+ status = 1
+ }
+
+ nlines == 0 && $0 !~ non_space { next }
+
+ { nlines++ }
+
+ nlines == 1 {
+ # Ignore special markers used by "git rebase --autosquash".
+ if (! sub(/^fixup! /, ""))
+ sub(/^squash! /, "")
+
+ if ($0 ~ "^" space) {
+ print "White space at start of commit message'\''s first line"
+ status = 1
+ }
+ }
+
+ nlines == 2 && $0 ~ non_space {
+ print "Nonempty second line in commit message"
+ status = 1
+ }
+
+ {
+ # Expand tabs to spaces for length calculations etc.
+ while (match($0, /\t/)) {
+ before_tab = substr($0, 1, RSTART - 1)
+ after_tab = substr($0, RSTART + 1)
+ $0 = sprintf("%s%*s%s", before_tab, 8 - (RSTART - 1) % 8, "", after_tab)
+ }
+ }
+
+ # Enforce line length unless the line contains a URL
+ $0 !~ http_url {
+ if (78 < length && $0 ~ space) {
+ print "Line longer than 78 characters in commit message"
+ status = 1
+ } else if (140 < length) {
+ print "Word longer than 140 characters in commit message"
+ status = 1
+ }
+ }
+
+ /^Signed-off-by: / {
+ print "'\''Signed-off-by:'\'' in commit message"
+ status = 1
+ }
+
+ $0 ~ unsafe_gnu_url {
+ needs_rewriting = 1
+ }
+
+ $0 ~ non_print {
+ print "Unprintable character in commit message"
+ status = 1
+ }
+ $0 ~ replacement_character {
+ print "Replacement character in commit message"
+ status = 1
+ }
+
+ END {
+ if (nlines == 0) {
+ print "Empty commit message"
+ status = 1
+ }
+ if (status == 0 && needs_rewriting) {
+ for (i = 1; i <= NR; i++) {
+ line = input[i]
+ while (match(line, unsafe_gnu_url)) {
+ prefix = substr(line, 1, RSTART - 1)
+ suffix = substr(line, RSTART)
+ line = prefix "https:" substr(suffix, 5 + (suffix ~ /^http:/))
+ }
+ print line >file
+ }
+ if (close(file) != 0) {
+ print "Cannot rewrite: " file
+ status = 1
+ }
+ }
+ if (status != 0) {
+ print "Commit aborted; please see the file 'CONTRIBUTE.org'"
+ }
+ exit status
+ }
+' <"$1"
diff --git a/git-hooks/commit-msg-files.awk b/git-hooks/commit-msg-files.awk
new file mode 100644
index 000000000..d6aec09cf
--- /dev/null
+++ b/git-hooks/commit-msg-files.awk
@@ -0,0 +1,128 @@
+# Check the file list of Org mode change log entries for each commit SHA.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This script accepts a list of (unabbreviated) Git commit SHAs, and
+# will then iterate over them to check that any files mentioned in the
+# commit message are actually present in the commit's diff. If not,
+# it will print out the incorrect file names and return 1.
+
+# You can also pass "-v reason=pre-push", which will add more-verbose
+# output, indicating the abbreviated commit SHA and first line of the
+# commit message for any improper commits.
+
+### Code:
+
+function get_commit_changes(commit_sha, changes, cmd, i, j, len, \
+ bits, filename) {
+ # Collect all the files touched in the specified commit.
+ cmd = ("git show --name-status --first-parent --format= " commit_sha)
+ while ((cmd | getline) > 0) {
+ for (i = 2; i <= NF; i++) {
+ len = split($i, bits, "/")
+ for (j = 1; j <= len; j++) {
+ if (j == 1)
+ filename = bits[j]
+ else
+ filename = filename "/" bits[j]
+ changes[filename] = 1
+ }
+ }
+ }
+ close(cmd)
+}
+
+function check_commit_msg_files(commit_sha, verbose, changes, good, \
+ cmd, msg, filenames_str, filenames, i) {
+ get_commit_changes(commit_sha, changes)
+ good = 1
+
+ cmd = ("git log -1 --format=%B " commit_sha)
+ while ((cmd | getline) > 0) {
+ if (verbose && ! msg)
+ msg = $0
+
+ # Find file entries in the commit message. We look at any line
+ # starting with "*" (possibly prefixed by "; ") followed by a ":",
+ # possibly on a different line. If we encounter a blank line
+ # without seeing a ":", then we don't treat that as a file entry.
+
+ # Accumulate the contents of a (possible) file entry.
+ if (/^[ \t]*$/)
+ filenames_str = ""
+ else if (/^(; )?\*[ \t]+[[:alnum:]]/)
+ filenames_str = $0
+ else if (filenames_str)
+ filenames_str = (filenames_str $0)
+
+ # We have a file entry; analyze it.
+ if (filenames_str && /:/) {
+ # Delete the leading "*" and any trailing information.
+ sub(/^(; )?\*[ \t]+/, "", filenames_str)
+ sub(/[ \t]*[[(<:].*$/, "", filenames_str)
+
+ # There might be multiple files listed in this entry, separated
+ # by spaces (and possibly a comma). Iterate over each of them.
+ split(filenames_str, filenames, ",[ \t]+")
+ for (i in filenames) {
+ # Remove trailing slashes from any directory entries.
+ sub(/\/$/, "", filenames[i])
+
+ if (length(filenames[i]) && ! (filenames[i] in changes)) {
+ if (good) {
+ # Print a header describing the error.
+ if (verbose)
+ printf("In commit %s \"%s\"...\n", substr(commit_sha, 1, 10), msg)
+ printf("Files listed in commit message, but not in diff:\n")
+ }
+ printf(" %s\n", filenames[i])
+ good = 0
+ }
+ }
+
+ filenames_str = ""
+ }
+ }
+ close(cmd)
+
+ return good
+}
+
+BEGIN {
+ if (reason == "pre-push")
+ verbose = 1
+}
+
+/^[a-z0-9]{40}$/ {
+ if (! check_commit_msg_files($0, verbose)) {
+ status = 1
+ }
+}
+
+END {
+ if (status != 0) {
+ if (reason == "pre-push")
+ error_msg = "Push aborted"
+ else
+ error_msg = "Bad commit message"
+ printf("%s; please see the file 'CONTRIBUTE.org'\n", error_msg)
+ }
+ exit status
+}
diff --git a/git-hooks/post-commit b/git-hooks/post-commit
new file mode 100755
index 000000000..0531eefdd
--- /dev/null
+++ b/git-hooks/post-commit
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Check the file list of Org mode change log entries after committing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs after a commit is finalized and checks that the files
+# mentioned in the commit message match the diff. We perform this in
+# the post-commit phase so that we can be sure we properly detect all
+# the files in the diff (this is difficult during the commit-msg hook,
+# since there's no cross-platform way to detect when a commit is being
+# amended).
+
+# However, since this is a post-commit hook, it's too late to error
+# out and abort the commit: it's already done! As a result, this hook
+# is purely advisory, and instead we error out when trying to push
+# (see "pre-push" in this directory).
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+git rev-parse HEAD | $awk -v reason=post-commit \
+ -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit
new file mode 100755
index 000000000..b17e9bd31
--- /dev/null
+++ b/git-hooks/pre-commit
@@ -0,0 +1,83 @@
+#!/bin/sh
+# Check file names in git commits for Org mode.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+LC_ALL=C
+export LC_ALL
+
+# If this is a system where /bin/sh isn't sufficient to
+# run git-sh-setup, use a working shell as a recourse.
+if test -x "/usr/xpg4/bin/sh" && test -z "$POSIX_SHELL"; then
+ POSIX_SHELL=1
+ export POSIX_SHELL
+ exec "/usr/xpg4/bin/sh" `dirname $0`/pre-commit
+fi
+
+exec >&2
+
+. git-sh-setup
+
+# When doing a two-way merge, ignore problems that came from the other
+# side of the merge.
+head=HEAD
+if test -r "$GIT_DIR"/MERGE_HEAD && test "$GIT_MERGE_CHECK_OTHER" != true; then
+ merge_heads=`cat "$GIT_DIR"/MERGE_HEAD` || exit
+ for merge_head in $merge_heads; do
+ case $head in
+ HEAD) head=$merge_head;;
+ # For multi-head merges, there's no easy way to ignore merged-in
+ # changes. But if you're doing multi-head merges, presumably
+ # you know how to handle any ensuing problems.
+ *) head=HEAD; break;;
+ esac
+ done
+fi
+
+git_diff='git diff --cached --name-only --diff-filter=A'
+
+# 'git diff' will backslash escape tabs and newlines, so we don't have
+# to worry about word splitting here.
+$git_diff $head |
+LC_ALL=C grep -E 'ChangeLog|^-|/-|[^-+./_0-9A-Z_a-z]' |
+while IFS= read -r new_name; do
+ case $new_name in
+ -* | */-*)
+ echo "$new_name: File name component begins with '-'."
+ exit 1;;
+ ChangeLog.android)
+ # This file is explicitly ok.
+ ;;
+ ChangeLog | */ChangeLog)
+ echo "$new_name: Please use git commit messages, not ChangeLog files."
+ exit 1;;
+ *)
+ echo "$new_name: File name does not consist of -+./_ or ASCII letters or digits."
+ exit 1;;
+ esac
+done
+
+# The '--check' option of git diff-index makes Git complain if changes
+# introduce whitespace errors. This can be a pain when editing test
+# files that deliberately contain lines with trailing whitespace.
+# To work around the problem you can run a command like 'git config
+# core.whitespace -trailing-space'. It may be better to revamp the
+# tests so that trailing spaces are generated on the fly rather than
+# being committed as source.
+
+exec git diff-index --check --cached $head --
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
new file mode 100755
index 000000000..1a3a6e9dd
--- /dev/null
+++ b/git-hooks/pre-push
@@ -0,0 +1,88 @@
+#!/bin/sh
+# Check the file list of Org mode change log entries before pushing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs before pushing a series of commits and checks that
+# the files mentioned in each commit message match the diffs. This
+# helps ensure that the resulting change logs are correct, which
+# should prevent errors when generating etc/AUTHORS.
+
+# These checks also happen in the "post-commit" hook (which see), but
+# that hook can't abort a commit; it just advises the committer to fix
+# the commit so that this hook runs without errors.
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+# Standard input receives lines of the form:
+# <local ref> SP <local sha> SP <remote ref> SP <remote sha> LF
+$awk -v origin_name="$1" '
+ # If the local SHA is all zeroes, ignore it.
+ $2 ~ /^0{40}$/ {
+ next
+ }
+
+ # Check any lines with a valid local SHA and whose remote ref is
+ # master or an emacs-NN release branch. (We want to avoid checking
+ # feature or scratch branches here.)
+ $2 ~ /^[a-z0-9]{40}$/ && $3 ~ /^refs\/heads\/(master|emacs-[0-9]+)$/ {
+ newref = $2
+ # If the remote SHA is all zeroes, this is a new object to be
+ # pushed (likely a branch)...
+ if ($4 ~ /^0{40}$/) {
+ back = 0
+ # ... Go backwards until we find a SHA on an origin branch.
+ # Stop trying after 1000 commits, just in case...
+ for (back = 0; back < 1000; back++) {
+ cmd = ("git branch -r -l '\''" origin_name "/*'\''" \
+ " --contains " newref "~" back)
+ rv = (cmd | getline)
+ close(cmd)
+ if (rv > 0)
+ break;
+ }
+
+ cmd = ("git rev-parse " newref "~" back)
+ cmd | getline oldref
+ if (!(oldref ~ /^[a-z0-9]{40}$/)) {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+ close(cmd)
+ } else if ($4 ~ /^[a-z0-9]{40}$/) {
+ oldref = $4
+ } else {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+
+ # Print every SHA after oldref, up to (and including) newref.
+ system("git rev-list --first-parent --reverse " oldref ".." newref)
+ }
+' | $awk -v reason=pre-push -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/prepare-commit-msg b/git-hooks/prepare-commit-msg
new file mode 100755
index 000000000..4caf6f50f
--- /dev/null
+++ b/git-hooks/prepare-commit-msg
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Check the format of Org mode change log entries.
+
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+# Next use /usr/xpg4/bin/awk if available, since the script
+# doesn't support Unix awk.
+elif test -x /usr/xpg4/bin/awk; then
+ awk="/usr/xpg4/bin/awk"
+else
+ awk="awk"
+fi
+
+exec $awk "
+ # Catch the case when someone ran git-commit with -s option,
+ # which automatically adds Signed-off-by.
+ /^Signed-off-by: / {
+ print \"'Signed-off-by:' in commit message\"
+ status = 1
+ }
+ END {
+ if (status != 0) {
+ print \"Commit aborted; please see the file 'CONTRIBUTE.org'\"
+ }
+ exit status
+ }
+" <"$COMMIT_MSG_FILE"
diff --git a/mk/targets.mk b/mk/targets.mk
index 20f2ae504..cedde0522 100644
--- a/mk/targets.mk
+++ b/mk/targets.mk
@@ -9,6 +9,9 @@ SUBDIRS = $(OTHERDIRS) $(LISPDIRS)
INSTSUB = $(SUBDIRS:%=install-%)
ORG_MAKE_DOC ?= info html pdf
+GITDIR = .git/hooks
+GITHOOKS = commit-msg commit-msg-files.awk post-commit pre-commit prepare-commit-msg pre-push
+
ifneq ($(wildcard .git),)
# Use the org.el header.
ORGVERSION := $(patsubst %-dev,%,$(shell $(BATCH) --eval "(require 'lisp-mnt)" \
@@ -130,12 +133,21 @@ autoloads: lisp
repro: cleanall autoloads
-@$(REPRO) &
+# Implicit rule to copy Git hooks in
+$(GITDIR)/%: git-hooks/%
+ cp -f $< $@
+
+githooks: $(addprefix $(GITDIR)/,$(GITHOOKS))
+
+cleangithooks:
+ $(RM) $(addprefix $(GITDIR)/,$(GITHOOKS))
+
cleandirs:
$(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) cleanall;)
clean: cleanlisp cleandoc
-cleanall: cleandirs cleantest
+cleanall: cleandirs cleantest cleangithooks
-$(FIND) . \( -name \*~ -o -name \*# -o -name .#\* \) -exec $(RM) {} +
-$(FIND) $(CLEANDIRS) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} +
--
2.43.0
[-- Attachment #5: 0001-org-contribute.org-Add-section-with-git-hook-instruc.patch --]
[-- Type: application/octet-stream, Size: 1478 bytes --]
From 7cb8ffc18f0e3774e9acf4aadfc703dee95cd8aa Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 18 Nov 2025 22:03:46 -0700
Subject: [PATCH] org-contribute.org: Add section with git hook instructions
These instructions go in concert with a set of changes to the Org codebase
to provide new git hook scripts for commit validation.
---
org-contribute.org | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/org-contribute.org b/org-contribute.org
index 15773859..3a501631 100644
--- a/org-contribute.org
+++ b/org-contribute.org
@@ -506,6 +506,29 @@ Here is an example for such a message:
TINYCHANGE
#+end_example
+** Git Hooks
+:PROPERTIES:
+:CUSTOM_ID: git-hooks
+:END:
+
+The Org repository has a set of git hook scripts that can be used to
+perform validation on commit messages and structure. If you would like
+to utilize these scripts, you can link the existing files under the
+~git-hooks~ directory into the ~.git/hooks~ subdirectory. If you
+want to use all of the hooks, there is a Makefile target that does
+this for you:
+
+#+begin_src shell
+make githooks
+#+end_src
+
+If you would like to remove the hooks, simply delete them from
+~.git/hooks~ or run the Makefile target (~cleanall~ will also run this):
+
+#+begin_src shell
+make cleangithooks
+#+end_src
+
** Producing ChangeLog entries
If you are using [[https://magit.vc/][magit]] in Emacs, the ChangeLog for such entries can be
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-07 5:01 ` Jacob S. Gordon
@ 2025-11-19 5:17 ` Derek Chen-Becker
0 siblings, 0 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-19 5:17 UTC (permalink / raw)
To: Jacob S. Gordon; +Cc: Ihor Radchenko, Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1791 bytes --]
Hi Jacob,
Thanks, the current set of patches is definitely just a starting point. I
think there are several other locations to address discussed in this
thread, including the one you mentioned. Once I get the initial set of
patches in I plan to follow up and refactor to use some of the new helper
functions I've defined to tackle other locations dealing with priorities.
Cheers,
Derek
On Thu, Nov 6, 2025 at 10:01 PM Jacob S. Gordon <[email protected]>
wrote:
> Hey Derek,
>
> Nice work! A small bit of feedback tying back to this bug with default
> priorities:
>
>
> https://list.orgmode.org/orgmode/[email protected]/
>
> In ‘org-entry-properties’ under lisp/org.el, ‘org-priority-default’ is
> treated like a character:
>
> (if (looking-at org-priority-regexp)
> (match-string-no-properties 2)
> (char-to-string org-priority-default))
>
> so, e.g., column view on the heading will show ‘^G’ instead of 7:
>
> #+PRIORITIES: 1 10 7
> * Heading
>
> I think something similar is happening in
> ‘org-property-get-allowed-values’, and in org-mouse (which does its
> own thing for priorities).
>
> Best,
>
> --
> Jacob S. Gordon
> [email protected]
> Please avoid sending me HTML emails and MS Office documents.
> https://useplaintext.email/#etiquette
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3906 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-19 5:15 ` Derek Chen-Becker
@ 2025-11-22 14:15 ` Ihor Radchenko
2025-11-22 15:07 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-22 14:15 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> OK, here are the updated numeric priority patches, along with a patch to
> add the git hook scripts (with updates based on your feedback) and a
> corresponding WORG patch with instructions on running the targets to link
> and clean up the scripts. I modified the commit test scripts to only
> validate length of lines not containing a URL and replaced mentions of GNU
> Emacs with Org mode. I'm looking forward to feedback.
Thanks! Almost :)
> + # Check any lines with a valid local SHA and whose remote ref is
> + # master or an emacs-NN release branch. (We want to avoid checking
> + # feature or scratch branches here.)
This is codifying Emacs conventions absent in Org mode.
We do not have "master", we do not have "emacs-XX".
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 14:15 ` Ihor Radchenko
@ 2025-11-22 15:07 ` Derek Chen-Becker
2025-11-22 15:32 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-22 15:07 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 898 bytes --]
On Sat, Nov 22, 2025 at 7:15 AM Ihor Radchenko <[email protected]> wrote:
>
> This is codifying Emacs conventions absent in Org mode.
> We do not have "master", we do not have "emacs-XX".
>
Maybe should I remove the `pre-push' script from the patch? The other
scripts at least seem to mostly focus on the commit message.
`prepare-commit-msg' also makes a check for "Signed-off-by" that I'm not
sure is an Org convention, so I could remove that, too.
Cheers,
Derek
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 2639 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 15:07 ` Derek Chen-Becker
@ 2025-11-22 15:32 ` Ihor Radchenko
2025-11-22 16:22 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-22 15:32 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
>> This is codifying Emacs conventions absent in Org mode.
>> We do not have "master", we do not have "emacs-XX".
>
> Maybe should I remove the `pre-push' script from the patch? The other
> scripts at least seem to mostly focus on the commit message.
I think you can simply replace (master|emacs-[0-9]+) with
main|release_[0-9.]+
> `prepare-commit-msg' also makes a check for "Signed-off-by" that I'm not
> sure is an Org convention, so I could remove that, too.
No, I think. The purpose of that check is avoiding --signoff practice
that is accepted by a number of projects [1], but firmly rejected by
FSF. Also, see
https://archive.fosdem.org/2025/schedule/event/fosdem-2025-5376-managing-copyrights-in-free-software-projects-discussion-panel/
[1] https://stackoverflow.com/questions/1962094/what-is-the-sign-off-feature-in-git-for
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 15:32 ` Ihor Radchenko
@ 2025-11-22 16:22 ` Derek Chen-Becker
2025-11-22 16:37 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-22 16:22 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1828 bytes --]
Here are the patches with the `pre-push' script updated to replace master
-> main and to fix emacs-NN -> release.
Cheers,
Derek
On Sat, Nov 22, 2025 at 8:32 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> >> This is codifying Emacs conventions absent in Org mode.
> >> We do not have "master", we do not have "emacs-XX".
> >
> > Maybe should I remove the `pre-push' script from the patch? The other
> > scripts at least seem to mostly focus on the commit message.
>
> I think you can simply replace (master|emacs-[0-9]+) with
> main|release_[0-9.]+
>
> > `prepare-commit-msg' also makes a check for "Signed-off-by" that I'm not
> > sure is an Org convention, so I could remove that, too.
>
> No, I think. The purpose of that check is avoiding --signoff practice
> that is accepted by a number of projects [1], but firmly rejected by
> FSF. Also, see
>
> https://archive.fosdem.org/2025/schedule/event/fosdem-2025-5376-managing-copyrights-in-free-software-projects-discussion-panel/
>
> [1]
> https://stackoverflow.com/questions/1962094/what-is-the-sign-off-feature-in-git-for
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 4332 bytes --]
[-- Attachment #2: 0001-org-contribute.org-Add-section-with-git-hook-instruc.patch --]
[-- Type: application/octet-stream, Size: 1478 bytes --]
From 7cb8ffc18f0e3774e9acf4aadfc703dee95cd8aa Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 18 Nov 2025 22:03:46 -0700
Subject: [PATCH] org-contribute.org: Add section with git hook instructions
These instructions go in concert with a set of changes to the Org codebase
to provide new git hook scripts for commit validation.
---
org-contribute.org | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/org-contribute.org b/org-contribute.org
index 15773859..3a501631 100644
--- a/org-contribute.org
+++ b/org-contribute.org
@@ -506,6 +506,29 @@ Here is an example for such a message:
TINYCHANGE
#+end_example
+** Git Hooks
+:PROPERTIES:
+:CUSTOM_ID: git-hooks
+:END:
+
+The Org repository has a set of git hook scripts that can be used to
+perform validation on commit messages and structure. If you would like
+to utilize these scripts, you can link the existing files under the
+~git-hooks~ directory into the ~.git/hooks~ subdirectory. If you
+want to use all of the hooks, there is a Makefile target that does
+this for you:
+
+#+begin_src shell
+make githooks
+#+end_src
+
+If you would like to remove the hooks, simply delete them from
+~.git/hooks~ or run the Makefile target (~cleanall~ will also run this):
+
+#+begin_src shell
+make cleangithooks
+#+end_src
+
** Producing ChangeLog entries
If you are using [[https://magit.vc/][magit]] in Emacs, the ChangeLog for such entries can be
--
2.43.0
[-- Attachment #3: 0003-lisp-org.el-Add-proper-support-for-numeric-prioritie.patch --]
[-- Type: application/octet-stream, Size: 21730 bytes --]
From d2ea53cd52fa6657adeb898d0ba6cc1bb7b678da Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Sat, 6 Sep 2025 07:07:34 -0600
Subject: [PATCH 3/3] lisp/org.el: Add proper support for numeric priorities
* doc/org-manual.org: Update the manual to clarify the allowed values for
priorities.
* lisp/org.el (org-priority-highest, org-priority-lowest,
org-priority-default): Update the `defcustom' types so that they perform
validation on the values entered.
(org-priority-numeric-value-regexp): Add a new regexp for valid numeric
priority values for use in input parsing.
(org-priority-regexp): Update the priority cookie regular expression to
fully validate numeric values by using the new
`org-priority-numeric-value-regexp', and update the documentation to match.
(org-priority-valid-cookie-string-p): Add a validation predicate to test
priority cookie strings against the updated `org-priority-regexp' regex.
(org-priority-valid-value-p): Add a validation predicate to test priority
values against allowed values and the currently defined high/low range.
Optionally ignore the defined range in the test so that the predicate can
be used in defcustom validation.
(org-priority-number-p): Add a validation predicate to test if a priority
is in the numeric range (0-64).
(org-priority-to-string): Add a function to provide a consistent string
value from a given priority value.
(org-priority): Update the docstring to clarify acceptable values as well
as the implicit conversion from lower-case to upper-case for alphabetic
values. Refactor to use the new predicate functions defined in this commit
for validation of values, particularly for double-digit numeric values.
Rename variables used in the function to clarify intent and improve
readability. Replace bare space characters ("?\ ") with explicit
escapes ("?\s") to improve readability. Refactor code for interactively
reading new priorities from the user to utilize the predicate functions
defined in this commit and perform additional validation. Utilize the new
predicate functions to clarify the wrapping logic for `up' and `down'
actions.
(org-entry-put): Replace bare space escape for removal with the `remove'
symbol for consistency.
* testing/lisp/test-org.el: Add unit tests for the new predicates and
format functions related to priority. Add unit tests for the new
validations for `org-priority-highest', `org-priority-lowest', and
`org-priority-default'.
---
doc/org-manual.org | 8 +-
lisp/org.el | 184 ++++++++++++++++++++++++++-------------
testing/lisp/test-org.el | 97 +++++++++++++++++++++
3 files changed, 224 insertions(+), 65 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 2da7f12b8..bf8d89360 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4627,7 +4627,7 @@ You can also use numeric values for priorities, such as
When using numeric priorities, you need to set ~org-priority-highest~,
~org-priority-lowest~ and ~org-priority-default~ to integers, which
-must all be strictly inferior to 65.
+must all be a non-negative integer between 0 and 64, inclusive.
Priorities can be attached to any heading; they do not need to be
TODO items.
@@ -4662,8 +4662,10 @@ TODO items.
#+vindex: org-priority-default
You can change the range of allowed priorities by setting the
variables ~org-priority-highest~, ~org-priority-lowest~, and
-~org-priority-default~. For an individual buffer, you may set these
-values (highest, lowest, default) like this (please make sure that the
+~org-priority-default~. Valid priority values are single uppercase
+Latin alphabetical characters A-Z, and non-negative integers in between 0
+and 64, inclusive. For an individual buffer, you may set these values
+(highest, lowest, default) like this (please make sure that the
highest priority is earlier in the alphabet than the lowest priority):
#+cindex: @samp{PRIORITIES}, keyword
diff --git a/lisp/org.el b/lisp/org.el
index 58b026594..fabbff077 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -2428,7 +2428,6 @@ set a priority."
:type 'boolean)
(defvaralias 'org-highest-priority 'org-priority-highest)
-
(defcustom org-priority-highest ?A
"The highest priority of TODO items.
@@ -2446,9 +2445,8 @@ smaller than `org-priority-lowest': for example, if \"A\" is the
highest priority, it is smaller than the lowest \"C\" priority:
65 < 67."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-lowest-priority 'org-priority-lowest)
(defcustom org-priority-lowest ?C
@@ -2468,9 +2466,8 @@ than `org-priority-highest': for example, if \"C\" is the lowest
priority, it is greater than the highest \"A\" priority: 67 >
65."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defvaralias 'org-default-priority 'org-priority-default)
(defcustom org-priority-default ?B
@@ -2484,9 +2481,8 @@ in this range exclusive or inclusive to the range boundaries. Else the
first step refuses to set the default and the second will fall back on
\(depending on the command used) the highest or lowest priority."
:group 'org-priorities
- :type '(choice
- (character :tag "Character")
- (integer :tag "Integer (< 65)")))
+ :type '(restricted-sexp :tag "Number 0-64 or uppercase character A-Z"
+ :match-alternatives ((lambda (val) (org-priority-valid-value-p val t)))))
(defcustom org-priority-start-cycle-with-default t
"Non-nil means start with default priority when starting to cycle.
@@ -11295,14 +11291,63 @@ from the `before-change-functions' in the current buffer."
;;;; Priorities
-(defvar org-priority-regexp ".*?\\(\\[#\\([A-Z0-9]+\\)\\] ?\\)"
+(defvar org-priority-value-regexp "[A-Z]\\|[0-9]\\|[1-5][0-9]\\|6[0-4]"
+ "Regular expression matching valid priority values.
+The priority value must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in the range 0
+through 64.")
+
+(defvar org-priority-regexp
+ (format ".*?\\(\\[#\\(%s\\)\\] ?\\)" org-priority-value-regexp)
"Regular expression matching the priority indicator.
A priority indicator can be e.g. [#A] or [#1].
+The value of the priority cookie must be a capital Latin
+alphabetic character, A through Z, or can be an integer value in
+the range 0 through 64.
This regular expression matches these groups:
0 : the whole match, e.g. \"TODO [#A] Hack\"
1 : the priority cookie, e.g. \"[#A]\"
2 : the value of the priority cookie, e.g. \"A\".")
+(defun org-priority-valid-cookie-string-p (priority)
+ "Return t if the PRIORITY is a valid priority cookie, nil otherwise."
+ (cond
+ ((stringp priority)
+ (let ((case-fold-search nil)) ;; Force case-sensitive match
+ ;; (and ... t) to force explicit t/nil
+ (and (string-match-p org-priority-regexp priority) t)))))
+
+(defun org-priority-valid-value-p (priority &optional ignore-user-bounds)
+ "Return t if the PRIORITY is a valid priority value (0-64, A-Z), nil otherwise.
+Also validate that the priority value is within the current bounds of
+ `org-priority-lowest' and `org-priority-highest' unless IGNORE-USER-BOUNDS is
+non-nil."
+ ;; Although we can have either numeric or alphabetic priorities,
+ ;; we simplify here by treating everything as integers because the ASCII
+ ;; alphabetic range also fits in an integer range.
+ (and (integerp priority)
+ ;; validate that the value is within valid ranges
+ (or (<= 0 priority 64)
+ (<= ?A priority ?Z))
+ ;; more specifically, validate within the current high/low ranges
+ ;; unless the caller is ignoring the range
+ (or ignore-user-bounds
+ (>= org-priority-lowest priority org-priority-highest))))
+
+(defun org-priority-number-p (priority)
+ "Return t if the PRIORITY is an integer 0 through 64, inclusive, nil otherwise."
+ (and (integerp priority)
+ (<= 0 priority 64)))
+
+(defun org-priority-to-string (priority)
+ "Return a string form of PRIORITY based on whether it's numeric or alphabetic."
+ ;; we check whether this is even a valid value, ignoring the current high/low bounds
+ (if (org-priority-valid-value-p priority t)
+ (if (org-priority-number-p priority)
+ (number-to-string priority)
+ (format "%c" priority))
+ (error "Invalid priority value `%s'" priority)))
+
(defun org-priority-up ()
"Increase the priority of the current item."
(interactive)
@@ -11319,57 +11364,74 @@ This regular expression matches these groups:
When called interactively with a `\\[universal-argument]' prefix,
show the priority in the minibuffer instead of changing it.
-When called programmatically, ACTION can be `set', `up', `down',
-or a character."
+When called programmatically, ACTION can be `set', `up', `down', `remove', an
+uppercase alphabetic character A through Z, or an integer 0 through 64,
+inclusive. If a lower-case character is passed as ACTION or entered via
+interactive prompt, it will automatically be converted to uppercase."
(interactive "P")
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
(user-error "Priority commands are disabled"))
+ ;; If action was not provided, default to "set", which will prompt the user
(setq action (or action 'set))
- (let ((nump (< org-priority-lowest 65))
- current new news have remove)
+ (let ((is-numeric-priority (org-priority-number-p org-priority-lowest))
+ current-value new-value new-value-string has-existing-cookie remove)
(save-excursion
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
- (setq current (org-priority-to-value ms)
- have t)))
+ (setq current-value (org-priority-to-value ms)
+ has-existing-cookie t)))
(cond
((eq action 'remove)
- (setq remove t new ?\ ))
+ (setq remove t new-value ?\s))
+ ;; set and a value are treated similarly, but only if the
+ ;; value is a valid priority
((or (eq action 'set)
- (integerp action))
+ (org-priority-valid-value-p action))
(if (not (eq action 'set))
- (setq new action)
+ (setq new-value action)
(setq
- new
- (if nump
- (let* ((msg (format "Priority %s-%s, SPC to remove: "
- (number-to-string org-priority-highest)
- (number-to-string org-priority-lowest)))
- (s (if (< 9 org-priority-lowest)
- (read-string msg)
- (message msg)
- (char-to-string (read-char-exclusive)))))
- (if (equal s " ") ?\s (string-to-number s)))
- (progn (message "Priority %c-%c, SPC to remove: "
- org-priority-highest org-priority-lowest)
- (save-match-data
- (setq new (read-char-exclusive)))))))
- (when (and (= (upcase org-priority-highest) org-priority-highest)
- (= (upcase org-priority-lowest) org-priority-lowest))
- (setq new (upcase new)))
- (cond ((equal new ?\s) (setq remove t))
- ((or (< (upcase new) org-priority-highest) (> (upcase new) org-priority-lowest))
- (user-error
- (if nump
- "Priority must be between `%s' and `%s'"
- "Priority must be between `%c' and `%c'")
- org-priority-highest org-priority-lowest))))
+ new-value
+ (let* ((msg (format "Priority %s-%s, SPC to remove: "
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ ;; Read input as a string if the current high/low range could
+ ;; be two digits. Otherwise, we can just read a single
+ ;; character and save the user from pressing <enter>
+ (s (if (and is-numeric-priority
+ (>= org-priority-lowest 10))
+ (read-string msg)
+ (char-to-string (read-char-exclusive msg)))))
+ ;; Space is a special value indicating removal. For numeric
+ ;; priorities, parse the numeric value or ensure an upper case
+ ;; character (and convert back to a character for validation)
+ (if (string-equal s " ")
+ ?\s
+ (if is-numeric-priority
+ (progn
+ ;; Validate the input string to ensure it's a valid value because
+ ;; string-to-number returns zero for a non-numeric string, which could
+ ;; be a valid priority
+ (unless (string-match-p org-priority-value-regexp s)
+ (user-error "Priority must be a number between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))
+ (string-to-number s))
+ ;; Validation of non-numerics is handled next
+ (string-to-char (upcase s)))))))
+ ;; After reading interactive input, set removal flag if needed and
+ ;; perform validation on the new value
+ (cond
+ ((equal new-value ?\s) (setq remove t))
+ ((not (org-priority-valid-value-p new-value))
+ (user-error "Priority must be between `%s' and `%s'"
+ (org-priority-to-string org-priority-highest)
+ (org-priority-to-string org-priority-lowest)))))
((eq action 'up)
- (setq new (if have
- (1- current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1- current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-lowest ; wrap around empty to lowest
@@ -11378,8 +11440,8 @@ or a character."
org-priority-default
(1- org-priority-default))))))
((eq action 'down)
- (setq new (if have
- (1+ current) ; normal cycling
+ (setq new-value (if has-existing-cookie
+ (1+ current-value) ; normal cycling
;; last priority was empty
(if (eq last-command this-command)
org-priority-highest ; wrap around empty to highest
@@ -11388,36 +11450,34 @@ or a character."
org-priority-default
(1+ org-priority-default))))))
(t (user-error "Invalid action")))
- (when (or (< (upcase new) org-priority-highest)
- (> (upcase new) org-priority-lowest))
+ ;; Check against the current high/low range if we need to wrap
+ (when (not (org-priority-valid-value-p new-value))
(if (and (memq action '(up down))
- (not have) (not (eq last-command this-command)))
- ;; `new' is from default priority
+ (not has-existing-cookie) (not (eq last-command this-command)))
+ ;; `new-value' is from default priority
(error
"The default can not be set, see `org-priority-default' why")
- ;; normal cycling: `new' is beyond highest/lowest priority
+ ;; normal cycling: `new-value' is beyond highest/lowest priority
;; and is wrapped around to the empty priority
(setq remove t)))
- ;; Numerical priorities are limited to 64, beyond that number,
- ;; assume the priority cookie is a character.
- (setq news (if (> new 64) (format "%c" new) (format "%s" new)))
- (if have
+ (setq new-value-string (org-priority-to-string new-value))
+ (if has-existing-cookie
(if remove
(replace-match "" t t nil 1)
- (replace-match news t t nil 2))
+ (replace-match new-value-string t t nil 2))
(if remove
(user-error "No priority cookie found in line")
(let ((case-fold-search nil)) (looking-at org-todo-line-regexp))
(if (match-end 2)
(progn
(goto-char (match-end 2))
- (insert " [#" news "]"))
+ (insert " [#" new-value-string "]"))
(goto-char (match-beginning 3))
- (insert "[#" news "] "))))
+ (insert "[#" new-value-string "] "))))
(when org-auto-align-tags (org-align-tags)))
(if remove
(message "Priority removed")
- (message "Priority of current item set to %s" news)))))
+ (message "Priority of current item set to %s" new-value-string)))))
(defalias 'org-show-priority 'org-priority-show)
(defun org-priority-show ()
@@ -13460,7 +13520,7 @@ decreases scheduled or deadline date by one day."
(org-todo value)
(when org-auto-align-tags (org-align-tags)))
((equal property "PRIORITY")
- (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s))
+ (org-priority (if (org-string-nw-p value) (string-to-char value) 'remove))
(when org-auto-align-tags (org-align-tags)))
((equal property "SCHEDULED")
(forward-line)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index c25ae52ee..1a28d01ed 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -10056,6 +10056,103 @@ two
(execute-kbd-macro (kbd "C-a C-f C-f C-f C-f TAB"))
(should (equal (minibuffer-contents) "test|uniq+test")))))))
+;;; Priority customization validation
+(ert-deftest test-org/priority-customization ()
+ "Test validation of priority customization."
+ (cl-flet ((validatef (lambda (variable value)
+ (widget-apply (widget-convert (get variable 'custom-type)) :match value))))
+ ;; Valid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var 0))
+ (should (validatef var 42))
+ (should (validatef var 64)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Valid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should (validatef var ?A))
+ (should (validatef var ?M))
+ (should (validatef var ?Z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid numeric values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var -1))
+ (should-not (validatef var 200)))
+ '(org-priority-highest org-priority-lowest org-priority-default))
+ ;; Invalid alphabetic values for high/low/default
+ (seq-every-p (lambda (var)
+ (should-not (validatef var ?a))
+ (should-not (validatef var ?z)))
+ '(org-priority-highest org-priority-lowest org-priority-default))))
+
+;;; Priority validation and handling
+(ert-deftest test-org/priority-validation ()
+ "Test validation of priority cookies."
+ ;; Simple bounds checks on single alphabetic characters
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence ?A ?Z)))
+ ;; Test all valid numbers
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%d]" p)))
+ (org-priority-valid-cookie-string-p cookie)))
+ (number-sequence 0 64)))
+ ;; Invalid characters (not exhaustive)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#$]")))
+ ;; Don't accept lower-case
+ (should
+ (seq-every-p (lambda (p)
+ (let ((cookie (format "[#%c]" p)))
+ (not (org-priority-valid-cookie-string-p cookie))))
+ (number-sequence ?a ?z)))
+ ;; Invalid numberic values (< 0 or > 64)
+ (should
+ (not (org-priority-valid-cookie-string-p "[#-1]")))
+ (should
+ (not (org-priority-valid-cookie-string-p "[#65]")))
+ ;; Value tests (as opposed to cookie tests)
+ ;;
+ ;; Numeric, full range
+ (should
+ (let ((org-priority-highest 0)
+ (org-priority-lowest 64))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence 0 64))))
+ ;; Numeric, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(0 5 9 21 42 64))))
+ ;; Numeric, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest 10)
+ (org-priority-lowest 20))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(0 5 9 21 42 64))))
+ ;; ;; Alphabetic, full range
+ (should
+ (let ((org-priority-highest ?A)
+ (org-priority-lowest ?Z))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ (number-sequence ?A ?Z))))
+ ;; Alphabetic, valid value, but out of range
+ (should-not
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv))
+ '(?A ?L ?N ?Z))))
+ ;; Alphabetic, valid value, out of range, but we ignore the range
+ (should
+ (let ((org-priority-highest ?C)
+ (org-priority-lowest ?K))
+ (seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
+ '(?A ?L ?N ?Z)))))
+
(provide 'test-org)
;;; test-org.el ends here
+
--
2.43.0
[-- Attachment #4: 0002-lisp-org.el-Remove-deprecated-show-command.patch --]
[-- Type: application/octet-stream, Size: 2166 bytes --]
From 29932a0802791df6f334cf0be02f90d27b928a42 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Tue, 29 Jul 2025 06:19:32 -0600
Subject: [PATCH 2/3] lisp/org.el: Remove deprecated show command
* org.el (org-priority): Remove the deprecated show command now that we're
several versions beyond when the deprecation was introduced.
* etc/ORG-NEWS: Add an announcement for the removal of the `show' parameter.
---
etc/ORG-NEWS | 6 ++++++
lisp/org.el | 6 +-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 9113e68ea..6ee0a263f 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -161,6 +161,12 @@ Previously, the whole contents of drawer, including blank lines at the beginning
parsed as paragraph. Now, the blank lines at the beginning are stored in ~:pre-blank~
property, just as in other greater elements.
+*** The deprecated =show= parameter to =org-priority= has been removed
+
+The =show= parameter for the =org-priority= function was deprecated in
+Org 9.2 (released in 2017). Sufficient time has passed and it is being
+removed as part of refactoring for numeric priorities.
+
** New features
# We list the most important features, and the features that may
diff --git a/lisp/org.el b/lisp/org.el
index 5e9def3af..58b026594 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -11313,7 +11313,7 @@ This regular expression matches these groups:
(interactive)
(org-priority 'down))
-(defun org-priority (&optional action show)
+(defun org-priority (&optional action)
"Change the priority of an item.
When called interactively with a `\\[universal-argument]' prefix,
@@ -11322,10 +11322,6 @@ show the priority in the minibuffer instead of changing it.
When called programmatically, ACTION can be `set', `up', `down',
or a character."
(interactive "P")
- (when show
- ;; Deprecation warning inserted for Org 9.2; once enough time has
- ;; passed the SHOW argument should be removed.
- (warn "`org-priority' called with deprecated SHOW argument"))
(if (equal action '(4))
(org-priority-show)
(unless org-priority-enable-commands
--
2.43.0
[-- Attachment #5: 0001-git-hooks-Add-git-hook-scripts-along-with-instructio.patch --]
[-- Type: application/octet-stream, Size: 23443 bytes --]
From e395a383819ff678d84f04a8fc472da1ee082d2d Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 6 Nov 2025 18:52:04 -0700
Subject: [PATCH 1/3] git-hooks: Add git hook scripts along with instructions
for use
* git-hooks/commit-msg: Add commit-msg hook script. Add a check for two
spaces between sentences. Update the abort message to reference
`CONTRIBUTE.org' instead of `CONTRIBUTE'.
* git-hooks/commit-msg-files.awk: Add commit-msg-files.awk hook script.
Update the abort message to reference `CONTRIBUTE.org' instead of
`CONTRIBUTE'.
* git-hooks/post-commit: Add post-commit hook script.
* git-hooks/pre-push: Add pre-push hook script.
* git-hooks/prepare-commit-msg: Add prepare-commit-msg hook script. Update
the abort message to reference `CONTRIBUTE.org' instead of `CONTRIBUTE'.
* mk/targets.mk: Add a `git' and `cleangit' target to install and remove
the new git hook scripts, respectively. Also add `git' as a dependency for
`all' and `cleangit' as a dependency for `cleanall'.
* CONTRIBUTE.org: Add instructions for utilizing the git hook scripts.
---
CONTRIBUTE.org | 5 +
git-hooks/commit-msg | 192 +++++++++++++++++++++++++++++++++
git-hooks/commit-msg-files.awk | 128 ++++++++++++++++++++++
git-hooks/post-commit | 47 ++++++++
git-hooks/pre-commit | 83 ++++++++++++++
git-hooks/pre-push | 88 +++++++++++++++
git-hooks/prepare-commit-msg | 49 +++++++++
mk/targets.mk | 14 ++-
8 files changed, 605 insertions(+), 1 deletion(-)
create mode 100755 git-hooks/commit-msg
create mode 100644 git-hooks/commit-msg-files.awk
create mode 100755 git-hooks/post-commit
create mode 100755 git-hooks/pre-commit
create mode 100755 git-hooks/pre-push
create mode 100755 git-hooks/prepare-commit-msg
diff --git a/CONTRIBUTE.org b/CONTRIBUTE.org
index 8f94e875b..93afb08ef 100644
--- a/CONTRIBUTE.org
+++ b/CONTRIBUTE.org
@@ -16,6 +16,11 @@ You can contribute with bug reports and patches.
See these [[https://orgmode.org/worg/org-contribute.html#org069b83a][directions]].
+** Git Hooks
+
+We provide several git hook scripts to facilitate validation of commit
+messages and structure. See [[https://orgmode.org/worg/org-contribute.html#git-hooks][these directions]] for how to set them up.
+
* As an Org maintainer
We encourage you to volunteer to maintain one of the Org files.
diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg
new file mode 100755
index 000000000..bff210f14
--- /dev/null
+++ b/git-hooks/commit-msg
@@ -0,0 +1,192 @@
+#!/bin/sh
+# Check the format of Org mode change log entries.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+# Written by Paul Eggert.
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk=gawk
+else
+ awk=awk
+fi
+
+# Use a UTF-8 locale if available, so that the UTF-8 check works.
+# Use U+00A2 CENT SIGN to test whether the locale works.
+cent_sign_utf8_format='\302\242\n'
+cent_sign=`printf "$cent_sign_utf8_format"`
+replacement_character_utf8_format='\357\277\275\n'
+replacement_character=`printf "$replacement_character_utf8_format"`
+print_at_sign='BEGIN {print substr("'$cent_sign'@", 2)}'
+at_sign=`$awk "$print_at_sign" </dev/null 2>/dev/null`
+if test "$at_sign" != @; then
+ at_sign=`LC_ALL=en_US.UTF-8 $awk "$print_at_sign" </dev/null 2>/dev/null`
+ if test "$at_sign" = @; then
+ LC_ALL=en_US.UTF-8
+ else
+ LC_ALL=C
+ fi
+ export LC_ALL
+fi
+
+# Check the log entry.
+exec $awk \
+ -v at_sign="$at_sign" \
+ -v cent_sign="$cent_sign" \
+ -v file="$1" \
+ -v replacement_character="$replacement_character" \
+'
+ BEGIN {
+ # These regular expressions assume traditional Unix unibyte behavior.
+ # They are needed for old or broken versions of awk, e.g.,
+ # mawk 1.3.3 (1996), or gawk on MSYS (2015), and/or for systems that
+ # cannot use UTF-8 as the codeset for the locale.
+ space = "[ \f\n\r\t\v]"
+ non_space = "[^ \f\n\r\t\v]"
+
+ # The non_print below rejects control characters and surrogates
+ # UTF-8 for: 0x01-0x1f 0x7f 0x80-0x9f 0xd800-0xdbff 0xdc00-0xdfff
+ non_print = "[\1-\37\177]|\302[\200-\237]|\355[\240-\277][\200-\277]"
+
+ # Match HTTP URLs so that we can ignore line length with long URLs
+ http_url = "http[s]?:"
+
+ # Prefer POSIX regular expressions if available, as they do a
+ # better job of checking. Similarly, prefer POSIX negated
+ # expressions if UTF-8 also works.
+ if (" " ~ /[[:space:]]/) {
+ space = "[[:space:]]"
+ if (at_sign == "@" && cent_sign ~ /^[[:print:]]$/) {
+ non_space = "[^[:space:]]"
+ non_print = "[^[:print:]]"
+ }
+ }
+ c_lower = "abcdefghijklmnopqrstuvwxyz"
+ unsafe_gnu_url = "(http|ftp)://([" c_lower ".]*\\.)?(gnu|fsf)\\.org"
+ }
+
+ { input[NR] = $0 }
+
+ /^#/ {
+ # Ignore every line after a scissors line.
+ if (/^# *---* *(>[8%]|[8%]<) *---* *$/) { exit }
+
+ # Ignore comment lines.
+ next
+ }
+
+ !/^.*$/ {
+ print "Invalid character (not UTF-8) in commit message"
+ status = 1
+ }
+
+ /(^|[^\\])`[^'\''`]+`/ {
+ print "Markdown-style quotes in commit message"
+ status = 1
+ }
+
+ /\. [[:alnum:]]/ {
+ print "Two spaces required between sentences."
+ status = 1
+ }
+
+ nlines == 0 && $0 !~ non_space { next }
+
+ { nlines++ }
+
+ nlines == 1 {
+ # Ignore special markers used by "git rebase --autosquash".
+ if (! sub(/^fixup! /, ""))
+ sub(/^squash! /, "")
+
+ if ($0 ~ "^" space) {
+ print "White space at start of commit message'\''s first line"
+ status = 1
+ }
+ }
+
+ nlines == 2 && $0 ~ non_space {
+ print "Nonempty second line in commit message"
+ status = 1
+ }
+
+ {
+ # Expand tabs to spaces for length calculations etc.
+ while (match($0, /\t/)) {
+ before_tab = substr($0, 1, RSTART - 1)
+ after_tab = substr($0, RSTART + 1)
+ $0 = sprintf("%s%*s%s", before_tab, 8 - (RSTART - 1) % 8, "", after_tab)
+ }
+ }
+
+ # Enforce line length unless the line contains a URL
+ $0 !~ http_url {
+ if (78 < length && $0 ~ space) {
+ print "Line longer than 78 characters in commit message"
+ status = 1
+ } else if (140 < length) {
+ print "Word longer than 140 characters in commit message"
+ status = 1
+ }
+ }
+
+ /^Signed-off-by: / {
+ print "'\''Signed-off-by:'\'' in commit message"
+ status = 1
+ }
+
+ $0 ~ unsafe_gnu_url {
+ needs_rewriting = 1
+ }
+
+ $0 ~ non_print {
+ print "Unprintable character in commit message"
+ status = 1
+ }
+ $0 ~ replacement_character {
+ print "Replacement character in commit message"
+ status = 1
+ }
+
+ END {
+ if (nlines == 0) {
+ print "Empty commit message"
+ status = 1
+ }
+ if (status == 0 && needs_rewriting) {
+ for (i = 1; i <= NR; i++) {
+ line = input[i]
+ while (match(line, unsafe_gnu_url)) {
+ prefix = substr(line, 1, RSTART - 1)
+ suffix = substr(line, RSTART)
+ line = prefix "https:" substr(suffix, 5 + (suffix ~ /^http:/))
+ }
+ print line >file
+ }
+ if (close(file) != 0) {
+ print "Cannot rewrite: " file
+ status = 1
+ }
+ }
+ if (status != 0) {
+ print "Commit aborted; please see the file 'CONTRIBUTE.org'"
+ }
+ exit status
+ }
+' <"$1"
diff --git a/git-hooks/commit-msg-files.awk b/git-hooks/commit-msg-files.awk
new file mode 100644
index 000000000..d6aec09cf
--- /dev/null
+++ b/git-hooks/commit-msg-files.awk
@@ -0,0 +1,128 @@
+# Check the file list of Org mode change log entries for each commit SHA.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This script accepts a list of (unabbreviated) Git commit SHAs, and
+# will then iterate over them to check that any files mentioned in the
+# commit message are actually present in the commit's diff. If not,
+# it will print out the incorrect file names and return 1.
+
+# You can also pass "-v reason=pre-push", which will add more-verbose
+# output, indicating the abbreviated commit SHA and first line of the
+# commit message for any improper commits.
+
+### Code:
+
+function get_commit_changes(commit_sha, changes, cmd, i, j, len, \
+ bits, filename) {
+ # Collect all the files touched in the specified commit.
+ cmd = ("git show --name-status --first-parent --format= " commit_sha)
+ while ((cmd | getline) > 0) {
+ for (i = 2; i <= NF; i++) {
+ len = split($i, bits, "/")
+ for (j = 1; j <= len; j++) {
+ if (j == 1)
+ filename = bits[j]
+ else
+ filename = filename "/" bits[j]
+ changes[filename] = 1
+ }
+ }
+ }
+ close(cmd)
+}
+
+function check_commit_msg_files(commit_sha, verbose, changes, good, \
+ cmd, msg, filenames_str, filenames, i) {
+ get_commit_changes(commit_sha, changes)
+ good = 1
+
+ cmd = ("git log -1 --format=%B " commit_sha)
+ while ((cmd | getline) > 0) {
+ if (verbose && ! msg)
+ msg = $0
+
+ # Find file entries in the commit message. We look at any line
+ # starting with "*" (possibly prefixed by "; ") followed by a ":",
+ # possibly on a different line. If we encounter a blank line
+ # without seeing a ":", then we don't treat that as a file entry.
+
+ # Accumulate the contents of a (possible) file entry.
+ if (/^[ \t]*$/)
+ filenames_str = ""
+ else if (/^(; )?\*[ \t]+[[:alnum:]]/)
+ filenames_str = $0
+ else if (filenames_str)
+ filenames_str = (filenames_str $0)
+
+ # We have a file entry; analyze it.
+ if (filenames_str && /:/) {
+ # Delete the leading "*" and any trailing information.
+ sub(/^(; )?\*[ \t]+/, "", filenames_str)
+ sub(/[ \t]*[[(<:].*$/, "", filenames_str)
+
+ # There might be multiple files listed in this entry, separated
+ # by spaces (and possibly a comma). Iterate over each of them.
+ split(filenames_str, filenames, ",[ \t]+")
+ for (i in filenames) {
+ # Remove trailing slashes from any directory entries.
+ sub(/\/$/, "", filenames[i])
+
+ if (length(filenames[i]) && ! (filenames[i] in changes)) {
+ if (good) {
+ # Print a header describing the error.
+ if (verbose)
+ printf("In commit %s \"%s\"...\n", substr(commit_sha, 1, 10), msg)
+ printf("Files listed in commit message, but not in diff:\n")
+ }
+ printf(" %s\n", filenames[i])
+ good = 0
+ }
+ }
+
+ filenames_str = ""
+ }
+ }
+ close(cmd)
+
+ return good
+}
+
+BEGIN {
+ if (reason == "pre-push")
+ verbose = 1
+}
+
+/^[a-z0-9]{40}$/ {
+ if (! check_commit_msg_files($0, verbose)) {
+ status = 1
+ }
+}
+
+END {
+ if (status != 0) {
+ if (reason == "pre-push")
+ error_msg = "Push aborted"
+ else
+ error_msg = "Bad commit message"
+ printf("%s; please see the file 'CONTRIBUTE.org'\n", error_msg)
+ }
+ exit status
+}
diff --git a/git-hooks/post-commit b/git-hooks/post-commit
new file mode 100755
index 000000000..0531eefdd
--- /dev/null
+++ b/git-hooks/post-commit
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Check the file list of Org mode change log entries after committing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs after a commit is finalized and checks that the files
+# mentioned in the commit message match the diff. We perform this in
+# the post-commit phase so that we can be sure we properly detect all
+# the files in the diff (this is difficult during the commit-msg hook,
+# since there's no cross-platform way to detect when a commit is being
+# amended).
+
+# However, since this is a post-commit hook, it's too late to error
+# out and abort the commit: it's already done! As a result, this hook
+# is purely advisory, and instead we error out when trying to push
+# (see "pre-push" in this directory).
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+git rev-parse HEAD | $awk -v reason=post-commit \
+ -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit
new file mode 100755
index 000000000..b17e9bd31
--- /dev/null
+++ b/git-hooks/pre-commit
@@ -0,0 +1,83 @@
+#!/bin/sh
+# Check file names in git commits for Org mode.
+
+# Copyright 2014-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+LC_ALL=C
+export LC_ALL
+
+# If this is a system where /bin/sh isn't sufficient to
+# run git-sh-setup, use a working shell as a recourse.
+if test -x "/usr/xpg4/bin/sh" && test -z "$POSIX_SHELL"; then
+ POSIX_SHELL=1
+ export POSIX_SHELL
+ exec "/usr/xpg4/bin/sh" `dirname $0`/pre-commit
+fi
+
+exec >&2
+
+. git-sh-setup
+
+# When doing a two-way merge, ignore problems that came from the other
+# side of the merge.
+head=HEAD
+if test -r "$GIT_DIR"/MERGE_HEAD && test "$GIT_MERGE_CHECK_OTHER" != true; then
+ merge_heads=`cat "$GIT_DIR"/MERGE_HEAD` || exit
+ for merge_head in $merge_heads; do
+ case $head in
+ HEAD) head=$merge_head;;
+ # For multi-head merges, there's no easy way to ignore merged-in
+ # changes. But if you're doing multi-head merges, presumably
+ # you know how to handle any ensuing problems.
+ *) head=HEAD; break;;
+ esac
+ done
+fi
+
+git_diff='git diff --cached --name-only --diff-filter=A'
+
+# 'git diff' will backslash escape tabs and newlines, so we don't have
+# to worry about word splitting here.
+$git_diff $head |
+LC_ALL=C grep -E 'ChangeLog|^-|/-|[^-+./_0-9A-Z_a-z]' |
+while IFS= read -r new_name; do
+ case $new_name in
+ -* | */-*)
+ echo "$new_name: File name component begins with '-'."
+ exit 1;;
+ ChangeLog.android)
+ # This file is explicitly ok.
+ ;;
+ ChangeLog | */ChangeLog)
+ echo "$new_name: Please use git commit messages, not ChangeLog files."
+ exit 1;;
+ *)
+ echo "$new_name: File name does not consist of -+./_ or ASCII letters or digits."
+ exit 1;;
+ esac
+done
+
+# The '--check' option of git diff-index makes Git complain if changes
+# introduce whitespace errors. This can be a pain when editing test
+# files that deliberately contain lines with trailing whitespace.
+# To work around the problem you can run a command like 'git config
+# core.whitespace -trailing-space'. It may be better to revamp the
+# tests so that trailing spaces are generated on the fly rather than
+# being committed as source.
+
+exec git diff-index --check --cached $head --
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
new file mode 100755
index 000000000..6a46f3d71
--- /dev/null
+++ b/git-hooks/pre-push
@@ -0,0 +1,88 @@
+#!/bin/sh
+# Check the file list of Org mode change log entries before pushing.
+
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+# This hook runs before pushing a series of commits and checks that
+# the files mentioned in each commit message match the diffs. This
+# helps ensure that the resulting change logs are correct, which
+# should prevent errors when generating etc/AUTHORS.
+
+# These checks also happen in the "post-commit" hook (which see), but
+# that hook can't abort a commit; it just advises the committer to fix
+# the commit so that this hook runs without errors.
+
+### Code:
+
+HOOKS_DIR=`dirname "$0"`
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+else
+ awk="awk"
+fi
+
+# Standard input receives lines of the form:
+# <local ref> SP <local sha> SP <remote ref> SP <remote sha> LF
+$awk -v origin_name="$1" '
+ # If the local SHA is all zeroes, ignore it.
+ $2 ~ /^0{40}$/ {
+ next
+ }
+
+ # Check any lines with a valid local SHA and whose remote ref is
+ # main or a release branch. (We want to avoid checking
+ # feature or scratch branches here.)
+ $2 ~ /^[a-z0-9]{40}$/ && $3 ~ /^refs\/heads\/(main|release_[0-9.]+)$/ {
+ newref = $2
+ # If the remote SHA is all zeroes, this is a new object to be
+ # pushed (likely a branch)...
+ if ($4 ~ /^0{40}$/) {
+ back = 0
+ # ... Go backwards until we find a SHA on an origin branch.
+ # Stop trying after 1000 commits, just in case...
+ for (back = 0; back < 1000; back++) {
+ cmd = ("git branch -r -l '\''" origin_name "/*'\''" \
+ " --contains " newref "~" back)
+ rv = (cmd | getline)
+ close(cmd)
+ if (rv > 0)
+ break;
+ }
+
+ cmd = ("git rev-parse " newref "~" back)
+ cmd | getline oldref
+ if (!(oldref ~ /^[a-z0-9]{40}$/)) {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+ close(cmd)
+ } else if ($4 ~ /^[a-z0-9]{40}$/) {
+ oldref = $4
+ } else {
+ # The SHA is misformatted! Skip this line.
+ next
+ }
+
+ # Print every SHA after oldref, up to (and including) newref.
+ system("git rev-list --first-parent --reverse " oldref ".." newref)
+ }
+' | $awk -v reason=pre-push -f "$HOOKS_DIR"/commit-msg-files.awk
diff --git a/git-hooks/prepare-commit-msg b/git-hooks/prepare-commit-msg
new file mode 100755
index 000000000..4caf6f50f
--- /dev/null
+++ b/git-hooks/prepare-commit-msg
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Check the format of Org mode change log entries.
+
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This file is part of Org mode.
+
+# Org mode is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Org mode is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Org mode. If not, see <https://www.gnu.org/licenses/>.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+# Prefer gawk if available, as it handles NUL bytes properly.
+if type gawk >/dev/null 2>&1; then
+ awk="gawk"
+# Next use /usr/xpg4/bin/awk if available, since the script
+# doesn't support Unix awk.
+elif test -x /usr/xpg4/bin/awk; then
+ awk="/usr/xpg4/bin/awk"
+else
+ awk="awk"
+fi
+
+exec $awk "
+ # Catch the case when someone ran git-commit with -s option,
+ # which automatically adds Signed-off-by.
+ /^Signed-off-by: / {
+ print \"'Signed-off-by:' in commit message\"
+ status = 1
+ }
+ END {
+ if (status != 0) {
+ print \"Commit aborted; please see the file 'CONTRIBUTE.org'\"
+ }
+ exit status
+ }
+" <"$COMMIT_MSG_FILE"
diff --git a/mk/targets.mk b/mk/targets.mk
index 20f2ae504..cedde0522 100644
--- a/mk/targets.mk
+++ b/mk/targets.mk
@@ -9,6 +9,9 @@ SUBDIRS = $(OTHERDIRS) $(LISPDIRS)
INSTSUB = $(SUBDIRS:%=install-%)
ORG_MAKE_DOC ?= info html pdf
+GITDIR = .git/hooks
+GITHOOKS = commit-msg commit-msg-files.awk post-commit pre-commit prepare-commit-msg pre-push
+
ifneq ($(wildcard .git),)
# Use the org.el header.
ORGVERSION := $(patsubst %-dev,%,$(shell $(BATCH) --eval "(require 'lisp-mnt)" \
@@ -130,12 +133,21 @@ autoloads: lisp
repro: cleanall autoloads
-@$(REPRO) &
+# Implicit rule to copy Git hooks in
+$(GITDIR)/%: git-hooks/%
+ cp -f $< $@
+
+githooks: $(addprefix $(GITDIR)/,$(GITHOOKS))
+
+cleangithooks:
+ $(RM) $(addprefix $(GITDIR)/,$(GITHOOKS))
+
cleandirs:
$(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) cleanall;)
clean: cleanlisp cleandoc
-cleanall: cleandirs cleantest
+cleanall: cleandirs cleantest cleangithooks
-$(FIND) . \( -name \*~ -o -name \*# -o -name .#\* \) -exec $(RM) {} +
-$(FIND) $(CLEANDIRS) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} +
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 16:22 ` Derek Chen-Becker
@ 2025-11-22 16:37 ` Ihor Radchenko
2025-11-22 16:52 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-22 16:37 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Here are the patches with the `pre-push' script updated to replace master
> -> main and to fix emacs-NN -> release.
Thanks!
Applied, onto main, after amending one of the commit messages to resolve
the hook checks you implemented :)
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=7410e86c6
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=d198e689c
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=4faa300eb
WORG commit:
https://git.sr.ht/~bzg/worg/commit/82d73026
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 16:37 ` Ihor Radchenko
@ 2025-11-22 16:52 ` Derek Chen-Becker
2025-11-22 16:55 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-22 16:52 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1395 bytes --]
Thank you! What did I miss in the commit message?
On Sat, Nov 22, 2025 at 9:37 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Here are the patches with the `pre-push' script updated to replace master
> > -> main and to fix emacs-NN -> release.
>
> Thanks!
> Applied, onto main, after amending one of the commit messages to resolve
> the hook checks you implemented :)
> https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=7410e86c6
> https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=d198e689c
> https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=4faa300eb
>
> WORG commit:
> https://git.sr.ht/~bzg/worg/commit/82d73026
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 3397 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 16:52 ` Derek Chen-Becker
@ 2025-11-22 16:55 ` Ihor Radchenko
2025-11-22 16:56 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-22 16:55 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Thank you! What did I miss in the commit message?
* org.el -> * lisp/org.el
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 16:55 ` Ihor Radchenko
@ 2025-11-22 16:56 ` Derek Chen-Becker
2025-11-23 12:45 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-22 16:56 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 914 bytes --]
Doh! Thanks for fixing that :)
On Sat, Nov 22, 2025 at 9:55 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Thank you! What did I miss in the commit message?
>
> * org.el -> * lisp/org.el
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 2433 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-22 16:56 ` Derek Chen-Becker
@ 2025-11-23 12:45 ` Ihor Radchenko
2025-11-23 14:13 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-23 12:45 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Doh! Thanks for fixing that :)
Note that the issues reported at the beginning of this thread still do
exist.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-23 12:45 ` Ihor Radchenko
@ 2025-11-23 14:13 ` Derek Chen-Becker
2025-11-23 14:17 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-23 14:13 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1872 bytes --]
Yes, I plan on tackling those next. To recap, what I can find in this
thread is:
Functions that use string-to-char to parse:
- org-entry-put
- org-mobile-edit (in org-mobile.el)
- org-agenda-fontify-priorities (org-agenda.el)
- org-mouse-context-menu (org-mouse.el)
The Org Element API also needs updating:
- ~org-element--headline-parse-title~ implements its own
org-priority-regexp, which doesn't account for multiple characters
- ~org-entry-put~ has bugs, but I don't understand the code well enough
- A couple of functions format priorities as "[#%c]", and should
include a %s case for values under 65.
* `org-entry-properties' assumes regexp/string that may need fixing
* `org-latex-format-headline-default-function' assumes "%c" formatting
* `org-element-property' makes some assumptions that may need addressed
It's been a fairly long thread, but I think that covers all of the items.
Did I miss any?
Cheers,
Derek
On Sun, Nov 23, 2025 at 5:45 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Doh! Thanks for fixing that :)
>
> Note that the issues reported at the beginning of this thread still do
> exist.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 4920 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-23 14:13 ` Derek Chen-Becker
@ 2025-11-23 14:17 ` Ihor Radchenko
2025-11-23 14:17 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-23 14:17 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> It's been a fairly long thread, but I think that covers all of the items.
> Did I miss any?
There is also
https://list.orgmode.org/orgmode/[email protected]/
thread reporting an issue with column view, but that likely falls within
`org-entry-properties' fix. Still worth confirming that the reproducer
is working correctly.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-23 14:17 ` Ihor Radchenko
@ 2025-11-23 14:17 ` Derek Chen-Becker
2025-11-23 14:54 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-23 14:17 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 1191 bytes --]
Will do!
On Sun, Nov 23, 2025 at 7:17 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > It's been a fairly long thread, but I think that covers all of the items.
> > Did I miss any?
>
> There is also
>
> https://list.orgmode.org/orgmode/[email protected]/
> thread reporting an issue with column view, but that likely falls within
> `org-entry-properties' fix. Still worth confirming that the reproducer
> is working correctly.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 2857 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-23 14:17 ` Derek Chen-Becker
@ 2025-11-23 14:54 ` Derek Chen-Becker
2025-11-23 14:57 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-23 14:54 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2183 bytes --]
I'm fixing the ox-latex stuff first since it seems fairly simple. I noticed
what appears to be a typo on
https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/testing/lisp/test-ox-latex.el#n32
while updating the unit tests. I think this should be `test-ox-latex', not
`text-ox-latex':
(ert-deftest text-ox-latex/protect-square-brackets ()
I can add this fix to my patch if this should be changed.
Cheers,
Derek
On Sun, Nov 23, 2025 at 7:17 AM Derek Chen-Becker <[email protected]>
wrote:
> Will do!
>
> On Sun, Nov 23, 2025 at 7:17 AM Ihor Radchenko <[email protected]>
> wrote:
>
>> Derek Chen-Becker <[email protected]> writes:
>>
>> > It's been a fairly long thread, but I think that covers all of the
>> items.
>> > Did I miss any?
>>
>> There is also
>>
>> https://list.orgmode.org/orgmode/[email protected]/
>> thread reporting an issue with column view, but that likely falls within
>> `org-entry-properties' fix. Still worth confirming that the reproducer
>> is working correctly.
>>
>> --
>> 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>
>>
>
>
> --
> +---------------------------------------------------------------+
> | Derek Chen-Becker |
> | GPG Key available at https://keybase.io/dchenbecker and |
> | https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
> | Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
> +---------------------------------------------------------------+
>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 6058 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-23 14:54 ` Derek Chen-Becker
@ 2025-11-23 14:57 ` Ihor Radchenko
2025-11-25 3:22 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-23 14:57 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I'm fixing the ox-latex stuff first since it seems fairly simple. I noticed
> what appears to be a typo on
> https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/testing/lisp/test-ox-latex.el#n32
> while updating the unit tests. I think this should be `test-ox-latex', not
> `text-ox-latex':
>
> (ert-deftest text-ox-latex/protect-square-brackets ()
>
> I can add this fix to my patch if this should be changed.
Yes, typo.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-23 14:57 ` Ihor Radchenko
@ 2025-11-25 3:22 ` Derek Chen-Becker
2025-11-30 11:55 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-11-25 3:22 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1860 bytes --]
OK, I have a fix for the `org-entry-property` issue and the LaTeX
issue, attached. I think that the `org-entry-property' fix also fixes
column view, and I've added a unit test for that as well. One thing I
noticed, though, was that the code around org-priority, even before my
latest set of patches, doesn't seem to consider buffer-level priorities
(e.g. "#+PRIORITIES: A D B"). That might be another thing that needs
fixing, so I've added it as a follow-up in my TODOs to look at (I could
just be missing something, but it didn't work in manual testing).
Cheers,
Derek
On Sun, Nov 23, 2025 at 7:57 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > I'm fixing the ox-latex stuff first since it seems fairly simple. I
> noticed
> > what appears to be a typo on
> >
> https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/testing/lisp/test-ox-latex.el#n32
> > while updating the unit tests. I think this should be `test-ox-latex',
> not
> > `text-ox-latex':
> >
> > (ert-deftest text-ox-latex/protect-square-brackets ()
> >
> > I can add this fix to my patch if this should be changed.
>
> Yes, typo.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 4162 bytes --]
[-- Attachment #2: 0002-lisp-ox-latex.el-Properly-format-priority-values.patch --]
[-- Type: application/octet-stream, Size: 2749 bytes --]
From 219c1b19823cd8364c2bdbf8becca0b320894a7d Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Mon, 24 Nov 2025 08:05:24 -0700
Subject: [PATCH 2/2] lisp/ox-latex.el: Properly format priority values
Fix LaTeX export so that it properly handles numeric priority values in
addition to characters.
* lisp/ox-latex.el (org-latex-format-headline-default-function): Change
format of priorities to use the `org-priority-to-string' function instead
of assuming a character value.
* testing/lisp/test-ox-latex.el: Fix a typo in the
`test-ox-latex/protect-square-brackets' test so that its name matches the
rest of the tests. Add unit tests for LaTeX export of headlines with
priorities for both numeric and character values.
---
lisp/ox-latex.el | 2 +-
testing/lisp/test-ox-latex.el | 29 ++++++++++++++++++++++++++++-
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index cf4f10f2a..af4196e09 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -2525,7 +2525,7 @@ holding contextual information."
See `org-latex-format-headline-function' for details."
(concat
(and todo (format "{\\bfseries\\sffamily %s} " todo))
- (and priority (format "\\framebox{\\#%c} " priority))
+ (and priority (format "\\framebox{\\#%s} " (org-priority-to-string priority)))
text
(and tags
(format "\\hfill{}\\textsc{%s}"
diff --git a/testing/lisp/test-ox-latex.el b/testing/lisp/test-ox-latex.el
index 35088deee..64ab7d467 100644
--- a/testing/lisp/test-ox-latex.el
+++ b/testing/lisp/test-ox-latex.el
@@ -29,7 +29,7 @@
\f
-(ert-deftest text-ox-latex/protect-square-brackets ()
+(ert-deftest test-ox-latex/protect-square-brackets ()
"Test [foo] being interpreted as plain text even after LaTeX commands."
(org-test-with-exported-text
'latex
@@ -284,5 +284,32 @@ is suppressed
(should (search-forward
"\\section[\\(\\psi\\) wraps too]{\\(\\phi\\) wraps}"))))
+(ert-deftest test-ox-latex/numeric-priority-headline ()
+ "Test numeric priorities in headlines."
+ (org-test-with-exported-text
+ 'latex
+ "#+OPTIONS: pri:t
+* [#3] Test
+"
+ (goto-char (point-min))
+ (should (search-forward "\\framebox{\\#3}")))
+ (org-test-with-exported-text
+ 'latex
+ "#+OPTIONS: pri:t
+* [#42] Test
+"
+ (goto-char (point-min))
+ (should (search-forward "\\framebox{\\#42}"))))
+
+(ert-deftest test-ox-latex/alphabetical-priority-headline ()
+ "Test numeric priorities in headlines."
+ (org-test-with-exported-text
+ 'latex
+ "#+OPTIONS: pri:t
+* [#C] Test
+"
+ (goto-char (point-min))
+ (should (search-forward "\\framebox{\\#C}"))))
+
(provide 'test-ox-latex)
;;; test-ox-latex.el ends here
--
2.43.0
[-- Attachment #3: 0001-lisp-org-element.el-Properly-handle-numeric-prioriti.patch --]
[-- Type: application/octet-stream, Size: 6830 bytes --]
From 28fab6146b8b59e0991a58cfb35f10319372c9b5 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Mon, 24 Nov 2025 08:04:19 -0700
Subject: [PATCH 1/2] lisp/org-element.el: Properly handle numeric priorities
Update org-element to properly parse numeric priorities by using a newly
defined helper function that parses priority values. Also update the
headline and inline task interpreters to properly format numeric
properties. Add unit tests for the new functionality.
* lisp/org-element.el (org-element--headline-parse-title,
org-element-headline-interpreter, org-element-inlinetask-interpreter):
Update `org-element--headline-parse-title' to use `org-priority-to-value'
for parsing of priorities so that numeric properies are handled properly.
Change `org-element-headline-interpreter' and
`org-element-inlinetask-interpreter' to use the `org-priority-to-string'
function for display conversion of priority values.
* testing/lisp/test-org-colview.el: Add unit tests to validate proper
handling of priority formatting in column view.
* testing/lisp/test-org.el: Add a unit test for the `org-priority-to-value'
function for single digit, double digit, and character values.
* testing/lisp/test-ox.el: Add a unit test for the org export interpreters
for headlines and inline tasks to ensure proper conversion of numeric
priorities.
---
lisp/org-element.el | 10 +++++-----
testing/lisp/test-org-colview.el | 27 +++++++++++++++++++++++++++
testing/lisp/test-org.el | 9 +++++++++
testing/lisp/test-ox.el | 23 +++++++++++++++++++++++
4 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/lisp/org-element.el b/lisp/org-element.el
index e1425d9eb..651790c19 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -1359,9 +1359,9 @@ Throw `:org-element-deferred-retry' signal at the end."
(org-element--get-cached-string (match-string-no-properties 1)))))
(todo-type
(and todo (if (member todo org-done-keywords) 'done 'todo)))
- (priority (and (looking-at "\\[#.\\][ \t]*")
- (progn (goto-char (match-end 0))
- (aref (match-string 0) 2))))
+ (priority (and (looking-at org-priority-regexp)
+ (progn (goto-char (match-end 0))
+ (org-priority-to-value (match-string 2)))))
(commentedp
(and (let ((case-fold-search nil))
(looking-at org-element--headline-comment-re))
@@ -1520,7 +1520,7 @@ CONTENTS is the contents of the element."
?*)
(and todo (concat " " todo))
(and commentedp (concat " " org-element-comment-string))
- (and priority (format " [#%c]" priority))
+ (and priority (format " [#%s]" (org-priority-to-string priority)))
" "
(if (and org-footnote-section
(org-element-property :footnote-section-p headline))
@@ -1739,7 +1739,7 @@ CONTENTS is the contents of inlinetask."
(format ":%s:" (mapconcat 'identity tag-list ":")))))
(task (concat (make-string level ?*)
(and todo (concat " " todo))
- (and priority (format " [#%c]" priority))
+ (and priority (format " [#%s]" (org-priority-to-string priority)))
(and title (concat " " title)))))
(concat task
;; Align tags.
diff --git a/testing/lisp/test-org-colview.el b/testing/lisp/test-org-colview.el
index 681f4fe48..bb283f61f 100644
--- a/testing/lisp/test-org-colview.el
+++ b/testing/lisp/test-org-colview.el
@@ -1783,5 +1783,32 @@ there are 4 parameters
(let ((org-columns-default-format "%ITEM")) (org-update-dblock))
(buffer-substring-no-properties (point) (point-max))))))
+(ert-deftest test-org-colview/priorities ()
+ "Test that column view properly handles priorities."
+ ;; test alphabetic priorities
+ (should
+ (equal "B"
+ (org-test-with-temp-text
+ "* [#B] Test"
+ (let ((org-columns-default-format "%PRIORITY"))
+ (org-columns)
+ (get-char-property (point) 'org-columns-value)))))
+ ;; test numeric single-digit priorities
+ (should
+ (equal "6"
+ (org-test-with-temp-text
+ "* [#6] Test"
+ (let ((org-columns-default-format "%PRIORITY"))
+ (org-columns)
+ (get-char-property (point) 'org-columns-value)))))
+ ;; test numeric double-digit priorities
+ (should
+ (equal "15"
+ (org-test-with-temp-text
+ "* [#15] Test"
+ (let ((org-columns-default-format "%PRIORITY"))
+ (org-columns)
+ (get-char-property (point) 'org-columns-value))))) )
+
(provide 'test-org-colview)
;;; test-org-colview.el ends here
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 1a28d01ed..76b2a58fb 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -10152,6 +10152,15 @@ two
(seq-every-p (lambda (pv) (org-priority-valid-value-p pv t))
'(?A ?L ?N ?Z)))))
+(ert-deftest test-org/priority-parsing ()
+ "Test parsing of priority values."
+ ;; single digit
+ (should (eq 7 (org-priority-to-value "7")))
+ ;; double digit
+ (should (eq 42 (org-priority-to-value "42")))
+ ;; alphabetic
+ (should (eq ?G (org-priority-to-value "G"))))
+
(provide 'test-org)
;;; test-org.el ends here
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index e9d9a06f7..858b8ee22 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -3149,6 +3149,29 @@ Para2"
(org-export-as (org-test-default-backend)
nil nil nil '(:with-tasks nil))))))))
+(ert-deftest test-org-export/handle-numeric-priorities ()
+ "Test handling of numeric priorities in headers and inlinetasks."
+ ;; Properly handle numeric priorities in normal headers
+ (should
+ (equal "* [#3] H2\nContents\n"
+ (let ()
+ (org-test-with-temp-text "* [#3] H2\nContents"
+ (org-export-as (org-test-default-backend))))))
+ ;; Properly handle numeric priorities in inline tasks
+ (should
+ (equal "* H2\n*** [#8] Inline\nContents\n"
+ (let ((org-inlinetask-min-level 3))
+ (org-test-with-temp-text "* H2\n*** [#8] Inline\nContents"
+ (org-export-as (org-test-default-backend)
+ nil nil nil '(:with-tasks t))))))
+ (should
+ (equal "* H2\n*** [#37] Inline\nContents\n"
+ (let ((org-inlinetask-min-level 3))
+ (org-test-with-temp-text "* H2\n*** [#37] Inline\nContents"
+ (org-export-as (org-test-default-backend)
+ nil nil nil '(:with-tasks t))))))
+ )
+
\f
;;; Keywords
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-25 3:22 ` Derek Chen-Becker
@ 2025-11-30 11:55 ` Ihor Radchenko
2025-12-04 5:27 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-11-30 11:55 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> OK, I have a fix for the `org-entry-property` issue and the LaTeX
> issue, attached.
The patch looks good.
Applied, onto main.
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=f08922964
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=ac0727b6e
Note that we also need to update worg/org-syntax.org.
> ... I think that the `org-entry-property' fix also fixes
> column view, and I've added a unit test for that as well
You still missed one failing scenario - default priority.
Consider
#+PRIORITIES: 1 7 7
#+COLUMNS: %ITEM %PRIORITY
* This
* That
* Foo
And try either column view of org-entry-get "PRIORITY".
> ... One thing I
> noticed, though, was that the code around org-priority, even before my
> latest set of patches, doesn't seem to consider buffer-level priorities
> (e.g. "#+PRIORITIES: A D B"). That might be another thing that needs
> fixing, so I've added it as a follow-up in my TODOs to look at (I could
> just be missing something, but it didn't work in manual testing).
Sorry, but I am not sure which code you are referring to.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-11-30 11:55 ` Ihor Radchenko
@ 2025-12-04 5:27 ` Derek Chen-Becker
2025-12-04 8:24 ` Christian Moe
2025-12-04 17:52 ` Ihor Radchenko
0 siblings, 2 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-12-04 5:27 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 2226 bytes --]
I've attached the patch for worg/org-syntax.org. I think the issue with
default priority and the one I was talking about with a "PRIORITIES"
property are related, so let me figure out the default case and that may
end up fixing what I found.
Cheers,
Derek
On Sun, Nov 30, 2025 at 4:55 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > OK, I have a fix for the `org-entry-property` issue and the LaTeX
> > issue, attached.
>
> The patch looks good.
> Applied, onto main.
> https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=f08922964
> https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=ac0727b6e
>
> Note that we also need to update worg/org-syntax.org.
>
> > ... I think that the `org-entry-property' fix also fixes
> > column view, and I've added a unit test for that as well
>
> You still missed one failing scenario - default priority.
> Consider
> #+PRIORITIES: 1 7 7
> #+COLUMNS: %ITEM %PRIORITY
> * This
> * That
> * Foo
> And try either column view of org-entry-get "PRIORITY".
>
> > ... One thing I
> > noticed, though, was that the code around org-priority, even before my
> > latest set of patches, doesn't seem to consider buffer-level priorities
> > (e.g. "#+PRIORITIES: A D B"). That might be another thing that needs
> > fixing, so I've added it as a follow-up in my TODOs to look at (I could
> > just be missing something, but it didn't work in manual testing).
>
> Sorry, but I am not sure which code you are referring to.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 4610 bytes --]
[-- Attachment #2: 0001-org-syntax.org-Update-definition-of-PRIORITY.patch --]
[-- Type: application/octet-stream, Size: 1159 bytes --]
From 47881558e3ca19059aaa1e14722d4d26af8e2eb1 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Wed, 3 Dec 2025 06:05:51 -0700
Subject: [PATCH] org-syntax.org: Update definition of PRIORITY
Update the definition of PRIORITY to include double-digit numbers and call
out the valid numeric range.
---
org-syntax.org | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/org-syntax.org b/org-syntax.org
index a8b67b91..637d0196 100644
--- a/org-syntax.org
+++ b/org-syntax.org
@@ -377,8 +377,9 @@ STARS KEYWORD PRIORITY COMMENT TITLE TAGS
be configurable at runtime so that in-file todo keywords are properly
interpreted.]
-+ PRIORITY (optional) :: A single alphanumeric character preceded by a
- hash sign =#= and enclosed within square brackets (e.g. =[#A]= or =[#1]=). This
++ PRIORITY (optional) :: A single uppercase alphabetic character or an
+ integral number between 0 and 64, preceded by a hash sign =#= and
+ enclosed within square brackets (e.g. =[#A], [#0],= or =[#27]=). This
is called a "priority cookie".
+ COMMENT (optional) :: String "COMMENT". Case is significant.
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-04 5:27 ` Derek Chen-Becker
@ 2025-12-04 8:24 ` Christian Moe
2025-12-04 14:51 ` Derek Chen-Becker
2025-12-04 17:52 ` Ihor Radchenko
1 sibling, 1 reply; 81+ messages in thread
From: Christian Moe @ 2025-12-04 8:24 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Ihor Radchenko, Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I've attached the patch for worg/org-syntax.org.
Thanks!
Applied, with a minor edit to the changelog entry.
Regards,
Christian
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-04 8:24 ` Christian Moe
@ 2025-12-04 14:51 ` Derek Chen-Becker
2025-12-07 20:11 ` Ihor Radchenko
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-12-04 14:51 UTC (permalink / raw)
To: emacs-orgmode; +Cc: Ihor Radchenko
[-- Attachment #1.1: Type: text/plain, Size: 993 bytes --]
Attached is the patch to fix formatting of the default (numeric) priorities
in column view. I can't reproduce the other issue I was seeing with column
view now, although it's not clear why. I'll continue work on some of the
other areas identified in this thread.
Cheers,
Derek
On Thu, Dec 4, 2025 at 1:24 AM Christian Moe <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > I've attached the patch for worg/org-syntax.org.
>
> Thanks!
>
> Applied, with a minor edit to the changelog entry.
>
> Regards,
> Christian
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 2731 bytes --]
[-- Attachment #2: 0001-lisp-org.el-Fix-numeric-priority-format-in-column-vi.patch --]
[-- Type: application/octet-stream, Size: 2076 bytes --]
From 64a9c53b49a11f86e0e3fda85264bb3e0d91b8c0 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 4 Dec 2025 06:54:47 -0700
Subject: [PATCH] lisp/org.el: Fix numeric priority format in column view
lisp/org.el (org-entry-properties): Replace incorrect usage of
`char-to-string' with the new `org-priority-to-string' so that numeric
property values are formatted correctly.
testing/lisp/test-org-colview.el: Add a new test case to the
`test-org-colview/priorities' test to exercise formatting of numeric
property values in column view.
---
lisp/org.el | 2 +-
testing/lisp/test-org-colview.el | 11 ++++++++++-
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/lisp/org.el b/lisp/org.el
index d0b349487..809e9dd32 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -13145,7 +13145,7 @@ strings."
(push (cons "PRIORITY"
(if (looking-at org-priority-regexp)
(match-string-no-properties 2)
- (char-to-string org-priority-default)))
+ (org-priority-to-string org-priority-default)))
props)
(when specific (throw 'exit props)))
(when (or (not specific) (string= specific "FILE"))
diff --git a/testing/lisp/test-org-colview.el b/testing/lisp/test-org-colview.el
index bb283f61f..3ed20fa01 100644
--- a/testing/lisp/test-org-colview.el
+++ b/testing/lisp/test-org-colview.el
@@ -1808,7 +1808,16 @@ there are 4 parameters
"* [#15] Test"
(let ((org-columns-default-format "%PRIORITY"))
(org-columns)
- (get-char-property (point) 'org-columns-value))))) )
+ (get-char-property (point) 'org-columns-value)))))
+ ;; test default numeric priority
+ (should
+ (equal "15"
+ (org-test-with-temp-text
+ "* Test"
+ (let ((org-columns-default-format "%PRIORITY")
+ (org-default-priority 15))
+ (org-columns)
+ (get-char-property (point) 'org-columns-value))))))
(provide 'test-org-colview)
;;; test-org-colview.el ends here
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-04 5:27 ` Derek Chen-Becker
2025-12-04 8:24 ` Christian Moe
@ 2025-12-04 17:52 ` Ihor Radchenko
2025-12-04 23:40 ` Derek Chen-Becker
1 sibling, 1 reply; 81+ messages in thread
From: Ihor Radchenko @ 2025-12-04 17:52 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I've attached the patch for worg/org-syntax.org. I think the issue with
> default priority and the one I was talking about with a "PRIORITIES"
> property are related, so let me figure out the default case and that may
> end up fixing what I found.
> -+ PRIORITY (optional) :: A single alphanumeric character preceded by a
> - hash sign =#= and enclosed within square brackets (e.g. =[#A]= or =[#1]=). This
> ++ PRIORITY (optional) :: A single uppercase alphabetic character or an
> + integral number between 0 and 64, preceded by a hash sign =#= and
> + enclosed within square brackets (e.g. =[#A], [#0],= or =[#27]=). This
This is not accurate. We do not allow alphabetic characters like ?я
Also integral -> integer.
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-04 17:52 ` Ihor Radchenko
@ 2025-12-04 23:40 ` Derek Chen-Becker
2025-12-05 13:05 ` Rudolf Adamkovič
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-12-04 23:40 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Rudolf Adamkovič, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1628 bytes --]
Patch attached to correct the inaccuracies.
Cheers,
Derek
On Thu, Dec 4, 2025 at 10:52 AM Ihor Radchenko <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > I've attached the patch for worg/org-syntax.org. I think the issue with
> > default priority and the one I was talking about with a "PRIORITIES"
> > property are related, so let me figure out the default case and that may
> > end up fixing what I found.
>
> > -+ PRIORITY (optional) :: A single alphanumeric character preceded by a
> > - hash sign =#= and enclosed within square brackets (e.g. =[#A]= or
> =[#1]=). This
> > ++ PRIORITY (optional) :: A single uppercase alphabetic character or an
> > + integral number between 0 and 64, preceded by a hash sign =#= and
> > + enclosed within square brackets (e.g. =[#A], [#0],= or =[#27]=). This
>
> This is not accurate. We do not allow alphabetic characters like ?я
> Also integral -> integer.
>
> --
> 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>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 3657 bytes --]
[-- Attachment #2: 0001-org-syntax.el-Clarify-PRIORITY-value-definition.patch --]
[-- Type: application/octet-stream, Size: 1331 bytes --]
From ccaab33309c55986e824f7368c7d11aa6f301e13 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 4 Dec 2025 16:07:20 -0700
Subject: [PATCH] org-syntax.el: Clarify PRIORITY value definition
org-syntax.org (Headings): Update the definition to clarify the accepted
range of alphabetic priority values. Fix incorrect usage of "integral" to
"integer".
---
org-syntax.org | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/org-syntax.org b/org-syntax.org
index 637d0196..a1f13c20 100644
--- a/org-syntax.org
+++ b/org-syntax.org
@@ -377,10 +377,10 @@ STARS KEYWORD PRIORITY COMMENT TITLE TAGS
be configurable at runtime so that in-file todo keywords are properly
interpreted.]
-+ PRIORITY (optional) :: A single uppercase alphabetic character or an
- integral number between 0 and 64, preceded by a hash sign =#= and
- enclosed within square brackets (e.g. =[#A], [#0],= or =[#27]=). This
- is called a "priority cookie".
++ PRIORITY (optional) :: A single uppercase alphabetic character
+ between A and Z or an integer number between 0 and 64, preceded by a
+ hash sign =#= and enclosed within square brackets (e.g. =[#A], [#0],= or
+ =[#27]=). This is called a "priority cookie".
+ COMMENT (optional) :: String "COMMENT". Case is significant.
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-04 23:40 ` Derek Chen-Becker
@ 2025-12-05 13:05 ` Rudolf Adamkovič
2025-12-05 15:21 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Rudolf Adamkovič @ 2025-12-05 13:05 UTC (permalink / raw)
To: Derek Chen-Becker, Ihor Radchenko; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> + between A and Z or an integer number between 0 and 64, preceded by
^^^^^^^^^^^^^^
just "integer"
(nobody says "integer number")
Rudy
--
"Arguing that you don't care about the right to privacy because you have
nothing to hide is no different than saying you don't care about free
speech because you have nothing to say."
--- Edward Snowden, 2015
Rudolf Adamkovič <[email protected]> [he/him]
http://adamkovic.org
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-05 13:05 ` Rudolf Adamkovič
@ 2025-12-05 15:21 ` Derek Chen-Becker
2025-12-05 17:23 ` Christian Moe
0 siblings, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-12-05 15:21 UTC (permalink / raw)
To: Rudolf Adamkovič; +Cc: Ihor Radchenko, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1124 bytes --]
Thanks, fixed!
On Fri, Dec 5, 2025 at 6:05 AM Rudolf Adamkovič <[email protected]>
wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > + between A and Z or an integer number between 0 and 64, preceded by
> ^^^^^^^^^^^^^^
> just "integer"
> (nobody says "integer number")
>
> Rudy
> --
> "Arguing that you don't care about the right to privacy because you have
> nothing to hide is no different than saying you don't care about free
> speech because you have nothing to say."
>
> --- Edward Snowden, 2015
>
> Rudolf Adamkovič <[email protected]> [he/him]
> http://adamkovic.org
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 2818 bytes --]
[-- Attachment #2: 0001-org-syntax.el-Clarify-PRIORITY-value-definition.patch --]
[-- Type: application/octet-stream, Size: 1324 bytes --]
From 9f304cb007592bfc2473fa4b8e6568e346a32b27 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 4 Dec 2025 16:07:20 -0700
Subject: [PATCH] org-syntax.el: Clarify PRIORITY value definition
org-syntax.org (Headings): Update the definition to clarify the accepted
range of alphabetic priority values. Fix incorrect usage of "integral" to
"integer".
---
org-syntax.org | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/org-syntax.org b/org-syntax.org
index 637d0196..86c12431 100644
--- a/org-syntax.org
+++ b/org-syntax.org
@@ -377,10 +377,10 @@ STARS KEYWORD PRIORITY COMMENT TITLE TAGS
be configurable at runtime so that in-file todo keywords are properly
interpreted.]
-+ PRIORITY (optional) :: A single uppercase alphabetic character or an
- integral number between 0 and 64, preceded by a hash sign =#= and
- enclosed within square brackets (e.g. =[#A], [#0],= or =[#27]=). This
- is called a "priority cookie".
++ PRIORITY (optional) :: A single uppercase alphabetic character
+ between A and Z or an integer between 0 and 64, preceded by a hash
+ sign =#= and enclosed within square brackets (e.g. =[#A], [#0],= or
+ =[#27]=). This is called a "priority cookie".
+ COMMENT (optional) :: String "COMMENT". Case is significant.
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-05 15:21 ` Derek Chen-Becker
@ 2025-12-05 17:23 ` Christian Moe
2025-12-05 17:31 ` Derek Chen-Becker
2025-12-06 12:03 ` Rudolf Adamkovič
0 siblings, 2 replies; 81+ messages in thread
From: Christian Moe @ 2025-12-05 17:23 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: Rudolf Adamkovič, Ihor Radchenko, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Thanks, fixed!
>
> On Fri, Dec 5, 2025 at 6:05 AM Rudolf Adamkovič <[email protected]> wrote:
>
> Derek Chen-Becker <[email protected]> writes:
>
> > + between A and Z or an integer number between 0 and 64, preceded by
> ^^^^^^^^^^^^^^
> just "integer"
> (nobody says "integer number")
Sorry for applying the earlier patch without reviewing it carefully
enough. I'd like to go ahead again and apply this patch as soon as
possible to fix the "integral".
But are there further refinements, while we're at it? Is "A single
uppercase alphabetic character between A and Z" clear and correct enough
now?
In English it's fairly conventional to use "alphabetic character" means
a letter of the /English/ alphabet. But as Ihor pointed out, there are
different alphabets, so it's ambiguous at the least, and on the syntax
page we should be careful.
However, the worg/org-syntax.org page also refers to "alphabetic
characters" under blocks (switches), entities (can be followed by a
non-alphabetic character), LaTeX fragments, and macros. I think most or
all of these mean [a-zA-Z]. Should we fix these as well?
Is "between A and Z" unambiguous? Can we assume everyone understands that we're
talking about charsets with an ASCII block? In my local alphabet,
"between A and Z" includes Č, Ć, and Š, but there is no charset with
this order. And do we all understand that "between...and" includes the
endpoints, or should we say "from A to Z"? :-)
Regards,
Christian
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-05 17:23 ` Christian Moe
@ 2025-12-05 17:31 ` Derek Chen-Becker
2025-12-06 12:16 ` Rudolf Adamkovič
2025-12-06 12:03 ` Rudolf Adamkovič
1 sibling, 1 reply; 81+ messages in thread
From: Derek Chen-Becker @ 2025-12-05 17:31 UTC (permalink / raw)
To: Christian Moe; +Cc: Rudolf Adamkovič, Ihor Radchenko, emacs-orgmode
[-- Attachment #1: Type: text/plain, Size: 2778 bytes --]
Hi Christian,
Thanks for the review, and apologies for the earlier patch with ambiguity.
I was thinking about this a bit and I have two ideas. The first would be to
specify "uppercase ASCII alphabetic character", since ASCII does not
support anything with accents. The other would be to use a precise
definition based on elisp of "characters whose literal form (e.g. A?, B?,
etc) evaluates to an integer between 65 and 90". I agree that maybe we
should provide one definition in the syntax file and refer to it.
Cheers,
Derek
On Fri, Dec 5, 2025 at 10:24 AM Christian Moe <[email protected]> wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > Thanks, fixed!
> >
> > On Fri, Dec 5, 2025 at 6:05 AM Rudolf Adamkovič <[email protected]>
> wrote:
> >
> > Derek Chen-Becker <[email protected]> writes:
> >
> > > + between A and Z or an integer number between 0 and 64, preceded by
> > ^^^^^^^^^^^^^^
> > just "integer"
> > (nobody says "integer number")
>
> Sorry for applying the earlier patch without reviewing it carefully
> enough. I'd like to go ahead again and apply this patch as soon as
> possible to fix the "integral".
>
> But are there further refinements, while we're at it? Is "A single
> uppercase alphabetic character between A and Z" clear and correct enough
> now?
>
> In English it's fairly conventional to use "alphabetic character" means
> a letter of the /English/ alphabet. But as Ihor pointed out, there are
> different alphabets, so it's ambiguous at the least, and on the syntax
> page we should be careful.
>
> However, the worg/org-syntax.org page also refers to "alphabetic
> characters" under blocks (switches), entities (can be followed by a
> non-alphabetic character), LaTeX fragments, and macros. I think most or
> all of these mean [a-zA-Z]. Should we fix these as well?
>
> Is "between A and Z" unambiguous? Can we assume everyone understands that
> we're
> talking about charsets with an ASCII block? In my local alphabet,
> "between A and Z" includes Č, Ć, and Š, but there is no charset with
> this order. And do we all understand that "between...and" includes the
> endpoints, or should we say "from A to Z"? :-)
>
> Regards,
> Christian
>
>
>
>
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #2: Type: text/html, Size: 5106 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-05 17:23 ` Christian Moe
2025-12-05 17:31 ` Derek Chen-Becker
@ 2025-12-06 12:03 ` Rudolf Adamkovič
1 sibling, 0 replies; 81+ messages in thread
From: Rudolf Adamkovič @ 2025-12-06 12:03 UTC (permalink / raw)
To: Christian Moe, Derek Chen-Becker; +Cc: Ihor Radchenko, emacs-orgmode
Christian Moe <[email protected]> writes:
Your attention to detail and correctness is admirable!
> But are there further refinements, while we're at it? Is "A single
> uppercase alphabetic character between A and Z" clear and correct enough
> now?
How about we say "A through Z" instead of "between A and Z" to highlight
the fact that the range is inclusive?
> In English it's fairly conventional to use "alphabetic character" means
> a letter of the /English/ alphabet. But as Ihor pointed out, there are
> different alphabets, so it's ambiguous at the least, and on the syntax
> page we should be careful.
While "uppercase English alphabetic character" is mouthful, it is 100%
precise, which is desirable.
A random bag of other ideas: We could also say "letter" instead of
"alphabetic character", especially when `char' type is irrelevant or
clear from context. Also, "capital" is often written instead of
"uppercase", as in "capital letter", which reads rather smoothly.
Rudy
--
"Those who cannot remember the past are condemned to repeat it."
--- George Santayana, Life of Reason: Reason in Common Sense, 1905
Rudolf Adamkovič <[email protected]> [he/him]
http://adamkovic.org
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-05 17:31 ` Derek Chen-Becker
@ 2025-12-06 12:16 ` Rudolf Adamkovič
2025-12-06 16:45 ` Derek Chen-Becker
0 siblings, 1 reply; 81+ messages in thread
From: Rudolf Adamkovič @ 2025-12-06 12:16 UTC (permalink / raw)
To: Derek Chen-Becker, Christian Moe; +Cc: Ihor Radchenko, emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> I was thinking about this a bit and I have two ideas. The first would
> be to specify "uppercase ASCII alphabetic character", since ASCII does
> not support anything with accents.
In non-English countries, for all practical purposed, ASCII means 8-bit
"extended ASCII". For example, in Central Europe, the second part of
ASCII would be ISO-8859-2 or CP-1250, containing accented characters.
> The other would be to use a precise definition based on elisp of
> "characters whose literal form (e.g. A?, B?, etc) evaluates to an
> integer between 65 and 90".
I would say something like "a capital English alphabet character A
through Z (ASCII code 65 through 90)", which is 100% precise.
Rudy
--
"Arguing that you don't care about the right to privacy because you have
nothing to hide is no different than saying you don't care about free
speech because you have nothing to say."
--- Edward Snowden, 2015
Rudolf Adamkovič <[email protected]> [he/him]
http://adamkovic.org
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-06 12:16 ` Rudolf Adamkovič
@ 2025-12-06 16:45 ` Derek Chen-Becker
0 siblings, 0 replies; 81+ messages in thread
From: Derek Chen-Becker @ 2025-12-06 16:45 UTC (permalink / raw)
To: Rudolf Adamkovič; +Cc: Christian Moe, Ihor Radchenko, emacs-orgmode
[-- Attachment #1.1: Type: text/plain, Size: 1677 bytes --]
That sounds reasonable. Updated patch attached.
Cheers,
Derek
On Sat, Dec 6, 2025 at 5:16 AM Rudolf Adamkovič <[email protected]>
wrote:
> Derek Chen-Becker <[email protected]> writes:
>
> > I was thinking about this a bit and I have two ideas. The first would
> > be to specify "uppercase ASCII alphabetic character", since ASCII does
> > not support anything with accents.
>
> In non-English countries, for all practical purposed, ASCII means 8-bit
> "extended ASCII". For example, in Central Europe, the second part of
> ASCII would be ISO-8859-2 or CP-1250, containing accented characters.
>
> > The other would be to use a precise definition based on elisp of
> > "characters whose literal form (e.g. A?, B?, etc) evaluates to an
> > integer between 65 and 90".
>
> I would say something like "a capital English alphabet character A
> through Z (ASCII code 65 through 90)", which is 100% precise.
>
> Rudy
> --
> "Arguing that you don't care about the right to privacy because you have
> nothing to hide is no different than saying you don't care about free
> speech because you have nothing to say."
>
> --- Edward Snowden, 2015
>
> Rudolf Adamkovič <[email protected]> [he/him]
> http://adamkovic.org
>
--
+---------------------------------------------------------------+
| Derek Chen-Becker |
| GPG Key available at https://keybase.io/dchenbecker and |
| https://pgp.mit.edu/pks/lookup?search=derek%40chen-becker.org |
| Fngrprnt: EB8A 6480 F0A3 C8EB C1E7 7F42 AFC5 AFEE 96E4 6ACC |
+---------------------------------------------------------------+
[-- Attachment #1.2: Type: text/html, Size: 3575 bytes --]
[-- Attachment #2: 0001-org-syntax.el-Clarify-PRIORITY-value-definition.patch --]
[-- Type: application/octet-stream, Size: 1344 bytes --]
From 15a856de03ccf211a002bac3eaecff497a9d1ed2 Mon Sep 17 00:00:00 2001
From: Derek Chen-Becker <[email protected]>
Date: Thu, 4 Dec 2025 16:07:20 -0700
Subject: [PATCH] org-syntax.el: Clarify PRIORITY value definition
org-syntax.org (Headings): Update the definition to clarify the accepted
range of alphabetic priority values. Fix incorrect usage of "integral" to
"integer".
---
org-syntax.org | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/org-syntax.org b/org-syntax.org
index 637d0196..7cdfc6f5 100644
--- a/org-syntax.org
+++ b/org-syntax.org
@@ -377,10 +377,10 @@ STARS KEYWORD PRIORITY COMMENT TITLE TAGS
be configurable at runtime so that in-file todo keywords are properly
interpreted.]
-+ PRIORITY (optional) :: A single uppercase alphabetic character or an
- integral number between 0 and 64, preceded by a hash sign =#= and
- enclosed within square brackets (e.g. =[#A], [#0],= or =[#27]=). This
- is called a "priority cookie".
++ PRIORITY (optional) :: A capital English alphabet character A
+ through Z (ASCII code 65 through 90) or an integer between 0 and 64,
+ preceded by a hash sign =#= and enclosed within square brackets
+ (e.g. =[#A], [#0],= or =[#27]=). This is called a "priority cookie".
+ COMMENT (optional) :: String "COMMENT". Case is significant.
--
2.43.0
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [BUG] Numeric priorities only partially supported
2025-12-04 14:51 ` Derek Chen-Becker
@ 2025-12-07 20:11 ` Ihor Radchenko
0 siblings, 0 replies; 81+ messages in thread
From: Ihor Radchenko @ 2025-12-07 20:11 UTC (permalink / raw)
To: Derek Chen-Becker; +Cc: emacs-orgmode
Derek Chen-Becker <[email protected]> writes:
> Attached is the patch to fix formatting of the default (numeric) priorities
> in column view. I can't reproduce the other issue I was seeing with column
> view now, although it's not clear why. I'll continue work on some of the
> other areas identified in this thread.
Thanks!
Applied, onto main.
https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=9d128ebbc
--
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>
^ permalink raw reply [flat|nested] 81+ messages in thread
end of thread, other threads:[~2025-12-07 20:12 UTC | newest]
Thread overview: 81+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-13 16:02 [BUG] Numeric priorities only partially supported Tommy Jollyboat
2024-10-13 16:11 ` Ihor Radchenko
2025-06-08 14:18 ` Derek Chen-Becker
2025-06-08 16:03 ` Ihor Radchenko
2025-06-17 12:29 ` Derek Chen-Becker
2025-07-10 13:19 ` Derek Chen-Becker
2025-07-12 16:11 ` Ihor Radchenko
2025-07-12 19:27 ` Derek Chen-Becker
2025-07-19 12:56 ` Derek Chen-Becker
2025-07-19 13:22 ` Ihor Radchenko
2025-07-19 13:58 ` Derek Chen-Becker
2025-07-19 14:07 ` Ihor Radchenko
2025-07-19 16:04 ` Derek Chen-Becker
2025-07-19 16:29 ` Ihor Radchenko
2025-07-20 23:32 ` Derek Chen-Becker
2025-07-21 16:53 ` Ihor Radchenko
2025-09-13 11:36 ` Derek Chen-Becker
2025-09-13 14:39 ` Rudolf Adamkovič
2025-09-14 12:43 ` Derek Chen-Becker
2025-09-15 7:12 ` Rudolf Adamkovič
2025-09-27 13:24 ` Ihor Radchenko
2025-09-28 14:20 ` Derek Chen-Becker
2025-09-28 16:12 ` Ihor Radchenko
2025-09-28 16:53 ` Derek Chen-Becker
2025-09-28 18:46 ` Derek Chen-Becker
2025-10-06 4:01 ` Derek Chen-Becker
2025-10-11 12:51 ` Ihor Radchenko
2025-10-28 16:21 ` Derek Chen-Becker
2025-10-29 11:25 ` Derek Chen-Becker
2025-11-02 13:08 ` Ihor Radchenko
2025-11-03 13:59 ` Derek Chen-Becker
2025-11-05 18:52 ` Ihor Radchenko
2025-11-05 19:15 ` Derek Chen-Becker
2025-11-07 2:39 ` Derek Chen-Becker
2025-11-07 5:01 ` Jacob S. Gordon
2025-11-19 5:17 ` Derek Chen-Becker
2025-11-08 11:03 ` Ihor Radchenko
2025-11-10 5:01 ` Derek Chen-Becker
2025-11-10 17:32 ` Ihor Radchenko
2025-11-11 1:19 ` Derek Chen-Becker
2025-11-15 18:13 ` Ihor Radchenko
2025-11-15 21:40 ` Derek Chen-Becker
2025-11-15 21:45 ` Ihor Radchenko
2025-11-15 22:51 ` Derek Chen-Becker
2025-11-16 9:30 ` Ihor Radchenko
2025-11-16 14:55 ` Derek Chen-Becker
2025-11-16 22:26 ` Derek Chen-Becker
2025-11-17 5:53 ` Ihor Radchenko
2025-11-16 23:18 ` Derek Chen-Becker
2025-11-17 5:55 ` Ihor Radchenko
2025-11-19 5:15 ` Derek Chen-Becker
2025-11-22 14:15 ` Ihor Radchenko
2025-11-22 15:07 ` Derek Chen-Becker
2025-11-22 15:32 ` Ihor Radchenko
2025-11-22 16:22 ` Derek Chen-Becker
2025-11-22 16:37 ` Ihor Radchenko
2025-11-22 16:52 ` Derek Chen-Becker
2025-11-22 16:55 ` Ihor Radchenko
2025-11-22 16:56 ` Derek Chen-Becker
2025-11-23 12:45 ` Ihor Radchenko
2025-11-23 14:13 ` Derek Chen-Becker
2025-11-23 14:17 ` Ihor Radchenko
2025-11-23 14:17 ` Derek Chen-Becker
2025-11-23 14:54 ` Derek Chen-Becker
2025-11-23 14:57 ` Ihor Radchenko
2025-11-25 3:22 ` Derek Chen-Becker
2025-11-30 11:55 ` Ihor Radchenko
2025-12-04 5:27 ` Derek Chen-Becker
2025-12-04 8:24 ` Christian Moe
2025-12-04 14:51 ` Derek Chen-Becker
2025-12-07 20:11 ` Ihor Radchenko
2025-12-04 17:52 ` Ihor Radchenko
2025-12-04 23:40 ` Derek Chen-Becker
2025-12-05 13:05 ` Rudolf Adamkovič
2025-12-05 15:21 ` Derek Chen-Becker
2025-12-05 17:23 ` Christian Moe
2025-12-05 17:31 ` Derek Chen-Becker
2025-12-06 12:16 ` Rudolf Adamkovič
2025-12-06 16:45 ` Derek Chen-Becker
2025-12-06 12:03 ` Rudolf Adamkovič
2025-06-08 20:42 ` Jacob S. Gordon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).