How do I prevent Vim from expanding ‘~’ during completion? - vim

I'm on my MacBook using Vim, and let's say for simplicity's sake that I have files ~/some_file.py, ~/some_other_file.py, and ~/user.py open. On macOS, ~ expands to /Users/<username>.
So if I type :b user in Vim command line and then hit tab to expand, it goes through each of the files instead of going straight to ~/user.py.
Is there any way to prevent this behavior?

I can't reproduce your problem under linux (tildes are not resolved in my vim's completion list, so :b home gives me ~/home.py before ~/some_file.py), but...
Try typing :b user then complete with Shift+Tab. In that case, my vim (7.2.442 if that matters) completes with the last match, which is what you want.

It is not possible to change Vim built-in buffer completion. The only
thing I can suggest (besides opening these files already from the home
directory) is to define your own version of the :b command with
the desired completion. It could be something like this:
function! CustomBufferComplete(a, l, p)
let buf_out = ''
redir => buf_out
silent buffers
redir END
let buf_list = map(split(buf_out, "\n"), 'substitute(v:val, ' .
\ '''^.*"\%(\~[/\\]\)\?\([^"]\+\)".*$'', "\\1", "g")')
return join(buf_list, "\n")
endfunction
command! -nargs=1 -complete=custom,CustomBufferComplete B b <args>
(Note that it cuts off the ~/ part of a path before returning
the completion list.)

Related

VIM check for search pattern match in vim script

I just started with vim script and try make translation of opencart language files easier. I want to have a function that looks for a given search pattern and selects it. If there is no match left in the file, it shall open the next file for editing. What I have so far:
function! Nextmatch()
normal /\(= *'\)\#<=.\{-\}\('\)\#=/
normal v//e
endfunction
function! Nextfile()
if !exists("s:filecounter")
execute "!find -iname *.php > files.txt"
normal <CR>
let s:filecounter=0
endif
let s:lines= system("wc -l < files.txt")
if s:filecounter<s:lines
w
let s:filecounter += 1
let s:sedcommand="sed '".s:filecounter."!d' files.txt"
let s:selectedfile=system(s:sedcommand)
execute 'edit' s:selectedfile
else
wq
endif
endfunction
How can I achieve that Nextfile() is called in Nextmatch() if the search pattern is not found between the cursor and the end of the current file? And is there something that you consider to be bad style in my snippet?
Quickfix commands are powerful and well integrated with some external plugins, but if you really need to use your own script, and if you need to check a match in an if statement, just do:
if search("=\\s*'\\zs[^']*\\ze", 'W') == 0
echo 'No match until the end of the buffer'
endif
See :h search(), and please note :
the double backslashes, due to the double quotes
the 'W' flag which forbids wrapping around the end of file
I simplified the pattern you gave
You could simply use the :vim command to get rid of all your script.
I think the following should do quite what you're expecting:
:noremap <f8> <esc>:cn<cr>gn
/\(= *'\)\#<=.\{-\}\('\)\#=
:vim //g *.php
Then, to go to the next pattern in all files while selecting it,
you just have to press the F8 key.
In the noremap line, gn let you select the next actual search.
You may need to do:
:set nohidden
to let you navigate threw modified buffers (but don't forget to save
them with :wa, or list them with :ls)
About your script:
It's a good habit in scripts to always use :normal! instead of :normal (unless you deliberately need it) : thus, your personnal mappings won't interfer in your scripts.

Vim print buffer names to stdout on :q

Let me jump right in.
What I'm trying to do is simply print out the file path of any open buffer when I exit vim. This is useful because I often open other files in vim buffers with a vim script I wrote that can search through my codebase for a specific function call.
I figure I can set up an autocommand, either for when I open a file or when I leave vim, and use the output from :ls to list all currently open buffers. The problem that I'm having is that I can't get any output to show up in terminal. I have tried various combinations of :!echo in my function, but to no avail. I have been trying something like the following in my .vimrc
function! PrintFileName()
:!echo "hello"
:exec "!echo world"
"... etc
endfunction
au BufRead * call PrintFileName()
Both :!echo foobar and :call PrintFileName() work for me if I do it from the command line. I also figure I might have to use some form of silent/redraw! so I don't have to hit enter to continue.
Really the main problem here is that I can't see a way to get output to stdout from inside my function and called by an autocommand.
Thanks for any help.
Okay, so I've found this solution, which works as long as I enter vim from the last line of my terminal. Otherwise this prints out a line below the current line and will get overwritten when you press enter. If anyone knows how to fix that let me know, otherwise I will use this.
function! PrintBuffers()
redir => files
:ls
redir END
" Regex to strip out everything from :ls but the buffer filenames
let files = substitute(files, '^[^"]*"', '', 'g')
let files = substitute(files, '"[^"]*\n[^"]*"', '\n', 'g')
let files = substitute(files, '"[^"]*$','','g')
" This is the magic line
exe '!echo; echo ' . shellescape(&t_te . files)
endfunction
au VimLeave * call PrintBuffers()
*Note - As I'm writing this, I realize that this won't display the right path if you did a :cd at some point. So I guess its pretty fragile, but it does the job.

How can I execute the current line as Vim EX commands?

Say I'm editing my _vimrc file and I've just added a couple of lines, for instance a new key mapping. I don't want to reload the whole file (:so %) since that will reset a lot of temporary stuff I'm experimenting with. I just want to run the two lines that I'm currently working on.
I'm having no luck trying to copy/paste the lines into the command buffer, since I can't use the put command in there. Is there any way I could run the current line (or current selection) as EX commands?
Summary:
After Anton Kovalenko's answer and Peter Rincker's comment I ended up with these key maps, which either executes the current line, or the current selected lines if in visual mode:
" Execute current line or current selection as Vim EX commands.
nnoremap <F2> :exe getline(".")<CR>
vnoremap <F2> :<C-w>exe join(getline("'<","'>"),'<Bar>')<CR>
To execute the current line as an ex command, you may also use:
yy:#"
This will yank the current line to the "-register and execute it. I don't think it is too much typing.
Executing the line under cursor as an Ex command:
:execute getline(".")
Convenient enough for 2 lines. (I'd figure out something for doing it with regions, but I'm not a vim user). And for currently selected region, the following seems to do the job:
:execute getreg("*")
As commented by Peter Rincker, this mapping can be used for executing the currently selected lines:
:vnoremap <f2> :<c-u>exe join(getline("'<","'>"),'<bar>')<cr>
For that purpose, I have defined the following commands and mappings:
":[range]Execute Execute text lines as ex commands.
" Handles |line-continuation|.
" The same can be achieved via "zyy#z (or yy#" through the unnamed register);
" but there, the ex command must be preceded by a colon (i.e. :ex)
command! -bar -range Execute silent <line1>,<line2>yank z | let #z = substitute(#z, '\n\s*\\', '', 'g') | #z
" [count]<Leader>e Execute current [count] line(s) as ex commands, then
" {Visual}<Leader>e jump to the following line (to allow speedy sequential
" execution of multiple lines).
nnoremap <silent> <Leader>e :Execute<Bar>execute 'normal! ' . v:count1 . 'j'<CR>
xnoremap <silent> <Leader>e :Execute<Bar>execute 'normal! ' . v:count1 . 'j'<CR>
Just after posting this, I found a work-around. I can copy text into the clipboard using "*y, then put that text into the command buffer by using the middle mouse button. This works for me, but is hardly a convenient solution for people without clipboard support, mouse support or just an aversion to removing their hands from the Vim position.
The accepted answer doesn't handle continuation sections. Also, surprisingly, the bar isn't needed, newlines are fine. This will work, first yanking the text into register x:
vno <c-x> "xy:exe substitute(#x,"\n\\",'','g')<cr>
As someone has already mentioned, the only exception are commands that "eat up" newlines. Eg, executing the above mapping on:
:sign define piet text=>> texthl=Search
:exe ":sign place 2 line=23 name=piet file=" . expand("%:p")
will cause vim to to think that the user is trying to define textl as "Search\n:exe ":sign place... etc.
You could also try
:<C-R><C-L><CR>
Per the vim docs, the combination will plop the current line into the command line. From there, hitting enter should do the trick. I realize that this does not handle multiline cases, however it doesn't require a .vimrc and therefore works out of the box.
If you're doing a lot of experimenting (trying things out that you might want to add to your vimrc, I assume?) it might help to do so in a scratch file like experimental.vim so you aren't just relying on your history to know what you're trying out. Now that you have these great mappings, it will be easy to rerun things from experimental or vimrc without sourcing the whole file.
Also (sorry, I can't comment on answers yet, it seems), I tried this mapping of Peter's:
vnoremap <Leader>es :<c-u>exec join(getline("'<","'>"),'<BAR>')<CR>
This works in most cases, but it fails specifically on function definitions.
function! TestMe()
echo "Yay!"
endfunction
This mapping joins the lines into a single string, separated by <BAR> and then execs them.
I'm not entirely sure why, but if I try to do that with a function definition in normal mode:
:exec 'function! TestMe()| echo "Yay!"|endfunction'
-> E488: Trailing characters
After some testing, I've found that it will work with newline separators instead:
:exec "function! TestMe()\n echo 'Yay!'\nendfunction"
:call TestMe()
-> Yay!
So, I've changed my mapping to this:
vnoremap <Leader>es :<c-u>exec join(getline("'<","'>"),"\n")<CR>
I suppose there is a vim or ex reason why the <BAR> method doesn't work on functions (maybe even some setting I have on?), and I'm curious to hear what it is if someone knows.
I don't want to reload the whole file (:so %) since that will reset a lot of temporary stuff I'm experimenting. I just want to run the two lines that I'm currently working on.
If you want to execute a command because you want to refine it before committing it to _.vimrc, then you should launch a Command Line Window for Ex-mode commands with q:.
At launch the Command Line Window is buffered with the contents of the command line history. It is a normal Vim window the contents of which can be edited as any text buffer with the exception of pressing on any line which executes the command on that line. It is very useful when you want to slightly change a long, complex command you wrote earlier and re-run it.
To launch a 'Command Line Window' for search strings press q/.
!! (shorthand for :.!) executes the current line as input to a command, per POSIX ex & vi. You may need to append sh if it is a system command.
Executing !! on a blank line (and omitting sh) is a shortcut for reading a shell command straight into the buffer. By it's nature :.! overwrites the current line while :.r! inserts on the line below.
ls -A | head -n +4
~
~
!sh
Results:
.sh_history
.sh_logout
.kshrc
corelist.txt
~
~
4 lines added; 1 line deleted`
This means there is no need to redirect pipelines to a file and then examine the data to see if the contents are valid. Just execute commands in vi directly and undo if you make a mistake.
Alternately, yanking a line as a named buffer allows you to execute it as an ex command, almost like a macro. You can still edit and undo the line to get it correct instead of trying to edit the : line in command mode.
The functions recommended here are all POSIX and have been supported for over 40 years, so no special vim or other enhanced features are required.
:%s/meep/pEEp/ | g/foo/ s//BAR
foo
grok
meep
~
~
Yank the ex command (line 1, :%s...) into a named buffer / macro.
I just use the label m for "macro".
"myy
or
:1y m
Now execute the named buffer / macro, in command mode, using #:
#m
Results:
:%s/pEEp/pEEp/ | g/BAR / s//BAR
BAR
grok
pEEp
~
~
4 lines changed
But remember that "multiple undo" is not POSIX. undo is only a toggle between undo and redo in a "historically accurate & compliant" ex / vi implementation.
The work-around is to save to a temporary (or valid) file name before executing a questionable edit:
:w $$.tmp
Then just :e! to "reset and reload" if needed.
You can also use :pre (preserve) to make a special temporary backup file prior to making multiple changes.
Then use :reco! % (recover this!) to restore back to that point.
Realize that :preserve creates a snapshot-like file which is deleted as soon as it is rolled back to. It does not matter if you save the edit(s) or not.
Therefore writing your own file (:w ...) and restoring with :e! may still have value because the system will not automatically delete it.
:pre is perfect when you should have ran sudo vi ... or otherwise do not have the necessary permissions - but you only realized the mistake after making several changes. i.e. vi /etc/sudoers instead of sudo vi /etc/sudoers.
^^ NEVER DO THIS! ONLY AN EXAMPLE! USE sudo visudo INSTEAD!
You can get a list of existing recovery files with vi -r and recover one directly with vi -r filename as needed, optionally with something like sudo vi -r filename.
The distinction here is that even though the ":preserved file" has it's own "special" name and path internally, it will :write to the original, intended location when ":recovered ==> /etc/sudoers
Just be sure to use :wq! and not something like ZZ when done with your "recovery" or you will still lose the edits which you tried to save.
By the way, ^R is expected to redraw or repaint the display per POSIX; it is not "undo" in any compliant vi implementation.

How to redirect stdout output to a new Vim tab?

I'm editing an XML file in Vim, and then I want to transform it a plain-text file with xsltproc, which by default outputs to a stdout (something like : !xsltproc TXTRULE.XSL %). Is it possible to redirect that xsltproc output to a new tab in Vim without creating any intermediate files?
(I've tried to read :help redir and some wiki notes, but still can't get it. would be greateful for some kind of simple example.)
You can use read like in the following:
:read !ls
Obviously you should change ls with your command. If you want to open a new tab prepend tabnew with a bar to the command like:
:tabnew|read !ls
To expand on lucapette's answer, you could create a map like this:
:map ,x :tabnew<Bar>read !xsltproc TXTRULE.XSL #
# expands to the previously opened buffer, which is the file you were editing, while % would expand to the new buffer opened by :tabnew.
<Bar> has to be used instead of |, because otherwise, the :map command would end at the |.
I am using the following to view my program outputs (very useful for a makefile with a make run rule)
It opens a new tab next to current one only if one was not already opened before for that purpose:
fu! RedirStdoutNewTabSingle(cmd)
let a:newt= expand('%:p') . ".out.tmp"
tabnext
if expand('%:p') != a:newt
tabprevious
exec "tabnew" . a:newt
else
exec "%d"
endif
exec 'silent r !' . a:cmd
set nomodified
endfunc
au FileType xml noremap <buffer> <F6> :call RedirStdoutNewTabSingle("xsltproc")<CR>

how to get :bwipe *.ext to wipe everyone matching the wildcard in vim

I often want to wipe all buffers loaded with a given extension (usually .rej files produced by patch). Just doing :bw[!] *.rej will complain if there is more than one match. Does anyone have any good tips? Currently I either repeatedly use :bw *.rej + tab-complete or, if there are a lot of buffers, use :ls and :bw a set of buffers by number.
Globbing in vim is a bit difficult (apart from for files on the file system). Therefore, the best way seems to be to convert the wildcard into a regular expression and then check each buffer in the buffer list to see whether it matches. Something like this:
" A command to make invocation easier
command! -complete=buffer -nargs=+ BWipe call BWipe(<f-args>)
function! BWipe(...)
let bufnames = []
" Get a list of all the buffers
for bufnumber in range(0, bufnr('$'))
if buflisted(bufnumber)
call add(bufnames, bufname(bufnumber))
endif
endfor
for argument in a:000
" Escape any backslashes, dots or spaces in the argument
let this_argument = escape(argument, '\ .')
" Turn * into .* for a regular expression match
let this_argument = substitute(this_argument, '\*', '.*', '')
" Iterate through the buffers
for buffername in bufnames
" If they match the provided regex and the buffer still exists
" delete the buffer
if match(buffername, this_argument) != -1 && bufexists(buffername)
exe 'bwipe' buffername
endif
endfor
endfor
endfunction
It can be used as:
:BWipe *.rej
or:
:BWipe *.c *.h
By the way, I ended up going with a very low-tech solution (I personally like to modify vim as little as possible so that I am at home on any machine):
I added a mapping:
:map <C-f> :bw *.rej
Then I repeatedly press
<C-f> <Tab> <CR>
a little bit around the corner, but works for me, with a lot of files:
:mksession!
:q
vi Session.vim
a) remove all 'badd' lines with files you do not want
b) :wq
vim -S Session.vim # restart with the state you had, but without the files
Do CTRL-A to insert all the matches of the pattern in front of the cursor. See :help c_CTRL-A. For example, if you have the files a.rej, b.rej, and c.rej loaded, then doing
:bw *.rej<C-A>
will leave you with
:bw a.rej b.rej c.rej
Then you can press Enter to wipe those buffers.

Resources