In Mac OS, I am trying to write a function that will say the current paragraph without moving the cursor, but something doesn't seem to work. This is what I have so far:

function! SayIt()
    let l:line = line(".")
    let l:col  = col(".")
    execute "normal V"
    execute "silent '<,'>w !say"
   call cursor(l:line,l:col)

vnoremap <leader>v :call SayIt()<cr>

If I remove the execute "normal V" line the function works when I have previously selected a paragraph (or rather a long soft wrapped line). What am I doing wrong?

  • 1
    What do you mean when you say the function doesn't work? What happens? Four things that occur to me: 1) you can only use the map from visual mode, because you create no mappings for other modes, 2) calling the function from visual mode, you may want to do execute "normal V:!say", or else escape visual mode before the second execute, because in visual mode, '< moves to beginning of the last visual selection, not the current, 3) normal V will select a line, not a paragraph–consider vip, and 4) the irony of "silent ... !say" ;)
    – jjaderberg
    Feb 22, 2016 at 20:25
  • 2
    I know it doesn't answer your question, but I think Vim automatically puts the marks '{ and '} around the current paragraph, so if you define the mapping nnoremap <silent> <leader>v :<C-U>silent! '{,'}w !say<CR> and hit <leader>v in normal mode, the contents of the current paragraph should be written on the standard input of the say program.
    – saginaw
    Feb 22, 2016 at 20:26
  • jjaderberg, you are right, one of the issues was the mapping. Yes, what an irony silent ... !say. saginaw, thank you very much. This solution worked great without the need of a function.
    – Amaru
    Feb 22, 2016 at 20:53

2 Answers 2


Your selection is being made with the V command, so your '<,'> marks are just set to that line.

Try to select the paragraph with the ip motion after going into visual mode, then the range will be what you want.

The executes are unnecessary. Also that cursor stuff isn't the way the pros do it. :)

function! SayIt()
    let l:winview = winsaveview()
    normal Vip
    silent '<,'>w !say
    call winrestview(l:winview)
  • Thank you. I keep learning great stuff here. This function works, but with some unexpected results. It seems to select, randomly the previous or the current paragraph.
    – Amaru
    Feb 22, 2016 at 20:49
  • 1
    Paragraphs are demarcated by blank lines, so if you have spaces on lines in between paragraphs they'll be treated as if there isn't a paragraph break. Otherwise I'm not familiar with what you're reporting, ip should select the containing paragraph if you're in one and the next paragraph if you aren't in one. Feb 22, 2016 at 20:51

Thank you for your help. In terms of functions, I think that the one posted by dash-tom-bang is the closest solution. But I have opted for the mapping suggested by saginaw.

nnoremap <silent> <leader>v :<C-U>silent! '{,'}w !say<CR>

It works without the function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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