+41
examples/.nvimrc
+41
examples/.nvimrc
···
1
+
" insert fancy signifiers with abbrevs
2
+
iabbrev todo ·
3
+
iabbrev done ×
4
+
5
+
" select the task list and hit `gq` to sort and group by status
6
+
set formatprg=sort\ -V
7
+
8
+
" syntax highlighting
9
+
augroup JournalSyntax
10
+
autocmd!
11
+
autocmd BufReadPost * set filetype=journal
12
+
13
+
autocmd BufReadPost * syntax match JournalAll /.*/ " captures the entire buffer
14
+
autocmd BufReadPost * syntax match JournalDone /^×.*/ " lines containing 'done' items: ×
15
+
autocmd BufReadPost * syntax match JournalTodo /^·.*/ " lines containing 'todo' items: ·
16
+
autocmd BufReadPost * syntax match JournalEvent /^o.*/ " lines containing 'event' items: o
17
+
autocmd BufReadPost * syntax match JournalNote /^- .*/ " lines containing 'note' items: -
18
+
autocmd BufReadPost * syntax match JournalMoved /^>.*/ " lines containing 'moved' items: >
19
+
autocmd BufReadPost * syntax match JournalHeader /^\<\u\+\>.*/ " lines starting with caps
20
+
21
+
autocmd BufReadPost * highlight JournalAll ctermfg=12
22
+
autocmd BufReadPost * highlight JournalHeader ctermfg=12
23
+
autocmd BufReadPost * highlight JournalDone ctermfg=12
24
+
autocmd BufReadPost * highlight JournalEvent ctermfg=6 " cyan
25
+
autocmd BufReadPost * highlight JournalMoved ctermfg=5 " pink
26
+
autocmd BufReadPost * highlight JournalNote ctermfg=3 " yellow
27
+
autocmd BufReadPost * highlight VertSplit ctermfg=0 ctermbg=0 " hide vert splits
28
+
augroup END
29
+
30
+
augroup JournalHideUIElements
31
+
autocmd!
32
+
" hide junk
33
+
autocmd VimEnter * set laststatus=0
34
+
autocmd VimEnter * set noruler nonumber nocursorline nocursorcolumn norelativenumber
35
+
36
+
" pin scrolling
37
+
autocmd VimEnter * set scrollbind
38
+
39
+
augroup END
40
+
41
+
syntax on
examples/06
examples/2025/06
examples/06
examples/2025/06
examples/07
examples/2025/07
examples/07
examples/2025/07
examples/08
examples/2025/08
examples/08
examples/2025/08
examples/09
examples/2025/09
examples/09
examples/2025/09
examples/10
examples/2025/10
examples/10
examples/2025/10
+353
readme.md
+353
readme.md
···
1
+
I cobbled together a journaling system with {neo,}vim,
2
+
coreutils and [dateutils](http://www.fresse.org/dateutils).
3
+
This system is loosely based on [Ryder
4
+
Caroll's](https://www.rydercarroll.com/) Bullet Journal
5
+
method.
6
+
7
+
[](https://u.peppe.rs/SpF.png)
8
+
9
+
### The format
10
+
11
+
The journal for a given year is a directory:
12
+
13
+
```bash
14
+
λ ls journal/
15
+
2022/ 2023/
16
+
```
17
+
18
+
In each directory are 12 files, one for each month of the
19
+
year, numbered like so:
20
+
21
+
```bash
22
+
λ ls journal/2023/
23
+
01 02 03 04 05 06 07 08 09 10 11 12
24
+
```
25
+
26
+
We can now begin writing stuff down:
27
+
28
+
```bash
29
+
λ vim journal/2023/1
30
+
```
31
+
32
+
Every month must start with a calendar of course, fill that
33
+
in with:
34
+
35
+
```vim
36
+
:read !cal -m
37
+
```
38
+
39
+
Your entry for January might look like this:
40
+
41
+
```bash
42
+
λ cat journal/2023/01
43
+
January 2023
44
+
Mo Tu We Th Fr Sa Su
45
+
1
46
+
2 3 4 5 6 7 8
47
+
9 10 11 12 13 14 15
48
+
16 17 18 19 20 21 22
49
+
23 24 25 26 27 28 29
50
+
30 31
51
+
```
52
+
53
+
I prefer planning week by week, as opposed to creating a
54
+
task-list every day, here's what I have for the first couple
55
+
of weeks:
56
+
57
+
```
58
+
January 2023
59
+
Mo Tu We Th Fr Sa Su
60
+
1
61
+
2 3 4 5 6 7 8
62
+
9 10 11 12 13 14 15
63
+
16 17 18 19 20 21 22
64
+
23 24 25 26 27 28 29
65
+
30 31
66
+
67
+
68
+
week 1
69
+
70
+
done apply leaves
71
+
done dload boarding pass
72
+
moved reply to dan
73
+
74
+
75
+
week 2
76
+
77
+
todo reply to dan
78
+
todo pack bags
79
+
done travel insurance
80
+
todo weigh luggage
81
+
```
82
+
83
+
I start the week by writing a header and each item that week
84
+
is placed on its own line. The items are prefixed with a
85
+
`todo` or a `done` signifier.
86
+
87
+
88
+
### Form over function
89
+
90
+
Right off the bat, the signifiers look very noisy, Even more
91
+
so once we start introducing variety (I use "event", "note"
92
+
and "moved"):
93
+
94
+
```
95
+
week 1
96
+
97
+
todo apply leaves
98
+
done dload boarding pass
99
+
todo reply to dan
100
+
event fr trip
101
+
note weight 68.6
102
+
```
103
+
104
+
We can clean this up with "abbreviations" (`:h abbreviations`):
105
+
106
+
```vim
107
+
:iabbrev todo ·
108
+
:iabbrev done ×
109
+
```
110
+
111
+
Now, typing this:
112
+
113
+
```
114
+
todo apply leaves
115
+
```
116
+
117
+
Automatically inserts:
118
+
119
+
```
120
+
· apply leaves
121
+
```
122
+
123
+
You can use `x` and `o` as well, but `×` (U+00D7,
124
+
MULTIPLICATION SIGN) and `·` (U+00B7, MIDDLE DOT) are more
125
+
... *gourmet*.
126
+
127
+
The other signifiers I use are:
128
+
129
+
- `-` for note
130
+
- `o` for event
131
+
- `>` for moved.
132
+
133
+
Nit #2 is the lack of order. We can employ vim to introduce
134
+
grouping and sorting. Select the list of entries for this
135
+
week:
136
+
137
+
```vim
138
+
vip " line-wise select inner paragraph
139
+
:'<,'>sort " the markers '< and '> are automatically inserted,
140
+
" they mark the start and end of the selection
141
+
```
142
+
143
+
We end up with:
144
+
145
+
```
146
+
week 1
147
+
148
+
· apply leaves
149
+
· reply to dan
150
+
× dload boarding pass
151
+
```
152
+
153
+
The lines are grouped by their signifiers, segregating todo
154
+
items from completed items. Luckily, MIDDLE DOT is lesser
155
+
than MULTIPLICATION SIGN, so todo items are placed at the
156
+
top. The same goes for `o` and `x` symbols, either set of
157
+
signifiers will result in the same sorting order.
158
+
159
+
We can shorten this select-paragraph-invoke-sort dance by
160
+
setting the `formatprg` variable:
161
+
162
+
```vim
163
+
:set formatprg=sort\ -V
164
+
```
165
+
166
+
Now, hitting `gqip` should automatically group and sort the
167
+
items for the week under the cursor, moving todo items to
168
+
the top. Finding signifier glyphs that suit your sorting
169
+
preference is a fun exercise.
170
+
171
+
### Syntax highlighting
172
+
173
+
Adding color to items introduces another layer of visual
174
+
distinction. In truth, I like to deck it out just because.
175
+
176
+
First, create a few syntax groups:
177
+
178
+
```vim
179
+
:syntax match JournalAll /.*/ " captures the entire buffer
180
+
:syntax match JournalDone /^×.*/ " lines containing 'done' items: ×
181
+
:syntax match JournalTodo /^·.*/ " lines containing 'todo' items: ·
182
+
:syntax match JournalEvent /^o.*/ " lines containing 'event' items: o
183
+
:syntax match JournalNote /^- .*/ " lines containing 'note' items: -
184
+
:syntax match JournalMoved /^>.*/ " lines containing 'moved' items: >
185
+
```
186
+
187
+
Add highlights to each group:
188
+
189
+
```vim
190
+
:highlight JournalAll ctermfg=12 " bright black
191
+
:highlight JournalDone ctermfg=12 " bright black
192
+
:highlight JournalEvent ctermfg=6 " cyan
193
+
:highlight JournalMoved ctermfg=5 " magenta
194
+
:highlight JournalNote ctermfg=3 " yellow
195
+
```
196
+
197
+
In my terminal, this is rendered like so:
198
+
199
+
[](https://u.peppe.rs/Du6.png)
200
+
201
+
### Habit tracking
202
+
203
+
While this is not a part of my journaling system anymore, a
204
+
few headers and an awk script is all it takes to track
205
+
habits. My weekly entries would include a couple of habit
206
+
headers like so:
207
+
208
+
```
209
+
week 1 --------------
210
+
211
+
× wake up on time
212
+
× water the plants
213
+
214
+
spend 7.5 7 10
215
+
---------------------
216
+
217
+
218
+
week 2 --------------
219
+
220
+
· make the bed
221
+
· go to bed
222
+
223
+
spend 30 2.75 6
224
+
---------------------
225
+
```
226
+
227
+
Here, under the `spend` header in week 1, are a list of
228
+
expenditures accumulated over the week. The monthly spend is
229
+
calculated with this awk script:
230
+
231
+
```awk
232
+
BEGIN {spend=0;}
233
+
/spend/ {for(i=1;i<=$NF;i++) spend+=$i;}
234
+
END { printf spend "eur"}
235
+
```
236
+
237
+
And invoked like so:
238
+
239
+
```
240
+
λ awk -f spend.awk journal/2023/01
241
+
63.25eur
242
+
```
243
+
244
+
### Reflection
245
+
246
+
Journaling is not just about planning what is to come, but
247
+
also reflecting on what has passed. It would make sense to
248
+
simultaneously look at the past few weeks' entries while
249
+
making your current one. To open multiple months of entries
250
+
at the same time:
251
+
252
+
```
253
+
λ vim -O journal/2023/0{1,2,3}
254
+
```
255
+
256
+
Opens 3 months, side-by-side, in vertical splits:
257
+
258
+
```
259
+
JANUARY ------------ │ FEBRUARY ----------- │ MARCH --------------
260
+
│ │
261
+
Mo Tu We Th Fr Sa Su │ Mo Tu We Th Fr Sa Su │ Mo Tu We Th Fr Sa Su
262
+
1 │ 1 2 3 4 5 │ 1 2 3 4 5
263
+
2 3 4 5 6 7 8 │ 6 7 8 9 10 11 12 │ 6 7 8 9 10 11 12
264
+
9 10 11 12 13 14 15 │ 13 14 15 16 17 18 19 │ 13 14 15 16 17 18 19
265
+
16 17 18 19 20 21 22 │ 20 21 22 23 24 25 26 │ 20 21 22 23 24 25 26
266
+
23 24 25 26 27 28 29 │ 27 28 │ 27 28 29 30 31
267
+
30 31 │ │
268
+
│ │
269
+
│ │
270
+
WEEK 1 ------------- │ WEEK 1 ------------- │ WEEK 1 -------------
271
+
│ │
272
+
> latex setup │ > forex │ - weight: 64
273
+
× make the bed │ × clean shoes │ > close sg-pr
274
+
× 03: dentist │ × buy clothes │ × facewash
275
+
× integrate tsg │ × draw │ × groceries
276
+
│ │
277
+
│ │
278
+
WEEK 2 ------------- │ WEEK 2 ------------- │ WEEK 2 -------------
279
+
│ │
280
+
× latex setup │ - viral fever │ > close sg-pr
281
+
× send invoice │ × forex │ × plan meet
282
+
× stack-graph pr │ × activate sim │ × sg storage
283
+
│ × bitlbee │
284
+
```
285
+
286
+
### Reducing friction
287
+
288
+
Journaling already requires a solid amount of discipline and
289
+
consistency. The added friction of typing `vim
290
+
journal/$CURRENT_YEAR/$CURRENT_MONTH` each time is doing no
291
+
favors.
292
+
293
+
To open the current month based on system time:
294
+
295
+
```bash
296
+
λ vim $(date +"%Y/%m")
297
+
```
298
+
299
+
To open all the months within a 2 month window of today, is
300
+
a little trickier. The command we wish to generate is (if
301
+
today is 2023/12):
302
+
303
+
```bash
304
+
λ vim -O 2023/10 2023/11 2023/12 2024/01 2024/02
305
+
```
306
+
307
+
And that is where `dateseq` from
308
+
[dateutils](http://www.fresse.org/dateutils) comes in handy,
309
+
for example:
310
+
311
+
```bash
312
+
λ dateseq 2012-02-01 2012-03-01
313
+
2012-02-01
314
+
2012-02-02
315
+
2012-02-03
316
+
...
317
+
2012-02-28
318
+
2012-02-29
319
+
2012-03-01
320
+
```
321
+
322
+
This script opens all months within a 2 month window of
323
+
today:
324
+
325
+
```bash
326
+
λ vim -O $(
327
+
dateseq \
328
+
"$(date --date "2 months ago" +%Y/%m)" \
329
+
"$(date --date "2 months" +%Y/%m)" \
330
+
-i %Y/%m \
331
+
-f %Y/%m
332
+
)
333
+
```
334
+
335
+
336
+
### Fin
337
+
338
+
You can find a sample vimrc in this repository,
339
+
along with a nix flake file to kick things off:
340
+
341
+
```
342
+
λ nix develop
343
+
λ cd examples
344
+
λ journal
345
+
```
346
+
347
+
Plain text journaling can be just as much fun as a pen and
348
+
paper. Throw in some ASCII art for each month, use swankier
349
+
signifiers, or louder syntax highlighting. Don't expect
350
+
forgiveness from org-mode users though.
351
+
352
+
[](https://u.peppe.rs/ZCK.png)
353
+