summaryrefslogtreecommitdiff
path: root/lua/telescope/pickers.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/telescope/pickers.lua')
-rw-r--r--lua/telescope/pickers.lua278
1 files changed, 133 insertions, 145 deletions
diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua
index 646af03..f8e48f1 100644
--- a/lua/telescope/pickers.lua
+++ b/lua/telescope/pickers.lua
@@ -1,15 +1,12 @@
+require "telescope"
+
local a = vim.api
-local async_lib = require "plenary.async_lib"
-local async_util = async_lib.util
+local async = require "plenary.async"
+local await_schedule = async.util.scheduler
+local channel = require("plenary.async.control").channel
local popup = require "plenary.popup"
-local async = async_lib.async
-local await = async_lib.await
-local channel = async_util.channel
-
-require "telescope"
-
local actions = require "telescope.actions"
local action_set = require "telescope.actions.set"
local config = require "telescope.config"
@@ -70,12 +67,13 @@ function Picker:new(opts)
selection_caret = get_default(opts.selection_caret, config.values.selection_caret),
entry_prefix = get_default(opts.entry_prefix, config.values.entry_prefix),
initial_mode = get_default(opts.initial_mode, config.values.initial_mode),
+ debounce = get_default(tonumber(opts.debounce), nil),
default_text = opts.default_text,
get_status_text = get_default(opts.get_status_text, config.values.get_status_text),
_on_input_filter_cb = opts.on_input_filter_cb or function() end,
- finder = opts.finder,
+ finder = assert(opts.finder, "Finder is required."),
sorter = opts.sorter or require("telescope.sorters").empty(),
all_previewers = opts.previewer,
@@ -228,7 +226,7 @@ function Picker:highlight_displayed_rows(results_bufnr, prompt)
end
function Picker:highlight_one_row(results_bufnr, prompt, display, row)
- local highlights = self:_track("_highlight_time", self.sorter.highlighter, self.sorter, prompt, display)
+ local highlights = self.sorter:highlighter(prompt, display)
if highlights then
for _, hl in ipairs(highlights) do
@@ -274,8 +272,6 @@ function Picker:find()
self:close_existing_pickers()
self:reset_selection()
- assert(self.finder, "Finder is required to do picking")
-
self.original_win_id = a.nvim_get_current_win()
-- User autocmd run it before create Telescope window
@@ -346,26 +342,50 @@ function Picker:find()
self.prompt_prefix = prompt_prefix
self:_reset_prefix_color()
- -- Temporarily disabled: Draw the screen ASAP. This makes things feel speedier.
- -- vim.cmd [[redraw]]
-
-- First thing we want to do is set all the lines to blank.
self.max_results = popup_opts.results.height
+ -- TODO(scrolling): This may be a hack when we get a little further into implementing scrolling.
vim.api.nvim_buf_set_lines(results_bufnr, 0, self.max_results, false, utils.repeated_table(self.max_results, ""))
+ -- TODO(status): I would love to get the status text not moving back and forth. Perhaps it is just a problem with
+ -- virtual text & prompt buffers or something though. I can't figure out why it would redraw the way it does.
+ --
+ -- A "hacked" version of this would be to calculate where the area I want the status to go and put a new window there.
+ -- With this method, I do not need to worry about padding or antying, just make it take up X characters or something.
local status_updater = self:get_status_updater(prompt_win, prompt_bufnr)
local debounced_status = debounce.throttle_leading(status_updater, 50)
- -- local debounced_status = status_updater
local tx, rx = channel.mpsc()
self.__on_lines = tx.send
- local main_loop = async(function()
+ local find_id = self:_next_find_id()
+
+ local main_loop = async.void(function()
+ self.sorter:_init()
+
+ -- Do filetype last, so that users can register at the last second.
+ pcall(a.nvim_buf_set_option, prompt_bufnr, "filetype", "TelescopePrompt")
+ pcall(a.nvim_buf_set_option, results_bufnr, "filetype", "TelescopeResults")
+
+ -- TODO(async): I wonder if this should actually happen _before_ we nvim_buf_attach.
+ -- This way the buffer would always start with what we think it should when we start the loop.
+ if self.default_text then
+ self:set_prompt(self.default_text)
+ end
+
+ if self.initial_mode == "insert" then
+ vim.cmd [[startinsert!]]
+ elseif self.initial_mode ~= "normal" then
+ error("Invalid setting for initial_mode: " .. self.initial_mode)
+ end
+
+ await_schedule()
+
while true do
- await(async_lib.scheduler())
+ -- Wait for the next input
+ rx.last()
- local _, _, _, first_line, last_line = await(rx.last())
self:_reset_track()
if not vim.api.nvim_buf_is_valid(prompt_bufnr) then
@@ -373,74 +393,58 @@ function Picker:find()
return
end
- if not first_line then
- first_line = 0
- end
- if not last_line then
- last_line = 1
- end
-
- if first_line > 0 or last_line > 1 then
- log.debug("ON_LINES: Bad range", first_line, last_line, self:_get_prompt())
- return
- end
-
- local original_prompt = self:_get_prompt()
- local on_input_result = self._on_input_filter_cb(original_prompt) or {}
+ local start_time = vim.loop.hrtime()
- local prompt = on_input_result.prompt or original_prompt
- local finder = on_input_result.updated_finder
+ local prompt = self:_get_prompt()
+ local on_input_result = self._on_input_filter_cb(prompt) or {}
- if finder then
- self.finder:close()
- self.finder = finder
+ local new_prompt = on_input_result.prompt
+ if new_prompt then
+ prompt = new_prompt
end
- if self.sorter then
- self.sorter:_start(prompt)
+ local new_finder = on_input_result.updated_finder
+ if new_finder then
+ self.finder:close()
+ self.finder = new_finder
end
- -- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display
+ self.sorter:_start(prompt)
self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats)
- local find_id = self:_next_find_id()
local process_result = self:get_result_processor(find_id, prompt, debounced_status)
local process_complete = self:get_result_completor(self.results_bufnr, find_id, prompt, status_updater)
local ok, msg = pcall(function()
- self.finder(prompt, process_result, vim.schedule_wrap(process_complete))
+ self.finder(prompt, process_result, process_complete)
end)
if not ok then
- log.warn("Failed with msg: ", msg)
+ log.warn("Finder failed with msg: ", msg)
+ end
+
+ local diff_time = (vim.loop.hrtime() - start_time) / 1e6
+ if self.debounce and diff_time < self.debounce then
+ async.util.sleep(self.debounce - diff_time)
end
end
end)
-- Register attach
vim.api.nvim_buf_attach(prompt_bufnr, false, {
- on_lines = tx.send,
- on_detach = function()
- -- TODO: Can we add a "cleanup" / "teardown" function that completely removes these.
- self.finder = nil
- self.previewer = nil
- self.sorter = nil
- self.manager = nil
+ on_lines = function(...)
+ find_id = self:_next_find_id()
- self.closed = true
+ self._result_completed = false
+ status_updater { completed = false }
- -- TODO: Should we actually do this?
- collectgarbage()
- collectgarbage()
+ tx.send(...)
+ end,
+ on_detach = function()
+ self:_detach()
end,
})
- if self.sorter then
- self.sorter:_init()
- end
- async_lib.run(main_loop())
- status_updater()
-
-- TODO: Use WinLeave as well?
local on_buf_leave = string.format(
[[ autocmd BufLeave <buffer> ++nested ++once :silent lua require('telescope.pickers').on_close_prompt(%s)]],
@@ -480,19 +484,8 @@ function Picker:find()
mappings.apply_keymap(prompt_bufnr, self.attach_mappings, config.values.mappings)
- -- Do filetype last, so that users can register at the last second.
- pcall(a.nvim_buf_set_option, prompt_bufnr, "filetype", "TelescopePrompt")
- pcall(a.nvim_buf_set_option, results_bufnr, "filetype", "TelescopeResults")
-
- if self.default_text then
- self:set_prompt(self.default_text)
- end
-
- if self.initial_mode == "insert" then
- vim.cmd [[startinsert!]]
- elseif self.initial_mode ~= "normal" then
- error("Invalid setting for initial_mode: " .. self.initial_mode)
- end
+ tx.send()
+ main_loop()
end
function Picker:hide_preview()
@@ -791,9 +784,7 @@ function Picker:set_selection(row)
end
local caret = self.selection_caret
- -- local display = string.format('%s %s', caret,
- -- (a.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1] or ''):sub(3)
- -- )
+
local display, display_highlights = entry_display.resolve(self, entry)
display = caret .. display
@@ -939,23 +930,6 @@ function Picker:_reset_track()
self.stats.highlights = 0
end
-function Picker:_track(key, func, ...)
- local start, final
- if self.track then
- start = vim.loop.hrtime()
- end
-
- -- Hack... we just do this so that we can track stuff that returns two values.
- local res1, res2 = func(...)
-
- if self.track then
- final = vim.loop.hrtime()
- self.stats[key] = final - start + self.stats[key]
- end
-
- return res1, res2
-end
-
function Picker:_increment(key)
self.stats[key] = (self.stats[key] or 0) + 1
end
@@ -987,12 +961,12 @@ function Picker:close_existing_pickers()
end
function Picker:get_status_updater(prompt_win, prompt_bufnr)
- return function()
- local text = self:get_status_text()
+ return function(opts)
if self.closed or not vim.api.nvim_buf_is_valid(prompt_bufnr) then
return
end
- local current_prompt = vim.api.nvim_buf_get_lines(prompt_bufnr, 0, 1, false)[1]
+
+ local current_prompt = self:_get_prompt()
if not current_prompt then
return
end
@@ -1001,10 +975,11 @@ function Picker:get_status_updater(prompt_win, prompt_bufnr)
return
end
- local prompt_len = #current_prompt
+ local text = self:get_status_text(opts)
+ local prompt_len = #self.prompt_prefix + #current_prompt
- local padding = string.rep(" ", vim.api.nvim_win_get_width(prompt_win) - prompt_len - #text - 3)
- vim.api.nvim_buf_clear_namespace(prompt_bufnr, ns_telescope_prompt, 0, 1)
+ local padding = string.rep(" ", vim.api.nvim_win_get_width(prompt_win) - prompt_len - #text)
+ vim.api.nvim_buf_clear_namespace(prompt_bufnr, ns_telescope_prompt, 0, -1)
vim.api.nvim_buf_set_virtual_text(prompt_bufnr, ns_telescope_prompt, 0, { { padding .. text, "NonText" } }, {})
-- TODO: Wait for bfredl
@@ -1022,7 +997,7 @@ end
function Picker:get_result_processor(find_id, prompt, status_updater)
local cb_add = function(score, entry)
self.manager:add_entry(self, score, entry)
- status_updater()
+ status_updater { completed = false }
end
local cb_filter = function(_)
@@ -1030,7 +1005,7 @@ function Picker:get_result_processor(find_id, prompt, status_updater)
end
return function(entry)
- if find_id ~= self._find_id or self.closed or self:is_done() then
+ if find_id ~= self._find_id then
return true
end
@@ -1059,61 +1034,62 @@ function Picker:get_result_processor(find_id, prompt, status_updater)
end
function Picker:get_result_completor(results_bufnr, find_id, prompt, status_updater)
- return function()
+ return vim.schedule_wrap(function()
if self.closed == true or self:is_done() then
return
end
- local selection_strategy = self.selection_strategy or "reset"
+ self:_do_selection(prompt)
- -- TODO: Either: always leave one result or make sure we actually clean up the results when nothing matches
- if selection_strategy == "row" then
- if self._selection_row == nil and self.default_selection_index ~= nil then
- self:set_selection(self:get_row(self.default_selection_index))
- else
- self:set_selection(self:get_selection_row())
- end
- elseif selection_strategy == "follow" then
- if self._selection_row == nil and self.default_selection_index ~= nil then
- self:set_selection(self:get_row(self.default_selection_index))
- else
- local index = self.manager:find_entry(self:get_selection())
+ state.set_global_key("current_line", self:_get_prompt())
+ status_updater { completed = true }
- if index then
- local follow_row = self:get_row(index)
- self:set_selection(follow_row)
- else
- self:set_selection(self:get_reset_row())
- end
- end
- elseif selection_strategy == "reset" then
- if self.default_selection_index ~= nil then
- self:set_selection(self:get_row(self.default_selection_index))
- else
- self:set_selection(self:get_reset_row())
- end
- elseif selection_strategy == "closest" then
- if prompt == "" and self.default_selection_index ~= nil then
- self:set_selection(self:get_row(self.default_selection_index))
+ self:clear_extra_rows(results_bufnr)
+ self:highlight_displayed_rows(results_bufnr, prompt)
+ self.sorter:_finish(prompt)
+
+ self:_on_complete()
+
+ self._result_completed = true
+ end)
+end
+
+function Picker:_do_selection(prompt)
+ local selection_strategy = self.selection_strategy or "reset"
+ -- TODO: Either: always leave one result or make sure we actually clean up the results when nothing matches
+ if selection_strategy == "row" then
+ if self._selection_row == nil and self.default_selection_index ~= nil then
+ self:set_selection(self:get_row(self.default_selection_index))
+ else
+ self:set_selection(self:get_selection_row())
+ end
+ elseif selection_strategy == "follow" then
+ if self._selection_row == nil and self.default_selection_index ~= nil then
+ self:set_selection(self:get_row(self.default_selection_index))
+ else
+ local index = self.manager:find_entry(self:get_selection())
+
+ if index then
+ local follow_row = self:get_row(index)
+ self:set_selection(follow_row)
else
self:set_selection(self:get_reset_row())
end
+ end
+ elseif selection_strategy == "reset" then
+ if self.default_selection_index ~= nil then
+ self:set_selection(self:get_row(self.default_selection_index))
else
- error("Unknown selection strategy: " .. selection_strategy)
+ self:set_selection(self:get_reset_row())
end
-
- local current_line = vim.api.nvim_get_current_line():sub(self.prompt_prefix:len() + 1)
- state.set_global_key("current_line", current_line)
-
- status_updater()
-
- self:clear_extra_rows(results_bufnr)
- self:highlight_displayed_rows(results_bufnr, prompt)
- if self.sorter then
- self.sorter:_finish(prompt)
+ elseif selection_strategy == "closest" then
+ if prompt == "" and self.default_selection_index ~= nil then
+ self:set_selection(self:get_row(self.default_selection_index))
+ else
+ self:set_selection(self:get_reset_row())
end
-
- self:_on_complete()
+ else
+ error("Unknown selection strategy: " .. selection_strategy)
end
end
@@ -1169,6 +1145,18 @@ function Picker:_reset_highlights()
self.highlighter:clear_display()
end
+function Picker:_detach()
+ self.finder:close()
+
+ -- TODO: Can we add a "cleanup" / "teardown" function that completely removes these.
+ -- self.finder = nil
+ -- self.previewer = nil
+ -- self.sorter = nil
+ -- self.manager = nil
+
+ self.closed = true
+end
+
pickers._Picker = Picker
return pickers