Apuntes mientras aprendo sobre software y computadoras.

Computación

Tomar notas con el método Zettelkasten usando Bash + Neovim + Markdown

En este apunte voy a revisar las herramientas que utilizo para escribir notas y capturar ideas con el método Zettelkasten usando una combinación de Bash, Neovim y Markdown.

La idea para este post salió de este otro blog, que tiene muy buena información sobre el tema.

Mi objetivo fue recrear de alguna forma el mismo entorno, pero modificando algunas cosas y utilizando la menor cantidad de plugins para conseguirlo.

¿Qué es tomar notas con el método Zettelkasten?

Lo que sigue es un resumen ultra ajustado del concepto.

En algún momento del siglo XX el sociólogo alemán Niklas Luhmann empezó a tomar notas utilizando fichas de biblioteca. Nada especialmente nuevo en eso. Lo importante es que cada ficha estaba vinculada a las demás por un sistema de etiquetas.

Las fichas no estaban distribuidas en cajas o carpetas que las englobaran por tema. El sistema buscaba más bien evitar que las notas quedaran aisladas entre ellas.

Siendo que las fichas compartían etiquetas temáticas, era posible saltar entre anotaciones vinculando estas etiquetas. Gracias a estos vínculos el autor podía encontrar asociaciones novedosas entre apuntes en apariencia completamente separados.

Este sistema de organización le permitió escribir una gran cantidad de libros y ensayos académicos.

Para redondear:

  1. Buscamos tomar notas de una forma práctica y fácil.
  2. Las notas tienen que estar vinculadas entre si por vínculos y etiquetas.
  3. Con la finalidad de alentar la originalidad y la asociación de temas, vamos a evitar la estructura de un árbol de directorios.

Para poder tomar notas rápido voy a utilizar un programa escrito en Bash. Vamos a usar archivos de texto en Markdown, y vamos a abrir esos archivos con Neovim.

El código de Bash

Necesitamos poder capturar ideas en cualquier momento, y utilizando Bash tenemos la opción de crear los documentos y abrirlos al mismo tiempo, sin necesidad de abandonar la terminal.

El programa completo es el siguiente:

#!/bin/bash

# Script de Bash para crear notas rápido usando Neovim

# Camino al directorio de notas
PROJECT_DIR="$HOME/notas/"

obtener_nombre() {
    # Pedimos el nombre del archivo.
    while true; do
        read -p "Nombre del archivo (sin extension): " filename
        # Revisamos si el nombre tiene espacios o extensiones para evitarlo.
        if [[ -z "$filename" || "$filename" =~ [^a-zA-Z0-9_\-] ]]; then
            echo "Error: el nombre '$filename' no es valido. Solamente puede contener letras, números y guiones bajos." >&2
        else
            echo "$filename"
            return 0
        fi
    done
}

# Función para crear y abrir la nota
abrir_archivo() {
    if [ ! -e "${PROJECT_DIR}/${filename}.mkd" ]; then
        # Si el nombre del archivo no existe, lo creamos
        touch "${PROJECT_DIR}/${filename}.mkd"
    else
        # Si el nombre existe, usamos expansión aritmética para agregarle un número
        i=1
        while [ -e "${PROJECT_DIR}/${filename}${i}.mkd" ]; do
            ((i++))
        done
        # Creamos el nuevo archivo
        touch "${PROJECT_DIR}/${filename}${i}.mkd"
    fi

    # entramos al directorio con cd
	cd "${PROJECT_DIR}" || exit

    # Conseguir el código de tiempo
    timestamp="$(date '+%d-%m-%Y %H:%M')"
    
    # Darle formato al archivo
    {
        echo -e "\n"
        echo "Nombre: ${filename}${i}"
        echo "Fecha: $timestamp"
        echo -e "\n"
        echo "Tags: "
        echo -e "\n\n"
        echo "Nota:"
        echo -e "\n\n\n"
        echo "Links:"
        echo -e "\n\n"
        echo "Referencias:"
        echo -e "\n\n"
    } >>"${PROJECT_DIR}/${filename}${i}.mkd"

    # Abrir el archivo en Neovim
    NVIM_BINARY=$(which nvim)

    if [ -z "$NVIM_BINARY" ] || [ ! -x "$NVIM_BINARY" ]; then
        echo "Neovim no fue encontrado en el sistema."
    else
        "$NVIM_BINARY" "${PROJECT_DIR}/${filename}${i}.mkd"
    fi
}

# Si el nombre del archivo no fue asignado, pedirlo
if [[ $# -eq 0 ]]; then
    filename=$(obtener_nombre)
fi

# si se asignaron más de un argumento, interrumpir el programa.
if [[ $# -gt 1 ]]; then
    echo "El nombre no puede tener espacios ni extensión"
    echo "Ejemplo de uso: nota nombre_apunte"
    exit 1
fi

# si se asignó un argumento, revisar si es valido
if [[ $# -eq 1 ]]; then
    filename=$1
    if [[ -z "$filename" || "$filename" =~ [^a-zA-Z0-9_\-] ]]; then

        if [[ -z "$filename" ]]; then
            echo "Error: El nombre del archivo no puede quedar vacio." >&2
        else
            echo "Error: el nombre '$filename' no es valido. Solamente puede contener letras, números y guiones bajos." >&2
        fi
        exit 1
    fi
fi


abrir_archivo

El código parte por parte

Lo primero que hacemos es crear una variable ajustando el directorio dónde vamos a guardar todos los archivos:

PROJECT_DIR="$HOME/notas/"

Acto seguido tenemos una función para capturar el nombre de la nota, vamos a llamar a esta función más tarde:

obtener_nombre() {
    # Pedimos el nombre del archivo.
    while true; do
        read -p "Nombre del archivo (sin extension): " filename
        # Revisamos si el nombre tiene espacios o extensiones para evitarlo.
        if [[ -z "$filename" || "$filename" =~ [^a-zA-Z0-9_\-] ]]; then
            echo "Error: el nombre '$filename' no es valido. Solamente puede contener letras, números y guiones bajos." >&2
        else
            echo "$filename"
            return 0
        fi
    done
}

Podemos invocar el comando agregando un argumento extra, ese argumento es el nombre de la nota. Si invocamos el programa sin argumento, nos va a pedir que agreguemos un nombre. Esto se resuelve en esta parte del código:

# Si el nombre del archivo no fue asignado, pedirlo
if [[ $# -eq 0 ]]; then
    filename=$(obtener_nombre)
fi

# si se asignaron más de un argumento, interrumpir el programa.
if [[ $# -gt 1 ]]; then
    echo "El nombre no puede tener espacios ni extensión"
    echo "Ejemplo de uso: nota nombre_apunte"
    exit 1
fi

# si se asignó un argumento, revisar si es valido
if [[ $# -eq 1 ]]; then
    filename=$1
    if [[ -z "$filename" || "$filename" =~ [^a-zA-Z0-9_\-] ]]; then

        if [[ -z "$filename" ]]; then
            echo "Error: El nombre del archivo no puede quedar vacio." >&2
        else
            echo "Error: el nombre '$filename' no es valido. Solamente puede contener letras, números y guiones bajos." >&2
        fi
        exit 1
    fi
fi

De momento esta función responde a errores si: se ingresa más de un argumento o si se utiliza algo distinto de guiones bajos para crear el nombre.

Si ya existe un archivo del mismo nombre en la carpeta, este bucle se encarga de agregarle un nuevo número al final para distinguirlo:

    if [ ! -e "${PROJECT_DIR}/${filename}.mkd" ]; then
        # Si el nombre del archivo no existe, lo creamos
        touch "${PROJECT_DIR}/${filename}.mkd"
    else
        # Si el nombre existe, usamos expansión aritmética para agregarle un número
        i=1
        while [ -e "${PROJECT_DIR}/${filename}${i}.mkd" ]; do
            ((i++))
        done
        # Creamos el nuevo archivo
        touch "${PROJECT_DIR}/${filename}${i}.mkd"
    fi

La siguiente parte del código le da la estructura al futuro archivo de la nota en si mismo:

# Darle formato al archivo
    {
        echo -e "\n"
        echo "Nombre: ${filename}${i}"
        echo "Fecha: $timestamp"
        echo -e "\n"
        echo "Tags: "
        echo -e "\n\n"
        echo "Nota:"
        echo -e "\n\n\n"
        echo "Links:"
        echo -e "\n\n"
        echo "Referencias:"
        echo -e "\n\n"
    } >>"${PROJECT_DIR}/${filename}${i}.mkd"

El formato de la nota incluye espacio para el título, las etiquetas y fecha. También hay espacio para agregar links internos y referencias.

No estamos utilizando markdown directamente ahora, pero podemos usarlo para darle algún formato extra más tarde.

La última parte de esa función abre el nuevo archivo en Neovim:

    # Abrir el archivo en Neovim
    NVIM_BINARY=$(which nvim)

    if [ -z "$NVIM_BINARY" ] || [ ! -x "$NVIM_BINARY" ]; then
        echo "Neovim no fue encontrado en el sistema."
    else
        "$NVIM_BINARY" "${PROJECT_DIR}/${filename}${i}.mkd"
    fi

Escribir las notas en Neovim

La parte de escribir la nota no supone ningún truco. El único complemento extra que estoy utilizando a la configuración básica de Neovim es “Telescope”, un plugin que entre varias opciones interesantes nos da otra forma para hacer búsquedas entre archivos dentro de un mismo directorio.

Si bien es posible utilizar Markdown para darle formato al texto, la nota original no lo utiliza. O bueno, mejor dicho lo utilizo principalmente para generar enlaces.

Cómo buscar entre las etiquetas de Markdown en Neovim

La nota tiene una parte para poner etiquetas. Yo las escribo de esta forma, imitando la clásica convención de redes sociales:

#etiqueta

Lo importante es no dejar espacio entre el numeral y la palabra, para que no se confunda con un heading de Markdown.

¿Y cómo buscamos entre etiquetas para encontrar coincidencias entre notas?

Para eso es que utilizo el plugin “Telescope”. Lo hago por medio del comando:

:Telescope live_grep

Y listo, con eso puedo ver todas las etiquetas en la carpeta.

Cómo generar los vínculos entre anotaciones con Neovim

Para crear los enlaces entre notas vamos a usar Markdown, de la siguiente forma:

[nombre de la nota para asociar](enlace al archivo)

¿Y en qué forma podemos revisar de forma rápida los nombres de los archivos existentes en la carpeta para poder enlazarlos?

Hay una forma de hacerlo, primero nos ponemos entre los paréntesis donde queremos poner la dirección. Liego en modo “INSERT” usamos el atajo de teclado:

Ctrl + x  Ctrl +f

Esto es “ctrl x”” seguido de “ctrl f”.

Con eso activamos el modo de auto-completar, y vamos a poder elegir el documento a enlazar rápido. Recordemos que el programa nos deja dentro de la carpeta donde se guardan los archivos, así que vamos a encontrar un listado de todo lo guardado en ese directorio.

El principal problema que tiene esta estrategia es que no vamos a poder abrir los enlaces directamente desde Neovim sin utilizar un plugin extra. Eso no es realmente algo que va a detenernos, porque sabiendo el nombre del enlace es fácil buscarlo desde dentro del programa. El enlace nos sirve de referencia de ese modo.

Y una vez creado el enlace, siempre vamos a poder seguirlo si abrimos el documento con otro programa (por ejemplo VScodium)) o si conseguimos un complemento para hacerlo desde Neovim.

Conclusión

Con este termino el apunte donde reviso la forma en que hago notas utilizando una combinación de Bash, Neovim y Markdown.

Recordemos que uno de los objetivos de este sistema es guardar todas las anotaciones en el mismo directorio, para poder vincularlas y encontrar asociaciones nuevas. Pero es posible modificar el código para crear nuevos directorios, o también podemos crear varias versiones del programa para generar notas de distintos temas.

Ya estuve escribiendo sobre Neovim antes, por ejemplo en esta otra entrada reviso la forma en que lo utilizo para escribir textos largos

¿Usas algún sistema para tomar notas? ¿Encontraste errores en el material o te interesa compartir tus opiniones? Te invito a que me escribas un mensaje al correo electrónico en la sección de contactos.

La seguimos en el próximo apunte.