How to search and replace in epub file opened with vim.zip - vim

Adding a simple line to my .vimrc I managed to open an ePub archive with zip.vim on a vim buffer. This basically opens a numbered list of htm, css, jpg, xml files that one can access individually. I want to perform a search and replace through all these numbered files as it is too painful to edit each of them and do it - there's around 400 files. :args and :argdo did not really work or at least I could not make them. Any ideas?

I'm not sure it will work for you, and I'm going to assume that when you hit Enter on a filepath inside your Vim buffer, a new viewport is opened (by the ZipBrowseSelect() function defined in $VIMRUNTIME/autoload/zip.vim) to display the contents of the file.
If this is the case, you could try the following method.
:%argd
This command deletes all the paths in the current arglist.
Then, you would have to visually select all the lines containing a path to a file you want to modify. From normal mode, you could hit vip, for example, and adjust the visual selection to exclude some lines if needed.
:'<,'>g/^/exe "norm \r" | argadd % | close
This command should hit Enter on each line inside your visual selection, add the file which has been opened in a new viewport, and close the latter to get back to the original window.
:vim /pattern/ ##
This command should populate the quickfix list with all the lines containing the pattern you're looking for.
:cfdo %s/pattern/replacement/ge | update
This command should replace pattern with replacement in each file present in the quickfix list, and save the file if it has been changed.
The last step uses the :cfdo command which was introduced in Vim version 7.4.858. If your Vim version is not new enough to support :cfdo, you could bypass the last 2 steps, and directly execute:
:argdo %s/pattern/replacement/ge | update
The benefit of:
:vim /pattern/ ##
:cfdo %s/pattern/replacement/ge | update
... is to prune the arglist from the files which don't contain your pattern, so that the substitutions commands are only executed when needed.
If you don't have :cfdo but still want to prune the arglist, you could source this custom command:
com! -nargs=0 -bar Qargs exe 'args '.s:qfl_names()
fu! s:qfl_names() abort
let buffer_numbers = {}
for qf_item in getqflist()
let buffer_numbers[qf_item['bufnr']] = bufname(qf_item['bufnr'])
endfor
return join(map(values(buffer_numbers), 'fnameescape(v:val)'))
endfu
I've copied it from this video: Project-wide find and replace.
Most of the other commands are also taken from this video, so it might help you to have a look at it, if you don't have already.
Then, you would replace the :cfdo command, with:
:Qargs
:argdo %s/pattern/replacement/ge | update
To summarize, you could try one of these 3 methods:
:%argd
visually select the paths of the files
:'<,'>g/^/exe "norm \r" | argadd % | close
:argdo %s/pattern/replacement/ge | update
Or:
:%argd
visually select the paths of the files
:'<,'>g/^/exe "norm \r" | argadd % | close
:vim /pattern/ ##
:cfdo %s/pattern/replacement/ge | update
Or:
:%argd
visually select the paths of the files
:'<,'>g/^/exe "norm \r" | argadd % | close
:vim /pattern/ ##
:Qargs
:argdo %s/pattern/replacement/ge | update
Edit:
You could also try to visually select the paths of the files, then execute:
:'<,'>g/^/e `='zipfile:'.expand('%:p').'::'.getline(".")` | %s/pattern/replacement/ge | update | b#
This method relies on the fact that the path to a file in the archive seems to follow this scheme:
zipfile:/path/to/epub::path/to/file/in/archive
So, you can get the path to a file under the cursor with the Vim expression:
'zipfile:'.expand('%:p').'::'.getline(".")
And you can edit this file using backticks (see :h `=):
:e `=Vim expression`
→
:e `='zipfile:'.expand('%:p').'::'.getline(".")`
From there, you need the global command, to repeat the edition on each line inside the visual selection.
In the epub I tested, all the paths were below the line mimetype. If this is the case for you, then you could merge the 2 steps (visual selection + global command) in a single command:
1/mimetype/+,$g/^/e `='zipfile:'.expand('%:p').'::'.getline(".")` | %s/pattern/replacement/ge | update | b#

I'm not sure but this mìght help you
On esc mode try
:%s/word you want to search/word you want yo replace with/g

Related

VIM: grep search all files listed in text file

I have a complected project hierarchy for my FPGA projects. I have written a passer which examines the "Vivado/ISE" project file and returns a file containing a list of all the source file the project users (I then run ctags over this list).
I would like to be able to search this list of files from vim 7.4 without needing to do a whole recursive search through the FPGA library. I can do this from the linux command line using something like:
cat ./files.txt | xargs grep -Hn my_function
Where ./files.txt contains the list of files I would to search over. However I would like to be able to use vim's internal grep function. I would also like to be able to run this from both Windows and Linux.
Is there an easy way to pass a list of files (contained within a file) to vim's grep ex function?
You should probably use a plugin such as CtrlSF. But if you insist to do it with Vim alone, you can do something like this:
function! Grep(what, where)
exec join(extend(['vimgrep', a:what],
\ map(filter(readfile(a:where), 'v:val !=# "" && filereadable(v:val)'),
\ 'fnameescape(v:val)')))
copen
endfunction
command! -nargs=+ Grep call Grep(<f-args>)
Then you'd just call :Grep /pattern/ filelist, instead of :vimgrep /pattern/ ....
First step, populate the argument list with all the files in your list:
" in UNIX-like environments
:args `cat files.txt`
" in Windows
:args `type files.txt`
Second step, search for pattern in the argument list:
:vim pattern ##
If you have opened all files in Vim—eg with
vim `<files.txt`—then you can search all of them with (e.g.)
:bufdo g/my_function/
If you :set nu you'll get line numbers as per
grep -nHth

Vim, Ag and Quickfix window, limit output width and height

Is there a way to limit :Ag output so it always takes one line and doesn't blow up the quickfix window?
At the moment it looks like this and it's awful. I can't see filenames, everything is super slow and just sucks:
Update For the record, I scrolled Quickfix window a bit to illustrate the point better. And while it is usable via :cn :cp, I would like to be able to quickly glance over the results with j k.
Looking over the man page, there does not seem to be any way to limit the output built into Ag itself.
Is there another way of limiting the line length? Actually, you do have the built in "cut" command in Linux, e.g. using it on the shell:
ag --column foo | cut -c 1-80
Limit all lines to 80.
Now we have to make ag.vim execute our specially crafted command, for which the g:agprg exists. So the first thing I thought of is this:
let g:agprg='ag --column \| cut -c 1-80' " doesn't work
The problem with this is that the ag.vim plugin just appends extra arguments to the end, and thus you end up executing something like ag --column | cut -c 1-80 something-i-searched-for. Is there a way to directly "insert" the arguments before the |?
One trick is to use a temporary shell function, like this:
f() { ag --column "$#" | cut -c 1-80 }; f something-i-search-for
Unfortunately, we still can't use this. ag.vim checks whether or not the first word is an actual command. So it complains that no executable by the name of "f()" exists. So my final solution:
let g:agprg='true ; f(){ ag --column "$#" \| cut -c 1-80 }; f'
As true always exists and doesn't do anything except return true, this works!
To your actual screenwidth instead of 80, you could use:
let g:agprg='true ; f(){ ag --column "$#" \| cut -c 1-'.(&columns - 6).' }; f'
I added the magic - 6 here to account for the extra characters Vim itself adds.
ag now supports a --width switch. rg has a similar --max-columns switch.
Assuming you are using this plugin. You should add this to your ~/.vimrc as specified by :h g:ag_qhandler
let g:ag_qhandler = 'copen 1'
However you can probably just do let :g:ag_qhandler = 'cc'. This will print the results at the in the bottom. When you move through the quickfix list via :cnext or :cprev it will print the current result as well.
For more help see:
:h g:ag_qhandler
:h :cope
Changing the geometry of the quickfix window won't help you fix your problem: the window is unusable not because of its size but because your search results are polluted by superfluous matches in minimized files.
Minimized JavaScript or CSS is the frontend development's equivalent of a binary and that kind of file should be ignored by search tools, indexing tools, file navigation tools and even version control tools, sometimes, because they are generally irrelevant.
Adding these lines to your ~/.agignore will make Ag search only in actual source files:
*.min*
*-min*
*_min*
*.min.*
bundle
min
vendor
tags
cscope.*
Adjust that list to your liking.

Open a file based on the name/dir of the currently opened file

I would like to either find a vim plugin or write a vimscript function to open
a file in (or under) the same directory as my present file. The file should
match one of a list of regex files that is defined in a list.
I have a project that looks something like this:
src
|- controllers
| ...
|- util
| ...
|- widgets
| - widgetA
| | - widgetA.js
| | - widgetA.template.html
| -widgetB
| | - widgetB.js
| | - widgetB.template.html
| -widgetC
| | - widgetC.js
| | - widgetC.template.html
| | - someHelpers.js
Sample usecase (in the command line):
cd src
vim widgets/widgetA/widgetA.js
Inside vim:
press F4 while in command mode
Result:
widgetA.template.html is opened in vertical split mode with widgetA.js
Template files will either be in one of the following:
Named the same as the presently opened file but with this regex run on it: s/js$/tempate.html/
Named the same as the presently opened file but with this regex run on it: s/js$/html/
In a subdirectory named templates named as above
I am presently using nerdtree and ctrl-p to speed up file opening, but this is a workflow that
I use so frequently that I thought it could make sense to try to speed it up. Any suggestion?
It feels like you are looking for something along the lines of projectionist or fswitch.
projectionist
With projectionist you could in theory then just execute :A/:SA to switch to the alternate file.
So in your .projections.json (not tested) file would look something like this:
{
"widgets/*.js": {
"alternate": "widgets/{}.template.html",
"type": "widget"
},
"widgets/*.template.js": {
"alternate": "widgets/{}.js",
"type": "template"
}
You can also use :Ewidget and :Etemplate commands to find a widget/template. These commands will also take fuzzy filenames. e.g. :Ewidget wta. You can also open the files in split, vertical splits and tabs via :Swidget, :Vwidget, and :Twidget respectively. Please see :h projectionist for more information.
fswitch
Another option is to use something like fswitch which is a C/C++ .h/.c switcher. Please see :h fswitch-setup for more information.
There are a few other plugins that do similar things: altr and a.vim to name a few.
vanilla
If plugins are not your thing then you can use % tricks. e.g. :sp %<.template.html
Or maybe a quick and dirty mapping:
nnoremap <f4> %:p:s,\.js$,.X123X,:s,\.template\.html$,\.js,:s,\.X123X$,\.template\.html,<CR>
For more information see the following vim wiki page: Easily switch between source and header file
conclusion
I personally use projectionist and find it meets my needs, especially for navigating a structured project which I find more useful than just a simple switcher like fswitch. Projectionist will also be easier than the vanilla approach to when your needs become wilder.
What you want is easy to do in the shell:
$ cd src
$ vim -O w*/*A/*
but I'm not exactly sure how you see that working in Vim itself. Do you want that to happen in a new tab? Do you want the new pair of files to replace the current pair of files?
Dropping this into the your vimrc and relaunch vim will work.
Presently only working for files ending in .template.html that are in the same directory but easy to see how it could work for multiple other cases. When it can't find the template it opens nerdTree to the current directory.
map <F4> :call OpenTemplate()<cr>
function! OpenTemplate()
" Get the Current directory
let l:jake = expand('%')
" Replace .js with .template.html
let l:jake = substitute(l:jake, ".js$", ".template.html", "")
" Verify that the file exists and it it does open in using vs (see :help vs)
" if the file can't be found, open nerdTree
if filereadable(l:jake)
execute 'vs ' . l:jake
else
echo 'Cant find template'
:NERDTreeFind
endif
endfunction

vim - process the output from the "read" command as a range in Ex mode

BACKGROUND:
In vim (Ex mode) it is possible to run an external command and have the output from that command automatically inserted into the current buffer.
In Example 001, we see how to cd to the current directory, get a listing of the files there and auto insert that into the current buffer
EXAMPLE 001:
:cd . | :r ! dir /w/s/b
QUESTIONS:
1) Is it possible to automatically specify or capture the Vim {range} to reflect the lines that were recently inserted into the file ?
2) Using the range obtained in question 1) is it possible to chain Ex mode commands to automatically process the lines that were inserted into the file ?
3) If it is not possible to do 1) or 2) above, is there an alternate way for Vim to recognize lines recently inserted into the buffer and run arbitrary commands on them ?
4) What is a relevant :help cross reference that can be used for this purpose ?
GOAL:
The goal is to be able to chain multiple Ex mode commands together to easily run process recently added lines to a file, without having to expressly identify the line number or manually select them using Visual mode or something similar.
The goal is do something similar to the (psuedo-code) in Example 002
Example 002:
:cd . | :r ! dir /w/s/b | :{auto-range}s/^/ /
Vim sets the change marks '[ and '] to the inserted range; you can use these to define a range for subsequent Ex commands:
:cd . | execute 'r ! dir /w/s/b' | '[,']s/^/ /
You need :execute because otherwise the | is interpreted to belong to the :r command.
What about processing those lines before inserting them in Vim?
:r!dir /w/s/b | sed -e "s/^/ /"

Efficient way to refactor a class/method/string within a directory using vim

So far, I have been manually refactoring code by using the find-and-replace operation
%s:/stringiwanttoreplace/newstring/g
in vim.
But this is a slow and laborious process if I have stringiwanttoreplace in many files inside a specific directory.
My current/typical slow and laborious process involves a grep:-
grep -rn "stringiwanttoreplace" .
in my terminal to reveal all the locations/filenames where stringiwanttoreplace are; and now that I know which files contain stringiwanttoreplace, I will open each file one-by-one to perform the find-and-replace operation in each file.
Is there a more efficient workflow (in vim) to get this done?
CLARIFICATION: I would prefer a vim-based solution instead of a bash script/one-liner.
Here's the full sequence of commands that I would use:
/stringiwanttoreplace
:vimgrep /<c-r>// **
:Qargs
:argdo %s//newstring/g
:argdo update
In the first line, we search for the target pattern. That populates the last search pattern register (:help quote/), which means that we won't have to type it out in full again.
The :vimgrep command searches the entire project for the specified pattern. Type <c-r>/ as ctlr+r followed by / - this inserts the contents of the last search pattern register onto the command line. The first and last / symbols are delimiters for the search field. The trailing ** tells Vim to look inside every file and directory below the current directory.
At this point, the quickfix list will be populated with search matches from all matching files. :Qargs is a custom command, which populates the argument list with all of the files listed in the quickfix list. Here's the implementation:
command! -nargs=0 -bar Qargs execute 'args ' . QuickfixFilenames()
function! QuickfixFilenames()
" Building a hash ensures we get each buffer only once
let buffer_numbers = {}
for quickfix_item in getqflist()
let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])
endfor
return join(values(buffer_numbers))
endfunction
Add that to your vimrc file.
Having run :Qargs, our argument list should now contain all of the files that include our target string. So we can run the substitution command with :argdo, to execute the command in each file. We can leave the search field of the substitution command blank, and it will automatically use the most recent search pattern. If you want, you could include the c flag when you run the substitution command, then you'll be prompted for confirmation.
Finally, the :argdo update command saves each file that was changed.
As #Peter Rincker pointed out, you should ensure that Vim's 'hidden' option is enabled, otherwise it will raise an error when you try to switch to another buffer before writing any changes to the active buffer.
Also, note that the last 3 commands can be executed in a single command line, by separating them with a pipe character.
:Qargs | argdo %s//replacement/gc | update
The :Qargs command is pinched from this answer (by me), which in turn was inspired by this answer by DrAl. A very similar solution was posted by #ib, which suggests to me that Vim should really implement something like :quickfixdo natively.
If you really want to do it in Vim you can follow the suggestions here.
You can call this from within Vim (:!find ...) but you don't need to:
find . -type f | xargs sed -i 's/stringiwanttoreplace/newstring/g'
Fine-tune the file selection with the dozens of parameters described in
man find
(e.g., replace only in HTML files: -name \*.html)
This solution will try to attempt the replacement in all files. You can filter that through grep before, but that is just doing twice the work for no gain.
By the way: sed uses almost the same syntax for regular expressions as Vim (stemming from the same history).
You could open all the files and type
:bufdo :s/stringiwanttoreplace/newstring/g
It performs the search/replace in all your buffers.
You don't need vim to do this, you can use command line tools. Using sed in a loop on the list of files to do this for you automatically. Something like this:
for each in `grep -l "stringiwanttoreplace" *` ;
do
cat $each | sed -e "s/stringiwanttoreplace/newstring/g" > $each
; done
vim7 has recursive grep built-in
:vimgrep /pattern/[j][g] file file1 file2 ... fileN
the result will be shown in a quickfix-window (:help quickfix)
to do the search recursively use the **-wildcard like
**/*.c to search through the current folder and recursively through all subdirectories.

Resources