tree-zip.nvim is a small Neovim plugin that adds zip and unzip actions to file explorers such as neo-tree.nvim and oil.nvim.
It lets you compress files and directories from your file explorer, and extract .zip files directly from Neovim.
- Compress one file or directory from Neo-tree or Oil.
- Compress multiple selected files/directories from Neo-tree visual selection.
- Compress multiple selected files/directories from Oil visual selection.
- Extract
.zipfiles from Neo-tree or Oil. - Extract one selected
.zipfile from Neo-tree or Oil visual selection. - Prompt for zip output name or path.
- Prompt for extraction destination folder name or path.
- Automatically appends
.zipwhen compressing. - Supports relative and absolute paths.
- Asks before replacing existing zip files or extraction folders.
- Shows notifications when operations start and finish.
- Automatically registers Oil keymaps from
setup(). - Automatically refreshes Oil after successful zip/unzip operations.
- Uses native tools:
- Windows:
Compress-Archive/Expand-Archive - Linux/macOS:
zip/unzip
- Windows:
- Neovim 0.10+
- At least one supported file explorer:
Platform requirements:
| Platform | Status | Required command |
|---|---|---|
| Windows | Supported | PowerShell tools |
| macOS | Supported | zip / unzip |
| Linux | Unverified | zip / unzip |
- Currently tested on Windows and macOS.
- Support for Linux is not fully verified.
- Linux/macOS require
zipandunzipto be installed.
{
"dfdezmonteiro/tree-zip.nvim",
lazy = false,
config = function()
require("tree-zip").setup()
end,
}With custom options:
{
"dfdezmonteiro/tree-zip.nvim",
lazy = false,
config = function()
require("tree-zip").setup({
overwrite = true,
notify = true,
integrations = {
oil = true,
refresh_oil = true,
},
keymaps = {
zip = "<leader>Z",
extract = "<leader>X",
},
})
end,
}Default configuration:
require("tree-zip").setup({
overwrite = true,
notify = true,
integrations = {
oil = true,
refresh_oil = true,
},
keymaps = {
zip = "<leader>Z",
extract = "<leader>X",
},
})Options:
| Option | Type | Default | Description |
|---|---|---|---|
overwrite |
boolean |
true |
Allows underlying zip/unzip commands to overwrite existing content after confirmation. |
notify |
boolean |
true |
Enables Neovim notifications. |
integrations.oil |
boolean |
true |
Automatically registers Oil buffer-local keymaps. |
integrations.refresh_oil |
boolean |
true |
Refreshes the active Oil view after successful zip/unzip operations. |
keymaps.zip |
string |
<leader>Z |
Keymap used to compress files/directories in Oil. |
keymaps.extract |
string |
<leader>X |
Keymap used to extract .zip files in Oil. |
To disable automatic Oil keymaps:
require("tree-zip").setup({
integrations = {
oil = false,
},
})To disable automatic Oil refresh:
require("tree-zip").setup({
integrations = {
refresh_oil = false,
},
})Neo-tree integration is configured manually because Neo-tree passes its internal state and visual selected_nodes through its own command system.
Add the custom commands and mappings to your Neo-tree configuration.
Example:
return {
"nvim-neo-tree/neo-tree.nvim",
opts = function(_, opts)
opts.filesystem = opts.filesystem or {}
opts.filesystem.commands = opts.filesystem.commands or {}
opts.filesystem.window = opts.filesystem.window or {}
opts.filesystem.window.mappings = opts.filesystem.window.mappings or {}
opts.filesystem.commands.zip_selected = function(state, selected_nodes)
if selected_nodes and #selected_nodes > 0 then
require("tree-zip").neo_tree_zip_visual(state, selected_nodes)
else
require("tree-zip").neo_tree_zip(state)
end
end
opts.filesystem.commands.extract_selected = function(state, selected_nodes)
if selected_nodes and #selected_nodes > 0 then
require("tree-zip").neo_tree_extract_visual(state, selected_nodes)
else
require("tree-zip").neo_tree_extract(state)
end
end
opts.filesystem.window.mappings["<leader>Z"] = "zip_selected"
opts.filesystem.window.mappings["<leader>X"] = "extract_selected"
return opts
end,
}Mappings in Neo-tree, if configured as above:
<leader>Z zip file/directory under cursor or visual selection
<leader>X extract selected .zip file
Neo-tree visual selection supports compressing multiple files/directories.
Neo-tree visual extraction currently supports one .zip file at a time.
Oil keymaps are registered automatically by setup().
Default mappings:
<leader>Z zip file/directory under cursor or visual selection
<leader>X extract selected .zip file
Normal mode:
<leader>Z zip file/directory under cursor
<leader>X extract .zip file under cursor
Visual mode:
<leader>Z zip selected files/directories
<leader>X extract selected .zip file
Oil visual selection supports compressing multiple selected files/directories.
Oil visual extraction currently supports one .zip file at a time.
Oil buffers are refreshed automatically after successful zip/unzip operations when integrations.refresh_oil is enabled.
Change the keymaps through setup():
require("tree-zip").setup({
keymaps = {
zip = "<leader>Z",
extract = "<leader>X",
},
})If you prefer to manage Oil keymaps yourself, disable the automatic integration:
require("tree-zip").setup({
integrations = {
oil = false,
},
})Then configure Oil manually:
require("oil").setup({
keymaps = {
["<leader>Z"] = {
desc = "Zip selected file/directory",
callback = function()
require("tree-zip").oil_zip()
end,
},
["<leader>X"] = {
desc = "Extract selected .zip",
callback = function()
require("tree-zip").oil_extract()
end,
},
},
})For visual mode, use buffer-local mappings:
vim.api.nvim_create_autocmd("FileType", {
pattern = "oil",
callback = function(event)
vim.keymap.set("x", "<leader>Z", function()
require("tree-zip").oil_zip_visual()
end, {
buffer = event.buf,
desc = "Zip Oil visual selection",
})
vim.keymap.set("x", "<leader>X", function()
require("tree-zip").oil_extract_visual()
end, {
buffer = event.buf,
desc = "Extract Oil visual selection",
})
end,
})In Neo-tree or Oil, place the cursor over a file or directory and press:
<leader>Z
You will be prompted:
Zip name or path:
Examples:
backup
creates:
backup.zip
backup.zip
creates:
backup.zip
dist/backup
creates:
dist/backup.zip
C:\Users\david\Desktop\backup
creates:
C:\Users\david\Desktop\backup.zip
The .zip extension is added automatically if omitted.
In Neo-tree:
Shift+V
j/k to expand the visual selection
<leader>Z
Then enter the zip name or path.
All selected files/directories are compressed into one .zip.
In Oil:
V
j/k to expand the visual selection
<leader>Z
Then enter the zip name or path.
All selected files/directories are compressed into one .zip.
The selected Oil entries are compressed relative to the current Oil directory, so the archive does not include unnecessary parent folders.
In Neo-tree or Oil, place the cursor over a .zip file and press:
<leader>X
You will be prompted:
Destination folder name or path:
If you leave it empty, the plugin extracts into a folder with the same name as the zip file.
Example:
archive.zip
empty destination:
Destination folder name or path:
extracts to:
archive/
If you enter:
test
it extracts to:
test/
If you enter:
../test
it extracts relative to the zip file location.
Absolute paths are also supported.
In Neo-tree or Oil visual selection, extraction currently supports one .zip file at a time.
If more than one .zip file is selected, the operation is cancelled.
If the target zip file already exists, the plugin asks:
Path already exists. Replace? (y/n):
If the extraction destination already exists, the plugin also asks:
Path already exists. Replace? (y/n):
Accepted answers:
y
yes
Any other answer cancels the operation.
Pressing Esc cancels the prompt.
Neo-tree cursor entry
→ <leader>Z
→ prompt for zip name/path
→ append .zip if needed
→ create parent output directory if needed
→ ask before replacing existing zip
→ create zip
Neo-tree visual selection
→ <leader>Z
→ prompt for zip name/path
→ append .zip if needed
→ create parent output directory if needed
→ ask before replacing existing zip
→ create zip with all selected files/directories
Oil cursor entry
→ <leader>Z
→ prompt for zip name/path
→ append .zip if needed
→ create parent output directory if needed
→ ask before replacing existing zip
→ create zip
→ refresh Oil view
Oil visual selection
→ <leader>Z
→ resolve selected Oil buffer lines to filesystem paths
→ use current Oil directory as archive root
→ prompt for zip name/path
→ append .zip if needed
→ create parent output directory if needed
→ ask before replacing existing zip
→ create zip with all selected files/directories
→ refresh Oil view
Neo-tree .zip cursor entry
→ <leader>X
→ prompt for destination folder/path
→ empty input uses default folder name
→ ask before extracting into existing folder
→ extract zip
Neo-tree visual selection
→ <leader>X
→ require one selected .zip file
→ prompt for destination folder/path
→ empty input uses default folder name
→ ask before extracting into existing folder
→ extract zip
Oil .zip cursor entry
→ <leader>X
→ prompt for destination folder/path
→ empty input uses default folder name
→ ask before extracting into existing folder
→ extract zip
→ refresh Oil view
Oil visual selection
→ <leader>X
→ require one selected .zip file
→ prompt for destination folder/path
→ empty input uses default folder name
→ ask before extracting into existing folder
→ extract zip
→ refresh Oil view
Generic API:
require("tree-zip").zip_paths(paths, opts)
require("tree-zip").extract_zip(zip_path)zip_paths() accepts a list of filesystem paths:
require("tree-zip").zip_paths({
"/path/to/file.txt",
"/path/to/directory",
})It also accepts an optional base_dir used as the archive root:
require("tree-zip").zip_paths({
"/project/src/a.lua",
"/project/src/b.lua",
}, {
base_dir = "/project/src",
})With base_dir, the archive stores:
a.lua
b.lua
instead of:
project/src/a.lua
project/src/b.lua
Neo-tree helpers:
require("tree-zip").neo_tree_zip(state)
require("tree-zip").neo_tree_zip_visual(state, selected_nodes)
require("tree-zip").neo_tree_extract(state)
require("tree-zip").neo_tree_extract_visual(state, selected_nodes)Oil helpers:
require("tree-zip").oil_zip()
require("tree-zip").oil_zip_visual()
require("tree-zip").oil_extract()
require("tree-zip").oil_extract_visual()Oil keymap registration:
require("tree-zip").register_oil_keymaps()tree-zip.nvim/
├── README.md
├── LICENSE
├── stylua.toml
└── lua/
└── tree-zip/
└── init.lua
- This plugin is currently focused on
.zipfiles only. - Neo-tree integration requires manual command/mapping registration.
- Oil keymaps are registered automatically by
setup()unless disabled. - Neo-tree visual selection supports compressing multiple files/directories.
- Oil visual selection supports compressing multiple files/directories.
- Visual extraction supports one
.zipfile at a time. - Linux/macOS require
zipandunzipto be installed. - Windows behavior depends on PowerShell archive commands.
- Linux/macOS support is not fully verified.
Possible future improvements:
- support for
.tar,.tar.gz,.7z - optional Neo-tree auto-registration
- progress indicator for long-running operations
- better error reporting from stderr
- support extracting multiple zip files
- support choosing destination through
vim.ui.select - healthcheck support
Contributions are welcome.
MIT