Comment calculer automatiquement les lignes de début et de fin lors de l'inclusion de fichiers source en mode org?

10

J'ai ci-dessous dans ma documentation:

#+INCLUDE: "code/basic.sv" :src systemverilog :lines "14-117"

Ici, la ligne 14 est où j'ai class basic extends ..et la ligne 116 est où j'ai endclass.

Existe-t-il un moyen d'insérer automatiquement les nombres 14 et 117 (= 116 + 1) afin de ne pas avoir à les mettre à jour manuellement chaque fois que je modifie le code/basic.sv?

Kaushal Modi
la source
Donc, vous voulez toujours qu'il passe de la classe à la classe finale?
Malabarba, le
1
Non, c'était un exemple. Je pense à une solution où je peux fournir des expressions rationnelles pour les lignes de début et de fin. Quelque chose évaluerait une fonctionorg-include-src(FILE, LANGUAGE, REGEX_BEGIN, REGEX_END)
Kaushal Modi
Une façon est de placer une sorte de marqueurs uniques (début fin) dans le fichier inclus et de les trouver avec une fonction qui serait accrochée org-export-before-processing-hookpour prétraiter les numéros de ligne. Une autre façon consiste simplement à envoyer un courrier de demande de fonctionnalité à la liste de diffusion de l'organisation :)
kindahero

Réponses:

8

Voici une autre option. Celui-ci vous permet de personnaliser les expressions régulières par inclusion. Il convient mieux à certains flux de travail, car vous n'êtes pas limité aux définitions basées sur les extensions.

Utiliser

Faites quelque chose comme ce qui suit dans votre fichier d'organisation. (Le :linesmot-clé est facultatif)

#+INCLUDE: "code/my-class.sv" :src systemverilog :range-begin "^class" :range-end "^endclass" :lines "14-80"

La fonction visitera "my-class.sv" et recherchera ces deux expressions régulières, puis elle mettra à jour le :linesmot - clé en fonction du résultat de la correspondance.

S'il :range-beginmanque, la plage sera "-80".
S'il :range-endmanque, la plage sera "14-".

Le code

(add-hook 'before-save-hook #'endless/update-includes)

(defun endless/update-includes (&rest ignore)
  "Update the line numbers of #+INCLUDE:s in current buffer.
Only looks at INCLUDEs that have either :range-begin or :range-end.
This function does nothing if not in org-mode, so you can safely
add it to `before-save-hook'."
  (interactive)
  (when (derived-mode-p 'org-mode)
    (save-excursion
      (goto-char (point-min))
      (while (search-forward-regexp
              "^\\s-*#\\+INCLUDE: *\"\\([^\"]+\\)\".*:range-\\(begin\\|end\\)"
              nil 'noerror)
        (let* ((file (expand-file-name (match-string-no-properties 1)))
               lines begin end)
          (forward-line 0)
          (when (looking-at "^.*:range-begin *\"\\([^\"]+\\)\"")
            (setq begin (match-string-no-properties 1)))
          (when (looking-at "^.*:range-end *\"\\([^\"]+\\)\"")
            (setq end (match-string-no-properties 1)))
          (setq lines (endless/decide-line-range file begin end))
          (when lines
            (if (looking-at ".*:lines *\"\\([-0-9]+\\)\"")
                (replace-match lines :fixedcase :literal nil 1)
              (goto-char (line-end-position))
              (insert " :lines \"" lines "\""))))))))

(defun endless/decide-line-range (file begin end)
  "Visit FILE and decide which lines to include.
BEGIN and END are regexps which define the line range to use."
  (let (l r)
    (save-match-data
      (with-temp-buffer
        (insert-file file)
        (goto-char (point-min))
        (if (null begin)
            (setq l "")
          (search-forward-regexp begin)
          (setq l (line-number-at-pos (match-beginning 0))))
        (if (null end)
            (setq r "")
          (search-forward-regexp end)
          (setq r (1+ (line-number-at-pos (match-end 0)))))
        (format "%s-%s" l r)))))
Malabarba
la source
2
C'est bien! Maintenant, je peux l'utiliser pour exporter plusieurs extraits de code à partir du même fichier. Snippet 1: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 1" :range-end "// End of Example 1". 2 Snippet: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 2" :range-end "// End of Example 2". L'exécution est sans faille! Merci d'avoir implémenté cela aussi rapidement!
Kaushal Modi
5

La meilleure façon de penser est de mettre à jour ces chiffres immédiatement avant l'exportation ou avant l'évaluation.

Le programme de mise à jour

C'est la fonction qui passe par le tampon. Vous pouvez le lier à une clé ou l'ajouter à un crochet. Le code suivant met à jour les lignes chaque fois que vous enregistrez le fichier , mais si votre cas d'utilisation est différent, découvrez simplement le crochet dont vous avez besoin! (le mode org est plein de crochets)

(add-hook 'before-save-hook #'endless/update-includes)

(defun endless/update-includes (&rest ignore)
  "Update the line numbers of all #+INCLUDE:s in current buffer.
Only looks at INCLUDEs that already have a line number listed!
This function does nothing if not in org-mode, so you can safely
add it to `before-save-hook'."
  (interactive)
  (when (derived-mode-p 'org-mode)
    (save-excursion
      (goto-char (point-min))
      (while (search-forward-regexp
              "^\\s-*#\\+INCLUDE: *\"\\([^\"]+\\)\".*:lines *\"\\([-0-9]+\\)\""
              nil 'noerror)
        (let* ((file (expand-file-name (match-string-no-properties 1)))
               (lines (endless/decide-line-range file)))
          (when lines
            (replace-match lines :fixedcase :literal nil 2)))))))

Les Regexps

C'est là que vous définissez les expressions rationnelles qui seront utilisées comme première et dernière lignes à inclure. Vous pouvez donner une liste d'expressions rationnelles pour chaque extension de fichier.

(defcustom endless/extension-regexp-map 
  '(("sv" ("^class\\b" . "^endclass\\b") ("^enum\\b" . "^endenum\\b")))
  "Alist of regexps to use for each file extension.
Each item should be
    (EXTENSION (REGEXP-BEGIN . REGEXP-END) (REGEXP-BEGIN . REGEXP-END))
See `endless/decide-line-range' for more information."
  :type '(repeat (cons string (repeat (cons regexp regexp)))))

Le travailleur d'arrière-plan

C'est le gars qui fait la plupart du travail.

(defun endless/decide-line-range (file)
  "Visit FILE and decide which lines to include.
The FILE's extension is used to get a list of cons cells from
`endless/extension-regexp-map'. Each cons cell is a pair of
regexps, which determine the beginning and end of region to be
included. The first one which matches is used."
  (let ((regexps (cdr-safe (assoc (file-name-extension file)
                                  endless/extension-regexp-map)))
        it l r)
    (when regexps
      (save-match-data
        (with-temp-buffer
          (insert-file file)
          (while regexps
            (goto-char (point-min))
            (setq it (pop regexps))
            (when (search-forward-regexp (car it) nil 'noerror)
              (setq l (line-number-at-pos (match-beginning 0)))
              (when (search-forward-regexp (cdr it) nil 'noerror)
                (setq regexps nil
                      r (line-number-at-pos (match-end 0))))))
          (when r (format "%s-%s" l (+ r 1))))))))
Malabarba
la source
1
Si je peux suggérer, éditez les deux fonctions et invoquez la première avec Mx. Cela devrait être très instructif. :-)
Malabarba
La fonction en elle-même fonctionne bien. Mais le hook doit passer un argument à la fonction qu'il appelle. De la documentation pour org-export-before-processing-hook, Every function in this hook will be called with one argument: the back-end currently used, as a symbol. Comme nous ne transmettons aucun argument, nous obtenons l'erreur run-hook-with-args: Wrong number of arguments. Maintenant, je ne sais pas à quel argument ajouter endless/update-includes... (&optional dummy)?
Kaushal Modi
@kaushalmodi oups, ma mauvaise. J'ai mis à jour la réponse. Vous pouvez également utiliser ce que vous avez écrit.
Malabarba du
OK .. l'ajout a (&optional dummy)réellement fonctionné! Mais un effet secondaire intéressant d'appeler la fonction via le crochet. Si j'appelle la fonction à l'aide M-x, elle modifie le .orgfichier avec les numéros de ligne mis à jour. Mais si j'exporte simplement en html et que le crochet appelle la fonction, les numéros de ligne mis à jour sont reflétés uniquement dans le fichier exporté, PAS dans le .orgfichier.
Kaushal Modi du
@kaushalmodi Oui, c'est ainsi que fonctionnent les hooks d'organisation. Vous pouvez à la place l'ajouter à before-save-hook.
Malabarba du