A modern, lightweight plugin manager for Neovim with a beautiful UI and intelligent dependency management.
đź“– Full Documentation: See
:help synapseor doc/synapse.txt for complete documentation.
- 📦 Configuration-based Management: Manage plugins through simple Lua configuration files
- đź”— Automatic Dependency Resolution: Automatically install, update, and protect plugin dependencies
- 🌿 Branch & Tag Support: Clone specific branches or lock to tag versions
- 🎨 Beautiful UI: Real-time progress display with customizable ASCII art headers
- ⚡ Smart Updates: Check for updates before applying them
- đź§ą Auto Cleanup: Remove unused plugins automatically
- đź”§ Post-Install Commands: Execute build commands after installation/update
Clone the repository to Neovim's default plugin directory:
# Get Neovim data directory and clone
git clone https://github.com/OriginCoderPulse/synapse.nvim \
"$(nvim --cmd 'echo stdpath("data")' --cmd 'qa')/site/pack/packer/start/synapse.nvim"Or manually:
# Unix/Linux/macOS
git clone https://github.com/OriginCoderPulse/synapse.nvim \
~/.local/share/nvim/site/pack/packer/start/synapse.nvim
# Windows
git clone https://github.com/OriginCoderPulse/synapse.nvim \
%LOCALAPPDATA%\nvim-data\site\pack\packer\start\synapse.nvimAdd the following to your init.lua:
-- Add synapse.nvim to runtimepath (if using custom location)
vim.opt.runtimepath:prepend(os.getenv("HOME") .. "/.nvim-utils/package/synapse.nvim")
-- Setup synapse.nvim
require("synapse").setup()Note: If you cloned to Neovim's default plugin directory (~/.local/share/nvim/site/pack/packer/start/), you don't need to set runtimepath manually.
Commands:
:SynapseDownload- Install missing plugins:SynapseUpgrade- Update all plugins:SynapseRemove- Remove unused plugins:SynapseError- View error messages from failed operations
Default Keymaps:
<leader>si- Install plugins (:SynapseDownload)<leader>sr- Remove unused plugins (:SynapseRemove)<leader>su- Update plugins (:SynapseUpgrade)
You can specify a custom directory for plugin installation:
require("synapse").setup({
opts = {
package_path = os.getenv("HOME") .. "/.nvim-utils/package",
-- ... other options
},
})Important: If using a custom package_path, you must also add it to runtimepath:
-- Add custom package directory to runtimepath
vim.opt.runtimepath:append(os.getenv("HOME") .. "/.nvim-utils/package/*")
vim.opt.runtimepath:append(os.getenv("HOME") .. "/.nvim-utils/package/*/after")
require("synapse").setup({
opts = {
package_path = os.getenv("HOME") .. "/.nvim-utils/package",
},
})Files ending with .config.lua in the specified directory will be automatically loaded:
require("synapse").setup({
opts = {
-- Directory to scan for .config.lua files (recursive)
load_config = vim.fn.stdpath("config") .. "/lua",
-- Directory to scan for plugin installation configs (.lua files)
config_path = vim.fn.stdpath("config") .. "/lua/plugins",
},
})Example: Create ~/.config/nvim/lua/plugins/example.config.lua:
-- Method 1: config as table (automatically calls plugin.setup(config))
return {
repo = "username/plugin-name",
config = {
-- Configuration options will be passed to plugin.setup()
option1 = "value1",
option2 = "value2",
},
}
-- Method 2: config as function (manual setup)
return {
repo = "username/plugin-name",
config = function()
local status, plugin = pcall(require, "plugin-name")
if not status then
vim.notify("plugin-name is not found ...", vim.log.levels.ERROR, { title = "Nvim" })
return
end
plugin.setup({
-- Your configuration
})
end,
}Note: When config is a table, Synapse will automatically:
- Extract the plugin name from the
repofield (or useprimaryif specified) - Try to
requirethe plugin - Call
plugin.setup(config_table)if the plugin has asetupfunction
primary field: Specify the actual require name if it differs from the extracted name:
return {
repo = "username/plugin-name",
primary = "custom-plugin-name", -- Use this as require name
config = {
option1 = "value1",
},
}initialization field: Execute a function before plugin setup. The function receives a package wrapper that allows accessing plugin submodules:
return {
repo = "username/plugin-name",
initialization = function(package)
-- package is a wrapper that allows accessing plugin submodules
-- Access submodules using: package({ "submodule", "path" })
-- Or using method calls: package.submodule()
-- This runs before plugin.setup() is called
end,
config = {
option1 = "value1",
},
}Create .lua files in your config_path directory to define which plugins to install:
Basic Format (config_path/example.lua):
return {
-- Repository URL (required)
repo = "username/plugin-name",
-- Dependencies (optional)
depend = {
-- String format (simple dependency)
"username/dependency-plugin",
-- Table format with opt configuration
{
"username/another-dependency",
opt = {
-- Configuration options for the dependency
option1 = "value1",
option2 = "value2",
}
},
-- Table format with primary and opt (same level)
{
"username/third-dependency",
primary = "custom-dep-name", -- Specify require name
opt = {
-- Configuration options for the dependency
option1 = "value1",
option2 = "value2",
}
},
},
-- Tag version (optional, takes precedence over branch)
tag = "v1.0.0",
-- Branch (optional, only if no tag specified)
clone_conf = {
branch = "main",
},
-- Post-install/update commands (optional)
execute = {
"make",
"cargo build --release",
}, -- Or use a single string: execute = "make"
-- Primary plugin name (optional)
-- Use this if the require name differs from the extracted name
primary = "custom-plugin-name",
-- Plugin configuration (optional)
-- Method 1: config as table (automatically calls plugin.setup(config))
config = {
option1 = "value1",
option2 = "value2",
},
-- Method 2: config as function (manual setup)
-- config = function()
-- require("plugin-name").setup({})
-- end,
-- Initialization function (optional)
-- Executed before plugin.setup() is called
-- initialization = function(package)
-- -- package is a wrapper for accessing plugin submodules
-- end,
}Examples:
-- config_path/mason.lua
return {
repo = "williamboman/mason.nvim",
depend = {
{
"williamboman/mason-lspconfig.nvim",
opt = {
ensure_installed = { "lua_ls", "pyright" },
automatic_installation = true,
}
},
},
}-- config_path/lualine.lua (using table config)
return {
repo = "nvim-lualine/lualine.nvim",
config = {
options = {
theme = "auto",
icons_enabled = true,
},
},
}-- config_path/versioned.lua
return {
repo = "username/plugin-name",
tag = "v1.2.3", -- Lock to specific tag version
}-- config_path/compiled.lua
return {
repo = "username/compiled-plugin",
execute = {
"make",
"cargo build --release",
},
}-- config_path/custom-name.lua (using primary field)
return {
repo = "username/plugin-name",
primary = "custom-plugin-name", -- Use this as require name
config = {
option1 = "value1",
},
}-- config_path/with-init.lua (using initialization function)
return {
repo = "username/plugin-name",
initialization = function(package)
-- Access plugin submodules before setup
local install = package({ "install" })
-- Or: local install = package.install()
end,
config = {
option1 = "value1",
},
}-- config_path/mason.lua (dependency with primary field)
return {
repo = "williamboman/mason.nvim",
depend = {
{
"williamboman/mason-lspconfig.nvim",
primary = "mason-lspconfig", -- Specify require name
opt = {
ensure_installed = { "lua_ls", "pyright" },
automatic_installation = true,
}
},
},
}You can customize the keymaps:
require("synapse").setup({
keys = {
download = "<leader>i", -- Install plugins
remove = "<leader>r", -- Remove unused plugins
upgrade = "<leader>u", -- Update plugins
},
})-- Add synapse.nvim to runtimepath (if using custom location)
vim.opt.runtimepath:prepend(os.getenv("HOME") .. "/.nvim-utils/package/synapse.nvim")
require("synapse").setup({
-- Git clone method: "ssh" or "https"
method = "https",
opts = {
-- Custom plugin installation directory
package_path = os.getenv("HOME") .. "/.nvim-utils/package",
-- Directory for plugin installation configs (.lua files)
config_path = os.getenv("HOME") .. "/.config/nvim/lua/plugins",
-- Directory for plugin load configs (.config.lua files, auto-loaded)
load_config = os.getenv("HOME") .. "/.config/nvim/lua",
-- UI customization
ui = {
style = "float",
},
},
-- Custom keymaps
keys = {
download = "<leader>i",
remove = "<leader>r",
upgrade = "<leader>u",
},
})Note: If using custom package_path, don't forget to add it to runtimepath:
vim.opt.runtimepath:append(os.getenv("HOME") .. "/.nvim-utils/package/*")
vim.opt.runtimepath:append(os.getenv("HOME") .. "/.nvim-utils/package/*/after")Synapse automatically handles plugin dependencies:
- Auto-install: Dependencies are installed automatically
- Deduplication: Shared dependencies are installed only once
- Priority: If a dependency is also a main plugin, its configuration takes precedence
- Protection: Dependencies are protected during removal unless unused
- Configuration with
opt: Dependencies can be configured using theoptfield
Loading Order:
- All
.config.luafiles are loaded first (main plugins are set up) - Then dependencies with
optare configured (ensuring proper initialization order)
This ensures that if plugin-a depends on plugin-b, and both have configurations, plugin-b will be set up before plugin-a's dependency configuration is applied.
Plugin Name Resolution:
Synapse automatically extracts plugin names from the repo field, but you can override this using the primary field. The resolution order is:
primaryfield (if specified) - highest priority- Extract from
repofield (e.g., "user/plugin-name" -> "plugin-name") - Extract from module name (e.g., "pkgs.plugin.config" -> "plugin")
- Extract from file path
If primary is not specified, Synapse will try multiple variations when requiring the plugin:
- Original extracted name
- Lowercase version (if contains uppercase)
- Without "-nvim" or ".nvim" suffix
This helps handle different plugin naming conventions automatically.
- Ensure installation configuration files (
.lua) are inconfig_path(supports subdirectories) - Check that files return a table with a
repofield - Verify
repofield is not empty
- Ensure
leaderkey is set beforesynapse.setup()is called - Check that keymaps are not overridden by other configurations
- Check
dependfield format (array of strings or tables) - Verify repository URLs are correct
- Check network connectivity
MIT License