-- Function to read patterns from .rgignore file local function read_rgignore_patterns() local patterns = {} local git_root = vim.fn.systemlist("git rev-parse --show-toplevel")[1] if git_root and git_root ~= "" then local rgignore_path = git_root .. "/.rgignore" local file = io.open(rgignore_path, "r") if file then for line in file:lines() do -- Skip comments and empty lines if line ~= "" and not line:match "^#" then -- Remove leading ./ if present line = line:gsub("^%./", "") -- Add the pattern table.insert(patterns, line) end end file:close() end end return patterns end -- Build exclude patterns for snacks.explorer local function build_exclude_patterns() -- Get patterns from .rgignore local rgignore_patterns = read_rgignore_patterns() -- Default hide patterns (from original neotree config) local hide_patterns = { "chain_*.json", "Library", ".typecopy", ".python-version", "*.pb.go", "*config.*.js", "*config.js", "deps.mjs", ".parcelrc", "worker-configuration.d.ts", "wrangler.jsonc", "*.pkl.go", ".trunk", ".config", ".cz.toml", "*_mock.go", } -- Never show by pattern (from original neotree config) local never_show_by_pattern = { "*DS_Store", ".obsidian", ".pkl-lsp", ".tsbuildinfo", "package-lock.json", ".prettierrc", "node_modules", ".DocumentRevisions-V100", ".Spotlight-V100", ".TemporaryItems", ".Trashes", ".fseventsd", ".editorconfig", "*.min.js", "*.lock", "*lock*", "*.lockb", "*.pulsar.go", "*.pb.gorm.go", "*.pb.gw.go", "*_templ.go", "*.tmp", "*.work.*", "*.sum", ".parcel-cache", "*.icns", "*.ico", ".aider.tags.cache.v4", "*.iml", "Icon?", "iCloud~", "com~", "*.icns", ".conform*", ".null-ls_*", } -- Never show directories (from original neotree config) local never_show = { ".git", "pnpm-lock.yaml", ".next", ".task", ".devbox", ".dart_tool", ".idea", ".metadata", ".venv", ".gradle", "gradle.bat", ".aider.chat.history.md", ".aider.input.history", ".aider.tags.cache.v3", ".devcontainer", "heighliner", ".tmp", "go.work.sum", ".DS_Store", ".git", "LICENSE", "tmp", "sonr.log", "DISCUSSION_TEMPLATE", "ISSUE_TEMPLATE", ".devbox", ".timemachine", "junit.xml", ".jj", ".spawn", ".turbo", } -- Merge all patterns local all_patterns = {} -- Add default hide patterns for _, pattern in ipairs(hide_patterns) do table.insert(all_patterns, pattern) end -- Add rgignore patterns for _, pattern in ipairs(rgignore_patterns) do table.insert(all_patterns, pattern) end -- Add never show by pattern for _, pattern in ipairs(never_show_by_pattern) do table.insert(all_patterns, pattern) end -- Add never show directories for _, pattern in ipairs(never_show) do table.insert(all_patterns, pattern) end return all_patterns end -- Custom transform function to filter items local function create_transform_filter() local exclude_patterns = build_exclude_patterns() return function(item, ctx) if not item then return false end local name = item.name or (item.file and vim.fn.fnamemodify(item.file, ":t")) or "" local path = item.file or "" -- Check each pattern for _, pattern in ipairs(exclude_patterns) do -- Convert glob pattern to Lua pattern local lua_pattern = pattern :gsub("([%.%+%-%[%]%(%)%$%^%%%?])", "%%%1") -- Escape special chars except * :gsub("%*%*", ".*") -- ** matches any characters including / :gsub("%*", "[^/]*") -- * matches any characters except / :gsub("%?", ".") -- ? matches single character -- Check against filename if name:match("^" .. lua_pattern .. "$") then return false -- Filter out this item end -- Check against full path if path:match("/" .. lua_pattern .. "$") or path:match("/" .. lua_pattern .. "/") then return false -- Filter out this item end end return item -- Keep the item end end return { { "folke/snacks.nvim", priority = 1000, lazy = false, ---@type snacks.Config opts = function() return { -- Enable explorer and replace netrw explorer = { replace_netrw = true, }, -- Dashboard configuration dashboard = { preset = { header = table.concat({ "", "", " ████████ ", " ██████████████ ", " ████████████████ ", " ██████████ █████ ", " ██████████ █ ", " ██████████ █████ ", " █████████ █████████ ", " █████████ ████ █████████ ", " ████████ ████████ ████████ ", "███████ ████████████ ███████", "███████ ██████████████ ██████", "███████ ████████████ ███████", " ████████ ████████ ████████ ", " █████████ ████ █████████ ", " ████████ ██████████ ", " ██████ ██████████ ", " ██ █████████ ", " ████ ██████████ ", " ████████████████ ", " ██████████████ ", " ████████ ", "", "", }, "\n"), }, sections = { { section = "header" }, { pane = 2, section = "terminal", cmd = "colorscript -e square", height = 5, padding = 1, }, { section = "keys", gap = 1, padding = 1 }, { pane = 2, icon = " ", desc = "Browse Repo", padding = 1, key = "b", action = function() require("snacks").gitbrowse() end, }, function() local in_git = require("snacks").git.get_root() ~= nil local cmds = { { title = "Notifications", cmd = "gh notify -s -a -n5", action = function() vim.ui.open "https://github.com/notifications" end, key = "n", icon = " ", height = 5, enabled = true, }, { title = "Open Issues", cmd = "gh issue list -L 3", key = "i", action = function() vim.fn.jobstart("gh issue list --web", { detach = true }) end, icon = " ", height = 7, }, { icon = " ", title = "Open PRs", cmd = "gh pr list -L 3", key = "P", action = function() vim.fn.jobstart("gh pr list --web", { detach = true }) end, height = 7, }, { icon = " ", title = "Git Status", cmd = "git --no-pager diff --stat -B -M -C", height = 10, }, } return vim.tbl_map( function(cmd) return vim.tbl_extend("force", { pane = 2, section = "terminal", enabled = in_git, padding = 1, ttl = 5 * 60, indent = 3, }, cmd) end, cmds ) end, { section = "startup" }, }, }, -- Configure the explorer picker source picker = { sources = { explorer = { -- Explorer configuration matching neotree tree = true, hidden = true, -- Show hidden files by default (. files) ignored = false, -- Don't show gitignored files follow_file = true, git_status = true, git_untracked = true, diagnostics = true, watch = true, -- Use transform to filter out unwanted files/directories transform = create_transform_filter(), focus = "list", auto_close = false, jump = { close = true }, -- Close explorer when jumping to a file layout = { preset = "sidebar", preview = false, layout = { width = 34, }, }, formatters = { file = { filename_only = true }, }, matcher = { sort_empty = false, fuzzy = false }, win = { list = { keys = { -- Navigation (matching neotree) ["H"] = "explorer_up", -- Navigate to parent directory ["L"] = "explorer_focus", -- Open file/expand directory (with auto-close for files) [""] = "confirm", -- Open file/expand directory (with auto-close for files) [""] = "confirm", -- Open without closing [""] = "explorer_close_all", -- Collapse all folders ["!"] = "toggle_ignored", -- Toggle ignored files ["."] = "toggle_hidden", -- Toggle hidden files ["/"] = function(self) -- Focus search input self:focus "input" end, ["f"] = function(self) -- Focus search input (filter) self:focus "input" end, [""] = function(self) -- Clear filter self.input:set "" end, ["s"] = "edit_vsplit", -- Open in vertical split ["S"] = "edit_split", -- Open in horizontal split ["t"] = "tab", -- Open in new tab -- File operations ["a"] = "explorer_add", -- Add file ["d"] = "explorer_del", -- Delete ["r"] = "explorer_rename", -- Rename ["y"] = "explorer_yank", -- Copy ["x"] = "explorer_move", -- Cut/Move ["p"] = "explorer_paste", -- Paste ["c"] = "explorer_yank", -- Copy to clipboard ["R"] = "explorer_update", -- Refresh -- Git keybindings from neotree ["o"] = function() vim.cmd "!gh repo view --web" end, ["h"] = function() require("snacks").terminal("gh dash", { hidden = true, auto_close = false, interactive = true, }) end, ["g"] = function() require("snacks").terminal("lazygit", { hidden = true, auto_close = true, interactive = true, }) end, ["d"] = function() require("snacks").picker.git_diff() end, ["b"] = function() require("snacks").picker.git_branches() end, -- Terminal keybindings from neotree ["m"] = function() _G.Mk_toggle() end, ["j"] = function() _G.Lazyjournal_toggle() end, ["l"] = function() require("snacks").terminal("lazydocker", { hidden = true, auto_close = true, start_in_insert = true, interactive = true, }) end, ["du"] = function() require("snacks").terminal("devbox services up", { hidden = true, auto_close = false, start_in_insert = true, interactive = true, }) end, ["dd"] = function() require("snacks").terminal("devbox services down", { hidden = true, auto_close = false, start_in_insert = true, interactive = true, }) end, ["da"] = function() require("snacks").terminal("devbox services attach", { hidden = true, auto_close = false, start_in_insert = true, interactive = true, }) end, ["."] = function() _G.Yazi_toggle() end, ["t"] = function() require("snacks").terminal() end, -- Additional navigation ["h"] = "explorer_close", -- Close directory ["l"] = "confirm", -- Open/expand ["o"] = "explorer_open", -- Open with system application ["P"] = "toggle_preview", ["I"] = "toggle_ignored", ["Z"] = "explorer_close_all", ["]g"] = "explorer_git_next", ["[g"] = "explorer_git_prev", ["]d"] = "explorer_diagnostic_next", ["[d"] = "explorer_diagnostic_prev", }, }, }, }, }, }, } end, }, }