Écrire tranquillement, réflechir calmement.
Zettelzen est l'incarnation de la façon dont mon cerveau fonctionne, en adéquation avec une forme de philosophie générale de vie vers laquelle je tends – ou du moins vers laquelle j'essaie de tendre.
J'utilise plusieurs outils pour réfléchir et écrire :
- acme comme éditeur sur le pc, vim sur le téléphone.
- plusieurs scripts me permettant de contextualiser, éditer facilement.
- iomlan pour générer le tout
Acme
L'éditeur peut déconcerter au début, mais la liberté et la maléabilité de ce dernier sont absolument incroyables. Il faut tout de même retrousser les manches pour le configurer avec des scripts et autres.
En revanche, une fois fait, c'est un réel plaisir.
Ainsi les liens internes et les fichiers inclus sont supportés en éditant les règles de plumper
:
# Règles plumbing pour acme
###
# Modifications personnelles:
# - Ajout de l'ouverture des liens iomlan
# - Ajout de l'ouverture des textes et codes
###
editor = acme
type is text
data matches '([a-zA-Z0-9_\-./]+)\}'
arg isfile $1.txt
data set $file
plumb to edit
plumb client window $editor
# host files go to acme
type is text
data matches 'HOST : ([a-z0-9_\-./]+)'
arg isfile $1.ndtl
data set $file
plumb to edit
plumb client window $editor
type is text
data matches '{([a-zA-Z0-9_\-./]+)'
arg isfile $1.txt
data set $file
plumb to edit
plumb client window $editor
type is text
data matches '{\^(text) ([a-zA-Z0-9_\-./]+)\}'
arg isfile ../inc/$1/$2.txt
data set $file
plumb to edit
plumb client window $editor
#plumb start azo $1 $2.txt
type is text
data matches '{\^(code) ([a-zA-Z0-9_\-./]+)\}'
arg isfile ../inc/$1/$2
data set $file
plumb to edit
plumb client window $editor
#plumb start azo $1 $2
include basic
# wikilink files go to acme
#type is text
#data matches '([a-zA-Z0-9_\-./]+)\]]'
#arg isfile $1.md
#data set $file
#plumb to edit
#plumb client window $editor
# wikilink aliases files go to acme
#type is text
#data matches '([a-zA-Z0-9_\-./]+)\|'
#arg isfile $1.md
#data set $file
#plumb to edit
#plumb client window $editor
Scripts
acme_utils
regroupent des fonctions et variables communes
azi
affiche les liens vers le fichier ainsi que sa structure d'idee.
azc
vérifie l'ortographe et la grammaire.
Ces scripts sont écrits en language shell (ksh
version OpenBSD) et utilisent également les outils suivants:
ripgrep
jq
find
,cut
,grep
,ls
Vim
Pour l'édition avec vim
j'ai essayé de reproduire ce que j'ai avec acme
:
- le plugin vim-grammalecte gère la grammaire.
- la correction orthographique est assurée par l'éditeur.
- un script vim perso gère la création et l'affichage de la structure de la note ainsi que les liens vers celle-ci.
" zettelkasten.vim
" Zettelkasten plugin for Vim with Folgezettel-style IDs and plain text files
" Function to generate the base ID for the root note
function! ZettelkastenGenerateBaseID()
return "0"
endfunction
" Function to generate a follow-up ID based on the parent ID
function! ZettelkastenGenerateFollowID(parent_id)
let pattern = glob("~/ssg/src/" . a:parent_id . "*.txt", 0, 1)
let suffixes = []
for file in pattern
let match = matchstr(fnamemodify(file, ':t'), '\v' . a:parent_id . '(\a|\d+)+')
if match != ""
call add(suffixes, match[len(a:parent_id):])
endif
endfor
call sort(suffixes)
if empty(suffixes)
return "a" " First child of the parent
endif
let last_suffix = suffixes[-1]
" Increment last suffix based on whether it's numeric or alphabetic
if last_suffix =~ '^\d+$'
return (str2nr(last_suffix) + 1)
elseif last_suffix =~ '^\a+$'
return substitute(last_suffix, '.\zs.$', nr2char(char2nr(last_suffix[-1]) + 1), '')
else
let prefix = last_suffix[:-2]
return prefix . nr2char(char2nr(last_suffix[-1]) + 1)
endif
endfunction
" Function to create a sibling note
function! ZettelkastenCreateSiblingNote()
" Get the current note's ID from the first line
let current_id = matchstr(getline(1), '^\S\+')
if current_id == ""
echo "No ID found in the current note."
return
endif
" Determine the prefix and last character of the current ID
let sibling_prefix = current_id[:-2] " ID without the last character
let last_char = current_id[-1:]
" Initialize the new_id variable for the sibling ID
let new_id = ""
" Check if the last character is alphabetic
if last_char =~# '\a'
" Increment alphabetically for siblings until an unused ID is found
while 1
let last_char = nr2char(char2nr(last_char) + 1)
let new_id = sibling_prefix . last_char
" Check all files in the directory for an existing ID
let exists = 0
for file in glob("~/ssg/src/*.txt", 0, 1)
let first_line = readfile(file, '', 1)[0]
let file_id = matchstr(first_line, '^\S\+')
if file_id == new_id
let exists = 1
break
endif
endfor
" If the new_id is unique, break the loop
if !exists
break
endif
endwhile
else
" If last character is numeric, increment numerically
let suffix_num = str2nr(last_char)
while 1
let suffix_num += 1
let new_id = sibling_prefix . suffix_num
" Check all files in the directory for an existing ID
let exists = 0
for file in glob("~/ssg/src/*.txt", 0, 1)
let first_line = readfile(file, '', 1)[0]
let file_id = matchstr(first_line, '^\S\+')
if file_id == new_id
let exists = 1
break
endif
endfor
" If new_id is unique, break the loop
if !exists
break
endif
endwhile
endif
" Prompt user for the sibling note title
let title = input("Enter sibling note title: ")
if title == ""
echo "Title cannot be empty."
return
endif
" Create the sibling note file
let filename = substitute(title, '\s\+', '_', 'g') . '.txt'
let filepath = expand("~/ssg/src/") . filename
execute "edit " . filepath
call setline(1, new_id . " " . title)
echo "Created sibling note with ID: " . new_id
endfunction
" Function to create a child note
function! ZettelkastenCreateChildNote()
" Get the current note's ID from the first line
let current_id = matchstr(getline(1), '^\S\+')
if current_id == ""
echo "No ID found in the current note."
return
endif
" Initialize numeric suffix for the child
let suffix_num = 1
let child_id = ""
" Increment numeric suffix until an unused child ID is found
while 1
let child_id = current_id . suffix_num
" Check all files in the directory for an existing ID
let exists = 0
for file in glob("~/ssg/src/*.txt", 0, 1)
let first_line = readfile(file, '', 1)[0]
let file_id = matchstr(first_line, '^\S\+')
if file_id == child_id
let exists = 1
break
endif
endfor
" If child_id is unique, break the loop
if !exists
break
endif
" Increment suffix_num for the next child ID
let suffix_num += 1
endwhile
" Prompt user for the child note title
let title = input("Enter child note title: ")
if title == ""
echo "Title cannot be empty."
return
endif
" Create the child note file
let filename = substitute(title, '\s\+', '_', 'g') . '.txt'
let filepath = expand("~/ssg/src/") . filename
execute "edit " . filepath
call setline(1, child_id . " " . title)
echo "Created child note with ID: " . child_id
endfunction
" Function to find notes based on a keyword
function! ZettelkastenFindNote()
let keyword = input("Search notes: ")
if keyword == ""
return
endif
let cmd = "grep -i -l '" . keyword . "' ~/ssg/src/*.txt"
let result_files = systemlist(cmd)
let display_results = []
for file in result_files
let first_line = readfile(file, '', 1)[0]
if first_line != ""
let id = matchstr(first_line, '^\S\+')
let title = matchstr(first_line, '\s\+\zs.*')
call add(display_results, "(" . id . ") " . title)
endif
endfor
if empty(display_results)
echo "No notes found."
else
let choice = inputlist(["Select a note:"] + display_results)
if choice > 0
execute "edit " . result_files[choice - 1]
endif
endif
endfunction
" Function to print the structure of the Zettelkasten note
function! ZettelkastenPrintStructure()
let first_line = getline(1)
let current_id = matchstr(first_line, '^\S\+')
if current_id == ""
echo "No Folgezettel ID found in the first line."
return
endif
let zettelkasten_dir = "~/ssg/src/"
let parent_id = current_id[:-2]
let siblings = []
let children = []
let sibling_prefix = parent_id
let files = glob(zettelkasten_dir . "*.txt", 0, 1)
for file in files
let first_line = readfile(file, '', 1)[0]
let file_id = matchstr(first_line, '^\S\+')
let file_title = matchstr(first_line, '\s\+\zs.*')
if file_id == parent_id
let parent = "(" . file_id . ") " . file_title
elseif file_id =~? '^' . sibling_prefix . '.$' && file_id != current_id
call add(siblings, "(" . file_id . ") " . file_title)
elseif file_id =~? '^' . current_id . '.'
call add(children, "(" . file_id . ") " . file_title)
endif
endfor
echo "Folgezettel Structure for (" . current_id . "):"
echo "Parent: " . (exists('parent') ? parent : "None")
echo "Siblings:"
for sibling in siblings
echo " " . sibling
endfor
echo "Children:"
for child in children
echo " " . child
endfor
endfunction
" Function to find backlinks to the current note
function! ZettelkastenBacklinks()
let current_filename = expand('%:t:r')
if current_filename == ""
echo "No file open."
return
endif
let zettelkasten_dir = "~/ssg/src/"
let backlinks = []
let files = glob(zettelkasten_dir . "*.txt", 0, 1)
for file in files
let file_contents = readfile(file)
let file_linked = 0
for line in file_contents
let pattern = '{\s*\(\S\+\)\s*\([^}]*\)\?}'
if line =~? pattern
let linked_filename = matchstr(line, '{\s*\(\S\+\)\s*\([^}]*\)\?}')
let linked_filename = substitute(linked_filename, '{\s*\(.*\)\s*}', '\1', '')
let linked_filename = substitute(linked_filename, '\s\+.*', '', '') " Remove any alias part
if linked_filename == current_filename
let file_linked = 1
break
endif
endif
endfor
if file_linked
let file_title = matchstr(readfile(file, '', 1)[0], '\s\+\zs.*')
let line_number = 1 " Assuming title is on the first line
call add(backlinks, {'filename': fnamemodify(file, ':t'), 'title': file_title, 'lnum': line_number})
endif
endfor
if empty(backlinks)
echo "No backlinks found."
else
call setloclist(0, [], 'r')
for backlink in backlinks
call setloclist(0, [{'filename': backlink.filename, 'lnum': backlink.lnum, 'text': backlink.title}], 'a')
endfor
echo "Backlinks populated in location list."
endif
endfunction
" Function to open a linked text file from the current line
function! OpenTxtFile()
let l:line = getline('.')
let l:match = matchstr(l:line, '{\zs\S*\ze[^}]*}')
if !empty(l:match)
let l:filename = l:match . '.txt'
if filereadable(l:filename)
execute 'e' l:filename
else
echohl ErrorMsg | echo "File not found: " . l:filename | echohl None
endif
else
echohl ErrorMsg | echo "Cursor not on {file} pattern" | echohl None
endif
endfunction
" Key mapping for opening a text file using the defined function
nnoremap o :call OpenTxtFile()
" Commands for the Zettelkasten functions
command! ZkBacklinks call ZettelkastenBacklinks()
command! ZkFz call ZettelkastenPrintStructure()
command! ZkCreateSibling call ZettelkastenCreateSiblingNote()
command! ZkCreateChild call ZettelkastenCreateChildNote()
command! ZkFindNote call ZettelkastenFindNote()
Principes pour qu'une note soit efficace
- Avoir une structure d'idée.
- une
description
consistante. (extension du titre plus succin.)
- Bien liée.
- Se poser les bonnes questions. (Boussole d'idées)