Skip to content

A Neovim plugin that sets the cwd to the project root, stores projects in a history file, includes a telescope picker, supports fzf-lua, and provides UI tools

License

Notifications You must be signed in to change notification settings

DrKJeff16/project.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Note

This was originally forked from ahmedkhalf/project.nvim. Ever since I've decided to extend it and address issues.

I will be maintaining this plugin for the foreseeable future!

Important

I'm looking for other maintainers in case I'm unable to keep this repo up to date!

project.nvim Mentioned in Awesome Neovim

MaintainedLast CommitLICENSEIssuesGitHub Release

============================ Breaking Changes ============================

Announcements | Discussions | Wiki | Credits | Contributing | Roadmap

project_nvim-showcase project_nvim_session

project.nvim is a Neovim plugin written in Lua that, under configurable conditions, automatically sets the user's cwd to the current project root and also allows users to manage, access and selectively include their projects in a history.

This plugin allows you to navigate through projects, "bookmark" and/or discard them, according to your needs.


Checkhealth Support Example
project_checkhealth.mp4
Telescope Integration Example
project_telescope.mp4
fzf-Lua Integration Example
project_fzf_lua.mp4

Alternatives

Show these much love!


Table of Contents

  1. Features
  2. Installation
    1. vim-plug
    2. lazy.nvim
    3. pckr.nvim
    4. paq-nvim
    5. LuaRocks
  3. Configuration
    1. Defaults
    2. Pattern Matching
    3. Nvim Tree
    4. Neo Tree
    5. Telescope
      1. Telescope Mappings
    6. mini.starter
  4. Commands
    1. :Project
    2. :ProjectFzf
    3. :ProjectTelescope
    4. :ProjectHealth
    5. :ProjectHistory
    6. :ProjectLog
    7. :ProjectLogClear
    8. :ProjectAdd
    9. :ProjectRoot
    10. :ProjectConfig
    11. :ProjectDelete
    12. :ProjectSession
  5. API
    1. get_project_root()
    2. get_recent_projects()
    3. get_config()
    4. get_history_paths()
  6. Utils
  7. Troubleshooting
    1. History File Not Created
  8. License

Features


Installation

Important

Requirements:

Use any plugin manager of your choosing. There currently instructions for the following.

Tip

If you want to add instructions for your plugin manager of preference please raise a BLANK ISSUE.

vim-plug

if has('nvim-0.11')
  Plug 'DrKJeff16/project.nvim'

  " OPTIONAL
  Plug 'nvim-telescope/telescope.nvim' | Plug 'nvim-lua/plenary.nvim'
  Plug 'ibhagwan/fzf-lua'

  lua << EOF
  require('project').setup()
  EOF
endif

lazy.nvim

{
  'DrKJeff16/project.nvim',
  dependencies = { -- OPTIONAL
    'nvim-lua/plenary.nvim',
    'nvim-telescope/telescope.nvim',
    'ibhagwan/fzf-lua',
  },
  opts = {},
}

Tip

If you wish to lazy-load this plugin:

{
  'DrKJeff16/project.nvim',
  cmd = { -- Lazy-load by commands
    'Project',
    'ProjectAdd',
    'ProjectConfig',
    'ProjectDelete',
    'ProjectHistory',
    'ProjectRecents',
    'ProjectRoot',
    'ProjectSession',
  },
  dependencies = { -- OPTIONAL
    'nvim-lua/plenary.nvim',
    'nvim-telescope/telescope.nvim',
    'ibhagwan/fzf-lua',
  },
  opts = {},
}

pckr.nvim

if vim.fn.has('nvim-0.11') == 1 then
  require('pckr').add({
    {
      'DrKJeff16/project.nvim',
      requires = { -- OPTIONAL
        'nvim-lua/plenary.nvim',
        'nvim-telescope/telescope.nvim',
        'ibhagwan/fzf-lua',
      },
      config = function()
        require('project').setup()
      end,
    };
  })
end

paq-nvim

local paq = require('paq')

paq({
  'DrKJeff16/project.nvim',

  'nvim-lua/plenary.nvim', -- OPTIONAL
  'nvim-telescope/telescope.nvim', -- OPTIONAL
  'ibhagwan/fzf-lua', -- OPTIONAL
})

require('project.nvim').setup()

LuaRocks

Note

The package can be found here.

luarocks install project.nvim # Global install

luarocks install --local project.nvim # Local install

Configuration

To enable the plugin you must call setup():

require('project').setup()

Defaults

Tip

You can find these in project/config/defaults.lua.

Note

The Project.Telescope.ActionNames type is an alias for:

---@alias Project.Telescope.ActionNames
---|'browse_project_files'
---|'change_working_directory'
---|'delete_project'
---|'find_project_files'
---|'help_mappings'
---|'recent_project_files'
---|'search_in_project_files'

setup()comes with these defaults.
{
    ---@param target_dir string
    ---@param method string
    before_attach = function(target_dir, method) end,
    ---@param dir string
    ---@param method string
    on_attach = function(dir, method) end,
    use_lsp = true,
    manual_mode = false,
    patterns = { ---@type string[]
        '.git',
        '.github',
        '_darcs',
        '.hg',
        '.bzr',
        '.svn',
        'Pipfile',
        'pyproject.toml',
        '.pre-commit-config.yaml',
        '.pre-commit-config.yml',
        '.csproj',
        '.sln',
        '.nvim.lua',
    },
    allow_patterns_for_lsp = false,
    allow_different_owners = false,
    enable_autochdir = false,
    show_hidden = false,
    ignore_lsp = {}, ---@type string[]
    exclude_dirs = {}, ---@type string[]
    silent_chdir = true,
    scope_chdir = 'global', ---@type 'global'|'tab'|'win'
    datapath = vim.fn.stdpath('data'),
    historysize = 100,
    log = { ---@type Project.Config.Logging
        enabled = false,
        max_size = 1.1,
        logpath = vim.fn.stdpath('state'),
    },
    fzf_lua = { enabled = false }, ---@type Project.Config.FzfLua
    disable_on = {
        ft = { -- `filetype`
            '',
            'NvimTree',
            'TelescopePrompt',
            'TelescopeResults',
            'alpha',
            'checkhealth',
            'lazy',
            'log',
            'ministarter',
            'neo-tree',
            'notify',
            'nvim-pack',
            'packer',
            'qf',
        },
        bt = { 'help', 'nofile', 'nowrite', 'terminal' }, -- `buftype`
    },
    telescope = { ---@type Project.Config.Telescope
        sort = 'newest', ---@type 'oldest'|'newest'
        prefer_file_browser = false,
        disable_file_picker = false,
        mappings = { ---@type table<'n'|'i', table<string, Project.Telescope.ActionNames>>
            n = {
                b = 'browse_project_files',
                d = 'delete_project',
                f = 'find_project_files',
                r = 'recent_project_files',
                s = 'search_in_project_files',
                w = 'change_working_directory',
            },
            i = {
                ['<C-b>'] = 'browse_project_files',
                ['<C-d>'] = 'delete_project',
                ['<C-f>'] = 'find_project_files',
                ['<C-r>'] = 'recent_project_files',
                ['<C-s>'] = 'search_in_project_files',
                ['<C-w>'] = 'change_working_directory',
            },
        },
    },
}

Pattern Matching

project.nvim comes with a vim-rooter-inspired pattern matching expression engine to give you better handling of your projects.

For your convenience here come some examples:

  • To specify the root is a certain directory, prefix it with =:

    patterns = { '=src' }
  • To specify the root has a certain directory or file (which may be a glob), just add it to the pattern list:

    patterns = { '.git', '.github', '*.sln', 'build/env.sh' }
  • To specify the root has a certain directory as an ancestor (useful for excluding directories), prefix it with ^:

    patterns = { '^fixtures' }
  • To specify the root has a certain directory as its direct ancestor / parent (useful when you put working projects in a common directory), prefix it with >:

    patterns = { '>Latex' }
  • To exclude a pattern, prefix it with !:

    patterns = { '!.git/worktrees', '!=extras', '!^fixtures', '!build/env.sh' }

Important

  • Make sure to put your pattern exclusions first, and then the patterns you DO want included.
  • If you have allow_patterns_for_lsp enabled, it will also work somewhat for your LSP clients.

Nvim Tree

Make sure these flags are enabled to support nvim-tree.lua:

require('nvim-tree').setup({
  sync_root_with_cwd = true,
  respect_buf_cwd = true,
  update_focused_file = {
    enable = true,
    update_root = true,
  },
})

Neo Tree

You can use :Neotree filesystem ... when changing a project:

vim.keymap.set('n', '<YOUR-TOGGLE-MAP>', ':Neotree filesystem toggle reveal_force_cwd<CR>', opts)
vim.keymap.set('n', '<YOUR-SHOW-MAP>', ':Neotree filesystem show reveal_force_cwd<CR>', opts)
vim.keymap.set('n', '<YOUR-FLOAT-MAP>', ':Neotree filesystem float reveal_force_cwd<CR>', opts)
-- ... and so on

Note

Not 100% certain whether the reveal_force_cwd flag is necessary, but better safe than sorry!

Telescope

To enable telescope.nvim integration use the following code in your config:

require('telescope').setup(...)
require('telescope').load_extension('projects')

After that you can now call it from the command line:

:Telescope projects

Tip

You can also configure the picker when calling require('telescope').setup() CREDITS: @ldfwbebp: ahmedkhalf/project.nvim#160

require('telescope').setup({
  extensions = {
    projects = {
      prompt_prefix = "󱎸  ",
      layout_strategy = "horizontal",
      layout_config = {
        anchor = "N",
        height = 0.25,
        width = 0.6,
        prompt_position = "bottom",
      },
    },
  },
})

Telescope Mappings

project.nvim comes with the following mappings for Telescope:

Normal mode Insert mode Action
f <C-f> find_project_files
b <C-b> browse_project_files
d <C-d> delete_project
s <C-s> search_in_project_files
r <C-r> recent_project_files
w <C-w> change_working_directory

Tip

You can find the Actions in telescope/_extensions/projects/actions.lua.


mini.starter

If you use nvim-mini/mini.starter you can include the following snippet in your MiniStarter setup:

require('mini.starter').setup({
  evaluate_single = true,
  items = {
    { name = 'Projects', action = 'Project', section = 'Projects' }, -- Runs `:Project`
    { name = 'Recent Projects', action = 'ProjectRecents', section = 'Projects' }, -- `:ProjectRecents`
    -- Other items...
  },
})

Commands

These are the user commands you can call from the cmdline:

:Project

The :Project command will open a UI window pointing to all the useful operations this plugin can provide. This one is subject to change, just as vim.ui is.

Tip

See commands.lua for more info.


:ProjectFzf

Important

This command works ONLY if you have fzf-lua installed and loaded

The :ProjectFzf command is a dynamicly enabled User Command that runs project.nvim through fzf-lua. For now it just executes require('project').run_fzf_lua().

Tip

See commands.lua for more info.


:ProjectTelescope

Important

This command works ONLY if you have telescope.nvim installed and loaded

The :ProjectTelescope command is a dynamicly enabled User Command that runs the Telescope projects picker. A shortcut, to be honest.

Tip

See commands.lua for more info.


:ProjectHealth

The :ProjectHealth command runs :checkhealth project in a single command.

Tip

See commands.lua for more info.


:ProjectHistory

The :ProjectHistory command opens the project.nvim history file in a new tab, which can be exited by pressing q in Normal Mode.

If a [!] is supplied at the end of the command (i.e. :ProjectHistory!), then it'll close any instance of a previously opened history file, if found. Otherwise nothing will happen.

Tip

See history.lua for more info.


:ProjectLog

Important

This command will not be available unless you set log.enabled = true in your setup().

The :ProjectLog command opens the project.nvim log file in a new tab, which can be exited by pressing q in Normal Mode.

If a [!] is supplied at the end of the command (i.e. :ProjectLog!), then it'll close any instance of a previously opened log file, if found. Otherwise nothing will happen.

Tip

See log.lua for more info.


:ProjectLogClear

Important

This command will not be available unless you set log.enabled = true in your setup().

The :ProjectLogClear command will delete all the contents of your log file, assuming there is one.

If this is called from a logfile tab, then it will attempt to close it.

Note

Once this command is called no more logs will be written for your current Neovim session. You will have to restart.

You could set vim.g.project_log_cleared to a value different to 1, BUT THIS SOLUTION IS NOT TESTED FULLY. EXPECT WEIRD BEHAVIOUR IF YOU DO THIS!

Tip

See log.lua for more info.


:ProjectAdd

The :ProjectAdd command is a manual hook that opens a prompt to input any directory through a UI prompt, to be saved to your project history.

If your prompt is valid, your cwd will be switched to said directory. Adding a [!] will set the prompt to your cwd.

Note

This is particularly useful if you've enabled manual_mode in setup().

Tip

See commands.lua for more info.


:ProjectRoot

The :ProjectRoot command is a manual hook to set the working directory to the current file's root, attempting to use any of the setup() detection methods set by the user.

The command is like doing the following in the cmdline:

:lua require('project.api').on_buf_enter()

Tip

See commands.lua for more info.


:ProjectConfig

The :ProjectConfig command will open your current config in a floating window, making it easier to access. To exit the window you can either press q in normal mode or by runnning :ProjectConfig again.

You can also print the output instead by running :ProjectConfig!.

Tip

See commands.lua for more info.


:ProjectDelete

The :ProjectDelete command is a utility to delete your projects.

If no arguments are given, a popup with a list of your current projects will be opened.

If one or more arguments are passed, it will expect directories separated by a space. The arguments have to be directories that are returned by get_recent_projects(). The arguments can be relative, absolute or un-expanded (~/path/to/project). The command will attemptto parse the args and, unless a ! is passed to the command (:ProjectDelete!). In that case, invalid args will be ignored.

If there's a successful deletion, you'll recieve a notification denoting success.

Note

USAGE

" Vim command line
:ProjectDelete[!] [/path/to/first [/path/to/second [...]]]

Tip

  • See :h :ProjectDelete for more info.
  • See commands.lua for more info.

:ProjectSession

Important

This command requires fd to be installed for it to work!

The :ProjectSession command opens a custom picker with a selection of your current session projects (stored in History.session_projects). Bear in mind this table gets filled on runtime.

If you select a session project, your cwd will be changed to what you selected. If the command is called with a ! (:ProjectSession!) the UI will close. Otherwise, another custom UI picker will appear for you to select the files/dirs. Selecting a directory will open another UI picker with its contents, and so on.

Tip



API

The API can be found in api.lua.

get_project_root()

get_project_root() is an API utility for finding out about the current project's root, if any:

---@type string?, string?
local root, lsp_or_method = require('project').get_project_root()

get_recent_projects()

You can get a list of recent projects by running the code below:

local recent_projects = require('project').get_recent_projects() ---@type string[]
vim.notify(vim.inspect(recent_projects))

Where get_recent_projects() returns either an empty table {} or a string array { '/path/to/project1', ... }.

get_config()

If setup() has been called, it returns a table containing the currently set options. Otherwise it will return nil.

local config = require('project').get_config()

-- Using `vim.notify()`
vim.notify(vim.inspect(config))

-- Using `vim.print()`
vim.print(config)

get_history_paths()

If no valid args are passed to this function, it will return the following dictionary:

---@type fun(path: ('datapath'|'projectpath'|'historyfile')?): string|{ datapath: string, projectpath: string, historyfile: string }
local get_history_paths = require('project').get_history_paths

-- A dictionary table containing all return values below
vim.print(get_history_paths())
--- { datapath = <datapath>, projectpath = <projectpath>, historyfile = <historyfile> }

Otherwise, if either 'datapath', 'projectpath' or 'historyfile' are passed, it will return the string value of said arg:

-- The directory where `project` sets its `datapath`
vim.print(get_history_paths('datapath'))

-- The directory where `project` saves the project history
vim.print(get_history_paths('projectpath'))

-- The path to where `project` saves its recent projects history
vim.print(get_history_paths('historyfile'))

Utils

A set of utilities that get repeated across the board.

Tip

You can import them the follow way:

local ProjUtil = require('project.utils.util')

See util.lua for further reference.

Note

These utilities are in part inspired by my own utilities found in Jnvim, my own Nvim configuration; particularly the User API (WIP).


Troubleshooting

History File Not Created

If you're in a UNIX environment, make sure you have read, write and access permissions (rwx) for the projectpath directory.

Important

The default value is vim.fn.stdpath('data')/project_nvim. See :h stdpath() for more info.

Tip

You can get the value of projectpath by running the following in the cmdline:

:lua vim.print(require('project').get_history_paths('projectpath'))

If you lack the required permissions for that directory, you can either:

  • Delete that directory (RECOMMENDED)
  • Run chmod 755 <project/path> (NOT SURE IF THIS WILL FIX IT)

License

Apache-2.0

About

A Neovim plugin that sets the cwd to the project root, stores projects in a history file, includes a telescope picker, supports fzf-lua, and provides UI tools

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors 20