I have found this great tip how to implement strikethrough for the unicode characters in gVim - http://vim.wikia.com/wiki/Create_underlines,_overlines,_and_strikethroughs_using_combining_characters
It is working great for me, but I am stuck in creating "reverse function" final result which I would like to achive would be in pressing e.g. t to striketrough characters and repeating this would remove the striketrough from the characters.
I think I would be able to create such function but I am missing a core thing and that is how to remove the combine character. In mentioned article there is mentioned that you can
:set delcombine
which enables to you deleting by 'x' only the combine character. But that is not something I would like to do :)
So is anyone able to change the following script stated in that article to also add there fucntion to remove the strikethrough from selection?
" modify selected text using combining diacritics
command! -range -nargs=0 Overline call s:CombineSelection(<line1>, <line2>, '0305')
command! -range -nargs=0 Underline call s:CombineSelection(<line1>, <line2>, '0332')
command! -range -nargs=0 DoubleUnderline call s:CombineSelection(<line1>, <line2>, '0333')
command! -range -nargs=0 Strikethrough call s:CombineSelection(<line1>, <line2>, '0336')
function! s:CombineSelection(line1, line2, cp)
execute 'let char = "\u'.a:cp.'"'
execute a:line1.','.a:line2.'s/\%V[^[:cntrl:]]/&'.char.'/ge'
endfunction
I would really appreciate that :)
This function either removes the passed combining character, or adds it to the text.
function! ToggleCombining( character, text )
let l:cleanedText = ''
let l:idx = 0
while l:idx < len(a:text)
let l:codepoint = nr2char(char2nr(a:text[l:idx :]))
if l:codepoint !=# a:character
let l:cleanedText .= l:codepoint
endif
let l:idx += len(l:codepoint)
endwhile
if l:cleanedText !=# a:text
return l:cleanedText
else
return substitute(a:text, '[^[:cntrl:]]', '&' . a:character, 'g')
endif
endfunction
Invoke with
:call ToggleCombining("\u0332", "sample text")
Related
I'm trying to write a vimscript that converts a line like this:
myFuncCall(param1, param2, param3="hey", param4)
To:
myFuncCall(param1,
param2,
param3="hey",
param4
)
While maintaining and adding indentation. So far I have:
function SplitParamLines() abort
let f_line_num = line(".")
let indent_length = indent(f_line_num)
echom indent_length
echom f_line_num
.s/\s*,/,\r/g
nohlsearch
0t)a<cr>
endfunction
How do I indent lines using vimscript?
Why does the last line produces this?:
Error detected while processing function SplitParamLines:
line 7:
E14: Invalid address
So there are 2 options which I chose to use both:
Using this incredible plugin - Splitjoin.vim
Using this simple function:
" Every parameter in its own line
function SplitParamLines() abort
let f_line_num = line(".")
let indent_length = indent(f_line_num)
exe ".s/\s*,/,\r" . repeat(" ", indent_length + &shiftwidth - 1) . "/g"
nohlsearch
" execute "normal! 0t)a\<cr>\<esc>"
endfunction
nnoremap <silent> <leader>( :call SplitParamLines()<cr>
Although not perfect, it works :)
If you want to execute something that you would have typed, you need :normal. If there are special characters, then you'll also need :exe and to escape these special characters. IOW
:exe "normal! 0t)i\<cr>"
How do I pass a visual selection to a function from a script and NOT command line / mapping. I was wondering if something like this was possible? Or if there is a function that gets a range ?
What I want:
:call LowerToUpper('<,'>)
:call LowerToUpper(GetVisualRange())
NOT THIS
:call LowerToUpper(1,5)
command! -range Edit call LowerToUpper(<line1>,<line2>)
:'<,'>LowerToUpper
Here is the function example:
function! LowerToUpper(first,last) abort
for lineno in range(a:first,a:last)
let line = getline(lineno)
let newLine= substitute(line, '\v(\w)','\U\1','g')
call setline(lineno,newLine)
endfor
endfunction
The solution might be a hack to make a function that returns the visual selection GetVisualSelection().
:call LowerToUpper('<,'>)
You're very very close. Use line() to get lines of the marks:
:call LowerToUpper(line("'<"), line("'>"))
You can have your function with range modifier.
function! LowerToUpper() abort range
for lineno in range(a:firstline, a:lastline)
let line = getline(lineno)
let newLine = substitute(line, '\v(\w)','\U\1','g')
call setline(lineno, newLine)
endfor
endfunction
'<,'>call LowerToUpper()
This mostly serves as a shortcut for passing line("'<") and line("'>") implicitly. The argument names a:firstline and a:lastline are fixed.
Say I have a bunch of lines:
#Override
public void draw(Graphics g) {
g.setColor(Color.MAGENTA);
g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
g.setColor(Color.BLACK);
g.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
When I want to comment them out with // (i prefer line comments instead of block comments), what I do is:
Place the cursor infront of the # symbol
Ctrl-V: Switch to enter block-select mode
Select the column down to the } closing parenthesis using multiple hits of j
Shift-I: to enter block-insert
Type //
ESC to excit
Enter to finish the command
--> The lines are now commented out.
Is there an easier way where I don't need to do the block-select? I found I can use a substitution like :'<, '>s/^/\/\///g but this has two problems:
Its very clumsy and error prone to type (multiple forward and backward slashes need to be
escaped)
It places the comment symbols (//) at the beginning of
the line (position 0), not at the position where the first character
of that line was (so indentation is lost).
How can I insert // on the selected lines at the position of the first character of each line using Vi?
You can define a custom mapping or command for your :substitute.
However, there are several commenter plugins that do this very well, and those are generic (and often extensible) so that they work for any filetype:
NERD Commenter plugin
tComment plugin
commentary.vim plugin
I'd highly recommend to use one of those plugins instead of trying to reinvent a poor solution yourself.
I use Commentary as in the other answer, but a few thoughts:
<C-v>jjjjj could be <C-v>} or <C-v>/}<CR>
:substitute doesn’t have to use / as a separator: :'<,'>s-^-//
with a visual selection, you can also do :'<,'>normal! I//
How can I insert // on the selected lines at the position of the first character of each line using Vi?
Although, I'm agree with others and the dedicated plugin is a must have, but, as it is formulated in the OP, that's quite an easy task which can be implemented as one-liner:
vnoremap <silent>gc :call setline(".", printf("%*s" . &cms, indent("."), "", trim(getline("."))))<CR>
Now select some text, press "gc", and, voila, it works. To force // usage instead of the default /**/ set the following option for your buffer: setlocal cms=//\ %s. See :h 'cms'.
" I have a 'toggle comment function' that looks like
" Reference: https://stackoverflow.com/a/24652257/2571881
" these lines are needed for ToggleComment()
" Reference: https://stackoverflow.com/a/24652257/2571881
autocmd FileType c,cpp,java let b:comment_leader = '//'
autocmd FileType arduino let b:comment_leader = '//'
autocmd FileType sh,ruby,python let b:comment_leader = '#'
autocmd FileType zsh let b:comment_leader = '#'
autocmd FileType conf,fstab let b:comment_leader = '#'
autocmd FileType matlab,tex let b:comment_leader = '%'
autocmd FileType vim let b:comment_leader = '"'
function! ToggleComment()
if exists('b:comment_leader')
let l:pos = col('.')
let l:space = ( &ft =~ '\v(c|cpp|java|arduino)' ? '3' : '2' )
if getline('.') =~ '\v(\s*|\t*)' .b:comment_leader
let l:space -= ( getline('.') =~ '\v.*\zs' . b:comment_leader . '(\s+|\t+)#!' ? 1 : 0 )
execute 'silent s,\v^(\s*|\t*)\zs' .b:comment_leader.'[ ]?,,g'
let l:pos -= l:space
else
exec 'normal! 0i' .b:comment_leader .' '
let l:pos += l:space
endif
call cursor(line("."), l:pos)
else
echo 'no comment leader found for filetype'
end
endfunction
nnoremap <Leader>t :call ToggleComment()<CR>
inoremap <Leader>t <C-o>:call ToggleComment()<CR>
xnoremap <Leader>t :'<,'>call ToggleComment()<CR>
" vnoremap <Leader>t :call ToggleComment()<CR>
So, once you have this function on your ~/.vimrc you can do:
vip ...................... visual inner paragraph
<leader>t ................ in order to call the function
Make a macro with q, lets put it into the a buffer, so hit qa on a given line. Then press I// to jump to start of line, and comment it out. hit Esc and q and now your macro is done. This macro will comment out the current line.
The full command is qaI//Escq
Now visually select a bunch of lines with V, and type :norm!#a to run your a macro over those lines. This will comment out a bunch of lines.
Record another macro to do the opposite with qb^xx. This can be invoked by visually selecting the lines you want to uncomment and typing norm!#b
You can save these macros in your .vimrc and map the specific macro to a key combination if you want to "save" these commands.
So I'm trying to write a function that makes inserting semi colons a bit more pleasant:
inoremap <leader>; <esc>:call InsertSemiColin()<cr>
Basically it checks to see if I'm standing at the end of the current line, if so I auto-format the code, insert the semi-colon at the end and break down to the next line (carriage return)
fun! InsertSemiColin()
if (!IsEOL()) | exec "normal! a;" | return | endif
exec "normal! \<esc>:OmniSharpCodeFormat\<cr>A;\<cr>"
endf:
fun! IsEOL()
" col number == length of current line?
return col('.') == strlen(getline(line('.'))) " or just getline('.')
endf
Expectation:
Result:
To try it out on your own, you can remove the code-formatting and just do:
exec "normal! a;\<cr>"
My indentation settings:
set smartindent
set tabstop=4
set shiftwidth=4
filetype plugin indent on
The weird thing is that if I don't insert the carriage return from a function, it works as expected!
inoremap <leader>; ;<cr>
Why is this happening? and how can I get the result I'm expecting?
Very frustrating, any help would be appreciated!
I would avoid leaving and re-entering insert mode for this via :help :map-expr:
inoremap <expr> <leader>; ';' . (IsEOL() ? '<esc>:OmniSharpCodeFormat<cr>A<cr>' : '')
For this to work, you need to change the comparison in the IsEOL() function:
fun! IsEOL()
" col number == length of current line?
return col('.') > strlen(getline(line('.'))) " or just getline('.')
endf
This also fixes the indent problem.
I often use visual block then inserting on multiple lines when for example commenting out a lot of code. This is great for inserting text in the same position on multiple lines but I can't figure out how to delete this text later using visual block mode, Backspace, Del and d all don't work. I am using MacVim.
You're looking for x:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
Then visual-block-select, x:
root:/root:/bin/bash
daeaemon:/usr/sbin:/bin/sh
bin/bin:/bin/sh
sys/dev:/bin/sh
I use this frequently, for exactly the same reason -- commenting and uncommenting large blocks of code.
This isn't directly answering the question (sarnold has already done so), but I would suggest there are more efficient ways of (un-)commenting code blocks. I have a CommentToggle function which either comments or uncomments the current line, depending on whether or not it begins with the "comchar".
function! CommentToggle(comchar)
let firstchar = matchstr(getline("."),"[^ ]")
if firstchar == a:comchar
sil exe 'normal ^xx'
else
sil exe 'normal ^i' . a:comchar . ' '
endif
endfunction
So, for perl files you can map:
nnoremap <silent> <leader>c :call CommentToggle('#')<CR>
and pressing 3 \ c (un-)comments three lines from the cursor position.
You can also write a visual-mode mapping:
vnoremap <silent> <leader>c :call CommentToggle('#')<CR>
allowing you to select a visual region and press \c to (un-)comment them all.
This particular function only works for one-character comments ("#", "%", etc.), but it is straightforward to extend it to longer strings (e.g. "//"), and even more complex replacements, such as HTML comments.
Hope this helps.
Prince Goulash's answer doesn't work in lines with leading tabs.
I changed it, adding the tab character to the pattern, although lines lose their indent after comment and uncomment.
function! CommentToggle( comchar )
let firstchar = matchstr( getline( "." ), "[^ \t]" )
if firstchar == a:comchar
sil exe 'normal ^2x'
else
sil exe 'normal ^i' . a:comchar . ' '
endif
endfunction
I like more adding the comment char to first position in line, this modification to Prince Goulash's function does the trick:
function! CommentToggle( comchar )
let firstchar = matchstr( getline( "." ), "[^ \t]" )
if firstchar == a:comchar
sil exe 'normal ^2x'
else
sil exe 'normal gI' . a:comchar . ' '
endif
endfunction