48 KiB
GNU Emacs configuration
[ Ultima revisione ed esportazione il {{{latest-export-date}}} con un conteggio di parole {{{word-count}}}. ]
Note generali
Rifacimento della configurazione partendo essenzialmente da:
Questo è il mio file di configurazione di Emacs in forma letterale. È una combinazione di prosa e codice.
Quello che state leggendo non è una configurazione standard di Emacs. Nella
maggior parte dei casi, si ha un file generico init.el con una chiamata alla
funzione org-babel-load-file che riceve come valore un documento Org. Questo
metodo funziona, ma è molto lento, perché dobbiamo caricare Org prima di avviare
Emacs (e Org carica anche un sacco di altre cose di cui non abbiamo bisogno in
una fase così iniziale).
Questo documento Org, invece, funge da (i) punto di accesso unico alla mia configurazione di Emacs e (ii) origine di tutte le mie configurazioni di Emacs.
Anche se definisco tutto in un singolo file Org, in realtà non avvio Emacs leggendo questo file. Piuttosto, sto istruendo Org a inserire i blocchi di codice qui definiti in file separati, organizzati per ambito. Il risultato finale è qualcosa per cui non si può capire se è stato eseguito un programma letterale o meno.
Questa è la bellezza di tutto ciò. Posso continuare a modificare un singolo file come "fonte di verità", pur potendo gestire ogni file singolarmente (ad esempio, se qualcuno vuole vedere come eseguo una determinata operazione, condivido solo quel file come allegato e-mail, senza bisogno di inviare l'intero documento).
Quando voglio modificare la mia configurazione di Emacs, modifico questo file e
poi valuto il blocco di codice seguente oppure eseguo C-c C-v C-t. Tutti i
file verranno aggiornati di conseguenza.
(org-babel-tangle)
Anatomia della mia configurazione di Emacs
Quello che stai leggendo ora è il file prot-emacs.org. È il documento che
genera - e quindi controlla - tutti gli altri file che sono alla base della mia
configurazione di Emacs.
Questo file Org non viene caricato direttamente. Il suo unico scopo è quello di produrre i file che svolgono il lavoro effettivo. Questi file sono organizzati in base al loro scopo e alla loro funzione:
-
early-init.el - Il file caricato prima di lanciare emacs, include ottimizzazioni per l'avvio di Emacs e imposta alcune funzionalità di base, come ad esempio evitare il lampeggio della luce all'avvio di Emacs in un ambiente scuro.
-
init.el - Il file standard di configurazione, caricato dopo il lancio di emacs; contiene i blocchi fondamentali del mio sistema, ovvero le macro Lisp che definisco, e carica i singoli moduli di configurazione.
-
emacs-modules - Directory che include tutti i miei moduli di
configurazione. Ogni modulo riguarda un tipo specifico di funzionalità, come
ad esempio
emacs-theme.elper i temi eemacs-essentials.elper gli strumenti di base. Questi moduli di configurazione modificano i pacchetti e non sono pensati per definire funzionalità aggiuntive. -
my-lisp - La directory in cui si trovano le librerie personalizzate che gestisco come parte della mia configurazione di Emacs. Sono scritte in conformità con le migliori pratiche per il packaging di Emacs Lisp, ma sono destinate esclusivamente all'uso all'interno della mia configurazione. Pertanto, non sono necessariamente allo stesso livello dei pacchetti pubblici che gestisco per Emacs: <https://protesilaos.com/emacs>.
-
Il file
emacs-pre-custom - Viene valutato prima del caricamento dei moduli. È destinato agli utenti della mia configurazione che desiderano utilizzare le opzioni che fornisco (Le opzioni utente di init.el).
-
Il file
emacs-post-custom - Come il precedente, questo file è destinato agli utenti della mia configurazione. Viene valutato dopo che il resto della mia configurazione è stato caricato. Gli utenti possono includere qualsiasi codice desiderino in questo file per sovrascrivere funzionalità esistenti o definirne di nuove.
-
Il file
My-emacs-config.org - Il codice sorgente di ciò che stai leggendo.
Ecco una rappresentazione schematica di questa struttura di directory (i file mostrati qui potrebbero non riflettere lo stato più recente del progetto):
~/.emacs.d $ tree -aF
./
├── early-init.el
├── init.el
├── emacs-modules/
│ ├── emacs-dired.el
│ ├── emacs-essentials.el
│ ├── emacs-icons.el
│ ├── emacs-org.el
│ ├── emacs-vertico.el
├── emacs-post-custom.el
├── emacs-pre-custom.el
├── My-emacs-config.org
└── my-lisp/
├── dired.el
├── embark.el
├── marginalia.el
├── orderless.el
├── org.el
Il motivo per cui utilizzo questa configurazione modulare è che mi permette di
eseguire il debug più facilmente, ma anche di condividere singoli frammenti di
codice con altri. Il file My-emacs-config.org non rappresenta un ostacolo in
questo senso: fornisce un ulteriore modo per condividere il mio lavoro, sotto
forma di questa visualizzazione consolidata che vedete ora.
Quando voglio apportare una modifica alla mia configurazione di Emacs, effettuo
le modifiche in My-emacs-config.org e poi digito C-c C-v C-t (M-x
org-babel-tangle) per propagare le modifiche ai file pertinenti.
Ho creato la mia configurazione da zero e seguo le migliori pratiche per quanto riguarda il funzionamento previsto da Emacs. Non utilizzo il demone di Emacs, poiché ho riscontrato problemi di instabilità. Invece, eseguo una singola istanza di Emacs e la configuro in modo che funga da server.
Questo significa che posso comunque connettermi alla sessione in esecuzione
tramite emacsclient, il che è utile quando voglio valutare il codice Elisp
dall'esterno di Emacs (ad esempio, con il mio script shell delight.sh che
cambia l'intero tema dell'"ambiente" del mio window manager a riquadri o
dell'ambiente desktop - vedi i miei dotfiles per i dettagli tecnici).
The early initialisation of Emacs (early-init.el)
Questo è il primo file che Emacs legge all'avvio.
Dovrebbe contenere codice che non dipenda da alcun pacchetto o dalle proporzioni
del frame di Emacs. In generale, questo file di inizializzazione iniziale ha lo
scopo di impostare alcune cose di base prima che Emacs produca il frame
iniziale, delegando a init.el il resto della configurazione.
Le impostazioni di base del frame early-init.el
Queste sono alcune impostazioni generali per i frame e le basi del toolkit. In
breve, voglio mantenere le cose al minimo. Notate frame-resize-pixelwise e
frame-inhibit-implied-resize: per impostazione predefinita Emacs ridimensiona
il frame se si modifica la dimensione del carattere, cosa che non voglio mai
fare.
(setq frame-resize-pixelwise t
frame-inhibit-implied-resize 'force
frame-title-format '("%b")
ring-bell-function 'ignore
use-dialog-box t ; only for mouse events, which I seldom use
use-file-dialog nil
use-short-answers t
inhibit-splash-screen t
inhibit-startup-screen t
inhibit-x-resources t
inhibit-startup-echo-area-message user-login-name ; read the docstring
inhibit-startup-buffer-menu t)
Il codice early-init.el per impostare i parametri del frame per desktop
Qui imposto i parametri per le dimensioni della finestra di Emacs: la prima e tutte quelle successive. In un window manager a riquadri, i parametri di larghezza e altezza non sono importanti, poiché tutte le finestre vengono forzatamente adattate a rettangoli (riquadri). Gli altri parametri, invece, sono rilevanti. In un window manager a sovrapposizione, tutte le impostazioni sono importanti.
Il parametro initial-frame-alist si riferisce alla prima finestra che viene
generata all'avvio di Emacs. Per questo motivo deve essere definito nel file
early-init.el: il codice qui presente viene eseguito prima che venga disegnata
la prima finestra.
Il parametro default-frame-alist si riferisce a tutte le finestre
successive. In linea di principio, potremmo definirlo al di fuori del file
early-init.el, ma è più logico averlo qui.
Qui si imposta se utilizzare la barra dei menu, la barra delle icone e le scrollbar:
(menu-bar-mode 1)
(tool-bar-mode -1)
(scroll-bar-mode 1)
(setq initial-frame-alist `((scroll-bar-width . 6)
(width . (text-pixels . 800))
(height . (text-pixels . 900))
(border-width . 0)
))
;; - ;; Ripeti l'operazione dopo l'inizializzazione in modo che eventuali
;; - ;; modifiche intermedie non vengano mantenute. Nota che non possiamo fare
;; - ;; affidamento sull'impostazione di questo valore su
;; - ;; `initial-frame-alist' poiché potrebbe cambiare nel
;; - ;; frattempo. Impostiamo esplicitamente il valore per essere certi del
;; - ;; risultato. Ciò non impedisce ad altri programmi di modificare
;; - ;; l'elenco, anche se lo considererei indesiderabile se modificassero
;; - ;; queste impostazioni specifiche.
;; - (add-hook 'after-init-hook (lambda ()
;; - (add-hook 'after-init-hook (lambda ()
;; - (setq default-frame-alist `((scroll-bar-width . 6)
;; - (width . (text-pixels . 800))
;; - (height . (text-pixels . 900))
;; - (border-width . 0)
;; - )
;; - )))
Le modifiche di early-init.el al tempo di avvio e alla garbage collection
Non ho una conoscenza approfondita della "garbage collection", ma ho imparato per tentativi ed errori che posso massimizzare la soglia durante l'avvio per rendere Emacs un po' più veloce.
Quello che sto facendo qui è impostare i valori rilevanti su valori molto alti durante l'avvio e poi riportarli a qualcosa di più pratico una volta che Emacs ha finito di caricarsi.
;; Temporarily increase the garbage collection threshold. These
;; changes help shave off about half a second of startup time. The
;; `most-positive-fixnum' is DANGEROUS AS A PERMANENT VALUE. See the
;; `emacs-startup-hook' a few lines below for what I actually use.
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.5)
;; Same idea as above for the `file-name-handler-alist' and the
;; `vc-handled-backends' with regard to startup speed optimisation.
;; Here I am storing the default value with the intent of restoring it
;; via the `emacs-startup-hook'.
(defvar prot-emacs--file-name-handler-alist file-name-handler-alist)
(defvar prot-emacs--vc-handled-backends vc-handled-backends)
(setq file-name-handler-alist nil
vc-handled-backends nil)
(add-hook 'emacs-startup-hook
(lambda ()
(setq gc-cons-threshold (* 100 100 8)
gc-cons-percentage 0.1
file-name-handler-alist prot-emacs--file-name-handler-alist
vc-handled-backends prot-emacs--vc-handled-backends)))
Il file early-init.el inizializza la cache dei pacchetti
Utilizzo il file package.el standard per gestire i miei pacchetti Emacs.
;; Initialise installed packages at this early stage, by using the
;; available cache. I had tried a setup with this set to nil in the
;; early-init.el, but (i) it ended up being slower and (ii) various
;; package commands, like `describe-package', did not have an index of
;; packages to work with, requiring a `package-refresh-contents'.
(setq package-enable-at-startup t)
Inizializzazione principale di Emacs (init.el)
Qui definisco le macro Lisp utilizzate nella mia configurazione e carico tutti i singoli moduli.
Opzioni utente di init.el
Definisco diverse opzioni utente per il mio Emacs. Queste mi sono utili se ho bisogno di testare rapidamente qualche aspetto della mia configurazione, anche se le fornisco principalmente a coloro che utilizzano i miei file come base per la propria configurazione.
Tutte le opzioni utente devono essere impostate in un file
prot-emacs-pre-custom.el nella stessa directory del file init.el
;; For those who use my dotfiles and need an easy way to write their
;; own extras on top of what I already load: search below for the files
;; emacs-pre-custom.el and emacs-post-custom.el
(defgroup mrk-emacs nil
"User options for my dotemacs.
These produce the expected results only when set in a file called
emacs-pre-custom.el. This file must be in the same
directory as the init.el."
:group 'file)
Le configurazioni di base di init.el per disabilitare backup e file di blocco
Per impostazione predefinita, Emacs tenta di bloccare i file in modo che non vengano modificati da altri programmi. Mantiene anche dei backup. Queste sono funzionalità di cui non ho bisogno perché (i) se mai dovessi modificare i miei file esternamente, so cosa sto facendo e (ii) tutti i file che mi interessano sono sotto controllo di versione o di cui è stato eseguito il backup su una chiavetta USB.
(setq make-backup-files nil)
(setq backup-inhibited nil) ; Not sure if needed, given `make-backup-files'
(setq create-lockfiles nil)
Le configurazioni di base di init.el per impostare il numero di caratteri
Per impostare la larghezza massima in caratteri (default 70) in modo iterattivo,
si può utilizzare il comando C-x f (fill-colum). Altrimenti per impostare
permanentemente la larghezza massima a 80 caratteri all'avvio di Emacs, aggiungi
questa riga al tuo file di configurazione:
(setq-default fill-column 80)
Le modifiche di init.el per rendere silenziosa la compilazione nativa
L'opzione di compilazione --with-native-compilation=yes di Emacs è molto
utile: abilita la "compilazione nativa" di Emacs Lisp, traducendolo in codice
macchina. Tuttavia, l'impostazione predefinita per la segnalazione degli errori
è impostata su un valore verboso che, nella mia esperienza di coaching, confonde
gli utenti: produce avvisi per problemi di compilazione che solo lo sviluppatore
del pacchetto in questione deve gestire. Questi includono fatti innocui come le
docstring che superano un certo numero di caratteri. A peggiorare ulteriormente
le cose, il buffer che mostra questi avvisi utilizza il carattere di stop,
risultando in un lungo elenco di righe con punti rossi ovunque, come se avessimo
completamente rotto Emacs.
;; Make native compilation silent.
(when (native-comp-available-p)
(setq native-comp-async-report-warnings-errors 'silent))
Impostazione init.el per mandare custom-file nell'oblio:
Per impostazione predefinita, Emacs scrive le personalizzazioni persistenti alla
fine del file di inizializzazione dell'utente. Questi elementi sono incapsulati
in un blocco "personalizzato". Emacs li scrive ogni volta che l'utente esegue
un'azione con M-x customize o comandi simili.
Personalmente, preferisco scrivere direttamente il codice Elisp piuttosto che
utilizzare l'interfaccia di M-x customize. Voglio anche che il mio file di
inizializzazione contenga solo ciò che ho scritto io e che non esegua mai, in
nessun caso, codice che non ho chiamato personalmente.
Pertanto, voglio impostare la variabile custom-file per indicare a Emacs di
scrivere le sue variabili persistenti in quel file anziché nel mio init.el.
Tuttavia, poiché non ho intenzione di utilizzare questo file, ho scelto di
renderlo temporaneo posizionandolo nella directory /tmp (questo è ciò che fa
la funzione make-temp-file). Quindi, quando chiudo il computer, il file
scompare. Infine, non richiamo la funzione load perché non farò mai
affidamento sul custom-file. Preferirei avere un'opzione per evitare
completamente l'infrastruttura Custom, ma non è possibile. Quindi eccoci qui…
;; - ;; Disable the damn thing by making it disposable.
;; - (setq custom-file (make-temp-file "emacs-custom-"))
;; Rendo il file persistente:
(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file :no-error-if-file-is-missing)
Impostazione init.el per eliminare il testo selezionato all'inserimento
Praticamente tutti i programmi eliminano il testo selezionato/evidenziato non appena l'utente digita qualcosa. Emacs non lo fa di default, pur avendo la funzionalità disponibile. Abilitiamola quindi (Configurazione completa):
(use-package delsel
:ensure nil ; no need to install it as it is built-in
:hook (after-init . delete-selection-mode))
Rendere C-g un po' più utile
Avendo notato che i principianti hanno difficoltà con C-g perché non chiude il
minibuffer aperto, so che il seguente miglioramento semplifica l'esperienza
utente:
(defun mrk/keyboard-quit-dwim ()
"Do-What-I-Mean behaviour for a general `keyboard-quit'.
The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open. Whereas we want it to close the
minibuffer, even without explicitly focusing it.
The DWIM behaviour of this command is as follows:
- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
(interactive)
(cond
((region-active-p)
(keyboard-quit))
((derived-mode-p 'completion-list-mode)
(delete-completion-window))
((> (minibuffer-depth) 0)
(abort-recursive-edit))
(t
(keyboard-quit))))
(define-key global-map (kbd "C-g") #'mrk/keyboard-quit-dwim)
Impostazione init.el per gestire i package:
Emacs ha un gestore di pacchetti basilare, ma comunque sufficientemente efficiente. Lo utilizziamo per estendere Emacs con funzionalità fornite dalla community. Emacs include molte funzioni utili, ma senza i pacchetti esterni saremmo in una situazione peggiore. Inoltre, tenete presente che il fatto che un pacchetto sia integrato in Emacs non lo rende necessariamente migliore delle alternative di terze parti.
;; Set the packet manager:
(setq package-archives
'(("gnu-elpa" . "https://elpa.gnu.org/packages/")
("gnu-elpa-devel" . "https://elpa.gnu.org/devel/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
;; Set the repository priority:
;; Highest number gets priority (what is not mentioned has priority 0)
(setq package-archive-priorities
'(("gnu-elpa" . 3)
("melpa" . 2)
("nongnu" . 1)))
Non visualizzare i warning durante il load dei pacchetti:
Quando si installa un nuovo pacchetto, Emacs mostra un buffer contenente tutti gli avvisi generati dal compilatore bytecode (Configura il gestore di pacchetti). Sebbene queste informazioni siano utili per gli sviluppatori, risultano molto confuse per gli utenti. Molte volte ho ricevuto messaggi del tipo "Emacs si è bloccato", quando in realtà gli avvisi riguardano problemi che non interessano l'utente.
Il seguente frammento di codice impedisce la visualizzazione di questi buffer (Configurazione completa). Rimangono comunque disponibili nell'elenco dei buffer, qualora fossero necessari.
(add-to-list 'display-buffer-alist
'("\\`\\*\\(Warnings\\|Compile-Log\\)\\*\\'"
(display-buffer-no-window)
(allow-no-window . t)))
Impostazione init.el per selezionare il tema utilizzato:
La scelta del tema è soggettiva, quindi vi prego di non considerare quanto segue come un'opinione definitiva. Ho semplicemente scelto il tema che mi piace di più. Usatelo come modello per configurare eventualmente un altro tema.
;; Esempio per utilizzare un pacchetto thema esterno
;; - (use-package modus-themes
;; - :ensure t
;; - :config
;; - (load-theme 'modus-vivendi-tinted :no-confirm-loading))
;; Esempio di utilizzo di un thema interno
(load-theme 'dichromacy :no-confirm-loading)
Impostazione init.el per i nerd icon
Proseguendo con i perfezionamenti stilistici di Emacs, il seguente frammento di codice mostrerà icone complementari nel minibuffer e in Dired.
Per far funzionare questa configurazione, l'utente deve digitare M-x e quindi
richiamare il comando nerd-icons-install-fonts. Questo salverà i file dei font
delle icone in una directory locale (su Linux è ~/.local/share/fonts). Per
assicurarsi che tutto funzioni correttamente, riavviare Emacs garantirà che i
nuovi file dei font vengano letti.
(use-package nerd-icons
:ensure t)
(use-package nerd-icons-completion
:ensure t
:after marginalia
:config
(add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))
(use-package nerd-icons-corfu
:ensure t
:after corfu
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package nerd-icons-dired
:ensure t
:hook
(dired-mode . nerd-icons-dired-mode))
Configurazione del minibuffer e delle relative opzioni
[Guarda anche il video sui pacchetti minibuffer moderni, alcuni dei quali non ho incluso qui perché sono un po' più avanzati: https://protesilaos.com/codelog/2024-02-17-emacs-modern-minibuffer-packages/.]
Il minibuffer è una componente fondamentale dell'esperienza Emacs. È il punto in cui l'utente interagisce con Emacs per rispondere ai prompt, passare a un altro buffer, aprire un nuovo file, eseguire un comando per nome completo e così via. Il minibuffer predefinito è minimale, il che potrebbe essere utile per gli utenti esperti, ma non facilita la scoperta delle opzioni.
Per questo motivo, utilizziamo il pacchetto vertico per creare un layout
verticale. Ciò semplifica la visualizzazione delle opzioni disponibili.
;; Load vertigo package
(use-package vertico
:ensure t
:config
(setq vertico-cycle t)
(setq vertico-resize nil)
(vertico-mode 1))
;; Configure directory extension.
(use-package vertico-directory
:after vertico
:ensure nil
;; More convenient directory navigation commands
:bind (:map vertico-map
("RET" . vertico-directory-enter)
("DEL" . vertico-directory-delete-char)
("M-DEL" . vertico-directory-delete-word))
;; Tidy shadowed file names
:hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
Il pacchetto Marginalia è un ottimo complemento al layout verticale, poiché
utilizza lo spazio libero per mostrare informazioni utili sulle opzioni
visualizzate. Ad esempio, quando l'utente digita M-x per visualizzare un
elenco di nomi di comandi, Marginalia aggiungerà una breve descrizione di
ciascun comando. A seconda delle specifiche dell'interazione con il minibuffer
(apertura di un file, selezione di un buffer, ecc.), verranno visualizzate le
informazioni pertinenti.
;; Load marginalia package
(use-package marginalia
:ensure t
:config
(marginalia-mode 1))
Il pacchetto Orderless è una soluzione ideale per tutti quei casi in cui non
ricordiamo l'ordine esatto delle parole. Ad esempio, per attivare/disattivare la
visualizzazione dei numeri di riga nel buffer corrente, utilizziamo il comando
M-x display-line-numbers-mode. Con Orderless configurato, possiamo digitare
qualcosa come li num dis al prompt M-x e ottenere comunque il risultato
desiderato. Questo perché Orderless interpreta i caratteri separati da spazi in
qualsiasi ordine. Emacs include altri stili di corrispondenza di pattern, ma
"orderless" è un buon punto di partenza. Pertanto, ci assicuriamo che le altre
variabili rilevanti siano impostate su un valore nullo, in modo da ottenere
"orderless" ovunque.
;; Load ordeless package
(use-package orderless
:ensure t
:config
(setq completion-styles '(orderless basic))
(setq completion-category-defaults nil)
(setq completion-category-overrides nil))
Il pacchetto consult fornisce numerosi comandi che sono varianti avanzate
delle funzionalità di base integrate. Una delle caratteristiche principali di
consult è la funzione di anteprima, che mostra in un'altra finestra di Emacs il
contesto di ciò che è attualmente corrispondente nel minibuffer.
Qui definisco le scorciatoie da tastiera per alcuni comandi che potrebbero
esservi utili. Il prefisso mnemonico è "alternative search" (in contrapposizione
alle scorciatoie da tastiera di base C-s o C-r).
;; Load consult package
(use-package consult
:ensure t
:bind (;; Search for files names recursively
("M-s M-f" . consult-find)
;; Search through the outline (headings) of the file
("M-s M-o" . consult-outline)
;; Search the current buffer
("M-s M-l" . consult-line)
;; Switch to another buffer, or bookmarked file, or recently opened file.
("M-s M-b" . consult-buffer)
;; Search through history
("M-s M-h" . consult-history)
;; Search through mark
("M-s M-m" . consult-mark)
;; Search through yank
("M-s M-y" . consult-yank-pop)
))
Il comando consult-find non funziona su windows, infatti utilizza il programma
find.exe che però non è quello unix. In particolare con il comando
(executable-find "find")
ritorna il valore
"c:/WINDOWS/system32/find.exe"
indicando che utilizza il find.exe di windows. Come find di unix, voglio utilizzare quello dato già assieme a git. Serve quindi impostare il path corretto, da internet ho trovato il comando
(setq find-program "C:/Program Files/Git/usr/bin/find.exe")
ma non funziona, emacs continua a puntare al find di windows. Procedo quindi a inserire il path del find di unix sul PATH complessivo che utilizza emacs. In questo modo non altero il PATH di windows e quindi sono più sicuro.
;; imposto la directory del find unix:
(setq find-program-path "C:/Program Files/Git/usr/bin")
;; la inserisco nel PATH di emacs:
(push find-program-path exec-path)
;; impostiamo la cartella di default:
(setq default-directory "~/Doc/Org/")
Il pacchetto integrato savehist tiene traccia degli input dell'utente e li
memorizza tra una sessione e l'altra. In questo modo, l'utente vedrà sempre le
sue ultime scelte in cima all'elenco (come con M-x).
;; Load savelist package
(use-package savehist
:ensure nil ; it is built-in
:hook (after-init . savehist-mode))
Il pacchetto corfu fornisce un'interfaccia popup per il completamento
automatico all'interno del buffer. Concettualmente, è simile a quanto
avviene con il minibuffer, sebbene sia pensato per l'espansione del
testo all'interno di un file normale. Viene tipicamente utilizzato
nella programmazione. Ci sono ulteriori funzionalità che potrebbero
renderlo utile per le modalità di programmazione (in particolare, la
configurazione del Language Server Protocol), ma poiché queste
dipendono molto dalle esigenze specifiche di ciascun utente, qui
fornisco solo le funzionalità principali.
(use-package corfu
:ensure t
:hook (after-init . global-corfu-mode)
:bind (:map corfu-map ("<tab>" . corfu-complete))
:config
(setq tab-always-indent 'complete)
(setq corfu-preview-current nil)
(setq corfu-min-width 20)
(setq corfu-popupinfo-delay '(1.25 . 0.5))
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
;; Sort by input history (no need to modify `corfu-sort-function').
(with-eval-after-load 'savehist
(corfu-history-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history)))
Personalizzare il file manager dired per Emacs
Emacs include un potente file manager integrato. Se ne parla a lungo in questo video: Emacs: file and Dired basics.
Le seguenti impostazioni definiscono alcune modifiche di base al comportamento delle operazioni comuni di copia, ridenominazione ed eliminazione dei file.
La visualizzazione predefinita di Dired contiene molti dettagli (la
configurazione completa). Utilizziamo dired-hide-details-mode per
nasconderli. È possibile attivarli/disattivarli in qualsiasi momento con M-x
dired-hide-details-mode, che è associato al tasto ( nei buffer di Dired.
;; Load dired package
(use-package dired
:ensure nil
:commands (dired)
:hook
((dired-mode . dired-hide-details-mode)
(dired-mode . hl-line-mode))
:config
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq delete-by-moving-to-trash t)
(setq dired-dwim-target t))
Il pacchetto dired-subtree fornisce comandi per visualizzare rapidamente il
contenuto di una cartella utilizzando il tasto TAB.
(use-package dired-subtree
:ensure t
:after dired
:bind
( :map dired-mode-map
("<tab>" . dired-subtree-toggle)
("TAB" . dired-subtree-toggle)
("<backtab>" . dired-subtree-remove)
("S-TAB" . dired-subtree-remove))
:config
(setq dired-subtree-use-backgrounds nil))
In precedenza abbiamo configurato Dired per spostare i file eliminati nel
cestino di sistema. Il pacchetto trashed è quindi utile per fornire
un'interfaccia simile a Dired per quella cartella (funziona allo stesso modo di
Dired.
(use-package trashed
:ensure t
:commands (trashed)
:config
(setq trashed-action-confirmer 'y-or-n-p)
(setq trashed-use-header-line t)
(setq trashed-sort-key '("Date deleted" . t))
(setq trashed-date-format "%Y-%m-%d %H:%M:%S"))
Impostazione init.el per la data e l'ora ( display-time-mode)
Mi piace visualizzare la data e l'ora correnti sulla barra di
navigazione. Ecco cosa display-time-mode fa. Nota che la mia barra
di navigazione personalizzata mostra l'ora solo nella finestra
attiva/selezionata. Altrimenti, l'impostazione predefinita è quella di
mostrare l'ora su tutte le barre di navigazione, il che è fastidioso.
La time.el libreria che fornisce display-time-mode definisce anche
funzioni per ottenere il carico medio e controllare una directory per
nuove email. Non ho bisogno di questo e quindi lo disattivo.
(setq display-time-format " %a %e %b, %H:%M ")
;;;; Covered by `display-time-format'
;; (setq display-time-24hr-format t)
;; (setq display-time-day-and-date t)
(setq display-time-interval 60)
(setq display-time-default-load-average nil)
;; NOTE 2022-09-21: For all those, I have implemented my own solution
;; that also shows the number of new items, although it depends on
;; notmuch: the `notmuch-indicator' package.
(setq display-time-mail-directory nil)
(setq display-time-mail-function nil)
(setq display-time-use-mail-icon nil)
(setq display-time-mail-string nil)
(setq display-time-mail-face nil)
;; I don't need the load average and the mail indicator, so let this
;; be simple:
(setq display-time-string-forms
'((propertize
(format-time-string display-time-format now)
'face 'display-time-date-and-time
'help-echo (format-time-string "%a %b %e, %Y" now))
" "))
;; Enable function
(display-time-mode 1)
Impostazione init.el per i fusi orari (world-clock)
Comunico con persone di tutto il mondo. Conoscere il loro orario locale è di
fondamentale importanza. Con M-x world-clock otteniamo un buffer con tutte le
città e i relativi fusi orari specificati in zone world-clock-list. Il
contenuto viene visualizzato in base a world-clock-time-format. Nota che
si può controllare il posizionamento di questo e molti altri buffer configurando
display-buffer-alist.
(setq world-clock-list
'(("America/Los_Angeles" "🇺🇸 Los Angeles")
("America/Vancouver" "🇨🇦 Vancouver")
("America/Denver" "🇺🇸 Denver")
("America/Chicago" "🇺🇸 Chicago")
("America/New_York" "🇺🇸 New York")
("America/Toronto" "🇨🇦 Toronto")
("America/Montreal" "🇨🇦 Montreal")
("UTC" "🌐 UTC")
("Europe/Lisbon" "🇵🇹 Lisbon")
("Europe/London" "🇬🇧 London")
("Europe/Paris" "🇫🇷 Paris")
("Europe/Brussels" "🇧🇪 Brussels")
("Europe/Berlin" "🇩🇪 Berlin")
("Europe/Athens" "🇬🇷 Athens")
("Asia/Nicosia" "🇨🇾 Lefkosia")
("Asia/Istanbul" "🇹🇷 Istanbul")
("Asia/Baghdad" "🇸🇾 Baghdad")
("Asia/Riyadh" "🇸🇦 Riyadh")
("Asia/Tehran" "🇮🇷 Tehran")
("Asia/Tbilisi" "🇬🇪 Tbilisi")
("Asia/Tashkent" "🇺🇿 Tashkent")
("Asia/Karachi" "🇵🇰 Karachi")
("Asia/Kolkata" "🇮🇳 Kolkata")
("Asia/Dhaka" "🇧🇩 Dhaka")
("Asia/Ho_Chi_Minh" "🇻🇳 Ho Chi Minh")
("Asia/Singapore" "🇸🇬 Singapore")
("Asia/Shanghai" "🇨🇳 Shanghai")
("Asia/Seoul" "🇰🇷 Seoul")
("Asia/Tokyo" "🇯🇵 Tokyo")
("Australia/Brisbane" "🇦🇺 Brisbane")
("Australia/Sydney" "🇦🇺 Sydney")
("Pacific/Auckland" "🇳🇿 Auckland")
))
;; Formattazione dell'orario mostrato
(setq world-clock-time-format "%a %d %b %R %Z")
Impostazione init.el per eseguire Emacs come server
Il "server" è funzionalmente simile al demone, tranne per il fatto che viene
eseguito dal primo frame di Emacs che avviamo. Con un server in esecuzione,
possiamo connetterci ad esso tramite una nuova chiamata emacsclient. Questo è
utile se vogliamo avviare nuovi frame che condividono risorse con il processo in
esecuzione. Tuttavia, emacsclient può essere utilizzato anche semplicemente
per eseguire codice al di fuori di un frame di Emacs (ad esempio per caricare un
nuovo tema, come faccio io con il mio delight.shscript di shell all'interno dei
miei dotfiles).
Quando chiudiamo l'ultimo frame, il server viene terminato. Il demone, invece, rimane attivo anche se tutti i frame di Emacs vengono chiusi. Per me, il server è più facile da usare, perché so con certezza quando inizia e finisce il suo ciclo di vita. Detto questo, ho provato seriamente anche il demone. Emacs si bloccava ogni volta che incontravo un errore in qualche valutazione Lisp. Il server, invece, funziona perfettamente.
Nota che ciò avviene solo server-start se non è in esecuzione alcun
server. Uno è sufficiente.
;; Emacs server (allow emacsclient to connect to running session)
(setq server-client-instructions nil)
(require 'server)
(unless (or (server-running-p) (daemonp))
(server-start))
Impostazione init.el per il salvataggio della cronologia ( savehist-mode)
I prompt del minibuffer possono avere una propria cronologia. In caso contrario,
condividono una cronologia comune degli input dell'utente. Emacs tiene traccia
di questa cronologia nella sessione corrente, ma la perde non appena la
chiudiamo. Con savehist-mode abilitata, tutte le cronologie del minibuffer
vengono scritte in un file e ripristinate al successivo avvio di Emacs.
Le storie sono utili in due modi:
- Le scelte più recenti compaiono in cima alla lista, così possiamo trovarle più facilmente.
- I comandi
M-p(previous-history-element) eM-n(next-history-element) nel minibuffer saranno utili subito dopo il ripristino di Emacs (tutti i miei pacchetti sfruttano al meglio le cronologie del minibuffer per ogni prompt, quindiM-peM-nmostrano solo i valori rilevanti).
Poiché stiamo già registrando la cronologia del minibuffer, possiamo istruirlo
savehist-mode a tenere traccia anche di variabili aggiuntive e a ripristinarle la
prossima volta che utilizziamo Emacs. Quindi savehist-additional-variables.
Si noti che l'opzione utente history-length si applica a ciascuna variabile della
cronologia singolarmente: non riguarda tutte le cronologie nel loro complesso.
Nel complesso, sono soddisfatto di questa funzionalità e ne traggo vantaggio quotidianamente.
;;;; `savehist' (minibuffer and related histories)
(setq savehist-file (locate-user-emacs-file "savehist"))
(setq history-length 100)
(setq history-delete-duplicates t)
(setq savehist-save-minibuffer-history t)
(with-eval-after-load 'savehist
(add-to-list 'savehist-additional-variables 'kill-ring))
(savehist-mode 1)
Impostazione init.el per le dynamic abbreviations
Il pacchetto integrato dabbrev fornisce un metodo di completamento del testo
che legge il contenuto di un buffer ed espande il testo davanti al cursore per
trovare possibili candidati. Questo viene fatto con M-/ (dabbrev-expand) che
è ciò che uso più spesso per eseguire i completamenti in buffer.
Mi piace dabbrev perché è minimalista. Non genera popup né influisce sul
layout della finestra, permettendomi di concentrarmi su ciò che sto
facendo. Vorrei che avesse una funzione che consentisse di avviarlo e, in
qualsiasi momento, richiedere una presentazione completa in minibuffer di ciò
che sta cercando di trovare, invece di scorrere i candidati ripetutamente
M-/. Certo, di solito non scorro in questo modo, dato che in genere digito
abbastanza da trovare una corrispondenza esatta o essere a un M-/ passo da
essa.
Oltre al dabbrev-expand comando, abbiamo dabbrev-completion. Non lo uso
perché non mi sembra naturale, mentre scrivo, fermarmi, controllare il
minibuffer per vedere se c'è del testo, selezionarlo e tornare a scrivere. Forse
questo dipende dal fatto che ho uno stile di scrittura senza interruzioni e
senza tornare immediatamente indietro a modificare ciò che ho scritto (a meno
che non stia facendo una dimostrazione, in cui lo spettatore deve seguire).
Il termine dabbrev sta per “dynamic abbreviations”. Emacs ha anche
abbreviazioni statiche definite dall’utente le prot-emacs-completion.
;;;; `dabbrev' (dynamic word completion (dynamic abbreviations))
(setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
(setq dabbrev-abbrev-skip-leading-regexp "[$*/=~']")
(setq dabbrev-backward-only nil)
(setq dabbrev-case-distinction 'case-replace)
(setq dabbrev-case-fold-search nil)
(setq dabbrev-case-replace 'case-replace)
(setq dabbrev-check-other-buffers t)
(setq dabbrev-eliminate-newlines t)
(setq dabbrev-upcase-means-case-search t)
(setq dabbrev-ignored-buffer-modes '(archive-mode image-mode docview-mode pdf-view-mode))
Impostazione init.el per le static abbreviations ToReview
[Vedi articolo/video relativo: Emacs: abbreviations with abbrev-mode]
A differenza delle abbreviazioni dinamiche che dipendono dal testo già disponibile in un buffer, possiamo definire abbreviazioni che si espandono sempre in base a quanto specificato.
Le abbreviazioni, o il abbrev meccanismo, sono stringhe di caratteri che, una
volta digitate, vengono sostituite da un'altra stringa. Ad esempio, se voglio
digitare l'URL del mio sito web, inserisco mewebe continuo a digitare. Emacs
espanderà quella parola in https://protesilaos.com.
A meno che tu non stia documentando la funzione della tua abbreviazione o che tu
abbia abbreviazioni facili da digitare in modo errato, non avrai mai bisogno di
dire a Emacs di non espandere l'input fornito. Tieni presente che il comando
unexpand-abbrev è disponibile se ne hai bisogno. Ho dovuto usarlo nel paragrafo
precedente per digitare prima l'abbreviazione che ho usato come esempio.
Allo stesso modo, possiamo espandere un'abbreviazione ovunque con il comando
expand-abbrev, anche se normalmente non è necessario farlo perché avviene
automaticamente mentre digitiamo.
Emacs gestisce in modo intelligente la definizione e l'utilizzo delle
abbreviazioni. Ogni modalità principale ha la sua tabella di abbreviazioni, alla
quale aggiungiamo le nostre definizioni. Quando ci troviamo in un buffer che
utilizza quella modalità principale, otteniamo l'accesso alle abbreviazioni
corrispondenti. In global-abbrev-table è disponibile anche un'opzione per le
abbreviazioni. In caso di conflitto tra una tabella specifica per la modalità
principale e quella globale, la prima ha la precedenza. Inoltre, le tabelle di
abbreviazioni seguono lo stesso principio di ereditarietà delle modalità
principali in generale, il che significa che una modalità come Org erediterà
l'opzione text-mode-abbrev-table perché org-mode deriva da text-mode. Le
tabelle di abbreviazioni sono quindi coerenti con il funzionamento di hook e
mappature di tasti in termini di precedenza ed ereditarietà.
Come noterai più avanti, utilizzo la prot-emacs-abbrev macro per semplificare la
definizione di queste abbreviazioni. La maggior parte delle mie abbreviazioni
sono semplici modifiche alla maiuscola/minuscola. Tieni presente, tuttavia, che
il meccanismo sottostante è potente e può valutare codice Elisp arbitrario (ci
ho giocato un po', anche se non uso template e quindi non ne ho un reale
bisogno, ma funziona).
;; - ;;;; `abbrev' (Abbreviations, else Abbrevs)
;; - ;; message-mode derives from text-mode, so we don't need a separate
;; - ;; hook for it.
;; - (prot-emacs-hook
;; - (text-mode-hook prog-mode-hook git-commit-mode-hook)
;; - abbrev-mode)
;; -
;; - (setq only-global-abbrevs nil)
;; -
;; - (prot-emacs-abbrev global-abbrev-table
;; - "csico" "CSiCo"
;; - ";home" "~"
;; - ";tilde" "~")
;; -
;; - (prot-emacs-abbrev text-mode-abbrev-table
;; - "asciidoc" "AsciiDoc"
;; - "auctex" "AUCTeX"
;; - "cafe" "café"
;; - "cliche" "cliché"
;; - "clojurescript" "ClojureScript"
;; - "emacsconf" "EmacsConf"
;; - "github" "GitHub"
;; - "gitlab" "GitLab"
;; - "javascript" "JavaScript"
;; - "latex" "LaTeX"
;; - "libreplanet" "LibrePlanet"
;; - "linkedin" "LinkedIn"
;; - "paypal" "PayPal"
;; - "sourcehut" "SourceHut"
;; - "texmacs" "TeXmacs"
;; - "typescript" "TypeScript"
;; - "visavis" "vis-à-vis"
;; - "deja" "déjà"
;; - "voila" "voilà"
;; - "youtube" "YouTube"
;; - ";up" "🙃"
;; - ";uni" "🦄"
;; - ";laugh" "🤣"
;; - ";smile" "😀"
;; - ";sun" "☀️")
;; -
;; - ;; Allow abbrevs with a prefix colon, semicolon, or underscore. I demonstrated
;; - ;; this here: <https://protesilaos.com/codelog/2024-02-03-emacs-abbrev-mode/>.
;; - (abbrev-table-put global-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)")
;; -
;; - (with-eval-after-load 'text-mode
;; - (abbrev-table-put text-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
;; -
;; - (with-eval-after-load 'org
;; - (prot-emacs-abbrev org-mode-abbrev-table
;; - ";dev" "}"
;; - ";key" #'prot-abbrev-org-macro-key
;; - ";cmd" #'prot-abbrev-org-macro-key-command)
;; - (abbrev-table-put org-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
;; -
;; - ;; Because the *scratch* buffer is produced before we load this, we
;; - ;; have to explicitly activate the mode there.
;; - (when-let* ((scratch (get-buffer "*scratch*")))
;; - (with-current-buffer scratch
;; - (abbrev-mode 1)))
;; -
;; - ;; By default, abbrev asks for confirmation on whether to use
;; - ;; `abbrev-file-name' to save abbrevations. I do not need that, nor
;; - ;; do I want it.
;; - (remove-hook 'save-some-buffers-functions #'abbrev--possibly-save)
Altro da rivedere