feat: Major improvements in API. Particularly relating to entries.
This commit is contained in:
@@ -42,22 +42,31 @@ function actions.goto_file_selection(prompt_bufnr)
|
||||
print("[telescope] Nothing currently selected")
|
||||
return
|
||||
else
|
||||
local filename, row, col
|
||||
if entry.filename then
|
||||
filename = entry.filename
|
||||
-- TODO: Check for off-by-one
|
||||
row = entry.row or entry.lnum
|
||||
col = entry.col
|
||||
else
|
||||
-- TODO: Might want to remove this and force people
|
||||
-- to put stuff into `filename`
|
||||
local value = entry.value
|
||||
if not value then
|
||||
print("Could not do anything with blank line...")
|
||||
return
|
||||
end
|
||||
|
||||
-- TODO: This is not great.
|
||||
if type(value) == "table" then
|
||||
value = entry.display
|
||||
end
|
||||
|
||||
local sections = vim.split(value, ":")
|
||||
|
||||
local filename = sections[1]
|
||||
local row = tonumber(sections[2])
|
||||
local col = tonumber(sections[3])
|
||||
filename = sections[1]
|
||||
row = tonumber(sections[2])
|
||||
col = tonumber(sections[3])
|
||||
end
|
||||
|
||||
vim.cmd(string.format([[bwipeout! %s]], prompt_bufnr))
|
||||
|
||||
|
||||
@@ -11,94 +11,69 @@ This will use the default configuration options.
|
||||
Other configuration options still in flux at the moment
|
||||
--]]
|
||||
|
||||
-- TODO: Give some bonus weight to files we've picked before
|
||||
-- TODO: Give some bonus weight to oldfiles
|
||||
|
||||
local actions = require('telescope.actions')
|
||||
local finders = require('telescope.finders')
|
||||
local make_entry = require('telescope.make_entry')
|
||||
local previewers = require('telescope.previewers')
|
||||
local pickers = require('telescope.pickers')
|
||||
local sorters = require('telescope.sorters')
|
||||
local utils = require('telescope.utils')
|
||||
|
||||
local flatten = vim.tbl_flatten
|
||||
|
||||
-- TODO: Support silver search here.
|
||||
-- TODO: Support normal grep here (in case neither are installed).
|
||||
local vimgrep_arguments = {'rg', '--color=never', '--no-heading', '--with-filename', '--line-number', '--column'}
|
||||
|
||||
local builtin = {}
|
||||
|
||||
builtin.git_files = function(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local make_entry = (
|
||||
opts.shorten_path
|
||||
and function(value)
|
||||
local result = {
|
||||
valid = true,
|
||||
display = utils.path_shorten(value),
|
||||
ordinal = value,
|
||||
value = value
|
||||
}
|
||||
|
||||
return result
|
||||
end)
|
||||
|
||||
or nil
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Git File',
|
||||
finder = finders.new_oneshot_job({ "git", "ls-files" }, make_entry),
|
||||
finder = finders.new_oneshot_job(
|
||||
{ "git", "ls-files", "-o", "--exclude-standard", "-c" },
|
||||
make_entry.gen_from_file(opts)
|
||||
),
|
||||
previewer = previewers.cat,
|
||||
sorter = sorters.get_fuzzy_file(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
builtin.live_grep = function(opts)
|
||||
local live_grepper = finders.new {
|
||||
fn_command = function(_, prompt)
|
||||
-- TODO: Make it so that we can start searching on the first character.
|
||||
opts = opts or {}
|
||||
|
||||
local live_grepper = finders.new_job(function(prompt)
|
||||
-- TODO: Probably could add some options for smart case and whatever else rg offers.
|
||||
|
||||
if not prompt or prompt == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
return {
|
||||
command = 'rg',
|
||||
args = {"--vimgrep", prompt},
|
||||
}
|
||||
end
|
||||
}
|
||||
return flatten { vimgrep_arguments, prompt }
|
||||
end,
|
||||
opts.entry_maker or make_entry.gen_from_vimgrep(opts),
|
||||
opts.max_results
|
||||
)
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Live Grep',
|
||||
finder = live_grepper,
|
||||
previewer = previewers.vimgrep,
|
||||
}):find()
|
||||
|
||||
-- TODO: Incorporate this.
|
||||
-- Weight the results somehow to be more likely to be the ones that you've opened.
|
||||
-- local old_files = {}
|
||||
-- for _, f in ipairs(vim.v.oldfiles) do
|
||||
-- old_files[f] = true
|
||||
-- end
|
||||
|
||||
-- local oldfiles_sorter = sorters.new {
|
||||
-- scoring_function = function(prompt, entry)
|
||||
-- local line = entry.value
|
||||
|
||||
-- if not line then
|
||||
-- return
|
||||
-- end
|
||||
|
||||
-- local _, finish = string.find(line, ":")
|
||||
-- local filename = string.sub(line, 1, finish - 1)
|
||||
-- local expanded_fname = vim.fn.fnamemodify(filename, ':p')
|
||||
-- if old_files[expanded_fname] then
|
||||
-- print("Found oldfiles: ", entry.value)
|
||||
-- return 0
|
||||
-- else
|
||||
-- return 1
|
||||
-- end
|
||||
-- end
|
||||
-- }
|
||||
end
|
||||
|
||||
-- TODO: document_symbol
|
||||
-- TODO: workspace_symbol
|
||||
|
||||
builtin.lsp_references = function(opts)
|
||||
opts = opts or {}
|
||||
opts.shorten_path = utils.get_default(opts.shorten_path, true)
|
||||
|
||||
local params = vim.lsp.util.make_position_params()
|
||||
params.context = { includeDeclaration = true }
|
||||
|
||||
@@ -108,15 +83,74 @@ builtin.lsp_references = function(opts)
|
||||
vim.list_extend(locations, vim.lsp.util.locations_to_items(server_results.result) or {})
|
||||
end
|
||||
|
||||
local results = utils.quickfix_items_to_entries(locations)
|
||||
|
||||
if vim.tbl_isempty(results) then
|
||||
if vim.tbl_isempty(locations) then
|
||||
return
|
||||
end
|
||||
|
||||
local reference_picker = pickers.new(opts, {
|
||||
pickers.new(opts, {
|
||||
prompt = 'LSP References',
|
||||
finder = finders.new_table(results),
|
||||
finder = finders.new_table {
|
||||
results = locations,
|
||||
entry_maker = make_entry.gen_from_quickfix(opts),
|
||||
},
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
builtin.lsp_document_symbols = function(opts)
|
||||
local params = vim.lsp.util.make_position_params()
|
||||
local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/documentSymbol", params)
|
||||
|
||||
if not results_lsp or vim.tbl_isempty(results_lsp) then
|
||||
print("No results from textDocument/documentSymbol")
|
||||
return
|
||||
end
|
||||
|
||||
local locations = {}
|
||||
for _, server_results in pairs(results_lsp) do
|
||||
vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result, 0) or {})
|
||||
end
|
||||
|
||||
if vim.tbl_isempty(locations) then
|
||||
return
|
||||
end
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'LSP Document Symbols',
|
||||
finder = finders.new_table {
|
||||
results = locations,
|
||||
entry_maker = make_entry.gen_from_quickfix(opts)
|
||||
},
|
||||
previewer = previewers.vim_buffer,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
builtin.lsp_workspace_symbols = function(opts)
|
||||
local params = {query = opts.query or ''}
|
||||
local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params, 1000)
|
||||
|
||||
if not results_lsp or vim.tbl_isempty(results_lsp) then
|
||||
print("No results from textDocument/documentSymbol")
|
||||
return
|
||||
end
|
||||
|
||||
local locations = {}
|
||||
for _, server_results in pairs(results_lsp) do
|
||||
vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result, 0) or {})
|
||||
end
|
||||
|
||||
if vim.tbl_isempty(locations) then
|
||||
return
|
||||
end
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'LSP Workspace Symbols',
|
||||
finder = finders.new_table {
|
||||
results = locations,
|
||||
entry_maker = make_entry.gen_from_quickfix(opts)
|
||||
},
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
@@ -124,15 +158,17 @@ end
|
||||
|
||||
builtin.quickfix = function(opts)
|
||||
local locations = vim.fn.getqflist()
|
||||
local results = utils.quickfix_items_to_entries(locations)
|
||||
|
||||
if vim.tbl_isempty(results) then
|
||||
if vim.tbl_isempty(locations) then
|
||||
return
|
||||
end
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Quickfix',
|
||||
finder = finders.new_table(results),
|
||||
finder = finders.new_table {
|
||||
results = locations,
|
||||
entry_maker = make_entry.gen_from_quickfix(opts),
|
||||
},
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
@@ -146,15 +182,16 @@ builtin.loclist = function(opts)
|
||||
value.filename = filename
|
||||
end
|
||||
|
||||
local results = utils.quickfix_items_to_entries(locations)
|
||||
|
||||
if vim.tbl_isempty(results) then
|
||||
if vim.tbl_isempty(locations) then
|
||||
return
|
||||
end
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Loclist',
|
||||
finder = finders.new_table(results),
|
||||
finder = finders.new_table {
|
||||
results = locations,
|
||||
entry_maker = make_entry.gen_from_quickfix(opts),
|
||||
},
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
@@ -165,9 +202,12 @@ builtin.grep_string = function(opts)
|
||||
|
||||
local search = opts.search or vim.fn.expand("<cword>")
|
||||
|
||||
local file_picker = pickers.new(opts, {
|
||||
pickers.new(opts, {
|
||||
prompt = 'Find Word',
|
||||
finder = finders.new_oneshot_job {'rg', '--vimgrep', search},
|
||||
finder = finders.new_oneshot_job(
|
||||
flatten { vimgrep_arguments, search},
|
||||
make_entry.gen_from_vimgrep(opts)
|
||||
),
|
||||
previewer = previewers.vimgrep,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
@@ -218,27 +258,32 @@ end
|
||||
-- vim.fn.setreg("+", "nnoremap $TODO :lua require('telescope.builtin').<whatever>()<CR>")
|
||||
-- TODO: Can we just do the names instead?
|
||||
builtin.builtin = function(opts)
|
||||
opts = opts or {}
|
||||
opts.hide_filename = utils.get_default(opts.hide_filename, true)
|
||||
opts.ignore_filename = utils.get_default(opts.ignore_filename, true)
|
||||
|
||||
local objs = {}
|
||||
|
||||
for k, v in pairs(builtin) do
|
||||
local debug_info = debug.getinfo(v)
|
||||
|
||||
table.insert(objs, {
|
||||
vimgrep_str = k,
|
||||
filename = string.sub(debug_info.source, 2),
|
||||
lnum = debug_info.linedefined,
|
||||
col = 0,
|
||||
text = k,
|
||||
|
||||
start = debug_info.linedefined,
|
||||
finish = debug_info.lastlinedefined,
|
||||
})
|
||||
end
|
||||
|
||||
local entries = utils.quickfix_items_to_entries(objs)
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Telescope Builtin',
|
||||
finder = finders.new_table(entries),
|
||||
finder = finders.new_table {
|
||||
results = objs,
|
||||
entry_maker = make_entry.gen_from_quickfix(opts),
|
||||
},
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
@@ -269,14 +314,10 @@ builtin.fd = function(opts)
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Find Files',
|
||||
finder = finders.new {
|
||||
fn_command = function()
|
||||
return {
|
||||
command = fd_string,
|
||||
cwd = cwd,
|
||||
}
|
||||
end,
|
||||
},
|
||||
finder = finders.new_oneshot_job(
|
||||
{fd_string},
|
||||
make_entry.gen_from_file(opts)
|
||||
),
|
||||
previewer = previewers.cat,
|
||||
sorter = sorters.get_fuzzy_file(),
|
||||
}):find()
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
local Entry = {}
|
||||
Entry.__index = Entry
|
||||
|
||||
-- TODO: Can we / should we make it so that "display" and "ordinal" are just values, instead of functions.
|
||||
-- It seems like that's what you'd want... No need to call the functions a million times.
|
||||
|
||||
-- Pass in a table, that contains some state
|
||||
-- Table determines it's ordinal value
|
||||
function Entry:new(line_or_obj)
|
||||
if type(line_or_obj) == "string" then
|
||||
return setmetatable({
|
||||
valid = line_or_obj ~= "",
|
||||
|
||||
value = line_or_obj,
|
||||
ordinal = line_or_obj,
|
||||
display = line_or_obj,
|
||||
}, self)
|
||||
else
|
||||
return line_or_obj
|
||||
end
|
||||
end
|
||||
|
||||
function Entry:__tostring()
|
||||
return "<" .. self.display .. ">"
|
||||
end
|
||||
|
||||
return Entry
|
||||
@@ -1,5 +1,6 @@
|
||||
local Job = require('plenary.job')
|
||||
|
||||
local make_entry = require('telescope.make_entry')
|
||||
local log = require('telescope.log')
|
||||
local utils = require('telescope.utils')
|
||||
|
||||
@@ -10,35 +11,43 @@ local finders = {}
|
||||
-- FunctionFinder(my_func)
|
||||
-- JobFinder(my_job_args)
|
||||
|
||||
---@class Finder
|
||||
local Finder = {}
|
||||
local _callable_obj = function()
|
||||
local obj = {}
|
||||
|
||||
Finder.__index = Finder
|
||||
Finder.__call = function(t, ... ) return t:_find(...) end
|
||||
obj.__index = obj
|
||||
obj.__call = function(t, ...) return t:_find(...) end
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
|
||||
--[[ =============================================================
|
||||
|
||||
JobFinder
|
||||
|
||||
Uses an external Job to get results. Processes results as they arrive.
|
||||
|
||||
For more information about how Jobs are implemented, checkout 'plenary.job'
|
||||
|
||||
-- ============================================================= ]]
|
||||
local JobFinder = _callable_obj()
|
||||
|
||||
--- Create a new finder command
|
||||
---
|
||||
---@param opts table Keys:
|
||||
-- fn_command function The function to call
|
||||
function Finder:new(opts)
|
||||
function JobFinder:new(opts)
|
||||
opts = opts or {}
|
||||
|
||||
-- TODO: Add config for:
|
||||
-- - cwd
|
||||
|
||||
assert(not opts.results, "`results` should be used with finder.new_table")
|
||||
-- TODO:
|
||||
-- - `types`
|
||||
-- job
|
||||
-- pipe
|
||||
-- vim.loop.new_pipe (stdin / stdout). stdout => filter pipe
|
||||
-- rg huge_search | fzf --filter prompt_is > buffer. buffer could do stuff do w/ preview callback
|
||||
-- string
|
||||
-- list
|
||||
-- ...
|
||||
local obj = setmetatable({
|
||||
results = opts.results,
|
||||
|
||||
entry_maker = opts.entry_maker,
|
||||
entry_maker = opts.entry_maker or make_entry.from_string,
|
||||
fn_command = opts.fn_command,
|
||||
static = opts.static,
|
||||
state = {},
|
||||
@@ -51,26 +60,7 @@ function Finder:new(opts)
|
||||
return obj
|
||||
end
|
||||
|
||||
-- Probably should use the word apply here, since we're apply the callback passed to us by
|
||||
-- the picker... But I'm not sure how we want to say that.
|
||||
|
||||
-- find_incremental
|
||||
-- find_prompt
|
||||
-- process_prompt
|
||||
-- process_search
|
||||
-- do_your_job
|
||||
-- process_plz
|
||||
function Finder:_find(prompt, process_result, process_complete)
|
||||
if self.results then
|
||||
assert(type(self.results) == 'table', "self.results must be a table")
|
||||
for _, v in ipairs(self.results) do
|
||||
process_result(v)
|
||||
end
|
||||
|
||||
process_complete()
|
||||
return
|
||||
end
|
||||
|
||||
function JobFinder:_find(prompt, process_result, process_complete)
|
||||
if self.job and not self.job.is_shutdown then
|
||||
self.job:shutdown()
|
||||
end
|
||||
@@ -140,22 +130,66 @@ function Finder:_find(prompt, process_result, process_complete)
|
||||
self.job:start()
|
||||
end
|
||||
|
||||
--- Return a new Finder
|
||||
--
|
||||
--@return Finder
|
||||
finders.new = function(opts)
|
||||
return Finder:new(opts)
|
||||
--[[ =============================================================
|
||||
Static Finders
|
||||
|
||||
A static finder has results that never change.
|
||||
They are passed in directly as a result.
|
||||
-- ============================================================= ]]
|
||||
local StaticFinder = _callable_obj()
|
||||
|
||||
function StaticFinder:new(opts)
|
||||
assert(opts, "Options are required. See documentation for usage")
|
||||
|
||||
local input_results
|
||||
if vim.tbl_islist(opts) then
|
||||
input_results = opts
|
||||
else
|
||||
input_results = opts.results
|
||||
end
|
||||
|
||||
-- TODO: Is this worth making?
|
||||
-- finders.new_responsive_job = function(opts)
|
||||
-- return finders.new {
|
||||
-- maximum_results = get_default(opts.maximum_results, 2000),
|
||||
-- }
|
||||
-- end
|
||||
local entry_maker = opts.entry_maker or make_entry.gen_from_string()
|
||||
|
||||
finders.new_job = function(command_generator)
|
||||
return finders.new {
|
||||
assert(input_results)
|
||||
assert(input_results, "Results are required for static finder")
|
||||
assert(type(input_results) == 'table', "self.results must be a table")
|
||||
|
||||
local results = {}
|
||||
for _, v in ipairs(input_results) do
|
||||
table.insert(results, entry_maker(v))
|
||||
end
|
||||
|
||||
return setmetatable({ results = results }, self)
|
||||
end
|
||||
|
||||
function StaticFinder:_find(_, process_result, process_complete)
|
||||
for _, v in ipairs(self.results) do
|
||||
process_result(v)
|
||||
end
|
||||
|
||||
process_complete()
|
||||
end
|
||||
|
||||
|
||||
-- local
|
||||
|
||||
|
||||
--- Return a new Finder
|
||||
--
|
||||
-- Use at your own risk.
|
||||
-- This opts dictionary is likely to change, but you are welcome to use it right now.
|
||||
-- I will try not to change it needlessly, but I will change it sometimes and I won't feel bad.
|
||||
finders._new = function(opts)
|
||||
if opts.results then
|
||||
print("finder.new is deprecated with `results`. You should use `finder.new_table`")
|
||||
return StaticFinder:new(opts)
|
||||
end
|
||||
|
||||
return JobFinder:new(opts)
|
||||
end
|
||||
|
||||
finders.new_job = function(command_generator, entry_maker, maximum_results)
|
||||
return JobFinder:new {
|
||||
fn_command = function(_, prompt)
|
||||
local command_list = command_generator(prompt)
|
||||
if command_list == nil then
|
||||
@@ -168,19 +202,24 @@ finders.new_job = function(command_generator)
|
||||
command = command,
|
||||
args = command_list,
|
||||
}
|
||||
end
|
||||
end,
|
||||
|
||||
entry_maker = entry_maker,
|
||||
maximum_results = maximum_results,
|
||||
}
|
||||
end
|
||||
|
||||
---@param command_list string[] Command list to execute.
|
||||
---@param entry_maker function Optional: function(line: string) => table
|
||||
finders.new_oneshot_job = function(command_list, entry_maker)
|
||||
command_list = vim.deepcopy(command_list)
|
||||
|
||||
local command = table.remove(command_list, 1)
|
||||
|
||||
return finders.new {
|
||||
return JobFinder:new {
|
||||
static = true,
|
||||
|
||||
entry_maker = entry_maker,
|
||||
entry_maker = entry_maker or make_entry.from_string,
|
||||
|
||||
fn_command = function()
|
||||
return {
|
||||
@@ -191,18 +230,14 @@ finders.new_oneshot_job = function(command_list, entry_maker)
|
||||
}
|
||||
end
|
||||
|
||||
--- Used to create a finder for a Lua table.
|
||||
-- If you only pass a table of results, then it will use that as the entries.
|
||||
--
|
||||
-- If you pass a table, and then a function, it's used as:
|
||||
-- results table, the results to run on
|
||||
-- entry_maker function, the function to convert results to entries.
|
||||
finders.new_table = function(t)
|
||||
return finders.new {
|
||||
results = t
|
||||
}
|
||||
return StaticFinder:new(t)
|
||||
end
|
||||
|
||||
-- We should add a few utility functions here...
|
||||
--
|
||||
-- finders.new_job
|
||||
-- finders.new_one_shot_job
|
||||
-- finders.new_table
|
||||
|
||||
-- finders.Finder = Finder
|
||||
|
||||
return finders
|
||||
|
||||
162
lua/telescope/make_entry.lua
Normal file
162
lua/telescope/make_entry.lua
Normal file
@@ -0,0 +1,162 @@
|
||||
local has_devicons, devicons = pcall(require, 'nvim-web-devicons')
|
||||
|
||||
local utils = require('telescope.utils')
|
||||
|
||||
local make_entry = {}
|
||||
|
||||
make_entry.types = {
|
||||
GENERIC = 0,
|
||||
FILE = 1,
|
||||
}
|
||||
|
||||
local transform_devicons
|
||||
if has_devicons then
|
||||
transform_devicons = function(filename, display, opts)
|
||||
if opts.disable_devicons then
|
||||
return display
|
||||
end
|
||||
|
||||
return (devicons.get_icon(filename, string.match(filename, '%a+$')) or ' ') .. ' ' .. display
|
||||
end
|
||||
else
|
||||
transform_devicons = function(_, display, _)
|
||||
return display
|
||||
end
|
||||
end
|
||||
|
||||
function make_entry.gen_from_string()
|
||||
return function(line)
|
||||
return {
|
||||
valid = line ~= "",
|
||||
entry_type = make_entry.types.SIMPLE,
|
||||
|
||||
value = line,
|
||||
ordinal = line,
|
||||
display = line,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function make_entry.gen_from_file(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local make_display = function(line)
|
||||
local display = line
|
||||
if opts.shorten_path then
|
||||
display = utils.path_shorten(line)
|
||||
end
|
||||
|
||||
display = transform_devicons(line, display, opts)
|
||||
|
||||
return display
|
||||
end
|
||||
|
||||
return function(line)
|
||||
local entry = {
|
||||
ordinal = line,
|
||||
value = line,
|
||||
|
||||
entry_type = make_entry.types.FILE,
|
||||
filename = line,
|
||||
}
|
||||
|
||||
entry.display = make_display(line)
|
||||
|
||||
return entry
|
||||
end
|
||||
end
|
||||
|
||||
function make_entry.gen_from_vimgrep(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local display_string = "%s:%s%s"
|
||||
|
||||
local make_display = function(entry)
|
||||
local display = entry.value
|
||||
|
||||
local display_filename
|
||||
if opts.shorten_path then
|
||||
display_filename = utils.path_shorten(entry.filename)
|
||||
else
|
||||
display_filename = entry.filename
|
||||
end
|
||||
|
||||
local coordinates = ""
|
||||
if not opts.disable_coordinates then
|
||||
coordinates = string.format("%s:%s:", entry.lnum, entry.col)
|
||||
end
|
||||
|
||||
display = transform_devicons(
|
||||
entry.filename,
|
||||
string.format(display_string, display_filename, coordinates, entry.text),
|
||||
opts
|
||||
)
|
||||
|
||||
return display
|
||||
end
|
||||
|
||||
return function(line)
|
||||
-- TODO: Consider waiting to do this string.find
|
||||
-- TODO: Is this the fastest way to get each of these?
|
||||
-- Or could we just walk the text and check for colons faster?
|
||||
local _, _, filename, lnum, col, text = string.find(line, [[([^:]+):(%d+):(%d+):(.*)]])
|
||||
|
||||
return {
|
||||
valid = line ~= "",
|
||||
|
||||
value = line,
|
||||
ordinal = line,
|
||||
display = make_display,
|
||||
|
||||
entry_type = make_entry.types.FILE,
|
||||
filename = filename,
|
||||
lnum = lnum,
|
||||
col = col,
|
||||
text = text,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function make_entry.gen_from_quickfix(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local make_display = function(entry)
|
||||
local to_concat = {}
|
||||
|
||||
if not opts.hide_filename then
|
||||
local filename = entry.filename
|
||||
if opts.shorten_path then
|
||||
filename = utils.path_shorten(filename)
|
||||
end
|
||||
|
||||
table.insert(to_concat, filename)
|
||||
table.insert(to_concat, ":")
|
||||
end
|
||||
|
||||
table.insert(to_concat, entry.text)
|
||||
|
||||
return table.concat(to_concat, "")
|
||||
end
|
||||
|
||||
return function(entry)
|
||||
return {
|
||||
valid = true,
|
||||
|
||||
value = entry,
|
||||
ordinal = (
|
||||
not opts.ignore_filename and entry.filename
|
||||
or ''
|
||||
) .. ' ' .. entry.text,
|
||||
display = make_display,
|
||||
|
||||
filename = entry.filename,
|
||||
lnum = entry.lnum,
|
||||
col = entry.col,
|
||||
text = entry.text,
|
||||
start = entry.start,
|
||||
finish = entry.finish,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return make_entry
|
||||
@@ -1,6 +1,5 @@
|
||||
local a = vim.api
|
||||
local popup = require('popup')
|
||||
local has_devicons, devicons = pcall(require, 'nvim-web-devicons')
|
||||
|
||||
local actions = require('telescope.actions')
|
||||
local log = require('telescope.log')
|
||||
@@ -8,8 +7,6 @@ local mappings = require('telescope.mappings')
|
||||
local state = require('telescope.state')
|
||||
local utils = require('telescope.utils')
|
||||
|
||||
local Entry = require('telescope.entry')
|
||||
|
||||
local get_default = utils.get_default
|
||||
|
||||
-- TODO: Make this work with deep extend I think.
|
||||
@@ -255,14 +252,19 @@ function Picker:find()
|
||||
return
|
||||
end
|
||||
|
||||
-- TODO: This really isn't the place to do this.
|
||||
local display = entry.display
|
||||
|
||||
if has_devicons then
|
||||
local icon = devicons.get_icon(display, vim.fn.fnamemodify(display, ":e"))
|
||||
display = (icon or ' ') .. ' ' .. display
|
||||
local display
|
||||
if type(entry.display) == 'function' then
|
||||
display = entry:display()
|
||||
elseif type(entry.display) == 'string' then
|
||||
display = entry.display
|
||||
else
|
||||
log.info("Weird entry", entry)
|
||||
return
|
||||
end
|
||||
|
||||
-- This is the two spaces to manage the '> ' stuff.
|
||||
-- Maybe someday we can use extmarks or floaty text or something to draw this and not insert here.
|
||||
-- until then, insert two spaces
|
||||
display = ' ' .. display
|
||||
|
||||
-- log.info("Setting row", row, "with value", entry)
|
||||
@@ -277,10 +279,9 @@ function Picker:find()
|
||||
end
|
||||
))
|
||||
|
||||
local process_result = function(line)
|
||||
local entry = Entry:new(line)
|
||||
|
||||
if not entry.valid then
|
||||
local process_result = function(entry)
|
||||
-- TODO: Should we even have valid?
|
||||
if entry.valid == false then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -559,10 +560,7 @@ pickers.entry_manager = function(max_results, set_entry)
|
||||
|
||||
return setmetatable({
|
||||
add_entry = function(self, score, entry)
|
||||
-- TODO: Consider forcing people to make these entries before we add them.
|
||||
if type(entry) == "string" then
|
||||
entry = Entry:new(entry)
|
||||
end
|
||||
assert(type(entry) == "table", "entry must be a table by the time it reaches here")
|
||||
|
||||
score = score or 0
|
||||
|
||||
@@ -647,6 +645,10 @@ function pickers.on_close_prompt(prompt_bufnr)
|
||||
local picker = status.picker
|
||||
|
||||
picker:close_windows(status)
|
||||
|
||||
if picker.previewer then
|
||||
picker.previewer:teardown()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ local Previewer = {}
|
||||
Previewer.__index = Previewer
|
||||
|
||||
local bat_options = " --style=grid --paging=always "
|
||||
local previewer_ns = vim.api.nvim_create_namespace('telescope.previewers')
|
||||
-- --terminal-width=%s
|
||||
|
||||
-- TODO: We shoudl make sure that all our terminals close all the way.
|
||||
@@ -20,6 +21,7 @@ function Previewer:new(opts)
|
||||
return setmetatable({
|
||||
state = nil,
|
||||
_setup_func = opts.setup,
|
||||
_teardown_func = opts.teardown,
|
||||
preview_fn = opts.preview_fn,
|
||||
}, Previewer)
|
||||
end
|
||||
@@ -29,13 +31,22 @@ function Previewer:preview(entry, status)
|
||||
return
|
||||
end
|
||||
|
||||
if not self.state and self._setup_func then
|
||||
if not self.state then
|
||||
if self._setup_func then
|
||||
self.state = self._setup_func()
|
||||
end
|
||||
end
|
||||
|
||||
self:teardown()
|
||||
return self:preview_fn(entry, status)
|
||||
end
|
||||
|
||||
function Previewer:teardown()
|
||||
if self._teardown_func then
|
||||
self:_teardown_func()
|
||||
end
|
||||
end
|
||||
|
||||
previewers.new = function(...)
|
||||
return Previewer:new(...)
|
||||
end
|
||||
@@ -69,20 +80,36 @@ previewers.new_termopen = function(opts)
|
||||
end
|
||||
|
||||
previewers.vim_buffer = previewers.new {
|
||||
preview_fn = function(_, entry, status)
|
||||
setup = function() return { last_set_bufnr = nil } end,
|
||||
|
||||
teardown = function(self)
|
||||
if self.state.last_set_bufnr then
|
||||
vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, previewer_ns, 0, -1)
|
||||
end
|
||||
end,
|
||||
|
||||
preview_fn = function(self, entry, status)
|
||||
local filename = entry.filename
|
||||
|
||||
if filename == nil then
|
||||
local value = entry.value
|
||||
if value == nil then
|
||||
filename = vim.split(value, ":")[1]
|
||||
end
|
||||
|
||||
if filename == nil then
|
||||
return
|
||||
end
|
||||
local file_name = vim.split(value, ":")[1]
|
||||
|
||||
log.trace("Previewing File: %s", file_name)
|
||||
log.trace("Previewing File: %s", filename)
|
||||
|
||||
-- vim.fn.termopen(
|
||||
-- string.format("bat --color=always --style=grid %s"),
|
||||
-- vim.fn.fnamemodify(file_name, ":p")
|
||||
local bufnr = vim.fn.bufadd(file_name)
|
||||
local bufnr = vim.fn.bufnr(filename)
|
||||
if bufnr == -1 then
|
||||
-- TODO: Is this the best way to load the buffer?... I'm not sure tbh
|
||||
bufnr = vim.fn.bufadd(bufnr)
|
||||
vim.fn.bufload(bufnr)
|
||||
end
|
||||
|
||||
self.state.last_set_bufnr = bufnr
|
||||
|
||||
-- TODO: We should probably call something like this because we're not always getting highlight and all that stuff.
|
||||
-- api.nvim_command('doautocmd filetypedetect BufRead ' .. vim.fn.fnameescape(filename))
|
||||
@@ -92,6 +119,11 @@ previewers.vim_buffer = previewers.new {
|
||||
-- vim.api.nvim_win_set_option(preview_win, 'winblend', 20)
|
||||
vim.api.nvim_win_set_option(status.preview_win, 'signcolumn', 'no')
|
||||
vim.api.nvim_win_set_option(status.preview_win, 'foldlevel', 100)
|
||||
|
||||
if entry.lnum then
|
||||
vim.api.nvim_buf_add_highlight(bufnr, previewer_ns, "Visual", entry.lnum - 1, 0, -1)
|
||||
-- print("LNUM:", entry.lnum)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,10 @@ utils.path_shorten = (function()
|
||||
]]
|
||||
|
||||
return function(path)
|
||||
if not path then
|
||||
return path
|
||||
end
|
||||
|
||||
local c_str = ffi.new("char[?]", #path + 1)
|
||||
ffi.copy(c_str, path)
|
||||
return ffi.string(ffi.C.shorten_dir(c_str))
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
RELOAD('telescope')
|
||||
require('plenary.reload').reload_module('telescope')
|
||||
|
||||
local actions = require('telescope.actions')
|
||||
local finders = require('telescope.finders')
|
||||
local previewers = require('telescope.previewers')
|
||||
local pickers = require('telescope.pickers')
|
||||
local sorters = require('telescope.sorters')
|
||||
local utils = require('telescope.utils')
|
||||
|
||||
|
||||
pickers.new({
|
||||
prompt = 'Telescope Builtin',
|
||||
finder = finders.new_table({"hello\nworld", "other", "item"}),
|
||||
finder = finders.new_table {
|
||||
results = {"hello\nworld", "other", "item"},
|
||||
entry_maker = false and function(line)
|
||||
return {
|
||||
value = line,
|
||||
ordinal = line,
|
||||
display = "wow: // " .. line,
|
||||
}
|
||||
end,
|
||||
},
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
|
||||
26
media/worflow.txt
Normal file
26
media/worflow.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
+-------------------------------------------------------------------+
|
||||
| Picker:find()--------------------+ +------>Picker |
|
||||
| | ^ | | |
|
||||
| | | v | |
|
||||
| | +----------------+ +----------------+ |
|
||||
| +->| Finder + | Sorter | |
|
||||
| +----------------+ +----------------+ |
|
||||
| [1] |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+-------------------------------------------------------------------+
|
||||
|
||||
Picker starts a `finder`.
|
||||
Finder returns a list of `entries` to Picker.
|
||||
Picker can optionally sort w/ `Sorter`.
|
||||
Picker can optionally preview selected with `Previewer`
|
||||
|
||||
Then you can map stuff in the picker to decide what to do next.
|
||||
|
||||
|
||||
|
||||
|
||||
55
scratch/batched_finder_and_sorter.lua
Normal file
55
scratch/batched_finder_and_sorter.lua
Normal file
@@ -0,0 +1,55 @@
|
||||
local actions = require('telescope.actions')
|
||||
local finders = require('telescope.finders')
|
||||
local previewers = require('telescope.previewers')
|
||||
local pickers = require('telescope.pickers')
|
||||
local sorters = require('telescope.sorters')
|
||||
local utils = require('telescope.utils')
|
||||
|
||||
local Job = require('plenary.job')
|
||||
|
||||
-- local live_grepper = finders.new {
|
||||
-- fn_command = function(_, prompt)
|
||||
-- -- TODO: Make it so that we can start searching on the first character.
|
||||
-- if not prompt or prompt == "" then
|
||||
-- return nil
|
||||
-- end
|
||||
|
||||
-- return {
|
||||
-- command = 'rg',
|
||||
-- args = {"--vimgrep", prompt},
|
||||
-- }
|
||||
-- end
|
||||
-- }
|
||||
|
||||
local f = function(prompt, process_result, process_complete)
|
||||
local fzf = Job:new {
|
||||
command = 'fzf';
|
||||
|
||||
writer = Job:new {
|
||||
command = "fdfind",
|
||||
args = nil,
|
||||
cwd = "/home/tj/build/neovim",
|
||||
|
||||
enable_handlers = false,
|
||||
},
|
||||
|
||||
-- Still doesn't work if you don't pass these args and just run `fzf`
|
||||
args = {'--no-sort', '--filter', prompt};
|
||||
}
|
||||
|
||||
|
||||
local start = vim.fn.reltime()
|
||||
print(vim.inspect(fzf:sync()), vim.fn.reltimestr(vim.fn.reltime(start)))
|
||||
end
|
||||
|
||||
|
||||
-- Process all the files
|
||||
-- f("", nil, nil)
|
||||
-- Filter on nvimexec
|
||||
f("nvim/executor", nil, nil)
|
||||
|
||||
-- pickers.new({}, {
|
||||
-- prompt = 'Live Grep',
|
||||
-- finder = f,
|
||||
-- previewer = previewers.vimgrep,
|
||||
-- }):find()
|
||||
130
scratch/clason_finders.lua
Normal file
130
scratch/clason_finders.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
vim.cmd [[packadd! plenary.nvim]]
|
||||
vim.cmd [[packadd! popup.nvim]]
|
||||
vim.cmd [[packadd! telescope.nvim]]
|
||||
|
||||
local finders = require('telescope.finders')
|
||||
local previewers = require('telescope.previewers')
|
||||
local pickers = require('telescope.pickers')
|
||||
local sorters = require('telescope.sorters')
|
||||
local utils = require('telescope.utils')
|
||||
|
||||
local rgargs = {'--color=never', '--no-heading', '--with-filename', '--line-number', '--column', '--smart-case'}
|
||||
-- grep typed string in current directory (live, not fuzzy!)
|
||||
finders.rg_live = function(opts)
|
||||
local live_grepper = finders.new {
|
||||
fn_command = function(_, prompt)
|
||||
if not prompt or prompt == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
return {
|
||||
command = 'rg',
|
||||
args = vim.tbl_flatten{rgargs, prompt},
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Live Grep',
|
||||
finder = live_grepper,
|
||||
previewer = previewers.vimgrep,
|
||||
}):find()
|
||||
end
|
||||
|
||||
-- fuzzy grep string in current directory (slow!)
|
||||
finders.rg = function(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local search = opts.search or ''
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'Find Word',
|
||||
finder = finders.new_oneshot_job(vim.tbl_flatten{'rg', rgargs, search}),
|
||||
previewer = previewers.vimgrep,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
-- fuzzy find files in current directory (may be slow in root dir)
|
||||
finders.fd = function(opts)
|
||||
pickers.new(opts, {
|
||||
prompt = 'Find Files',
|
||||
finder = finders.new_oneshot_job {"fd"},
|
||||
previewer = previewers.bat,
|
||||
sorter = sorters.get_fuzzy_file(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
-- fuzzy find in references to symbol under cursor
|
||||
finders.lsp_references = function(opts)
|
||||
local params = vim.lsp.util.make_position_params()
|
||||
params.context = { includeDeclaration = false }
|
||||
|
||||
local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/references", params)
|
||||
local locations = {}
|
||||
for _, server_results in pairs(results_lsp) do
|
||||
vim.list_extend(locations, vim.lsp.util.locations_to_items(server_results.result) or {})
|
||||
end
|
||||
|
||||
local results = utils.quickfix_items_to_entries(locations)
|
||||
|
||||
if vim.tbl_isempty(results) then
|
||||
return
|
||||
end
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'LSP References',
|
||||
finder = finders.new_table(results),
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
-- fuzzy find in document symbols
|
||||
finders.lsp_document_symbols = function(opts)
|
||||
local params = vim.lsp.util.make_position_params()
|
||||
local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/documentSymbol", params)
|
||||
local locations = {}
|
||||
for _, server_results in pairs(results_lsp) do
|
||||
vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result, 0) or {})
|
||||
end
|
||||
|
||||
local results = utils.quickfix_items_to_entries(locations)
|
||||
|
||||
if vim.tbl_isempty(results) then
|
||||
return
|
||||
end
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'LSP Document Symbols',
|
||||
finder = finders.new_table(results),
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
-- fuzzy find in all workspace symbols (may need longer timeout!)
|
||||
finders.lsp_workspace_symbols = function(opts)
|
||||
local params = {query = ''}
|
||||
local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params, 1000)
|
||||
local locations = {}
|
||||
for _, server_results in pairs(results_lsp) do
|
||||
vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result, 0) or {})
|
||||
end
|
||||
|
||||
local results = utils.quickfix_items_to_entries(locations)
|
||||
|
||||
if vim.tbl_isempty(results) then
|
||||
return
|
||||
end
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt = 'LSP Workspace Symbols',
|
||||
finder = finders.new_table(results),
|
||||
previewer = previewers.qflist,
|
||||
sorter = sorters.get_norcalli_sorter(),
|
||||
}):find()
|
||||
end
|
||||
|
||||
|
||||
return finders
|
||||
Reference in New Issue
Block a user