Update generated nvim config
This commit is contained in:
@ -0,0 +1,5 @@
|
||||
column_width = 120
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
quote_style = "AutoPreferDouble"
|
||||
201
config/neovim/store/lazy-plugins/project.nvim/LICENSE
Normal file
201
config/neovim/store/lazy-plugins/project.nvim/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
214
config/neovim/store/lazy-plugins/project.nvim/README.md
Normal file
214
config/neovim/store/lazy-plugins/project.nvim/README.md
Normal file
@ -0,0 +1,214 @@
|
||||
# 🗃️ project.nvim
|
||||
|
||||
**project.nvim** is an all in one neovim plugin written in lua that provides
|
||||
superior project management.
|
||||
|
||||

|
||||
|
||||
## ⚡ Requirements
|
||||
|
||||
- Neovim >= 0.5.0
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- Automagically cd to project directory using nvim lsp
|
||||
- Dependency free, does not rely on lspconfig
|
||||
- If no lsp then uses pattern matching to cd to root directory
|
||||
- Telescope integration `:Telescope projects`
|
||||
- Access your recently opened projects from telescope!
|
||||
- Asynchronous file io so it will not slow down vim when reading the history
|
||||
file on startup.
|
||||
- ~~Nvim-tree.lua support/integration~~
|
||||
- Please add the following to your config instead:
|
||||
```vim
|
||||
" Vim Script
|
||||
lua << EOF
|
||||
require("nvim-tree").setup({
|
||||
sync_root_with_cwd = true,
|
||||
respect_buf_cwd = true,
|
||||
update_focused_file = {
|
||||
enable = true,
|
||||
update_root = true
|
||||
},
|
||||
})
|
||||
EOF
|
||||
```
|
||||
```lua
|
||||
-- lua
|
||||
require("nvim-tree").setup({
|
||||
sync_root_with_cwd = true,
|
||||
respect_buf_cwd = true,
|
||||
update_focused_file = {
|
||||
enable = true,
|
||||
update_root = true
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
Install the plugin with your preferred package manager:
|
||||
|
||||
### [vim-plug](https://github.com/junegunn/vim-plug)
|
||||
|
||||
```vim
|
||||
" Vim Script
|
||||
Plug 'ahmedkhalf/project.nvim'
|
||||
|
||||
lua << EOF
|
||||
require("project_nvim").setup {
|
||||
-- your configuration comes here
|
||||
-- or leave it empty to use the default settings
|
||||
-- refer to the configuration section below
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
### [packer](https://github.com/wbthomason/packer.nvim)
|
||||
|
||||
```lua
|
||||
-- Lua
|
||||
use {
|
||||
"ahmedkhalf/project.nvim",
|
||||
config = function()
|
||||
require("project_nvim").setup {
|
||||
-- your configuration comes here
|
||||
-- or leave it empty to use the default settings
|
||||
-- refer to the configuration section below
|
||||
}
|
||||
end
|
||||
}
|
||||
```
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
**project.nvim** comes with the following defaults:
|
||||
|
||||
```lua
|
||||
{
|
||||
-- Manual mode doesn't automatically change your root directory, so you have
|
||||
-- the option to manually do so using `:ProjectRoot` command.
|
||||
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 rearangne the detection methods.
|
||||
detection_methods = { "lsp", "pattern" },
|
||||
|
||||
-- All the patterns used to detect root dir, when **"pattern"** is in
|
||||
-- detection_methods
|
||||
patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json" },
|
||||
|
||||
-- Table of lsp clients to ignore by name
|
||||
-- eg: { "efm", ... }
|
||||
ignore_lsp = {},
|
||||
|
||||
-- Don't calculate root dir on specific directories
|
||||
-- Ex: { "~/.cargo/*", ... }
|
||||
exclude_dirs = {},
|
||||
|
||||
-- Show hidden files in telescope
|
||||
show_hidden = false,
|
||||
|
||||
-- When set to false, you will get a message when project.nvim changes your
|
||||
-- directory.
|
||||
silent_chdir = true,
|
||||
|
||||
-- What scope to change the directory, valid options are
|
||||
-- * global (default)
|
||||
-- * tab
|
||||
-- * win
|
||||
scope_chdir = 'global',
|
||||
|
||||
-- Path where project.nvim will store the project history for use in
|
||||
-- telescope
|
||||
datapath = vim.fn.stdpath("data"),
|
||||
}
|
||||
```
|
||||
|
||||
Even if you are pleased with the defaults, please note that `setup {}` must be
|
||||
called for the plugin to start.
|
||||
|
||||
### Pattern Matching
|
||||
|
||||
**project.nvim**'s pattern engine uses the same expressions as vim-rooter, but
|
||||
for your convenience, I will copy paste them here:
|
||||
|
||||
To specify the root is a certain directory, prefix it with `=`.
|
||||
|
||||
```lua
|
||||
patterns = { "=src" }
|
||||
```
|
||||
|
||||
To specify the root has a certain directory or file (which may be a glob), just
|
||||
give the name:
|
||||
|
||||
```lua
|
||||
patterns = { ".git", "Makefile", "*.sln", "build/env.sh" }
|
||||
```
|
||||
|
||||
To specify the root has a certain directory as an ancestor (useful for
|
||||
excluding directories), prefix it with `^`:
|
||||
|
||||
```lua
|
||||
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
|
||||
`>`:
|
||||
|
||||
```lua
|
||||
patterns = { ">Latex" }
|
||||
```
|
||||
|
||||
To exclude a pattern, prefix it with `!`.
|
||||
|
||||
```lua
|
||||
patterns = { "!.git/worktrees", "!=extras", "!^fixtures", "!build/env.sh" }
|
||||
```
|
||||
|
||||
List your exclusions before the patterns you do want.
|
||||
|
||||
### Telescope Integration
|
||||
|
||||
To enable telescope integration:
|
||||
```lua
|
||||
require('telescope').load_extension('projects')
|
||||
```
|
||||
|
||||
#### Telescope Projects Picker
|
||||
To use the projects picker
|
||||
```lua
|
||||
require'telescope'.extensions.projects.projects{}
|
||||
```
|
||||
|
||||
#### Telescope mappings
|
||||
|
||||
**project.nvim** comes with the following mappings:
|
||||
|
||||
| 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 |
|
||||
|
||||
## API
|
||||
|
||||
Get a list of recent projects:
|
||||
|
||||
```lua
|
||||
local project_nvim = require("project_nvim")
|
||||
local recent_projects = project_nvim.get_recent_projects()
|
||||
|
||||
print(vim.inspect(recent_projects))
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
- All pull requests are welcome.
|
||||
- If you encounter bugs please open an issue.
|
||||
@ -0,0 +1,66 @@
|
||||
local M = {}
|
||||
|
||||
---@class ProjectOptions
|
||||
M.defaults = {
|
||||
-- Manual mode doesn't automatically change your root directory, so you have
|
||||
-- the option to manually do so using `:ProjectRoot` command.
|
||||
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 rearangne the detection methods.
|
||||
detection_methods = { "lsp", "pattern" },
|
||||
|
||||
-- All the patterns used to detect root dir, when **"pattern"** is in
|
||||
-- detection_methods
|
||||
patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json" },
|
||||
|
||||
-- Table of lsp clients to ignore by name
|
||||
-- eg: { "efm", ... }
|
||||
ignore_lsp = {},
|
||||
|
||||
-- Don't calculate root dir on specific directories
|
||||
-- Ex: { "~/.cargo/*", ... }
|
||||
exclude_dirs = {},
|
||||
|
||||
-- Show hidden files in telescope
|
||||
show_hidden = false,
|
||||
|
||||
-- When set to false, you will get a message when project.nvim changes your
|
||||
-- directory.
|
||||
silent_chdir = true,
|
||||
|
||||
-- What scope to change the directory, valid options are
|
||||
-- * global (default)
|
||||
-- * tab
|
||||
-- * win
|
||||
scope_chdir = 'global',
|
||||
|
||||
-- Path where project.nvim will store the project history for use in
|
||||
-- telescope
|
||||
datapath = vim.fn.stdpath("data"),
|
||||
}
|
||||
|
||||
---@type ProjectOptions
|
||||
M.options = {}
|
||||
|
||||
M.setup = function(options)
|
||||
M.options = vim.tbl_deep_extend("force", M.defaults, options or {})
|
||||
|
||||
local glob = require("project_nvim.utils.globtopattern")
|
||||
local home = vim.fn.expand("~")
|
||||
M.options.exclude_dirs = vim.tbl_map(function(pattern)
|
||||
if vim.startswith(pattern, "~/") then
|
||||
pattern = home .. "/" .. pattern:sub(3, #pattern)
|
||||
end
|
||||
return glob.globtopattern(pattern)
|
||||
end, M.options.exclude_dirs)
|
||||
|
||||
vim.opt.autochdir = false -- implicitly unset autochdir
|
||||
|
||||
require("project_nvim.utils.path").init()
|
||||
require("project_nvim.project").init()
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,8 @@
|
||||
local config = require("project_nvim.config")
|
||||
local history = require("project_nvim.utils.history")
|
||||
local M = {}
|
||||
|
||||
M.setup = config.setup
|
||||
M.get_recent_projects = history.get_recent_projects
|
||||
|
||||
return M
|
||||
@ -0,0 +1,286 @@
|
||||
local config = require("project_nvim.config")
|
||||
local history = require("project_nvim.utils.history")
|
||||
local glob = require("project_nvim.utils.globtopattern")
|
||||
local path = require("project_nvim.utils.path")
|
||||
local uv = vim.loop
|
||||
local M = {}
|
||||
|
||||
-- Internal states
|
||||
M.attached_lsp = false
|
||||
M.last_project = nil
|
||||
|
||||
function M.find_lsp_root()
|
||||
-- Get lsp client for current buffer
|
||||
-- Returns nil or string
|
||||
local buf_ft = vim.api.nvim_buf_get_option(0, "filetype")
|
||||
local clients = vim.lsp.buf_get_clients()
|
||||
if next(clients) == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
for _, client in pairs(clients) do
|
||||
local filetypes = client.config.filetypes
|
||||
if filetypes and vim.tbl_contains(filetypes, buf_ft) then
|
||||
if not vim.tbl_contains(config.options.ignore_lsp, client.name) then
|
||||
return client.config.root_dir, client.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function M.find_pattern_root()
|
||||
local search_dir = vim.fn.expand("%:p:h", true)
|
||||
if vim.fn.has("win32") > 0 then
|
||||
search_dir = search_dir:gsub("\\", "/")
|
||||
end
|
||||
|
||||
local last_dir_cache = ""
|
||||
local curr_dir_cache = {}
|
||||
|
||||
local function get_parent(path)
|
||||
path = path:match("^(.*)/")
|
||||
if path == "" then
|
||||
path = "/"
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
local function get_files(file_dir)
|
||||
last_dir_cache = file_dir
|
||||
curr_dir_cache = {}
|
||||
|
||||
local dir = uv.fs_scandir(file_dir)
|
||||
if dir == nil then
|
||||
return
|
||||
end
|
||||
|
||||
while true do
|
||||
local file = uv.fs_scandir_next(dir)
|
||||
if file == nil then
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(curr_dir_cache, file)
|
||||
end
|
||||
end
|
||||
|
||||
local function is(dir, identifier)
|
||||
dir = dir:match(".*/(.*)")
|
||||
return dir == identifier
|
||||
end
|
||||
|
||||
local function sub(dir, identifier)
|
||||
local path = get_parent(dir)
|
||||
while true do
|
||||
if is(path, identifier) then
|
||||
return true
|
||||
end
|
||||
local current = path
|
||||
path = get_parent(path)
|
||||
if current == path then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function child(dir, identifier)
|
||||
local path = get_parent(dir)
|
||||
return is(path, identifier)
|
||||
end
|
||||
|
||||
local function has(dir, identifier)
|
||||
if last_dir_cache ~= dir then
|
||||
get_files(dir)
|
||||
end
|
||||
local pattern = glob.globtopattern(identifier)
|
||||
for _, file in ipairs(curr_dir_cache) do
|
||||
if file:match(pattern) ~= nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function match(dir, pattern)
|
||||
local first_char = pattern:sub(1, 1)
|
||||
if first_char == "=" then
|
||||
return is(dir, pattern:sub(2))
|
||||
elseif first_char == "^" then
|
||||
return sub(dir, pattern:sub(2))
|
||||
elseif first_char == ">" then
|
||||
return child(dir, pattern:sub(2))
|
||||
else
|
||||
return has(dir, pattern)
|
||||
end
|
||||
end
|
||||
|
||||
-- breadth-first search
|
||||
while true do
|
||||
for _, pattern in ipairs(config.options.patterns) do
|
||||
local exclude = false
|
||||
if pattern:sub(1, 1) == "!" then
|
||||
exclude = true
|
||||
pattern = pattern:sub(2)
|
||||
end
|
||||
if match(search_dir, pattern) then
|
||||
if exclude then
|
||||
break
|
||||
else
|
||||
return search_dir, "pattern " .. pattern
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local parent = get_parent(search_dir)
|
||||
if parent == search_dir or parent == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
search_dir = parent
|
||||
end
|
||||
end
|
||||
|
||||
---@diagnostic disable-next-line: unused-local
|
||||
local on_attach_lsp = function(client, bufnr)
|
||||
M.on_buf_enter() -- Recalculate root dir after lsp attaches
|
||||
end
|
||||
|
||||
function M.attach_to_lsp()
|
||||
if M.attached_lsp then
|
||||
return
|
||||
end
|
||||
|
||||
local _start_client = vim.lsp.start_client
|
||||
vim.lsp.start_client = function(lsp_config)
|
||||
if lsp_config.on_attach == nil then
|
||||
lsp_config.on_attach = on_attach_lsp
|
||||
else
|
||||
local _on_attach = lsp_config.on_attach
|
||||
lsp_config.on_attach = function(client, bufnr)
|
||||
on_attach_lsp(client, bufnr)
|
||||
_on_attach(client, bufnr)
|
||||
end
|
||||
end
|
||||
return _start_client(lsp_config)
|
||||
end
|
||||
|
||||
M.attached_lsp = true
|
||||
end
|
||||
|
||||
function M.set_pwd(dir, method)
|
||||
if dir ~= nil then
|
||||
M.last_project = dir
|
||||
table.insert(history.session_projects, dir)
|
||||
|
||||
if vim.fn.getcwd() ~= dir then
|
||||
local scope_chdir = config.options.scope_chdir
|
||||
if scope_chdir == 'global' then
|
||||
vim.api.nvim_set_current_dir(dir)
|
||||
elseif scope_chdir == 'tab' then
|
||||
vim.cmd('tcd ' .. dir)
|
||||
elseif scope_chdir == 'win' then
|
||||
vim.cmd('lcd ' .. dir)
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
if config.options.silent_chdir == false then
|
||||
vim.notify("Set CWD to " .. dir .. " using " .. method)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function M.get_project_root()
|
||||
-- returns project root, as well as method
|
||||
for _, detection_method in ipairs(config.options.detection_methods) do
|
||||
if detection_method == "lsp" then
|
||||
local root, lsp_name = M.find_lsp_root()
|
||||
if root ~= nil then
|
||||
return root, '"' .. lsp_name .. '"' .. " lsp"
|
||||
end
|
||||
elseif detection_method == "pattern" then
|
||||
local root, method = M.find_pattern_root()
|
||||
if root ~= nil then
|
||||
return root, method
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.is_file()
|
||||
local buf_type = vim.api.nvim_buf_get_option(0, "buftype")
|
||||
|
||||
local whitelisted_buf_type = { "", "acwrite" }
|
||||
local is_in_whitelist = false
|
||||
for _, wtype in ipairs(whitelisted_buf_type) do
|
||||
if buf_type == wtype then
|
||||
is_in_whitelist = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not is_in_whitelist then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function M.on_buf_enter()
|
||||
if vim.v.vim_did_enter == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
if not M.is_file() then
|
||||
return
|
||||
end
|
||||
|
||||
local current_dir = vim.fn.expand("%:p:h", true)
|
||||
if not path.exists(current_dir) or path.is_excluded(current_dir) then
|
||||
return
|
||||
end
|
||||
|
||||
local root, method = M.get_project_root()
|
||||
M.set_pwd(root, method)
|
||||
end
|
||||
|
||||
function M.add_project_manually()
|
||||
local current_dir = vim.fn.expand("%:p:h", true)
|
||||
M.set_pwd(current_dir, 'manual')
|
||||
end
|
||||
|
||||
function M.init()
|
||||
local autocmds = {}
|
||||
if not config.options.manual_mode then
|
||||
autocmds[#autocmds + 1] = 'autocmd VimEnter,BufEnter * ++nested lua require("project_nvim.project").on_buf_enter()'
|
||||
|
||||
if vim.tbl_contains(config.options.detection_methods, "lsp") then
|
||||
M.attach_to_lsp()
|
||||
end
|
||||
end
|
||||
|
||||
vim.cmd([[
|
||||
command! ProjectRoot lua require("project_nvim.project").on_buf_enter()
|
||||
command! AddProject lua require("project_nvim.project").add_project_manually()
|
||||
]])
|
||||
|
||||
autocmds[#autocmds + 1] =
|
||||
'autocmd VimLeavePre * lua require("project_nvim.utils.history").write_projects_to_history()'
|
||||
|
||||
vim.cmd([[augroup project_nvim
|
||||
au!
|
||||
]])
|
||||
for _, value in ipairs(autocmds) do
|
||||
vim.cmd(value)
|
||||
end
|
||||
vim.cmd("augroup END")
|
||||
|
||||
history.read_projects_from_history()
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,139 @@
|
||||
-- Credits for this module goes to: David Manura
|
||||
-- https://github.com/davidm/lua-glob-pattern
|
||||
|
||||
local M = { _TYPE = "module", _NAME = "globtopattern", _VERSION = "0.2.1.20120406" }
|
||||
|
||||
function M.globtopattern(g)
|
||||
-- Some useful references:
|
||||
-- - apr_fnmatch in Apache APR. For example,
|
||||
-- http://apr.apache.org/docs/apr/1.3/group__apr__fnmatch.html
|
||||
-- which cites POSIX 1003.2-1992, section B.6.
|
||||
|
||||
local p = "^" -- pattern being built
|
||||
local i = 0 -- index in g
|
||||
local c -- char at index i in g.
|
||||
|
||||
-- unescape glob char
|
||||
local function unescape()
|
||||
if c == "\\" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- escape pattern char
|
||||
local function escape(c)
|
||||
return c:match("^%w$") and c or "%" .. c
|
||||
end
|
||||
|
||||
-- Convert tokens at end of charset.
|
||||
local function charset_end()
|
||||
while 1 do
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "]" then
|
||||
p = p .. "]"
|
||||
break
|
||||
else
|
||||
if not unescape() then
|
||||
break
|
||||
end
|
||||
local c1 = c
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "-" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "]" then
|
||||
p = p .. escape(c1) .. "%-]"
|
||||
break
|
||||
else
|
||||
if not unescape() then
|
||||
break
|
||||
end
|
||||
p = p .. escape(c1) .. "-" .. escape(c)
|
||||
end
|
||||
elseif c == "]" then
|
||||
p = p .. escape(c1) .. "]"
|
||||
break
|
||||
else
|
||||
p = p .. escape(c1)
|
||||
i = i - 1 -- put back
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Convert tokens in charset.
|
||||
local function charset()
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" or c == "]" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "^" or c == "!" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "]" then
|
||||
-- ignored
|
||||
else
|
||||
p = p .. "[^"
|
||||
if not charset_end() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
p = p .. "["
|
||||
if not charset_end() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Convert tokens.
|
||||
while 1 do
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = p .. "$"
|
||||
break
|
||||
elseif c == "?" then
|
||||
p = p .. "."
|
||||
elseif c == "*" then
|
||||
p = p .. ".*"
|
||||
elseif c == "[" then
|
||||
if not charset() then
|
||||
break
|
||||
end
|
||||
elseif c == "\\" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = p .. "\\$"
|
||||
break
|
||||
end
|
||||
p = p .. escape(c)
|
||||
else
|
||||
p = p .. escape(c)
|
||||
end
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,178 @@
|
||||
local path = require("project_nvim.utils.path")
|
||||
local uv = vim.loop
|
||||
local M = {}
|
||||
local is_windows = vim.fn.has('win32') or vim.fn.has('wsl')
|
||||
|
||||
M.recent_projects = nil -- projects from previous neovim sessions
|
||||
M.session_projects = {} -- projects from current neovim session
|
||||
M.has_watch_setup = false
|
||||
|
||||
local function open_history(mode, callback)
|
||||
if callback ~= nil then -- async
|
||||
path.create_scaffolding(function(_, _)
|
||||
uv.fs_open(path.historyfile, mode, 438, callback)
|
||||
end)
|
||||
else -- sync
|
||||
path.create_scaffolding()
|
||||
return uv.fs_open(path.historyfile, mode, 438)
|
||||
end
|
||||
end
|
||||
|
||||
local function dir_exists(dir)
|
||||
local stat = uv.fs_stat(dir)
|
||||
if stat ~= nil and stat.type == "directory" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function normalise_path(path_to_normalise)
|
||||
local normalised_path = path_to_normalise:gsub("\\", "/"):gsub("//", "/")
|
||||
|
||||
if is_windows then
|
||||
normalised_path = normalised_path:sub(1,1):lower()..normalised_path:sub(2)
|
||||
end
|
||||
|
||||
return normalised_path
|
||||
end
|
||||
|
||||
local function delete_duplicates(tbl)
|
||||
local cache_dict = {}
|
||||
for _, v in ipairs(tbl) do
|
||||
local normalised_path = normalise_path(v)
|
||||
if cache_dict[normalised_path] == nil then
|
||||
cache_dict[normalised_path] = 1
|
||||
else
|
||||
cache_dict[normalised_path] = cache_dict[normalised_path] + 1
|
||||
end
|
||||
end
|
||||
|
||||
local res = {}
|
||||
for _, v in ipairs(tbl) do
|
||||
local normalised_path = normalise_path(v)
|
||||
if cache_dict[normalised_path] == 1 then
|
||||
table.insert(res, normalised_path)
|
||||
else
|
||||
cache_dict[normalised_path] = cache_dict[normalised_path] - 1
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function M.delete_project(project)
|
||||
for k, v in ipairs(M.recent_projects) do
|
||||
if v == project.value then
|
||||
M.recent_projects[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function deserialize_history(history_data)
|
||||
-- split data to table
|
||||
local projects = {}
|
||||
for s in history_data:gmatch("[^\r\n]+") do
|
||||
if not path.is_excluded(s) and dir_exists(s) then
|
||||
table.insert(projects, s)
|
||||
end
|
||||
end
|
||||
|
||||
projects = delete_duplicates(projects)
|
||||
|
||||
M.recent_projects = projects
|
||||
end
|
||||
|
||||
local function setup_watch()
|
||||
-- Only runs once
|
||||
if M.has_watch_setup == false then
|
||||
M.has_watch_setup = true
|
||||
local event = uv.new_fs_event()
|
||||
if event == nil then
|
||||
return
|
||||
end
|
||||
event:start(path.projectpath, {}, function(err, _, events)
|
||||
if err ~= nil then
|
||||
return
|
||||
end
|
||||
if events["change"] then
|
||||
M.recent_projects = nil
|
||||
M.read_projects_from_history()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function M.read_projects_from_history()
|
||||
open_history("r", function(_, fd)
|
||||
setup_watch()
|
||||
if fd ~= nil then
|
||||
uv.fs_fstat(fd, function(_, stat)
|
||||
if stat ~= nil then
|
||||
uv.fs_read(fd, stat.size, -1, function(_, data)
|
||||
uv.fs_close(fd, function(_, _) end)
|
||||
deserialize_history(data)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function sanitize_projects()
|
||||
local tbl = {}
|
||||
if M.recent_projects ~= nil then
|
||||
vim.list_extend(tbl, M.recent_projects)
|
||||
vim.list_extend(tbl, M.session_projects)
|
||||
else
|
||||
tbl = M.session_projects
|
||||
end
|
||||
|
||||
tbl = delete_duplicates(tbl)
|
||||
|
||||
local real_tbl = {}
|
||||
for _, dir in ipairs(tbl) do
|
||||
if dir_exists(dir) then
|
||||
table.insert(real_tbl, dir)
|
||||
end
|
||||
end
|
||||
|
||||
return real_tbl
|
||||
end
|
||||
|
||||
function M.get_recent_projects()
|
||||
return sanitize_projects()
|
||||
end
|
||||
|
||||
function M.write_projects_to_history()
|
||||
-- Unlike read projects, write projects is synchronous
|
||||
-- because it runs when vim ends
|
||||
local mode = "w"
|
||||
if M.recent_projects == nil then
|
||||
mode = "a"
|
||||
end
|
||||
local file = open_history(mode)
|
||||
|
||||
if file ~= nil then
|
||||
local res = sanitize_projects()
|
||||
|
||||
-- Trim table to last 100 entries
|
||||
local len_res = #res
|
||||
local tbl_out
|
||||
if #res > 100 then
|
||||
tbl_out = vim.list_slice(res, len_res - 100, len_res)
|
||||
else
|
||||
tbl_out = res
|
||||
end
|
||||
|
||||
-- Transform table to string
|
||||
local out = ""
|
||||
for _, v in ipairs(tbl_out) do
|
||||
out = out .. v .. "\n"
|
||||
end
|
||||
|
||||
-- Write string out to file and close
|
||||
uv.fs_write(file, out, -1)
|
||||
uv.fs_close(file)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,37 @@
|
||||
local config = require("project_nvim.config")
|
||||
local uv = vim.loop
|
||||
local M = {}
|
||||
|
||||
M.datapath = vim.fn.stdpath("data") -- directory
|
||||
M.projectpath = M.datapath .. "/project_nvim" -- directory
|
||||
M.historyfile = M.projectpath .. "/project_history" -- file
|
||||
|
||||
function M.init()
|
||||
M.datapath = require("project_nvim.config").options.datapath
|
||||
M.projectpath = M.datapath .. "/project_nvim" -- directory
|
||||
M.historyfile = M.projectpath .. "/project_history" -- file
|
||||
end
|
||||
|
||||
function M.create_scaffolding(callback)
|
||||
if callback ~= nil then -- async
|
||||
uv.fs_mkdir(M.projectpath, 448, callback)
|
||||
else -- sync
|
||||
uv.fs_mkdir(M.projectpath, 448)
|
||||
end
|
||||
end
|
||||
|
||||
function M.is_excluded(dir)
|
||||
for _, dir_pattern in ipairs(config.options.exclude_dirs) do
|
||||
if dir:match(dir_pattern) ~= nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function M.exists(path)
|
||||
return vim.fn.empty(vim.fn.glob(path)) == 0
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,180 @@
|
||||
-- Inspiration from:
|
||||
-- https://github.com/nvim-telescope/telescope-project.nvim
|
||||
local has_telescope, telescope = pcall(require, "telescope")
|
||||
|
||||
if not has_telescope then
|
||||
return
|
||||
end
|
||||
|
||||
local finders = require("telescope.finders")
|
||||
local pickers = require("telescope.pickers")
|
||||
local telescope_config = require("telescope.config").values
|
||||
local actions = require("telescope.actions")
|
||||
local state = require("telescope.actions.state")
|
||||
local builtin = require("telescope.builtin")
|
||||
local entry_display = require("telescope.pickers.entry_display")
|
||||
|
||||
local history = require("project_nvim.utils.history")
|
||||
local project = require("project_nvim.project")
|
||||
local config = require("project_nvim.config")
|
||||
|
||||
----------
|
||||
-- Actions
|
||||
----------
|
||||
|
||||
local function create_finder()
|
||||
local results = history.get_recent_projects()
|
||||
|
||||
-- Reverse results
|
||||
for i = 1, math.floor(#results / 2) do
|
||||
results[i], results[#results - i + 1] = results[#results - i + 1], results[i]
|
||||
end
|
||||
local displayer = entry_display.create({
|
||||
separator = " ",
|
||||
items = {
|
||||
{
|
||||
width = 30,
|
||||
},
|
||||
{
|
||||
remaining = true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
local function make_display(entry)
|
||||
return displayer({ entry.name, { entry.value, "Comment" } })
|
||||
end
|
||||
|
||||
return finders.new_table({
|
||||
results = results,
|
||||
entry_maker = function(entry)
|
||||
local name = vim.fn.fnamemodify(entry, ":t")
|
||||
return {
|
||||
display = make_display,
|
||||
name = name,
|
||||
value = entry,
|
||||
ordinal = name .. " " .. entry,
|
||||
}
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local function change_working_directory(prompt_bufnr, prompt)
|
||||
local selected_entry = state.get_selected_entry(prompt_bufnr)
|
||||
if selected_entry == nil then
|
||||
actions.close(prompt_bufnr)
|
||||
return
|
||||
end
|
||||
local project_path = selected_entry.value
|
||||
if prompt == true then
|
||||
actions._close(prompt_bufnr, true)
|
||||
else
|
||||
actions.close(prompt_bufnr)
|
||||
end
|
||||
local cd_successful = project.set_pwd(project_path, "telescope")
|
||||
return project_path, cd_successful
|
||||
end
|
||||
|
||||
local function find_project_files(prompt_bufnr)
|
||||
local project_path, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd = project_path,
|
||||
hidden = config.options.show_hidden,
|
||||
mode = "insert",
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.find_files(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function browse_project_files(prompt_bufnr)
|
||||
local project_path, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd = project_path,
|
||||
hidden = config.options.show_hidden,
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.file_browser(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function search_in_project_files(prompt_bufnr)
|
||||
local project_path, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd = project_path,
|
||||
hidden = config.options.show_hidden,
|
||||
mode = "insert",
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.live_grep(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function recent_project_files(prompt_bufnr)
|
||||
local _, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd_only = true,
|
||||
hidden = config.options.show_hidden,
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.oldfiles(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function delete_project(prompt_bufnr)
|
||||
local selectedEntry = state.get_selected_entry(prompt_bufnr)
|
||||
if selectedEntry == nil then
|
||||
actions.close(prompt_bufnr)
|
||||
return
|
||||
end
|
||||
local choice = vim.fn.confirm("Delete '" .. selectedEntry.value .. "' from project list?", "&Yes\n&No", 2)
|
||||
|
||||
if choice == 1 then
|
||||
history.delete_project(selectedEntry)
|
||||
|
||||
local finder = create_finder()
|
||||
state.get_current_picker(prompt_bufnr):refresh(finder, {
|
||||
reset_prompt = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---Main entrypoint for Telescope.
|
||||
---@param opts table
|
||||
local function projects(opts)
|
||||
opts = opts or {}
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt_title = "Recent Projects",
|
||||
finder = create_finder(),
|
||||
previewer = false,
|
||||
sorter = telescope_config.generic_sorter(opts),
|
||||
attach_mappings = function(prompt_bufnr, map)
|
||||
map("n", "f", find_project_files)
|
||||
map("n", "b", browse_project_files)
|
||||
map("n", "d", delete_project)
|
||||
map("n", "s", search_in_project_files)
|
||||
map("n", "r", recent_project_files)
|
||||
map("n", "w", change_working_directory)
|
||||
|
||||
map("i", "<c-f>", find_project_files)
|
||||
map("i", "<c-b>", browse_project_files)
|
||||
map("i", "<c-d>", delete_project)
|
||||
map("i", "<c-s>", search_in_project_files)
|
||||
map("i", "<c-r>", recent_project_files)
|
||||
map("i", "<c-w>", change_working_directory)
|
||||
|
||||
local on_project_selected = function()
|
||||
find_project_files(prompt_bufnr)
|
||||
end
|
||||
actions.select_default:replace(on_project_selected)
|
||||
return true
|
||||
end,
|
||||
}):find()
|
||||
end
|
||||
|
||||
return telescope.register_extension({
|
||||
exports = {
|
||||
projects = projects,
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user