diff options
Diffstat (limited to 'lua/telescope')
| -rw-r--r-- | lua/telescope/builtin.lua | 31 | ||||
| -rw-r--r-- | lua/telescope/extensions/vimgrep.lua | 13 | ||||
| -rw-r--r-- | lua/telescope/finders.lua | 84 | ||||
| -rw-r--r-- | lua/telescope/init.lua | 353 | ||||
| -rw-r--r-- | lua/telescope/mappings.lua | 124 | ||||
| -rw-r--r-- | lua/telescope/pickers.lua | 180 | ||||
| -rw-r--r-- | lua/telescope/previewers.lua | 16 | ||||
| -rw-r--r-- | lua/telescope/state.lua | 18 |
8 files changed, 492 insertions, 327 deletions
diff --git a/lua/telescope/builtin.lua b/lua/telescope/builtin.lua new file mode 100644 index 0000000..7016b0a --- /dev/null +++ b/lua/telescope/builtin.lua @@ -0,0 +1,31 @@ +--[[ +A collection of builtin pipelines for telesceope. + +Meant for both example and for easy startup. +--]] + +local Finder = require('telescope.finder') +local pickers = require('telescope.pickers') + +local builtin = {} + +builtin.rg_vimgrep = setmetatable({}, { + __call = function(t, ...) + -- builtin.rg_vimgrep("--type lua function") + print(t, ...) + end +}) + +builtin.rg_vimgrep.finder = Finder:new { + fn_command = function(prompt) + return string.format('rg --vimgrep %s', prompt) + end, + + responsive = false +} + +builtin.rg_vimgrep.picker = pickers.new { +} + + +return builtin diff --git a/lua/telescope/extensions/vimgrep.lua b/lua/telescope/extensions/vimgrep.lua new file mode 100644 index 0000000..4a28a84 --- /dev/null +++ b/lua/telescope/extensions/vimgrep.lua @@ -0,0 +1,13 @@ +local vimgrep = {} + +vimgrep.parse_line = function(line) + local sections = vim.split(line, ":") + + return { + filename = sections[1], + row = tonumber(sections[2]), + col = tonumber(sections[3]), + } +end + +return vimgrep diff --git a/lua/telescope/finders.lua b/lua/telescope/finders.lua new file mode 100644 index 0000000..1601d09 --- /dev/null +++ b/lua/telescope/finders.lua @@ -0,0 +1,84 @@ +local a = vim.api + +local finders = {} + +local Finder = {} +Finder.__index = Finder + +--- Create a new finder command +--- +--@param fn_command function The function to call +function Finder:new(opts) + opts = opts or {} + + -- TODO: Add config for: + -- - cwd + + -- 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 + -- ... + return setmetatable({ + fn_command = opts.fn_command, + responsive = opts.responsive, + job_id = -1, + }, Finder) +end + +function Finder:get_results(win, bufnr, prompt) + if self.job_id > 0 then + -- Make sure we kill old jobs. + vim.fn.jobstop(self.job_id) + end + + self.job_id = vim.fn.jobstart(self.fn_command(prompt), { + -- TODO: Decide if we want this or don't want this. + stdout_buffered = true, + + on_stdout = function(_, data, _) + a.nvim_buf_set_lines(bufnr, -1, -1, false, data) + end, + + on_exit = function() + -- TODO: Add possibility to easily highlight prompt within buffer + -- without having to do weird stuff and with it actually working... + if false then + vim.fn.matchadd("Type", "\\<" .. prompt .. "\\>", 1, -1, {window = win}) + end + end, + }) + + --[[ + local function get_rg_results(bufnr, search_string) + local start_time = vim.fn.reltime() + + vim.fn.jobstart(string.format('rg %s', search_string), { + cwd = '/home/tj/build/neovim', + + on_stdout = function(job_id, data, event) + vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, data) + end, + + on_exit = function() + print("Finished in: ", vim.fn.reltimestr(vim.fn.reltime(start_time))) + end, + + stdout_buffer = true, + }) + end + --]] +end + +--- Return a new Finder +-- +--@return Finder +finders.new = function(...) + return Finder:new(...) +end + +return finders diff --git a/lua/telescope/init.lua b/lua/telescope/init.lua index fc2b037..76abb5a 100644 --- a/lua/telescope/init.lua +++ b/lua/telescope/init.lua @@ -1,338 +1,37 @@ -package.loaded['popup'] = nil -package.loaded['popup.border'] = nil -package.loaded['popup.init'] = nil - -- TODO: Debounce preview window maybe +-- TODO: Make filters +-- "fzf --filter" +-- jobstart() -> | fzf --filter "input on prompt" + +local finders = require('telescope.finders') +local pickers = require('telescope.pickers') +local previewers = require('telescope.previewers') +local state = require('telescope.state') + +local telescope = { + finders = finders, + pickers = pickers, + previewers = previewers, + state = state, +} -local a = vim.api - -local popup = require('popup') - -local telescope = {} - -local ns_telescope = a.nvim_create_namespace('telescope') -local ns_telescope_highlight = a.nvim_create_namespace('telescope_highlight') - -local Finder = {} -Finder.__index = Finder - -function Finder:new(fn_command) - -- TODO: Add config for: - -- - cwd - - -- 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 - -- ... - return setmetatable({ - fn_command = fn_command, - job_id = -1, - }, Finder) -end - -function Finder:display_results(win, bufnr, prompt) - if self.job_id > 0 then - -- Make sure we kill old jobs. - vim.fn.jobstop(self.job_id) - end - - self.job_id = vim.fn.jobstart(self.fn_command(prompt), { - -- TODO: Decide if we want this or don't want this. - stdout_buffered = true, - on_stdout = function(_, data, _) - a.nvim_buf_set_lines(bufnr, -1, -1, false, data) - end, - - on_exit = function() - -- TODO: Add possibility to easily highlight prompt within buffer - -- without having to do weird stuff and with it actually working... - if false then - vim.fn.matchadd("Type", "\\<" .. prompt .. "\\>", 1, -1, {window = win}) - end - end, - }) - - --[[ - local function get_rg_results(bufnr, search_string) - local start_time = vim.fn.reltime() - - vim.fn.jobstart(string.format('rg %s', search_string), { - cwd = '/home/tj/build/neovim', - - on_stdout = function(job_id, data, event) - vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, data) - end, - - on_exit = function() - print("Finished in: ", vim.fn.reltimestr(vim.fn.reltime(start_time))) - end, - - stdout_buffer = true, - }) - end - --]] -end - -local Previewer = {} -Previewer.__index = Previewer - -function Previewer:new(fn) - return setmetatable({ - fn = fn, - }, Previewer) -end - -local picker_finders = {} - -local Picker = {} -Picker.__index = Picker - -function Picker:new(opts) - opts = opts or {} - return setmetatable({ - filter = opts.filter, - previewer = opts.previewer, - maps = opts.maps, - }, Picker) -end - -local hack_status = {} - -local previewers = {} - -function Picker:find(finder) - local prompt_string = 'MUNITER > ' - -- Create three windows: - -- 1. Prompt window - -- 2. Options window - -- 3. Preview window - - local width = 100 - local col = 10 - local prompt_line = 50 - - local result_height = 25 - local prompt_height = 1 - - -- TODO: Add back the borders after fixing some stuff in popup.nvim - local results_win = popup.create('', { - height = result_height, - minheight = result_height, - width = width, - line = prompt_line - 2 - result_height, - col = col, - -- border = {}, - enter = false, - }) - local results_bufnr = a.nvim_win_get_buf(results_win) - picker_finders[results_bufnr] = finder - - local preview_win = popup.create('', { - height = result_height + prompt_height + 4, - minheight = result_height + prompt_height + 4, - width = 100, - line = prompt_line - 2 - result_height, - col = col + width + 2, - -- border = {}, - enter = false, - highlight = false, - }) - local preview_bufnr = a.nvim_win_get_buf(preview_win) - - -- TODO: For some reason, highlighting is kind of weird on these windows. - -- It may actually be my colorscheme tho... - a.nvim_win_set_option(preview_win, 'winhl', '') - - -- TODO: We need to center this and make it prettier... - local prompt_win = popup.create('', { - height = prompt_height, - width = width, - line = prompt_line, - col = col, - border = {}, - }) - local prompt_bufnr = a.nvim_win_get_buf(prompt_win) - - a.nvim_buf_set_option(prompt_bufnr, 'buftype', 'prompt') - vim.fn.prompt_setprompt(prompt_bufnr, prompt_string) - - -- TODO: Please use the cool autocmds once you get off your lazy bottom and finish the PR ;) - local autocmd_string = string.format( - [[ autocmd TextChanged,TextChangedI <buffer> :lua __TelescopeOnChange(%s, "%s", %s, %s)]], - prompt_bufnr, - prompt_string, - results_bufnr, - results_win) - - local buf_close = string.format( - [[ autocmd BufLeave <buffer> :bd! %s ]], - prompt_bufnr) - - local results_close = string.format( - [[ autocmd BufLeave <buffer> :call nvim_win_close(%s, v:true) ]], - results_win) - - local preview_close = string.format( - [[ autocmd BufLeave <buffer> :call nvim_win_close(%s, v:true) ]], - preview_win) - - vim.cmd([[augroup PickerCommands]]) - vim.cmd([[ au!]]) - vim.cmd( autocmd_string) - vim.cmd( buf_close) - vim.cmd( results_close) - vim.cmd( preview_close) - vim.cmd([[augroup END]]) - - previewers[prompt_bufnr] = self.previewer - - -- TODO: Clear this hack status stuff when closing - hack_status[prompt_bufnr] = { - prompt_bufnr = prompt_bufnr, - prompt_win = prompt_win, - results_bufnr = results_bufnr, - results_win = results_win, - preview_bufnr = preview_bufnr, - preview_win = preview_win, - } - - local function default_mapper(map_key, table_key) - a.nvim_buf_set_keymap( - prompt_bufnr, - 'i', - map_key, - string.format( - [[<C-O>:lua __TelescopeMapping(%s, %s, '%s')<CR>]], - prompt_bufnr, - results_bufnr, - table_key - ), - { - silent = true, - } - ) - end - - default_mapper('<c-n>', 'control-n') - default_mapper('<c-p>', 'control-p') - default_mapper('<CR>', 'enter') - - vim.cmd [[startinsert]] -end - --- TODO: All lower case mappings -local telescope_selections = {} - -local function update_current_selection(prompt_bufnr, results_bufnr, row) - a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_highlight, 0, -1) - a.nvim_buf_add_highlight( - results_bufnr, - ns_telescope_highlight, - 'Error', - row, - 0, - -1 - ) - - telescope_selections[prompt_bufnr] = row - - if previewers[prompt_bufnr] then - vim.g.got_here = true - local status = hack_status[prompt_bufnr] - previewers[prompt_bufnr].fn( - status.preview_win, - status.preview_bufnr, - status.results_bufnr, - row - ) - end -end - -local mappings = {} - --- TODO: Refactor this to use shared code. --- TODO: Move from top to bottom, etc. --- TODO: It seems like doing this brings us back to the beginning of the prompt, which is not great. -mappings["control-n"] = function(prompt_bufnr, results_bufnr) - if telescope_selections[prompt_bufnr] == nil then - telescope_selections[prompt_bufnr] = 0 - end - - local row = telescope_selections[prompt_bufnr] + 1 - update_current_selection(prompt_bufnr, results_bufnr, row) -end - -mappings["control-p"] = function(prompt_bufnr, results_bufnr) - if telescope_selections[prompt_bufnr] == nil then - telescope_selections[prompt_bufnr] = 0 - end - - local row = telescope_selections[prompt_bufnr] - 1 - update_current_selection(prompt_bufnr, results_bufnr, row) -end - -mappings["enter"] = function(prompt_bufnr, results_bufnr) - local extmark = a.nvim_buf_get_extmarks( - results_bufnr, - ns_telescope_highlight, - 0, - -1, - {} - ) +function __TelescopeOnLeave(prompt_bufnr) + local status = state.get_status(prompt_bufnr) + local picker = status.picker - print(vim.inspect(extmark)) -end - -function __TelescopeMapping(prompt_bufnr, results_bufnr, characters) - if mappings[characters] then - mappings[characters](prompt_bufnr, results_bufnr) - end + picker:close_windows(status) end -- TODO: Probably could attach this with nvim_buf_attach, and then I don't have to do the ugly global function stuff -function __TelescopeOnChange(bufnr, prompt, results_bufnr, results_win) - local line = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)[1] +function __TelescopeOnChange(prompt_bufnr, prompt, results_bufnr, results_win) + local line = vim.api.nvim_buf_get_lines(prompt_bufnr, 0, -1, false)[1] local prompt_input = string.sub(line, #prompt + 1) - print(string.format("|%s|", prompt_input)) - - local finder = picker_finders[results_bufnr] - a.nvim_buf_set_lines(results_bufnr, 0, -1, false, {}) - finder:display_results(results_win, results_bufnr, prompt_input) -end - --- Uhh, finder should probably just GET the results --- and then update some table. --- When updating the table, we should call filter on those items --- and then only display ones that pass the filter -local f = Finder:new(function(prompt) - return string.format('rg %s', prompt) -end) -local p = Picker:new { - previewer = Previewer:new(function(preview_win, preview_bufnr, results_bufnr, row) - local line = a.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1] - local file_name = vim.split(line, ":")[1] + local status = state.get_status(prompt_bufnr) + local finder = status.finder - -- print(file_name) - -- vim.fn.termopen( - -- string.format("bat --color=always --style=grid %s"), - local file_bufnr = vim.fn.bufnr(file_name, true) - -- 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)) - a.nvim_win_set_buf(preview_win, file_bufnr) - end) -} -p:find(f) - --- TODO: Make filters --- "fzf --filter" --- jobstart() -> | fzf --filter "input on prompt" --- function Filter:new(command) --- end + vim.api.nvim_buf_set_lines(results_bufnr, 0, -1, false, {}) + local results = finder:get_results(results_win, results_bufnr, prompt_input) +end return telescope diff --git a/lua/telescope/mappings.lua b/lua/telescope/mappings.lua new file mode 100644 index 0000000..b779b2f --- /dev/null +++ b/lua/telescope/mappings.lua @@ -0,0 +1,124 @@ +-- TODO: Customize keymap +local a = vim.api +local ns_telescope_selection = a.nvim_create_namespace('telescope_selection') + +local state = require('telescope.state') + +local mappings = {} +local keymap = {} +local telescope_selections = {} + +mappings.set_keymap = function(prompt_bufnr, results_bufnr) + local function default_mapper(map_key, table_key) + a.nvim_buf_set_keymap( + prompt_bufnr, + 'i', + map_key, + string.format( + [[<C-O>:lua __TelescopeMapping(%s, %s, '%s')<CR>]], + prompt_bufnr, + results_bufnr, + table_key + ), + { + silent = true, + } + ) + end + + default_mapper('<c-n>', 'control-n') + default_mapper('<c-p>', 'control-p') + default_mapper('<CR>', 'enter') +end + +local function update_current_selection(prompt_bufnr, results_bufnr, row) + local status = state.get_status(prompt_bufnr) + + a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_selection, 0, -1) + a.nvim_buf_add_highlight( + results_bufnr, + ns_telescope_selection, + 'Error', + row, + 0, + -1 + ) + + telescope_selections[prompt_bufnr] = row + + if status.previewer then + vim.g.got_here = true + + status.previewer.fn( + status.preview_win, + status.preview_bufnr, + status.results_bufnr, + row + ) + end +end + + +function __TelescopeMapping(prompt_bufnr, results_bufnr, characters) + if keymap[characters] then + keymap[characters](prompt_bufnr, results_bufnr) + end +end + +-- TODO: Refactor this to use shared code. +-- TODO: Move from top to bottom, etc. +-- TODO: It seems like doing this brings us back to the beginning of the prompt, which is not great. +keymap["control-n"] = function(prompt_bufnr, results_bufnr) + if telescope_selections[prompt_bufnr] == nil then + telescope_selections[prompt_bufnr] = 0 + end + + local row = telescope_selections[prompt_bufnr] + 1 + update_current_selection(prompt_bufnr, results_bufnr, row) +end + +keymap["control-p"] = function(prompt_bufnr, results_bufnr) + if telescope_selections[prompt_bufnr] == nil then + telescope_selections[prompt_bufnr] = 0 + end + + local row = telescope_selections[prompt_bufnr] - 1 + update_current_selection(prompt_bufnr, results_bufnr, row) +end + +keymap["enter"] = function(prompt_bufnr, results_bufnr) + local extmark = a.nvim_buf_get_extmarks( + results_bufnr, + ns_telescope_selection, + 0, + -1, + {} + ) + + local row = extmark[1][2] + if row == nil then + print("Could not do anything...") + return + else + local line = a.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1] + if line == nil then + print("Could not do anything with blank line...") + return + end + + local sections = vim.split(line, ":") + + local filename = sections[1] + local row = tonumber(sections[2]) + local col = tonumber(sections[3]) + + vim.cmd(string.format([[bdelete! %s]], prompt_bufnr)) + + local bufnr = vim.fn.bufnr(filename, true) + a.nvim_set_current_buf(bufnr) + a.nvim_buf_set_option(bufnr, 'buflisted', true) + a.nvim_win_set_cursor(0, {row, col}) + end +end + +return mappings diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua new file mode 100644 index 0000000..a8624b0 --- /dev/null +++ b/lua/telescope/pickers.lua @@ -0,0 +1,180 @@ +local a = vim.api +local popup = require('popup') + +local mappings = require('telescope.mappings') +local state = require('telescope.state') + +local pickers = {} + +local Picker = {} +Picker.__index = Picker + +function Picker:new(opts) + opts = opts or {} + return setmetatable({ + filter = opts.filter, + previewer = opts.previewer, + maps = opts.maps, + }, Picker) +end + + +function Picker:find(finder) + local prompt_string = 'Find File' + -- Create three windows: + -- 1. Prompt window + -- 2. Options window + -- 3. Preview window + + local width = 100 + local col = 10 + local prompt_line = 50 + + local result_height = 25 + local prompt_height = 1 + + -- TODO: Add back the borders after fixing some stuff in popup.nvim + local results_win, results_opts = popup.create('', { + height = result_height, + minheight = result_height, + width = width, + line = prompt_line - 2 - result_height, + col = col, + border = {}, + enter = false, + }) + local results_bufnr = a.nvim_win_get_buf(results_win) + + local preview_win, preview_opts = popup.create('', { + height = result_height + prompt_height + 2, + minheight = result_height + prompt_height + 2, + width = 100, + line = prompt_line - 2 - result_height, + col = col + width + 2, + border = {}, + enter = false, + highlight = false, + }) + local preview_bufnr = a.nvim_win_get_buf(preview_win) + + -- TODO: For some reason, highlighting is kind of weird on these windows. + -- It may actually be my colorscheme tho... + a.nvim_win_set_option(preview_win, 'winhl', 'Normal:Normal') + + -- TODO: We need to center this and make it prettier... + local prompt_win, prompt_opts = popup.create('', { + height = prompt_height, + width = width, + line = prompt_line, + col = col, + border = {}, + title = prompt_string + }) + local prompt_bufnr = a.nvim_win_get_buf(prompt_win) + + -- a.nvim_buf_set_option(prompt_bufnr, 'buftype', 'prompt') + -- vim.fn.prompt_setprompt(prompt_bufnr, prompt_string) + + vim.api.nvim_buf_attach(prompt_bufnr, true, { + on_lines = vim.schedule_wrap(function(_, _, _, first_line, last_line) + local line = vim.api.nvim_buf_get_lines(prompt_bufnr, first_line, last_line, false)[1] + + vim.api.nvim_buf_set_lines(results_bufnr, 0, -1, false, {}) + local results = finder:get_results(results_win, results_bufnr, line) + end), + + on_detach = function(...) + -- print("DETACH:", ...) + end, + }) + + -- -- TODO: Please use the cool autocmds once you get off your lazy bottom and finish the PR ;) + -- local autocmd_string = string.format( + -- [[ autocmd TextChanged,TextChangedI <buffer> :lua __TelescopeOnChange(%s, "%s", %s, %s)]], + -- prompt_bufnr, + -- '', + -- results_bufnr, + -- results_win) + + -- TODO: Use WinLeave as well? + local on_buf_leave = string.format( + [[ autocmd BufLeave <buffer> ++nested ++once :lua __TelescopeOnLeave(%s)]], + prompt_bufnr) + + vim.cmd([[augroup PickerInsert]]) + vim.cmd([[ au!]]) + vim.cmd( on_buf_leave) + vim.cmd([[augroup END]]) + + state.set_status(prompt_bufnr, { + prompt_bufnr = prompt_bufnr, + prompt_win = prompt_win, + prompt_border_win = prompt_opts.border.win_id, + + results_bufnr = results_bufnr, + results_win = results_win, + results_border_win = results_opts.border.win_id, + + preview_bufnr = preview_bufnr, + preview_win = preview_win, + preview_border_win = preview_opts.border.win_id, + + picker = self, + previewer = self.previewer, + finder = finder, + }) + + -- print(vim.inspect(state.get_status(prompt_bufnr))) + mappings.set_keymap(prompt_bufnr, results_bufnr) + + vim.cmd [[startinsert]] +end + +function Picker:close_windows(status) + -- vim.fn['popup#close_win'](state.prompt_win) + -- vim.fn['popup#close_win'](state.results_win) + -- vim.fn['popup#close_win'](state.preview_win) + local prompt_win = status.prompt_win + local results_win = status.results_win + local preview_win = status.preview_win + + local prompt_border_win = status.prompt_border_win + local results_border_win = status.results_border_win + local preview_border_win = status.preview_border_win + + local function del_win(name, win_id, force) + local file = io.open("/home/tj/test.txt", "a") + file:write(string.format("Closing.... %s %s\n", name, win_id)) + local ok = pcall(vim.api.nvim_win_close, win_id, force) + file:write(string.format("OK: %s\n", ok)) + file:write("...Done\n\n") + file:close() + end + + del_win("prompt_win", prompt_win, true) + del_win("results_win", results_win, true) + del_win("preview_win", preview_win, true) + + del_win("prompt_border_win", prompt_border_win, true) + del_win("results_border_win", results_border_win, true) + del_win("preview_border_win", preview_border_win, true) + + -- vim.cmd(string.format("bdelete! %s", status.prompt_bufnr)) + + -- Major hack?? Why do I have to od this. + -- Probably because we're currently IN the buffer. + -- Should wait to do this until after we're done. + vim.defer_fn(function() + del_win("prompt_win", prompt_win, true) + end, 10) + + state.clear_status(status.prompt_bufnr) +end + + + +pickers.new = function(...) + return Picker:new(...) +end + +return pickers diff --git a/lua/telescope/previewers.lua b/lua/telescope/previewers.lua new file mode 100644 index 0000000..a36ec5a --- /dev/null +++ b/lua/telescope/previewers.lua @@ -0,0 +1,16 @@ +local previewers = {} + +local Previewer = {} +Previewer.__index = Previewer + +function Previewer:new(fn) + return setmetatable({ + fn = fn, + }, Previewer) +end + +previewers.new = function(...) + return Previewer:new(...) +end + +return previewers diff --git a/lua/telescope/state.lua b/lua/telescope/state.lua new file mode 100644 index 0000000..0ae31dd --- /dev/null +++ b/lua/telescope/state.lua @@ -0,0 +1,18 @@ +local state = {} + +state._statuses = {} + +--- Set the status for a particular prompt bufnr +function state.set_status(prompt_bufnr, status) + state._statuses[prompt_bufnr] = status +end + +function state.get_status(prompt_bufnr) + return state._statuses[prompt_bufnr] or {} +end + +function state.clear_status(prompt_bufnr) + state.set_status(prompt_bufnr, nil) +end + +return state |
