how to query the highest digit in a search?

75 Views Asked by At

Searching for the pattern /{{c\d, I'd like to get the highest digit found and use it in a macro.

For context, I'm using Anki (flashcard tool) and its cloze card type, and recently started creating my cards within vim.

Example card:

## Front
reading: 
{{c1::A::reading A}}
B
{{c2::C::reading C}}
{{c1::D::reading D}}
E


## Back
...

In that example, given I'm positioned above E, I'd like to execute a macro that'll figure out that the highest cloze digit is 2 (in {{c2::C::reading C}}) and create a new cloze with highest digit incremented by one as {{c3::E::reading E}}

My macro currently looks like this:

:registers
"c   ysiw}wysiw}wyiwic3::^OP::reading
  • ysiw} uses vim-surround to wrap the word in braces.
  • wysiw} repeats that operation
  • wyiw yanks the word
  • ic3:: adds c3:: in insert mode <= How do I calculate 3 from the highest cloze number in the file?
  • ^OP paste the yanked word in insert mode
  • ::reading adds the remaining text.
1

There are 1 best solutions below

5
filbranden On BEST ANSWER

You can use a :%s command with the [/n] flag, which doesn't execute the substitution but only counts the matches.

Use that together with a \= expression on the replacement side, just for the side effects of the expression.

You can use that to append the cloze numbers to a list and then find the maximum after you've collected them all.

function! NextCloze()
    let nums = [0]
    let view = winsaveview()
    silent %s/\m{{c\zs\d\+\ze::/\=add(nums, submatch(0))/egn
    call winrestview(view)
    return 1 + max(nums)
endfunction

The function is also saving and restoring the view around the %s operation, since that operation will move the cursor and we want to keep it in place.

To insert it in the middle of a mapping you can use something like:

i{{c<C-R>=NextCloze()<CR>::

Though there are probably other ways you can put that result in your buffer. For instance, if you'd like a mapping that takes the current word under the cursor and replaces it with the {{cN::...::reading ...}} block, consider these:

nnoremap <Leader>c ciW{{c<C-R>=NextCloze()<CR>::<C-R>"::reading <C-R>"}}<Esc>
xnoremap <Leader>c c{{c<C-R>=NextCloze()<CR>::<C-R>"::reading <C-R>"}}<Esc>

In Normal mode, it will act on the Word (sequence of non-white space symbols) under the cursor. In Visual mode, it will act on the visual selection. These are closer to your original @c macro.