Skip to content

DrKJeff16/project.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

project.nvim

project.nvim is a Neovim plugin written in Lua that provides a dynamic project management solution.

This is forked from ahmedkhalf/project.nvim. I will be maintaining this plugin for the foreseeable future.

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

Checkhealth Support

project_checkhealth.mp4

Telescope Integration

project_telescope.mp4

Fzf-Lua Integration

project_fzf_lua.mp4

Alternatives

Show these much love!


Table of Contents

  1. Features
  2. TODO
  3. Installation
  4. Configuration
    1. Defaults
    2. Pattern Matching
    3. Nvim Tree
    4. Telescope
      1. Telescope Mappings
  5. Commands
    1. ProjectFzf
    2. ProjectTelescope
    3. ProjectAdd
    4. ProjectRoot
    5. ProjectRecents
    6. ProjectConfig
    7. ProjectDelete
  6. API
    1. run_fzf_lua()
    2. get_project_root()
    3. get_recent_projects()
    4. get_config()
    5. get_history_paths()
  7. Utils
  8. Troubleshooting
    1. History File Not Created
  9. Contributing
  10. Credits

Features

  • Automagically cd to the project root directory using vim.lsp or pattern matching
  • Asynchronous file IO so it will not slow down neovim when reading the history file on startup
  • Users can control whether to run this or not by filetype/buftype
  • (NEW!) Fzf-Lua integration (credits to @deathmaz)
  • Functional checkhealth hook :checkhealth project
  • Vim help documentation :h project-nvim
  • Logging capabilities (WIP, EXPERIMENTAL)
  • Telescope Integration :Telescope projects
  • nvim-tree Integration

TODO

Please refer to TODO.md for more info.

Installation


Requirements:


Use any plugin manager of your choosing.

Currently there are instructions for these:

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

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

  lua << EOF
  require('project').setup({
    -- your configuration comes here
    -- or leave it empty to use the default settings
    -- refer to the configuration section below
  })
  EOF
endif
lazy.nvim
require('lazy').setup({
  spec = {
    -- Other plugins
    {
      'DrKJeff16/project.nvim',
      dependencies = { -- OPTIONAL
        'nvim-lua/plenary.nvim',
        'nvim-telescope/telescope.nvim',
      },

      ---@module 'project'
      ---@type Project.Config.Options
      opts = {
        -- your configuration comes here
        -- or leave it empty to use the default settings
        -- refer to the configuration section below
      },
      cond = vim.fn.has('nvim-0.11') == 1, -- RECOMMENDED
    },
  },
})
pckr.nvim
if vim.fn.has('nvim-0.11') == 1 then
  require('pckr').add({
    {
      'DrKJeff16/project.nvim',
      requires = {
        'nvim-lua/plenary.nvim',
        'nvim-telescope/telescope.nvim',
      }, -- OPTIONAL
      config = function()
        require('project').setup({
          -- your configuration comes here
          -- or leave it empty to use the default settings
          -- refer to the configuration section below
        })
      end,
    };
  })
end

If you want to add instructions for your package manager please raise a BLANK ISSUE.


Configuration

To enable the plugin you may call setup():

require('project').setup()

Defaults

setup()comes with these defaults.

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

{
    ---If `true`, it enables logging in `<project_datapath>/project.log`.
    ---
    ---WARNING: Experimental.
    --- ---
    ---Default: `false`
    --- ---
    logging = false,

    ---Determines in what filetypes/buftypes will the plugin be disabled in.
    ---It's a table with two fields:
    ---
    --- - `ft` for filetypes to exclude
    --- - `bt` for buftypes to exclude
    ---
    ---The default value for this one can be found in the project's `README.md`.
    ---
    --- ---
    ---CREDITS TO @Zeioth !:
    ---[`Zeioth/project.nvim`](https://github.com/Zeioth/project.nvim/commit/95f56b8454f3285b819340d7d769e67242d59b53)
    --- ---
    ---@type { ft: string[], bt: string[] }
    disable_on = {
        ft = {
            '',
            'TelescopePrompt',
            'TelescopeResults',
            'alpha',
            'checkhealth',
            'lazy',
            'minimap', -- from `mini.map`
            'notify',
            'packer',
            'qf',
        }, ---`filetype`

        bt = {
            'help',
            'nofile',
            'terminal',
        }, ---`buftype`
    },

    ---If `true` your root directory won't be changed automatically,
    ---so you have the option to manually do so
    ---using the `:ProjectRoot` command.
    --- ---
    ---Default: `false`
    --- ---
    ---@type boolean
    manual_mode = false,

    ---Methods of detecting the root directory. `'lsp'` uses the native neovim
    ---LSP, while `'pattern'` uses vim-rooter like glob pattern matching. Here
    ---order matters: if one is not detected, the other is used as fallback. You
    ---can also delete or rearrange the detection methods.
    ---
    ---The detection methods get filtered and rid of duplicates during runtime.
    --- ---
    ---Default: `{ 'lsp' , 'pattern' }`
    --- ---
    ---@type ("lsp"|"pattern")[]
    detection_methods = { 'lsp', 'pattern' },

    ---All the patterns used to detect root dir, when **'pattern'** is in
    ---detection_methods.
    ---
    ---See `:h project-nvim.pattern-matching`
    --- ---
    ---Default: `{ '.git', '.github', '_darcs', '.hg', '.bzr', '.svn', 'Pipfile' }`
    --- ---
    ---@type string[]
    patterns = {
        '.git',
        '.github',
        '_darcs',
        '.hg',
        '.bzr',
        '.svn',
        'Pipfile',
    },

    ---Sets whether to use Pattern Matching rules on the LSP.
    ---
    ---If `false`, the Pattern Matching will only apply to the `pattern` detection method.
    --- ---
    ---Default: `true`
    --- ---
    ---@type boolean
    allow_patterns_for_lsp = true,

    ---Determines whether a project will be added if its project root is owned by a different user.
    ---
    ---If `false`, it won't add a project if its root is not owned by the
    ---current nvim `UID` **(UNIX only)**.
    --- ---
    ---Default: `true`
    --- ---
    ---@type boolean
    allow_different_owners = true,

    ---If enabled, set `vim.opt.autochdir` to `true`.
    ---
    ---This is disabled by default because the plugin implicitly disables `autochdir`.
    --- ---
    ---Default: `false`
    --- ---
    ---@type boolean
    enable_autochdir = false,

    ---The history size. (by @acristoffers)
    ---
    ---This will indicate how many entries will be written to the history file.
    ---Set to `0` for no limit.
    --- ---
    ---Default: `100`
    --- ---
    ---@type integer
    historysize = 100

    ---Table of options used for the telescope picker.
    --- ---
    ---@class Project.Config.Options.Telescope
    telescope = {
        ---Determines whether the `telescope` picker should be called.
        ---
        ---If telescope is not installed, this doesn't make a difference.
        ---
        ---Note that even if set to `false`, you can still load the extension manually.
        --- ---
        ---Default: `true`
        --- ---
        ---@type boolean
        enabled = true,

        ---Determines whether the newest projects come first in the
        ---telescope picker (`'newest'`), or the oldest (`'oldest'`).
        --- ---
        ---Default: `'newest'`
        --- ---
        ---@type 'oldest'|'newest'
        sort = 'newest',

        ---If `true`, `telescope-file-browser.nvim` instead of builtins.
        ---
        ---If you have `telescope-file-browser.nvim` installed, you can enable this
        ---so that the Telescope picker uses it instead of the `find_files` builtin.
        ---
        ---In case it is not available, it'll fall back to `find_files`.
        --- ---
        ---Default: `false`
        --- ---
        ---@type boolean
        prefer_file_browser = false,

        ---Make hidden files visible when using the `telescope` picker.
        --- ---
        ---Default: `false`
        --- ---
        ---@type boolean
        show_hidden = false,
    },

    ---Table of lsp clients to ignore by name,
    ---e.g. `{ 'efm', ... }`.
    ---
    ---If you have `nvim-lspconfig` installed **see** `:h lspconfig-all`
    ---for a list of servers.
    --- ---
    ---Default: `{}`
    --- ---
    ---@type string[]|table
    ignore_lsp = {},

    ---Don't calculate root dir on specific directories,
    ---e.g. `{ '~/.cargo/*', ... }`.
    ---
    ---See the `Pattern Matching` section in the `README.md` for more info.
    --- ---
    ---Default: `{}`
    --- ---
    ---@type string[]|table
    exclude_dirs = {},

    ---If `false`, you'll get a _notification_ every time
    ---`project.nvim` changes directory.
    ---
    ---This is useful for debugging, or for players that
    ---enjoy verbose operations.
    --- ---
    ---Default: `true`
    --- ---
    ---@type boolean
    silent_chdir = true,

    ---Determines the scope for changing the directory.
    ---
    ---Valid options are:
    --- - `'global'`: All your nvim `cwd` will sync to your current buffer's project
    --- - `'tab'`: _Per-tab_ `cwd` sync to the current buffer's project
    --- - `'win'`: _Per-window_ `cwd` sync to the current buffer's project
    --- ---
    ---Default: `'global'`
    --- ---
    ---@type 'global'|'tab'|'win'
    scope_chdir = 'global',

    ---Hook to run before attaching to a new project.
    ---
    ---If `nil`, it won't run.
    --- ---
    ---Default: `nil`
    --- ---
    ---@type nil|fun()
    before_attach = nil,

    ---Hook to run after attaching to a new project.
    ---
    ---If `nil`, it won't run.
    --- ---
    ---Default: `nil`
    --- ---
    ---@type nil|fun()
    on_attach = nil,

    ---The path where `project.nvim` will store the project history directory,
    ---containing the project history in it.
    ---
    ---For more info, run `:lua vim.print(require('project').get_history_paths())`
    --- ---
    ---Default: `vim.fn.stdpath('data')`
    --- ---
    ---@type string
    datapath = vim.fn.stdpath('data'),
}

Pattern Matching


2d81e5d: Now works with the LSP aswell.


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' }

NOTE: Make sure to put your pattern exclusions first, and then the patterns you DO want included.

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,
  },
})

Telescope

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

require('telescope').setup(...)
-- Other stuff may come here...
require('telescope').load_extension('projects')

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

For example:

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

      prompt_prefix = "󱎸  ",
    },
  },
})

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

" Vim command line
:Telescope projects

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

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


Commands

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

ProjectFzf

This command is only enabled if you have installed fzf-lua

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().

ProjectTelescope

This command is only enabled if you have telescope.nvim (and you loaded it)

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

ProjectAdd

The :ProjectAdd command is a manual hook to add to session projects, then subsequently cd to the current file's project directory (provided) it actually could.

The command does essentially the following:

" Vim command line
:lua require('project.api').add_project_manually()

See api.lua for more info on add_project_manually().

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 does essentially the following:

" Vim command line
:lua require('project.api').on_buf_enter()

See api.lua for more info on on_buf_enter().

ProjectRecents

The :ProjectRecents command is a hook to print a formatted list of your recent projects using vim.notify().

See api.lua for more info.

ProjectConfig

The :ProjectConfig command is a hook to display your current config using vim.notify(inspect())

The command does essentially the following:

" Vim command line
:lua vim.notify(vim.inspect(require('project').get_config()))

See api.lua for more info.

ProjectDelete

The :ProjectDelete command is one that needs at least one argument, and only accepts directories separated by a space. The arguments have to be directories that exist in the result of get_recent_projects().

The arguments can be relative, absolute or un-expanded (~/path/to/project). The command will attempt to parse the args. If there's a successful deletion, you'll recieve a notification through vim.notify().

See :h :ProjectDelete for more info.

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

See api.lua for more info.


API

The API can be found in api.lua.

run_fzf_lua()

run_fzf_lua() is an API utility to run this project using fzf-lua. See :ProjectFzf for more info.

get_project_root()

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

---@type fun(): (string|nil,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:

---@type string[]|table
local recent_projects = require('project').get_recent_projects()

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

-- Using `vim.print()`
vim.print(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.

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

You can import them the follow way:

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

See util.lua for further reference.


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.

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

" Vim command line
:lua vim.print(require('project').get_history_paths('projectpath'))

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

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)

Contributing

Please refer to CONTRIBUTING.md


Credits

About

A dynamic project management solution for neovim

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors 18