Skip to content

Commit 8667167

Browse files
feat(agenda)!: rewrite agenda rendering and fix filters (#848)
1 parent 738de39 commit 8667167

26 files changed

+1342
-1191
lines changed

lua/orgmode/agenda/agenda_item.lua

+12-35
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ end
2020
---@field is_in_date_range boolean
2121
---@field date_range_days number
2222
---@field label string
23-
---@field highlights table[]
2423
local AgendaItem = {}
2524

2625
---@param headline_date OrgDate single date in a headline
2726
---@param headline OrgHeadline
2827
---@param date OrgDate date for which item should be rendered
2928
---@param index? number
29+
---@return OrgAgendaItem
3030
function AgendaItem:new(headline_date, headline, date, index)
3131
local opts = {}
3232
opts.headline_date = headline_date
@@ -45,7 +45,6 @@ function AgendaItem:new(headline_date, headline, date, index)
4545
opts.is_in_date_range = headline_date:is_none() and headline_date:is_in_date_range(date)
4646
opts.date_range_days = headline_date:get_date_range_days()
4747
opts.label = ''
48-
opts.highlights = {}
4948
if opts.repeats_on_date then
5049
opts.real_date = opts.headline_date:apply_repeater_until(opts.date)
5150
end
@@ -77,13 +76,6 @@ end
7776

7877
function AgendaItem:_generate_data()
7978
self.label = self:_generate_label()
80-
self.highlights = {}
81-
local highlight = self:_generate_highlight()
82-
if highlight then
83-
table.insert(self.highlights, highlight)
84-
end
85-
self:_add_keyword_highlight()
86-
self:_add_priority_highlight()
8779
end
8880

8981
function AgendaItem:_is_valid_for_today()
@@ -220,63 +212,48 @@ function AgendaItem:_format_time(date)
220212
return formatted_time
221213
end
222214

223-
function AgendaItem:_generate_highlight()
215+
---@return string | nil
216+
function AgendaItem:get_hlgroup()
224217
if self.headline_date:is_deadline() then
225218
if self.headline:is_done() then
226-
return { hlgroup = hl_map.ok }
219+
return hl_map.ok
227220
end
228221
if self.is_today and self.headline_date:is_after(self.date, 'day') then
229222
local diff = math.abs(self.date:diff(self.headline_date))
230223
if diff <= FUTURE_DEADLINE_AS_WARNING_DAYS then
231-
return { hlgroup = hl_map.warning }
224+
return hl_map.warning
232225
end
233226
return nil
234227
end
235228

236-
return { hlgroup = hl_map.deadline }
229+
return hl_map.deadline
237230
end
238231

239232
if self.headline_date:is_scheduled() then
240233
if self.headline_date:is_past('day') and not self.headline:is_done() then
241-
return { hlgroup = hl_map.warning }
234+
return hl_map.warning
242235
end
243236

244-
return { hlgroup = hl_map.ok }
237+
return hl_map.ok
245238
end
246239

247240
return nil
248241
end
249242

250-
function AgendaItem:_add_keyword_highlight()
243+
function AgendaItem:get_todo_hlgroup()
251244
local todo_keyword, _, type = self.headline:get_todo()
252245
if not todo_keyword then
253246
return
254247
end
255-
local hlgroup = hl_map[todo_keyword] or hl_map[type]
256-
if hlgroup then
257-
table.insert(self.highlights, {
258-
hlgroup = hlgroup,
259-
todo_keyword = todo_keyword,
260-
})
261-
end
248+
return hl_map[todo_keyword] or hl_map[type], todo_keyword
262249
end
263250

264-
function AgendaItem:_add_priority_highlight()
251+
function AgendaItem:get_priority_hlgroup()
265252
local priority, priority_node = self.headline:get_priority()
266253
if not priority_node then
267254
return
268255
end
269-
local hlgroup = hl_map.priority[priority].hl_group
270-
local last_hl = self.highlights[#self.highlights]
271-
local start_col = 2
272-
if last_hl and last_hl.todo_keyword then
273-
start_col = start_col + last_hl.todo_keyword:len()
274-
end
275-
table.insert(self.highlights, {
276-
hlgroup = hlgroup,
277-
priority = priority,
278-
start_col = start_col,
279-
})
256+
return hl_map.priority[priority].hl_group, priority
280257
end
281258

282259
return AgendaItem

lua/orgmode/agenda/filter.lua

+32-104
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
1-
local utils = require('orgmode.utils')
21
---@class OrgAgendaFilter
32
---@field value string
4-
---@field available_tags table<string, boolean>
5-
---@field available_categories table<string, boolean>
6-
---@field filter_type 'include' | 'exclude'
7-
---@field tags table[]
8-
---@field categories table[]
3+
---@field available_values table<string, boolean>
4+
---@field values table[]
95
---@field term string
106
---@field parsed boolean
11-
---@field applying boolean
127
local AgendaFilter = {}
138

9+
---@return OrgAgendaFilter
1410
function AgendaFilter:new()
1511
local data = {
1612
value = '',
17-
available_tags = {},
18-
available_categories = {},
19-
filter_type = 'exclude',
20-
tags = {},
21-
categories = {},
13+
available_values = {},
14+
values = {},
2215
term = '',
2316
parsed = false,
24-
applying = false,
2517
}
2618
setmetatable(data, self)
2719
self.__index = self
@@ -40,156 +32,92 @@ function AgendaFilter:matches(headline)
4032
return true
4133
end
4234
local term_match = vim.trim(self.term) == ''
43-
local tag_cat_match_empty = #self.tags == 0 and #self.categories == 0
35+
local values_match_empty = #self.values == 0
4436

4537
if not term_match then
4638
local rgx = vim.regex(self.term) --[[@as vim.regex]]
4739
term_match = rgx:match_str(headline:get_title()) and true or false
4840
end
4941

50-
if tag_cat_match_empty then
42+
if values_match_empty then
5143
return term_match
5244
end
5345

54-
local tag_cat_match = false
55-
56-
if self.filter_type == 'include' then
57-
tag_cat_match = self:_matches_include(headline)
58-
else
59-
tag_cat_match = self:_matches_exclude(headline)
60-
end
46+
local tag_cat_match = self:_match(headline)
6147

6248
return tag_cat_match and term_match
6349
end
6450

65-
---@param headline OrgHeadline
6651
---@private
67-
function AgendaFilter:_matches_exclude(headline)
68-
for _, tag in ipairs(self.tags) do
69-
if headline:has_tag(tag.value) then
70-
return false
71-
end
72-
end
73-
74-
for _, category in ipairs(self.categories) do
75-
if headline:matches_category(category.value) then
76-
return false
77-
end
78-
end
79-
80-
return true
81-
end
82-
8352
---@param headline OrgHeadline
84-
---@private
85-
function AgendaFilter:_matches_include(headline)
86-
local tags_to_check = {}
87-
local categories_to_check = {}
88-
89-
for _, tag in ipairs(self.tags) do
90-
if tag.operator == '-' then
91-
if headline:has_tag(tag.value) then
92-
return false
93-
end
94-
else
95-
table.insert(tags_to_check, tag.value)
96-
end
97-
end
98-
99-
for _, category in ipairs(self.categories) do
100-
if category.operator == '-' then
101-
if headline:matches_category(category.value) then
53+
---@return boolean
54+
function AgendaFilter:_match(headline)
55+
for _, value in ipairs(self.values) do
56+
if value.operator == '-' then
57+
if headline:has_tag(value.value) or headline:matches_category(value.value) then
10258
return false
10359
end
104-
else
105-
table.insert(categories_to_check, category.value)
106-
end
107-
end
108-
109-
local tags_passed = #tags_to_check == 0
110-
local categories_passed = #categories_to_check == 0
111-
112-
for _, category in ipairs(categories_to_check) do
113-
if headline:matches_category(category) then
114-
categories_passed = true
115-
break
116-
end
117-
end
118-
119-
for _, tag in ipairs(tags_to_check) do
120-
if headline:has_tag(tag) then
121-
tags_passed = true
122-
break
60+
elseif not headline:has_tag(value.value) and not headline:matches_category(value.value) then
61+
return false
12362
end
12463
end
12564

126-
return tags_passed and categories_passed
65+
return true
12766
end
12867

12968
---@param filter string
13069
---@param skip_check? boolean do not check if given values exist in the current view
13170
function AgendaFilter:parse(filter, skip_check)
13271
filter = filter or ''
13372
self.value = filter
134-
self.tags = {}
135-
self.categories = {}
73+
self.values = {}
13674
local search_rgx = '/[^/]*/?'
13775
local search_term = filter:match(search_rgx)
13876
if search_term then
13977
search_term = search_term:gsub('^/*', ''):gsub('/*$', '')
14078
end
14179
filter = filter:gsub(search_rgx, '')
14280
for operator, tag_cat in string.gmatch(filter, '([%+%-]*)([^%-%+]+)') do
143-
if not operator or operator == '' or operator == '+' then
144-
self.filter_type = 'include'
145-
end
14681
local val = vim.trim(tag_cat)
14782
if val ~= '' then
148-
if self.available_tags[val] or skip_check then
149-
table.insert(self.tags, { operator = operator, value = val })
150-
elseif self.available_categories[val] or skip_check then
151-
table.insert(self.categories, { operator = operator, value = val })
83+
if self.available_values[val] or skip_check then
84+
table.insert(self.values, { operator = operator, value = val })
15285
end
15386
end
15487
end
15588
self.term = search_term or ''
156-
self.applying = true
157-
if skip_check then
158-
self.parsed = true
159-
end
89+
return self
16090
end
16191

16292
function AgendaFilter:reset()
16393
self.value = ''
16494
self.term = ''
16595
self.parsed = false
166-
self.applying = false
16796
end
16897

169-
---@param content table[]
170-
function AgendaFilter:parse_tags_and_categories(content)
98+
---@param agenda_views OrgAgendaViewType[]
99+
function AgendaFilter:parse_available_filters(agenda_views)
171100
if self.parsed then
172101
return
173102
end
174-
local tags = {}
175-
local categories = {}
176-
for _, item in ipairs(content) do
177-
if item.jumpable and item.headline then
178-
categories[item.headline:get_category():lower()] = true
179-
for _, tag in ipairs(item.headline:get_tags()) do
180-
tags[tag:lower()] = true
103+
local values = {}
104+
for _, agenda_view in ipairs(agenda_views) do
105+
for _, line in ipairs(agenda_view:get_lines()) do
106+
if line.headline then
107+
values[line.headline:get_category()] = true
108+
for _, tag in ipairs(line.headline:get_tags()) do
109+
values[tag] = true
110+
end
181111
end
182112
end
183113
end
184-
self.available_tags = tags
185-
self.available_categories = categories
114+
self.available_values = values
186115
self.parsed = true
187116
end
188117

189118
---@return string[]
190119
function AgendaFilter:get_completion_list()
191-
local list = vim.tbl_keys(self.available_tags)
192-
return utils.concat(list, vim.tbl_keys(self.available_categories), true)
120+
return vim.tbl_keys(self.available_values)
193121
end
194122

195123
return AgendaFilter

0 commit comments

Comments
 (0)