1

I want to create a binding for using vim-slime to run code between a code fence in markdown.

This worked with a simple fence, using i` as a text-object:

nmap <leader>s7 <Plug>SlimeMotionSendi`

but when adding the language to the code fence, e.g.:

```python
print("hello")
```

it obviously broke. So now I'm looking to define a text-object the would mean all the lines between the two triple backticks. How do I go about this?

5
  • What did you try?
    – romainl
    Jan 10 at 12:49
  • 1
  • Do you have something left open in your question? How can we help you further? Jan 11 at 15:31
  • 1
    @VivianDeSmedt sorry, I got extremely caught up in work and I wanted to spend a little time to understand what's going on in your answer and didn't manage to get there yet ...
    – fbence
    Jan 12 at 13:11
  • 1
    No problem, I understand that :-). It was just to encourage to give feedback in case the solution weren't working for you. Take your time and feel free to comeback to us when possible for you. Have a nice weekend. Jan 12 at 13:59

1 Answer 1

1

I would define the iq text object:

vnoremap iq ?```.*$?1<CR>o/```/-1<CR>$
omap iq :normal Viq<CR>

Where the v mapping does:

  • Search backward the triple quotes and move to the next line: ?```.*$<CR>?1
  • Select the other side of the selection: o
  • Search forward the triple quotes and move to the previous line: /```/-1<CR>
  • Go to the end of the line: $

And the do the following mapping:

nmap <leader>s7 <Plug>SlimeMotionSendiq

Here is another version that leave the / register unchanged:

function! VisualCodeBlock()
  call search('```', 'b')
  normal! j0v
  call search('```')
  normal! k$
endfunction

vnoremap iq :<C-u>call VisualCodeBlock()<CR>
omap iq :normal Viq<CR>

Remarks:

In a standard omap the start of the text object is the cursor position and end of the text object is the position of the cursor after the mapping has been executed.

If you need a text object that doesn't start with the cursor position you need to switch in visual mode and make the selection covering the range you want.

Usually in such case two mappings are defined such that the text object can also be used in visual mode.

Remark <C-u>:

The <C-u> trick is usually used when a mapping use the Command mode to kill the existing character in the command. If the user select some line before triggering the mapping a range will present when the mapping triggers :

5
  • 1
    Thanks, I finally got around to this. There are a couple of things I don't understand: a) is the only way to define on omap is to basically create a visual selection and say that this is the object? b) in the vnoremap why is there a <C-u>? I tested without and it worked fine. :h didn't help unfortunately. c) Why not just go omap iq :normal VisualCodeBlock()<CR>?
    – fbence
    Jan 20 at 10:00
  • 1
    Thanks for the feedback. I'll extend the answer to covers these aspects :-) Jan 20 at 10:27
  • 2
    <C-u> is necessary to prevent undesirable range injection when the user accidentally presses a number key before pressing the mapped keys. :help <Cmd> is a better and more modern alternative. Also, operator-pending mode mappings only work with operators. v, V, and <C-v> are not operators so they get their own mapping. The best practice is to a) define a visual mode mapping that covers the desired area, and then to b) define an operator-pending mode mapping that uses the visual mode mapping.
    – romainl
    Jan 20 at 11:26
  • 1
    So the more modern approach would be vnoremap iq <Cmd>call VisualCodeBlock()<CR>?
    – fbence
    Jan 20 at 20:21
  • Indeed :-) I believe I have tried that but it was not working. Maybe I should try once more :-) Jan 20 at 21:02

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.