canola.nvim/doc/recipes.md
Barrett Ruth eed6697ce2 docs: clarify get_current_dir nil return and add telescope recipe
Problem: get_current_dir returns nil in several cases that were not
documented, causing confusion when used in keymaps that open pickers
like Telescope (#682). Also, posix_to_os_path could crash on Windows
when no drive letter is found.

Solution: expand get_current_dir docs to explain nil return cases, add
a Telescope recipe with nil guards, and add a defensive nil check in
posix_to_os_path.

Cherry-picked from: stevearc/oil.nvim#727
2026-02-20 16:16:02 -05:00

4.7 KiB

Recipes

Have a cool recipe to share? Open a pull request and add it to this doc!

Toggle file detail view

local detail = false
require("oil").setup({
  keymaps = {
    ["gd"] = {
      desc = "Toggle file detail view",
      callback = function()
        detail = not detail
        if detail then
          require("oil").set_columns({ "icon", "permissions", "size", "mtime" })
        else
          require("oil").set_columns({ "icon" })
        end
      end,
    },
  },
})

Show CWD in the winbar

-- Declare a global function to retrieve the current directory
function _G.get_oil_winbar()
  local bufnr = vim.api.nvim_win_get_buf(vim.g.statusline_winid)
  local dir = require("oil").get_current_dir(bufnr)
  if dir then
    return vim.fn.fnamemodify(dir, ":~")
  else
    -- If there is no current directory (e.g. over ssh), just show the buffer name
    return vim.api.nvim_buf_get_name(0)
  end
end

require("oil").setup({
  win_options = {
    winbar = "%!v:lua.get_oil_winbar()",
  },
})

Hide gitignored files and show git tracked hidden files

-- helper function to parse output
local function parse_output(proc)
  local result = proc:wait()
  local ret = {}
  if result.code == 0 then
    for line in vim.gsplit(result.stdout, "\n", { plain = true, trimempty = true }) do
      -- Remove trailing slash
      line = line:gsub("/$", "")
      ret[line] = true
    end
  end
  return ret
end

-- build git status cache
local function new_git_status()
  return setmetatable({}, {
    __index = function(self, key)
      local ignore_proc = vim.system(
        { "git", "ls-files", "--ignored", "--exclude-standard", "--others", "--directory" },
        {
          cwd = key,
          text = true,
        }
      )
      local tracked_proc = vim.system({ "git", "ls-tree", "HEAD", "--name-only" }, {
        cwd = key,
        text = true,
      })
      local ret = {
        ignored = parse_output(ignore_proc),
        tracked = parse_output(tracked_proc),
      }

      rawset(self, key, ret)
      return ret
    end,
  })
end
local git_status = new_git_status()

-- Clear git status cache on refresh
local refresh = require("oil.actions").refresh
local orig_refresh = refresh.callback
refresh.callback = function(...)
  git_status = new_git_status()
  orig_refresh(...)
end

require("oil").setup({
  view_options = {
    is_hidden_file = function(name, bufnr)
      local dir = require("oil").get_current_dir(bufnr)
      local is_dotfile = vim.startswith(name, ".") and name ~= ".."
      -- if no local directory (e.g. for ssh connections), just hide dotfiles
      if not dir then
        return is_dotfile
      end
      -- dotfiles are considered hidden unless tracked
      if is_dotfile then
        return not git_status[dir].tracked[name]
      else
        -- Check if file is gitignored
        return git_status[dir].ignored[name]
      end
    end,
  },
})

Open Telescope file finder in the current oil directory

When using get_current_dir() in a keymap that also opens another plugin's UI (like Telescope), always capture the directory in a local variable before the call that changes the buffer context. Passing get_current_dir() directly as an argument works because Lua evaluates arguments before calling the function, but any subsequent calls will see the new buffer.

require("oil").setup({
  keymaps = {
    ["<leader>ff"] = {
      desc = "Find files in the current directory",
      callback = function()
        local dir = require("oil").get_current_dir()
        if not dir then
          vim.notify("Could not get oil directory", vim.log.levels.WARN)
          return
        end
        require("telescope.builtin").find_files({ cwd = dir })
      end,
    },
    ["<leader>fg"] = {
      desc = "Live grep in the current directory",
      callback = function()
        local dir = require("oil").get_current_dir()
        if not dir then
          vim.notify("Could not get oil directory", vim.log.levels.WARN)
          return
        end
        require("telescope.builtin").live_grep({ cwd = dir })
      end,
    },
  },
})

If you need the directory after an operation that might change the current buffer, pass the buffer number explicitly:

local bufnr = vim.api.nvim_get_current_buf()
-- ... some operation that changes the current buffer ...
local dir = require("oil").get_current_dir(bufnr)