···11+" insert fancy signifiers with abbrevs
22+iabbrev todo ·
33+iabbrev done ×
44+55+" select the task list and hit `gq` to sort and group by status
66+set formatprg=sort\ -V
77+88+" syntax highlighting
99+augroup JournalSyntax
1010+ autocmd!
1111+ autocmd BufReadPost * set filetype=journal
1212+1313+ autocmd BufReadPost * syntax match JournalAll /.*/ " captures the entire buffer
1414+ autocmd BufReadPost * syntax match JournalDone /^×.*/ " lines containing 'done' items: ×
1515+ autocmd BufReadPost * syntax match JournalTodo /^·.*/ " lines containing 'todo' items: ·
1616+ autocmd BufReadPost * syntax match JournalEvent /^o.*/ " lines containing 'event' items: o
1717+ autocmd BufReadPost * syntax match JournalNote /^- .*/ " lines containing 'note' items: -
1818+ autocmd BufReadPost * syntax match JournalMoved /^>.*/ " lines containing 'moved' items: >
1919+ autocmd BufReadPost * syntax match JournalHeader /^\<\u\+\>.*/ " lines starting with caps
2020+2121+ autocmd BufReadPost * highlight JournalAll ctermfg=12
2222+ autocmd BufReadPost * highlight JournalHeader ctermfg=12
2323+ autocmd BufReadPost * highlight JournalDone ctermfg=12
2424+ autocmd BufReadPost * highlight JournalEvent ctermfg=6 " cyan
2525+ autocmd BufReadPost * highlight JournalMoved ctermfg=5 " pink
2626+ autocmd BufReadPost * highlight JournalNote ctermfg=3 " yellow
2727+ autocmd BufReadPost * highlight VertSplit ctermfg=0 ctermbg=0 " hide vert splits
2828+augroup END
2929+3030+augroup JournalHideUIElements
3131+ autocmd!
3232+ " hide junk
3333+ autocmd VimEnter * set laststatus=0
3434+ autocmd VimEnter * set noruler nonumber nocursorline nocursorcolumn norelativenumber
3535+3636+ " pin scrolling
3737+ autocmd VimEnter * set scrollbind
3838+3939+augroup END
4040+4141+syntax on
examples/06
examples/2025/06
examples/07
examples/2025/07
examples/08
examples/2025/08
examples/09
examples/2025/09
examples/10
examples/2025/10
+353
readme.md
···11+I cobbled together a journaling system with {neo,}vim,
22+coreutils and [dateutils](http://www.fresse.org/dateutils).
33+This system is loosely based on [Ryder
44+Caroll's](https://www.rydercarroll.com/) Bullet Journal
55+method.
66+77+[](https://u.peppe.rs/SpF.png)
88+99+### The format
1010+1111+The journal for a given year is a directory:
1212+1313+```bash
1414+λ ls journal/
1515+2022/ 2023/
1616+```
1717+1818+In each directory are 12 files, one for each month of the
1919+year, numbered like so:
2020+2121+```bash
2222+λ ls journal/2023/
2323+01 02 03 04 05 06 07 08 09 10 11 12
2424+```
2525+2626+We can now begin writing stuff down:
2727+2828+```bash
2929+λ vim journal/2023/1
3030+```
3131+3232+Every month must start with a calendar of course, fill that
3333+in with:
3434+3535+```vim
3636+:read !cal -m
3737+```
3838+3939+Your entry for January might look like this:
4040+4141+```bash
4242+λ cat journal/2023/01
4343+ January 2023
4444+Mo Tu We Th Fr Sa Su
4545+ 1
4646+ 2 3 4 5 6 7 8
4747+ 9 10 11 12 13 14 15
4848+16 17 18 19 20 21 22
4949+23 24 25 26 27 28 29
5050+30 31
5151+```
5252+5353+I prefer planning week by week, as opposed to creating a
5454+task-list every day, here's what I have for the first couple
5555+of weeks:
5656+5757+```
5858+ January 2023
5959+Mo Tu We Th Fr Sa Su
6060+ 1
6161+ 2 3 4 5 6 7 8
6262+ 9 10 11 12 13 14 15
6363+16 17 18 19 20 21 22
6464+23 24 25 26 27 28 29
6565+30 31
6666+6767+6868+week 1
6969+7070+done apply leaves
7171+done dload boarding pass
7272+moved reply to dan
7373+7474+7575+week 2
7676+7777+todo reply to dan
7878+todo pack bags
7979+done travel insurance
8080+todo weigh luggage
8181+```
8282+8383+I start the week by writing a header and each item that week
8484+is placed on its own line. The items are prefixed with a
8585+`todo` or a `done` signifier.
8686+8787+8888+### Form over function
8989+9090+Right off the bat, the signifiers look very noisy, Even more
9191+so once we start introducing variety (I use "event", "note"
9292+and "moved"):
9393+9494+```
9595+week 1
9696+9797+todo apply leaves
9898+done dload boarding pass
9999+todo reply to dan
100100+event fr trip
101101+note weight 68.6
102102+```
103103+104104+We can clean this up with "abbreviations" (`:h abbreviations`):
105105+106106+```vim
107107+:iabbrev todo ·
108108+:iabbrev done ×
109109+```
110110+111111+Now, typing this:
112112+113113+```
114114+todo apply leaves
115115+```
116116+117117+Automatically inserts:
118118+119119+```
120120+· apply leaves
121121+```
122122+123123+You can use `x` and `o` as well, but `×` (U+00D7,
124124+MULTIPLICATION SIGN) and `·` (U+00B7, MIDDLE DOT) are more
125125+... *gourmet*.
126126+127127+The other signifiers I use are:
128128+129129+- `-` for note
130130+- `o` for event
131131+- `>` for moved.
132132+133133+Nit #2 is the lack of order. We can employ vim to introduce
134134+grouping and sorting. Select the list of entries for this
135135+week:
136136+137137+```vim
138138+vip " line-wise select inner paragraph
139139+:'<,'>sort " the markers '< and '> are automatically inserted,
140140+ " they mark the start and end of the selection
141141+```
142142+143143+We end up with:
144144+145145+```
146146+week 1
147147+148148+· apply leaves
149149+· reply to dan
150150+× dload boarding pass
151151+```
152152+153153+The lines are grouped by their signifiers, segregating todo
154154+items from completed items. Luckily, MIDDLE DOT is lesser
155155+than MULTIPLICATION SIGN, so todo items are placed at the
156156+top. The same goes for `o` and `x` symbols, either set of
157157+signifiers will result in the same sorting order.
158158+159159+We can shorten this select-paragraph-invoke-sort dance by
160160+setting the `formatprg` variable:
161161+162162+```vim
163163+:set formatprg=sort\ -V
164164+```
165165+166166+Now, hitting `gqip` should automatically group and sort the
167167+items for the week under the cursor, moving todo items to
168168+the top. Finding signifier glyphs that suit your sorting
169169+preference is a fun exercise.
170170+171171+### Syntax highlighting
172172+173173+Adding color to items introduces another layer of visual
174174+distinction. In truth, I like to deck it out just because.
175175+176176+First, create a few syntax groups:
177177+178178+```vim
179179+:syntax match JournalAll /.*/ " captures the entire buffer
180180+:syntax match JournalDone /^×.*/ " lines containing 'done' items: ×
181181+:syntax match JournalTodo /^·.*/ " lines containing 'todo' items: ·
182182+:syntax match JournalEvent /^o.*/ " lines containing 'event' items: o
183183+:syntax match JournalNote /^- .*/ " lines containing 'note' items: -
184184+:syntax match JournalMoved /^>.*/ " lines containing 'moved' items: >
185185+```
186186+187187+Add highlights to each group:
188188+189189+```vim
190190+:highlight JournalAll ctermfg=12 " bright black
191191+:highlight JournalDone ctermfg=12 " bright black
192192+:highlight JournalEvent ctermfg=6 " cyan
193193+:highlight JournalMoved ctermfg=5 " magenta
194194+:highlight JournalNote ctermfg=3 " yellow
195195+```
196196+197197+In my terminal, this is rendered like so:
198198+199199+[](https://u.peppe.rs/Du6.png)
200200+201201+### Habit tracking
202202+203203+While this is not a part of my journaling system anymore, a
204204+few headers and an awk script is all it takes to track
205205+habits. My weekly entries would include a couple of habit
206206+headers like so:
207207+208208+```
209209+week 1 --------------
210210+211211+× wake up on time
212212+× water the plants
213213+214214+spend 7.5 7 10
215215+---------------------
216216+217217+218218+week 2 --------------
219219+220220+· make the bed
221221+· go to bed
222222+223223+spend 30 2.75 6
224224+---------------------
225225+```
226226+227227+Here, under the `spend` header in week 1, are a list of
228228+expenditures accumulated over the week. The monthly spend is
229229+calculated with this awk script:
230230+231231+```awk
232232+BEGIN {spend=0;}
233233+/spend/ {for(i=1;i<=$NF;i++) spend+=$i;}
234234+END { printf spend "eur"}
235235+```
236236+237237+And invoked like so:
238238+239239+```
240240+λ awk -f spend.awk journal/2023/01
241241+63.25eur
242242+```
243243+244244+### Reflection
245245+246246+Journaling is not just about planning what is to come, but
247247+also reflecting on what has passed. It would make sense to
248248+simultaneously look at the past few weeks' entries while
249249+making your current one. To open multiple months of entries
250250+at the same time:
251251+252252+```
253253+λ vim -O journal/2023/0{1,2,3}
254254+```
255255+256256+Opens 3 months, side-by-side, in vertical splits:
257257+258258+```
259259+JANUARY ------------ │ FEBRUARY ----------- │ MARCH --------------
260260+ │ │
261261+Mo Tu We Th Fr Sa Su │ Mo Tu We Th Fr Sa Su │ Mo Tu We Th Fr Sa Su
262262+ 1 │ 1 2 3 4 5 │ 1 2 3 4 5
263263+ 2 3 4 5 6 7 8 │ 6 7 8 9 10 11 12 │ 6 7 8 9 10 11 12
264264+ 9 10 11 12 13 14 15 │ 13 14 15 16 17 18 19 │ 13 14 15 16 17 18 19
265265+16 17 18 19 20 21 22 │ 20 21 22 23 24 25 26 │ 20 21 22 23 24 25 26
266266+23 24 25 26 27 28 29 │ 27 28 │ 27 28 29 30 31
267267+30 31 │ │
268268+ │ │
269269+ │ │
270270+WEEK 1 ------------- │ WEEK 1 ------------- │ WEEK 1 -------------
271271+ │ │
272272+> latex setup │ > forex │ - weight: 64
273273+× make the bed │ × clean shoes │ > close sg-pr
274274+× 03: dentist │ × buy clothes │ × facewash
275275+× integrate tsg │ × draw │ × groceries
276276+ │ │
277277+ │ │
278278+WEEK 2 ------------- │ WEEK 2 ------------- │ WEEK 2 -------------
279279+ │ │
280280+× latex setup │ - viral fever │ > close sg-pr
281281+× send invoice │ × forex │ × plan meet
282282+× stack-graph pr │ × activate sim │ × sg storage
283283+ │ × bitlbee │
284284+```
285285+286286+### Reducing friction
287287+288288+Journaling already requires a solid amount of discipline and
289289+consistency. The added friction of typing `vim
290290+journal/$CURRENT_YEAR/$CURRENT_MONTH` each time is doing no
291291+favors.
292292+293293+To open the current month based on system time:
294294+295295+```bash
296296+λ vim $(date +"%Y/%m")
297297+```
298298+299299+To open all the months within a 2 month window of today, is
300300+a little trickier. The command we wish to generate is (if
301301+today is 2023/12):
302302+303303+```bash
304304+λ vim -O 2023/10 2023/11 2023/12 2024/01 2024/02
305305+```
306306+307307+And that is where `dateseq` from
308308+[dateutils](http://www.fresse.org/dateutils) comes in handy,
309309+for example:
310310+311311+```bash
312312+λ dateseq 2012-02-01 2012-03-01
313313+2012-02-01
314314+2012-02-02
315315+2012-02-03
316316+...
317317+2012-02-28
318318+2012-02-29
319319+2012-03-01
320320+```
321321+322322+This script opens all months within a 2 month window of
323323+today:
324324+325325+```bash
326326+λ vim -O $(
327327+ dateseq \
328328+ "$(date --date "2 months ago" +%Y/%m)" \
329329+ "$(date --date "2 months" +%Y/%m)" \
330330+ -i %Y/%m \
331331+ -f %Y/%m
332332+)
333333+```
334334+335335+336336+### Fin
337337+338338+You can find a sample vimrc in this repository,
339339+along with a nix flake file to kick things off:
340340+341341+```
342342+λ nix develop
343343+λ cd examples
344344+λ journal
345345+```
346346+347347+Plain text journaling can be just as much fun as a pen and
348348+paper. Throw in some ASCII art for each month, use swankier
349349+signifiers, or louder syntax highlighting. Don't expect
350350+forgiveness from org-mode users though.
351351+352352+[](https://u.peppe.rs/ZCK.png)
353353+