Dot Emacs

De Ensiwiki
Révision de 27 janvier 2020 à 14:32 par Deroidau (discussion | contributions) (Suppression des liens morts)

(diff) ← Version précédente | Voir la version courante (diff) | Version suivante → (diff)
Aller à : navigation, rechercher

Le .emacs (prononcé "dot emacs") est le fichier de configuration d'Emacs. Il s'agit d'un script, dans un langage spécifique, l'Emacs Lisp (une des nombreuses variantes du langage Lisp). Ce langage est un peu déroutant au début, mais se révèle très puissant et simple à manipuler. Dans le .emacs, vous pouvez changer des raccourcis claviers, charger des bibliothèques externes, et plus généralement changer à peu près totalement le comportement d'Emacs. Cette page présente les bases de l'écriture de .emacs, des modes utiles pour travailler sous Emacs à l'ENSIMAG, et une liste en vrac d'astuces diverses.

Un .emacs très simple pour commencer

Vos enseignants vous ont préparé un .emacs orienté débutant, sur le principe suivant :

  • Minimiser le nombre de choses qui changent par rapport à la configuration par défaut, pour éviter d'être perdu quand on se retrouve sur une autre machine,
  • Beaucoup de choses utiles fournies dans le fichier, mais commentées (il suffit donc de supprimer les ; en début de ligne et de relancer Emacs pour les activer).

Le fichier est disponible ici Dot Emacs simple.

Edition de .emacs

Le .emacs est situé dans votre répertoire personnel, le "home", sous le nom '.emacs' ou '.emacs.el'. Vous pouvez donc l'éditer en lançant emacs ~/.emacs dans un terminal.

Syntaxe

Ce paragraphe n'a pas prétention de vous expliquer le langage, mais de présenter les bases qui permettent de comprendre et de modifier le .emacs. Lisp est composé (en gros) de variables et de fonctions, comme à peu près tous les langages. La seule et unique façon d'éxécuter du code en Lisp est de construire une "form", c'est à dire une expression parenthésée, en notation infixée. Ainsi, f(x, y) en C devient (f x y), et 2*(3+4) s'écrit (* 2 (+ 3 4)). Par exemple :

;;définit la fonction square
(defun square (x)
  (* x x))
;;calcule le carré de 42
(square 42)

Pour l'essayer directement dans Emacs, plusieurs solutions s'offrent à vous :

  • vous pouvez utiliser M-: (qui prompte pour un bout de lisp à éxécuter) ;
  • vous pouvez copier coller le code dans Emacs et faire C-x C-e sur une parenthèse fermante pour éxécuter le code qui la précède ;
  • ou vous pouvez le copier coller dans le buffer *scratch*, dans lequel C-j évalue l'expression située avant le curseur (à la manière de C-x C-e) et insère le résultat sur la ligne suivante.

Fonctions pour la personalisation

  • setq change la valeur d'une variable. Par exemple, (setq c-default-style "linux") change la valeur de la variable c-default-style (le style d'indentation utilisé pour éditer du C) en la string "linux" (en l'occurence, le style d'indentation préféré par le noyau linux)
  • global-set-key ajoute un keybinding général à tout Emacs. Par exemple, (global-set-key (kbd "C-S-k") 'kill-whole-line) affecte au raccourci "C-S-k" (control + shift + k. Pour voir l'expression Emacs correspondant à un raccourci, faites C-h k suivi de votre raccourci) la fonction kill-whole-line. La simple quote est là pour retarder l'évaluation de kill-whole-line : on veut passer à la fonction global-set-key l'identifieur de la fonction afin qu'il puisse y faire référence ultérieurement, pas l'expression de la fonction (ce qui serait ce qui se passerait si on oubliait la quote).
  • customize (accessible interactivement via M-x customize) fournit une interface très pratique pour la modification de réglages d'Emacs. Les réglages modifiés sont enregistrés dans le .emacs (la partie "custom-set-variables").
  • add-hook permet d'éxécuter du code à certains moments précis. Par exemple :
(add-hook 'c-mode-hook (lambda ()
			 (message "Vous êtes maintenant en mode
			 C. Attention, Emacs ne protège pas
			 contre les segfaults !")))

Le lambda permet de définir des fonctions anonymes. L'alternative aurait été de déclarer une fonction avec defun et de la passer à add-hook. Lambda est souvent utilisé pour les petites fonctions.


Plus d'informations sont disponibles sur internet, notamment dans le manuel Emacs de GNU, et dans l'aide intégrée (voir Raccourcis claviers Emacs).

Modules utiles

Windmove

Permet de facilement naviguer entre les différentes fenêtres avec alt+flèches directionelles (ou autre au choix, voir documentation)

(windmove-default-keybindings 'meta)

Electric buffer

Une liste des buffers ouverts. Il y a aussi ibuffer.

(global-set-key (kbd "C-x C-b") 'electric-buffer-list)

Ido et iswitchb

Ido change le comportement de quelques commandes qui lisent le minibuffer (C-x C-f, C-x b ...) pour avoir une meilleure autocomplétion.

(ido-mode 1)

iswitchb fait essentiellement la même chose, mais seulement pour les commandes ayant un rapport avec le changement de buffer, pour ceux qui préfèrent quelque chose de plus classique pour ouvrir leurs fichiers.

(iswitchb-mode 1)

Compilation

Différentes fonctions pour la compilation. A lancer par M-x my-compile (ou à associer à un raccourci). On peut spécifier la commande à éxécuter par C-u M-x my-compile.

;;make compile window disappear after successful compilation
(setq compilation-finish-function
      (lambda (buf str)
	(if (string-match "*Compilation*" (buffer-name buf))
	    (if (string-match "abnormally" str)
		(message "There were errors :-(")
	      ;;no errors, make the compilation window go away in 1 second
	      (run-at-time 1 nil
			   (lambda (buf)
			     (delete-windows-on buf)
			     (bury-buffer buf))
			   buf)
	      (message "No errors :-)")))))

;;my-compile is smarter about how to display the new buffer
(defun display-buffer-by-splitting-largest (buffer force-other-window)
  "Display buffer BUFFER by splitting the largest buffer vertically, except if
  there is already a window for it."
  (or (get-buffer-window buffer)
      (let ((new-win 
	     (with-selected-window (get-largest-window)
	       (split-window-vertically))))
	(set-window-buffer new-win buffer)
	new-win)))

(defun my-compile ()
  "Ad-hoc display of compilation buffer."
  (interactive)
  (let ((display-buffer-function 'display-buffer-by-splitting-largest))
    (call-interactively 'compile)))

;;misc compilation settings
(setq-default
 compile-command "make"
 compilation-read-command nil
 compilation-scroll-output 'first-error
 compilation-ask-about-save nil
 compilation-window-height 6
 compilation-auto-jump-to-first-error t)

Astuces diverses

Switcher facilement entre deux buffers

Permet de facilement passer d'un buffer à l'autre avec super(touche windows) + tab.

(global-set-key [\s-tab] (lambda ()
			   (interactive)
			   (switch-to-buffer (other-buffer))))

Fichiers récents

Liste les fichiers récents. Le gros bout de lisp est là pour utiliser ido. Si vous n'utilisez pas ido, (recentf-mode 1) suffit.

(defun basename-cons(f)
  (cons (file-name-nondirectory f) f))
(defun recentf-ido-find-file-or-maybe-list (&optional arg)
  "Find a recent file using Ido, or list all recent files if prefixed"
  (interactive "P")
  (if arg
      (recentf-open-files)
    ;;build alist basename->name, offer user a choice of basenames,
    ;;then get matching file and find it
    (let ((file-alist (mapcar 'basename-cons recentf-list))
	  (basename-list (mapcar 'file-name-nondirectory recentf-list)))
      (let ((file (ido-completing-read
		   "Choose recent file: "
		   (mapcar 'file-name-nondirectory
			   recentf-list)
		   nil t)))
	(when file
	  (find-file (cdr (assoc file file-alist))))))))

(recentf-mode 1)
(global-set-key (kbd "C-x C-r") 'recentf-ido-find-file-or-maybe-list)

Tab complétion partout

L'autocomplétion avec TAB de bash, c'est magique. Pour avoir pareil partout dans Emacs, cette fonction lance une autocomplétion via dabbrev (autocomplétion basée sur les mots des buffers ouverts) à chaque fois que la touche TAB est pressée et ne bouge pas le curseur (typiquement en mode de programmation, ca préserve le comportement d'indentation).

(defmacro ad-add-advice-to-key (key expr)
  "Around advice the key KEY with expression EXPR. KEY should be
a key in the format accepted by key-binding and such, and EXPR an
expression of the same type as those required by around advices"
  `(add-hook 'pre-command-hook
	     (lambda ()
	       (when (equal (this-command-keys-vector) ,key)
		 (ad-add-advice this-command
				'(azerrswdf ;arbitrary advice name
				  nil	    ;not protected
				  t	    ;activated
				  (lambda ()
				    ,expr
				    (ad-unadvise this-command)))
				'around
				'last)
		 (ad-activate this-command)))))

(ad-add-advice-to-key [9] ;;tab
		      (let ((p (point)))
			ad-do-it
			(when (and (= p (point))
				   (not (bolp))
				   (looking-at "\\_>"))
			  (dabbrev-expand nil))))


Divers

Petits bouts de lisp en vrac.

;;gagner du temps sur les confirmations
(fset 'yes-or-no-p 'y-or-n-p)
 
;;garder l'historique du minibuffer entre les sessions
(savehist-mode 1)

;;sert à rien :)
(blink-cursor-mode -1)
 
;;nom du buffer en cours dans la barre de titre
(setq frame-title-format "%b - Emacs")
(setq icon-title-format "%b - Emacs")
 
;;backups/autosaves : pas d'autosaves (encombrant), et garder les backups dans un dossier à part
(setq auto-save-default nil)
(defvar backup-dir "~/.emacsbackups/")
(setq backup-directory-alist (list (cons "." backup-dir)))

;;toujours s'assurer que le fichier finit par une newline
(setq require-final-newline 't)
 
;;matching visuel des parenthèses
(show-paren-mode t)

Discipliner l'ada-mode

Par défaut, l'ada-mode est très peu emacsien-friendly ; il aime bien faire les choses lui-même sans vous laisser intervenir, ce qui est très contraire à l'esprit de configurabilité d'Emacs. Heureusement, voici quelques petits morceaux de code pour vous aider à dompter la bête. Ces bouts de code sont commentés ; vous pouvez les inclure verbatim, même si vous ne comprenez guère l'elisp, mais lisez tout de même les commentaires, avant de le faire, histoire de savoir à quoi vous attendre :

;; Une fonction de remplacement pour ada-indent-newline-indent, qui
;; n'indente pas la nouvelle ligne, comme il est conventionnel
;; d'attribuer à RET. Cette fonction respecte la variable
;; ada-indent-after-return, et ne laisse pas traîner d'espaces moches
;; sur la ligne si l'on est en début de ligne.
(defun my-ada-indent-newline ()
  "Insert a newline. The original line is indented first if
`ada-indent-after-return' is non-nil."
  (interactive "*")
  (when (and ada-indent-after-return (not (bolp)))
    (ada-indent-current))
  (newline))

;; Pareil qu'au-dessus mais là on indente la nouvelle ligne,
;; remplacement direct d'ada-indent-newline-indent, mais sans les
;; espaces moches qui traînent.
(defun my-ada-indent-newline-indent ()
  "Insert a newline then indent. The original line is indented
first if `ada-indent-after-return' is non-nil."
  (interactive "*")
  (minh-ada-indent-newline)
  (ada-indent-current))

;; Si vous ne voulez pas qu'ada-mode vous impose sa manière de mettre
;; des majuscules de partout.
(setq ada-auto-case nil)

;; Si vous voulez qu'ada-mode réindente votre ligne quand vous allez
;; à la ligne suivante.
(setq ada-indent-after-return t)

;; Ces trois variables contrôlent la manière dont est indentée
;; automatiquement le code Ada (sauf quand ada-mode se shitsu...). Le
;; style ci-dessous est celui que j'ai adopté pour l'instant, le style
;; par défaut est équivalent à mettre 3 partout. Pas très important,
;; pour les gens avec des ptit(e)s maniaques en puissance.
(setq ada-indent 4)
(setq ada-when-indent 0)
(setq ada-indent-record-rel-type 0)

;; Si vous avez laissé ada-auto-case par défaut, les lignes suivantes
;; permettent de remplacer les commandes associées aux touches RET et
;; C-j (aussi appelée LFD pour Line FeeD, ou plus simplement LF, ou
;; encore NL pour New Line, etc.) par les versions persos définies
;; plus haut.
(setq ada-ret-binding 'my-ada-indent-newline)
(setq ada-lfd-binding 'my-ada-indent-newline-indent)

(defun my-ada-mode-init ()
  ;; Si vous avez choisi de virer l'ada-auto-case, les deux lignes
  ;; suivantes font la même chose que les deux setq précédents.
  (define-key ada-mode-map "\r" 'my-ada-indent-newline)
  (define-key ada-mode-map "\C-j" 'my-ada-indent-newline-indent))
(add-hook 'ada-mode-hook 'my-ada-mode-init)

Et pour ceux qui touchent un peu l'elisp, voici quelques petites choses bonnes à savoir :

  • Quand ada-auto-case est t, Ada semble exécuter du code derrière votre dos, c'est-à-dire après le ada-mode-hook (qui devrait être la dernière chose exécutée par un mode lors de son initialisation), le vilain. Entre autre, ce code écrase certains bindings (raccourcis claviers) que vous auriez pu vouloir mettre dans votre hook, donc faites attention, si vous utilisez par ex. le skeleton-pair-mode, ou si tout simplement le comportement de la touche RET par défaut vous agace et que vous avez eu la bonne idée d'écraser le dans votre hook.
  • Il ne semble pas y avoir d'option pour qu'ada-mode associe RET à quelque chose de plus conventionnel que son indent-newline-indent (qui comme son nom l'indique, indente la ligne courante, va à la ligne, et indente la nouvelle ligne), donc il faudra le faire vous-même.
  • ada-mode ne se soucie pas de laisser des espaces à la fin des lignes un peu partout comme un porc, notamment quand vous utilisez sa fonction indent-newline-indent, donc il faudra faire vous-même la vôtre ou rajouter un hook à l'enregistrement pour appeler delete-trailing-whitespace.

Exemples de .emacs

Liens externes