开发者

Vim: How to number paragraphs automatically and how to refer to this numbering?

Let us say I have the following three paragraphs of text (separated from each other by empty lines—number 3 and 7, here):

This is my first paragraph line 1
This is my first paragraph line 2

This is my second paragraph line 4
This is my second paragraph line 5
This is my second paragraph line 6

This is my third paragraph line 8
This is my third paragraph line 9

Question 1: How can I number these paragraphs automatically, to obtain this result:

1   This is my first paragraph line 1
    This is my first paragraph line 2

2   This is my second paragraph line 4
    This is my second paragraph line 5
    This is my second paragraph line 6

3   This is my third paragraph line 8
    This is my third paragraph line 9

(I succeeded to do this, but only via a clumsy macro.)

Question 2: Is it possible to refer to the开发者_JAVA技巧se paragraphs? For instance, is it possible to index a text file as answered (by Prince Goulash and Herbert Sitz) in the earlier question, but this time with the paragraph numbers and not the line numbers?

Thanks in advance.


Here's one way to do the ref numbers, with a pair of functions:

function! MakeRefMarkers()
     " Remove spaces from empty lines:
     %s/^ \+$//
     " Mark all spots for ref number:
     %s/^\_$\_.\zs\(\s\|\S\)/_parref_/
     " Initialize ref val:
     let s:i = 0
     " Replace with ref nums:
     %s/^_parref_/\=GetRef()/
endfunction

function! GetRef()
     let s:i += 1
     return s:i . '.  '
endfunction

Then just do it by calling MakeRefMarkers(). It doesn't remove existing ref numbers if they're there, that would require another step. And it doesn't catch first paragraph if it's first line in file (i.e, without preceding blank line). But it does handle situations where there's more than one blank line between paragraphs.


Question One

Here is a function to enumerate paragraphs. Simply do :call EnumeratePara() anywhere in your file. The variable indent can be adjusted as you wish. Let me know if anything needs correcting or explaining.

function! EnumeratePara()
    let indent = 5
    let lnum = 1
    let para = 1
    let next_is_new_para = 1
    while lnum <= line("$")
        let this = getline(lnum)
        if this =~ "^ *$"
            let next_is_new_para=1
        elseif next_is_new_para == 1 && this !~ "^ *$"
            call cursor(lnum, 1)
            sil exe "normal i" . para . repeat(" ", indent-len(para))
            let para+=1
            let next_is_new_para = 0
        else
            call cursor(lnum, 1)
            sil exe "normal i" . repeat(" ", indent)
        endif
        let lnum += 1
    endwhile
endfunction

Question Two

This isn't a very elegant approach, but it seems to work. First of all, here's a function that maps each line in the file to a paragraph number:

function! MapLinesToParagraphs()
    let lnum = 1
    let para_lines = []
    let next_is_new_para = 1
    let current_para = 0
    while lnum <= line("$")
        let this = getline(lnum)
        if this =~ "^ *$"
            let next_is_new_para = 1
        elseif next_is_new_para == 1
            let current_para += 1
            let next_is_new_para = 0
        endif
        call add(para_lines, current_para)
        let lnum += 1
    endwhile
    return para_lines
endfunction

So that para_lines[i] will give the paragraph of line i.

Now we can use the existing IndexByWord() function, and use MapLinesToParagraph() to convert the line numbers into paragraph numbers before we return them:

function! IndexByParagraph(wordlist)
    let temp_dict = {}
    let para_lines = MapLinesToParagraphs()
    for word in a:wordlist
        redir => result
        sil! exe ':g/' . word . '/#'
        redir END
        let tmp_list = split(strtrans(result), "\\^\@ *")
        let res_list = []
        call map(tmp_list, 'add(res_list, str2nr(matchstr(v:val, "^[0-9]*")))')
        call map(res_list, 'para_lines[v:val]')
        let temp_dict[word] = res_list
    endfor
    let result_list = []
    for key in sort(keys(temp_dict))
        call add(result_list, key . ' : ' . string(temp_dict[key])[1:-2])
    endfor
    return join(result_list, "\n")
endfunction

I have not tested these functions very thoroughly, but they seem to work okay, at least in your example text. Let me know how you get on!


Both problems could be solved much easier than it is suggested by the other two answers.

1. In order to solve the first problem of numbering paragraphs, the following two steps are ample.

Indent the paragraphs (using tabs, here):

    :v/^\s*$/s/^/\t/

Insert paragraph numbering (see also my answer to the question on substitution with counter):

    :let n=[0] | %s/^\s*\n\zs\ze\s*\S\|\%1l/\=map(n,'v:val+1')

2. The second problem of creating index requires some scripting in order to be solved by Vim means only. Below is the listing of a small function, WordParIndex() that is supposed to be run after paragraphs are numbered according to the first problem's description.

function! WordParIndex()
    let [p, fq] = [0, {}]
    let [i, n] = [1, line('$')]
    while i <= n
        let l = getline(i)
        if l !~ '^\s*$'
            let [p; ws] = ([p] + split(l, '\s\+'))[l=~'^\S':]
            for w in ws
                let t = get(fq, w, [p])
                let fq[w] = t[-1] != p ? t + [p] : t
            endfor
        endif
        let i += 1
    endwhile
    return fq
endfunction

The return value of the WordParIndex() function is the target index dictionary. To append its text representation at the end of a buffer, run

:call map(WordParIndex(), 'append(line("$"),v:key.": ".join(v:val,","))')


My approach would be macro based:

  1. Yank the number "0" somehow and move to the start of the first paragraph.

  2. Record a macro to

    1. Indent the paragraph with >}

    2. Paste the stored number at the correct position p

    3. Increment the number by one with <ctrl>-a

    4. Yank the pasted number with yiw

    5. Move to the next paragraph with }l or /^\S

  3. Execute the macro as many times as needed to reach the end of the document.


The method of pasting a number, incrementing it, and then reyanking it inside a macro is quite a useful technique that comes in handy whenever you need to number things. And it's simple enough to just do it in a throw-away fashion. I mainly use it for carpet logging, but it has other uses as your question demonstrates.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜