Articles du blog - Page 1

Récupérer les images d’une galerie en python

Quand je veux récupérer plusieurs pages, je le fais souvent avec wget. L’utilisation des options --load-cookies, --save-cookies et --keep-session-cookies avec --post-data étant plus que pratique. Pour parser, ensuite, c’est souvent pénible, même si awk est là pour aider.

man wget donne :

# Log in to the server.  This can be done only once.
 wget --save-cookies cookies.txt \
      --post-data 'user=foo&password=bar' \
      http://server.com/auth.php

 # Now grab the page or pages we care about.
 wget --load-cookies cookies.txt \
      -p http://server.com/interesting/article.php

Pour parser, j’aime bien python ; je trouve ça plus lisible. Par contre, ce que j’utilisais jusqu’à présent pour me logguer sur une page était moins glop :

import urllib, urllib2, cookielib

username = 'foo'
password = 'bar'

cj = cookielib.CookieJar()
handle = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
login_data = urllib.urlencode({'username' : username, 'pwd' : password})
handle.open('http://www.example.com/login.php', login_data)
res = handle.open('http://www.example.com/my_account.php')
print res.read()

Mais ça, c’était avant. Depuis, j’ai découvert Requests. Pour faire un POST, c’est aussi simple que :

import requests

url= 'http://www.example.com/login.php'
login_data = {username = 'myuser', password = 'mypassword'}

req = requests.post(url, login_data)
page = req.content

Si on a besoin de gérer les cookies, on peut passer par une Session :

import requests

url= 'http://www.example.com'
login_data = {username = 'myuser', password = 'mypassword'}

s = requests.Session()
req = s.post(url + '/login.php', login_data)
req = s.get(url + '/my_account.php')

C’est quand même plus lisible, non ?

En fait, récemment, je voulais parcourir les images de la galerie d’un site. Mais cette galerie contenait 10 images seulement par page, pour 40 pages. J’ai donc fait un script pour mettre toutes les images sur une seule page.

Les images sont encapsulées par une balise a class="foo" et donc j’utilise BeautifulSoup pour toutes les lister (soup.findAll("a","foo")).

Comme j’ai « besoin » de consulter les images plusieurs fois, j’ai préféré enregistrer les images en local plutôt que de laisser un lien sur le site. Pour ça j’ai utilisé stream=True de la méthode .get (cf ce lien sur SO).

J’ai pas fait très propre puisque j’affiche la page sur stdout que je pipe avec tee pour mettre dans un fichier. Mais au moins, je vois si ça marche :þ

# -*- coding: utf-8 -*-
from BeautifulSoup import BeautifulSoup
from requests import session

login_data = { 'login': 'fredtantini', 
               'password': 'toto1234',
               'action': 1 }

urlToVisit = 'http://www.example.com'

#on ouvre une session
with session() as s:
    #on se connecte
    req = s.post(urlToVisit + '/login.php', data=login_data)
    print "<html><body>"
    #comme je sais qu’il y a 40 pages
    for nPage in xrange(1,41):
        #on récupère la page
        request = s.get(urlToVisit + '/some_page.php?p='+str(nPage))
        #que l’on passe à BS
        soup = BeautifulSoup(request.text)    
        print "<h1> Page - ",nPage,"</h1>"
        #Pour tous les liens
        for i in soup.findAll("a","foo"):
            #on récupère l’image
            r = s.get(urlToVisit + '/' + i.img['src'], stream=True)
            #on la télécharge par parties (cf lien SO plus haut)
            if r.status_code == 200:
                with open(i.img['alt'] + ".jpg", 'wb') as f:
                    for chunk in r.iter_content():
                        f.write(chunk)
            print str(i).replace('./images/', './')

print "</body></html>"

Quelques notes SoapUI

Voici un premier article sur SoapUI. Un second arrivera prochainement :]

Les informations sont un peu brutes, cela me sert surtout de notes. Du coup, c’est directement technique et suppose que vous connaissiez déjà SoapUI. D’abord un « rappel » sur les property expansions puis comment on peut passer des paramètres entre les différents pas du cas de test et comment faire des assertions variables.

Property Expansion

Voir property expansion (soapui.org).

À l’intérieur d’une requête/assertion/… on peut mettre des choses comme ${[scope]propertyName[#xpath-expression]}. Avec scope parmi :

  • #Project# ;
  • #TestSuite# ;
  • #TestCase# ;
  • #MockService# ;
  • #Global# ;
  • #System# ;
  • #Env# ;
  • [TestStep name]#.

Par exemple ${Search Request#Response#//ns1:Item[1]/n1:Author[1]/text()} prend la réponse (propertyName = Response) du step Search Request (scope = Search Request#) et prend le texte du nœud Item/Author (xpath-expression = #//ns1:Item[1]/n1:Author[1]/text()).

On peut également utiliser une expression groovy avec ${= expr groovy) à l’intérieur d’une requête :

  • ${=(int)(Math.random()*1000)} insère un nombre aléatoire entre 0 et 999.
  • ${=request.operation.interface.project.name} insère le nom du projet (on part de la requète et on remonte l’arbre soapUI ModelItem.

Passer des paramètres entre test steps

On va voir comment récupérer des valeurs dans un test step. Tout d’abord, créer des propriétés dans le projet (ou le testSuite ou le testCase).

  • Cliquer sur le projet.
  • Cliquer sur Custom Properties.
  • Ajouter des propriétés. (Pour cet exemple : login et datejma.)

On va les changer dès l’initialisation, donc pas besoin de mettre des valeurs.

Dans le test Case

On se place maintenant au niveau du cas de test et on va initialiser nos deux « propriétés ».

Initialisations

Voir scripting and the script library (soapui.org).

On peut initialiser grâce à un Groovy Script que l’on renommera init par exemple, ou alors dans le test Case, dans Setup Script.

Dans les deux cas, on ajoute :

def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )

//on définit nos variables
def login = "user" + System.currentTimeMillis().toString()[-5..-1] //user12345
def datejma = new Date().format("dd/MM/yyyy")

//on change les propriétés du projet
testRunner.testCase.testSuite.project.setPropertyValue("login", login)
testRunner.testCase.testSuite.project.setPropertyValue("datejma" ,datejma)

//on récupère la requête du step authentification
def holder = groovyUtils.getXmlHolder("authentification#Request")
holder.namespaces["ns"] = "http://schemas.xmlsoap.org/soap/envelope/"
//On change le noeud qui va bien (<in0...>user12345</in0>)
holder.setNodeValue("//ns:Body/*/in0", login)

//On met à jour les changements dans l’enveloppe
holder.updateProperty()

Une ligne comme testRunner.testCase.testSuite.project.setPropertyValue("maPropriete", maValeur) permet d’instancier maPropriete à la valeur maValeur dans les Custom Properties du projet. (get and set properties (soapui.org).)

  • Si on enlève .project on change la propriété dans la test suite.
  • Si on enlève .testSuite.project on change dans le test case.

Ici, on définit donc deux variables et on instancie les propriétés du projet à ces valeurs.

On met ensuite l’enveloppe de la requête à jour. Dans cette exemple, on pourrait bien évidemment mettre directement dans le nœud <in0...>${#Project#login}</in0>

Passage de valeurs entre Steps

On peut également récupérer la valeur d’une balise d’une réponse pour s’en servir dans la requête suivante.

Pour cela, on peut ajouter un step property transfer.

On choisit les propriétés de la source (Request ou Response d’un step), de la cible, puis on choisit le nœud à extraire/renseigner. Par exemple :

declare namespace ns = "http://schemas.xmlsoap.org/soap/envelope/";
//soap:Body/*/retour/idSession

dans la source et

declare namespace ns = "http://schemas.xmlsoap.org/soap/envelope/";
//soap:Header/*/identification/idSession

dans la cible.

Assertions

On va voir ici comment vérifier que la balise d’une réponse contient bien ce que l’on veut. Parce que « Contains » peut être assez limité (même avec des regexp), on va voir comment utiliser XPath Match, puis un script.

XPath Match

On choisit le nœud a tester :

declare namespace ns = "http://schemas.xmlsoap.org/soap/envelope/";
//ns:Body/*/maBalise

On dit ce que l’on attend :

${#Project#login},1234 * auie,${#Project#datejma}

Le *, lorsque Allow Wildcards est coché, permet de matcher n’importe quelle valeur. (Voir xpath and xquery assertions (soapui.org). J’ai eu des soucis lorsqu’il y en a plusieurs, ou comme ici, lorsqu’on l’utilise avec les property expansions.)

script Assertion

Voir script assertions (soapui.org).

On peut également utiliser un script groovy si l’on veut faire des choses plus poussées. Bon, là l’exemple est le plus simple possible :

groovyUtils = new com.eviware.soapui.support.GroovyUtils(context) 

//la requete
holder = groovyUtils.getXmlHolder( "authentification#Request" )
holder.namespaces["ns"] = "http://schemas.xmlsoap.org/soap/envelope/"
reqLogin = holder.getNodeValue("//ns:Body/*/in0")

//la reponse
holder2 = groovyUtils.getXmlHolder( "authentification#Request" )
holder2.namespaces["ns"] = "http://schemas.xmlsoap.org/soap/envelope/"
repLogin = holder2.getNodeValue("//ns:Body/*/libelleCodeRetour")

//on teste que la reponse est bien celle attendue
assert repLogin.equals(reqLogin)

Bien pratique si l’on veut formatter la chaîne (mettre en capitales par exemple, supprimer les traits d’union…).

Différence entre eval-after-load et add-hook

J’expliquais dans le billet sur le démarrage d’Emacs que

le code de eval-after-load sera exécuté seulement une fois […]

Les nomdumode-mode-hooks, eux, sont exécutés pour chaque buffer dans lequel le mode sera activé.

Pour illustrer mes propos, voici un code à mettre dans un nouveau buffer (C-x b auie) :

(setq auie 0)
(setq nrst 0)
(eval-after-load "python" ;puisque dans python.el
  '(progn
     (setq auie (1+ auie))
     ))


(add-hook 'python-mode-hook
          (lambda ()
            (setq nrst (1+ nrst))
            ))
(progn
  (python-mode)
  (message (concat "auie vaut :" (number-to-string auie) ", nrst : " (number-to-string nrst))))

Passez en mode elisp (M-x emacs-lisp-mode), puis évaluez le buffer (M-x eval-buffer). Un petit message devrait s’afficher. Allez à la fin du buffer (M->) et évaluez la dernière sexp (C-x C-e). Évaluez plusieurs fois ; constatez que auie n’augmente pas, nrst si.

Autrement dit, si l’on veut exécuter du code lorsque l’on passe dans le mode foo, et que l’on ne veut pas charger le fichier foo.el ni faire un (require 'foo) dans son fichier init.el, on a deux possibilités :

  1. Utiliser eval-after-load pour exécuter du code une fois.
  2. Utiliser add-hook pour exécuter du code plusieurs fois.

En fait, eval-after-load va exécuter le code à chaque fois que la bibliothèque est chargée. Mais généralement, on fait un (autoload 'foo-mode "foo" "docstring") ; au premier appel de foo-mode (avec un M-x ou avec un add-to-list 'auto-mode-alist), foo.el est chargé et le code du eval-after-load est exécuté. Aux prochains appels de foo-mode, foo.el est déjà chargé, le code n’est plus exécuté.

eval-after-load est donc approprié si l’on veut changer des valeurs par défaut :

(eval-after-load "ace-jump-mode"
  '(ace-jump-mode-enable-mark-sync)) ;par défaut, c’est à disable

(eval-after-load "foo-mode"
  '(my-init-foo-mode)) ; on charge des choses en plus

(eval-after-load "org"
  '(require 'ox-md nil t)) ;l’export markdown n’est pas chargé automatiquement

(eval-after-load 'foo-mode
  '(define-key foo-mode-map (kbd "C-à") 'my-useful-fonction)) ; un raccourci utilisé seulement pour ce mode

Le add-hook lui est utilisé à chaque fois que foo-mode est lancé (et non pas à chaque fois que foo.el est chargé). C’est ce choix que l’on fera si l’on veut activer un minor mode pour un major mode ou si on veut changer des valeurs de variables changées par le major mode.

; à chaque fois que l’on passe en elisp mode, on met les parenthèses en couleur
(add-hook 'emacs-lisp-mode 'rainbow-delimiters-mode)

;avant de sauver, on lance un nettoyage
(add-hook 'before-save-hook 'whitespace-cleanup)

;le mode foo redéfinit à chaque fois C-> et je ne veux pas
(add-hook 'foo-mode-hook '(lambda ()
  (define-key foo-mode-map "\C->" 'foobar))) 

;on met c-basic-offset à 4
(add-hook 'c-mode-common-hook
  '(lambda () 
     (setq c-basic-offset 4)))

;si on veut différentes valeurs selon que l’on soit en foo ou en bar
(add-hook foo-mode-hook
  '(lambda () 
    (setq spam 42)) nil t)

(add-hook bar-mode-hook
  '(lambda () 
    (setq spam 3.14)) nil t)

En espérant avoir été clair.

Rubrique-en-Vrac

Du divers, du varié, mais que de l’Emacs aujourd’hui (ce qui laisse présager autre chose dans un futur… lointain). Voire, que des choses qui sont à installer.

Je n’ai pas forcément tout testé et je ne testerai pas tout. Mais je suis sûr que ça peut servir à d’autres.

ace-jump-mode

Pour se déplacer n’importe où dans la fenêtre rapidement.

Après avoir fait C-c SPc, on rentre la lettre du début d’un mot, et tous les mots dont la première lettre correspond ont la première lettre qui devient a, b, c… Il ne reste plus qu’à choisir la lettre qui correspond au mot sur lequel on veut aller.

Il est également possible de choisir une lettre dans un mot (C-u C-c SPC ou C-c C-c lors d’une requête) ou une ligne (C-u C-u C-c SPC). Avec la version 2, on peut naviguer entre les buffers (si on a splitté avec C-x 2 par exemple).

on choisit où aller, et avec les raccourcis, on y arrive en 3 touches

source de l’image, sources (github.com), démonstration (emacsrocks.com)

auto-revert-tail-mode

Quand on surveille des logs avec tail -f on voit les ajouts en fin de fichier. Quand en fichier est modifié par un autre programme, dans Emacs on peut faire un M-x revert-buffer. Et sinon, on peut utiliser le mode approprié : auto-revert-tail-mode (dans autorevert.el, qui est inclus dans Emacs).

highlight-changes-mode

Dans le même genre et dans hilit-chg.el (inclus par défaut aussi), ce mode rend visible les changements du buffer. On peut également mettre en avant les changements du buffer par rapport au fichier, comparer des buffers… Les couleurs par défaut sont un peu trop… colorés et TrackChanges (emacswiki) présente des astuces pour changer ça et d’autres infos pour connaître les changement d’un fichier ou d’un buffer.

company-mode

Pour COMPlete ANYthing : une popup qui affiche les complétions possibles. Comme auto-complete, mais apparemment, en moins buggué.

popup qui s’affiche et propose des complétions

homepage (github.io), sources (github.com)

easy-kill

« Kill & Mark Things Easily in Emacs »

Pour sélectionner facilement mot, sexp, list, defun… étendre la sélection, la réduire, etc.

Contrairement à expand-region, il ne me semble pas que l’on puisse étendre des deux côtés.

sources (github.com)

expand-region

Pour augmenter la région par unité sémantique. Par exemple

(setq alphabet-start "abc def")

Avec le curseur sur le c, ça commence par marquer le mot abc en entier, puis on augmente la région à abc def, puis aux guillemets "abc def", ensuite au contenu des parenthèses setq alphabet-start "abc def" et enfin à toute la sexp.

Plus lourd que easy-kill, mais peut-être plus pratique aussi…

sources (github.com), démonstration (emacsrocks.com)

fancy-narrow

Comme comme narrow-to-region (C-x n n), mais au lieu de supprimer le texte autour de la région, ce dernier est grisé.

une partie du buffer «narrowisé», le reste est grisé

sources (github)

god-mode

Pour ne plus avoir à appuyer sur la touche Ctrl.

Après un appui sur ESC, au lieu de taper C-a C-k C-n C-y, on a juste besoin d’appuyer sur akny. Au lieu de M-f M-f M-f on tape gf...

Une sorte de séparation commandes/insertions à la vi. À rapprocher d’Evil (gitorious).

sources (github)

guide-key

Ce mode sert à afficher les suites possibles d'un raccourci dans une fenêtre. Par exemple, si on ne se souvient jamais des différentes suites de C-x r qui mèle à la fois rectangle et registre, on aura tendance à faire C-x r C-h. Ce qui affichera un buffer d’aide et perdra la séquence en cours.

Avec ce mode, paramétré correctement, on peut visualiser facilement les suites qui ne concernent que les rectangles :

guide-key propose les raccourcis pour les rectangle après un C-x r

sources (github.com)

impatient-mode

Pour voir le changement dans le navigateur dès qu'on tape dans le buffer.

sources (github)

keychord

Pour lancer une commande quand on appuie sur 2 touches en même temps.

Comme s’il n’y avait pas assez de raccourcis…

sources (emacswiki), What are your key-chord abbreviations? (reddit)

make-it-so

Pour faire de la conversion en masse via un Makefile.

Et ça dans Emacs, sisisi. En fait, on met la commande de conversion dans un Makefile (avec d’autres choses), et une fois dans un dossier (en dired-mode donc), on appelle make-it-so qui propose suivant les extensions disponibles les conversions possibles.

sources (github)

manage-minor-mode

Un mode pour gérer les minor modes facilement.

Ça les liste dans un buffer, et on peut activer/désactiver simplement.

À gauche, les modes activés, à droite, les inactifs

sources (github)

rainbow-delimiters

Pour des parenthèses/crochets/… en couleur pour savoir où on en est.

chaque parenthèse fermante a la même couleur que l’ouvrante

source de l’image, sources (github), Plus d'info (emacswiki)

Existe aussi pour mettre en couleur tout le bloc, et pas seulement la parenthèse/crochet : rainbow-blocks (github)

visual-regexp

Comme replace-regexp, mais avec des couleurs pour chaque partie de la regexp.

On voit aussi le remplacement lorsque l’on utilise des \1

exemple lorsque l’on remplace «you \(.+?\) \(.+?\)\b» par «foo \2 \1»

sources (github)

web-mode

Major mode pour html + block php/jsp/template

Indentation, coloration, commentaires qui dépend du contexte (html/code/js/css?), et plein d’autres choses qui permettent de travailler sur un fichier html qui contient du js, du css, du php, de l’asp, du go, etc.

des couleurs adaptées pour chaque bloc (html/css/js/…)

home

which-function-mode

Pour afficher le nom de la fonction dans la mode-line (dans which-func.el).

On peut aussi faire afficher le nom en première ligne (cf which-function-mode (emacsredux)).

Raccourcis utilisés

C-c ^ a

Pour trier alphabétiquement mes entrées

C-c C-x C-f

Pour marquer une sélection avec =, , etc.

Sources des images

J’ai enregistré chaque image plutôt que de pointer sur le site.

Toutes les images proviennent du premier lien suivant l’image.

Le démarrage d’Emacs

Introduction

Il y a quelques temps de cela (je viens de me rendre compte que cela fait plus de 10 ans déjà…), je commençais à utiliser Emacs. Et, comme beaucoup je suppose, pour configurer, je copiais/collais des bouts de code dans mon .emacs sans chercher à comprendre ce que ça faisait (comment ça c’est encore le cas ‽). Typiquement, j’avais plein de :

(autoload 'php-mode "php-mode" "Major mode for editing php code." t)
(add-to-list 'auto-mode-alist '("\\.php$" . php-mode))

plutôt obscurs.

Et au fil du temps, les lignes s’accumulaient, et Emacs n’était plus aussi rapide à charger.

Je me suis alors intéressé à comment le rendre plus rapide au démarrage. Bien que je me sois rendu compte que la meilleure solution pour moi serait d’utiliser emacsclient1, j’ai d’abord fait du nettoyage, en utilisant le plus possible customize et en regroupant ça dans un seul fichier avec :

(setq custom-file "~/.emacs.d/config/custom.el") (load custom-file)

Puis j’ai supprimé tout ce dont je ne me servais en fait pas, et je me suis penché sur ce qu’il se passe au démarrage d’Emacs, pour savoir si je ne lui faisais pas faire des choses inutiles. Et, avec les différentes versions d’Emacs certainement, j’ai bien vu que la plupart de ces lignes étaient (devenues ?) inutiles.

tl;dr

Que fait autoload ? Qu’y a-t-il dans auto-mode-alist ? Le bout de code précédent pour avoir le mode python est-il nécessaire ? suffisant ? Peut-on faire autrement ? C’est à ça que je vais m’intéresser par la suite. Pour cela, je me suis penché dans le code et le manuel d’Emacs et je vous partage ce que j’en ai retenu et laisse des informations pas forcément pertinentes, mais fortement en relation et toujours instructives.

Pour ceux qui ne veulent pas tout lire : ça se passe .

Table of Contents

Comprendre ce qui est chargé et lancé au démarrage.

Cette partie ne se focalise pas sur tout ce qui est fait au démarrage, mais mets l’accent sur qu’est-ce que charge Emacs, et où il va chercher ce qu’il charge, pour mieux comprendre comment faire son .emacs, et qu’il soit plus léger/rapide (charger un fichier = ouvrir + évaluer tout ce qu’il y a dedans).

Un bon résumé des opérations effectuées par Emacs est disponible, comme toujours, dans le manuel : Startup-Summary (manuel elisp).

La variable top-level contient la fonction (en fait la form, objet Lisp qui est sensé être évalué ~ expression, mais je laisserai form par la suite) qui sera lancée au démarrage. Par défaut, c’est normal-top-level.

normal-top-level

La fonction normal-top-level est donc appelée au lancement d’Emacs. On peut la voir dans le fichier startup.el 2 (pour trouver le fichier, taper dans Emacs M-x find-library startup. Ou, via l’aide avec C-h f normal-top-level (juste q pour fermer les fenêtres d’aide), si emacs2x-el est installé, la première ligne devrait être normal-top-level is a compiled Lisp function in `startup.el'. avec startup.el qui est un lien).

Elle :

  • fait diverses configurations tout au long de son appel (qui ne nous concerne pas pour le sujet présent) ;
  • cherche des subdirs.el/leim-list.el dans chaque dossier de load-path et les charge3 ;
  • lance la fonction command-line ;
  • lance des hooks comme emacs-startup-hook et window-setup.

    Bref, elle initialise, remplit un peu plus la variable load-path, et surtout délègue à command-line.

command-line

Aussi située dans startup.el, cette fonction :

  • fait aussi des configurations tout du long ;
  • initialise before-init-time ;
  • met en chemin absolu les noms de fichiers dans load-history (C-h v load-history pour prendre peur) ;
  • definit le fichier init de quel utilisateur il faut utiliser ;
  • parse quelques options de la ligne de commande (-d, -D, -q, -Q, -nbc, etc.) ;
  • lance before-init-hook ;
  • charge site-run-file ;
  • cherche le fichier init (cf plus tard) s’il existe et le lit (puis default.el) ;
  • charge le fichier abbrev, s’il existe et qu’Emacs n’est pas en -batch ;
  • si un dossier de package existe, initialise le système de package (package-initialize) ;
  • initialise after-init-time ;
  • lance after-init-hook ;
  • lance command-line-1 (avec le reste des arguments) ;
  • appelle kill-emacs si option -batch ;
  • lance le serveur, si Emacs est lancé en démon ;
  • lance emacs-session-restore.

C’est donc là que sont chargés les fichiers de configuration : site puis init et default. Il faut voir également les hooks before-init-hook et after-init-hook (qui sont définis à nil par défaut, mais dont la valeur peut changer selon les systèmes Init-File (manuel elisp))

La fonction command-line-1 boucle pour chaque option, exécute l’option (potentiellement -L pour ajouter des dossiers à load-path) et se positionne sur le bon buffer, ou affiche le startup screen.

Avant d’ajouter des choses dans votre .emacs, il faut donc vérifier que ce n’est pas déjà ajouté dans site-run-file.

Le fichier init

Je traduis ici très grossièrement les parties intéressantes du manuel (Init-File (manuel emacs), manuel que je vous encourage à lire).

Quand Emacs démarre, il essaie de charger un fichier d’initialisation qui dit comment l’utilisateur souhaite que démarre Emacs (suivant l’utilisateur, voir Find-Init (manuel emacs)). Emacs cherche le fichier init parmi ~/.emacs, ~/.emacs.el, ou ~/.emacs.d/init.el.

Il peut également y avoir un fichier init par défaut, qui est la bibliothèque default.el, trouvé dans le load-path. Si elle existe, elle est chargée à chaque démarrage d’Emacs (sauf si -q), mais le fichier init est chargé en premier. S’il met inhibit-default-init à non-nil, default n’est pas chargé.

Il peut y a voir aussi un fichier de démarrage de « site », site-start.el. Il est chargé avant le fichier init, sauf si --no-site-file.

Les fichiers default.el et site-start.el peuvent être dans n’importe quel dossier de la variable load-path (souvent dans un dossier site-lisp comme : /usr/local/share/emacs/site-lisp).

Pour voir le load-path de votre Emacs en cours : C-h v load-path.

Pour connaître votre fichier init, et donc savoir quel fichier ~/.emacs(.d/init)?.elc? a été chargé, C-h v user-init-file.

En bref

Au démarrage, il se passe tout un tas de choses, dont le chargement de plein de fichiers2. Je vais résumé ici les fichiers chargés (et les fonctions lancées) qui peuvent avoir un impact sur load-path, et sur lesquels l’utilisateur peut avoir une influence.

Le load-path initial contient quelque chose comme :

/.../emacs24/share/emacs/24.3.50/site-lisp
/.../emacs24/share/emacs/site-lisp
/.../emacs24/share/emacs/24.3.50/lisp

avant même qu’Emacs ne soit lancé.

Puis sont ajoutés les sous-dossiers de ces dossiers (qui sont dans les subdirs.el respectifs) récursivement.

Ensuite, Emacs :

  • lance before-init-hook ;
  • charge le fichier site-run-file (si présent et si pas -no-site-file) qui est commun à tous les utilisateurs et que seul l’admin du site devrait toucher (normalement site-start.el) ;
  • charge le fichier init de l’utilisateur (si pas -q), qui peut être ~/.emacs, ~/.emacs.el, ou ~/.emacs.d/init.el ;
  • charge le fichier default (normalement default.el) sauf si -q ou si inhibit-default-init n’est pas nil ;
  • lance after-init-hook ;
  • ajoute les dossiers passé en argument à l’option -L ou -directory.

Concernant le chargement des fichiers : par défaut (variable auto-compression-mode), il cherche la version compressée (.gz, jka-compr-load-suffixes), et si ce n’est pas précisé autrement lors de l’appel de load, il cherche avec les extensions .el, .elc et sans extension.

Savoir comment charger une fonction/un fichier.

On va voir ici ce que fait la fonction autoload et si c’est bien elle qu’il faut appeler.

Rien de mieux qu’une traduction approximative du manuel et de l’aide pour connaître nos options. Si vous lisez l’anglais, allez donc plutôt voir Loading (manuel elisp), puis allez au résumé.

How Programs Do Loading: The load function and others.

How-Programs-Do-Loading (manuel elisp)

  • Function: load filename &optional missing-ok nomessage nosuffix must-suffix :

    Ouvre un fichier lisp, évalue, ferme le fichier.

    Cherche d’abord filename.elc. Si existe, est chargé. Sinon, cherche filename.el. Si existe, est chargé. Sinon, cherche filename et charge si existe. (Attention à foo.el.el : (load "foo.el") marche…)

    Si auto-compression-mode est activé (cas par défaut) cherche la version compressée si le fichier n’est pas trouvé, puis passe aux suivants. Décompresse et charge si existe (ajoute chacun des suffixes dans jka-compr-load-suffixes).

    Si nosuffix non-nil, n’essaie pas d’ajouter .el/.elc

    Si must-suffix non-nil, load insiste sur le fait que le nom du fichier doit finir par .el/.elc.

    Si filename est un nom de fichier relatif (foo ou baz/foo.bar), load cherche le fichier par la variable load-path. Il charge le premier fichier possible. Si nil dans load-path, essaie le dossier courant. Les trois premiers fichiers dans le premier dossier de load-path sont tentés, puis les trois du second dossier, etc.

    Peu importe où Emacs trouve le fichier (nom/dossier) la variable load-file-name sera valorisée à ce nom de fichier.

    Des messages comme ‘Loading foo...’ et ‘Loading foo...done’ apparaissent, sauf si nomessage est non-nil.

    Si load ne trouve pas le fichier, normalement, il signale l’erreur file-error. Mais si missing-ok non-nil, alors load retourne seulement nil.

    load retourne t si le fichier est chargé avec succès.

    • Donc load nomfichier [retourneNilSiAbsent?SinonErreur nonVerbeux? tenteDAjouter.el/.elc? doitFinirPar.el/.elc?]

      Du coup, on utilisera le plus souvent (load "chose") ou (load "chose.el" t t t) (avec des variantes sur missing-ok et nomessage).

      C-h f load : Charger un fichier enregistre ses définitions, ses appels provide et require dans load-history. La variable load-in-progress est non-nil pendant le chargement, load-file-name contientle nom du fichier.

  • Command: load-file filename :

    Charge le fichier filename. Si filemane est un nom relatif, le dossier courant est présumé. N’utilise pas load-path, n’utilise pas de suffixes, mais cherche une version compressée.

    • En très gros, fait (load (expand-file-name file) nil nil t)).
  • Command: load-library library :

    Cette commande charge la bibliothèque library. Comme load, sauf que lit son argument de façon interactive.

    • En gros, fait (load library).

Load Suffixes: Details about the suffixes that load tries.

Quels suffixes sont tentés (.el, etc.): voir point précédent.

Library Search: Finding a library to load.

Library-Search (manuel elisp)

Quand Emacs cherche une bibliothèque, il cherche dans load-path qui contient des chaînes de caractères correspondant à des dossiers ou nil pour le dossier courant.

À chaque démarrage, il initialise load-path par la variable d’environnement EMACSLOADPATH. Si elle n’est pas défini, Emacs initialise load-path avec /usr/local/share/emacs/version/site-lisp et /usr/local/share/emacs/site-lisp et ajoute à load-path les sous-dossiers récursivement (sauf si ne commence pas par chiffre/lettre, vaut CVS ou RCS, contient un fichier .nosearch). Puis, comme vu précédemment, ajoute ceux indiqués par -L, et ceux du fichier init de l’utilisateur.

  • Command: locate-library library &optional nosuffix path interactive-call :

    Trouve le vrai nom de la bibliothèque library. Cherche comme le fait load, l’argument nosuffix ayant le même sens : ne pas ajouter .elc ou .el au nom.

    Si path est non-nil, cette liste de dossier est utilisée au lieu de load-path.

    Si la commande est appelé depuis un programme, retourne le nom en tant que string. Si appelé avec M-x l’argument interactive-call est t, et le résultat est affichée dans la zone d’affichage des messages (echo area).

  • Command: list-load-path-shadows &optional stringp :

    Cette commande montre une liste de fichiers Emacs lisp « occultés » (shadowed). Un fichier occulté est un fichier qui ne sera normalement pas chargé alors qu’il est dans un dossier dans load-path, mais parce qu’un autre fichier au nom similaire se situe dans un dossier de load-path.

    Par exemple, si load-path vaut ("/opt/emacs/site-lisp" "/usr/share/emacs/23.3/lisp") et que dans les deux dossiers un fichier foo.el existe, alors (require 'foo) ne chargera jamais le fichier du second dossier. Une telle situation peut indiquer un problème dans la façon dont Emacs a été installé.

    Quand appelée depuis Lisp, cette fonction affiche un message contenant la liste des fichiers occultés, plutôt que de les afficher dans un buffer. Si l’argument stringp est non-nil, elle retourne les fichiers occultés dans un string.

Loading Non-ASCII: Non-ASCII characters in Emacs Lisp files.

Pas pertinent pour le sujet.

Autoload: Setting up a function to autoload.

Autoload (manuel elisp)

autoload permet d’enregistrer l’existence d’une fonction ou macro, et repousse le chargement du fichier qui la définit. Le premier appel à la fonction charge automatiquement la bonne bibliothèque, pour installer la vraie définition (et d’autres choses associées), puis exécute la définition comme si cela avait déjà été chargé. L’autoloading peut également être déclanché en regardant la doc d’une fonction (C-h f).

Il y a deux façons de faire une fonction « autoloaded » : en appelant autoload, et en écrivant un commentaire « magique » avant la vraie définition. autoload est la primitive bas niveau pour l’autochargement ; n’importe quel programme Lisp peut appeler autoload à n’importe quel moment. Les commentaires magiques sont le moyen le plus pratique de faire « autoloader » une fonction, pour les paquets installés avec Emacs. Ces commentaires ne font rien en eux-même, mais ils servent de guide pour la commande update-file-autoloads, qui construit les appels à autoload et fait en sorte de les exécuter quand Emacs est construit.

  • Function: autoload function filename &optional docstring interactive type

    Cette fonction définit la fonction (ou macro) function pour pouvoir charger automatiquement à partir du fichier filename. La chaîne filename spécifie le fichier à charger pour obtenir la vraie définition de la fonction.

    Si filename ne contient ni un nom de dossier, ni les suffixes .el ou .elc, cette fonction ajoute un de ces suffixes, et ne chargera pas à partir d’un fichier dont le nom est juste filename sans suffixe. (La variable load-suffixes spécifie les suffixes nécessaires.)

    L’argument docstring est la chaîne contenant la doc de la fonction. Mettre cette documentation permet de la regarder sans charger la vraie définition. Normalement, elle devrait être identique à celle de la définition de la fonction. Si ce n’est pas le cas, la doc de la définition de la fonction prendra effet quand elle sera chargée.

    Si interactive est non-nil, la fonction peut être appelée interactivement. Cela permet à la complétion dans M-x de marcher sans charger la vraie définition de la fonction. La vraie façon dont la fonction est interactive est inutile, sauf si l’utilisateur appelle vraiment la fonction, et dans ce cas la vraie définition est chargée.

    Il est possible d’autoloader les macros et raccourcis clavier comme les fonctions ordinaires. Mettre macro dans type si la fonction est en fait une macro, keymap si c’est un raccourci clavier.

    Si function a déjà une définition qui est non vide et qui n’est pas un objet autoload, la fonction ne fait rien et retourne nil. Sinon, elle construit un objet autoload, et le met en tant que définition de la fonction function.

  • Function: autoloadp object

    Cette fonction retourne non-nil si object est un objet autoloadé.

    Le fichier autoloadé contient généralement d’autres définitions et peut faire des require ou provide. Si le fichier n’est pas complétement chargé (à cause d’une erreur dans l’évaluation de son contenu), toutes les définitions des fonctions et provide qui sont apparus dans le chargement sont défaits. Ceci est fait pour s’assurer que la prochaine tentative d’appel à une fonction autochargée de ce fichier essayera à nouveau de charger le fichier pour être sûr que les fonctions marchent.

    Si le fichier autochargé échoue à définir la fonction ou macro Lisp désirée, alors une erreur est signalée « Autoloading failed to define function function-name ».

    (La suite concerne moins le sujet, mais c’est intéressant, donc je mets pour me souvenir.)

    Un commentaire magique autoload (souvent appelé autoload cookie) est composé de ;;;###autoload, sur une ligne, tout seul, juste avant la vraie définition de la fonction. La commande M-x update-file-autoloads écrit l’appel autoload correspondant dans loaddefs.el. (La chaîne commentaire et le fichier peuvent être changés, voir plus bas.) Construire Emacs charge loaddefs.el et appelle autoload. M-x update-directory-autoloads est encore plus puissant ; ça met à jour les autoloads pour tous les fichiers dans le dossier courant.

    Le même commentaire magique peut copier n’importe quel type de form dans loaddefs.el. La form qui suit le commentaire magique est copié tel quel, sauf si c’est une des forms que autoload gère différemment (par exemple en convertissant en appel autoload)  :

    Les définitions des objets fonction :

    defun et defmacro [comme on vient de voir pour ceux qui suivent] ; et aussi cl-defun, cl-defmacro et define-overloadable-function.

    Les définitions des major/minor modes :

    define-minor-mode, define-globalized-minor-mode, define-generic-mode, define-derived-mode, easy-mmode-define-minor-mode, easy-mmode-define-global-mode, define-compilation-mode, et define-global-minor-mode.

    D’autres types de définition :

    defcustom, defgroup, defclass, et define-skeleton.

    On peut aussi utiliser un commentaire magique pour exécuter une form au moment de la construction sans l’exécuter quand le fichier lui-même est chargé. Pour ce faire, écrire la form sur la même ligne que le commentaire magique. Puisque c’est un commentaire, ça ne fait rien quand on charge le fichier source ; mais M-x update-file-autoloads le copie dans loaddefs.el, où il sera exécuté lors de la construction d’Emacs.

    Un exemple de fonction avec le commentaire magique :

;;;###autoload
(defun doctor ()
  "Switch to *doctor* buffer and start giving psychotherapy."
  (interactive)
  (switch-to-buffer "*doctor*")
  (doctor-mode))

Ce qui est écrit dans loaddefs.el :

(autoload (quote doctor) "doctor" "\
Switch to *doctor* buffer and start giving psychotherapy.

\(fn)" t nil)

Si on a écrit une définition de fonction avec une macro qui n’est pas celle utilisée d’habitude (par exemple mydefunmacro), l’utilisation d’un commentaire magique normal copierait toute la définition dans loaddefs.el (puisque ce n’est pas une des exceptions vues précédemment). Ce n’est pas ce que l’on souhaite. Pour mettre le bon appel autoload dans loaddefs.el, il faut plutôt écrire :

;;;###autoload (autoload 'foo "myfile")
(mydefunmacro foo
  ...)

Comme dit précédemment, on peut utiliser une chaîne différente pour le cookie autoload et quand même avoir l’appel autoload correspondant écrit dans un fichier différent de loaddefs.el.

  • Variable: generate-autoload-cookie

    La valeur de cette variable doit être une chaîne dont la syntaxe est une commande Lisp. M-x update-file-autoloads copie la form Lisp qui suit le cookie dans le fichier autoload qu’il génère.

  • Variable: generated-autoload-file

    La valeur de cette variable nomme un fichier Emacs Lisp où les appels autoload doivent aller (par défaut loaddefs.el)

    Pour charger explicitement la bibliothèque spécifiée par un objet autoload, on peut utiliser la fonction :

  • Function: autoload-do-load autoload &optional name macro-only

    Cette fonction effectue le chargement spécifié par autoload, que devrait être un objet autoload.

Repeated Loading: Precautions about loading a file twice.

Comme le dit le titre. Intéressant, mais pas pour notre problème.

Named Features: Loading a library if it isn't already loaded.

provide et require sont une autre façon de charger automatiquement des fichiers. Le chargement automatique est déclanchée en appelant une fonction particulière, mais une feature est chargée la première fois qu’un autre programme la demande, par son nom.

Un nom de feature est un symbole qui représente une collection de fonctions, variables, etc. Le fichier qui les définit doit faire un provide de la feature.

Un autre programme qui les utilise peut s’assurer qu’elles sont définies en faisant un require de la feature. Cela charge le fichier de définitions s’il n’était pas déjà chargé (en regardant la variable globale features). Si le fichier n’a pas d’appel « haut niveau » à provide, require signale une erreur.

Where Defined: Finding which file defined a certain symbol.

Avec la fonction symbol-file (qui cherche dans load-history). (Par exemple, évaluez (C-x e derrière la parenthèse fermante) dans un buffer (symbol-file 'emacs-lisp-mode) ou (symbol-file 'split-window).)

Unloading: How to "unload" a library that was loaded.

unload-feature feature (qui s’aide lui aussi de load-history) « décharge » la librairie qui fait un provide feature. Ça enlève les définitions des fonctions, macros… crées par des defun, defalias, defsubst, defmacro, et restaure les autoloads.

Hooks for Loading: Providing code to be run when particular libraries are loaded.

On peut demander à ce que du code soit exécuté chaque fois qu’Emacs charge une bibliothèque, en utilisant la variable after-load-functions :

— Variable: after-load-functions

Ce hook est lancé après le chargement d’un fichier. Chaque fonction dans le hook est appelé avec un seul argument, le nom avec chemin absolu du fichier qui vient d’être chargé.

Si on veut que du code soit exécuté quand une bibliothèque en particulier est chargée, il faut utiliser eval-after-load :

— Function: eval-after-load library form

Cette fonction évalue form à la fin du chargement du fichier library, chaque fois que library est chargée. Si elle est déjà chargée, elle évalue form immédiatement. Par exemple :

(eval-after-load "edebug" '(def-edebug-spec c-point t))

Normalement, les programmes Lisp bien faits ne devraient pas utiliser eval-after-load. Si on a besoin d’examiner et d’initialiser des variables définies dans une autre bibliothèque (celles définies pour un usage extérieur à la bibliothèque), on peut le faire immédiatement. Il n’y a pas besoin d’attendre qu’une bibliothèque soit chargée. Si on a besoin d’appeler des fonctions définies dans cette bibliothèque, il faut charger la bibliothèque (de préférence avec require).

— Variable: after-load-alist

Contient une alist construite par eval-after-load, contenant les expressions à évaluer quand certaines bibliothèques sont chargées.

En bref

Pour charger un fichier (et évaluer ce qu’il y a dedans), on utilise load. Mais, pour dire à emacs « Hey, la fonction foo est dans le fichier bar.el, mais tu charges (c’est-à-dire évalues) le fichier qui contient la fonction que quand t’en as vraiment besoin. », et ainsi pouvoir utiliser foo sans charger le fichier, on utilise autoload. Par exemple,

(autoload 'foo "bar" "Some docstring." t)

dans le .emacs, va dire que la fonction php-mode se trouve dans un fichier php-mode (avec extension .el ou .elc).

De plus, si dans un fichier « standard » il y a des lignes ;;;###autoload, alors pas besoin d’exécuter des (autoload ...), le fichier loaddefs.el contient certainement l’appel qui va bien.

Si par contre c’est votre fichier, et que vous voulez ajouter les fonctions qui suivent les ;;;###autoload à loaddefs.el, il faut appeler update-file-autoloads (ou update-directory-autoloads).

Comment est choisi le mode d’un fichier

Choosing-Modes (manuel emacs) Auto-Major-Mode (manuel elisp)

Quand on ouvre un fichier, avec find-file fichier ou recover-file, les fonctions appellent normal-mode fichier (dans files.el).

normal-mode fichier appelle fundamental-mode sur le buffer puis appelle set-auto-mode.

Quand on regarde C-h f set-auto-mode :

La fonction cherche un mode de plusieurs façons :

  • Elle cherche un -*-foo-mode-*- (voir C-h f set-auto-mode-1) dans le buffer.
  • Si pas trouvé, cherche un #!/ à la première ligne (en fait C-h v auto-mode-interpreter-regexp pour être exact).
  • Si pas trouvé, cherche une regexp pour la première ligne du buffer (dans C-h v magic-mode-alist (défaut à vide)). Par exemple ("<\\?xml " . xml-mode) dit que si la première ligne commence par <\\?xml, on passe en xml-mode.
  • Si pas trouvé, cherche une regexp sur le nom de fichier, souvent l’extension (dans C-h v auto-mode-alist (case-sensitive, puis insensitive)). Par exemple ("\\.svgz?\\'" . xml-mode) dit que si le fichier se termine par .svg ou .svgz, on passe en xml-mode.
  • Si pas trouvé, cherche une regexp pour la première ligne du buffer (dans C-h v magic-fallback-mode-alist).
  • Si toujours pas trouvé, on appelle set-buffer-major-mode qui dit que :
    • si le buffer est *scratch*, initial-major-mode ;
    • sinon major-mode (c’est-à-dire fundamental-mode par défaut).

La plupart du temps, une ligne (add-to-list 'auto-mode-alist '("\\.[ch]\\'" . c-mode)) sera présente dans le fichier (précédée ou non d’un commentaire magique).

Bonus

Specifying-File-Variable (manuel emacs)

Le -*- ne sert en fait pas qu’à mettre le mode. On peut aussi mettre des variables spécialement pour un fichier. (Pour tous les fichiers d’un dossier, c’est possible aussi, avec un fichier .dir-locals.el Directory-Variables (manuel emacs).)

Par exemple,

;; -*- mode: Lisp; fill-column: 75; comment-column: 50; -*-

mettra en mode Lisp et instanciera les deux variables. (Plutôt que d’éditer à la main, on peut utiliser M-x add-file-local-variable-prop-line).

Normalement, c’est sur la première ligne, mais pour les scripts shell (entre autres) où la première ligne sert à connaître l’interpréteur, il peut être sur la seconde ligne.

Ces variables peuvent également être définies à la fin du fichier (avec M-x add-file-local-variable par exemple) sous forme de liste :

/* Local Variables:  */
/* mode: c           */
/* comment-column: 0 */
/* End:              */

La liste doit commencer par Local Variables: et se terminer par End:. Emacs reconnaît automatiquement le préfixe (et comme ici, éventuellement suffixe) en cherchant autour Local Variables:.

Pour activer un mode mineur, il faut utiliser eval: :

;; Local Variables:
;; eval: (eldoc-mode)
;; eval: (font-lock-mode -1)
;; End:

Comme ça peut être dangereux, Emacs peut demander si on veut vraiment les utiliser (Safe-File-Variables (manuel emacs), C-h v safe-local-variable-values).

Hooks

Les hooks (manuel elisp) sont des variables où l’on peut stocker une ou des fonctions à appeler pour une occasion particulière. Emacs permet ainsi de modifier le comportement de certaines fonctions.

Après avoir chargé un fichier, le mode est choisi, puis appelé. Pour ajouter des fonctions ou appeler des modes mineurs sur ces fichiers, on peut donc utiliser les hooks. Généralement pour chaque mode, il existe un hook qui est exécuté après le chargement du mode : pour nomdumode-mode il y a nomdumode-mode-hook. La dernière chose qu’un major-mode fait, c’est appeler run-mode-hooks. Cette fonction lance change-major-mode-after-body-hook, nomdumode-mode-hook puis after-change-major-mode-hook.

Si un mode dérive d’un autre, le -mode-hook du parent est d’abord exécuté. Du coup, pour changer tous les modes de programmation, il suffit d’ajouter une fonction à prog-mode-hook.

Setting-Hooks (manuel elisp)

Un exemple qui utilise le mode hook pour mettre passer en Auto Fill mode quand on passe en mode Lisp Interaction :

(add-hook 'lisp-interaction-mode-hook 'auto-fill-mode)

Normalement, la fonction que l’on ajoute au hook ne prend pas d’argument, mais il est possible de contourner cela avec les fonctions lambda (ModeHooks (emacswiki)) :

(add-hook 'text-mode-hook (lambda () (set-fill-column 72)))
(add-hook 'text-mode-hook (lambda () (column-number-mode 1)))

Hooks-for-Loading (manuel elisp)

(Je reprends ce que je dis plus haut, en ajoutant des exemples.)

On peut également demander à ce que du code soit exécuté à chaque fois qu’Emacs charge une bibliothèque avec le hook after-load-functions (attention, chaque fonction du hook est appelée avec un argument : le chemin complet du fichier qui vient d’être chargé).

Si on veut que le code soit exécuté à chaque fois qu’une bibliothèque est chargée, il faut utiliser eval-after-load. La fonction évalue le code à la fin de chaque chargement de la bibliothèque. Si elle est déjà chargée, le code est exécuté immédiatement.

Par exemple, pour charger la bibliothèque my-foo-config après que file-foo.txt ait été chargé :

(eval-after-load "file-foo.txt" '(load "my-foo-config"))

Pour exécuter la fonction foo-bar après avoir chargé le mode foobar :

(eval-after-load "foobar-mode" '(foo-bar"))

Différences

On peut parfois utiliser indifférement l’une ou l’autre des méthodes. Mais, le code de eval-after-load sera exécuté seulement une fois, et donc est généralement utilisé pour par exemple instancier des valeurs globales par défaut ou configurer un keymap par défaut pour un certain mode. Dans le code du eval-after-load, il n’y a pas non plus de notion de buffer courant.

Les nomdumode-mode-hooks, eux, sont exécutés pour chaque buffer dans lequel le mode sera activé.

Conclusion

Que fait le code du début

Reprenons notre bout de code du début :

(autoload 'php-mode "php-mode" "Major mode for editing php code." t)
(add-to-list 'auto-mode-alist '("\\.php$" . php-mode))

La première ligne indique donc à Emacs où chercher la fonction php-mode – dans un fichier php-mode.el (ou .elc) – mais que pour le moment, ce n’est pas la peine de charger et d’évaluer le fichier (et donc la fonction).

La deuxième indique que quand Emacs va ouvrir un fichier, s’il n’a pas trouvé de ligne contenant -*-foo-mode-*-, de shebang (#!) indiquant quel mode charger, et que le fichier se termine par .php, alors, il faut lancer la fonction php-mode.

Ces lignes permettent donc de (et sont nécessaire pour) passer en php-mode quand on ouvre un fichier dont l’extension est .php sans forcément charger le fichier php-mode.el.

C’est plutôt pas mal, sauf que, ces lignes sont sans doute totalement inutiles… Pour s’en rendre compte, il suffit de lancer Emacs sans fichier de config avec -q ou --no-init-file (voire -Q qui fait --no-init-file --no-site-file --no-splash), et de regarder si le fichier qu’on veut ouvrir se met bien dans le bon mode. Ou alors, de regarder auto-mode-alist, ainsi que loaddefs.el.

Comment configurer un mode

En utilisant les hooks. La façon la plus simple est d’utiliser :

(add-hook 'text-mode-hook 'auto-fill-mode)

Si on veut utiliser une fonction avec argument, il faut passer par des fonctions lambda :

(add-hook 'latex-mode-hook (lambda () (auto-fill-mode -1)))

Voilà, je ne sais pas si c’est plus clair pour vous, mais en tout cas, ça m’a permis d’en apprendre un peu plus sur Emacs. (Et encore, je ne vous ai pas parlé de hack-local-variables-hook, du fichier .dir-locals.el (manuel emacs), de…

(Merci aux relecteurs.)

Edit dans dired/occured = awesome

Voici des exemples qui font que je ne pourrais pas me passer d’Emacs tant il me facilite la vie. (Attention, certaines des fonctionnalités que je vais utiliser ne marchent qu’avec Emacs > 24.)

dired mode

Mise en place des exemples

Dans une console, tapez les commandes

mkdir -p /tmp/testEmacs{1..2} && cd /tmp/testEmacs1
for i in {01..10};do dd if=/dev/urandom count=$((RANDOM%10+1)) bs=1024 | base64 > fic$i;done
touch /tmp/testEmacs2/fic{{01..10},{03..05}.bck}

qui vont créer dix fichiers contenant du texte aléatoire dans le dossier /tmp/testEmacs1 et des fichiers dont certains possèdent une sauvegarde.

Dans Emacs, en tapant C-x f /tmp/testEmacs1 on se trouve dans le dossier en mode Dired.

Le mode édition ou wdired mode

Renommer les gros fichiers

Le but ici va être de renommer les fichiers de taille supérieur à 10000, pour par exemple vérifier plus tard leur contenu, effectuer des remplacements à l’intérieur des fichiers, ou juste pour l’exemple en fait…

Dans un buffer en mode dired, il est possible de passer en mode édition avec C-x C-q (en fait on passe en wdired-mode (pour Writable Dired)). Cela permet entre autres choses de renommer les fichiers ou changer leur permission.

Pour marquer nos fichiers, il est plus simple de les trier par taille. Pour cela un petit C-u s nous permet de spécifier comment trier les fichiers. En ajoutant S aux options du ls qui s’affichent, on a donc les plus gros fichiers en premiers.

En mode édition (C-x C-q donc), on se met à la fin du premier fichier, C-spc, on descend jusqu’au fichier voulu, on insère un rectangle avec C-x r t .aVerifier et les fichiers sont renommés. Il ne reste plus qu’à valider avec un C-c C-c.

Remplacer les .bck

Pour ce deuxième exemple (C-x f /tmp/testEmacs2), on a un certain nombre de fichiers qui possède une sauvegarde (un fichier .bck). On veut les changer : le fichier ficXY deviendra ficXY.bck et inversement.

Pour ça, je propose la macro suivante :

  • C-x C-q on passe en mode édition
  • F3 début d’enregistrement de la macro
  • C-s .bck pour chercher la prochaine sauvegarde
  • C-p .bck pour renommer le fichier sans extension
  • C-n C-u BCKSPC pour renommer la sauvegarde
  • F4 pour terminer la macro
  • M-0 F4 pour exécuter la macro sur les autres fichiers
  • C-c C-c pour valider

Le .bck ne nous plait pas et on veut renommer en .save ?

  • C-x C-q on passe en mode édition
  • M-< on remonte au début du fichier
  • C-M-% .bck$ .save pour modifier l’extension .bck en .save
  • ! pour renommer tous les fichiers
  • C-c C-c pour valider

occur mode

(C’est cette partie qui nécessite un Emacs récent.)

Présentation

Vu que je n’ai pas encore parlé d’occur, je fais une rapide présentation :

M-s o lance la commande occur et demande une regexp. Elle affiche dans un buffer *Occur* les lignes qui contiennent la regexp. Un argument permet de définir le contexte (comme grep -C2 par exemple).

multi-occur, respectivement multi-occur-in-matching-buffers, permet de montrer les lignes dans les buffers que l’on a renseigné un par un, resp. par une regexp (utilisez . pour tous les buffers donc). Pour faire pareil avec les buffers marqués dans la liste des buffers (C-x C-b), il existe Buffer-menu-multi-occur.

Dans la fenêtre qui s’ouvre, on a le nombre de concordances avec le nom du buffer. Si on clique sur la ligne, on va dans le buffer d’origine.

C’est donc pratique pour par exemple avoir un index des fonctions d’un fichier (M-s o function), lister tous les endroits où l’on a une adresse http (M-s o http), etc.

Le mode édition ou occur-edit mode

À partir de la version 24, un appui sur e perment de passer en mode Occur-Edit. Dans ce mode, les changements dans le buffer sont aussi appliqués dans le buffer d’origine. C’est un bon moyen plus visuel pour faire du remplacement sélectif.

Par exemple, dans un buffer, on a les lignes suivantes :

[...]
variable 1
valeur: chose 2
[...]
variable x
valeur: chose y
[...]
variable 3
valeur: chose 4
[...]
variable x
valeur: chose y
[...]
variable 4
valeur: chose 4
[...]

Et on veut remettre la valeur correspondant à la variable sauf dans le cas des variables x.

Bien entendu, il est possible de faire ça en utilisant des regexps/macro/fonctions lisp. Mais il y a aussi moins « prise de tête ».

Pour notre problème, M-- M-1 M-s o valeur permet d’afficher les lignes contenant « valeur » et celle d’avant.

En appuyant sur e on passe en mode édition, et il est facile de changer uniquement les lignes qu’il faut (comme on le ferait dans le buffer d’origine).

Pour sortir du mode édition, un C-c C-c, puis q pour quitter le mode occur.

Si l’on veut supprimer « chose » et garder uniquement le numéro (pas quand c’est chose y donc) :

  • M-s o valeur: chose pour afficher les bonnes lignes
  • C-x o pour passer dans le buffer *occur*
  • e pour passer en mode édition
  • M-x flush-lines chose y pour supprimer du buffer *occur* les lignes que l’on ne veut pas changer
  • M-% chose SPC ENTER ENTER ! pour supprimer tous les « chose﹏ »
  • C-c C-c puis q pour sortir du mode étition et quitter occur.

Le buffer ressemble maintenant à :

[...]
variable 1
valeur: 1
[...]
variable x
valeur: chose y
[...]
variable 3
valeur: 3
[...]
variable x
valeur: chose y
[...]
variable 4
valeur:  4
[...]

Autre exemple :

variable 1
valeur: bépo
[...]
variable 2
valeur: auie
[...]
variable 3
valeur: êàyx
[...]
variable 4
valeur: vdlj
[...]
variable 5
valeur: tsrn
[...]

et on veut décaler les valeurs (variable 2 aura pour valeur bépo, etc.).

Pour cela, M-s o valeur: suivi de e pour passer en mode occur-edit. Puis on kill bépo et on se place au début de auie.

On est prêt pour notre macro :

  • F3
  • M-d, on kill le mot (ou C-k si plusieurs mots)
  • C-y M-y on colle le précédent1
  • C-x C-x pour revenir où on était (au début du mot)
  • C-n pour passer à la ligne suivante
  • F4

Alors oui, on peut se passer d’occur-edit en faisant un C-s valeur: SPC ENTER à la place de C-n. Mais souvent, il y a un valeur: dans les [...] que je ne veux pas changer ; plutôt que de faire du F4 au coup par coup (ou de me rendre compte du changement non voulu trop tard), il est plus simple, je trouve, de supprimer la ligne dans *occur*, puis d’appliquer la macro avec un M-0 F4.

À lire

Un peu de typographie et marquer les raccourcis clavier dans org-mode

Utilisant la disposition de clavier bépo, et soucieux d’écrire le plus correctement possible, j’ai pris l’habitude de mettre des espaces insécables lorsque c’est nécessaire (même si, je l’avoue, je n’utilise pas les espaces fines… désolé). Depuis plusieurs jours, j’utilise également les apostrophes courbes (!wfr) et plus celles qui se trouve sur la touche 4 des claviers azerty, des droites : l’apostrophe courbe vs l'apostrophe droite.

Ajouter des caractères autorisés à org-emphasis-regexp-components

Le souci avec org-mode, c’est que mettre des espaces insécables après des signes d’emphases (mise en gras *, italique /, etc.) n’est pas possible par défaut : « =chose=﹏: » où ﹏ est une espace insécable n’affichera pas « chose : », mais s’affichera tel quel. Dans mes précédents articles, je remplaçais l’espace insécable par une espace « normale ». Et c’est quelque chose que je ne voulais pas. J’ai donc regardé dans le code, et dans org.el est trouvé la variable org-emphasis-regexp-components. Malheureusement, elle est précédée du commentaire suivant :

;; This used to be a defcustom (Org <8.0) but allowing the users to
;; set this option proved cumbersome.  See this message/thread:
;; http://article.gmane.org/gmane.emacs.orgmode/68681

Comme j’utilise maintenant une version ≥ 8.0, je suis obligé de la modifier « en dur » dans un fichier org-conf.el (ou dans le .emacs, mais je vais en fait faire plusieurs modifications ; je crée un fichier à part pour ma configuration d’org-mode). La documentation de la variable indique clairement les différents parties : les caractères possibles avant le signe de début, après le signe le fin, interdits après le signe de début/avant le signe de fin, etc.

Ce qui m’intéresse, ce sont les signes autorisés avant et après. Voilà donc ce que j’ai dans mon fichier :

;on ajoute _ et … dans post, ’ dans pre
(defvar org-emphasis-regexp-components
  '(" \t ’('\"{" "- \t ….,:!?;'\")}\\" " \t\r\n,'" "." 1)
  "support for ' ', '…' and '’'")

Mes modifications sont les deux caractères qui suivent la tabulation.

Indiquer les appuis sur des touches

Une autre modification que je souhaitais depuis quelques temps, est celle de séparer la façon dont je mets en avant les noms de fonctions des raccourcis clavier. Avant, j’utilisais (essayais d’utiliser) ~ (verbatim) pour marquer les raccourcis, et = (code) pour les fonctions. Mais cela ne me satisfaisait pas. Et puis, j’ai découvert la balise <kbd> (w3.org), et je me suis dit que j’allais utiliser ça pour l’export. Sauf que je voulais garder la signification de ~ et =. Je me suis dit qu’il me fallait un nouveau marqueur. J’utilise dorénavant pour mettre les commandes qu’il faut taper. ‰C-x C-s‰ sera alors exporté en <kbd>C-x C-s</kbd>. Il faudrait que ce soit <kbd><kbd>C</kbd>-<kbd>x</kbd> <kbd>C</kbd>-<kbd>s</kbd></kbd> pour que ce soit plus juste, mais je m’en passerai pour l’instant.

J’avais dans l’idée de modifier mon css pour que le raccourci apparaisse avec un beau petit cadre autour. Et puis je me suis dit que ce serait sympa d’avoir la même chose dans Emacs. Du coup, je me suis créé une face pour cela :

(defface my-face-org-keystroke
  '((t (:inherit shadow 
        :box (:line-width -2 ;neg. in order to keep the same size of lines
              :color "grey75"
              :style pressed-button)))) "Face for keystrokes"
        :group 'org-faces)

Pour indiquer que est un marqueur, il faut customizer la variable org-emphasis-alist. La mienne vaut maintenant :

(("*" bold)
 ("/" italic)
 ("_" underline)
 ("=" org-code verbatim)
 ("~" org-verbatim verbatim)
 ("+"
  (:strike-through t))
 ("‰" my-face-org-keystroke verbatim));cette ligne correspond à mon ajout

Il faut maintenant définir/modifier plusieurs fonctions pour qu’org-mode se comporte comme attendu lors de l’export.

Dans mon org-conf.el, il me faut charger ox.el sous peine d’avoir des erreurs :

(require 'ox) 
;;otherwise org-element--parse-objects: Symbol's function definition is void: org-element-my-object-keystroke-parser

J’ai ensuite défini les fonctions qui existent pour bold ou code et modifié celles où apparaissent tous les objets concernés par l’export :

;creation
(defun org-html-keystroke (keystroke contents info)
  "Transcode KEYSTROKE from Org to HTML.
CONTENTS is nil.  INFO is a plist holding contextual
information."
  (format (or (cdr (assq 'my-object-keystroke org-html-text-markup-alist)) "%s")
          (org-html-encode-plain-text (org-element-property :value keystroke))))


;creation
(defun org-element-my-object-keystroke-parser ()
  "Parse code object at point.

Return a list whose CAR is `my-object-keystroke' and CDR is a plist with
`:value', `:begin', `:end' and `:post-blank' keywords.

Assume point is at the first tilde marker."
  (interactive)
  (save-excursion
    (unless (bolp) (backward-char 1))
    (looking-at org-emph-re)
    (let ((begin (match-beginning 2))
          (value (org-match-string-no-properties 4))
          (post-blank (progn (goto-char (match-end 2))
                             (skip-chars-forward " \t")))
          (end (point)))
      (list 'my-object-keystroke
            (list :value value
                  :begin begin
                  :end end
                  :post-blank post-blank)))))

;creation
(defun org-element-my-object-keystroke-interpreter (keystroke contents)
  "Interpret KEYSTROKE object as Org syntax.
CONTENTS is nil."
  (format "‰%s‰" (org-element-property :value keystroke)))


;modification
(defconst org-element-object-successor-alist
  '((subscript . sub/superscript) (superscript . sub/superscript)
    (bold . text-markup) (code . text-markup) (italic . text-markup)
    (strike-through . text-markup) (underline . text-markup)
    (verbatim . text-markup) (entity . latex-or-entity)
    (latex-fragment . latex-or-entity) (my-object-keystroke . text-markup))
  "Alist of translations between object type and successor name.
Sharing the same successor comes handy when, for example, the
regexp matching one object can also match the other object.")

;modification
(defconst org-element-all-objects
  '(bold code entity export-snippet footnote-reference inline-babel-call
         inline-src-block italic line-break latex-fragment link macro
         radio-target statistics-cookie strike-through subscript superscript
         table-cell target timestamp underline verbatim my-object-keystroke)
  "Complete list of object types.")


;modification
(defun org-element-text-markup-successor ()
  "Search for the next text-markup object.

Return value is a cons cell whose CAR is a symbol among `bold',
`italic', `underline', `strike-through', `code' and `verbatim'
and CDR is beginning position."
  (save-excursion
    (unless (bolp) (backward-char))
    (when (re-search-forward org-emph-re nil t)
      (let ((marker (match-string 3)))
        (cons (cond
               ((equal marker "*") 'bold)
               ((equal marker "/") 'italic)
               ((equal marker "_") 'underline)
               ((equal marker "+") 'strike-through)
               ((equal marker "~") 'code)
               ((equal marker "=") 'verbatim)
               ((equal marker "‰") 'my-object-keystroke) ;a ajouter
               (t (error "Unknown marker at %d" (match-beginning 3))))
              (match-beginning 2))))))

Et voilà, on a ce qu’il faut pour exporter ‰C-x C-s‰ en « C-x C-s » et pour que dans Emacs ça y ressemble également.

Reste maintenant l’export via C-c C-e. On peut redéfinir le backend html en copiant/collant les lignes d’ox-html.el qui commencent par (org-export-define-backend 'html et en ajoutant (my-object-keystroke . org-html-keystroke) dans la liste qui va bien. Mais ça fait un peu long. Et puis on perd l’export par défaut.

J’ai préféré définir mon propre export, vu que ce n’est pas très difficile :

(org-export-define-derived-backend 'my-html 'html
  :translate-alist '((my-object-keystroke . org-html-keystroke))
  :menu-entry ' (?h 1
                    ((?r "my-html"  org-html-export-to-my-html))))

(defun org-html-export-to-my-html
  (&optional async subtreep visible-only body-only ext-plist)
  "Export current buffer to a HTML file.

Return output file's name."
  (interactive)
  (let* ((extension (concat "." org-html-extension))
         (file (org-export-output-file-name extension subtreep))
         (org-export-coding-system org-html-coding-system))
    (org-export-to-file 'my-html file
      async subtreep visible-only body-only ext-plist)))


(defun org-html-publish-to-my-html (plist filename pub-dir)
  "Publish an org file to my-html.
Return output file name."
  (org-publish-org-to 'my-html filename
                      (concat "." (or (plist-get plist :html-extension)
                                      org-html-extension "html"))
                      plist pub-dir))

(defun org-html-convert-region-to-my-html ()
  "Assume the current region has org-mode syntax, and convert it to HTML.
This can be used in any buffer.  For example, you can write an
itemized list in org-mode syntax in an HTML buffer and use this
command to convert it."
  (interactive)
  (org-export-replace-region-by 'my-html))

Je n’ai plus qu’à faire un C-c C-e h r pour exporter comme il faut.

Il m’a fallu également modifier ma configuration pour publier, mais vu que je suis en train de faire des modifications, j’y reviendrai un autre jour.

Il va falloir maintenant que je reprenne mes anciens articles pour changer les ~ par des , mais ça, ça viendra plus tard :þ

Une espace une tabulation

Aujourd'hui, je vais parler des espaces dans Emacs, mais pas que (oui, ces temps-ci, ce sera beaucoup, beaucoup d'Emacs dont il sera question). Et oui, les espaces dont je vais parler, ce sont les espaces aux féminins : le caractère typographique, représenté par un espace (au masculin celui-ci) entre les mots.

Pour voir les espaces que vous avez tapées, et savoir si un espace est constituée d'une ou plusieurs espaces, voire de tabulations, il existe un mode pour cela : M-x whitespace-mode permet de voir tout ce qui est d'habitude invisible (ou presque).

Les espaces seront alors représentées par des · (ou seront en couleur si la ligne ne dépasse pas du buffer), les fins de lignes par $, les espaces (qui devraient être fines) insécables par ¤, les tabulations seront en couleur, etc.

Bon, c'est pas le truc dont je vais me servir souvent. Par contre, dans la bibliothèque du mode (find-library whitespace si vous avez installé le paquet emacs2x-el), il y a tout un tas de fonctions liés aux espaces en général, comme whitespace-cleanup qui se charge des :

  1. lignes vides en début de buffer ;
  2. lignes vides en fin de buffer ;
  3. 8 espaces ou plus en début de lignes ;
  4. espaces avant les tabulations ;
  5. espaces et tabulations en fin de ligne ;
  6. 8 espaces ou plus après une tabulations.

(extrait de l'aide de la fonction, C-h f whitespace-cleanup).

Bref, de quoi faire du ménage si vous n'avez pas fait C-x C-o à la fin de votre fichier par exemple (voir précédemment).

La bibliothèque propose tout un tas d'autres fonctions, qui sont grandement customizable, et je vous invite à y jeter un coup d'œil. Si le whitespace cleanup est trop radical pour vous, ce hook est plutôt sympa :

(add-hook 'before-save-hook 'delete-trailing-whitespace)

Raccourcis utilisés

Pour entrer un caractère utf-8 avec son code hexadécimal (ou son nom) C-x 8 Enter puis le code.

Relancer une précédente commande en ksh

Pour le boulot, on fait parfois des sudo su - someUser avec someUser qui est en ksh et qui en plus est en mode vi (Adieu C-r, C-p, C-k, C-e. Bonjour /, Esc-k, dd, A). Du coup, comme il est « partagé », je ne mets pas de bash par défaut ni de set -o emacs (sympa hein ‽). Et au final, je m’y retrouve plutôt bien. Il n’y a pas tellement de différences entre ksh et bash.

Sauf une chose dont je me sers au final assez souvent, l’history expansion (avec le fameux sudo !! par exemple, mais aussi !!:gs:/foo/bar/). Mais en cherchant un peu, il y a quelque chose qui y ressemble vaguement : la commande r. Ça n’est pas la panacée, mais c’est un début : ça permet de relancer la dernière commande qui commence par l’argument de r.

$> echo "toto"
$> touch toto
$> r e
#lance echo "toto"
$> r t
#lance touch toto

Avec un argument entier, cela fait la même chose que ! en bash : un positif = la nième commande, un négatif = la nième commande avant celle-ci.

$> echo "toto"
$> touch toto
$> r -2
#lance echo "toto"

Et enfin, avec r avant=apres, on relance la dernière commande en changeant le premier avant par après.

$> cp fic1 /tmp/fic1
$> r 1=2
#lance cp fic2 /tmp/fic1

Des raccourcis en vrac

Bon, j’ai tout plein de choses à faire, mais pas de temps. Quelques choses à partager aussi, mais pas de temps non plus. Du coup, je vais essayer de partager moins mais plus fréquemment.

C’est pas gagné…

En attendant, voici des choses que j’ai trouvées récemment, sans rapport, et sans transition.

M-1 C-x $ / C-x $

Le « code folding » du pauvre (trouvé sur un vieil article d’emacs.wordpress.com). C-x $ appelle la fonction set-selective-display qui la met dans le premier cas à 1 (dans le second à 0) et n’affiche que les lignes dont l’indentation est >= à 1 (resp. 0). Bien pratique quand on indente correctement son code pour n’afficher que les définitions de fonctions…

Dans simple.el

Quand on fait comme le suggère l’article précédent C-h k C-x $, on voit que que la commande est définit dans simple.el. Et comme je suis curieux, je suis allé y faire un tour. Je vous conseille d’en faire autant (pour cela il faut avoir installé les fichiers emacs23-el (à adapter selon la version)). C’est dans ce fichier que sont définies les fonctions comme beginning-of-buffer, goto-line, what-line, count-lines, kill-ring-save, zap-to-char, toggle-word-wrap, etc. Voici les fonctions que j’ai trouvées intéressantes :

split-line C-M-o

Le | représente le curseur :

texte :|texte2

Après C-M-o :

texte curseur:|
               texte2

Bref, comme C-o, mais garde la colonne.

delete-indentation M-^

Relie la ligne à la précédente et arrange les espaces à la jointure (plus parlant sur l’alias join-line). Avec un argument, relie à la suivante :

Les _ représentent des espaces, | le curseur

auie_____
___au|ie___
___auie

C-u M-^

auie_____
___auie_|auie

M-^

auie_|auie_auie

Le mieux étant d’essayer…

delete-blank-line C-x C-o

Plus ou moins comme just-one-space M-spc mais pour les lignes. (À ce propos,je pense que je vais mettre cycle-spacing à la place de M-spc. Mais bon, ça fait partie des choses à faire… Changer certains raccourcis. Je me suis dit ça en voyant certains changements faits par Mickey).

M-x follow-mode

Ou carrément M-x follow-delete-other-windows-and-split qui splitte la fenêtre et passe en follow-mode. Les deux fenêtres représentent le même buffer, et l’une suit l’autre. C’est assez sympa comme effet, même si je ne m’en servirai probablement jamais.

Dired : C-x C-q

y u no in C-h m !!??

Je ferai (ou pas) certainement un billet sur les fonctionnalités dont je me sers de ce mode, mais j’ai (re-)découvert via sachachua (je crois, c’était il y a quelques temps), que l’on pouvait passer en mode édition, et donc renommer plein de fichiers facilement. Et quand on fait un C-h m ce raccourci n’apparaît pas. Pourquoi ‽

occur M-s o

À partir d’emacs24 seulement, e et on passe en mode edit sur les lignes qui matchent : que du bonheur ! Pareil, un billet à faire tellement c’est pratique…

C-x C-0, C-x C-=, C-x C--, C-x C-+, text-scale-adjust

Pour augmenter/diminuer la taille de la police du texte du buffer courant.

Footnotes:

1

J’aurais pu faire l’inverse : coller le premier, puis killer le second. Mais visiblement, certaines personnes, qui utilisent pourtant souvent Emacs, n’ont pas pris la peine de faire le tuto (C-h t) et ne connaissent pas le kill ring.

Dans Emacs, il existe pour certaines commandes un ring qui permet de retrouver d’anciennes valeurs (C-h v -ring TAB). Par exemple search-ring garde les 16 (search-ring-max) dernière recherche, et quand on fait un C-s, il est possible de faire un M-p ou M-n pour parcourir cette liste. Quand on met une mark avec C-SPC (set-mark-command) elle est mise dans le mark-ring et il est possible de revenir aux précédentes avec C-x C-SPC ou C-u C-SPC (attention, il existe un ring local au buffer, et un global !).

Vous l’avez deviné, c’est la même chose avec le kill-ring : quand on utilise C-k, M-d, C-w, etc. on ajoute la ligne/le mot/la région au kill ring (avec C-M-w on ajoute la région au dernier élément du kill-ring ; si on fait deux C-d à la suite, le deuxième mot coupé est ajouté au premier : le kill-ring n’a qu’un nouvel élément). Quand on colle avec C-y on réinsert le kill le plus récent. Si on fait M-y ensuite, on remonte le kill-ring. (Il est possible d’utiliser un argument pour coller le n-ième élément du kill-ring. (Et j’aurais également pu éviter le C-x C-x avec C-u C-y qui colle en laissant le point (curseur) au début, et la mark à la fin.)

Voir le chapitre Killing and Moving Text (manuel emacs) du manuel pour plus d’infos.

Ce qui est sympa, c’est d’utiliser desktop-save-mode et sa variable desktop-globals-to-save pour enregistrer des rings (par défaut le search-ring est sauvé par exemple).

2

Rien ne sert de modifier startup.el, le fichier sert « seulement » à construire les exécutables Emacs DumpingEmacs (emacswiki). Il est chargé par loadup.el (qui est en fait la première chose chargée (source c)) après plusieurs autres bibliothèques comme :

  • subr : qui contient des fonctions lisp comme push, caar, last, add-hook, with-temp-buffer, save-window-excursion, etc.) ;
  • custom : qui contient le code nécessaire à la déclaration et l’initialisation des options via customize ;
  • window : qui touche aux windows et frames (delete-other-window, split-window, switch-to-buffer, etc.) ;
  • files : ce qui touche aux fichiers (find-file, recover-file, save-buffer, write-file, etc.) ;
3

C’est un peu compliqué de savoir comment est définit load-path (source c) à la compilation d’Emacs (comme ça dépend de comment ça a été compilé) et donc connaître les additions des répertoires, mais comme les (sous-)dossiers sont ajoutés à la fin, on peut retrouver sa valeur au lancement d’Emacs.

Autres billets

Date:

Generated by Emacs 24.3.1 (Org mode 8.2.4) - Show Org source (htmlized)

CSS inspired by Tontof, colors by Chaotic Soul

Validate