Como calcular automaticamente as linhas inicial e final ao incluir arquivos de origem no modo organizacional?


10

Tenho o seguinte na minha documentação:

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

Aqui a linha 14 é onde eu tenho class basic extends ..e a linha 116 é onde eu tenho endclass.

Existe uma maneira de inserir automaticamente os números 14 e 117 (= 116 + 1) para que eu não precise atualizá-los manualmente toda vez que modificar o code/basic.sv?


Então você sempre quer que vá da classe para a classe final?
Malabarba

11
Não. Isso foi um exemplo. Estou pensando em uma solução onde eu posso fornecer regex para começar e linhas de fundo .. Algo iria avaliar uma funçãoorg-include-src(FILE, LANGUAGE, REGEX_BEGIN, REGEX_END)
Kaushal Modi

Uma maneira é colocar alguns tipos de marcadores exclusivos (begin end) no arquivo incluído e encontrá-los com uma função que seria conectada org-export-before-processing-hookao pré-processamento dos números de linha. Outra maneira é simplesmente enviar um e-mail pedido de recurso à lista de discussão org :)
kindahero

Respostas:


8

Aqui está outra opção. Neste exemplo, vamos personalizar as expressões regulares por inclusão. Ele deve se encaixar melhor em alguns fluxos de trabalho, pois você não está limitado a definições baseadas em extensões.

Usar

Faça algo como o seguinte em seu arquivo organizacional. (A :linespalavra-chave é opcional)

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

A função visitará "my-class.sv" e procurará esses dois regexps e, em seguida, atualizará a :linespalavra - chave de acordo com o resultado da correspondência.

Se :range-beginestiver faltando, o intervalo será "-80".
Se :range-endestiver faltando, o intervalo será "14-".

O código

(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
Isso é ótimo! Agora eu posso usar isso para exportar vários trechos do mesmo arquivo. Trecho 1: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 1" :range-end "// End of Example 1". Trecho 2: #+INCLUDE: "code/basic.sv" :src systemverilog :range-begin "// Example 2" :range-end "// End of Example 2". A execução é impecável! Obrigado por implementar isso tão rápido!
Kaushal Modi

5

A melhor maneira de pensar é atualizar esses números imediatamente antes de exportar ou antes de avaliar.

O Atualizador

Essa é a função que passa pelo buffer. Você pode vinculá-lo a uma chave ou adicioná-lo a um gancho. O código a seguir atualiza as linhas sempre que você salva o arquivo , mas se o seu caso de uso for diferente, basta descobrir qual gancho você precisa! (o modo organizacional está cheio de ganchos)

(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

É aqui que você define os regexps que serão usados ​​como a primeira e a última linha a serem incluídas. Você pode fornecer uma lista de regexps para cada extensão de arquivo.

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

O trabalhador em segundo plano

Esse é o cara que faz a maior parte do trabalho.

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

11
Se eu puder sugerir, edebug as duas funções e depois invoque a primeira com Mx. Isso deve ser muito informativo. :-)
Malabarba

A função por si só funciona bem. Mas o gancho precisa passar um argumento para a função que está chamando. De docs para org-export-before-processing-hook, Every function in this hook will be called with one argument: the back-end currently used, as a symbol. Como não estamos passando nenhum argumento, obtemos o erro run-hook-with-args: Wrong number of arguments. Agora não tenho certeza de qual argumento adicionar a endless/update-includes... (&optional dummy)?
Kaushal Modi

@kaushalmodi oops, meu mal. Eu atualizei a resposta. Você pode usar o que escreveu também.
Malabarba

OK .. adicionando (&optional dummy)realmente funcionou! Mas um efeito colateral interessante de chamar a função via gancho. Se eu chamar a função usando M-x, ela modifica o .orgarquivo com os números de linha atualizados. Mas se eu simplesmente exportar para html e permitir que o gancho chame a função, os números de linha atualizados serão refletidos apenas no arquivo exportado, NÃO no .orgarquivo.
Kaushal Modi

@kaushalmodi Sim, é assim que os ganchos organizacionais funcionam. Você pode adicioná-lo ao antes de salvar o gancho.
Malabarba 25/09
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.