Une Configuration Emacs

Voici ma configuration Emacs - un chantier jamais fini - au 14/02/2019. Cette configuration prend la forme d'un fichier Org, à partir duquel sont générés une documentation HTML, ainsi que le fichier Lisp qui constituait auparavant mon .emacs. Ce dernier s'est transformé en un petit fichier de chargement, qui valorise quelques raccourcis (et autres détails spécifiques au poste sur lequel il se trouve) avant de charger le Lisp généré.

C'est également un essai pour voir ce qu'on peut faire avec Org. Ce fichier n'a pas fini de se transformer, et peut-être retournera-t-il à sa forme originelle, à savoir un simple fichier Lisp. Un attendant, un grand merci à Zhitao Gong pour le CSS qui habille les exports HTML.

Table des matières

À propos du temps

La fonction last-step-duration, appelée régulièrement, permet de voir comment le temps s'écoule lors du chargement du .emacs. À chaque appel, elle laisse une trace dans le buffer *Messages* avec le temps écoulé depuis l'appel précédent, ainsi que le temps total écoulé depuis le début du chargement du fichier. Certainement pas un modèle de Lisp, mais remplit son office et aide à comprendre pourquoi Emacs est parfois lent à démarrer.

(defun reset-ts()
  (setq first-ts (current-time))
  (setq last-ts first-ts))

(defun step-duration()
  (let* ((ts (current-time))
         (elapsed (float-time (time-subtract ts last-ts))))
    (setq last-ts ts)
    elapsed))

(defun total-duration()
  (let* ((ts (current-time))
         (elapsed (float-time (time-subtract ts first-ts))))
    elapsed))

(defun last-step-duration(title)
  (message "Τ=%.3fs\tΔ=%.3fs\t%s" (total-duration) (step-duration) title))

(reset-ts)

Ce code apparait en début de fichier afin d'initialiser une variable globale contenant le time stamp de début de chargement de ce .emacs. Bien que ses appels ne soient pas exportés dans le HTML généré, la fonction last-step-duration est utilisée régulièrement, à la fin de chaque section. Exemple de trace :

Τ=2.516s	Δ=0.016s	Rainbow delimiters

Généralités

Désactive l'écran d'accueil, Emacs s'ouvre avec le buffer scratch

(setq inhibit-startup-screen t)

Affichage du nom du buffer dans la barre de titre

(setq frame-title-format '(buffer-file-name "Emacs: %b (%f)" "Emacs: %b"))

Dans le terminal, pas de barre de menu

(unless window-system (menu-bar-mode -1))

En mode graphique, cache la barre d'outils

(when window-system (tool-bar-mode -1))

En mode graphique, gouttière réduite à gauche uniquement (nil pour remettre la taille par défaut).

(when window-system (set-fringe-mode '(4 . 0)))

En mode graphique, barre de défilement à droite (-1 pour désactiver)

(when window-system (set-scroll-bar-mode 'right))

Un buffer pour lequel Emacs n'a attribué aucun mode majeur s'ouvre en mode texte.

(setq-default major-mode 'text-mode)

Une ligne de 80 caractères est suffisamment longue. La variable default-fill-column affecte de nombreuses commandes comme fill-paragraph, ainsi que des modes mineurs comme whitespace-mode. Pour ceux qui le souhaitent, appliquer la règle des 80 colonnes commence par là.

(setq default-fill-column 80)

Indentation avec des tabulations de 4 caractères

(setq-default c-basic-offset 4
              tab-width 4
              indent-tabs-mode t)

En fin de buffer, les touches flèches ne créent pas une nouvelle ligne

(setq-default next-line-add-newlines nil)

Affichage de la ligne et de la colonne du curseur dans la mode line

(line-number-mode t)
(column-number-mode t)

Pour utiliser narrow-to-region via son raccourci clavier C-n n

(put 'narrow-to-region 'disabled nil)

Désactive la transient mark. La région est toujours active, mais pas surlignée. Au besoin, la transient mark est activée le temps d'une commande avec C-SPC C-SPC. Correspond à l'ancien comportement d'Emacs.

(setq transient-mark-mode nil)

Dans un environnement graphique, lance Emacs en mode serveur

(when window-system (server-start))

Des modules peuvent se trouver dans site-lisp. On ajoute ce répertoire à la variable load-path afin qu'ils soient trouvés par la commande require.

(add-to-list 'load-path "~/.emacs.d/site-lisp/")

Place les backups (copie d'un fichier avant modifications) et les sauvegardes automatiques (faites périodiquement pendant qu'on édite un fichier) dans un répertoire temporaire, selon la plateforme :

  • sur Mac : /var/folders/xxx
  • sur Windows : c:/Users/yyy/AppData/Local/Temp
  • sur Linux : /tmp

L'expression régulière ".*" ci-après indique que la règle concerne tous les fichiers, car on peut faire, par exemple, des règles différentes selon l'extension.

(setq backup-directory-alist `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))

Mise en évidence de la ligne en cours dans Dired

(add-hook 'dired-mode-hook 'hl-line-mode)

Sur Mac, Dired utilise GNU ls si disponible

(when (eq system-type 'darwin)
  (if (file-executable-p "/usr/local/bin/gls")
      (setq insert-directory-program "/usr/local/bin/gls")))

Common Lisp, pour ignore-errors et d'autres

(require 'cl)

Raccourcis Fn

Quelques raccourcis pratiquent sur les touches fonctions, certains inspirés de Visual Studio ou de Xcode. Lorsqu'il est lancé en mode texte depuis un terminal, Emacs ne voit pas les raccourcis en C-Fn. Pour cette raison, ils sont en général doublés sur S-Fn.

  • F1 pour view-mode, qui permet de passer un buffer qu'on ne veut pas modifier en lecture seule
  • C-F1 pour revert-buffer, qui permet de revenir à la dernière version sauvegardée du fichier
;; À la place de view-order-manuals qui est normalement sur f1
(global-set-key (kbd "<f1>")    'view-mode)
(global-set-key (kbd "C-<f1>")  'revert-buffer)
(global-set-key (kbd "S-<f1>")  'revert-buffer)
  • F2 pour bm-toggle, qui insère (ou retire) un marque-page sur la ligne. La ligne marquée est alors surlignée
  • C-F2 et S-F2 pour bm-next et bm-previous, qui permettent de parcourir les marques-pages

En mode terminal C-f2 ne fonctionne pas, et parcourir les marques-pages avec Helm via C-F2 est plus pratique. Dans tous les cas, helm-bm est disponible via C-c b.

(global-set-key (kbd "<f2>")    'bm-toggle)
(global-set-key (kbd "C-<f2>")  'bm-next)
(global-set-key (kbd "S-<f2>")  'bm-previous)

(unless window-system
  (global-set-key (kbd "S-<f2>")    'helm-bm))
  • F3 pour hl-line-mode, bien pratique quand on a besoin de se concentrer sur une ligne, ou lorsqu'on doit traiter un buffer une ligne après l'autre.
(global-set-key (kbd "<f3>")    'hl-line-mode)
  • F4 pour projectile-grep (ou helm-projectile-grep si Helm est activé). Si Projectile est disponible pour un projet, permet de chercher interactivement dans les fichiers de ce projet. Vraiment très utile ! Fonctionne particulièrement bien avec C-c h b, qui comme toujours avec Helm, permet de ramener les dernières propositions…
(global-set-key (kbd "<f4>")    'projectile-grep)
  • F5 pour run-compilation, fonction perso qui sauvegarde l'agencement des buffers affichés avant de lancer une compilation
  • C-F5 pour end-compilation, qui met fin à une session de compilation et restaure l'affichage
(global-set-key (kbd "<f5>")    'run-compilation)
(global-set-key (kbd "C-<f5>")  'end-compilation)
(global-set-key (kbd "S-<f5>")  'end-compilation)
  • F6 pour projectile-multi-occur, qui lance occur dans tous les buffers ouverts appartenant au projet du buffer courant
  • C-F6 pour helm-occur, qui lance une version interactive depuis Helm de occur : La liste des correspondances est construite dynamiquement lors de la frappe, et à tout moment et permettent d'en sélectionner une, pour laquelle TAB donnera un aperçu.
(global-set-key (kbd "<f6>")    'projectile-multi-occur)
(global-set-key (kbd "C-<f6>")  'helm-occur)
(global-set-key (kbd "S-<f6>")  'helm-occur)
  • F8 pour Fill column indicator, qui met en évidence la colonne 80 dans les modes de développement.
  • C-F8 pour désactiver (ou réactiver) Whitespace lorsqu'il devient trop intrusif, en particulier avec les lignes trop longues.
(global-set-key (kbd "<f8>")    'fci-mode)
(global-set-key (kbd "C-<f8>")  'whitespace-mode)
(global-set-key (kbd "S-<f8>")  'whitespace-mode)

UTF-8

Autant que possible, on veut de l'UTF-8. De nombreuses variables permettent de configurer finement les divers comportements d'Emacs en matière d'encodage (voir par exemple cette réponse sur Stack Overflow). Mais la simple directive set-language-environment réalise un paramétrage par défaut satisfaisant.

(set-language-environment 'utf-8)

Navigation dans les buffers

Lorsqu'on navigue dans les buffers, on préfère sauter les buffers ouverts automatiquement par Dired, Helm, ou autre, comme *compilation* par exemple. On définit pour cela la fonction navigate-nostar-buffer, qui cherche le prochain buffer ne commençant pas par * (ou le précédent si appelé avec un argument).

(defun navigate-nostar-buffer (&optional previous)
  "Navigate to next \"no star\" buffer, or previous one if PREVIOUS is t."
  (let ((start-buffer (buffer-name)))
    (cl-flet ((next-f () (if previous (next-buffer) (previous-buffer))))
      (next-f)
      (while
          (and (string-match-p "^\*" (buffer-name))
               (not (equal start-buffer (buffer-name))))
        (next-f)))))

(defun navigate-next-nostar-buffer ()
  "Navigate to next \"no star\" buffer."
  (interactive)
  (navigate-nostar-buffer))

(defun navigate-previous-nostar-buffer ()
  "Navigate to previous \"no star\" buffer."
  (interactive)
  (navigate-nostar-buffer t))

Puis on utilise remap pour réaffecter les raccourcis de next-buffer et previous-buffer, C-x et C-x respectivement.

(global-set-key [remap next-buffer] 'navigate-next-nostar-buffer)
(global-set-key [remap previous-buffer] 'navigate-previous-nostar-buffer)

Compilation

Définit run-compilation et end-compilation qui sauvegardent et restaurent l'affichage des buffers lors d'une séance de compilation : Si l'on doit faire plusieurs allers / retours entre le buffer de compilation et les sources, pour comprendre et corriger les erreurs, on peut ensuite restaurer son environnement de travail.

(defun get-compilation-buffer ()
  "Get the compilation buffer, or nil if it does not exist."
  ;; Voir aussi `compilation-buffer-name-function'
  (car (cl-remove-if-not
        (lambda (b)
          (equal "*compilation*" (buffer-name b)))
        (buffer-list))))

(defun get-compilation-layout-register ()
  "Returns the register used to save the layout before compilation,
and restore it later."
  (message "get-compilation-layout-register ()")
  ;; It seems we can use more than one letter register !
  'comp-layout-reg)

(defun start-new-compilation ()
  "Prompt for command and run a new compilation"
  ;; Passer en plein écran
  ;; (let ((current-prefix-arg '(4)))   ; C-u
  ;; (call-interactively 'compile)))
  (call-interactively 'compile))

(defun return-to-compilation ()
  "Get compilation buffer back in full screen"
  ;; Relancer la compil automatiquement ?
  (switch-to-buffer (get-compilation-buffer))
  (delete-other-windows))

(setq ongoing-compilation-session nil)

(defun start-compilation-session ()
  "Save layout and start a new compilation session"
  (frame-configuration-to-register (get-compilation-layout-register))
  (start-new-compilation)
  (setq ongoing-compilation-session t))

(defun end-compilation-session ()
  "Restore pre-compilation layout and terminate compilation session"
  ;; Supprimer le buffer de compil ?
  (setq ongoing-compilation-session nil)
  (jump-to-register (get-compilation-layout-register)))

(defun run-compilation ()
  "Start new compilation session or restore an old one"
  (interactive)
  (if (and ongoing-compilation-session (get-compilation-buffer))
      (return-to-compilation)
    (start-compilation-session)))

(defun end-compilation ()
  "Terminate a compilation session"
  (interactive)
  (if ongoing-compilation-session
      (end-compilation-session)))

Le buffer *compilation* défile jusqu'à la première erreur

(setq compilation-scroll-output 'first-error)

Dans ce buffer, un clic sur une erreur emmène dans les sources, et g permet de relancer une nouvelle compilation.

Copier/coller

Le bouton du milieu colle le texte au niveau du point, sans le déplacer

(setq mouse-yank-at-point t)

Lorsqu'on copie depuis Emacs alors qu'une autre application avait placé du texte dans le presse-papier, ce texte est ajouté au kill ring avant d'être remplacé. Très pratique, car cela évite de perdre du texte copié, par exemple depuis internet, avant d'avoir eu le temps de l'utiliser.

(setq save-interprogram-paste-before-kill t)

Le texte surligné est automatiquement copié dans le kill ring

(setq mouse-drag-copy-region t)

Parenthèses et Cie

Lorsque le curseur est sur une parenthèse ouvrante, ou immédiatement derrière une parenthèse fermante, la paire de parenthèses est surlignée. De même avec les accolades et les crochets.

(show-paren-mode 1)

Éventuellement, si on veut surligner également l'expression entre parenthèses

(setq show-paren-style 'expression)

Les parenthèses sont surlignées en bleu. Voir aussi Rainbow delimiters.

(set-face-background 'show-paren-match "#b5d5ff")

Suggestions

J'ai beaucoup utilisé (et apprécié) iswitchb-mode, et lorsqu'il est devenu obsolète, j'ai tenté de le remplacer par icomplete-mode, mais ce n'était convaincant, si bien que j'utilise maintenant Helm. Cette configuration (que je n'utilise plus) est ce que j'ai pu obtenir de mieux, avec un comportement ressemblant à iswitchb-mode.

(icomplete-mode)

Suggère les buffers sur un C-x b avant qu'on commence à taper

(setq icomplete-show-matches-on-no-input t)

Ignore la casse lorsqu'on demande à compléter avec TAB

(setq read-buffer-completion-ignore-case t)

Met en évidence dans le minibuffer le candidat qui pourrait être sélectionné

(copy-face 'minibuffer-prompt 'icomplete-first-match)

Contourne les problèmes liés au buffer par défaut. En particulier, prend le premier élément de la liste, sélectionné avec C-s et C-r, sans qu'on ait commencé à saisir le nom d'un buffer. Voir réouverture du bug #17545.

(defun my-icomplete-forward-completions ()
  "Step forward completions by one entry."
  (interactive)
  (progn (setq minibuffer-default nil)
         (icomplete-forward-completions)))

(defun my-icomplete-backward-completions ()
  "Step backward completions by one entry."
  (interactive)
  (progn (setq minibuffer-default nil)
         (icomplete-backward-completions)))

(defun my-minibuffer-force-complete-and-exit ()
  "Select the current completion."
  (interactive)
  (progn (setq minibuffer-default nil)
         (minibuffer-force-complete-and-exit)))

;;Configure des raccourcis ressemblant à ceux de iswitchb
(when (boundp 'icomplete-minibuffer-map)
  (let ((map icomplete-minibuffer-map))
    (define-key map (kbd "C-s") 'my-icomplete-forward-completions)
    (define-key map (kbd "C-r") 'my-icomplete-backward-completions)
    (define-key map (kbd "C-j") 'my-minibuffer-force-complete-and-exit)
    (define-key map (kbd "C-<return>") 'my-minibuffer-force-complete-and-exit)
    ))

Mise à jour du path

C'est parfois utile d'enrichir un peu le path utilisé par Emacs. Il y a en fait plusieurs chemins, dont ces deux variables :

  • exec-path, qui est le path utilisé par Emacs pour chercher les binaires
  • PATH, qui est le path transmis aux commandes lancées par Emacs

Pour ma part je les traite ensembles, avec la fonction add-to-exec-paths, qui ajoute (s'il existe) à la fin des deux paths le répertoire passé en argument :

;; TODO - Filtrer les doublons
(defun add-to-exec-paths(some-folder)
  (interactive)
  (cond
   ((file-exists-p some-folder)
    (add-to-list 'exec-path some-folder)
    ;;(message "exec-path=%s" exec-path)
    (setenv "PATH" (concat (getenv "PATH") ":" some-folder))
    ;;(message "PATH=%s" (getenv "PATH"))
    (message "Added '%s' to PATH and exec-path" some-folder))))

Reste à ajouter quelques répertoires, selon ses habitudes…

(add-to-exec-paths "~/scripts")
(add-to-exec-paths "~/local/bin")
(add-to-exec-paths "/usr/bin")
(add-to-exec-paths "/usr/local/bin")
(add-to-exec-paths "/opt/local/bin")

Valorisation du proxy

On cherche d'abord le proxy dans le fichier de configuration ~/.emacs.d/proxy.el :

(setq my-proxy ())

(if (file-exists-p "~/.emacs.d/proxy.el")
    (progn
      (load-file "~/.emacs.d/proxy.el")))

Si ce fichier existe, il contient une simple ligne, telle que :

(setq my-proxy 'proxy.mon.domaine.fr:80)

Si l'on ne l'a pas trouvé, on cherche dans l'environnement la variable http_proxy

(unless my-proxy (setq my-proxy (getenv "http_proxy")))

On valorise correctement url-proxy-services et on affiche le proxy utilisé

(if my-proxy
    (progn
      (setq url-proxy-services (list (cons "http" (symbol-name my-proxy))))
      (message "Set HTTP proxy to '%s'" my-proxy)))

Voir la doc d'Url Package sur EmacsWiki pour plus d'infos sur les proxys.

Ajustements à l'OS

Selon la plateforme, on effectue quelques ajustements, en particulier sur les polices de caractères.

Sous Linux

Fonte du terminal Xfce

(set-default-font "Liberation Mono 10" t t)

Sous Mac

Réaffectation des touches à problèmes sur Mac :

  • cmd devient meta
  • alt permet les raccourcis pour |, ~ et cie.
(setq mac-option-key-is-meta nil)
(setq mac-command-key-is-meta t)
(setq mac-command-modifier 'meta)
(setq mac-option-modifier nil)

Fonte Xcode

(set-face-font
 'menu "-apple-menlo-medium-r-normal--11-110-72-72-m-110-iso10646-1")
(set-face-font
 'default "-apple-menlo-medium-r-normal--11-110-72-72-m-110-iso10646-1")

Require

Un wrapper pour require, qui n'empêche pas le chargement du .emacs lorsqu'un module n'est pas disponible, et génère des traces dans *Messages*, avec en cas de succès, le temps passé par Emacs pour charger le module.

(defun my-require(feature)
  (condition-case nil
      (let ((ts (current-time)))
        (progn
          (require feature)
          (let ((elapsed (float-time (time-subtract (current-time) ts))))
            (message "Successfully load '%s' in %.3fs" feature elapsed))))
    (file-error
     (progn (message "Fail to load required feature '%s'" feature) nil))))

Il s'utilise ainsi :

(when (my-require 'package-xxx)
  (message "Do foo")
  (message "Do bar")
  (message "Do baz"))

my-require est largement utilisé dans la suite de ce fichier, mais afin de ne pas avoir partout un niveau d'indentation, le when est sous-entendu, et n'est pas exporté dans le HTML. Le code apparait donc ainsi :

(my-require 'package-xxx)
(message "Do foo")
(message "Do bar")
(message "Do baz")

C'est-à-dire comme avec un require classique. Exemple de traces :

Fail to load required feature ’p4’
Successfully load ’rainbow-mode’ in 0.031s

On peut aussi utiliser le soft require, qui ne génère pas d'erreur quand le module n'est pas trouvé :

(require 'package-foobar nil t)

Gestionnaire de paquets

Charge le gestionnaire de paquets

(my-require 'package)

On peut ensuite ajouter des sources de paquets, qui viendront alimenter la liste affichée par package-list-packages.

Milkypostman’s Emacs Lisp Package Archive (MELPA), une source de paquets à avoir, la seule que j'ajoute systématiquement.

(add-to-list 'package-archives
             '("MELPA" . "http://melpa.milkbox.net/packages/") t)

Pour ceux qui veulent une version bien à jour de Org, la source dédiée est pratique également.

(add-to-list 'package-archives
             '("Org" . "https://orgmode.org/elpa/") t)

En cas de problème avec les paquets, on peut supprimer le répertoire où tout est installé : ~/.emacs.d/elpa/archives/melpa/archive-contents.

Marmelade une autre source de paquets intéressante, moins dynamique que Melpa ces derniers temps, mais avec avec un système de validation différent.

(add-to-list 'package-archives
             '("Marmalade" . "http://marmalade-repo.org/packages/") t)

GNU Emacs Lisp Package Archive (ELPA) est la source de paquets officielle d'Emacs.

(add-to-list 'package-archives
             '("GNU" . "http://elpa.gnu.org/packages/") t)

Org dispose également de sa propre source, pour ceux qui veulent garder très à jour ce paquet majeur.

(add-to-list 'package-archives
             '("Org" . "http://orgmode.org/elpa/") t)

Initialise le gestionnaire de paquet. Avec Helm, on peut utiliser helm-list-elisp-packages via C-c h @

(package-initialize)

Thème Leuven

Les couleurs par défaut d'Emacs fonctionnent mal lorsqu'il est lancé en mode texte dans un terminal à fond noir, car les couleurs foncées de la mise en évidence de syntaxe ne ressortent pas. Le thème Leuven, sur fond blanc, est très bien intégré à Emacs (et particulièrement à Org), lisible et agréable en mode texte comme en mode graphique. Le gestionnaire de package initilisé, on regarde si Leuven est disponible, et si oui, on le charge.

(if (member 'leuven-theme (mapcar 'car package-alist))
    (load-theme 'leuven t))

TabTab minor mode

Une tentative pour changer le comportement de la touche TAB dans les fichiers CMake :

  • TAB ne sert plus à indenter la ligne, mais aligne le texte sur le prochain arrêt de tabulation
  • S-TAB remplit la fonction opposée, et aligne le texte sur l'arrêt de tabulation précédent
(defun prev-tab-to-tab-stop ()
  "Remove spaces or tabs to next defined tab-stop column."
  (interactive)
  (and abbrev-mode (= (char-syntax (preceding-char)) ?w)
       (expand-abbrev))
  (let ((nexttab (indent-next-tab-stop (current-column) t)))
    (delete-horizontal-space t)
    (indent-to nexttab)))

(define-minor-mode tab-tab-mode
  "Tab-to-tab in both directions"
  :lighter " TTm"
  :keymap (let ((map (make-sparse-keymap)))
            (define-key map (kbd "<tab>") 'tab-to-tab-stop)
            (define-key map (kbd "S-<tab>") 'prev-tab-to-tab-stop)
            map))

(add-hook 'cmake-mode-hook 'tab-tab-mode)

Cependant ça ne fonctionne pas très bien, car cmake-mode n'a pas l'air de prendre en compte la key-map. Et d'ailleurs si l'on tente d'installer les raccourcis avec un hook, ça ne fonctionne pas non plus…

(add-hook 'cmake-mode-hook (local-set-key (kbd "<tab>") 'tab-to-tab-stop))

À creuser donc. Voir How to Make an Emacs Minor Mode pour un tutoriel clair et bien fait.

Auto Complete

Auto Complete présente dans un menu en mode texte différents choix pour compléter le texte au point. Simple et efficace !

(my-require 'auto-complete-config)

Les choix proposés proviennent d'un certain nombre de sources ; on utilise les sources par défaut, vérifiables avec M-: ac-sources

(ac-config-default)

Auto Complete ne s'active pas automatiquement dans tous les modes, mais seulement dans ceux listés dans ac-modes. Cette liste contient par défaut la majorité des modes où Auto Complete est intéressant, mais on peut quand même rajouter quelques uns :

(add-to-list 'ac-modes 'cmake-mode)
(add-to-list 'ac-modes 'org-mode)
(add-to-list 'ac-modes 'text-mode)

Active Auto Complete dans tous les modes sélectionnés

(global-auto-complete-mode t)

Le menu s'affiche sur demande, et non sur temporisation, avec le raccourci habituel M-/

(setq ac-auto-start nil)
(global-set-key (kbd "M-/") 'auto-complete)

C-s permet de filtrer une sous-chaine dans le menu

(setq ac-use-menu-map t)

Recherche approximative

On souhaite profiter de la recherche approximative, dans le cas d'un préfix mal orthographié ou écrit en abrégé.

(setq ac-use-fuzzy t)

Cela fonctionne au minimum avec le contenu du buffer courant, mais manifestement pas avec les autres sources. Et même avec le buffer courant, il semblerait que Fuzzy ait ses raisons, que la raison ignore : soit le fichier texte contenant cette seule ligne

aaaaaaaabbbbccd

Diverses tentatives pour compléter ce mot donnent les résultats suivants

préfix erroné résultat
aaaab échoue
aaaaab ok
aaaabb échoue
aaaaabc échoue
aaaaabbc ok

Nécessite le paquet fuzzy pour fonctionner une fois de temps en temps.

Intégration à Helm

Intègre Auto Complete à Helm, et permet d'avoir avec C-: dans un buffer helm, le contenu du menu qu'on aurait eu avec M-/. Nécessite le paquet ac-helm.

(global-set-key (kbd "C-:") 'ac-complete-with-helm)

Source pour completion-at-point

Une source pour completion-at-point, très utile par exemple pour rust-mode avec Racer. Nécessite le paquet ac-capf.

(add-hook 'prog-mode-hook 'ac-capf-setup)

Bookmarks

Marque-pages à la Visual Studio : la ligne marquée est surlignée, et l'on peut ensuite naviguer d'un marque-page à l'autre. Beaucoup d'autres fonctionnalités très pratiques comme mettre les marque-pages automatiquement avec une expression régulière, par exemple pour explorer des logs.

(my-require 'bm)

On parcourt avec bm-next et bm-previous l'ensemble des marque-pages de tous les buffers, et pas juste ceux du buffer courant.

(setq bm-cycle-all-buffers t)

On surligne les lignes marquées en fuchsia, comme avec un coup de Stabilo !

(custom-set-faces
 '(bm-face ((t (:background "#ffafff")))))

C & C++

Accolades ouvrantes alignées sous le mot clé

(c-set-offset (quote substatement-open) 0)

Ouvre les .h comme du C++, et non comme du C

(setq auto-mode-alist (append '(("\.h$" . c++-mode)) auto-mode-alist))

Global

Une configuration simple pour GNU Global, en remplacement du vénérable Etags, et qui fonctionne un peu moins mal que ce dernier avec C++.

(my-require 'ggtags)
(add-hook 'c-mode-common-hook 'ggtags-mode)

Reprend les raccourcis usuels d'Etags:

  • M-. pour aller à la définition d'un symbole
  • M-* pour revenir d'où on vient
(add-hook 'c-mode-common-hook (lambda () (local-set-key (kbd "M-.") 'gtags-find-tag)))
(add-hook 'c-mode-common-hook (lambda () (local-set-key (kbd "M-*") 'pop-tag-mark)))

Global, comme la plupart des autres outils d'indexation, ne fonctionne pas très bien avec C++, et nécessite en plus d'être configuré et périodiquement relancé… Il se trouve que helm-projectile-grep fonctionne tellement bien, que je n'utilise presque plus que ça pour naviguer dans les projets.

Column Marker

Surligne les caractères qui tombent sur certaines colonnes. On peut configurer plusieurs colonnes, par exemple à 70 et 80 caractères. Visuellement moins réussi que Fill column indicator, mais cause moins de problèmes de compatibilité que ce dernier. Je le garde dans ma configuration, mais je ne l'utilise plus.

(my-require 'column-marker)

Si l'on est dans un mode de programmation, surligne le caractère tombant après la 80e colonne.

(add-hook 'prog-mode-hook
          (lambda () (interactive) (column-marker-1 80)))

CMake

Le mode majeur pour éditer les fichiers CMake. Perfectible à mon avis, mais utile quand même. Plus d'informations sur le wiki de Kitware dédié à CMake.

(my-require 'cmake-mode)

Les fichiers CMake habituels s'ouvrent avec cmake-mode

(setq auto-mode-alist
      (append '(("CMakeLists\\.txt\\'" . cmake-mode))
              '(("\\.cmake\\'" . cmake-mode))
              auto-mode-alist))

Pas d'indentation automatique, en particulier après Enter

(add-hook 'cmake-mode-hook (lambda () (electric-indent-mode -1)))

DTrace

Un mode majeur pour DTrace, très bel outil d'instrumentation et d'analyse issu de Sun, à l'avenir bien incertain. Il reste utile sur macOS puisqu'il est derrière Instruments. Plus d'infos sur le blog de l'un de ses auteurs.

(my-require 'dtrace-script-mode)

Les fichiers .d sont des scripts DTrace et s'ouvrent avec dtrace-script-mode

(setq auto-mode-alist
      (append '(("\\.d\\'" . dtrace-script-mode))
              auto-mode-alist))

EasyPG Assistant

Une interface à GnuPG pour Emacs, qui permet entre autres de manipuler des fichiers cryptés. GnuPG doit être installé et configuré sur la machine.

(my-require 'epa-file)

Rend transparent la lecture et l'écriture des fichiers .gpg

(epa-file-enable)

Indique à EasyPG une clé par défaut, afin qu'il ne pose pas la question à chaque sauvegarde d'un fichier. Je vous laisse mettre la vôtre !

(setq epa-file-encrypt-to "julien.montmartin@fastmail.fm")

Fonctionne bien aussi dans une variable en début de fichier :

-*- epa-file-encrypt-to: ("julien.montmartin@fastmail.fm") -*-

Ediff

Un mode très pratique pour gérer les diffs.

Les fichiers à comparer sont ouverts cote à cote, et non pas l'un en dessous de l'autre. On aurait presque envie de dire que la fenêtre est partagée verticalement, mais pour une obscure raison, la fonction concernée s'appelle au contraire split-window-horizontally.

(setq ediff-split-window-function 'split-window-horizontally)

Ediff n'ouvre pas de nouvelle fenêtre, tout se passe dans la fenêtre courante, en mode graphique comme en mode texte.

(setq ediff-window-setup-function 'ediff-setup-windows-plain)

Find File At Point

Sur un C-x C-f FFAP essaie de deviner le fichier à ouvrir en fonction du texte sous le curseur, ce qui fonctionne avec les includes par exemple.

Active FFAP et remplace quelques raccourcis comme C-x C-f

(ffap-bindings)

Avant Projectile, FFAP était très pratique pour passer d'un .h à un .cpp. Mais comme la recherche s'effectue sur la base de chemins prédéfinis, ça ne fonctionne pas bien avec les hiérarchies un peu compliquées.

J'utilisais C-t (avec un 't' comme toggle) pour basculer entre source et entête, mais je réserve maintenant ce raccourci pour 'projectile-find-other-file, plus efficace dès lors qu'on a un dépôt qui puisse faire office de projet.

(global-set-key (kbd "C-t") (quote ff-find-other-file))

Ajoute quelques chemins usuels pour trouver les sources et les entêtes

(setq ff-search-directories
      '("." ".."
        "./src" "./include"
        "../src" "../include"
        "../src/*"  "../include/*"
        "../../src" "../../include"))

Fill column indicator

Mode qui permet de visualiser la 80e colonne (ou n'importe quelle autre). Pratique, mais pas sans inconvénients. Mes préférés :

  • casse les menus d'auto-complete
  • casse l'export HTML des fichiers Org
  • copie-colle des pipes depuis le terminal

Avoir besoin de fci-mode, c'est devoir choisir entre la peste et le choléra (n'ayons pas peur des mots !).

(my-require 'fill-column-indicator)

Matérialise la colonne 80

(setq-default fci-rule-column 80)

Active FCI dans tous les modes de développement

(add-hook 'prog-mode-hook 'fci-mode)

Graphviz

Mode majeur pour éditer les fichiers Graphviz, outil très pratique pour dessiner des graphes ou des diagrammes de classe. S'intègre très bien à Org, ce qui ne gâche rien !

(my-require 'graphviz-dot-mode)

Pour effectuer un rendu depuis Emacs avec C-c v

(define-key graphviz-dot-mode-map (kbd "C-c v") 'graphviz-dot-preview)

Helm

Helm - Un framework plus qu'un simple module, de suggestion et d'aide à la sélection. S'appliquera bientôt à tous les aspects d'Emacs. Beaucoup plus intrusif que son principal concurrent, Ido, il change radicalement l'expérience utilisateur, et j'ai mis du temps à sauter le pas. Si ce n'est pas déjà fait, je vous encourage à faire de même ! En plus du nombre incroyable de fonctionnalités simplifiées et enrichies, Helm favorise la découverte et l'exploration d'Emacs. Que du bon en fin de compte !

(my-require 'helm)

Pas nécessaire d'après la doc, mais certaines versions fonctionnent mieux avec !

(require 'helm-config)

Préfixe pour les commandes Helm, remplace C-x c (à faire avant que Helm ne soit chargé)

(global-set-key (kbd "C-c h") 'helm-command-prefix)
(global-unset-key (kbd "C-x c"))

Le traditionnel M-x appelle la version Helm de execute-extended-command. Beaucoup de raccourcis appellent des commandes ainsi redéfinies. En particulier :

  • C-x C-f permet parcourir et sélectionner les fichiers avec helm-find-files
  • C-x r b permet de parcourir et sélectionner ses marque-pages avec helm-filtered-bookmarks
(global-set-key (kbd "M-x") 'helm-M-x)
(global-set-key (kbd "C-x C-f") 'helm-find-files)
(global-set-key (kbd "C-x r b") 'helm-filtered-bookmarks)

Dans un buffer Helm, TAB essaye de compléter ce qui peut l'être

(define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action)

Pour lister les actions, à la place de TAB

(define-key helm-map (kbd "C-z") 'helm-select-action)

Dans un chemin, sélectionne immédiatement un répertoire dès lors qu'il est le seul à correspondre à la saisie

(setq helm-ff-auto-update-initial-value t)

S'il est disponible, on utilise cURL pour télécharger des données

(when (executable-find "curl")
  (setq helm-net-prefer-curl t))

Ouvre le buffer Helm en partageant le buffer courant (partage horizontal)

(setq helm-split-window-in-side-p t)

Quand on arrive à la fin des candidats, on boucle et on retourne au début

(setq helm-move-to-line-cycle-in-source t)

Une couleur plus discrète pour les répertoires . et ... La couleur par défaut est trop foncée : Elle donne l'impression qu'ils sont toujours sélectionnés quand on préfèrerait au contraire moins les voir.

(custom-set-faces
 '(helm-ff-dotted-directory ((t (:foreground "DimGrey")))))

Finalement, on active Helm

(helm-mode t)

Pour plus de lecture, voir cette introduction à Helm : A Package in a league of its own.

Htmlize

Ce paquet définit htmlize-buffer, qui permet d'exporter un buffer en HTML, tout en respectant la mise en évidence de syntaxe réalisée par Emacs. Simple et efficace, même si Org, avec ses blocs #+BEGIN_SRC et #+END_SRC, offre souvent une bonne alternative.

(my-require 'htmlize)

Idle HighLight Mode

Après un petit temps d'inactivité, surligne toutes les occurrences du mot se trouvant sous le curseur. Notepad++ fait cela par défaut. Très pratique pour voir où un symbole est utilisé, et repérer les fautes de frappe.

(my-require 'idle-highlight-mode)

Ce mode mineur est activé dans tous les modes de développement.

(add-hook 'prog-mode-hook (lambda () (idle-highlight-mode t)))

Emacs Lisp

Dans les contextes où il y a du Lisp, on affiche les éventuelles informations disponibles sur une fonction ou une variable dans la zone d'écho, c.à.d à l'emplacement du minibuffer.

(add-hook 'emacs-lisp-mode-hook 'eldoc-mode)
(add-hook 'lisp-interaction-mode-hook 'eldoc-mode)
(add-hook 'ielm-mode-hook 'eldoc-mode)

IELM est un mode assez utile pour tester des petits bouts de Lisp. Il s'agit d'un Read-Eval-Print-Loop comme en ont la plupart des langages interprétés. Une très courte introduction dans ce billet IELM: a REPL for emacs.

Magit

Un module exceptionnel, à essayer absolument si vous êtes utilisateur de Git et d'Emacs !

(my-require 'magit)

La plupart des opérations dans Magit commencent par le buffer status accessible par le raccourci C-x g.

(global-set-key (kbd "C-x g") 'magit-status)

MMM Mode

Permet de faire cohabiter plusieurs modes majeurs dans un même buffer.

(my-require 'mmm-auto)

Reparse un buffer qui a été modifié dès qu'Emacs a un peu de temps

(setq mmm-parse-when-idle t)

Ce mode n'est pas activé automatiquement, mais uniquement dans les buffer pour lesquels on va définir une règle de sous mode

(setq mmm-global-mode 'sometimes)

Définit la règle here-doc qui active le mode shell-script-mode entre les motifs <<EOF et ^EOF, qui délimitent habituellement un here document

(mmm-add-classes
 '((here-doc
    :submode shell-script-mode
    :front "<<EOF"
    :back "^EOF")))

La règle here-doc est activée dans les buffers qui sont dans le très élémentaire mode text-mode (sans critère d'extension)

(mmm-add-mode-ext-class 'text-mode nil 'here-doc)

Org

Un autre mode exceptionnel. Au départ un outliner, puis un outil de publication avec environnement de literate programming, mais aussi de GTD, de suivi de temps, un agenda, etc.

Les lignes ne sont pas tronquées. Plus pratique comme ça, tant pis pour les tableaux !

(setq org-startup-truncated nil)

Les lignes sont indentées selon leur profondeur dans l'arborescence

(setq org-startup-indented t)

Dans les blocs de code, on veut la mise en évidence de syntaxe, et l'on utilise la touche TAB pour indenter (et non pour insérer une tabulation)

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

Les blocs de code sont évalués sans demande de confirmation, ainsi que les liens spéciaux qui exécutent du shell ou du Lisp

(setq org-confirm-babel-evaluate nil)
(setq org-confirm-shell-link-function nil)
(setq org-confirm-elisp-link-function nil)

Sauf indication contraire, les éléments de texte générés par Org sont en français

(setq org-export-default-language "fr")

Concernant l'export (en HTML par exemple) :

  • On ne veut pas que les titres des sections soient numérotés
  • Au-delà de trois niveaux de titres, on préfère des listes
  • On ne veut pas d'auteur (valorisé par Org avec le nom de l'utilisateur)
(setq org-export-with-section-numbers nil)
(setq org-export-headline-levels 3)
(setq org-export-with-author nil)

Mise à jour

La dernière version d'Org n'est en général pas celle qui est livrée avec Emacs. Il faut la mettre à jour avec le gestionnaire de paquets. Pour une raison que j'ignore, Org n'est pas listé comme built-in, mais quand on l'installe manuellement, le gestionnaire de paquet indique bien "shadowing a built-in package".

Ensuite, en cas de bug suspect, il se peut qu'il faille supprimer les fichiers lisp précompilés se trouvant dans ~/.emacs.d/elpa/org-x.y.z, relancer Emacs, puis les générer à nouveau avec byte-recompile-directory.

Perforce

Permet d'utiliser Perforce depuis Emacs, principalement pour sortir des fichiers. Conventionnellement, les fichiers sont en lecture seule. Lorsqu'il les sort, Perforce les passe en lecture / écriture.

(my-require 'p4)

Le client Perforce en ligne de commande doit être correctement configuré. Le fichier caché .P4CONFIG contient les informations de connexion.

(setenv "P4CONFIG" ".P4CONFIG")

Lorsqu'elle doit sortir un fichier, la commande Perforce cherche les informations de connexion dans le répertoire du fichier, puis dans son répertoire parent, etc. Jusqu'à la racine. Un bon endroit pour placer le fichier .P4CONFIG est donc la racine du dépôt Perforce.

Ce fichier contient par exemple les informations de connexion suivantes :

P4PASSWD=xxxxxxxx
P4CLIENT=precise-dell-jmo
P4USER=julien.montmartin
P4PORT=srv-sources:1666

Prettify Symbols

prettify-symbols-mode permet de remplacer certaines séquences par un caractère composé (ou de faire des ligatures, pour reprendre la terminologie d'autres éditeurs). Par exemple, lorsqu'on écrit a<=b, Emacs affiche a≤b. Les séquences à enjoliver sont définies par la liste prettify-symbols-alist, que chaque mode majeur est susceptible d'enrichir. Dans la pratique toutefois, elle semble vide la plupart du temps. Voici donc quelques ajouts faits au niveau de prog-mode, dont tous les modes de développement dérivés profiteront.

(add-hook 'prog-mode-hook
          (lambda ()
            (push '("/=" . ?≠) prettify-symbols-alist)
            (push '("!=" . ?≠) prettify-symbols-alist)
            (push '("==" . ?⩵) prettify-symbols-alist)
            (push '("&&" . ?∧) prettify-symbols-alist)
            (push '("||" . ?∨) prettify-symbols-alist)
            (push '("<=" . ?≤) prettify-symbols-alist)
            (push '(">=" . ?≥) prettify-symbols-alist)
            (push '("<<" . ?≪) prettify-symbols-alist)
            (push '(">>" . ?≫) prettify-symbols-alist)
            (push '("::" . ?∷) prettify-symbols-alist)
            (push '("->" . ?→) prettify-symbols-alist)
            (push '("=>" . ?⇒) prettify-symbols-alist)
            (push '("and" . ?∧) prettify-symbols-alist)
            (push '("not" . ?¬) prettify-symbols-alist)
            (push '("or" . ?∨) prettify-symbols-alist)
            ))

Pour fixer les idées, voici ce à quoi ça ressemble sur quelques lignes de C++

void foo(std∷pair<int, int>* p)
{
    //Print something if first != second
    if(p ≠ nullptr(p→first ≠ p→second))
        std∷cout ≪ p→first « "!=" ≪ p→second « std∷endl;
}

On note que les séquences ne sont pas remplacées dans les chaines de caractères ni dans les commentaires. Elles ne sont pas non plus exportées par Org (j'ai modifié cet exemple à la main). Par ailleurs, la longueur des lignes reste correctement calculée, ce qui facilite la cohabitation avec les paquets comme fci-mode. Il reste quelques petits problèmes, comme une petite marche sur la limite pour les lignes trop longues, mais avec fci-mode, il faut savoir faire des compromis

J'utilise Prettify Symbols depuis peu de temps, il est donc encore en phase de test. Pour l'instant, on l'active dans tous les modes qui le supportent.

(global-prettify-symbols-mode t)

Projectile

Un module qui fournit une fonctionnalité bien pratique : regrouper les fichiers d'un même projet. Lorsqu'on ouvre un fichier, Projectile cherche un dépôt (via la présence d'un .git ou autre) dans le répertoire du fichier ouvert, puis dans ses répertoires parents.

S'il identifie un dépôt, Projectile considère que tous les fichiers du dépôt font partie d'un même projet, et fournit des fonctions pour les traiter ensemble.

(my-require 'projectile)

Active Projectile dans tous les modes le supportant

(projectile-global-mode)

Raccourci C-t pour projectile-find-other-file qui ouvre un fichier associé : Si, par exemple, on est dans un buffer visitant foo.h, Projectile cherche dans le projet foo.c, et l'ouvre s'il le trouve. J'utilisais avant Find File At Point pour cette fonctionnalité, mais je trouve maintenant Projectile plus efficace.

(global-set-key (kbd "C-t") 'projectile-find-other-file)

Projectile utilise Helm pour l'aide à la sélection

(setq projectile-completion-system 'helm)

Active les raccourcis Helm pour les fonctions Projectile

(helm-projectile-on)

Qt

Pour ceux qui développent avec Qt, la fonction generate-qt-includes appelle un petit morceau de shell qui essaie de générer la liste des entêtes nécessaires aux types Qt utilisés. Sans doute pas parfait (tente régulièrement d'inclure QStringLiteral), mais mieux que rien !

(defun generate-qt-includes ()
    "Insert a list of Qt includes matching Qt types found in this buffer"
    (interactive)
    (shell-command-on-region
     (point-min) (point-max)
     ;; Pourquoi ne peut-on pas mettre le pipe en début de ligne ?
     "sed 's/\#.*include.*<.*>/#include <header>/' |
sed 's://.*:// comment:' |
sed -n 's/.*\\(Q[A-Z][a-zA-Z]*\\).*/#include <\\1>/p' |
sort | uniq" )
  (insert-buffer "*Shell Command Output*"))

Le raccourci M-# appelle generate-qt-includes

(global-set-key (kbd "M-#") 'generate-qt-includes)

Rainbow delimiters

Met en évidence les symboles ouvrants et fermants, comme les parenthèses, les accolades ou les crochets, avec des couleurs appariées. Raffiné et élégant !

(my-require 'rainbow-delimiters)

Exemple avec des parenthèses :

(when (foo (bar (baz t))))

Active cette fonctionnalité dans tous les modes de développement

(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)

Rainbow mode

Cherche dans un buffer les chaines représentant une couleur, et les surligne avec cette couleur. Quelquefois il devine mal et surligne des couleurs qui n'en sont pas, mais ça reste très pratique, et joli, ce qui ne gâche rien ! L'essayer c'est l'adopter.

(my-require 'rainbow-mode)

Quelques exemples de couleurs reconnues :

(message "De la couleur ! #ffafff #F5DEB3 #def DeepPink")

Active cette fonctionnalité dans tous les modes de développement

(add-hook 'prog-mode-hook 'rainbow-mode)

Related

Package perso, disponible sur Melpa. Related simplifie les noms des buffers pour obtenir une base, et tous les buffers ayant la même base forment un groupe.

  • C-x appelle related-switch-forward et passe au prochain buffer du groupe
  • C-x appelle related-switch-backward et revient au précédent buffer du groupe

Related permet de naviguer facilement parmi des buffers qui vont ensemble. Par exemple si les trois fichiers suivants sont ouverts :

  • /path/to/include/foo.h
  • /path/to/source/foo.c
  • /path/to/doc/foo.org

On peut passer de l'un à l'autre avec C-x : foo.hfoo.cfoo.orgfoo.h etc.

(my-require 'related)

Active Related, qui est un mode mineur global

(related-mode)

C-x END propose de choisir un buffer du groupe, en utilisant Helm, s'il est activé

(global-set-key (kbd "C-x <end>") 'related-switch-buffer )

Rust

Rust dispose d'un très bon support dans Emacs, au travers des paquets suivants :

  • rust-mode : mode majeur pour Rust, mise en évidence de syntaxe et cie.
  • cargo : raccourcis clavier pour piloter Cargo, le front-end à tout faire de Rust
  • flycheck-rust : mise en évidence des erreurs au fil de la saisie
  • ob-rust : exécution des blocs de code Rust dans Org avec Babel
  • racer : support de Racer, outil externe d'indexation du code
(my-require 'rust-mode)
(my-require 'cargo)
(my-require 'flycheck-rust)
(my-require 'ob-rust)
(my-require 'racer)

On ajoute un peu de configuration pour Racer afin d'avoir les raccourcis habituels M-. et M-* pour aller à la définition d'un symbol et revenir.

(add-hook 'rust-mode-hook 'racer-mode)
(add-hook 'rust-mode-hook (lambda () (local-set-key (kbd "M-.") 'racer-find-definition)))
(add-hook 'rust-mode-hook (lambda () (local-set-key (kbd "M-*") 'pop-tag-mark)))

On peut également utiliser C-M-i pour invoquer completion-at-point, qui ouvre dans un buffer Helm une liste des définitions appropriées trouvées par Racer. Cette liste est plus intéressante que le menu d'Auto Complete, qui se contente des symboles, et non de leurs définitions complètes.

Racer doit évidemment être installé, configuré, et sur le PATH. On peut vérifier que son installation est fonctionnelle en lançant depuis le terminal une commande racer complete :

racer complete std::io::B
MATCH BufRead,1375,10,[...]/rust/src/libstd/io/mod.rs,Trait,pub trait BufRead: Read
MATCH Bytes,1995,11,[...]/rust/src/libstd/io/mod.rs,Struct,pub struct Bytes<R>
MATCH BufReader,56,11,[...]/rust/src/libstd/io/buffered.rs,Struct,pub struct BufReader<R>
MATCH BufWriter,420,11,[...]/rust/src/libstd/io/buffered.rs,Struct,pub struct BufWriter<W: Write>

rust-indent-offset to equal tab-width

Shell scripts

Les fichiers en .sh s'ouvrent avec shell-script-mode, indépendamment de leur shebang. Pour une raison qui reste à déterminer, cette ligne ne fonctionne pas quand elle se trouve au début de ce fichier de configuration.

(add-to-list 'auto-mode-alist '("\\.sh\\'" . sh-mode))

Souris

La molette de la souris fait défiler les lignes deux par deux

(setq mouse-wheel-scroll-amount '(2))

Le défilement à l'écran suit la molette, et le pas reste constant quand elle accélère

(setq mouse-wheel-progressive-speed nil)

Tcl

Ouvre les .tm comme des modules Tcl

(setq auto-mode-alist (append '(("\.tm$" . tcl-mode)) auto-mode-alist))

Tramp

Transfère les fichiers en utilisant ssh, plutôt que ftp

(setq tramp-default-method "ssh")

Unfill

La fonction fill permet de découper correctement les lignes trop longues, et le paquet unfill fournit la fonction inverse, celle qui recolle les lignes. Et même mieux, ce paquet fournit également unfill-toggle, qui passe d'un état à l'autre. Si simple et tellement utile ! Travaille sur la région si elle est définie, sur le paragraphe sinon.

(my-require 'unfill)

Le raccourci par défaut pour fill est remplacé sans états d'âme

(global-set-key (kbd "M-q") 'unfill-toggle)

Uniquify

Génère des libellés plus pertinents pour les buffers de mêmes noms

(my-require 'uniquify)

Soit les fichiers xxx/yyy/foo.txt et zzz/ttt/foo.txt tous les deux ouverts dans des buffers. Plutôt que d'avoir deux buffers affichant foo.txt, on aura avec la méthode post-forward les libellés foo.txt|xxx/yyy et foo.txt|zzz/ttt

(setq uniquify-buffer-name-style 'post-forward)

Whitespace

Un mode mineur qui permet de visualiser les espaces, de nettoyer les lignes blanches, ou encore de visualiser les lignes trop longues. Très paramétrable.

(my-require 'whitespace)

La variable whitespace-style contrôle les types d'espaces qui seront affichés. La première valeur, face, est particulière et active la mise en évidence des espaces en changeant leur couleur de fond. Viennent ensuite :

  • trailing, qui met en évidence les espaces inutiles en fin de ligne
  • lines, qui met en évidence les lignes, essentiellement vides, ne contenant que des espaces, ainsi que les lignes trop longues (voir whitespace-line-column)
  • empty, qui met en évidence les lignes vides en début et fin de fichier
(setq whitespace-style '(face trailing lines empty))

C-h o avec le curseur sur whitespace-style vous emmènera vers la doc qui énumère toutes les catégories d'espaces. À noter que les espaces ne sont pas nécessairement mis en évidence en surlignant leur couleur de fond : on peut également les remplacer et afficher par exemple "›" à la place d'une tabulation.

Lorsqu'on sauvegarde un fichier, on veut que les espaces soient automatiquement remis en ordre : Suppression des lignes vides en début ou fin de fichier, des espaces qui ne servent à rien, application des politiques de tabulation vs espace, etc. Attention au diff la première fois qu'on fait ça sur un fichier.

(add-hook 'before-save-hook 'whitespace-cleanup)

Active whitespace-mode dans tous les modes de développement

(add-hook 'prog-mode-hook 'whitespace-mode)

XML

Une fonction pour remettre en forme du XML. Prise sur le site de Benjamin Ferrari.

(defun pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    (goto-char begin)
    ;; split <foo><foo> or </foo><foo>, but not <foo></foo>
    (while (search-forward-regexp ">[ \t]*<[^/]" end t)
      (backward-char 2) (insert "\n") (incf end))
    ;; split <foo/></foo> and </foo></foo>
    (goto-char begin)
    (while (search-forward-regexp "<.*?/.*?>[ \t]*<" end t)
      (backward-char) (insert "\n") (incf end))
    (indent-region begin end nil)
    (normal-mode))
  (message "All indented!"))

Installation automatique

Finalement, pour simplifier la configuration d'Emacs sur une nouvelle machine, la fonction my-setup installe automatiquement la plupart des paquets utilisés ici. La plupart seulement, car pour certains, il est quand même préférable de voir au cas par cas s'ils sont nécessaires. Inspiré de cette réponse sur Stack Overflow.

(defun my-setup()
  (add-to-list 'package-archives
               '("MELPA" . "http://melpa.milkbox.net/packages/") t)
  (unless package-archive-contents
    (package-refresh-contents))
  (dolist (package '(ac-capf
                     ac-helm
                     auto-complete-config
                     bm
                     cargo
                     cmake-mode
                     column-marker
                     epa-file
                     fill-column-indicator
                     flycheck-rust
                     fuzzy
                     ggtags
                     graphviz-dot-mode
                     helm
                     helm-projectile
                     htmlize
                     idle-highlight-mode
                     leuven
                     magit
                     mmm-mode
                     ob-rust
                     projectile
                     racer
                     rainbow-delimiters
                     rainbow-mode
                     related
                     rust-mode
                     unfill
                     uniquify
                     whitespace))
      (message "---> %s" package)
      (unless (package-installed-p package)
        (ignore-errors
          (package-install package)))))