Files
NewCfg/Old - My-emacs-config.org

48 KiB
Raw Permalink Blame History

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.el per i temi e emacs-essentials.el per 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) e M-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, quindi M-p e M-n mostrano 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