Skip to content

Commit

Permalink
fix(calendar): fix bug with empty date (#744)
Browse files Browse the repository at this point in the history
- simplify state by replacing month with date
- handle rendering correctly when changing month

Co-authored-by: Sebastian Flügge <[email protected]>
  • Loading branch information
seflue and seflue authored Jun 9, 2024
1 parent c9bf6d8 commit 3e4dbeb
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 23 deletions.
45 changes: 22 additions & 23 deletions lua/orgmode/objects/calendar.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ local small_minute_step = config.calendar.min_small_step or config.org_time_stam
---@field callback fun(date: OrgDate | nil, cleared?: boolean)
---@field namespace function
---@field date OrgDate?
---@field month OrgDate
---@field title? string
---@field on_day? OrgCalendarOnRenderDay
---@field selected OrgDate?
Expand All @@ -26,8 +25,7 @@ local small_minute_step = config.calendar.min_small_step or config.org_time_stam
local Calendar = {
win = nil,
buf = nil,
date = nil,
month = Date.today():start_of('month'),
date = Date.today():start_of('month'),
selected = nil,
select_state = SelState.DAY,
clearable = false,
Expand All @@ -46,9 +44,8 @@ function Calendar.new(data)
this.on_day = data.on_day
if data.date then
this.date = data.date
this.month = this.date:set({ day = 1 })
else
this.month = Date.today():start_of('month')
this.date = Date.today()
end
return this
end
Expand Down Expand Up @@ -157,24 +154,28 @@ function Calendar:render()
end

-- construct title (Month YYYY)
local title = self.month:format('%B %Y')
local title = self.date:format('%B %Y')
title = string.rep(' ', math.floor((width - title:len()) / 2)) .. title

-- insert whitespace before first day of month
local start_weekday = self.month:get_isoweekday()
local first_of_month = self.date:start_of('month')

local end_of_month = self.date:end_of('month')
local start_weekday = first_of_month:get_isoweekday()
if start_from_sunday then
start_weekday = self.month:get_weekday()
start_weekday = first_of_month:get_weekday()
end

while start_weekday > 1 do
table.insert(cal_rows[1], ' ')
start_weekday = start_weekday - 1
end

-- insert dates into cal_rows
local dates = self.month:get_range_until(self.month:end_of('month'))
local dates = first_of_month:get_range_until(end_of_month)
local current_row = 1
for _, date in ipairs(dates) do
table.insert(cal_rows[current_row], date:format('%d'))
for _, day in ipairs(dates) do
table.insert(cal_rows[current_row], day:format('%d'))
if #cal_rows[current_row] % 7 == 0 then
current_row = current_row + 1
end
Expand Down Expand Up @@ -236,8 +237,8 @@ function Calendar:render()
break
end
if from and to then
local date = self.month:set({ day = num })
self:on_render_day(date, {
local day = self.date:set({ day = num })
self:on_render_day(day, {
from = from,
to = to,
line = i,
Expand Down Expand Up @@ -322,7 +323,7 @@ end

function Calendar:forward()
self:_ensure_day()
self.month = self.month:add({ month = vim.v.count1 })
self.date = self.date:start_of('month'):add({ month = vim.v.count1 })
self:render()
vim.fn.cursor(2, 1)
vim.fn.search('01')
Expand All @@ -331,9 +332,9 @@ end

function Calendar:backward()
self:_ensure_day()
self.month = self.month:subtract({ month = vim.v.count1 })
self.date = self.date:start_of('month'):subtract({ month = vim.v.count1 }):end_of('month')
self:render()
vim.fn.cursor(vim.fn.line('$'), 0)
vim.fn.cursor(8, 0)
vim.fn.search([[\d\d]], 'b')
self:render()
end
Expand Down Expand Up @@ -499,13 +500,13 @@ end

function Calendar:reset()
self:_ensure_day()
local today = self.month:set_todays_date()
self.month = today:set({ day = 1 })
self.date = self.date:set_todays_date()
self:render()
vim.fn.cursor(2, 1)
vim.fn.search(today:format('%d'), 'W')
vim.fn.search(self.date:format('%d'), 'W')
end

---@return OrgDate?
function Calendar:get_selected_date()
if self.select_state ~= SelState.DAY then
return self.date
Expand All @@ -519,7 +520,6 @@ function Calendar:get_selected_date()
return utils.echo_warning('Please select valid day number.', nil, false)
end
return self.date:set({
month = self.month.month,
day = day,
date_only = self.date.date_only,
})
Expand Down Expand Up @@ -549,7 +549,7 @@ end
function Calendar:dispose()
self.win = nil
self.buf = nil
self.month = Date.today():start_of('month')
self.date = Date.today():start_of('month')
if self.callback then
self.callback(nil)
self.callback = nil
Expand All @@ -566,7 +566,7 @@ end

function Calendar:read_date()
self:_ensure_day()
local current_date = self:get_selected_date()
local current_date = self:get_selected_date() or Date.today()
vim.ui.input({ prompt = 'Enter date: ', default = current_date:to_string() }, function(result)
if result then
local date = Date.from_string(result)
Expand All @@ -575,7 +575,6 @@ function Calendar:read_date()
end

self.date = date
self.month = date:set({ day = 1 })
self:render()
vim.fn.cursor(2, 1)
vim.fn.search(date:format('%d'), 'W')
Expand Down
1 change: 1 addition & 0 deletions lua/orgmode/objects/date.lua
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@ function Date:is_in_date_range(date)
return false
end

---Range of dates, excluding date
---@param date OrgDate
---@return OrgDate[]
function Date:get_range_until(date)
Expand Down
2 changes: 2 additions & 0 deletions lua/orgmode/utils/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ end
---@param msg string|table
---@param additional_msg? table
---@param store_in_history? boolean
---@return nil
function utils.echo_warning(msg, additional_msg, store_in_history)
return utils._echo(msg, 'WarningMsg', additional_msg, store_in_history)
end
Expand All @@ -127,6 +128,7 @@ function utils.echo_info(msg, additional_msg, store_in_history)
end

---@private
---@return nil
function utils._echo(msg, hl, additional_msg, store_in_history)
vim.cmd([[redraw!]])
if type(msg) == 'table' then
Expand Down
83 changes: 83 additions & 0 deletions tests/plenary/object/calendar_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
local helpers = require('tests.plenary.helpers')
local Calendar = require('orgmode.objects.calendar')
local Date = require('orgmode.objects.date')

local close_all_buffers = function()
vim.cmd([[silent! %bw!]])
end

local feed = function(keys)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, true, true), 'x', true)
end

describe('Calendar', function()
after_each(close_all_buffers)
it('should open a calendar at plain headline', function()
helpers.create_agenda_file({
'#+TITLE: Test',
'',
'* Some Headline',
})
vim.fn.cursor(3, 1)
vim.cmd('norm ,oid')
end)

it('should change a deadline', function()
helpers.create_agenda_file({
'#+TITLE: Test',
'',
'* Some Headline',
' DEADLINE: <2024-06-01 Sat>',
})
vim.fn.cursor(3, 1)
vim.cmd('norm ,oid')
feed('l<CR>')

assert.are.same({
'* Some Headline',
' DEADLINE: <2024-06-02 Sun>',
}, vim.api.nvim_buf_get_lines(0, 2, 4, false))
end)

it('should render the month correctly', function()
helpers.create_agenda_file({
'#+TITLE: Test',
'',
'* Some Headline',
' DEADLINE: <2024-05-31 Fri>',
})
vim.fn.cursor(3, 1)
vim.cmd('norm ,oid')

assert.are.same({
' Mon Tue Wed Thu Fri Sat Sun',
' 01 02 03 04 05 ',
' 06 07 08 09 10 11 12 ',
' 13 14 15 16 17 18 19 ',
' 20 21 22 23 24 25 26 ',
' 27 28 29 30 31 ',
' ',
' --:-- ',
'',
}, vim.api.nvim_buf_get_lines(0, 1, 10, false))
end)

it('should render at the end of a long month', function()
local date = Date.from_string('2024-05-31')
local cal_instance = Calendar.new({ date = date, title = date:to_string() })
cal_instance:open()
local cal_date = cal_instance.date
assert.are.same('2024-05-31 Fri', cal_date and cal_date:to_string())
end)

it('should handle the end of the month correctly', function()
local date = Date.from_string('2024-05-31')
local cal_instance = Calendar.new({ date = date, title = date:to_string() })
cal_instance:open()
feed('l')
vim.wait(50)
feed('<CR>')
local cal_date = cal_instance.date
assert.are.same('2024-06-01 Sat', cal_date and cal_date:to_string())
end)
end)
10 changes: 10 additions & 0 deletions tests/plenary/object/date_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@ describe('Date object', function()
assert.are.same('2024-01-03 Wed 14:00', date:to_string())
end)

it('should be robust for some arithmetic', function()
local date = Date.from_string('2024-06-04')
date = date:start_of('month')
assert.are.same('2024-06-01 Sat', date:to_string())
date = date:subtract({ month = 1 })
assert.are.same('2024-05-01 Wed', date:to_string())
date = date:end_of('month')
assert.are.same('2024-05-31 Fri', date:to_string())
end)

it('should compare dates', function()
local date = Date.from_string('2021-05-12 14:00')
local date_end = Date.from_string('2021-05-12 23:30')
Expand Down

0 comments on commit 3e4dbeb

Please sign in to comment.