Repository: Olical/vim-enmasse
Branch: master
Commit: c2286f1d7bd7
Files: 15
Total size: 17.8 KB
Directory structure:
gitextract_gubi8ydj/
├── .gitignore
├── .travis.yml
├── README.md
├── UNLICENSE
├── autoload/
│ └── enmasse.vim
├── doc/
│ └── enmasse.txt
├── plugin/
│ └── enmasse.vim
└── test/
├── clearing.vader
├── enmasse.vader
├── grepable.txt
├── hints.vader
├── preview.vader
├── run
├── version.vader
└── writing.vader
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
doc/tags
test/vader
================================================
FILE: .travis.yml
================================================
language: vim
before_script: |
git clone https://github.com/junegunn/vader.vim.git
script: |
vim -Nu <(cat << VIMRC
filetype off
set rtp+=vader.vim
set rtp+=.
set rtp+=after
filetype plugin indent on
VIMRC) -c 'Vader! test/*.vader' > /dev/null
================================================
FILE: README.md
================================================
# En Masse [![Build Status][travis-image]][travis]
[](https://gitter.im/Wolfy87/vim-enmasse?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Takes a quickfix list and makes it editable. You can then write each change back to their respective files using your favourite way of writing files, `:w` or `ZZ`, for example. Fix multiple [JSHint][] issues at once or perform a complex find and replace across your project all within the comfort of Vim.

## Using the plugin
As you can see in the demonstration above, all you have to do is populate a quickfix list in some way (I used [JSHint][], but you could use [Ag][], for example), then execute `:EnMasse`. This will open a new buffer with each line corresponding to a line in the quickfix list.
You can then edit each line in any way you want. When done just write this magical buffer and it will update each line in their corresponding files. For more information, check out [the documentation!][docs]
## Installation
### [vim-plug](https://github.com/junegunn/vim-plug#readme)
add this line to `.vimrc`
```
Plug 'Olical/vim-enmasse'
```
### [vim-pathogen](https://github.com/tpope/vim-pathogen#readme)
```
cd ~/.vim/bundle
git clone https://github.com/Olical/vim-enmasse
```
### [Vundle.vim](https://github.com/gmarik/Vundle.vim#readme)
add this line to `.vimrc`
```
Plugin 'Olical/vim-enmasse'
```
## Tests
Tests are performed using [vader][], to pull the dependencies and run them simply execute `./tests/run`. The tests are automatically executed by [TravisCI][travis] too, so keep an eye on that if you push changes or open a PR. The badge up the top of this README indicates the state of master, it should ALWAYS be green. A test should be written before any change is made.
## Author
[Oliver Caldwell][author-site] ([@OliverCaldwell][author-twitter])
## Unlicenced
Find the full [unlicense][] in the `UNLICENSE` file, but here's a snippet.
>This is free and unencumbered software released into the public domain.
>
>Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
Do what you want. Learn as much as you can. Unlicense more software.
[unlicense]: http://unlicense.org/
[author-site]: http://oli.me.uk/
[author-twitter]: https://twitter.com/OliverCaldwell
[jshint]: https://github.com/walm/jshint.vim
[ag]: https://github.com/rking/ag.vim
[docs]: https://github.com/Olical/vim-enmasse/blob/master/doc/enmasse.txt
[travis-image]: https://travis-ci.org/Olical/vim-enmasse.svg?branch=master
[travis]: https://travis-ci.org/Olical/vim-enmasse
[vader]: https://github.com/junegunn/vader.vim
================================================
FILE: UNLICENSE
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
================================================
FILE: autoload/enmasse.vim
================================================
function! enmasse#Open()
let list = s:GetQuickfixList()
let sourceLines = s:GetSourceLinesFromList(list)
if len(list) > 0 && len(sourceLines) > 0
call s:CreateEnMasseBuffer(list, sourceLines)
else
call s:EchoError("No entries to edit.")
endif
endfunction
function! enmasse#GetVersion()
return "1.1.1"
endfunction
function! enmasse#WriteCurrentBuffer()
let list = b:enMasseList
let sourceLines = getline(1, "$")
if len(list) ==# len(sourceLines)
call s:WriteSourceLinesAgainstList(list, sourceLines)
else
call s:EchoError("Mismatch between buffer lines and quickfix list. Refusing to write.")
endif
endfunction
function! enmasse#DisplayQuickfixEntryForCurrentLine()
let quickfixItem = s:GetQuickfixItemForCurrentLine()
call s:EchoTruncated(quickfixItem.text)
endfunction
function! s:EchoTruncated(msg)
let saved=&shortmess
set shortmess+=T
exec "echomsg a:msg"
let &shortmess=saved
endfunction
function! s:EchoError(message)
echohl ErrorMsg
echo "EnMasse:" a:message
echohl None
endfunction
function! s:GetQuickfixList()
let list = getqflist()
let uniqueList = []
for item in list
let existingItem = s:GetMatchingLineFromQuickfix(item, uniqueList)
if has_key(existingItem, "bufnr")
let existingItem.text = join([existingItem.text, item.text], " | ")
else
call add(uniqueList, item)
endif
endfor
call sort(uniqueList, "s:SortByBufferAndLine")
return uniqueList
endfunction
function! s:SortByBufferAndLine(i1, i2)
if a:i1.bufnr > a:i2.bufnr || (a:i1.bufnr ==# a:i2.bufnr && a:i1.lnum > a:i2.lnum)
return 1
else
return -1
endif
endfunction
function! s:GetMatchingLineFromQuickfix(target, list)
for item in a:list
if a:target.bufnr ==# item.bufnr && a:target.lnum ==# item.lnum
return item
endif
endfor
return {}
endfunction
function! s:GetSourceLinesFromList(list)
let sourceLines = []
for item in a:list
let file = bufname(item.bufnr)
let line = item.lnum
call add(sourceLines, s:GetLineFromFile(file, line))
endfor
return sourceLines
endfunction
function! s:GetLineFromFile(file, line)
let lines = readfile(a:file, "b")
return lines[a:line - 1]
endfunction
function! s:CreateEnMasseBuffer(list, sourceLines)
noautocmd keepalt botright new! __EnMasse__
setlocal stl=\ EnMasse
setlocal buftype=acwrite
setlocal bufhidden=hide
setlocal noswapfile
setlocal nobuflisted
normal! gg"_dG
call setbufvar(bufnr(''), "enMasseList", a:list)
call append(0, a:sourceLines)
normal! "_ddgg
nnoremap <silent><buffer> <CR> :call <SID>OpenLineInPreviewWindow()<CR>
set nomodified
if line('$') < winheight(winnr())
execute 'resize' line('$')
end
endfunction
function! s:OpenLineInPreviewWindow()
let quickfixItem = s:GetQuickfixItemForCurrentLine()
let file = bufname(quickfixItem.bufnr)
execute printf("pedit +%d %s", quickfixItem.lnum, file)
endfunction
function! s:GetQuickfixItemForCurrentLine()
let list = b:enMasseList
let currentLine = line(".")
let quickfixItem = list[currentLine - 1]
return quickfixItem
endfunction
function! s:WriteSourceLinesAgainstList(list, sourceLines)
let toWrite = s:MergeChangesUnderPaths(a:list, a:sourceLines)
for [filePath, fileChanges] in items(toWrite)
let lines = readfile(filePath, "b")
let changed = 0
for lineChange in fileChanges
if lines[lineChange.line] !=# lineChange.change
let lines[lineChange.line] = lineChange.change
let changed = 1
endif
endfor
if changed
execute "silent doautocmd FileWritePre " . filePath
call writefile(lines, filePath, "b")
execute "silent doautocmd FileWritePost " . filePath
endif
endfor
set nomodified
checktime
endfunction
function! s:MergeChangesUnderPaths(list, sourceLines)
let index = 0
let paths = {}
for item in a:list
let path = bufname(item.bufnr)
let changes = get(paths, path, [])
let paths[path] = add(changes, {"change": a:sourceLines[index], "line": item.lnum - 1})
let index += 1
endfor
return paths
endfunction
================================================
FILE: doc/enmasse.txt
================================================
*enmasse.txt*
=======================================================
____ __ _ _ _ __ ____ ____ ____
( __)( ( \ ( \/ ) / _\ / ___)/ ___)( __)
) _) / / / \/ \/ \\___ \\___ \ ) _)
(____)\_)__) \_)(_/\_/\_/(____/(____/(____)
Edit every file in a quickfix list at the same time.
=======================================================
===============================================================================
Introduction *enmasse* *enmasse-introduction*
Takes a |quickfix| list and makes it editable. You can then write each change
back to their respective files using your favourite way of writing files, |:w|
or |ZZ|, for example. Fix multiple linting issues at once or perform a complex
find and replace across your project all within the comfort of Vim.
===============================================================================
Usage *enmasse-usage*
All you have to do is populate a quickfix list in some way (using JSHint or Ag,
for example), then execute :EnMasse. This will open a new buffer with each line
corresponding to a line in the quickfix list.
You can then edit each line in any way you want. When done just write this
magical buffer and it will update each line in their corresponding files. Do
not delete or create any new lines, that will not work, EnMasse will prevent
you from writing if it spots a discrepancy because it no longer knows which
lines should go where.
Pressing enter on a line will open the preview window to that line so you can
get the context of what you're about to edit. This mimics the functionality of
the quickfix list.
===============================================================================
Autocommands *enmasse-autocommands*
When writing changes to files, EnMasse will batch writes together. That means
that if you have multiple changes for one file, only one write will take place.
With this in mind, EnMasse will fire |FileWritePre| and |FileWritePost| for
each file that is changed.
===============================================================================
Quickfix hints *enmasse-quickfix-hints*
As you move your cursor through the lines the matching quickfix entry message
will be echoed at the bottom of the screen. So if you're scrolling through a
buffer created from a JSHint quickfix list, you'll be provided with the
corresponding JSHint message for each line at the bottom of the window.
If there were multiple quickfix entries for a single line (missing semi-colon
and unused variable, for example) then their messages will be merged into one
in the hint. If the message is too long to fit on one line it will be
truncated. It's either that or you have a "press enter to continue" prompt pop
up every time the echo wraps onto the next line. Not cool. So truncation is the
better alternative, even if you lose a bit of information sometimes.
===============================================================================
Author *enmasse-author*
Oliver Caldwell <http://oli.me.uk/> / @OliverCaldwell
===============================================================================
Unlicence *enmasse-unlicence*
This is free and unencumbered software released into the public domain. For
more information, please refer to <http://unlicense.org/> or the "README" of
this project.
vim:tw=78:sw=4:ts=4:ft=help:norl:
================================================
FILE: plugin/enmasse.vim
================================================
command! EnMasse :call enmasse#Open()
command! EnMasseVersion :echo enmasse#GetVersion()
augroup EnMasseDefault
autocmd!
autocmd WinLeave __EnMasse__ wincmd p
autocmd BufWriteCmd __EnMasse__ call enmasse#WriteCurrentBuffer()
autocmd CursorMoved __EnMasse__ call enmasse#DisplayQuickfixEntryForCurrentLine()
augroup END
================================================
FILE: test/clearing.vader
================================================
Execute (the line counts are correct):
silent grep! -i a test/grepable.txt
EnMasse
let before = line("$")
normal G
quit
silent grep! -i b test/grepable.txt
EnMasse
let after = line("$")
AssertEqual 5, before
AssertEqual 2, after
================================================
FILE: test/enmasse.vader
================================================
Before (read the example grepable file and grep for quickfix):
let lines = readfile("test/grepable.txt", "b")
silent grep! quickfix test/grepable.txt
Execute (can't call :EnMasse without a quickfix list):
call setqflist([])
redir => messages
EnMasse
redir END
let result = get(split(messages, "\n"), -1, "")
AssertEqual "EnMasse: No entries to edit.", result
Execute (:EnMasse with a quickfix list creates a buffer):
EnMasse
let name = bufname("%")
quit
AssertEqual "__EnMasse__", name
Execute (the buffer contains the correct line from the quickfix list):
EnMasse
let firstLine = getline("1")
quit
AssertEqual lines[1], firstLine
Execute (duplicate quickfix lines are joined together):
silent grepadd! loaded test/grepable.txt
EnMasse
let firstLine = getline("1")
let secondLine = getline("2")
let lineCount = line("$")
quit
AssertEqual lines[1], firstLine
AssertEqual lines[3], secondLine
AssertEqual 2, lineCount
================================================
FILE: test/grepable.txt
================================================
This is an example file.
It should be loaded into the quickfix list.
Then you can run :EnMasse
And edit that quickfix list.
And this line is way too long so it will truncated. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin et ornare libero, quis vulputate odio. In luctus id velit sed gravida. Cras turpis nibh, luctus a lorem non, sollicitudin auctor justo. Vivamus tristique dolor a lectus gravida molestie. Nunc varius mi ante, vitae bibendum quam ultricies vitae.
Dupe!
================================================
FILE: test/hints.vader
================================================
Before (read the example grepable file and grep for quickfix):
let lines = readfile("test/grepable.txt", "b")
silent grep! quickfix test/grepable.txt
Execute (when the cursor is moved, the hint updates):
EnMasse
normal j
redir => messages
doautocmd CursorMoved
redir END
quit
let result = get(split(messages, "\n"), -1, "")
AssertEqual lines[3], result
Execute (when lines have been joined, the hint contains both of the results seperated with a pipe):
silent grep! "Dupe!" test/grepable.txt
silent grepadd! "Dupe!" test/grepable.txt
EnMasse
normal jk
redir => messages
doautocmd CursorMoved
redir END
quit
let result = get(split(messages, "\n"), -1, "")
AssertEqual join([lines[5], lines[5]], " | "), result
================================================
FILE: test/preview.vader
================================================
Before (set up an EnMasse buffer):
let lines = readfile("test/grepable.txt", "b")
silent grep! quickfix test/grepable.txt
EnMasse
Execute (hitting enter on a line opens it in the preview window):
let before = getline(".")
execute "normal \<CR>\<C-W>k"
let after = getline(".")
let bufferName = expand("%")
AssertEqual "test/grepable.txt", bufferName
AssertEqual before, after
pclose
================================================
FILE: test/run
================================================
#!/usr/bin/env bash
if [[ ! -d test/vader ]]; then
git clone https://github.com/junegunn/vader.vim.git test/vader
else
pushd test/vader
git pull
popd
fi
vim -Nu <(cat << VIMRC
filetype off
set rtp=.
set rtp+=test/vader
filetype plugin indent on
VIMRC
) -c 'Vader! test/*.vader' > /dev/null
================================================
FILE: test/version.vader
================================================
Before (set up regular expression):
let versionRegExp = '\v\d+\.\d+\.\d+'
Execute (can print the version number with the command):
redir => messages
EnMasseVersion
redir END
let result = get(split(messages, "\n"), -1, "")
Assert result =~# versionRegExp
Execute (can get the version number with the function):
Assert enmasse#GetVersion() =~# versionRegExp
================================================
FILE: test/writing.vader
================================================
Before (define test data and setup EnMasse):
let original = ["EnMasse is cOoL", "Hello, World!", "This is EnMasse.", "EnMasse is useful."]
let expected = ["EnMasse is Cool", "Hello, World!", "This is EnMasse, a Vim plugin.", "EnMasse is handy."]
let filePath = tempname()
set nomodified
call writefile(original, filePath)
execute "silent grep! EnMasse " . filePath
EnMasse
After (remove the temporary file and close the previous EnMasse buffer):
call delete(filePath)
quit!
Execute (editing and writing in an EnMasse buffer changes the file):
%s/is EnMasse/is EnMasse, a Vim plugin/
%s/useful/handy/
%s/cOoL/Cool/
set ignorecase
write
set noignorecase
let actual = readfile(filePath)
AssertEqual expected, actual
Execute (will not let you write if a line is deleted):
normal dd
redir => messages
write
redir END
let actual = readfile(filePath)
let latestMessage = get(split(messages, "\n"), -1, "")
AssertEqual original, actual
AssertEqual "EnMasse: Mismatch between buffer lines and quickfix list. Refusing to write.", latestMessage
Execute (will not let you write if a line is added):
normal o
redir => messages
write
redir END
let actual = readfile(filePath)
let latestMessage = get(split(messages, "\n"), -1, "")
AssertEqual original, actual
AssertEqual "EnMasse: Mismatch between buffer lines and quickfix list. Refusing to write.", latestMessage
Execute (doesn't write if no lines have changed):
let before = getftime(filePath)
write
let after = getftime(filePath)
AssertEqual before, after
Execute (changing a file that you have open will prompt for a reload):
execute "split " . filePath
%s/is EnMasse/is EnMasse, a Vim plugin/
%s/useful/handy/
write
execute "normal! l\<CR>\<C-W>j"
let bufferLines = getline(1, "$")
let actual = readfile(filePath)
AssertEqual actual, bufferLines
Execute (multiple changes to one file are written in one batched write):
%s/is EnMasse/is EnMasse, a Vim plugin/
%s/useful/handy/
let writes = 0
let actual = readfile(filePath)
autocmd FileWritePost * let writes += 1
write
AssertEqual 1, writes
gitextract_gubi8ydj/
├── .gitignore
├── .travis.yml
├── README.md
├── UNLICENSE
├── autoload/
│ └── enmasse.vim
├── doc/
│ └── enmasse.txt
├── plugin/
│ └── enmasse.vim
└── test/
├── clearing.vader
├── enmasse.vader
├── grepable.txt
├── hints.vader
├── preview.vader
├── run
├── version.vader
└── writing.vader
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (20K chars).
[
{
"path": ".gitignore",
"chars": 19,
"preview": "doc/tags\ntest/vader"
},
{
"path": ".travis.yml",
"chars": 261,
"preview": "language: vim\n\nbefore_script: |\n git clone https://github.com/junegunn/vader.vim.git\n\nscript: |\n vim -Nu <(cat << VIMR"
},
{
"path": "README.md",
"chars": 2895,
"preview": "# En Masse [![Build Status][travis-image]][travis]\n\n[\n let list = s:GetQuickfixList()\n let sourceLines = s:GetSourceLinesFromList(list)\n\n if len(l"
},
{
"path": "doc/enmasse.txt",
"chars": 3742,
"preview": "*enmasse.txt*\n\n =======================================================\n ____ __ _ _ _"
},
{
"path": "plugin/enmasse.vim",
"chars": 328,
"preview": "command! EnMasse :call enmasse#Open()\ncommand! EnMasseVersion :echo enmasse#GetVersion()\n\naugroup EnMasseDefault\n autoc"
},
{
"path": "test/clearing.vader",
"chars": 250,
"preview": "Execute (the line counts are correct):\n silent grep! -i a test/grepable.txt\n EnMasse\n let before = line(\"$\")\n normal"
},
{
"path": "test/enmasse.vader",
"chars": 976,
"preview": "Before (read the example grepable file and grep for quickfix):\n let lines = readfile(\"test/grepable.txt\", \"b\")\n silent"
},
{
"path": "test/grepable.txt",
"chars": 490,
"preview": "This is an example file.\nIt should be loaded into the quickfix list.\nThen you can run :EnMasse\nAnd edit that quickfix li"
},
{
"path": "test/hints.vader",
"chars": 756,
"preview": "Before (read the example grepable file and grep for quickfix):\n let lines = readfile(\"test/grepable.txt\", \"b\")\n silent"
},
{
"path": "test/preview.vader",
"chars": 403,
"preview": "Before (set up an EnMasse buffer):\n let lines = readfile(\"test/grepable.txt\", \"b\")\n silent grep! quickfix test/grepabl"
},
{
"path": "test/run",
"chars": 307,
"preview": "#!/usr/bin/env bash\n\nif [[ ! -d test/vader ]]; then\n git clone https://github.com/junegunn/vader.vim.git test/vader\ne"
},
{
"path": "test/version.vader",
"chars": 373,
"preview": "Before (set up regular expression):\n let versionRegExp = '\\v\\d+\\.\\d+\\.\\d+'\n\nExecute (can print the version number with "
},
{
"path": "test/writing.vader",
"chars": 2145,
"preview": "Before (define test data and setup EnMasse):\n let original = [\"EnMasse is cOoL\", \"Hello, World!\", \"This is EnMasse.\", \""
}
]
About this extraction
This page contains the full source code of the Olical/vim-enmasse GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (17.8 KB), approximately 5.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.