Jak automatycznie obliczyć linie początkową i końcową, gdy dołączasz pliki źródłowe w trybie org?

10

Mam następujące dokumenty w mojej dokumentacji:

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

Tutaj mam linię 14, class basic extends ..a mam linię 116 endclass.

Czy istnieje sposób automatycznego wstawiania liczb 14 i 117 (= 116 + 1), aby nie musiałem ręcznie ich aktualizować za każdym razem, gdy modyfikuję code/basic.sv?

Kaushal Modi
źródło
Więc zawsze chcesz, aby przechodził od klasy do klasy końcowej?
Malabarba
1
Nie. To był przykład. Myślę o rozwiązaniu, w którym mogę zapewnić wyrażenie regularne dla linii początkowej i końcowej. Coś org-include-src(FILE, LANGUAGE, REGEX_BEGIN, REGEX_END)
oceniłoby
Jednym ze sposobów jest umieszczenie pewnego rodzaju unikalnych znaczników (początek i koniec) w dołączonym pliku i znalezienie ich za pomocą funkcji, która byłaby zaczepiona w org-export-before-processing-hookcelu wstępnego przetworzenia dla numerów linii. Innym sposobem jest po prostu wysłanie wiadomości z prośbą o
dodanie

Odpowiedzi:

8

Oto kolejna opcja. Ten pozwala ci dostosować wyrażenia regularne dla poszczególnych elementów. Powinien lepiej pasować do niektórych przepływów pracy, ponieważ nie jesteś ograniczony do definicji opartych na rozszerzeniach.

Używać

Zrób coś takiego w swoim pliku org. (Słowo :lineskluczowe jest opcjonalne)

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

Funkcja odwiedzi „my-class.sv” i wyszuka te dwa wyrażenia regularne, a następnie zaktualizuje :linessłowo kluczowe zgodnie z wynikiem dopasowania.

Jeśli go :range-beginbrakuje, zakres będzie wynosić „-80”.
Jeśli go :range-endbrakuje, zakres będzie wynosić „14-”.

Kod

(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
źródło
2
To jest świetne! Teraz mogę użyć tego do wyeksportowania wielu fragmentów z tego samego pliku. Fragment 1: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 1" :range-end "// End of Example 1". Fragment 2: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 2" :range-end "// End of Example 2". Wykonanie jest bezbłędne! Dziękujemy za wdrożenie tego tak szybko!
Kaushal Modi
5

Najlepszym sposobem, jaki mogę wymyślić, jest aktualizacja tych liczb bezpośrednio przed eksportem lub oceną.

Aktualizator

Jest to funkcja przechodząca przez bufor. Możesz powiązać go z kluczem lub dodać do haka. Poniższy kod aktualizuje wiersze za każdym razem, gdy zapisujesz plik , ale jeśli Twój przypadek użycia jest inny, po prostu dowiedz się, jakiego haka potrzebujesz! (tryb org jest pełen haków)

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

Regexps

W tym miejscu definiujesz wyrażenia regularne, które będą używane jako pierwsza i ostatnia linia do uwzględnienia. Możesz podać listę wyrażeń regularnych dla każdego rozszerzenia pliku.

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

Pracownik w tle

To facet, który wykonuje większość pracy.

(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
źródło
1
Jeśli mogę zasugerować, edebuguj dwie funkcje, a następnie wywołaj pierwszą z Mx. To powinno być bardzo pouczające. :-)
Malabarba
Sama funkcja działa dobrze. Ale hook musi przekazać argument do funkcji, którą wywołuje. Od docs dla org-export-before-processing-hook, Every function in this hook will be called with one argument: the back-end currently used, as a symbol. Ponieważ nie przekazujemy żadnego argumentu, otrzymujemy błąd run-hook-with-args: Wrong number of arguments. Teraz nie jestem pewien, jaki argument dodać do endless/update-includes... (&optional dummy)?
Kaushal Modi
@kaushalmodi oops, my bad. Zaktualizowałem odpowiedź. Możesz także użyć rzeczy, którą napisałeś.
Malabarba,
OK .. dodanie (&optional dummy)faktycznie działało! Ale ciekawy efekt uboczny wywołania funkcji przez hak. Jeśli wywołam funkcję za pomocą M-x, modyfikuje ona .orgplik za pomocą zaktualizowanych numerów linii. Ale jeśli po prostu wyeksportuję do html i pozwolę hookowi wywołać funkcję, zaktualizowane numery wierszy zostaną odzwierciedlone tylko w eksportowanym pliku, a NIE w .orgpliku.
Kaushal Modi
@kaushalmodi Tak, tak działają haki org. Zamiast tego możesz dodać go do zaczepu przed zapisem.
Malabarba