Comente (automaticamente) um arquivo fonte LaTeX para torná-lo mais legível

3

Estou procurando uma maneira simples de fazer o seguinte:

Entrada:

\begin{document}
...
\section{}
...
\subsection{}
...
\subsubsection{}
...
\section{}
...
\end{document}

Saída:

\begin{document}
...
%1
\section{}
...
%1.1
\subsection{}
...
%1.1.1
\subsubsection{}
...
%2
\section{}
...
\end{document}

O objetivo é tornar os documentos longos mais legíveis. Eu quero sempre saber onde estou em todo o plano do documento.

    
por bela83 26.03.2014 / 18:39

3 respostas

2

Eu tenho uma solução rápida que é útil muitas vezes. Observe que é apenas para uso pessoal (1); poderia ser refinado, adicionado edição no local, controle de erros, qualquer coisa. Mas eu acho que é útil como é também. A ideia é aproveitar a própria numeração do LaTeX.

Portanto, antes de tudo, você precisa adicionar marcadores ao seu documento (o que é bom, no entanto):

\documentclass[12pt]{article}
\begin{document}
\section{a}
\label{sec:a}

\section{b}
\label{sec:b}

\subsection{b a}
\label{sec:ba}

\newpage

\subsection{b b}
\label{sec:bb}

\section{c}
\label{sec:c}
\end{document}

Em seguida, execute o latex como sempre, suponha que ele seja chamado de walla.tex . Agora você executa este pequeno script python:

#!/usr/bin/env python
#
# 
import sys
import re

labels=[]
# build a list of label
for l in open(sys.argv[1] + ".aux"):
    if l.find("newlabel{") != -1: 
        m = re.search(r'\newlabel{(.*?)}{{(.*?)}{(.*?)}}', l)
        if m:
            labels.append("label: %s will be number: %s at page: %s" % (
                m.group(1), m.group(2), m.group(3)))
        else:
            labels.append(l)

# scan input file
for l in  open(sys.argv[1] + ".tex"):
    if l.find("\label") != -1:
        # we have a label, try to match it
        m = re.search(r'\label{(.*?)}', l)
        # if not m: continue ERROR not managed here
        key = m.group(1) 
        for lab in labels:
            if lab.find(key) != -1:
                # modify this to pretty print
                sys.stdout.write("%%%%%% %s\n" % lab.strip())
                break
    # output the tex file avoiding old ones
    if not l.startswith(r'%%% label'):
        sys.stdout.write(l)

Chame find_tex_labels , torne-a executável e execute-a como find_tex_labels walla > walla_annotated.tex (note, sem extensões nos argumentos).

Você terá seu arquivo LaTeX anotado na saída:

\documentclass[12pt]{article}
\begin{document}
\section{a}
%%% label: sec:a will be number: 1 at page: 1
\label{sec:a}

\section{b}
%%% label: sec:b will be number: 2 at page: 1
\label{sec:b}

\subsection{b a}
%%% label: sec:ba will be number: 2.1 at page: 1
\label{sec:ba}

\newpage

\subsection{b b}
%%% label: sec:bb will be number: 2.2 at page: 2
\label{sec:bb}

\section{c}
%%% label: sec:c will be number: 3 at page: 2
\label{sec:c}
\end{document}

... isso funcionará para todos os rótulos. Eu acho bastante útil para a equação de referência cruzada etc quando estou editando em um dispositivo que não possui LaTeX. Agora você pode substituir o walla.tex original pelo novo.

É sua responsabilidade manter as coisas em sincronia ... e não usar "%%% label" comentários em nenhum lugar.

Notas de rodapé:

(1) Prometo o refinamento muitas vezes. Então, dado que eu sou o único a usá-lo, corrijo os erros se e quando eles saem ... e nunca encontro tempo para limpá-lo.

    
por 26.03.2014 / 21:18
1

A parte relativamente difícil é que você tem que armazenar em buffer uma linha comentada para ver se ela precisa ser atualizada caso a próxima linha seja um indicador de seção. Seria mais simples se esses dados estivessem na mesma linha ou na próxima linha.

O seguinte deve ajudá-lo. Pode ser invocado como python script.py input output ou você pode deixar de fora a saída e escreve para stdout. Não faça 'python script.py xx.tex xx.tex', mas escreva em um arquivo temporário e copie-o de volta ao original.

Isso atualiza as linhas existentes no formulário %x.y.z rest of comment deixando rest of comment intocado. Se ainda não houver esse comentário, insira-o. Os comentários especiais devem começar no início da linha, assim como os comandos de seção.

import sys

class ProcessLaTeX:
    def __init__(self, ifp, ofp):
        self.ofp = ofp
        self.prev_comment = None
        self.level = []
        for line in ifp:
            self.process(line)
        # emit last line if comment
        if self.prev_comment:
            self.ofp.write(self.prev_comment)

    def output(self, line):
        pass

    def process(self, line):
        if line[0] == '%':
            # store comment line, emitting any previously stored line
            if self.prev_comment:
                self.ofp.write(self.prev_comment)
            self.prev_comment = line
            return
        lvl = self.check_level(line)
        if lvl > -1:
            self.output_level_comment(lvl)
        if self.prev_comment:
            self.ofp.write(self.prev_comment)
            self.prev_comment = None
        self.ofp.write(line)

    def output_level_comment(self, lvl):
        if self.prev_comment: # check if we overwrite an old one
            # do not use the starting '%' and final newline
            words = self.prev_comment[1:-1].split(' ', 1)
            for c in words[0]:
                if c not in '01234567890.':
                    self.ofp.write(self.prev_comment)
                    self.prev_comment = None
                    break
        self.level.append(0) # in case this is a deeper level
        self.level[lvl] += 1
        self.level = self.level[:lvl+1] # cut of excess levels
        lvls = '%' + '.'.join([str(l) for l in self.level])
        if self.prev_comment: # overwrite the previous words[1]
            words[0] = lvls
            outs = ' '.join(words)
            if not outs[-1] == '\n':
                outs += '\n'
            self.prev_comment = None
        else:
            outs = lvls + '\n'
        self.ofp.write(outs)

    def check_level(self, line):
        if line and not line[0] == '\':
            return -1
        cmd = line[1:].split('{', 1)[0]
        try:
            res = ['section', 'subsection', 'subsubsection',
                     'paragraph', 'subparagraph'].index(cmd)
        except ValueError:
            return -1
        return res

out = sys.stdout if len(sys.argv) < 3 else open(sys.argv[2], 'w')
pl = ProcessLaTeX(open(sys.argv[1]), out)
    
por 08.12.2014 / 00:02
1

O que você está procurando, como eu acho, é a opção delimitador de seção do nl . De info nl :

  • nl decompõe sua entrada em páginas (lógicas); Por padrão, o número da linha é redefinido para 1 na parte superior de cada página lógica. nl trata todos os arquivos de entrada como um único documento; ele não redefine números de linha ou páginas lógicas entre arquivos.

  • Uma página lógica consiste em três seções: cabeçalho , corpo e rodapé . Qualquer uma das seções pode estar vazia. Cada um pode ser numerado em um estilo diferente dos outros.

  • O início das seções de páginas lógicas é indicado no arquivo de entrada por uma linha contendo exatamente uma dessas strings delimitadoras:

    • \:\:\: - início do cabeçalho;
    • \:\: - início do corpo;
    • \: - início do rodapé.

Você pode definir a página lógica de nl -d elimiter na linha de comando, como:

nl -dCC <infile

... onde CC representa quaisquer dois caracteres para substituir o \: , conforme indicado na documentação. Dada a sua opinião, não acho que seja necessário - podemos inserir os padrões quando aplicável com um pouco de filtragem de entrada. Aqui está nl e sed emparelhados em uma função de shell que escrevi projetada para se filtrar recursivamente:

sd() { n='
';     nl -bp"^\\$1section" -w1 -s"$n\:\:\:$n" |
       sed '/./!d;/^[0-9]/!s/^[[:blank:]]*//;/^%[0-9.]*$/h;t
            s/./%&/;x;/%/G;s//./2;/\n.*/h;s///;x;s/\n//;N
            s/\(\(.*\)\(\n\)\)\(\(.*\)\(..\)\)//'
}

Eu a alimentei com algo parecido com seus dados de exemplo e enviei sua saída de volta para ela algumas vezes:

sd <<\IN |sd sub | sd subsub | sd subsubsub
\begin{document}
\section{}
some ordinary lines
\subsection{}
whatever
\subsubsection{}
\subsection{}
\subsubsection{}
\subsubsubsection{}
\section{}
\subsection{}
\end{document}
IN

Executar como acima, é impresso:

\begin{document}
%1
\section{}
some ordinary lines
%1.1
\subsection{}
whatever
%1.1.1
\subsubsection{}
%1.2
\subsection{}
%1.2.1
\subsubsection{}

\:\:\:
%1.2.1.1
\:\:
\subsubsubsection{}
%2
\section{}
%2.1
\subsection{}
\end{document}

Como você pode ver, o trabalho de filtro não está completamente concluído, mas parece que faz o trabalho. nl numera a -b ody de sua entrada com base no -bp'attern' que é alimentado - e inicia sua contagem para cada página lógica - delimitada por uma linha composta somente de sua lógica delimitador de cabeçalho de página \:\:\: .

Então ... sed filtra sua saída - que já inclui o delimitador como definido em nl -s eparator arg e basicamente sed apenas reorganiza um pouco, então nl encontrará sua seção delimitador onde deveria no próximo passo. sed também mantém uma cópia da última linha ^%[0-9.]*$ em seu espaço h old - e se o espaço de espera não estiver vazio quando encontrar uma linha que comece com um número, ele anexará essa linha ao conteúdo de seu espaço. segure espaço seguindo um . . E essa é a carne e as batatas, realmente.

Ainda assim - como eu digo, não está feito. A última passagem deixou os delimitadores de seção e as linhas em branco na saída. Então, para limpar isso:

sd <<\IN |sd sub | sd subsub | sd subsubsub | grep -v '^\:\|^$'
\begin{document}
\section{}
some ordinary lines
\subsection{}
whatever
\subsubsection{}
\subsection{}
\subsubsection{}
\subsubsubsection{}
\subsubsection{}
\subsubsubsection{}
\section{}
\subsection{}
\end{document}
IN

OUTPUT:

\begin{document}
%1
\section{}
some ordinary lines
%1.1
\subsection{}
whatever
%1.1.1
\subsubsection{}
%1.2
\subsection{}
%1.2.1
\subsubsection{}
%1.2.1.1
\subsubsubsection{}
%1.2.2
\subsubsection{}
%1.2.2.1
\subsubsubsection{}
%2
\section{}
%2.1
\subsection{}
\end{document}
    
por 08.12.2014 / 12:54