From d0baf31181435c2378dc2c19a57dcebcc36d9464 Mon Sep 17 00:00:00 2001 From: Kristijan Husak Date: Mon, 19 Aug 2024 10:57:40 +0200 Subject: [PATCH] fix(dates): Fix parsing dates from headlines Fixes #796 #797 --- lua/orgmode/files/headline.lua | 7 +++--- lua/orgmode/utils/treesitter/init.lua | 11 ++++++++++ tests/plenary/files/headline_spec.lua | 31 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/lua/orgmode/files/headline.lua b/lua/orgmode/files/headline.lua index 659b5fc2e..cf86dd2c4 100644 --- a/lua/orgmode/files/headline.lua +++ b/lua/orgmode/files/headline.lua @@ -665,9 +665,10 @@ end memoize('get_non_plan_dates') ---@return OrgDate[] function Headline:get_non_plan_dates() - local section = self:node():parent() + local headline_node = self:node() + local section = headline_node:parent() local body = section and section:field('body')[1] - local headline_text = self.file:get_node_text(self:_get_child_node('item')) or '' + local headline_text = self.file:get_node_text(headline_node) or '' local dates = Date.parse_all_from_line(headline_text, self:node():start() + 1) local properties_node = section and section:field('property_drawer')[1] @@ -684,7 +685,7 @@ function Headline:get_non_plan_dates() end local start_line = body:range() - local lines = self.file:get_node_text_list(body) + local lines = self.file:get_node_text_list(body, ts_utils.range_with_zero_start_col(body)) for i, line in ipairs(lines) do local line_dates = Date.parse_all_from_line(line, start_line + i) local is_clock_line = line:match('^%s*:?CLOCK:') ~= nil diff --git a/lua/orgmode/utils/treesitter/init.lua b/lua/orgmode/utils/treesitter/init.lua index fbab1822a..9526151b3 100644 --- a/lua/orgmode/utils/treesitter/init.lua +++ b/lua/orgmode/utils/treesitter/init.lua @@ -121,6 +121,17 @@ function M.node_to_lsp_range(node) return rtn end +---Return the range of the given node, but override the start column to be 0. +---This is needed when we want to parse the lines manually to ensure that +---we parse from the start of the line +---@param node TSNode +---@return number[] +function M.range_with_zero_start_col(node) + local range = { node:range() } + range[2] = 0 + return range +end + -- Memoizes a function based on the buffer tick of the provided bufnr. -- The cache entry is cleared when the buffer is detached to avoid memory leaks. -- The options argument is a table with one optional value: diff --git a/tests/plenary/files/headline_spec.lua b/tests/plenary/files/headline_spec.lua index 3bf5d8650..f0bd1ab6a 100644 --- a/tests/plenary/files/headline_spec.lua +++ b/tests/plenary/files/headline_spec.lua @@ -53,4 +53,35 @@ describe('Headline', function() assert.are.same('headline_2_category', file:get_headlines()[2]:get_category()) end) end) + + describe('get_all_dates', function() + it('should properly parse dates from the headline and body', function() + local file = helpers.create_file({ + '* TODO testing <2024-08-17 Sat>', + 'DEADLINE: <2024-08-19 Mon>', + '* stuff <2024-01-17 Wed>', + ' <2024-02-17 Sat>', + ' <2024-03-18 Sun>', + }) + + local assert_range = function(date, expected_range) + assert.are.same(expected_range[1], date.range.start_line, 'Start line is not matching') + assert.are.same(expected_range[2], date.range.start_col, 'Start col is not matching') + assert.are.same(expected_range[3], date.range.end_line, 'End line is not matching') + assert.are.same(expected_range[4], date.range.end_col, 'End col is not matching') + end + + local first_headline_dates = file:get_headlines()[1]:get_all_dates() + assert.are.same(2, #first_headline_dates) + assert_range(first_headline_dates[1], { 2, 11, 2, 26 }) + assert_range(first_headline_dates[2], { 1, 16, 1, 31 }) + + local second_headline_dates = file:get_headlines()[2]:get_all_dates() + assert.are.same(3, #second_headline_dates) + -- First date in the list is is always a plan date + assert_range(second_headline_dates[1], { 4, 3, 4, 18 }) + assert_range(second_headline_dates[2], { 3, 9, 3, 24 }) + assert_range(second_headline_dates[3], { 5, 3, 5, 18 }) + end) + end) end)