Skip to content

Commit c2595bd

Browse files
authored
Fix priorities (#784) (#785)
* test: implement test to expose the bug * test: add more cases in priority_state_spec * refactor: make priority_state independent of config To prepare the fix, we need to avoid a dependency cycle between config and priority_state, because we will need priority_state in config to implement the fix. We inject the needed values, whereever an instance of PriorityState is created. * fix: generate missing intermediate priorities A headline priority gets only detected correctly, if it is part of the table returned by Config:get_priorities(). While it previously only returned the priorities explicitly defined by the user, it now uses PriorityState to generate also intermediate ones. This happens, if the highest and the lowest priority define a range of more than three priorities. * docs: mention new highlight groups Add new highlight groups to "Colors" chapter. --------- Co-authored-by: Sebastian Flügge <[email protected]>
1 parent 98d9db3 commit c2595bd

File tree

8 files changed

+164
-33
lines changed

8 files changed

+164
-33
lines changed

DOCS.md

+2
Original file line numberDiff line numberDiff line change
@@ -1708,7 +1708,9 @@ The following highlight groups are used:
17081708
- `@org.headline.level7`: Headline at level 7 - linked to `Special`
17091709
- `@org.headline.level8`: Headline at level 8 - linked to `String`
17101710
- `@org.priority.highest`: Highest priority marker - linked to `@comment.error`
1711+
- `@org.priority.high`: High priority marker - Not linked to anything, defaults to normal text
17111712
- `@org.priority.default`: Default priority marker - Not linked to anything, defaults to normal text
1713+
- `@org.priority.low`: Lowest priority marker - Not linked to anything, defaults to normal text
17121714
- `@org.priority.lowest`: Lowest priority marker - Not linked to anything, defaults to normal text
17131715
- `@org.timestamp.active`: An active timestamp - linked to `@keyword`
17141716
- `@org.timestamp.inactive`: An inactive timestamp - linked to `@comment`

lua/orgmode/api/headline.lua

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local OrgPosition = require('orgmode.api.position')
2+
local config = require('orgmode.config')
23
local PriorityState = require('orgmode.objects.priority_state')
34
local Date = require('orgmode.objects.date')
45
local Calendar = require('orgmode.objects.calendar')
@@ -112,7 +113,8 @@ function OrgHeadline:priority_up()
112113
return self:_do_action(function()
113114
local headline = org.files:get_closest_headline()
114115
local current_priority = headline:get_priority()
115-
local priority_state = PriorityState:new(current_priority)
116+
local prio_range = config:get_priority_range()
117+
local priority_state = PriorityState:new(current_priority, prio_range)
116118
return headline:set_priority(priority_state:increase())
117119
end)
118120
end
@@ -123,7 +125,8 @@ function OrgHeadline:priority_down()
123125
return self:_do_action(function()
124126
local headline = org.files:get_closest_headline()
125127
local current_priority = headline:get_priority()
126-
local priority_state = PriorityState:new(current_priority)
128+
local prio_range = config:get_priority_range()
129+
local priority_state = PriorityState:new(current_priority, prio_range)
127130
return headline:set_priority(priority_state:decrease())
128131
end)
129132
end

lua/orgmode/config/init.lua

+29-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ local defaults = require('orgmode.config.defaults')
55
---@type table<string, OrgMapEntry>
66
local mappings = require('orgmode.config.mappings')
77
local TodoKeywords = require('orgmode.objects.todo_keywords')
8+
local PriorityState = require('orgmode.objects.priority_state')
89

910
---@class OrgConfig:OrgDefaultConfig
1011
---@field opts table
@@ -331,12 +332,37 @@ function Config:get_inheritable_tags(headline)
331332
end, headline.tags)
332333
end
333334

334-
function Config:get_priorities()
335+
function Config:get_priority_range()
335336
return {
337+
highest = self.org_priority_highest,
338+
default = self.org_priority_default,
339+
lowest = self.org_priority_lowest,
340+
}
341+
end
342+
343+
function Config:get_priorities()
344+
local priorities = {
336345
[self.opts.org_priority_highest] = { type = 'highest', hl_group = '@org.priority.highest' },
337-
[self.opts.org_priority_default] = { type = 'default', hl_group = '@org.priority.default' },
338-
[self.opts.org_priority_lowest] = { type = 'lowest', hl_group = '@org.priority.lowest' },
339346
}
347+
348+
local current_prio = PriorityState:new(self.opts.org_priority_highest, self:get_priority_range())
349+
while current_prio:as_num() < current_prio:default_as_num() do
350+
current_prio:decrease()
351+
priorities[current_prio.priority] = { type = 'high', hl_group = '@org.priority.high' }
352+
end
353+
354+
-- we need to overwrite the default value set by the first loop
355+
priorities[self.opts.org_priority_default] = { type = 'default', hl_group = '@org.priority.default' }
356+
357+
while current_prio:as_num() < current_prio:lowest_as_num() do
358+
current_prio:decrease()
359+
priorities[current_prio.priority] = { type = 'low', hl_group = '@org.priority.low' }
360+
end
361+
362+
-- we need to overwrite the lowest value set by the second loop
363+
priorities[self.opts.org_priority_lowest] = { type = 'lowest', hl_group = '@org.priority.lowest' }
364+
365+
return priorities
340366
end
341367

342368
function Config:setup_ts_predicates()

lua/orgmode/files/headline.lua

+2-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ end
192192

193193
function Headline:get_priority_sort_value()
194194
local priority = self:get_priority()
195-
return PriorityState:new(priority):get_sort_value()
195+
local prio_range = config:get_priority_range()
196+
return PriorityState:new(priority, prio_range):get_sort_value()
196197
end
197198

198199
function Headline:is_archived()

lua/orgmode/objects/priority_state.lua

+39-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
local config = require('orgmode.config')
21
local utils = require('orgmode.utils')
32

43
---@class OrgPriorityState
@@ -9,12 +8,13 @@ local utils = require('orgmode.utils')
98
local PriorityState = {}
109

1110
---@param priority string
12-
function PriorityState:new(priority)
11+
---@param prio_range { highest: string, lowest: string, default: string }
12+
function PriorityState:new(priority, prio_range)
1313
local o = {}
1414

15-
o.high_priority = tostring(config.org_priority_highest)
16-
o.low_priority = tostring(config.org_priority_lowest)
17-
o.default_priority = tostring(config.org_priority_default)
15+
o.high_priority = tostring(prio_range.highest)
16+
o.low_priority = tostring(prio_range.lowest)
17+
o.default_priority = tostring(prio_range.default)
1818
o.priority = tostring(priority or o.default_priority)
1919

2020
setmetatable(o, self)
@@ -78,13 +78,44 @@ function PriorityState:decrease()
7878
return self.priority
7979
end
8080

81+
function PriorityState:highest_as_num()
82+
return PriorityState._as_number(self.high_priority)
83+
end
84+
85+
function PriorityState:default_as_num()
86+
return PriorityState._as_number(self.default_priority)
87+
end
88+
89+
function PriorityState:lowest_as_num()
90+
return PriorityState._as_number(self.low_priority)
91+
end
92+
93+
function PriorityState:as_num()
94+
return PriorityState._as_number(self.priority)
95+
end
96+
8197
---@param direction number
8298
---@return string
8399
function PriorityState:_apply(direction)
84-
if type(tonumber(self.priority)) == 'number' then
85-
return tostring(tonumber(self.priority) + direction)
100+
local new_value = PriorityState._as_number(self.priority) + direction
101+
if PriorityState._is_number(self.priority) then
102+
return tostring(new_value)
103+
end
104+
return string.char(new_value)
105+
end
106+
107+
---@param prio string
108+
---@return number?
109+
function PriorityState._as_number(prio)
110+
if PriorityState._is_number(prio) then
111+
return tonumber(prio)
86112
end
87-
return string.char(string.byte(self.priority) + direction)
113+
return string.byte(prio)
114+
end
115+
116+
---@return boolean
117+
function PriorityState._is_number(prio)
118+
return type(tonumber(prio)) == 'number'
88119
end
89120

90121
return PriorityState

lua/orgmode/org/mappings.lua

+2-1
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,8 @@ end
320320
function OrgMappings:set_priority(direction)
321321
local headline = self.files:get_closest_headline()
322322
local current_priority = headline:get_priority()
323-
local priority_state = PriorityState:new(current_priority)
323+
local prio_range = config:get_priority_range()
324+
local priority_state = PriorityState:new(current_priority, prio_range)
324325

325326
local new_priority = direction
326327
if direction == 'up' then

tests/plenary/object/priority_state_spec.lua

+24-18
Original file line numberDiff line numberDiff line change
@@ -18,101 +18,107 @@ describe('Priority state', function()
1818
})
1919
end
2020

21+
local create_priority = function(prio)
22+
return PriorityState:new(prio, config:get_priority_range())
23+
end
24+
2125
it('should increase single numeric priority', function()
2226
numeric_config()
23-
local priority = PriorityState:new(10)
27+
local priority = create_priority(10)
2428
assert.are.same('9', priority:increase())
2529
end)
2630

2731
it('should decrease single numeric priority', function()
2832
numeric_config()
29-
local priority = PriorityState:new(9)
33+
local priority = create_priority(9)
3034
assert.are.same('10', priority:decrease())
3135
end)
3236

3337
it('should increase single alpha priority', function()
3438
alpha_config()
35-
local priority = PriorityState:new('C')
39+
local priority = create_priority('D')
40+
assert.are.same('C', priority:increase())
3641
assert.are.same('B', priority:increase())
42+
assert.are.same('A', priority:increase())
3743
end)
3844

3945
it('should decrease single alpha priority', function()
4046
alpha_config()
41-
local priority = PriorityState:new('B')
47+
local priority = create_priority('B')
4248
assert.are.same('C', priority:decrease())
4349
end)
4450

4551
it('should change to empty priority when numeric increased beyond highest', function()
4652
numeric_config()
47-
local priority = PriorityState:new('1')
53+
local priority = create_priority('1')
4854
assert.are.same('', priority:increase())
4955
end)
5056

5157
it('should change to empty priority when numeric decreased beyond lowest', function()
5258
numeric_config()
53-
local priority = PriorityState:new('15')
59+
local priority = create_priority('15')
5460
assert.are.same('', priority:decrease())
5561
end)
5662

5763
it('should change to empty priority when alpha increased beyond highest', function()
5864
alpha_config()
59-
local priority = PriorityState:new('A')
65+
local priority = create_priority('A')
6066
assert.are.same('', priority:increase())
6167
end)
6268

6369
it('should change to empty priority when alpha decreased beyond lowest', function()
6470
alpha_config()
65-
local priority = PriorityState:new('D')
71+
local priority = create_priority('D')
6672
assert.are.same('', priority:decrease())
6773
end)
6874

6975
it('should convert numeric priorities to a string for comparison', function()
7076
numeric_config()
71-
local priority = PriorityState:new(1)
77+
local priority = create_priority(1)
7278
assert.are.same(priority.priority, '1')
7379
end)
7480

7581
it('should return the string representation of the value to use for sorting for alpha strings', function()
7682
alpha_config()
77-
local priority = PriorityState:new('A')
83+
local priority = create_priority('A')
7884
assert.are.same(-65, priority:get_sort_value())
7985
end)
8086

8187
it('should return the string representation of the value to use for sorting for numeric strings', function()
8288
numeric_config()
83-
local priority = PriorityState:new(1)
89+
local priority = create_priority(1)
8490
assert.are.same(-49, priority:get_sort_value())
8591
end)
8692

8793
it('should return default priority value if empty when sorting', function()
8894
alpha_config()
89-
local priority = PriorityState:new('')
95+
local priority = create_priority('')
9096
assert.are.same(-1 * string.byte(config.org_priority_default), priority:get_sort_value())
9197
end)
9298

9399
it('should compare alpha priorities correctly', function()
94100
alpha_config()
95-
local higher = PriorityState:new('A')
96-
local lower = PriorityState:new('B')
101+
local higher = create_priority('A')
102+
local lower = create_priority('B')
97103
assert.Is.True(higher:get_sort_value() > lower:get_sort_value())
98104
end)
99105

100106
it('should compare numeric priorities correctly', function()
101107
numeric_config()
102-
local higher = PriorityState:new(1)
103-
local lower = PriorityState:new(2)
108+
local higher = create_priority(1)
109+
local lower = create_priority(2)
104110
assert.Is.True(higher:get_sort_value() > lower:get_sort_value())
105111
end)
106112

107113
it('should change to highest priority if priority increased and currently empty', function()
108114
alpha_config()
109-
local priority = PriorityState:new('')
115+
local priority = create_priority('')
110116
assert.are.same('D', priority:increase())
111117
end)
112118

113119
it('should change to lowest priority if priority decreased and currently empty', function()
114120
alpha_config()
115-
local priority = PriorityState:new('')
121+
local priority = create_priority('')
116122
assert.are.same('A', priority:decrease())
117123
end)
118124
end)

tests/plenary/ui/mappings/priority_spec.lua

+61
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1+
local config = require('orgmode.config')
12
local helpers = require('tests.plenary.helpers')
23

34
describe('Priority mappings', function()
5+
local alpha_config = function()
6+
config:extend({
7+
org_priority_highest = 'A',
8+
org_priority_default = 'C',
9+
org_priority_lowest = 'E',
10+
})
11+
end
12+
13+
local numeric_config = function()
14+
config:extend({
15+
org_priority_highest = 1,
16+
org_priority_default = 5,
17+
org_priority_lowest = 15,
18+
})
19+
end
20+
421
after_each(function()
522
vim.cmd([[silent! %bw!]])
623
end)
@@ -64,4 +81,48 @@ describe('Priority mappings', function()
6481
vim.cmd('norm ,o,a\r')
6582
assert.are.same('* [#A] Test orgmode', vim.fn.getline(1))
6683
end)
84+
85+
it('should increase the default character priority, when it is explicitly defined', function()
86+
alpha_config()
87+
helpers.create_file({
88+
'* [#C] Test orgmode',
89+
})
90+
vim.fn.cursor(1, 1)
91+
assert.are.same('* [#C] Test orgmode', vim.fn.getline(1))
92+
vim.cmd('norm ciR')
93+
assert.are.same('* [#B] Test orgmode', vim.fn.getline(1))
94+
end)
95+
96+
it('should increase a numeric priority, which is not explicitly defined', function()
97+
numeric_config()
98+
helpers.create_file({
99+
'* [#5] Test orgmode',
100+
})
101+
vim.fn.cursor(1, 1)
102+
assert.are.same('* [#5] Test orgmode', vim.fn.getline(1))
103+
vim.cmd('norm ciR')
104+
assert.are.same('* [#4] Test orgmode', vim.fn.getline(1))
105+
end)
106+
107+
it('should increase a character priority, which is not explicitly defined', function()
108+
alpha_config()
109+
helpers.create_file({
110+
'* [#D] Test orgmode',
111+
})
112+
vim.fn.cursor(1, 1)
113+
assert.are.same('* [#D] Test orgmode', vim.fn.getline(1))
114+
vim.cmd('norm ciR')
115+
assert.are.same('* [#C] Test orgmode', vim.fn.getline(1))
116+
end)
117+
118+
it('should increase a numeric priority, which is not explicitly defined', function()
119+
numeric_config()
120+
helpers.create_file({
121+
'* [#10] Test orgmode',
122+
})
123+
vim.fn.cursor(1, 1)
124+
assert.are.same('* [#10] Test orgmode', vim.fn.getline(1))
125+
vim.cmd('norm ciR')
126+
assert.are.same('* [#9] Test orgmode', vim.fn.getline(1))
127+
end)
67128
end)

0 commit comments

Comments
 (0)