2026-05-29 18:35:10 -04:00
|
|
|
--- Neo-tree configuration with dynamic pattern filtering
|
|
|
|
|
--- @module plugins.neotree
|
|
|
|
|
|
|
|
|
|
-- Import shared modules
|
|
|
|
|
local ignore_patterns = require "config.ignore_patterns"
|
|
|
|
|
local terminals = require "config.terminals"
|
|
|
|
|
|
|
|
|
|
---@type LazySpec
|
|
|
|
|
return {
|
|
|
|
|
{
|
|
|
|
|
"neo-tree.nvim",
|
|
|
|
|
init = function()
|
|
|
|
|
vim.api.nvim_create_autocmd("DirChanged", {
|
|
|
|
|
group = vim.api.nvim_create_augroup("NeoTreeRefreshPatterns", { clear = true }),
|
|
|
|
|
callback = function()
|
|
|
|
|
local ok, manager = pcall(require, "neo-tree.sources.manager")
|
|
|
|
|
if ok then pcall(manager.refresh, "filesystem") end
|
|
|
|
|
end,
|
|
|
|
|
})
|
|
|
|
|
end,
|
|
|
|
|
opts = function()
|
|
|
|
|
-- Get all hide patterns from shared module
|
|
|
|
|
local hide_patterns = ignore_patterns.get_all_patterns()
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
|
|
popup_border_style = "rounded",
|
|
|
|
|
enable_git_status = true,
|
|
|
|
|
enable_diagnostics = true,
|
|
|
|
|
sort_case_insensitive = false,
|
|
|
|
|
auto_expand_width = true,
|
|
|
|
|
git_status_async = true, -- Asynchronous git status. Improves performance.
|
|
|
|
|
hide_root_node = true, -- Hide the root node.
|
|
|
|
|
retain_hidden_root_indent = true, -- IF the root node is hidden, keep the indentation anyhow.
|
2026-06-21 13:25:29 -04:00
|
|
|
use_libuv_file_watcher = true,
|
2026-05-29 18:35:10 -04:00
|
|
|
sort = {
|
|
|
|
|
sorter = "name",
|
|
|
|
|
},
|
|
|
|
|
window = {
|
|
|
|
|
position = "left",
|
2026-06-21 13:25:29 -04:00
|
|
|
width = 32,
|
2026-05-29 18:35:10 -04:00
|
|
|
mappings = {
|
|
|
|
|
["H"] = "navigate_up",
|
|
|
|
|
["L"] = function(state)
|
|
|
|
|
require("neo-tree.sources.filesystem.commands").set_root(state)
|
|
|
|
|
-- Refresh patterns when changing root
|
|
|
|
|
vim.defer_fn(function()
|
|
|
|
|
local new_patterns = ignore_patterns.get_all_patterns()
|
|
|
|
|
require("neo-tree").config.filesystem.filtered_items.hide_by_pattern = new_patterns
|
|
|
|
|
require("neo-tree.sources.manager").refresh "filesystem"
|
|
|
|
|
end, 50)
|
|
|
|
|
end,
|
|
|
|
|
["<bs>"] = "toggle_hidden",
|
|
|
|
|
["."] = "toggle_hidden",
|
|
|
|
|
["/"] = "fuzzy_finder",
|
|
|
|
|
["f"] = "filter_on_submit",
|
|
|
|
|
["<c-x>"] = "clear_filter",
|
|
|
|
|
["<CR>"] = "open_and_close_neotree",
|
|
|
|
|
["<S-CR>"] = "open",
|
|
|
|
|
-- Claude Code: add file under cursor to context
|
|
|
|
|
["<C-a>"] = function() vim.cmd "ClaudeCodeTreeAdd" end,
|
2026-06-28 13:54:52 -04:00
|
|
|
-- Preview image under cursor via kitty's icat kitten
|
|
|
|
|
["<leader>p"] = "image_kitty",
|
2026-05-29 18:35:10 -04:00
|
|
|
-- Git keybindings from astrocore
|
|
|
|
|
["<C-g>o"] = function() vim.cmd "!gh repo view --web" end,
|
|
|
|
|
["<C-g>h"] = function()
|
|
|
|
|
require("snacks").terminal("gh dash", {
|
|
|
|
|
hidden = true,
|
|
|
|
|
auto_close = false,
|
|
|
|
|
interactive = true,
|
|
|
|
|
})
|
|
|
|
|
end,
|
|
|
|
|
["<C-g>g"] = function()
|
|
|
|
|
require("snacks").terminal("lazygit", {
|
|
|
|
|
hidden = true,
|
|
|
|
|
auto_close = true,
|
|
|
|
|
interactive = true,
|
|
|
|
|
})
|
|
|
|
|
end,
|
|
|
|
|
["<C-g>d"] = function() require("snacks").picker.git_diff() end,
|
|
|
|
|
["<C-g>b"] = function() require("snacks").picker.git_branches() end,
|
|
|
|
|
-- Terminal keybindings from astrocore
|
|
|
|
|
["<C-t>m"] = terminals.mk_toggle,
|
|
|
|
|
["<C-t>l"] = function()
|
|
|
|
|
require("snacks").terminal("lazydocker", {
|
|
|
|
|
hidden = true,
|
|
|
|
|
auto_close = true,
|
|
|
|
|
start_in_insert = true,
|
|
|
|
|
interactive = true,
|
|
|
|
|
})
|
|
|
|
|
end,
|
|
|
|
|
["<C-t>du"] = function()
|
|
|
|
|
require("snacks").terminal("devbox services up", {
|
|
|
|
|
hidden = true,
|
|
|
|
|
auto_close = false,
|
|
|
|
|
start_in_insert = true,
|
|
|
|
|
interactive = true,
|
|
|
|
|
})
|
|
|
|
|
end,
|
|
|
|
|
["<C-t>dd"] = function()
|
|
|
|
|
require("snacks").terminal("devbox services down", {
|
|
|
|
|
hidden = true,
|
|
|
|
|
auto_close = false,
|
|
|
|
|
start_in_insert = true,
|
|
|
|
|
interactive = true,
|
|
|
|
|
})
|
|
|
|
|
end,
|
|
|
|
|
["<C-t>da"] = function()
|
|
|
|
|
require("snacks").terminal("devbox services attach", {
|
|
|
|
|
hidden = true,
|
|
|
|
|
auto_close = false,
|
|
|
|
|
start_in_insert = true,
|
|
|
|
|
interactive = true,
|
|
|
|
|
})
|
|
|
|
|
end,
|
|
|
|
|
["<C-t>."] = terminals.yazi_toggle,
|
|
|
|
|
["<C-t>t"] = function() require("snacks").terminal() end,
|
|
|
|
|
-- Find keybindings from astrocore
|
|
|
|
|
-- These Find keybindings are defined in astrocore.lua
|
|
|
|
|
-- NeoTree only needs the basic navigation and terminal bindings
|
|
|
|
|
-- Mappings for Mason and formatting
|
|
|
|
|
["N"] = function(state)
|
|
|
|
|
local node = state.tree:get_node()
|
|
|
|
|
local path = node.type == "directory" and node.path or vim.fn.fnamemodify(node.path, ":h")
|
|
|
|
|
|
|
|
|
|
-- Check if running on Arch Linux
|
|
|
|
|
local is_arch = vim.fn.filereadable "/etc/arch-release" == 1
|
|
|
|
|
|
|
|
|
|
if is_arch then
|
|
|
|
|
-- Use uwsm to launch alacritty with yazi on Arch Linux
|
|
|
|
|
vim.fn.jobstart(
|
|
|
|
|
string.format("uwsm app -- alacritty --working-directory %s -e yazi", vim.fn.shellescape(path)),
|
|
|
|
|
{
|
|
|
|
|
detach = true,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
else
|
|
|
|
|
-- Fallback to direct alacritty launch with yazi on other systems
|
|
|
|
|
vim.fn.jobstart(string.format("alacritty --working-directory %s -e yazi", vim.fn.shellescape(path)), {
|
|
|
|
|
detach = true,
|
|
|
|
|
})
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
header = {
|
|
|
|
|
highlight = "NeoTreeHeader",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
default_component_configs = {
|
|
|
|
|
container = {
|
|
|
|
|
enable_character_fade = true,
|
|
|
|
|
},
|
|
|
|
|
indent = {
|
|
|
|
|
indent_size = 2,
|
|
|
|
|
padding = 1, -- extra padding on left hand side
|
|
|
|
|
-- indent guides
|
|
|
|
|
with_markers = true,
|
|
|
|
|
indent_marker = "│",
|
|
|
|
|
last_indent_marker = "└",
|
|
|
|
|
highlight = "NeoTreeIndentMarker",
|
|
|
|
|
-- expander config, needed for nesting files
|
|
|
|
|
expander_collapsed = "",
|
|
|
|
|
expander_expanded = "",
|
|
|
|
|
expander_highlight = "NeoTreeExpander",
|
|
|
|
|
},
|
|
|
|
|
name = {
|
|
|
|
|
trailing_slash = false, -- append a trailing slash to folder names
|
|
|
|
|
use_git_status_colors = true, -- use git status colors instead of the default colors
|
|
|
|
|
highlight = "NeoTreeFileName",
|
|
|
|
|
},
|
|
|
|
|
git_status = {
|
|
|
|
|
symbols = {
|
|
|
|
|
-- Change type
|
|
|
|
|
added = "", -- or "✚", but this is redundant info if you use git_status_colors on the name
|
|
|
|
|
modified = "", -- or "", but this is redundant info if you use git_status_colors on the name
|
|
|
|
|
deleted = "✖", -- this can only be used in the git_status source
|
|
|
|
|
renamed = "", -- this can only be used in the git_status source
|
|
|
|
|
-- Status type
|
|
|
|
|
untracked = "",
|
|
|
|
|
ignored = "",
|
|
|
|
|
unstaged = "",
|
|
|
|
|
staged = "",
|
|
|
|
|
conflict = "",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
-- If you don't want to use these columns, you can set `enabled = false` for each of them individually
|
|
|
|
|
file_size = {
|
|
|
|
|
enabled = true,
|
|
|
|
|
width = 12, -- width of the column
|
|
|
|
|
required_width = 48, -- min width of window required to show this column
|
|
|
|
|
},
|
|
|
|
|
type = {
|
|
|
|
|
enabled = true,
|
|
|
|
|
width = 10, -- width of the column
|
|
|
|
|
required_width = 122, -- min width of window required to show this column
|
|
|
|
|
},
|
|
|
|
|
last_modified = {
|
|
|
|
|
enabled = true,
|
|
|
|
|
width = 20, -- width of the column
|
|
|
|
|
required_width = 88, -- min width of window required to show this column
|
|
|
|
|
},
|
|
|
|
|
created = {
|
|
|
|
|
enabled = true,
|
|
|
|
|
width = 20, -- width of the column
|
|
|
|
|
required_width = 110, -- min width of window required to show this column
|
|
|
|
|
},
|
|
|
|
|
symlink_target = {
|
|
|
|
|
enabled = false,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
buffers = {
|
|
|
|
|
follow_current_file = {
|
|
|
|
|
enabled = true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
-- Filesystem specific settings
|
|
|
|
|
filesystem = {
|
|
|
|
|
commands = {
|
|
|
|
|
-- Refresh patterns when changing directories
|
|
|
|
|
refresh_patterns = function(state)
|
|
|
|
|
-- Re-read patterns when navigating
|
|
|
|
|
local new_patterns = ignore_patterns.get_all_patterns()
|
|
|
|
|
state.filtered_items.hide_by_pattern = new_patterns
|
|
|
|
|
require("neo-tree.sources.manager").refresh "filesystem"
|
|
|
|
|
end,
|
|
|
|
|
-- If item is a file it will close neotree after opening it.
|
|
|
|
|
open_and_close_neotree = function(state)
|
|
|
|
|
require("neo-tree.sources.filesystem.commands").open(state)
|
|
|
|
|
local tree = state.tree
|
|
|
|
|
local success, node = pcall(tree.get_node, tree)
|
|
|
|
|
if not success then return end
|
|
|
|
|
if node.type == "file" then require("neo-tree.command").execute { action = "close" } end
|
|
|
|
|
end,
|
|
|
|
|
|
2026-06-28 13:54:52 -04:00
|
|
|
-- Preview an image with kitty's icat kitten in a snacks terminal
|
|
|
|
|
image_kitty = function(state)
|
|
|
|
|
local node = state.tree:get_node()
|
|
|
|
|
if node and node.type == "file" then
|
|
|
|
|
require("snacks").terminal("kitten icat --hold " .. vim.fn.shellescape(node.path), {
|
|
|
|
|
hidden = true,
|
|
|
|
|
auto_close = false,
|
|
|
|
|
interactive = true,
|
|
|
|
|
})
|
|
|
|
|
else
|
|
|
|
|
vim.notify("Not a file", vim.log.levels.WARN, { title = "Neo-tree" })
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
|
2026-05-29 18:35:10 -04:00
|
|
|
-- Custom command to open Mason
|
|
|
|
|
mason_open = function() vim.cmd "Mason" end,
|
|
|
|
|
|
|
|
|
|
-- Custom command to format a file using the LSP
|
|
|
|
|
format_file = function(state)
|
|
|
|
|
local node = state.tree:get_node()
|
|
|
|
|
if node and node.type == "file" then
|
|
|
|
|
-- Format the file in a temporary buffer without opening it in a window
|
|
|
|
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
|
|
|
|
vim.api.nvim_buf_set_name(bufnr, node.path)
|
|
|
|
|
vim.api.nvim_buf_load(bufnr)
|
|
|
|
|
vim.lsp.buf.format { bufnr = bufnr, async = false, timeout_ms = 5000 }
|
|
|
|
|
vim.api.nvim_buf_call(bufnr, function() vim.cmd "write" end)
|
|
|
|
|
vim.notify("Formatted " .. vim.fs.basename(node.path), vim.log.levels.INFO, { title = "Neo-tree" })
|
|
|
|
|
else
|
|
|
|
|
vim.notify("Not a file", vim.log.levels.WARN, { title = "Neo-tree" })
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
bind_to_cwd = true,
|
|
|
|
|
follow_current_file = {
|
|
|
|
|
enabled = true,
|
|
|
|
|
leave_dirs_open = false,
|
|
|
|
|
},
|
|
|
|
|
filtered_items = {
|
|
|
|
|
force_visible_in_empty_folder = false,
|
|
|
|
|
group_empty = false,
|
|
|
|
|
show_hidden_count = false,
|
|
|
|
|
hide_dotfiles = false,
|
|
|
|
|
hide_gitignored = true,
|
|
|
|
|
hide_by_pattern = hide_patterns,
|
|
|
|
|
always_show = {},
|
|
|
|
|
always_show_by_pattern = {},
|
|
|
|
|
visible = function(item)
|
|
|
|
|
-- Hide all symlinks
|
|
|
|
|
if item.type == "link" then return false end
|
|
|
|
|
return true
|
|
|
|
|
end,
|
|
|
|
|
hide_by_name = {
|
|
|
|
|
-- "contracts",
|
|
|
|
|
-- "crypto",
|
|
|
|
|
-- "CONSTITUTION.md",
|
|
|
|
|
-- "CONTRIBUTING.md",
|
|
|
|
|
-- "api",
|
|
|
|
|
-- "turbo.json",
|
|
|
|
|
-- "OpenCode.md",
|
|
|
|
|
-- "AGENT.md",
|
|
|
|
|
-- "AGENTS.md",
|
|
|
|
|
-- "svgo.config.mjs",
|
|
|
|
|
-- ".prettierrc.cjs",
|
|
|
|
|
-- ".eslintrc.cjs",
|
|
|
|
|
-- "tsconfig.json",
|
|
|
|
|
-- "tsconfig.prod.json",
|
|
|
|
|
-- ".prettierignore",
|
|
|
|
|
-- "CONVENTIONS.md",
|
|
|
|
|
-- ".gitkeep",
|
|
|
|
|
-- ".source",
|
|
|
|
|
-- "settings.json",
|
|
|
|
|
-- ".gitignore",
|
|
|
|
|
-- "analysis_options.yaml",
|
|
|
|
|
-- "slumber.yml",
|
|
|
|
|
-- ".eslintignore",
|
|
|
|
|
-- "CONTRIBUTING.md",
|
|
|
|
|
-- "CHANGELOG.md",
|
|
|
|
|
-- "devbox.d",
|
|
|
|
|
-- "env",
|
|
|
|
|
-- ".husky",
|
|
|
|
|
-- "translations",
|
|
|
|
|
-- "LICENSE.md",
|
|
|
|
|
-- ".envrc",
|
|
|
|
|
-- "CLAUDE.md",
|
|
|
|
|
-- "chains",
|
|
|
|
|
-- "test",
|
|
|
|
|
-- ".golangci.yml",
|
|
|
|
|
-- "go.mod",
|
|
|
|
|
-- "pnpm-workspace.yaml",
|
|
|
|
|
-- "examples",
|
|
|
|
|
-- "bridge",
|
|
|
|
|
-- "client",
|
|
|
|
|
},
|
|
|
|
|
never_show_by_pattern = ignore_patterns.never_show_patterns,
|
|
|
|
|
never_show = ignore_patterns.never_show,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
diagnostics = {
|
|
|
|
|
enable = false,
|
|
|
|
|
icons = {
|
|
|
|
|
hint = "",
|
|
|
|
|
info = "",
|
|
|
|
|
warning = "",
|
|
|
|
|
error = "",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
source_selector = {
|
|
|
|
|
winbar = false,
|
|
|
|
|
statusline = false,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
-- Main sources
|
|
|
|
|
sources = {
|
|
|
|
|
"filesystem",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
}
|