]>
Commit | Line | Data |
---|---|---|
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 |