FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

wsdjeg/rooter.nvim: Changes Neovim working directory to project root. · GitHub

Latest commit

 

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rooter.nvim

rooter.nvim changes the working directory to the project root when you open a file. It is inspired by vim-rooter.

This plugin also provides telescope and picker.nvim extensions to fuzzy find recently opened projects.

✨ Features

  • Automatic project root detection on BufEnter / VimEnter
  • Re-detect root on BufWritePost (e.g. after creating a new .git/)
  • Project history caching to disk for persistence across sessions
  • Outermost vs nearest root directory support
  • Flexible behavior for non-project files ('', 'home', or 'current')
  • Configurable buffer exclusion patterns (skip filetree/popup buffers)
  • Toggle root detection on/off at runtime (:Rooter toggle)
  • Automatic logging via logger.nvim (optional dependency)
  • Callback APIs for project switch events
  • Command-line interface (:Rooter)
  • Telescope integration
  • Picker.nvim integration

📦 Installation

using nvim-plug

require('plug').add({
  {
    'wsdjeg/rooter.nvim',
    config = function()
      require('rooter').setup({
        root_patterns = { '.git/' },
      })
    end,
  }
})

🔧 Configuration

require('rooter').setup({
  root_patterns = { '.git/' },
  outermost = true,
  enable_cache = true,
  project_non_root = '',
  command = 'lcd',
  exclude_patterns = {
    '%[denite%]',
    'denite%-filter',
    '%[defx%]',
    '^git://', -- git.vim
    '^neo%-tree', -- neo-tree.nvim
    '^NvimTree_', -- nvim-tree.nvim
    '^__Tagbar__', -- tagbar.vim
  },
})
Option Type Default Description
root_patterns table<string> { '.git/' } Patterns to identify project root. Directories end with /, files do not.
outermost boolean true When true, find the outermost matching directory. When false, find the nearest (innermost).
enable_cache boolean true Persist project history to stdpath('data')/nvim-rooter.json for cross-session persistence.
project_non_root string '' Behavior for files outside any project: '' = keep cwd, 'home' = switch to $HOME, 'current' = switch to file's directory.
command string 'lcd' Vim command used to change directory: 'cd', 'tcd', or 'lcd'.
exclude_patterns table<string> see above Lua patterns matched against the buffer name; matching buffers (filetrees, popups...) are skipped by root detection. Remember to escape magic characters (- → %-).

Note: list options (root_patterns, exclude_patterns) are replaced as a whole when you set them, they are not merged with the defaults.

Example: multiple patterns

require('rooter').setup({
  root_patterns = { '.git/', '.hg/', 'Cargo.toml', 'go.mod', 'package.json' },
  outermost = false,  -- find nearest root
  command = 'tcd',    -- use tab-local cd
})

Example: exclude plugin buffers

require('rooter').setup({
  exclude_patterns = {
    '^neo%-tree', -- lua pattern, escape the magic `-`
    '^NvimTree_',
    '^myfiletree://', -- add your own plugin buffers
  },
})

⚙️ Basic Usage

Once setup() is called, rooter.nvim automatically changes the working directory whenever you open a file or switch buffers. You can also use the commands and APIs below.

Commands

This plugin provides a user command :Rooter:

Command Description
:Rooter Manually trigger root detection for current buffer.
:Rooter toggle Toggle root detection on/off.
:Rooter enable Enable root detection.
:Rooter disable Disable root detection (keeps current cwd).
:Rooter clear Clear all cached projects.
:Rooter kill project1 project2 Delete all buffers belonging to the specified project(s).

Telescope extension

Requires telescope.nvim.

:Telescope project

Lists all cached projects sorted by last opened time. Press <CR> to open a project in a new tab.

Picker.nvim extension

Requires picker.nvim.

:Picker project

Key bindings for picker project:

Key binding Description
<CR> Open project in a new tab (default action)
<C-f> Browse project files
<C-d> Delete project from history
<C-s> Search text in project, requires flygrep.nvim

Callback function

To run custom logic when the project changes, register a callback with rooter.reg_callback:

-- Update code-runner config based on project's .clang file
local c_runner = {
    exe = 'gcc',
    targetopt = '-o',
    usestdin = true,
    opt = { '-std=c11', '-xc', '-' },
}
require('code-runner').setup({
    runners = {
        c = { c_runner, '#TEMP#' },
    },
})

local function update_clang_flag()
    if vim.fn.filereadable('.clang') == 1 then
        local flags = vim.fn.readfile('.clang')
        local opt = { '-std=c11' }
        for _, v in ipairs(flags) do
            table.insert(opt, v)
        end
        table.insert(opt, '-xc')
        table.insert(opt, '-')
        c_runner.opt = opt
    end
end

require('rooter').reg_callback(update_clang_flag, 'update clang flags')

The callback receives the current project object:

Field Description
path Absolute root path (with trailing /)
name Project name (same as b:rooter_project_name)
opened_time Timestamp when the project was opened

Callbacks are called via pcall so errors won't crash the root detection flow. You can also pass a Vimscript function name as a string; it receives the project as a dict argument.

Per-project configuration

Per-project settings can be implemented right inside the callback, by matching project.name (or project.path for an exact match):

-- Per-project options: map project name -> option table
local project_options = {
    ['my-blog'] = {
        shiftwidth = 2,
        expandtab = true,
        makeprg = 'hugo server',
    },
    ['dotfiles'] = {
        shiftwidth = 4,
    },
}

-- Options applied when entering a project that is not in the table above
local default_options = {
    shiftwidth = 4,
}

local function apply_options(opts)
    for key, value in pairs(opts) do
        vim.opt[key] = value
    end
end

require('rooter').reg_callback(function(project)
    -- project = { path = '/home/me/my-blog/', name = 'my-blog', opened_time = 1712345678 }
    apply_options(project_options[project.name] or default_options)
end, 'per-project options')

Of course you can do much more than options, for example loading a project-local keymap file, starting an LSP server only for certain projects, etc.

API

Function Description
setup(opt) Initialize rooter.nvim with config options and set up autocmds.
current_root() Detect and switch to the project root for the current buffer. Returns the root path.
current_name() Returns the current project name (from b:rooter_project_name).
toggle() Toggle root detection, returns the new state.
enable() Enable root detection, returns true.
disable() Disable root detection, returns false.
is_enabled() Returns whether root detection is currently enabled.
list() Open the project picker (Picker.nvim or Telescope, whichever is available).
open(project_path) Open a project by its path in a new tab.
clear() Clear all cached projects and write empty cache to disk.
kill_project(name) Delete all buffers belonging to the named project.
reg_callback(func, desc?) Register a callback function (or Vimscript function name) to run on project switch. The callback receives the project object (path, name, opened_time). desc is an optional description for logging.
get_project_history() Returns the table of all cached projects.

🧪 Development

make test      # run the test suite
make coverage  # run tests with luacov, enforces 100% line coverage

The coverage gate covers lua/rooter/*.lua and plugin/rooter.lua. The telescope/picker integration files are excluded since they cannot be loaded without their host plugins.

🐛 Debug

Install logger.nvim as a dependency. Logging is automatically enabled when logger.nvim is available - no extra config needed.

require('plug').add({
  {
    'wsdjeg/rooter.nvim',
    config = function()
      require('rooter').setup({
        root_patterns = { '.git/' },
      })
    end,
    depends = {
      {
        'wsdjeg/logger.nvim',
        config = function()
          vim.keymap.set(
            'n',
            '<leader>hL',
            '<cmd>lua require("logger").viewRuntimeLog()<cr>',
            { silent = true }
          )
        end,
      },
    },
  },
})

Sample runtime log:

[   rooter ] [23:22:50:576] [ Info  ] start to find root for: D:/wsdjeg/rooter.nvim/lua/rooter/init.lua
[   rooter ] [23:22:50:576] [ Info  ]         (.git/):D:/wsdjeg/rooter.nvim/
[   rooter ] [23:22:50:576] [ Info  ] switch to project:[rooter.nvim]
[   rooter ] [23:22:50:576] [ Info  ]        rootdir is:D:/wsdjeg/rooter.nvim/

💬 Feedback

If you encounter any bugs or have suggestions, please file an issue in the issue tracker

🙏 Credits

📣 Self-Promotion

Like this plugin? Star the repository on GitHub.

Love this plugin? Follow me on GitHub.

📄 License

Licensed under GPL-3.0.

About

Changes Neovim working directory to project root.

Topics

Resources

Stars

26 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages


Back | FazBrowse Home | New Git URL