How to remove space at the end of a cab abbreviation and wait for input in GVim? - vim

I have created a Vim abbreviation for the start of a file path, i.e.
cab \x\ M:\xmlexport\Output\
and I want to have this expand, remove the space and wait for further input. I have tried using =Eatchar('s') but don't know what to put before it to make it trigger correctly. All the examples I have seen use <C-R> which adds in a new line. I have tried <left> just to move back one space but this creates M:\xmlexport\Output \

Perhaps I have misunderstood the question, but I've tested the function you pointed out and seems to work:
I've added the Eatchar function to .vimrc:
func Eatchar(pat)
let c = nr2char(getchar(0))
return (c =~ a:pat) ? '' : c
endfunc
And inside vim I've created your abbreviation, like:
:cab \x\ M:\xmlexport\Output\ <left><C-R>=Eatchar('\s')<CR>
Then I write :\x\ in command-line and it expands to :M:\xmlexport\Output\, and the cursor position is just after the last backslash to write your desired input.

Related

In vim toggle case of all characters in a text file with a single command

In Vim I need to convert all lowercase to uppercase and all uppercase to lowercase with a single command. So if my text file looks like this..
Hello World
.. it needs to be toggled to look like this..
hELLO wORLD
I know :%s/[a-z]/\U&/g will change all lowercase to uppercase and that :%s/[A-Z]/\L&/g will change all uppercase to lowercase. But how would I write that to do both at the same time?
In addition I know if my cursor is at the top of the file VG~ will toggle case everything but that's not the answer I need. Thank you.
<Esc>1GVG~
Explanation:
<Esc> — return to Normal mode; just in case we're in Insert mode or Command line
1G — jump to the 1st line
V — start Visual mode
G — jump to the last line extending selection
~ — toggle case in the selection
Or
<Esc>1Gg~G
g~<motion> — change case during motion; the motion is G (jump to last line)
Docs: http://vimdoc.sourceforge.net/htmldoc/change.html#~
Looks like you already know everything you need. ggVG~ marks all your code and toggles the case. If you want a single command you can either use:
:nnoremap <keybinding> ggVG~
or use this function, which does the same, but keeps your current position in the file:
function ToggleCase()
exec "normal! mqHmw"
exec "normal! ggVG~"
exec "normal! 'wzt`q"
endfunction
command ToggleCase silent call ToggleCase()
the first and last exec mark your position in the file and restore them, after the case toggling. See: :h marks
type :ToggleCase to use the function. Of cause you can bind this to a keybinding as well.
:nnoremap <keybinding> :ToggleCase<cr>
Since you mentioned using a single command and you mentioned some :%s/.../ substitutions, I'll offer this one:
:%normal! g~~
This will run the g~~ command to switch case of a single line, for each line of the buffer.
One more way to accomplish this, if you're ok adopting a plug-in, is to use the kana/vim-textobj-entire plug-in for a text object for the entire buffer.
As the plug-in README.md file says:
Though these are trivial operations (e.g. ggVG), text object versions are more handy, because you do not have to be conscious of the cursor position (e.g. vae).
With this plug-in installed and enabled, you can switch case of the whole buffer with:
g~ae

Using abbreviation to insert comment

I’m trying to set up an abbreviation in my .vimrc that will insert a comment template for heading-level comments in my CSS files.
The comment I want to insert is:
/* ==========================================================================
#
========================================================================== */
I will then jump back to the # and add my title there (e.g. BUTTONS).
The abbreviation I have attempted to set up looks like this:
iab comsec·
\/* ==========================================================================
\<Cr>#
\<Cr>========================================================================== */
(Where · represents a trailing space.)
Right away this feels pretty crude, but the specific problem is that if try and drop a comsec in my CSS, it starts wrapping it in more comments. The output looks like this:
/* ==========================================================================
* #
* ========================================================================== */
Notice the two * at the beginnings of lines 2 and 3?
Is there a way to tell vim not to try and be clever and to just drop in exactly what I’ve told it? A way to prevent vim from trying to wrap comments around the comment?
I’m not a particularly hardcore vim user, so there’s every chance I’m overcomplicating things, or missing something obvious, or using the wrong tool for the job.
Thanks!
If you are the type of person who can keep track of your personal utilities, this isn't so fancy but works. You can import the output of an external command into your buffer, so I put a mapping like this in my .vimrc file:
"bc = block comment
map ,bc :read! python ~/my_personal_utils/insert_css_comment.py
So, I just have to type ",bc" to add the comment. On my Mac at least, this leaves me hanging in command mode, so that my cursor is after '.py' and I can quickly type an argument like BUTTONS (i.e. the python script takes an optional argument).
Here is a function to do that.
:function! Comment()
:normal! i/*
:normal! 80a=
:normal! o#
:normal! o
:normal! 80i=
:normal! a*/
:endfunction
You can put this in vimrc file and create a command or map for this.
Command
:cmap comsec call Comment()
You can keep the cursor on a line and then call this command.
Or an in insert mode mapping
:imap comsec <ESC>:comsec<CR>
As alternatives I'd suggest nerdcommenter for commenting/uncommenting with a few key strokes.
Or, even better, ultisnips. In which you can easily make your own template for those headings:
open a .css file
exec command : UltiSnipsEdit
create your own snip:
snippet heading "heading comments"
/* ===================================
* ${1}
* =================================== */
endsnippet
Here is a better and simple way to to insert your comment which check everytime if the line is surrounded by the comment template.
All you have to do is to write your comment on new line and then press ; during the insert mode. (you can change the character ; by
any combination you want.)
The comment template is set by the variable l:start, l:begin, l:end
so you can change the number of spaces or = as you like.
If you would like to change the template completely keep in mind that you need to change also the regular expressions for the variables l:matchprev, l:matchhier, l:matchnext .
inoremap <silent> ; <Esc>mx:call Comment()<cr>i<Right>
function! Comment()
let l:start= "/* ====="
let l:begin=" # "
let l:end= " ==== */"
let l:next=getline(line(".")+1)
let l:hier=getline(line("."))
let l:prev=getline(line(".")-1)
let l:matchnext= matchstr( l:next , '^\s*=\+\s*\*/\s*$')
let l:matchhier= matchstr( l:hier , '^\s*#\s*.*$')
let l:matchprev= matchstr( l:prev , '^\s*/\*\s*=\+\s*$')
if l:matchnext != '' && l:matchprev != '' && l:matchhier != ''
return 0
else
execute ":s:^.*$:".l:start."\r".l:begin."&\r".l:end."\r:"
"the number 3 is the length of the variable l:begin
normal! `xj3l
endif
endfunction
write this code in another file scriptname and then you can use the mapping in any css file by typing in the command mode first :so scriptname
Another alternative is to put all that simply in your .vimrc file

Custom vi command: How to insert current date with a variable's content?

I'm new to the vi editor and I would like to create a simple custom command in .vimrc that inserts something like 2012-03-13 22:21:17.0 +0100 / Daniel.
Actually, my command (in .vimrc) is as follows:
command! InsertTime :normal a<C-R>=strftime('%F %H:%M:%S.0 %z')<CR>
I also set a variable:
let myname="Daniel"
InsertTime inserts the date perfectly. But how can I concatenate it with the content of my variable?
To concatenate, vim scripts use . caracter. So try this one :
In vimrc:
let myname="Daniel"
command! InsertTime :normal a<C-R>=strftime('%F %H:%M:%S.0 %z') . "/" . myname<CR>
no tested there.
Since you said you're new to "vim" I am going to assume you don't know any of the things I'm about tell you. Mucho sorry if you already know them.
If you're going to do this a lot (insert the line "%F %H:%M:%S.0 %z / Daniel"), instead of defining a command, which you have to invoke with a :command_name, define a macro and/or an input macro that can be invoked with just two or three character.
To define an input macro, do the following at the ':' prompt, or add it to your $HOME/.exrc or $HOME/.vimrc file (without the preceding ':'):
:map <C-X><C-X> Go<ESC>!!date '+\%F \%H:\%M:\%S.0 \%z'<CR>A / Daniel<ESC>
Now when you're in "vi" (but not in input mode), typing control-Xcontrol-X will:
G go to last line in file; replace this with the "motion" keys sequence appropriate for your use (or nothing at all if you want to append the line right after the cursor)
o open a new line
<ESC> escape out of input mode
!!date ... invoke the date command, replace the current line with its stdout (output)
A append at the end of the line (now having the "date")
/ Dan... verbatim intput text
<ESC> escape out of input mode
control-Xcontrol-X can be some unusual sequence that you'd normally not use for anything, nor used by any "vi" operation that you might use. I use as the first character, because in "vi", decrements the next integer on the line after the cursor, if any. That is something I hardly ever do. I define my macros to be invoked with <C-X><C-B>, <C-X><C-D>, <C-X>s1, etc.
To create an input macro, well, that's another whole long subject, and I'm tired of typing today, so, another day. :)

How to diff two lines in an open file in vim?

I occasionally see very long lines in my code that I need to check if they are the same. Is there a way in vim to select two lines and diff them to show any differences between the two?
For example, given the two lines in vim:
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *lists);
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *1ists);
I would like vim to tell me that the two lines are in fact different because each spells "lists" differently. Is this possible, and if so, how do I do it?
A quick and dirty solution is to just select both lines and sort them while removing duplicates:
select lines
":sort u"
if only one line remains, both were equal
if both remain, there most be some difference
An undo recovers everything again.
An alternative to #sehe's approach would not require the use of temp files:
funct! DiffTwoTexts(text1, text2)
new
put =a:text1
normal ggdd
diffthis
new
put =a:text2
normal ggdd
diffthis
endfunct
funct! DiffTwoLines(line1, line2)
let text1 = getline(a:line1)
let text2 = getline(a:line2)
call DiffTwoTexts(text1, text2)
endfunct
comma! DiffWithNext call DiffTwoLines('.', line('.') + 1)
This will still be pretty hard to read, since it keeps everything on a single line, so I came up with this modification:
funct! EvalTextPreprocessor(expr, text)
let text = a:text
return eval(a:expr)
endfunct
comma! -nargs=1 DiffWithNextPre call DiffTwoTexts(
\ EvalTextPreprocessor(<q-args>, getline('.')),
\ EvalTextPreprocessor(<q-args>, getline(line('.') + 1)))
This new command takes a vimscript expression as its argument, wherein the variable text refers to whichever line is being preprocessed. So you can call, e.g.
DiffWithNextPre split(text, '[(,)]\zs')
For your sample data, this gives the two buffers
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
Overloaded *with,
Multiple *different,
Parameter *lists)
;
and
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
Overloaded *with,
Multiple *different,
Parameter *1ists)
;
Only the lines that start with Parameter are highlighted.
You can even build up from there, creating a command
comma! DiffTwoCFunctionSigs DiffWithNextPre split(text, '[(,)]\s*\zs')
Notice that I modified the regexp a bit so that it will keep trailing spaces at the end of lines. You could get it to ignore them entirely by moving the \s* to after the \zs. See :help /\zs if you're unfamiliar with what that vim-specific RE atom does.
A nicety would be to make the command take a range (see :help command-range), which you could use by diffing the first line of the range with the last line. So then you just visual-select from the first line to the second and call the command.
I used linediff.vim.
This plugin provides a simple command, ":Linediff", which is used to diff two separate blocks of text.
That is not a feature, however it is easily scripted, e.g. in your vimrc:
function! DiffLineWithNext()
let f1=tempname()
let f2=tempname()
exec ".write " . f1
exec ".+1write " . f2
exec "tabedit " . f1
exec "vert diffsplit " . f2
endfunction
This will open the current and next lines in vertical split in another tab.
Note that this code is a sample
it doesn't check whether next line exists (there are any following lines)
it doesn't cleanup the tempfiles created
a nice improvement would be to take a range, or use the '' mark to select the other line
You can leave off the 'vert' in order to have a horizontal split
Map it to something fancy so you don't have to :call it manually:
:nnoremap <F10> :call DiffLineWithNext()^M
you could also just create a new empty window buffer and copy line, then make command:
:windo diffthis
this should open a new window showing the differences of those 2 lines

How to delete line(s) below current line in vim?

Is there a command to delete a line (or several lines) that is immediately below current line?
Currently I'm doing it as:
jdd and then . to repeat as needed.
Is there a command that would combine all these?
UPDATE: The reason I would like to have such command is that I don't like to move away from current position, yet be able to delete lines below.
The delete ex command will work nicely.
:+,$d
This will delete all the lines from current +1 till the end ($)
To delete the next 2 lines the follow range would work, +1,+2 or shorthand +,+2
:+,+2d
As #ib mentioned the :delete or :d command will move the cursor to the start of the line next to the deleted text. (Even with nostartofline set). To overcome this we can issue the `` normal mode command. `` will jump back to the exact position before the last jump, in this case the :d command. Our command is now
:+,+2denter``
Or as one ex command
:+,+2d|norm! ``
To make this easier we wrap this all up in a command:
command! -count=1 -register D :+,+<count>d <reg><bar>norm! ``
Now to delete the next following 3 lines:
:3D
This command can also take a {reg} like :delete and :yank do. So deleting the next 4 lines into register a would be:
:4D a
For more information
:h :d
:h :command
:h :command-register
:h :command-count
:h ``
dG should work.
This means delete all rows until end of file from current cursor.
This will delete ALL lines below the current one:
jdG
Unfortunately that will move the cursor to the beginning of current line after the deletion is made.
well, to do it simply you could use the xxdd command. Most of the time I know (at least have an idea) the size of the script I am editing. So, the command as below is usually more than enough :
99dd
999dd to remove 999lines starting at the cursor position.
9999dd
99999dd for very long script ;)
The other solutions are informative, but I feel it'd be simpler to use a macro for this:
qq (begins recording)
jddk (go down, delete the line, and go back up - i.e. the thing you want to do)
q (end recording)
Now you can do #q to perform this action, maintaining the cursor at the current position. You could also do something like 5#q to delete 5 lines below the cursor.
And finally, if you're repeating the action more than once, you could just type ## after the first time you run #q (this repeats the last used macro - in this case q)
This is a job for marks!
Try maj20dd`a
ma sets the file-specific mark 'a', j20dd does the deletion you want (20 lines in this case), and `a restores you to the mark's position (line and column).
Obviously this pattern can be extended to do anything you want before returning to the mark. If you use mA (or any other capital letter) the mark will actually be unique across files, so you can even edit elsewhere before returning. If you have a very frequent usage you could make it a macro as suggested above.
You could enter the number of lines to delete: j 20 dd k.
Just for the fun of it, you can define a little function that does
exactly what you described: deletes the next n lines below the
current line and restores the initial cursor position.
function! DeleteNextLines(n, reg)
let l = line('.')
let m = min([a:n, line('$')-l])
if m > 0
let c = col('.')
exe '+,+'.m 'd' a:reg
call cursor(l, c)
endif
endfunction
Also, you can define a command that accepts the number of lines
to delete (one, if omitted) and the register name to use as an
optional argument (just like the :delete command).
:command! -range=1 -register -bar D call DeleteNextLines(<count>, <q-reg>)
Additionally, you can define a mapping for triggering the above
:D command, if it is necessary.

Resources