------------------------------------------------------------------------------- -- Tools to deal with notes ------------------------------------------------------------------------------- local Notes = {} local Util = require('util') local Configuration = require('configuration') local api = vim.api ------------------------------------------------------------------------------- -- Internal Functions ------------------------------------------------------------------------------- local templates = { daily = '%Y-%m-%d', weekly = '%Y-w%V', monthly = '%Y-%m', yearly = '%Y' } local patterns = { daily = '^(%d+)-(%d+)-(%d+).md$', weekly = '^(%d+)-w(%d+).md$', monthly = '^(%d+)-(%d+).md$', seasonal = '^(%d+)-s(%d+).md$', yearly = '^(%d+).md$' } local function open_or_create_from_template(type, file_path) local journal_file = io.open(file_path, 'r') if not journal_file then local template_contents = Configuration.load_template(type) journal_file = io.open(file_path, 'w') journal_file:write(template_contents) journal_file:close() end vim.cmd('edit ' .. file_path) end local function open_periodic_note(type, filename) local file_directory_path = Configuration.path_for(Configuration.configuration.periodic_locations[type]) Util.ensure_directory_exists(file_directory_path) local file_path = Util.join(file_directory_path, filename) open_or_create_from_template(type, file_path) end local function find_next(type, filename, go_back_in_time) local multiplier = 1 if go_back_in_time then multiplier = -1 end local pattern = patterns[type] local year, second_match, day = filename:match(pattern) year = tonumber(year) -- Depending on context, second_match is either month, week, or season. if second_match then second_match = tonumber(second_match) end if day then day = tonumber(day) end local next = nil if type == 'daily' and year and second_match and day then next = os.date(templates.daily, os.time({ year = year, month = second_match, day = day }) + multiplier * 24 * 60 * 60) elseif type == 'weekly' and year and second_match then -- According to ISO, the first week is the one that contains january 4 local first_day_of_the_week = os.time({year = year, month = 1, day = 4}) if multiplier < 0 then second_match = second_match - 2 end next = os.date(templates.weekly, first_day_of_the_week + second_match * 7 * 24 * 60 * 60) elseif type == 'monthly' and year and second_match then next = os.date(templates.monthly, os.time({ year = year, month = second_match + multiplier, day = 1 })) elseif type == 'seasonal' and year and second_match then if multiplier > 0 and second_match == 4 then year = year + 1 end if multiplier < 0 and second_match == 1 then year = year - 1 end if multiplier > 0 then second_match = (second_match % 4) + 1 else second_match = (second_match + 2) % 4 + 1 end next = year .. '-s' .. second_match elseif type == 'yearly' and year then next = os.date(templates.yearly, os.time({ year = year + multiplier, month = 1, day = 1 })) end if next then Notes['open_' .. type](next) end end ------------------------------------------------------------------------------- -- Public Interface ------------------------------------------------------------------------------- --- Opens the daily note function Notes.open_daily(date) if not date or date == '' then date = os.date(templates.daily) end local filename = date .. '.md' open_periodic_note('daily', filename) end --- Opens the weekly note function Notes.open_weekly(date) if not date or date == '' then date = os.date(templates.weekly) end local filename = date .. '.md' open_periodic_note('weekly', filename) end --- Opens the monthly note function Notes.open_monthly(date) if not date or date == '' then date = os.date(templates.monthly) end local filename = date .. '.md' open_periodic_note('monthly', filename) end --- Opens the seasonal note function Notes.open_seasonal(date) if not date or date == '' then local year = os.date('%Y') local month = tonumber(os.date('%m')) local season = math.ceil(month / 3) date = year .. '-s' .. season end local filename = date .. '.md' open_periodic_note('seasonal', filename) end --- Opens the yearly note function Notes.open_yearly(date) if not date or date == '' then date = os.date(templates.yearly) end local filename = date .. '.md' open_periodic_note('yearly', filename) end --- Opens the next note function Notes.open_next() local filename = vim.fn.expand('%:t') for type, pattern in pairs(patterns) do if filename:match(pattern) ~= nil then return find_next(type, filename) end end api.nvim_err_writeln('Opening next only works on periodic notes.') end --- Opens the previous note function Notes.open_previous() local filename = vim.fn.expand('%:t') for type, pattern in pairs(patterns) do if filename:match(pattern) ~= nil then return find_next(type, filename, true) end end api.nvim_err_writeln('Opening previous only works on periodic notes.') end --- Opens an arbitrary note function Notes.open() local success, module = pcall(require, 'fzf-lua') if success then local notes_path = Configuration.path_for() Util.ensure_directory_exists(notes_path) module.files({ cwd = notes_path }) else api.nvim_err_writeln('This feature requires optional dependency fzf-lua') end end return Notes