Come calcolare automaticamente le linee di inizio e fine quando si includono i file di origine in modalità organizzazione?


10

Ho il seguito nella mia documentazione:

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

Qui la linea 14 è dove ho class basic extends ..e la linea 116 è dove ho endclass.

C'è un modo per inserire automaticamente i numeri 14 e 117 (= 116 + 1) in modo da non doverli aggiornare manualmente ogni volta che modifico il code/basic.sv?


Quindi vuoi sempre che vada dalla classe all'endclass?
Malabarba,

1
No. Quello era un esempio. Sto pensando a una soluzione in cui posso fornire regex per le linee di inizio e fine. Qualcosa valuterebbe una funzioneorg-include-src(FILE, LANGUAGE, REGEX_BEGIN, REGEX_END)
Kaushal Modi,

Un modo è, posizionando una sorta di marcatori univoci (inizio fine) nel file incluso e trovarli con una funzione che sarebbe agganciata org-export-before-processing-hookper preelaborare i numeri di riga. Un altro modo è semplicemente inviare un messaggio di richiesta di funzionalità alla mailing list
dell'organizzazione

Risposte:


8

Ecco un'altra opzione. Questo è ti consente di personalizzare le espressioni regolari in base al tipo di inclusione. Dovrebbe adattarsi meglio ad alcuni flussi di lavoro poiché non sei limitato alle definizioni basate sull'estensione.

Usare

Fai qualcosa di simile al seguente nel tuo file-org. (La :linesparola chiave è facoltativa)

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

La funzione visiterà "my-class.sv" e cercherà quei due regexps, quindi aggiornerà la :linesparola chiave in base al risultato della corrispondenza.

Se :range-beginmanca, l'intervallo sarà "-80".
Se :range-endmanca, l'intervallo sarà "14-".

Il codice

(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)))))

2
Questo è fantastico! Ora posso usarlo per esportare più frammenti dallo stesso file. Snippet 1: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 1" :range-end "// End of Example 1". Snippet 2: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 2" :range-end "// End of Example 2". L'esecuzione è impeccabile! Grazie per aver implementato questo così in fretta!
Kaushal Modi,

5

Il modo migliore che mi viene in mente è di aggiornare questi numeri immediatamente prima dell'esportazione o prima della valutazione.

The Updater

Questa è la funzione che passa attraverso il buffer. È possibile associarlo a una chiave o aggiungerlo a un hook. Il codice seguente aggiorna le righe ogni volta che salvi il file , ma se il tuo caso d'uso è diverso, scopri quale hook hai bisogno! (la modalità org è piena di hook)

(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)))))))

The Regexps

Qui è dove si definiscono le regexps che verranno utilizzate come prima e ultima riga da includere. Puoi dare un elenco di regexps per ogni estensione di file.

(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)))))

Il lavoratore in background

Questo è il ragazzo che fa la maggior parte del lavoro.

(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))))))))

1
Se posso suggerire, edebug le due funzioni e quindi invocare la prima con Mx. Questo dovrebbe essere molto istruttivo. :-)
Malabarba,

La funzione da sola funziona bene. Ma l'hook deve passare un argomento alla funzione che sta chiamando. Dalla documentazione per org-export-before-processing-hook, Every function in this hook will be called with one argument: the back-end currently used, as a symbol. Dato che non stiamo discutendo, otteniamo l'errore run-hook-with-args: Wrong number of arguments. Ora non sono sicuro di quale argomento aggiungere a endless/update-includes... (&optional dummy)?
Kaushal Modi,

@kaushalmodi oops, il mio male. Ho aggiornato la risposta. Puoi usare anche le cose che hai scritto.
Malabarba,

OK .. l'aggiunta (&optional dummy)ha funzionato davvero! Ma un interessante effetto collaterale di chiamare la funzione tramite hook. Se chiamo la funzione usando M-x, modifica il .orgfile con i numeri di riga aggiornati. Ma se esporto semplicemente in HTML e consento all'hook di chiamare la funzione, i numeri di riga aggiornati si riflettono solo nel file esportato, NON nel .orgfile.
Kaushal Modi,

@kaushalmodi Sì, è così che funzionano gli hook per org. Puoi invece aggiungerlo a before-save-hook.
Malabarba,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.