How to :bufdo only on modifiable buffers in vim? - vim

Sometimes I need to substitute across multiple buffers. For the purpose I use :bufdo %s/old/new/gec. Recently I noticed that the command fails when there is non-modifiable buffer in the buffer list (in my case it's opened file explorer/netrw). After running the command vim leaves me with E21: Cannot make changes, 'modifiable' is off and opened Netrw window.
Are the ways to :bufdo only on modifiable buffers? I've already tried :bufdo!, but the behaviour was the same (just without showing the error).
UPDATE
I find the line of .vimrc that poses this problematic behaviour:
let g:netrw_liststyle=3
I don't know what the magic here, but when I set this option neither of the suggested solutions/commands work for me. Now, the question is how to keep this line and make the :bufdo behaviour skip the buffer created by Netrw.

Well, if :bufdo sil! :%s/old/new/gec does not work for you (this silently ignores errors). you need to wrap the command into an if statement. Something like this:
:bufdo if &ma | :%s/old/new/gec | endif
which checks for each buffer, if it is modifiable and only then attempts to replace old by new.
Note: You might also want to check for the 'readonly' option in addition to the 'modifiable' setting.

Related

How to source vimrc in All buffers

To source vimrc in all buffers in the current window I do :bufdo so~/.vimrc ,to source vimrc in the ACTIVE buffers in all windows I do :windo so~/.vimrc. How to source vimrc in all buffers in all windows?
for some reason if I do:bufdo so~/.vimrc | :windo so~/.vimrc the active buffer in the non-active window changes.
example: if I set set number in my .vimrc, I want all buffers in all windows affect the change.
the solution might be something like this:
for window in windowlist
execute 'bufdo so~/.vimrc'
I use vim 8.2 .
PLEASE NOTE: I did try all of the commands you guys suggested but it seems that things aren't clear enough. all of the commands that includes :tabdo :windo :bufdo or combination of them doesn't work properly for what I want. please note that this is the same as changing vimrc and sourcing it with chain of these commands like :tabdo bufdo so~/.vimrc. for example :windo bufdo set number does set number to all buffers(active or hidden) in the active window but on non-active windows only does set number for the active buffer(buffer that is shown in the window) or :tabdo windo set number only does set number in active buffers in every window meaning that hidden buffers won't get line numbers.
I even made a reddit post about this problem on r/vim and made a video about it but I don't know why nobody gets what I say. honestly don't think this problem will be solved by someone else but me even though its a simple one.
The :bufdo command will consume | as part of the command to execute, so in effect you're running the :windo command for each buffer that you have active!
See :help :bar, which documents this behavior.
That help section also mentions a way to work around this behavior, by using the :execute command to run the first command from a string, which allows you to delimit the first command. Like so:
:execute 'bufdo so ~/.vimrc' | windo so ~/.vimrc
Please note that sourcing your vimrc file "in all buffers" or "in all windows" doesn't make much sense... The vimrc file typically has global commands that usually need to be sourced only once, and usually if you modify your vimrc, sourcing it again only once should be enough...
This might make sense with a separate *.vim script that affects local settings and is meant to act on a single buffer. The ftplugin, indent and syntax scripts come to mind. But also with those, they're normally run per buffer, not per window... It's not completely inconceivable that you'd have scripts that you want to run on every buffer and window, but it surely seems odd...
.vimrc shall only contain global definitions. That the way it's supposed to be used. Sourcing it in several buffers makes no sense.
I wonder if you're fighting with local settings for which the best tool to use is either ftplugin (when the setting are filetype driven), or a local_vimrc plugin for project driven settings.
To apply in all buffers and all windows:
:windo bufdo set number
If you have tabs, from :tabe, :tabf, :tab and friends. Just add tabdo like this:
:tabdo windo bufdo set number
I would advise not to source your vimrc like this. If you want a quick setting, just use the set command and friends.
:help tabdo
:help windo
:help bufdo
:help source
While I agree with most answers on sourcing your vimrc. I do believe there are uses of the source command. Particularly batch fixing. I've never tried using
:windo with this, I mostly use :argdo and :cdo. As I have more fine-grained control on what files I need to apply.
Batch fixing is particularly useful on a large codebase. You do your fixes with :g, :v, :s for example, and save them in a file called fix.vim. This is so useful, you could even pair macros with those commands (:g and :v) via :norm command.
:help norm
Then update your argslist via :args *.js or similar commands (like backtick expression) and finally do the :argdo source fix.vim

Hard refresh in vim (not :so %)

Sometimes in vim I'll need to exit the vimrc file and enter back into it to update changes (for example if I'm editing other files related to it). To do this I'll normally do:
:wq
$ vim
ctrl-o ctrl-o " in vim
Would there be a way to do this all within vim? Something like:
:wq | !vim %
Additionally, why does it require me to do ctrl-o two times to go to the previous buffer (it almost seems like the first ctrl-o does nothing)?
Update: Please note that I am aware of doing :so % or :so $MYVIMRC, etc. My question here is how do I basically reset 100% of the things to whatever are in my current files? That is, unset ALL mappings, variables, etc. that may have been updated, removed, etc; update ALL files that may have changed (functions, plugins, colorschemes, etc.). I don't think "Running :so % on 20 files" is a good solution here, which is why my current solution is to close the file and re-open it.
As others have mentioned, you can source your .vimrc, but that doesn't completely reset Vim. If you want to just restart Vim, then you can do so by re-execing it.
Vim doesn't provide a built-in way to exec processes from within it, since typically one doesn't want to replace one's editor with another process, but it is possible to do so with Perl or Ruby, as you see fit:
:perl exec "vim"
or
:ruby exec "vim"
This may or may not work on Windows, and it of course requires that your Vim version have been compiled with support for the appropriate interpreters. Debian provides both in the vim-nox, vim-gtk3, and vim-athena packages, but not in vim or vim-tiny; Ubuntu, last I checked, did not provide Ruby support but did include Perl.
If you want to re-exec with the same buffer, you can use one of these:
:perl exec "vim", $curbuf->Name();
or
:ruby exec "vim", Vim::Buffer.current.name
Note that re-execing may cause your screen to be slightly messed up when exiting, so you may need to use reset to set it back to normal.
I don't know if you had tried this but you can source your vimrc file from vim itself by typing
:so $MYVIMRC
In order to apply the changes, you don't have to exit vim and open it again, no need for a "hard refresh" :)
If you want to apply in on the .vimrc file itself, you can type
:so %
to apply the changes in another file, you can type:
:so ~/.vimrc #path to your .vimrc file
in normal mode, Ctrl-O takes you to where your cursor has been backward and Ctrl-I forward. You can check your jump list by typing :jumps, to clear your jumps :clearjumps
in insert mode, Ctrl-O escapes to normal mode and lets the user to do one normal mode command.

How can I get rid of this "Auto Commands" message from vim?

If I have this line in my vimrc
au VimLeave %bdelete
Then whenever vim starts it says
--- Auto-Commands ---
Press ENTER or type command to continue
I have that line there to empty the buffers from gvim, because new gvim instances have massive :ls output from previous runs. Notably, gvim doesn't actually produce this prompt.
Obviously I can set this instance up to only occur during gvim startup and not console vim, but I'd like to understand this rather than avoid it. Mostly I'm confused that VimLeave seems to cause things to happen on startup.
TIA
Altreus
The problem is that this is an incomplete :autocmd definition, so Vim attempts to list all VimLeave autocommands defined for the pattern %bdelete. You need to specify the any file pattern to make it work:
au VimLeave * %bdelete
Also, check whether you have % in your 'viminfo' option; that one enables the saving and restoring of the buffer list you're complaining about. The f option of file marks may also result in buffers being restored; you could try :set viminfo+=f0.
New Vim instances don't inherit the buffer list of the previous instance unless you add % to the viminfo option.
Setting that option to a sane value will remove the need for your broken fix. Reading the documentation before adding options to your config will prevent you from similar issues.
See :help 'viminfo'.

vim :make automatically jumps to first file with error

When executing :make from vim, after make is complete it automatically jumps to a file with errors. Is there a way to avoid this
EDIT
This is usecase i want to achieve
I want :make to execute then quicklist to open but the current file which i am working on should not be switched to the one with errors
with default settings after :make execution quicklist opens and the current file also changes
From the docs:
7. If [!] is not given the first error is jumped to.
So, just invoke it as :make!.
You can run :make! | copen, which should place your cursor in the quickfix list instead of changing the current buffer. You can make this even easier by putting command Mymake make! | copen in your .vimrc, so you only have to run :Mymake to do this.
Note that when selecting errors from the quickfix list, they will scroll a buffer with the file already open rather than change the current window if possible, and you can open the files in new windows with <C-w> Enter.
It might not be the cleanest solution, but setting the errorformat to an empty string should do the trick, ie.
:set errorformat=""
That should keep it from matching the compiler error strings.

How do I close a single buffer (out of many) in Vim?

I open several files in Vim by, for example, running
vim a/*.php
which opens 23 files.
I then make my edit and run the following twice
:q
which closes all my buffers.
How can you close only one buffer in Vim?
A word of caution: “the w in bw does not stand for write but for wipeout!”
More from manuals:
:bd
Unload buffer [N] (default: current
buffer) and delete it from
the buffer list. If the buffer was changed, this fails,
unless when [!] is specified, in which case changes are
lost.
The file remains unaffected.
If you know what you’re doing, you can also use :bw
:bw
Like |:bdelete|, but really delete the
buffer.
If this isn't made obvious by the the previous answers:
:bd will close the current buffer. If you don't want to grab the buffer list.
Check your buffer id using
:buffers
you will see list of buffers there like
1 a.php
2 b.php
3 c.php
if you want to remove b.php from buffer
:2bw
if you want to remove/close all from buffers
:1,3bw
Rather than browse the ouput of the :ls command and delete (unload, wipe..) a buffer by specifying its number, I find that using file names is often more effective.
For instance, after I opened a couple of .txt file to refresh my memories of some fine point.. copy and paste a few lines of text to use as a template of sorts.. etc. I would type the following:
:bd txt <Tab>
Note that the matching string does not have to be at the start of the file name.
The above displays the list of file names that match 'txt' at the bottom of the screen and keeps the :bd command I initially typed untouched, ready to be completed.
Here's an example:
doc1.txt doc2.txt
:bd txt
I could backspace over the 'txt' bit and type in the file name I wish to delete, but where this becomes really convenient is that I don't have to: if I hit the Tab key a second time, Vim automatically completes my command with the first match:
:bd doc1.txt
If I want to get rid of this particular buffer I just need to hit Enter.
And if the buffer I want to delete happens to be the second (third.. etc.) match, I only need to keep hitting the Tab key to make my :bd command cycle through the list of matches.
Naturally, this method can also be used to switch to a given buffer via such commands as :b.. :sb.. etc.
This approach is particularly useful when the 'hidden' Vim option is set, because the buffer list can quickly become quite large, covering several screens, and making it difficult to spot the particular buffer I am looking for.
To make the most of this feature, it's probably best to read the following Vim help file and tweak the behavior of Tab command-line completion accordingly so that it best suits your workflow:
:help wildmode
The behavior I described above results from the following setting, which I chose for consistency's sake in order to emulate bash completion:
:set wildmode=list:longest,full
As opposed to using buffer numbers, the merit of this approach is that I usually remember at least part of a given file name letting me target the buffer directly rather than having to first look up its number via the :ls command.
Use:
:ls - to list buffers
:bd#n - to close buffer where #n is the buffer number (use ls to get it)
Examples:
to delete buffer 2:
:bd2
You can map next and previous to function keys too, making cycling through buffers a breeze
map <F2> :bprevious<CR>
map <F3> :bnext<CR>
from my vimrc
Close buffer without closing the window
If you want to close a buffer without destroying your window layout (current layout based on splits), you can use a Plugin like bbye. Based on this, you can just use
:Bdelete (instead of :bdelete)
:Bwipeout (instead of :bwipeout)
Or just create a mapping in your .vimrc for easier access like
:nnoremap <Leader>q :Bdelete<CR>
Advantage over vim's :bdelete and :bwipeout
From the plugin's documentation:
Close and remove the buffer.
Show another file in that window.
Show an empty file if you've got no other files open.
Do not leave useless [no file] buffers if you decide to edit another file in that window.
Work even if a file's open in multiple windows.
Work a-okay with various buffer explorers and tabbars.
:bdelete vs :bwipeout
From the plugin's documentation:
Vim has two commands for closing a buffer: :bdelete and :bwipeout. The former removes the file from the buffer list, clears its options, variables and mappings. However, it remains in the jumplist, so Ctrl-o takes you back and reopens the file. If that's not what you want, use :bwipeout or Bbye's equivalent :Bwipeout where you would've used :bdelete.
How about
vim -O a a
That way you can edit a single file on your left and navigate the whole dir on your right...
Just a thought, not the solution...
[EDIT: this was a stupid suggestion from a time I did not know Vim well enough. Please don't use tabs instead of buffers; tabs are Vim's "window layouts"]
Maybe switch to using tabs?
vim -p a/*.php opens the same files in tabs
gt and gT switch tabs back and forth
:q closes only the current tab
:qa closes everything and exits
:tabo closes everything but the current tab
Those using a buffer or tree navigation plugin, like Buffergator or NERDTree, will need to toggle these splits before destroying the current buffer - else you'll send your splits into wonkyville
I use:
"" Buffer Navigation
" Toggle left sidebar: NERDTree and BufferGator
fu! UiToggle()
let b = bufnr("%")
execute "NERDTreeToggle | BuffergatorToggle"
execute ( bufwinnr(b) . "wincmd w" )
execute ":set number!"
endf
map <silent> <Leader>w <esc>:call UiToggle()<cr>
Where "NERDTreeToggle" in that list is the same as typing :NERDTreeToggle. You can modify this function to integrate with your own configuration.

Resources