vim-markdown
vim-markdown copied to clipboard
Include text object for fenced code block?
Hi,
I have written text objects for fenced code blocks.
E.g. cif
replaces the code inside, daf
deletes a code block with fence markers, or vif
visually selects code inside so it can be pasted (middle mouse click) to an interpreter.
Do you consider this a good feature? Would you accept an pull request?
Thanks, Jakob
Very interesting, I concur too! Why not extend the text object to any code block (default markdown indented ones too)? Would love to use that in combination with a plugin like Slimux!
I was looking for that! @schoettl can you share what you've done?
Here is my attempt at creating text objects for fenced code blocks. It is severly untested so YMMV (:
function! s:inCodeFence()
" Search backwards for the opening of the code fence.
call search('^```.*$', 'bceW')
" Move one line down
normal! j
" Move to the begining of the line at start selecting
normal! 0v
" Search forward for the closing of the code fence.
call search("```", 'ceW')
normal! kg_
endfunction
function! s:aroundCodeFence()
" Search backwards for the opening of the code fence.
call search('^```.*$', 'bcW')
normal! v$
" Search forward for the closing of the code fence.
call search('```', 'eW')
endfunction
autocmd Filetype markdown xnoremap <silent> if :<c-u>call <sid>inCodeFence()<cr>
autocmd Filetype markdown onoremap <silent> if :<c-u>call <sid>inCodeFence()<cr>
autocmd Filetype markdown xnoremap <silent> af :<c-u>call <sid>aroundCodeFence()<cr>
autocmd Filetype markdown onoremap <silent> af :<c-u>call <sid>aroundCodeFence()<cr>
Sorry for the late answer, I'll also share my solution: Put a file markdown.vim
into ~/.vim/ftplugin/
with this content:
function! s:SelectFencedCodeA()
execute "normal! $?^````*$\<CR>V/^````*$\<CR>o"
endfunction
function! s:SelectFencedCodeI()
call <SID>SelectFencedCodeA()
normal! joko
endfunction
nmap <buffer> va` :call <SID>SelectFencedCodeA()<CR>
nmap <buffer> vi` :call <SID>SelectFencedCodeI()<CR>
onoremap <buffer><silent> a` :call <SID>SelectFencedCodeA()<CR>
onoremap <buffer><silent> i` :call <SID>SelectFencedCodeI()<CR>
" Good default for Markdown:
set textwidth=80
@pyrho I'm not sure but I think, your solution registers the key mapping in every buffer. That's why I use <buffer>
.
PS: My current solution only works for "plain" fenced code without a language. You will have to change the first backward search regex (after ?
) to also match a language name.
@schoettl Thanks for sharing.
Indeed Creating the mappings via ftplugin/markdown.vim
is better than my autocmd
on FileType
(after/ftplugin/
maybe even better suited for user-defined mappings).
your solution registers the key mapping in every buffer.
I think you are right, <buffer>
is better (vimways confirms this ^^ I didn't know about <buffer>
)
@pyrho @schoettl IIRIC, markdown code blocks can have indented spaces before them. So the code block does not necessarily need to begin with backticks. It may well begin with white spaces. You may add \s*
before your pattern.
@schoettl I think we should use three backticks instead of four. Also setting code blocks text objects to i`
and a`
will shadow the text objects for inline markdown code. Maybe it should be better avoided.
This is my attempt to provide a text object for code blocks:
vnoremap <silent> ic :<C-U>call <SID>MdCodeBlockTextObj('i')<CR>
onoremap <silent> ic :<C-U>call <SID>MdCodeBlockTextObj('i')<CR>
vnoremap <silent> ac :<C-U>call <SID>MdCodeBlockTextObj('a')<CR>
onoremap <silent> ac :<C-U>call <SID>MdCodeBlockTextObj('a')<CR>
function! s:MdCodeBlockTextObj(type) abort
" the parameter type specify whether it is inner text objects or arround
" text objects.
let start_row = searchpos('\s*```', 'bn')[0]
let end_row = searchpos('\s*```', 'n')[0]
let buf_num = bufnr()
if a:type ==# 'i'
let start_row += 1
let end_row -= 1
endif
" echo a:type start_row end_row
call setpos("'<", [buf_num, start_row, 1, 0])
call setpos("'>", [buf_num, end_row, 1, 0])
execute 'normal! `<V`>'
endfunction
Hi @jdhao
@schoettl I think we should use three backticks instead of four.
My code allows three or more backticks. Actually, it's even more special: the fence must not be indented more than three spaces ^^
Also setting code blocks text objects to
i`
anda`
will shadow the text objects for inline markdown code. Maybe it should be better avoided.
Do you know which plugin is it, that already provides the inline code text objects? Or is it built-in in markdown.vim?
@schoettl The i`
and a`
objects are builtin at least in Neovim, as can be verified by using nvim -u NORC test.md
and use these two objects. There is also help tag for them: see :h i`
and :h a`
.