Les outils de développement sous FreeBSD : Guide de l'utilisateur
Précédent |  | Suivant

# 6. Utiliser Emacs comme environnement de développement

## 6.1. Emacs

Les systèmes Unix ne s'accompagnent malheureusement pas du type d'environnement de développement intégré du genre ``tout ce que vous avez toujours voulu et encore beaucoup plus en un seul monstrueux paquetage'' dont disposent d'autres systèmes[1]. Il est cependant possible de mettre au point votre propre environnement. Il ne sera peut-être pas aussi esthétique et il ne sera peut-être pas aussi intégré, mais vous pourrez le configurer comme vous le voulez. Et il est gratuit. En plus, vous en avez les sources.

Emacs est la clé de tout. Il y a bien des gens qui le décrient, mais nombreux sont ceux qui l'aiment. Si vous êtes des premiers, j'ai peur que cette section n'ait que peu d'intérêt pour vous. Il vous faudra aussi pas mal de mémoire pour l'utiliser. Je conseille 8Mo en mode texte et 16Mo sous X comme strict minimum pour avoir des temps de réponse raisonnables.

Emacs est essentiellement un éditeur extrêmement configurable - il a de fait été configuré au point de ressembler plus à un système d'exploitation qu'à un éditeur! De nombreux développeurs et administrateurs système passent le plus clair de leur temps à travailler sous Emacs, le quittant seulement pour se déconnecter.

Il est impossible de même résumer ici tout ce qu'Emacs est capable de faire, mais voici quelques fonctionnalités qui intéressent les développeurs:

- Editeur très puissant qui permet et de rechercher et remplacer des chaînes de caractères et d'utiliser pour le faire des expressions régulières (motifs), d'aller au début ou à la fin de blocs syntaxiques, etc, etc.

- Menus déroulants et aide en ligne.

- Mise en valeur et indentation en fonction de la syntaxe du langage utilisé.

- Complètement configurable.

- Vous pouvez compiler et déboguer des programmes depuis Emacs.

- En cas d'erreur à la compilation, vous pouvez aller directement à la ligne qui en est la cause.

- Interface ergonomique au programme `info` qui sert à lire la documentation hypertexte GNU, dont celle d'Emacs lui-même.

- Interface conviviale pour `gdb`, qui vous permet de visualiser le code source en même temps que vous exécutez pas à pas le programme.

- Vous pouvez lire les ``news'' Usenet et votre courrier électronique pendant que votre programme compile.

Et sans aucun doute bien d'autres choses qui m'ont échappées.

Emacs peut être installé sous FreeBSD sous forme de logiciel porté.

Une fois qu'il est installé, lancez-le et tapez `C-h t` - ce qui signifie maintenir enfoncée la touche Ctrl, taper h, relâcher la touche Ctrl, et appuyer ensuite sur t - pour lire le guide d'Emacs (Vous pouvez aussi utiliser la souris pour sélectionner Emacs Tutorial - ``Guide Emacs'' - depuis le menu Help - ``Aide'').

Bien qu'Emacs ait des menus, il vaut la peine d'apprendre à utiliser les raccourcis claviers, parce qu'il est bien plus rapide quand vous éditez quelque chose d'appuyer sur deux ou trois touches que de courir après la souris et cliquer ensuite au bon endroit. Si, par ailleurs, vous discutez avec des utilisateurs expérimentés d'Emacs, vous vous apercevrez qu'ils utilisent assez couramment des expressions comme ```M-x replace-s RET foo RET bar RET`'', il peut donc servir de comprendre ce qu'ils veulent dire. Et de toute façon, Emacs a bien plus de fonctions utiles qu'il ne peut en tenir sur une barre de menus.

Il est heureusement assez facile de découvrir les raccourcis claviers, ils sont affichés dans les menus. Je vous conseille d'utiliser les menus pour, par exemple, ouvrir un fichier jusqu'à ce que vous compreniez comment cela marche et ayez suffisamment confiance en vous, puis d'essayer C-x C-f. Une fois que cela vous convient, passez à une autre des commandes des menus.

Si vous ne vous rappelez pas ce que fait une combinaison donnée de touches, choisissez Describe Key - ``Description d'une touche'' - dans le menu Help - ``Aide'' - et tapez cette combinaison - Emacs vous dira ce qu'elle fait. Vous pouvez aussi utiliser le choix Command Apropos - ``A propos d'une commande'' - pour connaître toutes les commandes comportant un mot donné et les touches qui leur correspondent.

Au fait, l'expression plus haut signifie: enfoncer la touche Méta, appuyer sur x, relâcher la touche Méta, taper `replace-s` (abréviation de `replace-string` - ``remplacer une chaîne de caractères'' - une autre caractéristique d'Emacs est de vous permettre d'abréger les commandes), appuyer sur Entrée, taper `foo` (la chaîne que vous voulez remplacer), appuyer sur Entrée, taper `bar` (la chaîne avec laquelle vous voulez remplacer `foo`) et appuyer encore sur Entrée. Emacs effectuera alors l'opération de recherche et remplacement que vous venez de demander.

Si vous vous demandez ce qu'est la touche Méta, c'est une touche spéciale qu'ont beaucoup de stations Unix. Malheureusement, les PCs n'en ont pas, c'est habituellement la touche Alt qui est utilisée (ou si vous n'avez pas de chance, la touche Echap).

Oh, et pour sortir d'Emacs, tapez `C-x C-c` (Ce qui signifie: enfoncer la touche Ctrl, appuyer sur c, appuyer sur x et relâcher la touche Ctrl). S'il y a des fichiers ouverts que vous n'avez pas sauvegardés, Emacs vous demandera si vous voulez les sauvegarder. (Oubliez que la documentation dit que la méthode habituelle pour quitter Emacs est d'utiliser `C-z` - cela laisse Emacs actif en tâche de fond et n'est réellement utile que si vous êtes sur un système qui ne gère pas de terminaux virtuels).

## 6.2. Configurer Emacs

Emacs fait des choses admirables; certaines fonctionnalités sont incorporées, d'autres doivent être configurées.

Au lieu d'utiliser un langage de macros-instructions propriétaires, Emacs se sert d'une version de Lisp spécialement adaptée aux éditeurs, connue sous le nom de Emacs Lisp. Ce peut être très utile si vous voulez aller plus loin et apprendre ensuite par exemple Common Lisp, parce qu'il est considérablement plus léger que Common Lisp (quoique qu'encore assez imposant!).

La meilleure façon d'apprendre Emacs Lisp est de télécharger le Guide Emacs Lisp.

Il n'y a cependant pas besoin de connaître quoique ce soit à Lisp pour commencer à configurer Emacs, parce que j'ai inclu un fichier `.emacs` d'exemple, qui devrait suffire au début. Copiez-le simplement dans votre répertoire utilisateur et relancez Emacs, s'il s'exécute déjà; il lira les commandes du fichier et (je l'espère) vous fournira une configuration de base utile.

## 6.3. Un exemple de fichier `.emacs`

Il contient malheureusement beaucoup trop de choses pour tout expliquer en détails; il y a cependant un ou deux points intéressants à mentionner.

- Tout ce qui commence par un `;` est en commentaire et est ignoré par Emacs.

- La première ligne, `-*- Emacs-Lisp -*-` permet d'éditer le fichier `.emacs` lui-même sous Emacs et de profiter de toutes les fonctionnalitées liées à l'édition de code Emacs Lisp. Emacs tente habituellement de deviner le type de fichier en fonction de son nom, mais risque de ne pas y arriver pour le fichier `.emacs`.

- La touche Tab est utilisée pour l'indentation dans certains modes, de sorte que si vous appuyez sur cette touche cela indente la ligne de code courante. Si vous voulez mettre un caractère tabulation dans votre texte, enfoncer la touche Ctrl en même temps que vous appuyez sur Tab.

- Ce fichier permet le mise en valeur syntaxique de code C, C++, Perl, Lisp et Scheme, en déterminant le langage d'après le nom du fichier édité.

- Emacs a déjà une fonction prédéfinie appelée `next-error` - ``erreur suivante''. Dans la fenêtre de résultats d'une compilation, cela vous permet d'aller d'une erreur à la suivante avec `M-n`; nous définissons la fonction complémentaire `previous-error` - ``erreur précédente'', qui vous permet de retourner à l'erreur précédente avec `M-p`. Le plus sympathique est que `C-c C-c` ouvrira le fichier source où l'erreur s'est produite et ira à la ligne concernée.

- Nous activons la possibilité qu'a Emacs d'agir comme serveur, de façon à ce que si vous travaillez hors d'Emacs et voulez éditer un fichier, il vous suffise de taper:

      % emacsclient nom_du_fichier

  pour pouvoir ensuite le modifier avec Emacs![2].

Exemple 1. Un exemple de fichier `.emacs`

    ;; -*-Emacs-Lisp-*-

    ;; Ce fichier est conçu pour être relu; la variable
    ;; first-time est utilisée pour éviter les problèmes
    ;; que cela pourra poser.
    (defvar first-time t
      "Indicateur signifiant que le fichier .emacs est lu pour la première fois")

    ;; Méta
    (global-set-key "\M- " 'set-mark-command)
    (global-set-key "\M-\C-h" 'backward-kill-word)
    (global-set-key "\M-\C-r" 'query-replace)
    (global-set-key "\M-r" 'replace-string)
    (global-set-key "\M-g" 'goto-line)
    (global-set-key "\M-h" 'help-command)

    ;; Touches fonction
    (global-set-key [f1] 'manual-entry)
    (global-set-key [f2] 'info)
    (global-set-key [f3] 'repeat-complex-command)
    (global-set-key [f4] 'advertised-undo)
    (global-set-key [f5] 'eval-current-buffer)
    (global-set-key [f6] 'buffer-menu)
    (global-set-key [f7] 'other-window)
    (global-set-key [f8] 'find-file)
    (global-set-key [f9] 'save-buffer)
    (global-set-key [f10] 'next-error)
    (global-set-key [f11] 'compile)
    (global-set-key [f12] 'grep)
    (global-set-key [C-f1] 'compile)
    (global-set-key [C-f2] 'grep)
    (global-set-key [C-f3] 'next-error)
    (global-set-key [C-f4] 'previous-error)
    (global-set-key [C-f5] 'display-faces)
    (global-set-key [C-f8] 'dired)
    (global-set-key [C-f10] 'kill-compilation)

    ;; Touches curseur
    (global-set-key [up] "\C-p")
    (global-set-key [down] "\C-n")
    (global-set-key [left] "\C-b")
    (global-set-key [right] "\C-f")
    (global-set-key [home] "\C-a")
    (global-set-key [end] "\C-e")
    (global-set-key [prior] "\M-v")
    (global-set-key [next] "\C-v")
    (global-set-key [C-up] "\M-\C-b")
    (global-set-key [C-down] "\M-\C-f")
    (global-set-key [C-left] "\M-b")
    (global-set-key [C-right] "\M-f")
    (global-set-key [C-home] "\M-<")
    (global-set-key [C-end] "\M->")
    (global-set-key [C-prior] "\M-<")
    (global-set-key [C-next] "\M->")

    ;; Souris
    (global-set-key [mouse-3] 'imenu)

    ;; Divers
    (global-set-key [C-tab] "\C-q\t")   ; Ctrl tab = caractère tabulation.
    (setq backup-by-copying-when-mismatch t)

    ;; 'y' ou <CR> équivaut à yes, 'n' à no.
    (fset 'yes-or-no-p 'y-or-n-p)
        (define-key query-replace-map [return] 'act)
        (define-key query-replace-map [?\C-m] 'act)

    ;; Paquetages à charger
    (require 'desktop)
    (require 'tar-mode)

    ;; Mode diff évolué
    (autoload 'ediff-buffers "ediff" "Interface Emacs intelligente pour diff" t)
    (autoload 'ediff-files "ediff" "Interface Emacs intelligente pour diff" t)
    (autoload 'ediff-files-remote "ediff"
      "Interface Emacs intelligente pour diff")

    (if first-time
        (setq auto-mode-alist
              (append '(("\\.cpp$" . c++-mode)
                         ("\\.hpp$" . c++-mode)
                         ("\\.lsp$" . lisp-mode)
                         ("\\.scm$" . scheme-mode)
                         ("\\.pl$" . perl-mode)
                         ) auto-mode-alist)))

    ;; Mise en valeur syntaxique automatique
    (defvar font-lock-auto-mode-list
      (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'lisp-mode 'perl-mode 'scheme-mode)
      "Listes des modes à démarrer toujours avec mise en valeur")

    (defvar font-lock-mode-keyword-alist
      '((c++-c-mode . c-font-lock-keywords)
        (perl-mode . perl-font-lock-keywords))
      "Associations entre modes et mots-clés")

    (defun font-lock-auto-mode-select ()
      "Sélectionne automatiquement type de mise en valeur si le major mode courant est dans font-lock-auto-mode-list"
      (if (memq major-mode font-lock-auto-mode-list)
          (progn
          (font-lock-mode t))
        )
      )

    (global-set-key [M-f1] 'font-lock-fontify-buffer)

    ;; Nouveau dabbrev
    ;(require 'new-dabbrev)
    (setq dabbrev-always-check-other-buffers t)
    (setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
    (add-hook 'emacs-lisp-mode-hook
              '(lambda ()
                (set (make-local-variable 'dabbrev-case-fold-search) nil)
                (set (make-local-variable 'dabbrev-case-replace) nil)))
    (add-hook 'c-mode-hook
              '(lambda ()
                (set (make-local-variable 'dabbrev-case-fold-search) nil)
                (set (make-local-variable 'dabbrev-case-replace) nil)))
    (add-hook 'text-mode-hook
              '(lambda ()
                (set (make-local-variable 'dabbrev-case-fold-search) t)
                (set (make-local-variable 'dabbrev-case-replace) t)))

    ;; Mode C++ et C...
    (defun my-c++-mode-hook ()
      (setq tab-width 4)
      (define-key c++-mode-map "\C-m" 'reindent-then-newline-and-indent)
      (define-key c++-mode-map "\C-ce" 'c-comment-edit)
      (setq c++-auto-hungry-initial-state 'none)
      (setq c++-delete-function 'backward-delete-char)
      (setq c++-tab-always-indent t)
      (setq c-indent-level 4)
      (setq c-continued-statement-offset 4)
      (setq c++-empty-arglist-indent 4))

    (defun my-c-mode-hook ()
      (setq tab-width 4)
      (define-key c-mode-map "\C-m" 'reindent-then-newline-and-indent)
      (define-key c-mode-map "\C-ce" 'c-comment-edit)
      (setq c-auto-hungry-initial-state 'none)
      (setq c-delete-function 'backward-delete-char)
      (setq c-tab-always-indent t)
    ;; indentation style BSD
      (setq c-indent-level 4)
      (setq c-continued-statement-offset 4)
      (setq c-brace-offset -4)
      (setq c-argdecl-indent 0)
      (setq c-label-offset -4))

    ;; Mode Perl...
    (defun my-perl-mode-hook ()
      (setq tab-width 4)
      (define-key c++-mode-map "\C-m" 'reindent-then-newline-and-indent)
      (setq perl-indent-level 4)
      (setq perl-continued-statement-offset 4))

    ;; Mode Scheme...
    (defun my-scheme-mode-hook ()
      (define-key scheme-mode-map "\C-m" 'reindent-then-newline-and-indent))

    ;; Mode Emacs-Lisp...
    (defun my-lisp-mode-hook ()
      (define-key lisp-mode-map "\C-m" 'reindent-then-newline-and-indent)
      (define-key lisp-mode-map "\C-i" 'lisp-indent-line)
      (define-key lisp-mode-map "\C-j" 'eval-print-last-sexp))

    ;; Ajouts des précédents
    (add-hook 'c++-mode-hook 'my-c++-mode-hook)
    (add-hook 'c-mode-hook 'my-c-mode-hook)
    (add-hook 'scheme-mode-hook 'my-scheme-mode-hook)
    (add-hook 'emacs-lisp-mode-hook 'my-lisp-mode-hook)
    (add-hook 'lisp-mode-hook 'my-lisp-mode-hook)
    (add-hook 'perl-mode-hook 'my-perl-mode-hook)

    ;; L'inverse de next-error
    (defun previous-error (n)
      "Aller à l'erreur de compilation précédente et au code correspondant."
      (interactive "p")
      (next-error (- n)))

    ;; Divers...
    (transient-mark-mode 1)
    (setq mark-even-if-inactive t)
    (setq visible-bell nil)
    (setq next-line-add-newlines nil)
    (setq compile-command "make")
    (setq suggest-key-bindings nil)
    (put 'eval-expression 'disabled nil)
    (put 'narrow-to-region 'disabled nil)
    (put 'set-goal-column 'disabled nil)

    ;; Recherche dans les archives Elisp
    (autoload 'format-lisp-code-directory "lispdir" nil t)
    (autoload 'lisp-dir-apropos "lispdir" nil t)
    (autoload 'lisp-dir-retrieve "lispdir" nil t)
    (autoload 'lisp-dir-verify "lispdir" nil t)

    ;; Mise en valeur syntaxique
    (defun my-make-face (face colour &optional bold)
      "Créer une apparence pour une couleur, éventuellement en gras"
      (make-face face)
      (copy-face 'default face)
      (set-face-foreground face colour)
      (if bold (make-face-bold face))
      )

    (if (eq window-system 'x)
        (progn
          (my-make-face 'blue "blue")
          (my-make-face 'red "red")
          (my-make-face 'green "dark green")
          (setq font-lock-comment-face 'blue)
          (setq font-lock-string-face 'bold)
          (setq font-lock-type-face 'bold)
          (setq font-lock-keyword-face 'bold)
          (setq font-lock-function-name-face 'red)
          (setq font-lock-doc-string-face 'green)
          (add-hook 'find-file-hooks 'font-lock-auto-mode-select)

          (setq baud-rate 1000000)
          (global-set-key "\C-cmm" 'menu-bar-mode)
          (global-set-key "\C-cms" 'scroll-bar-mode)
          (global-set-key [backspace] 'backward-delete-char)
                                           ;      (global-set-key [delete] 'delete-char)
          (standard-display-european t)
          (load-library "iso-transl")))

    ;; X11 ou PC écrivant directement à l'écran
    (if window-system
        (progn
          ;;      (global-set-key [M-f1] 'hilit-repaint-command)
          ;;      (global-set-key [M-f2] [?\C-u M-f1])
          (setq hilit-mode-enable-list
                 '(not text-mode c-mode c++-mode emacs-lisp-mode lisp-mode
                       scheme-mode)
                 hilit-auto-highlight nil
                 hilit-auto-rehighlight 'visible
                 hilit-inhibit-hooks nil
                 hilit-inhibit-rebinding t)
          (require 'hilit19)
          (require 'paren))
      (setq baud-rate 2400)         ; Pour les connections série lentes
      )

    ;; Terminal type TTY
    (if (and (not window-system)
             (not (equal system-type 'ms-dos)))
        (progn
          (if first-time
              (progn
                (keyboard-translate ?\C-h ?\C-?)
                (keyboard-translate ?\C-? ?\C-h)))))

    ;; Sous UNIX
    (if (not (equal system-type 'ms-dos))
        (progn
          (if first-time
              (server-start))))

    ;; Ajouter ici toute modification d'apparence des caractères
    (add-hook 'term-setup-hook 'my-term-setup-hook)
    (defun my-term-setup-hook ()
      (if (eq window-system 'pc)
          (progn
    ;;  (set-face-background 'default "red")
        )))

    ;; Restaurer le "bureau" - le faire le plus tard possible
    (if first-time
        (progn
          (desktop-load-default)
          (desktop-read)))

    ;; Indique que le fichier a été lu au moins une fois
    (setq first-time nil)

    ;; Plus besoin de déboguer quoi que ce soit maintenant.
    (setq debug-on-error nil)

    ;; C'est tout
    (message "OK, %s%s" (user-login-name) ".")

## 6.4. Permettre à Emacs de comprendre de nouveaux langages

Bon, tout cela est très bien si vous ne voulez programmer qu'avec les langages déjà introduits dans le fichier `.emacs` (C, C++, Perl, Lisp et Scheme), mais que se passe-t-il quand un nouveau langage appelé ``whizbang'' fait son apparition, avec plein de nouvelles fonctionnalités attrayantes?

La première chose à faire est de regarder si whizbang s'accompagne de fichiers de configuration d'Emacs pour ce langage. Ces fichiers ont généralement comme extension `.el`, raccourci pour ``Emacs Lisp''. Par exemple, si whizbang est un logiciel porté pour FreeBSD, ces fichiers peuvent être repérés par:

    % find /usr/ports/lang/whizbang -name "*.el" -print

et il faut les installer en le copiant dans le répertoire du ``site Lisp'' d'Emacs. Sous FreeBSD 2.1.0-RELEASE, c'est le répertoire `/usr/local/share/emacs/site-lisp`.

Ainsi par exemple, si la commande précédente nous donnait:

    /usr/ports/lang/whizbang/work/misc/whizbang.el

nous le copierions alors comme suit:

    % cp /usr/ports/lang/whizbang/work/misc/whizbang.el /usr/local/share/emacs/site-lisp

Décidons ensuite de l'extension que doivent avoir les fichiers source whizbang. Supposons, pour les besoins de l'exemple, qu'ils se terminent tous par `.wiz`. Il faut ajouter une entrée à notre fichier `.emacs`, pour être sûr qu'Emacs puisse utiliser les informations du fichier `whizbang.el`.

Recherchons l'entrée `auto-mode-alist` dans `.emacs` et ajoutons une ligne pour whizbang, par exemple:

    ...
    ("\\.lsp$" . lisp-mode)
    ("\\.wiz$" . whizbang-mode)
    ("\\.scm$" . scheme-mode)
    ...

Cela signifie qu'Emacs passera automatiquement en `whizbang-mode` à l'édition d'un fichier d'extension `.wiz`.

Juste après, il y a une entrée `font-lock-auto-mode-list`. Ajoutez-y `whizbang-mode` comme ceci:

    ;; Auto font-lock-mode
    (defvar font-lock-auto-mode-list
      (list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'whizbang-mode 'lisp-mode 'perl-mode 'scheme-mode)
      "Listes des modes à démarrer toujours en font-lock-mode")

Ce qui signifie qu'Emacs activera toujours le `font-lock-mode` (i.e., la mise en valeur syntaxique) à l'édition d'un fichier `.wiz`.

Cela suffit. S'il y a autre chose que vous voulez automatiser à l'ouverture d'un fichier `.wiz`, vous pouvez ajouter un `whizbang-mode hook` (voyez mon `my-scheme-mode-hook` pour avoir un exemple simple qui ajoute un `auto-indent` - indentation automatique).

### Notes

[1] | Au moins, pas à moins que vous ne soyez prêt à les payer une somme astronomique.
[2] | De nombreux utilisateurs d'Emacs affectent à leur variable d'environnement `EDITOR` la valeur `emacsclient` de façon à ce que ce soit ce qui se produise chaque fois qu'ils ont besoin d'éditer un fichier.

Précédent | Sommaire | Suivant
Déboguer |  | A lire pour aller plus loin

Ce document, ainsi que d'autres peut être téléchargé sur ftp.FreeBSD.org/pub/FreeBSD/doc/.

Pour toutes questions à propos de FreeBSD, lisez la documentation avant de contacter <questions@FreeBSD.org>.
Pour les questions sur cette documentation, contactez <doc@FreeBSD.org>.