Skip to content

baracunatana/alfred

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 

Repository files navigation

Configuración de Alfred

Introducción

Alfred es un servidor personal en el que tengo los siguientes servicios:

Más que un servicio pensado en producción, es un proyecto de aprendizaje y diversión.

Este archivo contiene toda la configuración del servidor y de los servicios expuestos en el mismo. Esta escrito en formato de programación literaria (prosa mas código) y usa las capacidades de orgmode para ejecutar código bash y escribir archivos de configuración de diferente naturaleza.

Este archivo contiene todo lo que se debe hacer para llegar, dese 0, a una configuración estable de Alfred.

Hardware actual

La presente configuración se escribe para un Rapsberry Pi 4 B de 8 gigas de RAM usando Raspberry OS de 64 bits. Los detalles técnicos son:

Configuración inicial de hardware y sistema operativo

Una vez se tenga ensamblado el Raspberry Pi (se recomienda usar enfriamiento activo con este kit), se debe instalar el sistema operativo. Esto se logra usando Raspberry Pi Imager (version 1.5) para instalar Ubuntu Server 20.10 LTS de 64 bits en la tarjeta micro SD.

Dado que no vamos a conectar el RPi a ninguna pantalla ni teclado para hacer la configuración, tenemos que hacerlo todo por SSH. Ubuntu Server tiene habilitado por defecto el servicio de ssh[fn:1] usando el usuario ubuntu:ubuntu. Para evitar problemas de seguridad, lo primero que nos va a pedir es que cambiemos la contraseña de este usuario. Si conectamos el RPi por puerto ethernet, no hay que hacer nada más por ahora.

Configuración de SSH y seguridad inicial

Ahora tenemos que cambiar el usuario por defecto (para evitar problemas de seguridad). Esto se hace ejecutando los siguientes comandos en el RPi:

sudo adduser juan
sudo usermod -a -G adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,input,netdev juan
sudo pkill -u ubuntu
sudo deluser ubuntu

Luego de esto, cerramos la sesión de SSH e iniciamos una nuevo con usuario juan (y nos olvidamos por ahora del usuario ubuntu) para continuar con la configuración.

Para actualizar el sistema operativo corremos:

sudo apt update
sudo apt full-upgrade

Es recomendable crear una tarea recurrente para mantener actualizado el paquete openssh-server con parches de seguridad. Eso se hace creando una tarea cron ejecutando:

crontab -e 

y agregando la siguiente línea en el archivo mostrado[fn:2]:

# * * * * *  command to execute
# ┬ ┬ ┬ ┬ ┬
# │ │ │ │ │
# │ │ │ │ │
# │ │ │ │ └───── day of week (0 - 7) (0 to 6 are Sunday to Saturday, or use names; 7 is Sunday, the same as 0)
# │ │ │ └────────── month (1 - 12)
# │ │ └─────────────── day of month (1 - 31)
# │ └──────────────────── hour (0 - 23)
# └───────────────────────── min (0 - 59)
0 0 * * * apt install openssh-server

Para evitar ataques de diccionario con usuario pi (que no se eliminó), es buena idea desactivar el acceso de este usuario por ssh. Se hace agregando DenyUsers pi al archivo /etc/ssh/sshd_config y ejecutando:

sudo systemctl restart ssh

Firewall y fail2ban

Como medida adicional de seguridad, vamos a instalar un firewall ([[https://www.linux.com/learn/introduction-uncomplicated-firewall-ufw][ufw]]) y fail2ban para que nos automatice algunas tareas de gestión del firewall.

Los siguientes comando hacen esta instalación y configuran el firewall para aceptar conexiones de ssh:

sudo apt install ufw
sudo ufw allow ssh/tcp
sudo ufw enable
sudo ufw limit ssh/tcp
sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Ahora tenemos que editar el archivo /etc/fail2ban/jail.local agregando las siguientes líneas:

[ssh]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
bantime = -1

(Opcional) Agregar almacenamiento externo

Para determinar la ubicación del disco externo se ejecuta lsblk. Luego, se ejecutan los siguientes comando para definir punto de montaje del disco y hacer el montaje inicial.

sudo mkdir /media/externo
sudo mount -t ntfs-3g /dev/sda1 /media/externo

Para montar automáticamente, se agrega la siguiente línea a /etc/fstab (reemplanzando <xxx> con el ID del disco externo que se puede consultar ejecutando blkid /dev/sda1):

#Disco externo
UUID=<xxx> /media/externo ntfs rw,auto,users,exec,nls=utf8,umask=003,gid=46,uid=1000    0   0

Software básico

Como base para continuar con la configuración del servidor, vamos a instalar las siguientes aplicaciones:

sudo apt install git emacs-nox docker docker.io

De aquí en adelante podemos clonar este repositorio en /home/juan, abrir README.org en emacs local (a través de conexión ssh) y ejecutar los bloques sh en el servidor con C-c C-c.

Configuración de emacs

Para usar emacs para la configuración del resto de servidor vamos a crear una configuración mínima que nos facilite la vida. Para desplegarla tenemos que abrir este archivo en emacs y ejecutar M-x org-babel-tagle

use-package y straight

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Cargar el org de straight
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
(use-package org
  :config)

Interfaz básica

Quitar barra de menu y de herramientas

La barra de menú quita espacio y nunca la uso. Además, en terminar no se muestran barras y íconos.

(menu-bar-mode -1)
(tool-bar-mode -1)

Configuración de tema gráfico

Luego de leer https://observer.com/2015/02/meet-the-man-behind-solarized-the-most-important-color-scheme-in-computer-history/, me decidí por Solarized como tema gráfico.

Intenté con solarized-themes, pero usaba fuente no-mono y además los colores en orgmode eran horribles. Uso, entonces, doom-themes.

Para ambientes tty usa misterioso

(if (display-graphic-p)
    (use-package doom-themes
      :config
      ;; Global settings (defaults)
      (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
            doom-themes-enable-italic t) ; if nil, italics is universally disabled
      (load-theme 'doom-solarized-dark t))
  (load-theme 'misterioso t))

which-key

Which-key es un paquete que muestra los acordes disponibles luego de iniciar un acorde normal o tipo vim. Lo que hace es mostrar un diálogo que muestra las opciones disponibles luego de iniciar un comando. Por ejemplo, si se presiona C-c, which-key muestra un diálogo con todos los comandos que se pueden ejecutar luego de C-c y la descripción de la función a ejecutar. También muestra comando que abren más opciones (e.g. C-c C-x).

(use-package which-key
  :config
  (which-key-mode))

Word wrapping por defecto

(global-visual-line-mode)

Auto-revert por defecto

(global-auto-revert-mode t)

Dired

Por defecto, dired crea un nuevo buffer cada vez que se da RET en una carpeta. Si no se quiere abrir tantos buffers, una opción es usar dired-find-alternate-file (mapeado por defecto a a) en lugar de de dired-find-file (mapeado por defecto a RET). Sin embargo, esta función está deshabilitada porque los usuarios la encontraban confusa. Para habilitarla por defecto y evitar el diálogo de confirmación se debe ejecutar:

(put 'dired-find-alternate-file 'disabled nil)
(use-package all-the-icons-dired
  :hook (dired-mode . all-the-icons-dired-mode))

Emacs Dashboard

Uso Emacs dashboard para generar la pantalla de inicio en emacs.

(use-package dashboard
  :init
  (setq dashboard-items '((recents  . 5)
                          (bookmarks . 5)
                          (projects . 5)
                                        ;(registers . 5)
                          (agenda . 5)))
  (setq dashboard-show-shortcuts t)
  (setq dashboard-set-heading-icons t)
  (setq dashboard-set-file-icons t)
  (setq dashboard-set-file-icons t)
  (setq dashboard-center-content t)
  :config
  (add-to-list 'recentf-exclude ; Excluir de recentf archivos que no quiero ver en dasboard
               (expand-file-name "~/.emacs.d/excorporate/diary-excorporate-transient"))
  (add-to-list 'recentf-exclude ; Excluir de recentf archivos que no quiero ver en dasboard
               (expand-file-name "~/.emacs.d/bookmarks"))
  (add-to-list 'recentf-exclude ; Excluir de recentf archivos que no quiero ver en dasboard
               (expand-file-name "~/.emacs.d/excorporate/diary-excorporate-today"))
  (add-to-list 'recentf-exclude ; Excluir de recentf archivos que no quiero ver en dasboard
               (expand-file-name "~/.orhc-bibtex-cache"))
  (add-to-list 'recentf-exclude ; Excluir de recentf archivos que no quiero ver en dasboard
               (expand-file-name "~/.emacs.d/diary"))
  (dashboard-setup-startup-hook))

Yasnippet

Yasnippet es un paquete que permite la definición de snippets (o plantillas) y su utilización en diferentes modos. Por defecto, no trae plantillas definidas; si se quieren plantillas, se debe agregar el paquete yasnippet-snippets que trae una colección de plantillas predefinidas para modos populares.

(use-package yasnippet
  :defer 1
  :diminish yas-minor-mode
  :config (yas-global-mode)
  (use-package yasnippet-snippets
    :after yasnippet
    :config (yasnippet-snippets-initialize))
  (yas-reload-all))

La definición de plantillas se hace a partir de un archivo de texto plano que se guarda en .emacs.d/snippets/<mode>/.

Doom modeline

(use-package doom-modeline
  :init (doom-modeline-mode 1) ; Activar doom-modeline en todos los modos
  :config
  (setq doom-modeline-modal-icon nil)) ; Usar letras en lugar de íconos para indicar el modo de edición 

Rainbow delimiters

(use-package rainbow-delimiters
  :defer 1
  :init
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

Helpful

Helpful es un paquete que mejora los buffers de ayuda estándares de Emacs. Es usado en Doom emacs.

(use-package helpful
  :defer 2
  :custom
  (counsel-describe-function-function #'helpful-callable)
  (counsel-describe-variable-function #'helpful-variable))

Configuración de tema gráfico

Luego de leer https://observer.com/2015/02/meet-the-man-behind-solarized-the-most-important-color-scheme-in-computer-history/, me decidí por Solarized como tema gráfico.

Intenté con solarized-themes, pero usaba fuente no-mono y además los colores en orgmode eran horribles.

Instalación y activación:

(if (display-graphic-p)
    (use-package doom-themes
      :config
      ;; Global settings (defaults)
      (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
            doom-themes-enable-italic t) ; if nil, italics is universally disabled
      (load-theme 'doom-solarized-dark t))
  (load-theme 'misterioso t))

Mostrar número de líneas globalmente

(global-display-line-numbers-mode)

All-the-icons

Instalación de íconos para usar en Emacs dashboard y otros paquetes.

(use-package all-the-icons)

Luego de esto hay que correr all-the-icons-install-fonts desde emacs para instalar las fuent

magit

(use-package magit
  :defer 1
  :config )

projectile

(use-package projectile
  :custom
  ((projectile-enable-caching t) ; Habilitar cache de archivos de proyecto para acelerar 
   (projectile-globally-ignored-files '("*.org~")) ; extensiones de archivo a ignorar globalmente
   (projectile-completion-system 'ivy)) ;Usar ivy como motor de terminación
  :config
  (projectile-mode)
  (setq projectile-enable-caching t) 
  (use-package counsel-projectile
    :config
    (counsel-projectile-mode)))

Company

Company se usa para terminación en modos de programación o similares. La terminación se hace a partir de un diálogo flotante en el punto en donde se ingresa un término “terminable”.

(use-package company
  :defer 2
  :config )

Ivy

Configuración de Ivy

Configuración inicial de Ivy

Uso Ivy como framework de completitud. La configuración básica se muestra a continuación:

(use-package ivy
  ;; Para que no liste a Ivy en la barra de modos activados
  :diminish (ivy-mode . "")
  :config
  ;; Lo activa por defecto
  (ivy-mode 1))

Configuración de Counsel

Activo counsel y enlazo M-x a counsel-M-x. Con esto se logra ver los binds actuales de los comandos

(use-package counsel
  :config
  (global-set-key (kbd "M-x") 'counsel-M-x))

Ivy-rich y all-the-icons-ivy-rich

Los siguientes paquetes se usan para agregar íconos a ivy. Por ejemplo, agrega íconos a la selección de buffers.

(use-package all-the-icons-ivy-rich
  :init (all-the-icons-ivy-rich-mode 1))
(use-package ivy-rich
  :init (ivy-rich-mode 1))

Prescient

(use-package prescient
  :config
  (use-package ivy-prescient
    :after counsel
    :config
    (ivy-prescient-mode)
    (prescient-persist-mode)))

evil

Configuración básica

Es necesario instalar y configurar general. General es un paquete que permite la definición de acordes usando teclas lideres (globales y locales) para diferentes modos (e.g. emacs, normal, insert, etc).

(use-package general
  :config
  ;; defniciión de tecla lider global para modo normal.
  (general-create-definer leader
    ;; :prefix leader
    :states '(normal insert emacs)
    :keymaps 'override
    :prefix "SPC"
    ;; tecla lider desde modos diferente a normal.
    :non-normal-prefix "M-SPC")
  ;; definición de tecla lider local (relativo al major mode) para modo normal.
  (general-create-definer local-leader
    ;; prefix local-leader.
    :states '(normal insert emacs)
    :prefix "SPC m"
    ;; tecla lider local desde modos diferentes a normal
    :non-normal-prefix "M-SPC m"))

Ahora se carga evil y se hace la configuración inicial.

(use-package evil
  :config
  ;; Iniciar en modo NORMAL por defecto para todos los modos
  (setq evil-default-state 'normal)
  ;; Agregar modos extraños (que empiezan en E o M) a lista de modos visuales
  (add-to-list 'evil-normal-state-modes 'help-mode)
  (add-to-list 'evil-normal-state-modes 'help-mode)
  ;; Arrancar emacs con evil-mode por defecto.
  (evil-mode))

Evil-mc

Evil-mc es un paquete que permite la creación de multiples cursores cuando se usa evil-mode. Esto se puede usar para hacer ediciones simultáneas en múltiples puntos del mismo buffer.

(use-package evil-mc
  :config
  (global-evil-mc-mode  1))

Acordes globales

Operaciones sobre archivos

(leader
  :infix "f"
  "" '(:ignore t :which-key "file")
  "f" '(counsel-find-file :which-key "find-file")
  "F" '(counsel-recentf :which-key "recentf")
  "s" '(save-buffer :which-key "save-buffer")
  "d" '(j/delete-file-and-buffer :which-key "j/delete-file-and-buffer")
  "S" '(write-file :which-key "write-file"))

Algunas opciones de menú de archivo requieren funciones auxiliares:

(defun j/delete-file-and-buffer ()
  "Eliminar el archivo actual del disco duro y cierra su buffer"
  (interactive)
  (let ((filename (buffer-file-name)))
    (if filename
        (if (y-or-n-p (concat "Do you really want to delete file " filename " ?"))
            (progn
              (delete-file filename)
              (message "Deleted file %s." filename)
              (kill-buffer)))
      (message "Not a file visiting buffer!"))))

Operaciones sobre ventanas

(leader
  :infix "w"
  "" '(:ignore t :which-key "window")
  "d" '(evil-window-delete :which-key "evil-window-delete")
  "s" '(evil-window-split :which-key "evil-window-split")
  "<" '(evil-window-decrease-width :which-key "reducir ancho")
  ">" '(evil-window-increase-width :which-key "aumentar ancho")
  "j" '(evil-window-down :which-key "evil-window-down")
  "q" '(evil-quit-all :which-key "salir de emacs")
  "k" '(evil-window-up :which-key "evil-window-up")
  "h" '(evil-window-left :which-key "evil-window-left")
  "l" '(evil-window-right :which-key "evil-window-right")
  "o" '(delete-other-windows :which-key "delete-other-window")
  "TAB" '(evil-window-next :which-key "evil-window-next")
  "v" '(evil-window-vsplit :which-key "evil-window-vsplit"))

Operaciones sobre buffers

(leader
  :infix "b"
  "" '(:ignore t :which-key "buffer")
  "d" '(kill-this-buffer :which-key "kill-this-buffer")
  "k" '(previous-buffer :which-key "previous-buffer")
  "-" '(text-scale-adjust :which-key "reducir fuente")
  "+" '(text-scale-adjust :which-key "aumentar fuente")
  "r" '(revert-buffer :which-key "revert-buffer")
  "v" '(visual-line-mode :which-key "visual-line-mode")
  "b" '(counsel-switch-buffer :which-key "ivy-switch-buffer")
  "l" '(evil-switch-to-windows-last-buffer :which-key "evil-switch-to-windows-last-buffer")
  "j" '(next-buffer :which-key "next-buffer"))

Projectile

(leader
  "p" '(:keymap projectile-command-map :which-key "projectile"))

Menú de ayuda

(leader
  :infix "h"
  "" '(:ignore t :which-key "Help")
  "m" '(describe-mode :which-key "describe-mode")
  "f" '(counsel-describe-function :which-key "describe-function")
  "v" '(counsel-describe-variable :which-key "describe-veariable")
  "K" '(describe-key-briefly :which-key "describe-key-briefly")
  "w" '(where-is :which-key "where-is")
  "F" '(counsel-describe-face :which-key "describe-face")
  "c" '(j/abrir-config :which-key "j/abrir-config")
  "k" '(helpful-key :which-key "describe-key"))

Para completar algunas de las opciones definidas en el menú de ayuda se necesitan funciones auxiliares:

(defun j/abrir-config ()
  "Abre el archivo de configuración"
  (interactive)
  (find-file "~/.emacs"))

Operaciones sobre modos de edición

(leader
  "SPC" '(evil-normal-state :which-key "evil-normal-state"))

Inserción de plantilla con yasnippet

(leader
  "y" '(yas-insert-snippet :which-key "insertar plantilla"))

Edición

(leader
  :infix "e"
  "" '(:ignore t :which-key "edición")
  "e" '(j/zen-mode :which-key "Zen mode"))

Movimiento

Mapa de movimiento general (cualquier modo)

(general-define-key
 :states '(normal)
 :infix "g"
 "h" '(evil-beginning-of-line :which-key "evil-beginning-of-line")
 "J" '(evil-goto-first-line :which-key "evil-goto-first-line")
 "K" '(end-of-buffer :which-key "end-of-buffer")
 "l" '(evil-end-of-line :which-key "evil-end-of-line"))

Menú de accesos directos

(leader
  :infix "o"
  "" '(:ignore t :which-key "open")
  "a" '(org-agenda :which-key "agenda")
  "d" '(dired :which-key "dired")
  "b" '(eshell :which-key "eshell")
  "t" '(org-todo-list :which-key "full TO-DO list"))

Acordes locales

dashboard

(general-define-key
 :states '(normal insert emacs)
 :keymaps 'dashboard-mode-map
 ;; Marcas 
 "r" '(j/dashboard-goto-recent-files :which-key "archivos recientes")
 "m" '(j/dashboard-goto-bookmarks :which-key "bookmarks")
 "p" '(j/dashboard-goto-projects :which-key "projects")
 "a" '(j/dashboard-goto-agenda :which-key "agenda"))

(defun j/dashboard-goto-recent-files ()
  "Go to recent files."
  (interactive)
  (funcall (local-key-binding "r")))

(defun j/dashboard-goto-projects ()
  "Go to projects."
  (interactive)
  (funcall (local-key-binding "p")))

(defun j/dashboard-goto-bookmarks ()
  "Go to bookmarks."
  (interactive)
  (funcall (local-key-binding "m")))

(defun j/dashboard-goto-agenda ()
  "Go to agenda."
  (interactive)
  (funcall (local-key-binding "a")))

helpful

(general-define-key
 :states '(normal insert emacs)
 :keymaps '(helpful-mode-map)
 "g" '(helpful-update :which-key "helpful-update")
 "RET" '(helpful-visit-reference :which-key "seguir referencia")
 "q" '(kill-buffer-and-window :which-key "salir"))

ibuffer

(use-package ibuffer
   :config
   (add-to-list 'evil-normal-state-modes
           'ibuffer-mode))

Org-mode

Orgmode es mi herramienta principal de trabajo. Lo uso para gestión de tareas, conocimiento, construcción de documentos, entre otros.

Configuración básica de orgmode

(setq org-return-follows-link t ; RET se usa para seguir links en orgmode
      org-startup-folded t ;Colapsar contenido al abrir un archivo .org
      org-startup-align-all-table t ; Empezar con las tablas colapsadas
      org-startup-indented t ; Activar org-indent-mode por defecto 
      org-tags-column 0) ; Quitar espacio entre título y etiquetas
(add-hook 'org-mode-hook ; Desactivar electric-indent-local-mode en orgmode
          (lambda () (electric-indent-local-mode -1)))
(setf (alist-get 'file org-link-frame-setup) #'find-file) ; Abrir links en la misma ventana

Estados de tareas personalizados

(setq org-todo-keywords
      '((sequence "TODO(t)" "IDEA(i)" "NEXT(n)" "ESPE(e)" "PROY(p)" "|" "DONE(d)")
        (sequence "|" "CANC(c)" "FUTU(f)")))
(setq org-todo-keyword-faces
      '(("IDEA" . (:foreground "#268bd2" :weight bold))
        ("PROY" . (:foreground "#d33682" :weight bold))
        ("NEXT" . (:foreground "#dc322f" :weight bold))
        ("ESPE" . (:foreground "#b58900" :weight bold))
        ("DONE" . (:foreground "#859900" :weight bold))
        ("CANC" . (:foreground "#859900" :weight bold))
        ("FUTU" . (:foreground "#2aa198" :weight bold))
        ("TODO" . (:foreground "#6c71c4" :weight bold))))

Definición de etiquetas

(setq org-tag-persistent-alist
      '(("@Casa" . ?c)
        ("@Oficina" . ?o)
        ("@PC" . ?p)
        ("@Internet" . ?i)
        ("@Lectura" . ?l)
        ("@Calle" . ?k)
        ("@Noche" . ?h)
        ("@Transmi" . ?t)
        ("#Docencia" . ?d)
        ("#Carrera" . ?u)
        ("#DevP" . ?v)
        ("#ProyPer" . ?y)
        ("#Ciclismo" . ?f)
        ("#IngresoAdicional" . ?s)
        ("#Puntos" . ?n)
        ("Urgente" . ?g)
        ("Corta" . ?r)
        ("PasarBalon" . ?b)))

Autocompletar rutas en links

Se puede usar company mode para completar rutas al crear links en orgmode. Sin embargo, pasando el argumento universal a org-insert-link se logra un resultado igual o mejor porque puedo buscar de forma más inteligente.

;(with-eval-after-load org (set-company-backend! 'org-mode 'company-files))

Bitácora de tareas repetidas

Cada vez que marco como completada (o cancelada) una tarea con repetición, se guarda una línea de cambio de estado en el cuerpo de la tarea. Para que estas líneas de cambio de estado se guarden dentro de un drawer (logrando tareas más limpias), se modifica el valor de la variable org-log-into-drawer. Esta variable acepta como parámetro el nombre del cajón en donde se quiere guardar las líneas de cambio de estado. También acepta t, usando LOGBOOK como nombre por defecto del cajón.

(setq org-log-into-drawer "BITÁCORA")
                                        ;(setq org-log-into-drawer t)

Indentación de bloques de código

(setq org-src-tab-acts-natively t)

Habilitar listas ordenadas con letras

Orgmode no permite, por defecto, usar letras para los elementos de listas ordenadas. Para activar este compratamiento, hay que asignar la variable org-list-allow-alphabetical. Como ya se tiene cargado orgmode, hay que ejecutar org-element-update-syntax para que el cambio surja efecto.

(setq org-list-allow-alphabetical t)
(org-element-update-syntax)

Habilitar org-table-header-line-mode

org-table-header-line-mode fue incluido en org 9.4 y sirve para mostrar, de forma permanente, la primera fila con texto de una tabla a medida que se navega. Es equivalente a congelar la primera fila en editores como LibreOffice calc.

(setq org-table-header-line-p t)

org-superstar-mode

org-superstar es un paquete que mejora el aspecto visual de archivos org. En concreto, lo que hace es

(use-package org-superstar
  :config
  (add-hook 'org-mode-hook
            (lambda ()
              (org-superstar-mode 1))))

Org-refile

Configuración necesaria para que org-refile tome en cuenta elementos hasta de nivel 4 en el archivo actual.

;; Destinos hasta de nivel 3
(setq org-refile-targets '((org-agenda-files :maxlevel . 3)))
;; Construcción del destino paso a paso
(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)

Hábitos

Para hacer definición y rastreo de hábitos uso un módulo de org-mode llamado org-habit. Se usa ol-habit en lugar de org-habit porque desde la versión 9.3 de org se cambió el nombre de los paquetes.

(add-to-list 'org-modules 'ol-habit)

org-edna

org-edna ofrece funcionalidades para generar dependencias entre tareas en org. Lo hace a través de definir disparadores (trigger) y bloqueadores (blocker) en las tareas, y una serie de acciones muy completa. Los triggers se evaluan cada vez que una actividad se marca como completa. Cada trigger debe tener un espacio de aplicación y una acción a ejecutar en cada tarea que esté dentro del espacio de aplicación.

(use-package org-edna
  :config
  (org-edna-mode))

Prioridades

;; Prioridades de A a D
(setq org-highest-priority ?A)
(setq org-default-priority ?D)
(setq org-lowest-priority ?D)
;; Colores para las prioridades
(setq org-priority-faces '((?A . (:foreground "#dc322f" :weight bold))
                           (?B . (:foreground "#b58900" :weight bold))
                           (?C . (:foreground "#2aa198"))
                           (?D . (:foreground "#859900"))))

Acordes locales de org-mode

Baśicos

(local-leader
  :states '(normal insert emacs)
  :keymaps 'org-mode-map
  "t" '(org-todo :which-key "change TODO state")
  "a" '(org-archive-subtree-default :which-key "archieve subtree")
  "e" '(org-export-dispatch :which-key "export")
  "p" '(org-priority :which-key "set priority")
  "w" '(org-copy-special :which-key "org-copy-special")
  "D" '(org-insert-drawer :which-key "org-insert-drawer")
  "y" '(org-paste-special :which-key "org-paste-special")
  "q" '(org-set-tags-command :which-key "set tags")
  "r" '(org-refile :which-key "refile")
  "o" '(org-set-property :which-key "set property"))

(general-define-key
 :states '(normal insert emacs)
 :keymaps 'org-mode-map
 "TAB" '(org-cycle :which-key "org-cycle"))     (general-define-key
 :states '(normal)
 :keymaps 'org-mode-map
 "RET" '(j/dwim-at-point :which-key "org-return"))

De agenda

Para que org-agenda arranque en modo visual de evil, tenemos que agregarlo a evil-visual-state-modes.

(add-to-list 'evil-normal-state-modes 'org-agenda-mode)

También es importante deshabilitar org-super-agenda-map para evitar bindings por defecto en encabezados de org-super-agenda. En particular, si no se deshabilita esto, no se pueden usar los bindigs para movimiento de evil en encabezados de org-super-agenda.

(setq org-super-agenda-header-map (make-sparse-keymap))
(general-define-key
 :states '(normal insert emacs)
 :keymaps 'org-agenda-mode-map
 "i" '(org-agenda-clock-in :which-key "Clock-in")
 "o" '(org-agenda-clock-out :which-key "Clock-out")
 "g" '(org-agenda-clock-goto :which-key "Go to clocked task")
 "c" '(org-agenda-clock-cancel :which-key "Cancel clock")
 "e" '(org-agenda-set-effort :which-key "Set effort estimate")
 "d" '(org-agenda-entry-text-mode :which-key "Display task text")
 "t" '(org-agenda-todo :which-key "change TODO state")
 "q" '(org-agenda-quit :which-key "quit")
 "RET" '(org-agenda-switch-to :which-key "go to task")
 "a" '(org-agenda-archive :which-key "archieve")
 "Q" '(org-agenda-set-tags :which-key "set tags")
 "r" '(org-agenda-redo :which-key "refresh agenda")
 "s" '(org-save-all-org-buffers :which-key "save org buffers")
 "I" '(org-clock-in-last :which-key "Clock in last"))

De reloj

(local-leader
  :states '(normal insert emacs)
  :keymaps 'org-mode-map
  :infix "c"
  "" '(:ignore t :which-key "Clock functions")
  "i" '(org-clock-in :which-key "Clock-in")
  "o" '(org-clock-out :which-key "Clock-out")
  "g" '(org-clock-goto :which-key "Go to clocked task")
  "c" '(org-clock-cancel :which-key "Cancel clock")
  "d" '(org-clock-display :which-key "display clocked time")
  "e" '(org-set-effort :which-key "Set effort estimate")
  "E" '(org-clock-modify-effort-estimate :which-key "Modify effort")
  "I" '(org-clock-in-last :which-key "Clock in last"))

De pie de página

(local-leader
  :states '(normal insert emacs)
  :keymaps 'org-mode-map
  :infix "f"
  "" '(:ignore t :which-key "Pie de página")
  "f" '(org-footnote-new :which-key "agregar pie de página")
  "n" '(org-footnote-normalize :which-key "normalizar pie de página"))

De enlace

(local-leader
  :states '(normal insert emacs)
  :keymaps 'org-mode-map
  :infix "l"
  "" '(:ignore t :which-key "link functions")
  "l" '(org-insert-link :which-key "org-insert-link")
  "o" '(org-open-at-point :which-key "org-open-at-point"))

De calendario

(local-leader
  :states '(normal insert emacs)
  :keymaps 'org-mode-map
  :infix "d"
  "" '(:ignore t :which-key "date functions")
  "d" '(org-deadline :which-key "org-deadline")
  "s" '(org-schedule :which-key "org-schedule")
  "f" '(j/org-set-futu :which-key "j/org-set-futu") 
  "t" '(org-time-stamp-inactive :which-key "org-time-stamp-inactive"))

De movimiento

(general-define-key
 :states '(normal)
 :keymaps 'org-mode-map
 "H" '(outline-up-heading :which-key "go to parent heading")
 "j" '(evil-next-visual-line :which-key "evil-next-visual-line")
 "k" '(evil-previous-visual-line :which-key "evil-previous-visual-line")
 "J" '(evil-next-line :which-key "evil-next-line")
 "K" '(evil-previous-line :which-key "evil-previous-line"))

De tabla

(local-leader
  :states '(normal insert emacs)
  :keymaps 'org-mode-map
  :infix "b"
  "" '(:ignore t :which-key "date functions")
  "c" '(org-table-convert :which-key "org-table-convert")
  "TAB" '(org-table-shrink :which-key "org-table-shrink")
  "d" '(org-edit-special :which-key "org-edit-special"))

;; (local-leader
;;   :states '(normal insert emacs)
;;   :keymaps 'table-cell-map
;;   :infix "b"
;;   "" '(:ignore t :which-key "table functions")
;;   "p" '(table-span-cell :which-key "table-span-cell")
;;   "f" '(table-justify :which-key "table-justify")
;;   "j" '(table-heighten-cell :which-key "table-heighten-cell")
;;   "k" '(table-shorten-cell :which-key "table-shoren-cell")
;;   "s" '(table-split-cell-horizontally :which-key "table-split-cell-horizontally")
;;   "v" '(table-split-cell-vertically :which-key "table-split-cell-vertically")
;;   "i" '(table-insert-row-column :which-key "table-insert-row-column")
;;   "<" '(table-narrow-cell :which-key "table-narrow-cell")
;;   ">" '(table-widen-cell :which-key "table-widen-cell"))

dwim

La siguiente función dwim (do what I mean) se toma del código de Doom. Se le modifica el nombre para conservar convención de nombramiento de funciones propias.

(defun j/dwim-at-point (&optional arg)
  "Do-what-I-mean at point.
If on a:
- checkbox list item or todo heading: toggle it.
- clock: update its time.
- headline: cycle ARCHIVE subtrees, toggle latex fragments and inline images in
  subtree; update statistics cookies/checkboxes and ToCs.
- footnote reference: jump to the footnote's definition
- footnote definition: jump to the first reference of this footnote
- table-row or a TBLFM: recalculate the table's formulas
- table-cell: clear it and go into insert mode. If this is a formula cell,
  recaluclate it instead.
- babel-call: execute the source block
- statistics-cookie: update it.
- latex fragment: toggle it.
- link: follow it
- otherwise, refresh all inline images in current tree."
  (interactive "P")
  (let* ((context (org-element-context))
         (type (org-element-type context)))
    ;; skip over unimportant contexts
    (while (and context (memq type '(verbatim code bold italic underline strike-through subscript superscript)))
      (setq context (org-element-property :parent context)
            type (org-element-type context)))
    (pcase type
      (`headline
       (cond ((memq (bound-and-true-p org-goto-map)
                    (current-active-maps))
              (org-goto-ret))
             ((and (fboundp 'toc-org-insert-toc)
                   (member "TOC" (org-get-tags)))
              (toc-org-insert-toc)
              (message "Updating table of contents"))
             ((string= "ARCHIVE" (car-safe (org-get-tags)))
              (org-force-cycle-archived))
             ((or (org-element-property :todo-type context)
                  (org-element-property :scheduled context))
              (org-todo
               (if (eq (org-element-property :todo-type context) 'done)
                   (or (car (+org-get-todo-keywords-for (org-element-property :todo-keyword context)))
                       'todo)
                 'done))))
       ;; Update any metadata or inline previews in this subtree
       (org-update-checkbox-count)
       (org-update-parent-todo-statistics)
       (when (and (fboundp 'toc-org-insert-toc)
                  (member "TOC" (org-get-tags)))
         (toc-org-insert-toc)
         (message "Updating table of contents"))
       (let* ((beg (if (org-before-first-heading-p)
                       (line-beginning-position)
                     (save-excursion (org-back-to-heading) (point))))
              (end (if (org-before-first-heading-p)
                       (line-end-position)
                     (save-excursion (org-end-of-subtree) (point))))
              (overlays (ignore-errors (overlays-in beg end)))
              (latex-overlays
               (cl-find-if (lambda (o) (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay))
                           overlays))
              (image-overlays
               (cl-find-if (lambda (o) (overlay-get o 'org-image-overlay))
                           overlays)))
         (+org--toggle-inline-images-in-subtree beg end)
         (if (or image-overlays latex-overlays)
             (org-clear-latex-preview beg end)
           (org--latex-preview-region beg end))))

      (`clock (org-clock-update-time-maybe))

      (`footnote-reference
       (org-footnote-goto-definition (org-element-property :label context)))

      (`footnote-definition
       (org-footnote-goto-previous-reference (org-element-property :label context)))

      ((or `planning `timestamp)
       (org-follow-timestamp-link))

      ((or `table `table-row)
       (if (org-at-TBLFM-p)
           (org-table-calc-current-TBLFM)
         (ignore-errors
           (save-excursion
             (goto-char (org-element-property :contents-begin context))
             (org-call-with-arg 'org-table-recalculate (or arg t))))))

      (`table-cell
       (org-table-blank-field)
       (org-table-recalculate arg)
       (when (and (string-empty-p (string-trim (org-table-get-field)))
                  (bound-and-true-p evil-local-mode))
         (evil-change-state 'insert)))

      (`babel-call
       (org-babel-lob-execute-maybe))

      (`statistics-cookie
       (save-excursion (org-update-statistics-cookies arg)))

      ((or `src-block `inline-src-block)
       (org-babel-execute-src-block arg))

      ((or `latex-fragment `latex-environment)
       (org-latex-preview arg))

      (`link
       (let* ((lineage (org-element-lineage context '(link) t))
              (path (org-element-property :path lineage)))
         (if (or (equal (org-element-property :type lineage) "img")
                 (and path (image-type-from-file-name path)))
             (+org--toggle-inline-images-in-subtree
              (org-element-property :begin lineage)
              (org-element-property :end lineage))
           (org-open-at-point arg))))

      ((guard (org-element-property :checkbox (org-element-lineage context '(item) t)))
       (let ((match (and (org-at-item-checkbox-p) (match-string 1))))
         (org-toggle-checkbox (if (equal match "[ ]") '(16)))))

      (_
       (if (or (org-in-regexp org-ts-regexp-both nil t)
               (org-in-regexp org-tsr-regexp-both nil  t)
               (org-in-regexp org-link-any-re nil t))
           (call-interactively #'org-open-at-point)
         (+org--toggle-inline-images-in-subtree
          (org-element-property :begin context)
          (org-element-property :end context)))))))

(defun +org--toggle-inline-images-in-subtree (&optional beg end refresh)
  "Refresh inline image previews in the current heading/tree."
  (let ((beg (or beg
                 (if (org-before-first-heading-p)
                     (line-beginning-position)
                   (save-excursion (org-back-to-heading) (point)))))
        (end (or end
                 (if (org-before-first-heading-p)
                     (line-end-position)
                   (save-excursion (org-end-of-subtree) (point)))))
        (overlays (cl-remove-if-not (lambda (ov) (overlay-get ov 'org-image-overlay))
                                    (ignore-errors (overlays-in beg end)))))
    (dolist (ov overlays nil)
      (delete-overlay ov)
      (setq org-inline-image-overlays (delete ov org-inline-image-overlays)))
    (when (or refresh (not overlays))
      (org-display-inline-images t t beg end)
      t)))

(defun +org-get-todo-keywords-for (&optional keyword)
  "Returns the list of todo keywords that KEYWORD belongs to."
  (when keyword
    (cl-loop for (type . keyword-spec)
             in (cl-remove-if-not #'listp org-todo-keywords)
             for keywords =
             (mapcar (lambda (x) (if (string-match "^\\([^(]+\\)(" x)
                                     (match-string 1 x)
                                   x))
                     keyword-spec)
             if (eq type 'sequence)
             if (member keyword keywords)
             return keywords)))

Footnotes

[fn:2] Las líneas de comentario se agregan para tener una explicación de la tarea cron y cuándo se va a ejecutar.

[fn:1] Se recomienda usar la aplicación movil Fing para encontrar el ip asignado al RPi en la red local.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published