Как автоматически рассчитать начальную и конечную строки при включении исходных файлов в org-режиме?

10

У меня есть ниже в моей документации:

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

Здесь строка 14 - там, где я есть, class basic extends ..а строка 116 - там, где я есть endclass.

Есть ли способ автоматически вставить числа 14 и 117 (= 116 + 1), чтобы мне не приходилось обновлять их вручную каждый раз, когда я изменяю code/basic.sv?

Каушал Моди
источник
Таким образом, вы всегда хотите, чтобы оно переходило из класса в класс?
Малабарба
1
Это был пример. Я думаю о решении, в котором я могу предоставить регулярное выражение для начала и конца строки. Что-то могло бы оценить функциюorg-include-src(FILE, LANGUAGE, REGEX_BEGIN, REGEX_END)
Kaushal Modi
Одним из способов является размещение каких-то уникальных маркеров (начало и конец) во включаемом файле и поиск их с помощью функции, к которой можно было бы подключиться org-export-before-processing-hookдля предварительной обработки номеров строк. Другой способ - просто отправить запрос на добавление функции в список рассылки org :)
kindahero

Ответы:

8

Вот еще один вариант. Это позволяет вам настраивать регулярные выражения для каждого включения. Он должен лучше подходить для некоторых рабочих процессов, поскольку вы не ограничены определениями на основе расширений.

Использовать

Сделайте что-то вроде следующего в вашем орг-файле. ( :linesКлючевое слово необязательно)

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

Функция посетит my-class.sv и выполнит поиск этих двух регулярных выражений, а затем обновит :linesключевое слово в соответствии с результатом совпадения.

Если :range-beginотсутствует, диапазон будет "-80".
Если :range-endотсутствует, диапазон будет "14-".

Код

(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
источник
2
Это замечательно! Теперь я могу использовать это для экспорта нескольких фрагментов из одного файла. Отрывок 1: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 1" :range-end "// End of Example 1". Отрывок 2: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 2" :range-end "// End of Example 2". Исполнение безупречно! Спасибо за реализацию этого так быстро!
Каушал Моди
5

Лучший способ, которым я могу придумать, - обновить эти числа непосредственно перед экспортом или перед оценкой.

Updater

Это функция, которая проходит через буфер. Вы можете привязать его к ключу или добавить его к крючку. Следующий код обновляет строки всякий раз, когда вы сохраняете файл , но если ваш вариант использования отличается, просто выясните, какой хук вам нужен! (орг-режим полон хуков)

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

Регулярные выражения

Здесь вы определяете регулярные выражения, которые будут использоваться в качестве первой и последней строк, которые будут включены. Вы можете дать список регулярных выражений для каждого расширения файла.

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

Фоновый рабочий

Это парень, который делает большую часть работы.

(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
источник
1
Если я могу предложить, отладьте две функции и затем вызовите первую с помощью Mx. Это должно быть очень информативным. :-)
Малабарба
Функция сама по себе работает нормально. Но хук должен передать аргумент функции, которую он вызывает. Из документов для org-export-before-processing-hook, Every function in this hook will be called with one argument: the back-end currently used, as a symbol. Поскольку мы не передаем никаких аргументов, мы получаем ошибку run-hook-with-args: Wrong number of arguments. Теперь я не уверен, какой аргумент добавить в endless/update-includes... (&optional dummy)?
Каушал Моди
@kaushalmodi ой, мой плохой. Я обновил ответ. Вы также можете использовать то, что написали.
Малабарба
ОК .. добавление (&optional dummy)фактически сработало! Но интересный побочный эффект вызова функции через хук. Если я вызываю функцию с помощью M-x, она изменяет .orgфайл с обновленными номерами строк. Но если я просто экспортирую в html и позволю хуку вызывать функцию, обновленные номера строк будут отражены только в экспортированном файле, а НЕ в .orgфайле.
Каушал Моди
@kaushalmodi Да, так работают орг-хуки. Вместо этого вы можете добавить его в before-save-hook.
Малабарба