0

I have a function that accepts a count of iterations, its command is defined with -range:

command -nargs=1 -range DocbkPrintTag :call s:DocbkPrintTag(<f-args>)

and to output

<listitem>
<para>
</para>
</listitem>

I map it followingly:

nnoremap <unique> <localleader>li :<C-U>DocbkPrintTag li<CR>

the <listitem> is defined in a custom dictionary as

let s:tags.li = { 'tag': 'listitem', 'type': 'outline', 'include': ['pa'] }

while <para> ['pa'] is defined as

let s:tags.pa = { 'tag': 'para', 'type': 'outline' }

The DocbkPrintTag function calls another function DocbkRenderTag which assembles the XML output for a tag, possibly rendering it n-times if count is specified on a keyboard before the mapping itself. The problem is that for the case of , I call DocbkRenderTag recursively from itself to first render <para/> as an inside element of <listitem/>. Everyhing works fine for 1 repetition, but if i want to print 2 or more such structures, the v:count1 parameter is passed over to the inner function calling but the inner tag should be repeated only once. How can i force to do the inner function call only once?

For completeness, here is the important part of the code:

if !exists(":DocbkPrintTag")
  command -nargs=1 -range DocbkPrintTag :call s:DocbkPrintTag(<f-args>)
endif

function s:DocbkPrintTag(tag)
  let result = s:DocbkRenderTag(a:tag)
  if !empty(result)
    call feedkeys('a' . result, 'n')
  endif
endfunction

function s:DocbkRenderTag(tag)
  [...]
  let result = ''
  " number of repetitions
  let i = 1
  while i <= v:count1 
    [...]
    if !empty(include)
      for incl_tag in include
        let incl_result .= s:DocbkRenderTag(incl_tag)
      endfor
    endif
    [...]
  endwhile
  return result
endfunction

The output now is for example

<listitem><para></para><para></para></listitem>
<listitem><para></para><para></para></listitem>

instead of just

<listitem><para></para></listitem>
<listitem><para></para></listitem>
  • 2
    I think your question would be easier to understand if you gave us a bit more code to understand what you are doing: You could create a minimal example to reproduce your behavior (we don't need the whole tag rendering for example) which would help people to help you. – statox Jul 1 at 12:03
  • Still missing code related to v:count1. You should also describe how you invoke your command. – dedowsdi Jul 2 at 8:08
1

There are two <para> because you apply v:count1 to every tag you render. Try this:

function s:DocbkPrintTag(tag)

  " apply v:count1 to the outside tag
  put =repeat(s:DocbkRenderTag(a:tag), v:count1)
endfunction

function s:DocbkRenderTag(tag)
  ...

  " render single tag here
  if !empty(include)
    for incl_tag in include
      let incl_result .= s:DocbkRenderTag(incl_tag)
    endfor
  endif
  ...
endfunction

IMO, raw command with :h :command-completion-customlist is way better than map here. v:count1 can be replaced with :h optional-function-argument .

  • Thank you for your hint, but: i'm writing a lot of DocBook text, and i need a fast access to render complex DocBook structures. for example, after pressing ,7il i expect to render whole <itemizedlist> with 7 <listitem><para></para></listitem> inside. i dont want to call a function, that's too much keypresses for me – Tomáš Bažant Jul 2 at 11:25
  • You can create a map like this nnoremap ,d :DocbkPrintTag<space>, then you only need to type ,dlistitem 7, the listitem can be completed with <tab>. Did my answer solve the v:count1 > 1 problem? It feels like you didn't think it did. – dedowsdi Jul 2 at 11:37
  • Your proposed way with repeat() works perfectly! Thanks! – Tomáš Bažant Jul 2 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.