]> git.r.bdr.sh - rbdr/nota.nvim/blob - lua/notes.lua
ca61735ab7fefec17ae625c6fd8fd641b82c120d
[rbdr/nota.nvim] / lua / notes.lua
1 -------------------------------------------------------------------------------
2 -- Tools to deal with notes
3 -------------------------------------------------------------------------------
4 local Notes = {}
5
6 local Util = require('util')
7 local Configuration = require('configuration')
8 local api = vim.api
9 -------------------------------------------------------------------------------
10 -- Internal Functions
11 -------------------------------------------------------------------------------
12 local templates = {
13 daily = '%Y-%m-%d',
14 weekly = '%Y-w%V',
15 monthly = '%Y-%m',
16 yearly = '%Y'
17 }
18
19 local patterns = {
20 daily = '^(%d+)-(%d+)-(%d+).md$',
21 weekly = '^(%d+)-w(%d+).md$',
22 monthly = '^(%d+)-(%d+).md$',
23 seasonal = '^(%d+)-s(%d+).md$',
24 yearly = '^(%d+).md$'
25 }
26
27 local function open_or_create_from_template(type, file_path)
28
29 local journal_file = io.open(file_path, 'r')
30 if not journal_file then
31 local template_contents = Configuration.load_template(type)
32
33 journal_file = io.open(file_path, 'w')
34 journal_file:write(template_contents)
35 journal_file:close()
36 end
37 vim.cmd('edit ' .. file_path)
38 end
39
40 local function open_periodic_note(type, filename)
41 local file_directory_path = Configuration.path_for(Configuration.configuration.periodic_locations[type])
42
43 Util.ensure_directory_exists(file_directory_path)
44 local file_path = Util.join(file_directory_path, filename)
45
46 open_or_create_from_template(type, file_path)
47 end
48
49 local function find_next(type, filename, go_back_in_time)
50 local multiplier = 1
51 if go_back_in_time then
52 multiplier = -1
53 end
54
55 local pattern = patterns[type]
56 local year, second_match, day = filename:match(pattern)
57 year = tonumber(year)
58
59 -- Depending on context, second_match is either month, week, or season.
60 if second_match then
61 second_match = tonumber(second_match)
62 end
63
64 if day then
65 day = tonumber(day)
66 end
67
68 local next = nil
69
70 if type == 'daily' and year and second_match and day then
71 next = os.date(templates.daily, os.time({
72 year = year,
73 month = second_match,
74 day = day
75 }) + multiplier * 24 * 60 * 60)
76 elseif type == 'weekly' and year and second_match then
77 -- According to ISO, the first week is the one that contains january 4
78 local first_day_of_the_week = os.time({year = year, month = 1, day = 4})
79 if multiplier < 0 then
80 second_match = second_match - 2
81 end
82 next = os.date(templates.weekly, first_day_of_the_week + second_match * 7 * 24 * 60 * 60)
83 elseif type == 'monthly' and year and second_match then
84 next = os.date(templates.monthly, os.time({
85 year = year,
86 month = second_match + multiplier,
87 day = 1
88 }))
89 elseif type == 'seasonal' and year and second_match then
90 if multiplier > 0 and second_match == 4 then
91 year = year + 1
92 end
93 if multiplier < 0 and second_match == 1 then
94 year = year - 1
95 end
96 if multiplier > 0 then
97 second_match = (second_match % 4) + 1
98 else
99 second_match = (second_match + 2) % 4 + 1
100 end
101 next = year .. '-s' .. second_match
102 elseif type == 'yearly' and year then
103 next = os.date(templates.yearly, os.time({
104 year = year + multiplier,
105 month = 1,
106 day = 1
107 }))
108 end
109
110 if next then
111 Notes['open_' .. type](next)
112 end
113 end
114
115 -------------------------------------------------------------------------------
116 -- Public Interface
117 -------------------------------------------------------------------------------
118
119 --- Opens the daily note
120 function Notes.open_daily(date)
121 if not date or date == '' then
122 date = os.date(templates.daily)
123 end
124 local filename = date .. '.md'
125 open_periodic_note('daily', filename)
126 end
127
128 --- Opens the weekly note
129 function Notes.open_weekly(date)
130 if not date or date == '' then
131 date = os.date(templates.weekly)
132 end
133 local filename = date .. '.md'
134 open_periodic_note('weekly', filename)
135 end
136
137 --- Opens the monthly note
138 function Notes.open_monthly(date)
139 if not date or date == '' then
140 date = os.date(templates.monthly)
141 end
142 local filename = date .. '.md'
143 open_periodic_note('monthly', filename)
144 end
145
146 --- Opens the seasonal note
147 function Notes.open_seasonal(date)
148 if not date or date == '' then
149 local year = os.date('%Y')
150 local month = tonumber(os.date('%m'))
151 local season = math.ceil(month / 3)
152 date = year .. '-s' .. season
153 end
154
155 local filename = date .. '.md'
156 open_periodic_note('seasonal', filename)
157 end
158
159 --- Opens the yearly note
160 function Notes.open_yearly(date)
161 if not date or date == '' then
162 date = os.date(templates.yearly)
163 end
164 local filename = date .. '.md'
165 open_periodic_note('yearly', filename)
166 end
167
168 --- Opens the next note
169 function Notes.open_next()
170 local filename = vim.fn.expand('%:t')
171 for type, pattern in pairs(patterns) do
172 if filename:match(pattern) ~= nil then
173 return find_next(type, filename)
174 end
175 end
176 api.nvim_err_writeln('Opening next only works on periodic notes.')
177 end
178
179 --- Opens the previous note
180 function Notes.open_previous()
181 local filename = vim.fn.expand('%:t')
182 for type, pattern in pairs(patterns) do
183 if filename:match(pattern) ~= nil then
184 return find_next(type, filename, true)
185 end
186 end
187 api.nvim_err_writeln('Opening previous only works on periodic notes.')
188 end
189
190 --- Opens an arbitrary note
191 function Notes.open()
192 local success, module = pcall(require, 'fzf-lua')
193 if success then
194 local notes_path = Configuration.path_for()
195 Util.ensure_directory_exists(notes_path)
196 module.files({ cwd = notes_path })
197 else
198 api.nvim_err_writeln('This feature requires optional dependency fzf-lua')
199 end
200 end
201
202 return Notes