Trayendo a mi Emacs del pasado

Translations: en - Tags: emacs

Empecé a usar Emacs en 1995, y desde ese entonces vengo cargando un .emacs que ya tiene mucho tiradero acumulado. Es una configuración tan vieja que ni siquiera usa la convención moderna de ~/.emacs.d/init.el (y que parece que en una nueva versión de Emacs lo van a mover a .config/emacs para seguir el estándar de XDG... por fin).

Desde hace tiempo que quiero cambiar mi configuración de Emacs para ponerle juguetes lindos y modernos.

Lo que más me importa es esto:

  • No tener un tiradero en .emacs si es posible.
  • Colores bonitos.
  • Magit.
  • Rust-mode o lo nuevo para rust-analyzer con el Language Server.

Después de ver varios ejemplos de configuración que mencionan use-package como una forma unificada de cargar paquetes y configurarlos, me encontré con esta configuración que está súper bien documentada. Hacen varias monerías con org-mode para hacer "programación literaria" sobre la configuración de emacs — cosa que me interesa como curiosidad, no para aplicarla ahora mismo — pero así le quedó al autor todo muy bien explicado y fácil de leer.

Fui sacando pedacitos de esa configuración y acabé con lo siguiente.

Todo en ~/.emacs.d/init.el y con use-package

;; Initialize package system

(require 'package)

(setq package-archives
      '(("org"     .       "https://orgmode.org/elpa/")
        ("gnu"     .       "https://elpa.gnu.org/packages/")
        ("melpa"   .       "https://melpa.org/packages/")))

(package-initialize)
;(package-refresh-contents)

;; Use-package for civilized configuration

(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(require 'use-package)

(setq use-package-always-ensure t)

~/.emacs.d/custom.el para lo de M-x customize

;; Set customization data in a specific file, without littering
;; my init files.

(setq custom-file "~/.emacs.d/custom.el")
(load custom-file)

Which-key para tener ayuda con los prefijos de comandos

;; Make it easier to discover key shortcuts

(use-package which-key
  :diminish
  :config
  (which-key-mode)
  (which-key-setup-side-window-bottom)
  (setq which-key-idle-delay 0.1))

No contaminar el modeline para los modos comunes

;; Do not show some common modes in the modeline, to save space

(use-package diminish
  :defer 5
  :config
  (diminish 'org-indent-mode))

Magit para usar git de forma civilizada

;; Magit

(use-package magit
  :config
  (global-set-key (kbd "C-x g") 'magit-status))

Moverse entre ventanas con Shift-flechas

;; Let me switch windows with shift-arrows instead of "C-x o" all the time
(windmove-default-keybindings)

Colores bonitos

Usaba solarized-dark pero este me gustó aun más.

;; Pretty colors

(use-package flatland-theme
  :config
  (custom-theme-set-faces 'flatland
   '(show-paren-match ((t (:background "dark gray" :foreground "black" :weight bold))))
   '(show-paren-mismatch ((t (:background "firebrick" :foreground "orange" :weight bold))))))

Nyan cat en vez de barras de desplazamiento

;; Nyan cat instead of scrollbar
;; scroll-bar-mode is turned off in custom.el

(use-package nyan-mode
  :config
  (nyan-mode 1))

Mover buffers a ventanas adyacentes

;; Move buffers between windows

(use-package buffer-move
  :config
  (global-set-key (kbd "<C-S-up>")     'buf-move-up)
  (global-set-key (kbd "<C-S-down>")   'buf-move-down)
  (global-set-key (kbd "<C-S-left>")   'buf-move-left)
  (global-set-key (kbd "<C-S-right>")  'buf-move-right))

Cambiar el nombre de buffers con archivos con el mismo nombre

;; Note that ‘uniquify’ is builtin.
(require 'uniquify)
(setq uniquify-separator "/"               ;; The separator in buffer names.
      uniquify-buffer-name-style 'forward) ;; names/in/this/style

Helm para auto-completar a lo grande

(use-package helm
 :diminish
 :init (helm-mode t)
 :bind (("M-x"     . helm-M-x)
        ("C-x C-f" . helm-find-files)
        ("C-x b"   . helm-mini)     ;; See buffers & recent files; more useful.
        ("C-x r b" . helm-filtered-bookmarks)
        ("C-x C-r" . helm-recentf)  ;; Search for recently edited files
        ("C-c i"   . helm-imenu)
        ("C-h a"   . helm-apropos)
        ;; Look at what was cut recently & paste it in.
        ("M-y" . helm-show-kill-ring)

        :map helm-map
        ;; We can list ‘actions’ on the currently selected item by C-z.
        ("C-z" . helm-select-action)
        ;; Let's keep tab-completetion anyhow.
        ("TAB"   . helm-execute-persistent-action)
        ("<tab>" . helm-execute-persistent-action)))

Ripgrep para buscar a lo grande

;; Ripgrep

(use-package rg
  :config
  (global-set-key (kbd "M-s g") 'rg)
  (global-set-key (kbd "M-s d") 'rg-dwim))

(use-package helm-rg)

Modo de Rust y el Language Server

Ahora que RLS está en proceso de volverse obsoleto, se va a sustituir con rust-analyzer. También se sustituye rust-mode por rustic.

;; Rustic, LSP

(use-package flycheck)

(use-package rustic)

(use-package lsp-ui)

(use-package helm-lsp
  :config
  (define-key lsp-mode-map [remap xref-find-apropos] #'helm-lsp-workspace-symbol))

Hacer como que no quiero distraerme

;;; Show a notification when compilation finishes

(setq compilation-finish-functions
      (append compilation-finish-functions
          '(fmq-compilation-finish)))

(defun fmq-compilation-finish (buffer status)
  (when (not (member mode-name '("Grep" "rg")))
    (call-process "notify-send" nil nil nil
          "-t" "0"
          "-i" "emacs"
          "Compilation finished in Emacs"
          status)))

Lo de custom.el

Aquí lo interesante es hacer que LSP funcione; lo demás son preferencias.

(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(column-number-mode t)
 '(custom-safe-themes
   (quote
    ("2540689fd0bc5d74c4682764ff6c94057ba8061a98be5dd21116bf7bf301acfb" "bffa9739ce0752a37d9b1eee78fc00ba159748f50dc328af4be661484848e476" "0fffa9669425ff140ff2ae8568c7719705ef33b7a927a0ba7c5e2ffcfac09b75" "2809bcb77ad21312897b541134981282dc455ccd7c14d74cc333b6e549b824f3" default)))
 '(delete-selection-mode nil)
 '(lsp-rust-analyzer-display-chaining-hints t)
 '(lsp-rust-analyzer-display-parameter-hints nil)
 '(lsp-rust-analyzer-macro-expansion-method (quote rustic-analyzer-macro-expand))
 '(lsp-rust-analyzer-server-command (quote ("/home/federico/.cargo/bin/rust-analyzer")))
 '(lsp-rust-analyzer-server-display-inlay-hints nil)
 '(lsp-rust-full-docs t)
 '(lsp-rust-server (quote rust-analyzer))
 '(lsp-ui-doc-alignment (quote window))
 '(lsp-ui-doc-position (quote top))
 '(lsp-ui-sideline-enable nil)
 '(menu-bar-mode nil)
 '(package-selected-packages
   (quote
    (helm-lsp lsp-ui lsp-mode flycheck rustic rg helm-rg ripgrep helm-projectile helm buffer-move nyan-mode flatland-black-theme flatland-theme afternoon-theme spacemacs-theme solarized-theme magit diminish which-key use-package)))
 '(rustic-lsp-server (quote rust-analyzer))
 '(scroll-bar-mode nil)
 '(scroll-step 0)
 '(tool-bar-mode nil))
(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 )

Resultados

Estoy muy contento con rustic / rust-analyzer y el Language Server. Eso de tener documentación sobre cada cosa cuando mueves el cursor en el código es algo que parecía que nunca iba a funcionar bien en Emacs. Todavía no me decido si M-x lsp-rust-analyzer-inlay-hints-mode es maravilloso o si me vuelve loco; te muestra los nombres de los argumentos de función y los tipos inferidos, intercalados con el código. Supongo que lo apagaré o encenderé según sea necesario.

Hace unos días, antes de usar helm, tenía puesto projectile-mode para trabajar con checkouts de git y me estaba gustando. Todavía no encuentro cómo configurar helm-projectile para que funcione; hay que seguir experimentando.