Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to use locate to list repos #9

Merged
merged 7 commits into from
Dec 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,12 @@ use {

### Required

* [`fd`][] to find the repositories on the filesystem
* [`fd`][] to find the repositories on the filesystem with `list`
* [`plocate`][] or [`locate`][] to find the repositories on the filesystem with `cached_list`

[`fd`]: https://github.com/sharkdp/fd
[`locate`]: https://man.archlinux.org/man/locate.1
[`plocate`]: https://man.archlinux.org/man/plocate.1

### Optional

Expand Down Expand Up @@ -175,12 +178,63 @@ Here is how you can use this plugin with various SCM:

Is your favorite SCM missing? It should be straightforward to support it by changing the pattern parameter. If you want it to be considered for addition here, open a PR!

### cached_list

`:Telescope repo cached_list`

This relies on a `locate` command to find repositories. This should be much faster than the `list` command, as it relies on a pre-built index but results may be stalled.

*Note*: at this point, the plugin does not manage index update. Updating the index often requires to run a command like `updatedb` as root.

#### Troubleshooting

You should try to run:
```
sudo updatedb
```
if you encounter any problems. If it’s not the case by default, you should automate such index update with for instance `cron` or `systemd-timers`. See https://wiki.archlinux.org/title/Locate.

#### Options

Options are the similar to `repo list`, bearing in mind that we use `locate` instead of `fd`. Note that:

* `fd_opts` is not supported, as we don’t use `fd`

#### Examples

##### Exclude Irrelevant Results

Chances are you will get results from folders you don’t care about like `.cache` or `.cargo`. In that case, you can use the `file_ignore_patterns` option of Telescope, like so (these are [Lua regexes](https://www.lua.org/manual/5.1/manual.html#5.4.1)).

Hide all git repositories that may be in `.cache` or `.cargo`:
```lua
:lua require'telescope'.extensions.repo.cached_list{file_ignore_patterns={"/%.cache/", "/%.cargo/"}}
```
###### Notes

* These patterns are used to filter the output of the `locate` command, so they don’t speed up the search in any way. You should use them mainly to exclude git repositories you won’t want to jump into, not in the hope to enhance performance.
* The `%.` in Lua regex is an escaped `.` as `.` matches any characters.
* These patterns are applied against whole paths like `/home/myuser/.cache/some/dir`, so if you want to exclude only `/home/myuser/.cache`, you need a more complicated pattern like so:
```lua
:lua require'telescope'.extensions.repo.cached_list{file_ignore_patterns={"^".. vim.env.HOME .. "/%.cache/", "^".. vim.env.HOME .. "/%.cargo/"}}
```

##### Use With Other SCMs

Here is how you can use this plugin with various SCM (we match on the whole path with `locate`, so patterns differ slightly from `repo list`: notice the `^` that becomes a `/`):

| SCM | Command |
|--------|----------------------------------------------------------------------------|
| git | `:Telescope repo list` or `lua require'telescope'.extensions.repo.list{}` |
| pijul | `lua require'telescope'.extensions.repo.list{pattern=[[/\.pijul$]]}` |
| hg | `lua require'telescope'.extensions.repo.list{pattern=[[/\.hg$]]}` |
| fossil | `lua require'telescope'.extensions.repo.list{pattern=[[/\.fslckout$]]}` |

## FAQ

### Getting the repository list is slow

You can use your `.fdignore` to exclude some folders from your filesystem. You can even use a custom ignore file with the `--ignore-file` option, like so:

If `:Telescope repo list` is slow, you can use your `.fdignore` to exclude some folders from your filesystem. . You can even use a custom ignore file with the `--ignore-file` option, like so:
```
lua require'telescope'.extensions.repo.list{fd_opts=[[--ignore-file=myignorefile]]}
```
Expand Down
3 changes: 2 additions & 1 deletion lua/telescope/_extensions/repo.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local repo_builtin = require'telescope._extensions.repo_builtin'

return require'telescope'.register_extension{
exports = {
list = repo_builtin.list
list = repo_builtin.list,
cached_list = repo_builtin.cached_list,
},
}
121 changes: 86 additions & 35 deletions lua/telescope/_extensions/repo_builtin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ local conf = require'telescope.config'.values
local entry_display = require'telescope.pickers.entry_display'
local finders = require'telescope.finders'
local from_entry = require'telescope.from_entry'
local log = require "telescope.log"
local pickers = require'telescope.pickers'
local previewers = require'telescope.previewers'
local utils = require'telescope.utils'
Expand Down Expand Up @@ -34,7 +35,8 @@ local function search_doc(dir)
return nil
end

local function gen_from_ghq(opts)
-- Was gen_from_ghq in telescope-ghq.nvim
local function gen_from_fd(opts)
local displayer = entry_display.create{
items = {{}},
}
Expand Down Expand Up @@ -74,48 +76,34 @@ local function gen_from_ghq(opts)
end
end

-- Wrap entries to remove the part we used to detect the VCS. For instance, for git:
-- - we get entries like “/home/me/repo/.git”
-- - we want to send entries like “/home/me/repo”
local function gen_from_locate_wrapper(opts)
log.trace "Called gen_from_locate_wrapper"
-- TODO Make this a wrapper over any function, not just gen_from_fd
-- TODO It’s not great for performance to parse paths in the whole list like this
return function(line_with_dotgit)
log.trace("line_with_dotgit " .. line_with_dotgit)
local line = Path:new(line_with_dotgit):parent().filename
return gen_from_fd(opts)(line)
end
end

local function project_files(opts)
local ok = pcall(require'telescope.builtin'.git_files, opts)
if not ok then require'telescope.builtin'.find_files(opts) end
end

-- Find under what name fd is installed
local function find_fd_binary()
for _, binary in ipairs({'fdfind', 'fd'}) do
if vim.fn.executable(binary) == 1 then
return binary
end
local function call_picker(opts, command, prompt_title_supplement)
local prompt_title = 'Git repositories'
if prompt_title_supplement ~= nil then
prompt_title = prompt_title .. prompt_title_supplement
end

error("fd not found, is fd installed?")
end

M.list = function(opts)
opts = opts or {}
opts.bin = opts.bin or find_fd_binary()
opts.cwd = vim.env.HOME
opts.entry_maker = utils.get_lazy_default(opts.entry_maker, gen_from_ghq, opts)

local fd_command = {opts.bin}
local repo_pattern = opts.pattern or [[^\.git$]]

-- Don’t filter only on directories with fd as git worktrees actually have a
-- .git file in them.
local find_repo_opts = {'--hidden', '--case-sensitive', '--absolute-path'}
local find_user_opts = opts.fd_opts or {}
local find_exec_opts = {'--exec', 'echo', [[{//}]], ';'}
local find_pattern_opts = {repo_pattern}

table.insert(fd_command, find_repo_opts)
table.insert(fd_command, find_user_opts)
table.insert(fd_command, find_exec_opts)
table.insert(fd_command, find_pattern_opts)
fd_command = vim.tbl_flatten(fd_command)

pickers.new(opts, {
prompt_title = 'Git repositories',
prompt_title = prompt_title,
finder = finders.new_oneshot_job(
fd_command,
command,
opts
),
previewer = previewers.new_termopen_previewer{
Expand Down Expand Up @@ -155,4 +143,67 @@ M.list = function(opts)
}):find()
end

-- List of repos built using locate (or variants)
M.cached_list = function(opts)
opts = opts or {}
opts.entry_maker = utils.get_lazy_default(opts.entry_maker, gen_from_locate_wrapper, opts)
opts.cwd = vim.env.HOME
opts.bin = opts.bin and vim.fn.expand(opts.bin) or nil
-- Use alternative locate if possible
if opts.bin == nil then
if vim.fn.executable'plocate' == 1 then
opts.bin = 'plocate'
elseif vim.fn.executable'locate' == 1 then -- Fallback
opts.bin = 'locate'
else
error "Please install locate (or one of its alternatives)"
end
end
local bin = vim.fn.expand(opts.bin)

local repo_pattern = opts.pattern or [[/\.git$]] -- We match on the whole path
local locate_opts = opts.locate_opts or {}
local locate_command = vim.tbl_flatten{{bin}, locate_opts, {'-r', repo_pattern}}
log.trace("locate_command: "..vim.inspect(locate_command))

call_picker(opts, locate_command, ' (cached)')
end

-- Find under what name fd is installed
local function find_fd_binary()
for _, binary in ipairs({'fdfind', 'fd'}) do
if vim.fn.executable(binary) == 1 then
return binary
end
end

error("fd not found, is fd installed?")
end

-- Always up to date list of repos built using fd
M.list = function(opts)
opts = opts or {}
opts.entry_maker = utils.get_lazy_default(opts.entry_maker, gen_from_fd, opts)
opts.bin = opts.bin or find_fd_binary()
opts.cwd = vim.env.HOME

local fd_command = {opts.bin}
local repo_pattern = opts.pattern or [[^\.git$]]

-- Don’t filter only on directories with fd as git worktrees actually have a
-- .git file in them.
local find_repo_opts = {'--hidden', '--case-sensitive', '--absolute-path'}
local find_user_opts = opts.fd_opts or {}
local find_exec_opts = {'--exec', 'echo', [[{//}]], ';'}
local find_pattern_opts = {repo_pattern}

table.insert(fd_command, find_repo_opts)
table.insert(fd_command, find_user_opts)
table.insert(fd_command, find_exec_opts)
table.insert(fd_command, find_pattern_opts)
fd_command = vim.tbl_flatten(fd_command)

call_picker(opts, fd_command, ' (built on the fly)')
end

return M