Skip to content

Commit 5b3c783

Browse files
Add mappings for adding/updating schedule/deadline date. Closes #65.
1 parent 9b412b3 commit 5b3c783

File tree

8 files changed

+203
-11
lines changed

8 files changed

+203
-11
lines changed

DOCS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,12 @@ Go to previous heading on same level. Doesn't go outside of parent.<br />
572572
#### **outline_up_heading**
573573
*mapped to*: `g{`<br />
574574
Go to parent heading.<br />
575+
#### **org_deadline**
576+
*mapped to*: `<Leader>oid`<br />
577+
Insert/Update deadline date.<br />
578+
#### **org_schedule**
579+
*mapped to*: `<Leader>ois`<br />
580+
Insert/Update scheduled date.<br />
575581
#### **org_show_help**
576582
*mapped to*: `?`<br />
577583
Show help popup with mappings

doc/orgmode.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ CONTENTS *orgmode-content
8888
1.2.4.27. org_forward_heading_same_level.|orgmode-org_forward_heading_same_level|
8989
1.2.4.28. org_backward_heading_same_level.|orgmode-org_backward_heading_same_level|
9090
1.2.4.29. outline_up_heading..............|orgmode-outline_up_heading|
91-
1.2.4.30. org_show_help........................|orgmode-org_show_help|
91+
1.2.4.30. org_deadline..........................|orgmode-org_deadline|
92+
1.2.4.31. org_schedule..........................|orgmode-org_schedule|
93+
1.2.4.32. org_show_help........................|orgmode-org_show_help|
9294
1.3. Autocompletion...................................|orgmode-autocompletion|
9395
1.4. Abbreviations.....................................|orgmode-abbreviations|
9496
1.5. Colors...................................................|orgmode-colors|
@@ -787,6 +789,16 @@ OUTLINE_UP_HEADING *orgmode-outline_up_headin
787789
mapped to: `g{`
788790
Go to parent heading.
789791

792+
ORG_DEADLINE *orgmode-org_deadline*
793+
794+
mapped to: `<Leader>oid`
795+
Insert/Update deadline date.
796+
797+
ORG_SCHEDULE *orgmode-org_schedule*
798+
799+
mapped to: `<Leader>ois`
800+
Insert/Update scheduled date.
801+
790802
ORG_SHOW_HELP *orgmode-org_show_help*
791803

792804
mapped to: `?`

lua/orgmode/config/defaults.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ return {
9595
org_forward_heading_same_level = ']]',
9696
org_backward_heading_same_level = '[[',
9797
outline_up_heading = 'g{',
98+
org_deadline = '<Leader>oid',
99+
org_schedule = '<Leader>ois',
98100
org_show_help = '?',
99101
},
100102
},

lua/orgmode/config/mappings.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ return {
5151
org_forward_heading_same_level = { 'org_mappings.forward_heading_same_level' },
5252
org_backward_heading_same_level = { 'org_mappings.backward_heading_same_level' },
5353
outline_up_heading = { 'org_mappings.outline_up_heading' },
54+
org_deadline = { 'org_mappings.org_deadline' },
55+
org_schedule = { 'org_mappings.org_schedule' },
5456
org_show_help = { 'org_mappings.show_help' },
5557
},
5658
}

lua/orgmode/objects/help.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ local helps = {
3232
{ key = 'org_forward_heading_same_level', description = 'Go to next heading on same level' },
3333
{ key = 'org_backward_heading_same_level', description = 'Go to previous heading on same level' },
3434
{ key = 'outline_up_heading', description = 'Go to parent heading' },
35+
{ key = 'org_deadline', description = 'Insert/Update deadline date' },
36+
{ key = 'org_schedule', description = 'Insert/Update scheduled date' },
3537
{ key = 'org_show_help', description = 'Show this help' },
3638
},
3739
orgagenda = {

lua/orgmode/org/mappings.lua

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,28 @@ function OrgMappings:outline_up_heading()
389389
return vim.fn.cursor(item.parent.range.start_line, 1)
390390
end
391391

392+
function OrgMappings:org_deadline()
393+
local item = Files.get_current_file():get_closest_headline()
394+
local deadline_date = item:get_deadline_date()
395+
local cb = function(new_date)
396+
item:remove_closed_date()
397+
item = Files.get_current_file():get_closest_headline()
398+
item:add_deadline_date(new_date)
399+
end
400+
Calendar.new({ callback = cb, date = deadline_date or Date.today() }).open()
401+
end
402+
403+
function OrgMappings:org_schedule()
404+
local item = Files.get_current_file():get_closest_headline()
405+
local scheduled_date = item:get_scheduled_date()
406+
local cb = function(new_date)
407+
item:remove_closed_date()
408+
item = Files.get_current_file():get_closest_headline()
409+
item:add_scheduled_date(new_date)
410+
end
411+
Calendar.new({ callback = cb, date = scheduled_date or Date.today() }).open()
412+
end
413+
392414
---@param direction string
393415
---@param skip_fast_access boolean
394416
---@return string

lua/orgmode/parser/headline.lua

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,20 @@ function Headline:get_deadline_and_scheduled_dates()
213213
end, self.dates)
214214
end
215215

216+
---@return Date
217+
function Headline:get_scheduled_date()
218+
return vim.tbl_filter(function(date)
219+
return date:is_scheduled()
220+
end, self.dates)[1]
221+
end
222+
223+
---@return Date
224+
function Headline:get_deadline_date()
225+
return vim.tbl_filter(function(date)
226+
return date:is_deadline()
227+
end, self.dates)[1]
228+
end
229+
216230
function Headline:_get_content_by_lnum(lnum)
217231
return self.content[lnum - self.range.start_line]
218232
end
@@ -304,17 +318,25 @@ function Headline:add_closed_date()
304318
if closed_date then
305319
return nil
306320
end
307-
local planning = self.content[1]
308-
if planning and planning:is_planning() then
309-
return vim.api.nvim_call_function('setline', {
310-
planning.range.start_line,
311-
string.format('%s CLOSED: [%s]', planning.line, Date.now():to_string()),
312-
})
321+
return self:_add_planning_date(Date.now(), 'CLOSED')
322+
end
323+
324+
---@param date Date
325+
function Headline:add_scheduled_date(date)
326+
local scheduled_date = self:get_scheduled_date()
327+
if scheduled_date then
328+
return self:_update_date(scheduled_date, date)
313329
end
314-
return vim.api.nvim_call_function('append', {
315-
self.range.start_line,
316-
string.format('%sCLOSED: [%s]', string.rep(' ', self.level + 1), Date.now():to_string()),
317-
})
330+
return self:_add_planning_date(date, 'SCHEDULED', true)
331+
end
332+
333+
---@param date Date
334+
function Headline:add_deadline_date(date)
335+
local deadline_date = self:get_deadline_date()
336+
if deadline_date then
337+
return self:_update_date(deadline_date, date)
338+
end
339+
return self:_add_planning_date(date, 'DEADLINE', true)
318340
end
319341

320342
function Headline:remove_closed_date()
@@ -457,4 +479,50 @@ function Headline:get_category()
457479
return self.category
458480
end
459481

482+
function Headline:_update_date(date, new_date)
483+
date = date:set({
484+
year = new_date.year,
485+
month = new_date.month,
486+
day = new_date.day,
487+
})
488+
local line = vim.api.nvim_call_function('getline', { date.range.start_line })
489+
local view = vim.fn.winsaveview()
490+
local new_line = string.format(
491+
'%s%s%s',
492+
line:sub(1, date.range.start_col),
493+
date:to_string(),
494+
line:sub(date.range.end_col)
495+
)
496+
vim.api.nvim_call_function('setline', {
497+
date.range.start_line,
498+
new_line,
499+
})
500+
vim.fn.winrestview(view)
501+
return true
502+
end
503+
504+
---@param date Date
505+
---@param type string
506+
---@param active boolean
507+
---@return string
508+
function Headline:_add_planning_date(date, type, active)
509+
local planning = self.content[1]
510+
local date_string = string.format('%s%s%s', active and '<' or '[', date:to_string(), active and '>' or ']')
511+
if planning and planning:is_planning() then
512+
planning.line = string.format('%s %s: %s', planning.line, type, date_string)
513+
return vim.api.nvim_call_function('setline', {
514+
planning.range.start_line,
515+
planning.line,
516+
})
517+
end
518+
local indent = ''
519+
if config.org_indent_mode == 'indent' then
520+
indent = string.rep(' ', self.level + 1)
521+
end
522+
return vim.api.nvim_call_function('append', {
523+
self.range.start_line,
524+
string.format('%s%s: %s', indent, type, date_string),
525+
})
526+
end
527+
460528
return Headline

tests/plenary/org/org_spec.lua

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,82 @@ describe('Org file', function()
132132
assert.stub(api.nvim_call_function).was.called_with('deletebufline', { 4, 2 })
133133
mock.revert(api)
134134
end)
135+
136+
it('should add and update deadline date', function()
137+
local deadline_date = Date.from_string('2021-08-18 Wed')
138+
local lines = {
139+
'* TODO Test orgmode :WORK:',
140+
'* TODO Another todo',
141+
}
142+
local parsed = parser.parse(lines, 'work')
143+
local api = mock(vim.api, true)
144+
api.nvim_call_function.returns(true)
145+
local headline = parsed:get_item(1)
146+
local result = headline:add_deadline_date(deadline_date)
147+
assert.are.same(true, result)
148+
assert.stub(api.nvim_call_function).was.called_with('append', {
149+
1,
150+
' DEADLINE: <2021-08-18 Wed>',
151+
})
152+
lines = {
153+
'* TODO Test orgmode :WORK:',
154+
' DEADLINE: <2021-08-18 Wed>',
155+
'* TODO Another todo',
156+
}
157+
parsed = parser.parse(lines, 'work')
158+
headline = parsed:get_item(1)
159+
api.nvim_call_function.returns(' DEADLINE: <2021-08-18 Wed>')
160+
result = headline:add_deadline_date(deadline_date:add({ day = 2 }))
161+
assert.stub(api.nvim_call_function).was.called_with('setline', {
162+
2,
163+
' DEADLINE: <2021-08-20 Fri>',
164+
})
165+
mock.revert(api)
166+
end)
167+
168+
it('should add and update scheduled date', function()
169+
local scheduled_date = Date.from_string('2021-08-18 Wed')
170+
local lines = {
171+
'* TODO Test orgmode :WORK:',
172+
'* TODO Another todo',
173+
}
174+
local parsed = parser.parse(lines, 'work')
175+
local api = mock(vim.api, true)
176+
api.nvim_call_function.returns(true)
177+
local headline = parsed:get_item(1)
178+
local result = headline:add_scheduled_date(scheduled_date)
179+
assert.are.same(true, result)
180+
assert.stub(api.nvim_call_function).was.called_with('append', {
181+
1,
182+
' SCHEDULED: <2021-08-18 Wed>',
183+
})
184+
185+
lines = {
186+
'* TODO Test orgmode :WORK:',
187+
' DEADLINE: <2021-08-18 Wed>',
188+
'* TODO Another todo',
189+
}
190+
parsed = parser.parse(lines, 'work')
191+
headline = parsed:get_item(1)
192+
result = headline:add_scheduled_date(scheduled_date:add({ day = 2 }))
193+
assert.stub(api.nvim_call_function).was.called_with('setline', {
194+
2,
195+
' DEADLINE: <2021-08-18 Wed> SCHEDULED: <2021-08-20 Fri>',
196+
})
197+
198+
lines = {
199+
'* TODO Test orgmode :WORK:',
200+
' DEADLINE: <2021-08-18 Wed> SCHEDULED: <2021-08-18 Wed>',
201+
'* TODO Another todo',
202+
}
203+
parsed = parser.parse(lines, 'work')
204+
headline = parsed:get_item(1)
205+
api.nvim_call_function.returns(' DEADLINE: <2021-08-18 Wed> SCHEDULED: <2021-08-18 Wed>')
206+
result = headline:add_scheduled_date(scheduled_date:add({ day = 4 }))
207+
assert.stub(api.nvim_call_function).was.called_with('setline', {
208+
2,
209+
' DEADLINE: <2021-08-18 Wed> SCHEDULED: <2021-08-22 Sun>',
210+
})
211+
mock.revert(api)
212+
end)
135213
end)

0 commit comments

Comments
 (0)