diff --git a/lua/telescope/builtin/files.lua b/lua/telescope/builtin/files.lua new file mode 100644 index 0000000..d1484ed --- /dev/null +++ b/lua/telescope/builtin/files.lua @@ -0,0 +1,248 @@ +local actions = require('telescope.actions') +local finders = require('telescope.finders') +local make_entry = require('telescope.make_entry') +local pickers = require('telescope.pickers') +local previewers = require('telescope.previewers') +local utils = require('telescope.utils') + +local conf = require('telescope.config').values + +local flatten = vim.tbl_flatten + +local files = {} + +files.live_grep = function(opts) + 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 flatten { conf.vimgrep_arguments, prompt } + end, + opts.entry_maker or make_entry.gen_from_vimgrep(opts), + opts.max_results + ) + + pickers.new(opts, { + prompt_title = 'Live Grep', + finder = live_grepper, + previewer = previewers.vimgrep.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +-- Special keys: +-- opts.search -- the string to search. +files.grep_string = function(opts) + -- TODO: This should probably check your visual selection as well, if you've got one + local search = opts.search or vim.fn.expand("") + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_vimgrep(opts) + opts.word_match = opts.word_match or nil + + pickers.new(opts, { + prompt_title = 'Find Word', + finder = finders.new_oneshot_job( + flatten { conf.vimgrep_arguments, opts.word_match, search}, + opts + ), + previewer = previewers.vimgrep.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +-- TODO: Maybe just change this to `find`. +-- Support `find` and maybe let people do other stuff with it as well. +files.find_files = function(opts) + local find_command = opts.find_command + + if not find_command then + if 1 == vim.fn.executable("fd") then + find_command = { 'fd', '--type', 'f' } + elseif 1 == vim.fn.executable("fdfind") then + find_command = { 'fdfind', '--type', 'f' } + elseif 1 == vim.fn.executable("rg") then + find_command = { 'rg', '--files' } + elseif 1 == vim.fn.executable("find") then + find_command = { 'find', '.', '-type', 'f' } + end + end + + if not find_command then + print("You need to install either find, fd, or rg. You can also submit a PR to add support for another file finder :)") + return + end + + if opts.cwd then + opts.cwd = vim.fn.expand(opts.cwd) + end + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) + + pickers.new(opts, { + prompt_title = 'Find Files', + finder = finders.new_oneshot_job( + find_command, + opts + ), + previewer = previewers.cat.new(opts), + sorter = conf.file_sorter(opts), + }):find() +end + +local function prepare_match(entry, kind) + local entries = {} + + if entry.node then + entry["kind"] = kind + table.insert(entries, entry) + else + for name, item in pairs(entry) do + vim.list_extend(entries, prepare_match(item, name)) + end + end + + return entries +end + +files.treesitter = function(opts) + opts.show_line = utils.get_default(opts.show_line, true) + + local has_nvim_treesitter, _ = pcall(require, 'nvim-treesitter') + if not has_nvim_treesitter then + print('You need to install nvim-treesitter') + return + end + + local parsers = require('nvim-treesitter.parsers') + if not parsers.has_parser() then + print('No parser for the current buffer') + return + end + + local ts_locals = require('nvim-treesitter.locals') + local bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + + local results = {} + for _, definitions in ipairs(ts_locals.get_definitions(bufnr)) do + local entries = prepare_match(definitions) + for _, entry in ipairs(entries) do + table.insert(results, entry) + end + end + + if vim.tbl_isempty(results) then + return + end + + pickers.new(opts, { + prompt_title = 'Treesitter Symbols', + finder = finders.new_table { + results = results, + entry_maker = make_entry.gen_from_treesitter(opts) + }, + previewer = previewers.vimgrep.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +files.current_buffer_fuzzy_find = function(opts) + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + local lines_with_numbers = {} + for k, v in ipairs(lines) do + table.insert(lines_with_numbers, {k, v}) + end + + local bufnr = vim.api.nvim_get_current_buf() + + pickers.new(opts, { + prompt_title = 'Current Buffer Fuzzy', + finder = finders.new_table { + results = lines_with_numbers, + entry_maker = function(enumerated_line) + return { + bufnr = bufnr, + display = enumerated_line[2], + ordinal = enumerated_line[2], + + lnum = enumerated_line[1], + } + end + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function() + actions._goto_file_selection:enhance { + post = function() + local selection = actions.get_selected_entry() + vim.api.nvim_win_set_cursor(0, {selection.lnum, 0}) + end, + } + + return true + end + }):find() +end + +files.tags = function(opts) + local ctags_file = opts.ctags_file or 'tags' + + if not vim.loop.fs_open(vim.fn.expand(ctags_file), "r", 438) then + print('Tags file does not exists. Create one with ctags -R') + return + end + + local fd = assert(vim.loop.fs_open(vim.fn.expand(ctags_file), "r", 438)) + local stat = assert(vim.loop.fs_fstat(fd)) + local data = assert(vim.loop.fs_read(fd, stat.size, 0)) + assert(vim.loop.fs_close(fd)) + + local results = vim.split(data, '\n') + + pickers.new(opts,{ + prompt = 'Tags', + finder = finders.new_table { + results = results, + entry_maker = make_entry.gen_from_ctags(opts), + }, + previewer = previewers.ctags.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function() + actions._goto_file_selection:enhance { + post = function() + local selection = actions.get_selected_entry() + + local scode = string.gsub(selection.scode, '[$]$', '') + scode = string.gsub(scode, [[\\]], [[\]]) + scode = string.gsub(scode, [[\/]], [[/]]) + scode = string.gsub(scode, '[*]', [[\*]]) + + vim.cmd('norm! gg') + vim.fn.search(scode) + vim.cmd('norm! zz') + end, + } + return true + end + }):find() +end + +files.current_buffer_tags = function(opts) + return files.tags(vim.tbl_extend("force", {only_current_file = true, hide_filename = true}, opts)) +end + + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod +end + +return apply_checks(files) diff --git a/lua/telescope/builtin/init.lua b/lua/telescope/builtin/init.lua index 9853072..f180cbe 100644 --- a/lua/telescope/builtin/init.lua +++ b/lua/telescope/builtin/init.lua @@ -23,1018 +23,45 @@ if 2 > vim.o.report then vim.api.nvim_err_writeln("[telescope] If you do not, you will have a bad experience") end - -local actions = require('telescope.actions') -local finders = require('telescope.finders') -local make_entry = require('telescope.make_entry') -local path = require('telescope.path') -local pickers = require('telescope.pickers') -local previewers = require('telescope.previewers') -local sorters = require('telescope.sorters') -local utils = require('telescope.utils') - -local conf = require('telescope.config').values - -local filter = vim.tbl_filter -local flatten = vim.tbl_flatten - local builtin = {} +builtin.live_grep = require('telescope.builtin.files').live_grep +builtin.grep_string = require('telescope.builtin.files').grep_string +builtin.find_files = require('telescope.builtin.files').find_files +builtin.fd = builtin.find_files +builtin.treesitter = require('telescope.builtin.files').treesitter +builtin.current_buffer_fuzzy_find = require('telescope.builtin.files').current_buffer_fuzzy_find +builtin.tags = require('telescope.builtin.files').tags +builtin.current_buffer_tags = require('telescope.builtin.files').current_buffer_tags + builtin.git_files = require('telescope.builtin.git').files builtin.git_commits = require('telescope.builtin.git').commits builtin.git_bcommits = require('telescope.builtin.git').bcommits builtin.git_branches = require('telescope.builtin.git').branches builtin.git_status = require('telescope.builtin.git').status -builtin.commands = function() - pickers.new({}, { - prompt_title = 'Commands', - finder = finders.new_table { - results = (function() - local command_iter = vim.api.nvim_get_commands({}) - local commands = {} - - for _, cmd in pairs(command_iter) do - table.insert(commands, cmd) - end - - return commands - end)(), - entry_maker = function(line) - return { - valid = line ~= "", - value = line, - ordinal = line.name, - display = line.name - } - end - }, - sorter = conf.generic_sorter(), - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - actions.close(prompt_bufnr) - local val = selection.value - local cmd = string.format([[:%s ]], val.name) - - if val.nargs == "0" then - vim.cmd(cmd) - else - vim.cmd [[stopinsert]] - vim.fn.feedkeys(cmd) - end - end) - - return true - end - }):find() -end - -builtin.live_grep = function(opts) - 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 flatten { conf.vimgrep_arguments, prompt } - end, - opts.entry_maker or make_entry.gen_from_vimgrep(opts), - opts.max_results - ) - - pickers.new(opts, { - prompt_title = 'Live Grep', - finder = live_grepper, - previewer = previewers.vimgrep.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - -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 } - - local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/references", params, opts.timeout or 10000) - local locations = {} - for _, server_results in pairs(results_lsp) do - if server_results.result then - vim.list_extend(locations, vim.lsp.util.locations_to_items(server_results.result) or {}) - end - end - - if vim.tbl_isempty(locations) then - return - end - - pickers.new(opts, { - prompt_title = 'LSP References', - finder = finders.new_table { - results = locations, - entry_maker = make_entry.gen_from_quickfix(opts), - }, - previewer = previewers.qflist.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - -builtin.lsp_document_symbols = function(opts) - opts = opts or {} - - local params = vim.lsp.util.make_position_params() - local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/documentSymbol", params, opts.timeout or 10000) - - 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_title = 'LSP Document Symbols', - finder = finders.new_table { - results = locations, - entry_maker = make_entry.gen_from_quickfix(opts) - }, - previewer = previewers.vim_buffer.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - -builtin.lsp_code_actions = function(opts) - opts = opts or {} - - local params = vim.lsp.util.make_range_params() - - params.context = { - diagnostics = vim.lsp.diagnostic.get_line_diagnostics() - } - - local results_lsp, err = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, opts.timeout or 10000) - - if err then - print("ERROR: " .. err) - return - end - - if not results_lsp or vim.tbl_isempty(results_lsp) then - print("No results from textDocument/codeAction") - return - end - - local results = (results_lsp[1] or results_lsp[2]).result; - - if not results or #results == 0 then - print("No code actions available") - return - end - - for i,x in ipairs(results) do - x.idx = i - end - - pickers.new(opts, { - prompt_title = 'LSP Code Actions', - finder = finders.new_table { - results = results, - entry_maker = function(line) - return { - valid = line ~= nil, - value = line, - ordinal = line.idx .. line.title, - display = line.idx .. ': ' .. line.title - } - end - }, - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - actions.close(prompt_bufnr) - local val = selection.value - - if val.edit or type(val.command) == "table" then - if val.edit then - vim.lsp.util.apply_workspace_edit(val.edit) - end - if type(val.command) == "table" then - vim.lsp.buf.execute_command(val.command) - end - else - vim.lsp.buf.execute_command(val) - end - end) - - return true - end, - sorter = conf.generic_sorter(opts), - }):find() -end - -builtin.lsp_workspace_symbols = function(opts) - opts = opts or {} - opts.shorten_path = utils.get_default(opts.shorten_path, true) - - local params = {query = opts.query or ''} - local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params, opts.timeout or 10000) - - if not results_lsp or vim.tbl_isempty(results_lsp) then - print("No results from workspace/symbol") - return - end - - local locations = {} - for _, server_results in pairs(results_lsp) do - if server_results.result then - vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result, 0) or {}) - end - end - - if vim.tbl_isempty(locations) then - return - end - - pickers.new(opts, { - prompt_title = 'LSP Workspace Symbols', - finder = finders.new_table { - results = locations, - entry_maker = make_entry.gen_from_quickfix(opts) - }, - previewer = previewers.qflist.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - -builtin.quickfix = function(opts) - opts = opts or {} - - local locations = vim.fn.getqflist() - - if vim.tbl_isempty(locations) then - return - end - - pickers.new(opts, { - prompt_title = 'Quickfix', - finder = finders.new_table { - results = locations, - entry_maker = make_entry.gen_from_quickfix(opts), - }, - previewer = previewers.qflist.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - -builtin.loclist = function(opts) - local locations = vim.fn.getloclist(0) - local filename = vim.api.nvim_buf_get_name(0) - - for _, value in pairs(locations) do - value.filename = filename - end - - if vim.tbl_isempty(locations) then - return - end - - pickers.new(opts, { - prompt_title = 'Loclist', - finder = finders.new_table { - results = locations, - entry_maker = make_entry.gen_from_quickfix(opts), - }, - previewer = previewers.qflist.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - --- Special keys: --- opts.search -- the string to search. -builtin.grep_string = function(opts) - opts = opts or {} - - -- TODO: This should probably check your visual selection as well, if you've got one - local search = opts.search or vim.fn.expand("") - - opts.entry_maker = opts.entry_maker or make_entry.gen_from_vimgrep(opts) - opts.word_match = opts.word_match or nil - - pickers.new(opts, { - prompt_title = 'Find Word', - finder = finders.new_oneshot_job( - flatten { conf.vimgrep_arguments, opts.word_match, search}, - opts - ), - previewer = previewers.vimgrep.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - -builtin.oldfiles = function(opts) - opts = opts or {} - - pickers.new(opts, { - prompt_title = 'Oldfiles', - finder = finders.new_table(vim.tbl_filter(function(val) - return 0 ~= vim.fn.filereadable(val) - end, vim.v.oldfiles)), - sorter = conf.file_sorter(opts), - previewer = previewers.cat.new(opts), - }):find() -end - -builtin.command_history = function(opts) - local history_string = vim.fn.execute('history cmd') - local history_list = vim.split(history_string, "\n") - - local results = {} - for i = #history_list, 3, -1 do - local item = history_list[i] - local _, finish = string.find(item, "%d+ +") - table.insert(results, string.sub(item, finish + 1)) - end - - pickers.new(opts, { - prompt_title = 'Command History', - finder = finders.new_table(results), - sorter = sorters.fuzzy_with_index_bias(), - - attach_mappings = function(_, map) - map('i', '', actions.set_command_line) - - -- TODO: Find a way to insert the text... it seems hard. - -- map('i', '', actions.insert_value, { expr = true }) - - return true - end, - }):find() -end - -builtin.vim_options = function(opts) - opts = opts or {} - - -- Load vim options. - local vim_opts = loadfile(utils.data_directory() .. path.separator .. 'options' .. path.separator .. 'options.lua')().options - - pickers.new(opts, { - prompt = 'options', - finder = finders.new_table { - results = vim_opts, - entry_maker = make_entry.gen_from_vimoptions(opts), - }, - -- TODO: previewer for Vim options - -- previewer = previewers.help.new(opts), - sorter = sorters.get_fzy_sorter(), - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - local esc = "" - - if vim.fn.mode() == "i" then - -- TODO: don't make this local - esc = vim.api.nvim_replace_termcodes("", true, false, true) - end - - -- TODO: Make this actually work. - - -- actions.close(prompt_bufnr) - -- vim.api.nvim_win_set_var(vim.fn.nvim_get_current_win(), "telescope", 1) - -- print(prompt_bufnr) - -- print(vim.fn.bufnr()) - -- vim.cmd([[ autocmd BufEnter ++nested ++once startinsert!]]) - -- print(vim.fn.winheight(0)) - - -- local prompt_winnr = vim.fn.getbufinfo(prompt_bufnr)[1].windows[1] - -- print(prompt_winnr) - - -- local float_opts = {} - -- float_opts.relative = "editor" - -- float_opts.anchor = "sw" - -- float_opts.focusable = false - -- float_opts.style = "minimal" - -- float_opts.row = vim.api.nvim_get_option("lines") - 2 -- TODO: include `cmdheight` and `laststatus` in this calculation - -- float_opts.col = 2 - -- float_opts.height = 10 - -- float_opts.width = string.len(selection.last_set_from)+15 - -- local buf = vim.fn.nvim_create_buf(false, true) - -- vim.fn.nvim_buf_set_lines(buf, 0, 0, false, {"default value: abcdef", "last set from: " .. selection.last_set_from}) - -- local status_win = vim.fn.nvim_open_win(buf, false, float_opts) - -- -- vim.api.nvim_win_set_option(status_win, "winblend", 100) - -- vim.api.nvim_win_set_option(status_win, "winhl", "Normal:PmenuSel") - -- -- vim.api.nvim_set_current_win(status_win) - -- vim.cmd[[redraw!]] - -- vim.cmd("autocmd CmdLineLeave : ++once echom 'beep'") - vim.api.nvim_feedkeys(string.format("%s:set %s=%s", esc, selection.name, selection.current_value), "m", true) - end) - - return true - end - }):find() -end - -builtin.help_tags = function(opts) - opts = opts or {} - - local tags = {} - for _, file in pairs(vim.fn.findfile('doc/tags', vim.o.runtimepath, -1)) do - local f = assert(io.open(file, "rb")) - for line in f:lines() do - table.insert(tags, line) - end - f:close() - end - - pickers.new(opts, { - prompt_title = 'Help', - finder = finders.new_table { - results = tags, - entry_maker = make_entry.gen_from_taglist(opts), - }, - -- TODO: previewer for Vim help - previewer = previewers.help.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions._goto_file_selection:replace(function(_, cmd) - local selection = actions.get_selected_entry() - actions.close(prompt_bufnr) - if cmd == 'edit' or cmd == 'new' then - vim.cmd('help ' .. selection.value) - elseif cmd == 'vnew' then - vim.cmd('vert bo help ' .. selection.value) - elseif cmd == 'tabedit' then - vim.cmd('tab help ' .. selection.value) - end - end) - - return true - end - }):find() -end - -builtin.reloader = function(opts) - opts = opts or {} - local package_list = vim.tbl_keys(package.loaded) - - -- filter out packages we don't want and track the longest package name - opts.column_len = 0 - for index, module_name in pairs(package_list) do - if type(require(module_name)) ~= 'table' or module_name:sub(1,1) == "_" or package.searchpath(module_name, package.path) == nil then - table.remove(package_list, index) - elseif #module_name > opts.column_len then - opts.column_len = #module_name - end - end - - pickers.new(opts, { - prompt_title = 'Packages', - finder = finders.new_table { - results = package_list, - entry_maker = make_entry.gen_from_packages(opts), - }, - -- previewer = previewers.vim_buffer.new(opts), - sorter = conf.generic_sorter(opts), - - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - - actions.close(prompt_bufnr) - require('plenary.reload').reload_module(selection.value) - print(string.format("[%s] - module reloaded", selection.value)) - end) - - return true - end - }):find() -end - --- TODO: What the heck should we do for accepting this. --- vim.fn.setreg("+", "nnoremap $TODO :lua require('telescope.builtin').()") --- 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, { - filename = string.sub(debug_info.source, 2), - lnum = debug_info.linedefined, - col = 0, - text = k, - - start = debug_info.linedefined, - finish = debug_info.lastlinedefined, - }) - end - - pickers.new(opts, { - prompt_title = 'Telescope Builtin', - finder = finders.new_table { - results = objs, - entry_maker = make_entry.gen_from_quickfix(opts), - }, - previewer = previewers.qflist.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(_) - actions.goto_file_selection_edit:replace(actions.run_builtin) - return true - end - }):find() -end - - --- TODO: Maybe just change this to `find`. --- Support `find` and maybe let people do other stuff with it as well. -builtin.find_files = function(opts) - opts = opts or {} - - local find_command = opts.find_command - - if not find_command then - if 1 == vim.fn.executable("fd") then - find_command = { 'fd', '--type', 'f' } - elseif 1 == vim.fn.executable("fdfind") then - find_command = { 'fdfind', '--type', 'f' } - elseif 1 == vim.fn.executable("rg") then - find_command = { 'rg', '--files' } - elseif 1 == vim.fn.executable("find") then - find_command = { 'find', '.', '-type', 'f' } - end - end - - if not find_command then - print("You need to install either find, fd, or rg. You can also submit a PR to add support for another file finder :)") - return - end - - if opts.cwd then - opts.cwd = vim.fn.expand(opts.cwd) - end - - opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) - - pickers.new(opts, { - prompt_title = 'Find Files', - finder = finders.new_oneshot_job( - find_command, - opts - ), - previewer = previewers.cat.new(opts), - sorter = conf.file_sorter(opts), - }):find() -end - --- Leave this alias around for people. -builtin.fd = builtin.find_files - --- TODO: I'd like to use the `vim_buffer` previewer, but it doesn't seem to work due to some styling problems. --- I think it has something to do with nvim_open_win and style='minimal', --- Status, currently operational. -builtin.buffers = function(opts) - opts = opts or {} - - local bufnrs = filter(function(b) - return - (opts.show_all_buffers - or vim.api.nvim_buf_is_loaded(b)) - and 1 == vim.fn.buflisted(b) - end, vim.api.nvim_list_bufs()) - - local buffers = {} - local default_selection_idx = 1 - for _, bufnr in ipairs(bufnrs) do - local flag = bufnr == vim.fn.bufnr('') and '%' or (bufnr == vim.fn.bufnr('#') and '#' or ' ') - - if opts.sort_lastused and flag == "#" then - default_selection_idx = 2 - end - - local element = { - bufnr = bufnr, - flag = flag, - info = vim.fn.getbufinfo(bufnr)[1], - } - - if opts.sort_lastused and (flag == "#" or flag == "%") then - local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1) - table.insert(buffers, idx, element) - else - table.insert(buffers, element) - end - end - - if not opts.bufnr_width then - local max_bufnr = math.max(unpack(bufnrs)) - opts.bufnr_width = #tostring(max_bufnr) - end - - pickers.new(opts, { - prompt_title = 'Buffers', - finder = finders.new_table { - results = buffers, - entry_maker = make_entry.gen_from_buffer(opts) - }, - previewer = previewers.vim_buffer.new(opts), - sorter = conf.generic_sorter(opts), - default_selection_index = default_selection_idx, - }):find() -end - -local function prepare_match(entry, kind) - local entries = {} - - if entry.node then - entry["kind"] = kind - table.insert(entries, entry) - else - for name, item in pairs(entry) do - vim.list_extend(entries, prepare_match(item, name)) - end - end - - return entries -end - -builtin.treesitter = function(opts) - opts = opts or {} - - opts.show_line = utils.get_default(opts.show_line, true) - - local has_nvim_treesitter, _ = pcall(require, 'nvim-treesitter') - if not has_nvim_treesitter then - print('You need to install nvim-treesitter') - return - end - - local parsers = require('nvim-treesitter.parsers') - if not parsers.has_parser() then - print('No parser for the current buffer') - return - end - - local ts_locals = require('nvim-treesitter.locals') - local bufnr = opts.bufnr or vim.api.nvim_get_current_buf() - - local results = {} - for _, definitions in ipairs(ts_locals.get_definitions(bufnr)) do - local entries = prepare_match(definitions) - for _, entry in ipairs(entries) do - table.insert(results, entry) - end - end - - if vim.tbl_isempty(results) then - return - end - - pickers.new(opts, { - prompt_title = 'Treesitter Symbols', - finder = finders.new_table { - results = results, - entry_maker = make_entry.gen_from_treesitter(opts) - }, - previewer = previewers.vim_buffer.new(opts), - sorter = conf.generic_sorter(opts), - }):find() -end - -builtin.planets = function(opts) - opts = opts or {} - local show_pluto = opts.show_pluto or false - - local sourced_file = require('plenary.debug_utils').sourced_filepath() - local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h") - - local globbed_files = vim.fn.globpath(base_directory .. '/data/memes/planets/', '*', true, true) - local acceptable_files = {} - for _, v in ipairs(globbed_files) do - if not show_pluto and v:find("pluto") then - else - table.insert(acceptable_files,vim.fn.fnamemodify(v, ':t')) - end - end - - pickers.new { - prompt_title = 'Planets', - finder = finders.new_table { - results = acceptable_files, - entry_maker = function(line) - return { - ordinal = line, - display = line, - filename = base_directory .. '/data/memes/planets/' .. line, - } - end - }, - previewer = previewers.cat.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - actions.close(prompt_bufnr) - - print("Enjoy astronomy! You viewed:", selection.display) - end) - - return true - end, - }:find() -end - -builtin.current_buffer_fuzzy_find = function(opts) - local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) - local lines_with_numbers = {} - for k, v in ipairs(lines) do - table.insert(lines_with_numbers, {k, v}) - end - - local bufnr = vim.api.nvim_get_current_buf() - - pickers.new(opts, { - prompt_title = 'Current Buffer Fuzzy', - finder = finders.new_table { - results = lines_with_numbers, - entry_maker = function(enumerated_line) - return { - bufnr = bufnr, - display = enumerated_line[2], - ordinal = enumerated_line[2], - - lnum = enumerated_line[1], - } - end - }, - sorter = sorters.get_generic_fuzzy_sorter(), - attach_mappings = function() - actions._goto_file_selection:enhance { - post = function() - local selection = actions.get_selected_entry() - vim.api.nvim_win_set_cursor(0, {selection.lnum, 0}) - end, - } - - return true - end - }):find() -end - -builtin.man_pages = function(opts) - opts = opts or {} - - local cmd = opts.man_cmd or "apropos --sections=1 ''" - - local pages = utils.get_os_command_output(cmd) - - local lines = {} - for s in pages:gmatch("[^\r\n]+") do - table.insert(lines, s) - end - - pickers.new(opts, { - prompt_title = 'Man', - finder = finders.new_table { - results = lines, - entry_maker = make_entry.gen_from_apropos(opts), - }, - previewer = previewers.man.new(opts), - sorter = sorters.get_generic_fuzzy_sorter(), - attach_mappings = function(prompt_bufnr) - actions._goto_file_selection:replace(function(_, cmd) - local selection = actions.get_selected_entry() - - actions.close(prompt_bufnr) - if cmd == 'edit' or cmd == 'new' then - vim.cmd('Man ' .. selection.value) - elseif cmd == 'vnew' then - vim.cmd('vert bo Man ' .. selection.value) - elseif cmd == 'tabedit' then - vim.cmd('tab Man ' .. selection.value) - end - end) - - return true - end - }):find() -end - -builtin.colorscheme = function(opts) - opts = opts or {} - - local colors = vim.list_extend(opts.colors or {}, vim.fn.getcompletion('', 'color')) - - pickers.new(opts,{ - prompt = 'Change Colorscheme', - finder = finders.new_table { - results = colors - }, - -- TODO: better preview? - sorter = sorters.get_generic_fuzzy_sorter(), - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - - actions.close(prompt_bufnr) - vim.cmd("colorscheme " .. selection.value) - end) - - return true - end - }):find() -end - -builtin.marks = function(opts) - opts = opts or {} - - local marks = vim.api.nvim_exec("marks", true) - local marks_table = vim.fn.split(marks, "\n") - - -- Pop off the header. - table.remove(marks_table, 1) - - pickers.new(opts,{ - prompt = 'Marks', - finder = finders.new_table { - results = marks_table, - entry_maker = make_entry.gen_from_marks(opts), - }, - previewer = previewers.vimgrep.new(opts), - sorter = sorters.get_generic_fuzzy_sorter(), - }):find() -end - -builtin.registers = function(opts) - opts = opts or {} - - local registers_table = {"\"", "_", "#", "=", "_", "/", "*", "+", ":", ".", "%"} - - -- named - for i = 0, 9 do - table.insert(registers_table, tostring(i)) - end - - -- alphabetical - for i = 65, 90 do - table.insert(registers_table, string.char(i)) - end - - pickers.new(opts,{ - prompt_title = 'Registers', - finder = finders.new_table { - results = registers_table, - entry_maker = make_entry.gen_from_registers(opts), - }, - -- use levenshtein as n-gram doesn't support <2 char matches - sorter = sorters.get_levenshtein_sorter(), - attach_mappings = function(_, map) - map('i', '', actions.paste_register) - map('i', '', actions.edit_register) - - return true - end, - }):find() -end - --- find normal mode mappings -builtin.keymaps = function(opts) - opts = opts or {} - local modes = {"n", "i", "c"} - local keymaps_table = {} - for _, mode in pairs(modes) do - local keymaps_iter = vim.api.nvim_get_keymap(mode) - for _, keymap in pairs(keymaps_iter) do - table.insert(keymaps_table, keymap) - end - end - - pickers.new({}, { - prompt_title = 'Key Maps', - finder = finders.new_table { - results = keymaps_table, - entry_maker = function(line) - return { - valid = line ~= "", - value = line, - ordinal = line.lhs .. line.rhs, - display = line.mode .. ' ' .. utils.display_termcodes(line.lhs) .. ' ' .. line.rhs - } - end - }, - sorter = conf.generic_sorter() - }):find() -end - -builtin.filetypes = function(opts) - opts = opts or {} - - local filetypes = vim.fn.getcompletion('', 'filetype') - - pickers.new({}, { - prompt_title = 'Filetypes', - finder = finders.new_table { - results = filetypes, - }, - sorter = conf.generic_sorter(), - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - actions.close(prompt_bufnr) - vim.cmd('setfiletype ' .. selection[1]) - end) - return true - end - }):find() -end - -builtin.highlights = function(opts) - opts = opts or {} - - local highlights = vim.fn.getcompletion('', 'highlight') - - pickers.new({}, { - prompt_title = 'Highlights', - finder = finders.new_table { - results = highlights, - entry_maker = make_entry.gen_from_highlights(opts) - }, - sorter = conf.generic_sorter(), - attach_mappings = function(prompt_bufnr) - actions.goto_file_selection_edit:replace(function() - local selection = actions.get_selected_entry() - actions.close(prompt_bufnr) - vim.cmd('hi ' .. selection.value) - end) - return true - end, - previewer = previewers.display_content.new(opts), - }):find() -end - -builtin.tags = function(opts) - opts = opts or {} - - local ctags_file = opts.ctags_file or 'tags' - - if not vim.loop.fs_open(vim.fn.expand(ctags_file), "r", 438) then - print('Tags file does not exists. Create one with ctags -R') - return - end - - local fd = assert(vim.loop.fs_open(vim.fn.expand(ctags_file), "r", 438)) - local stat = assert(vim.loop.fs_fstat(fd)) - local data = assert(vim.loop.fs_read(fd, stat.size, 0)) - assert(vim.loop.fs_close(fd)) - - local results = vim.split(data, '\n') - - pickers.new(opts,{ - prompt = 'Tags', - finder = finders.new_table { - results = results, - entry_maker = make_entry.gen_from_ctags(opts), - }, - previewer = previewers.ctags.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function() - actions._goto_file_selection:enhance { - post = function() - local selection = actions.get_selected_entry() - - local scode = string.gsub(selection.scode, '[$]$', '') - scode = string.gsub(scode, [[\\]], [[\]]) - scode = string.gsub(scode, [[\/]], [[/]]) - scode = string.gsub(scode, '[*]', [[\*]]) - - vim.cmd('norm! gg') - vim.fn.search(scode) - vim.cmd('norm! zz') - end, - } - return true - end - }):find() -end - -builtin.current_buffer_tags = function(opts) - opts = opts or {} - return builtin.tags(vim.tbl_extend("force", {only_current_file = true, hide_filename = true}, opts)) -end +builtin.builtin = require('telescope.builtin.internal').builtin +builtin.planets = require('telescope.builtin.internal').planets +builtin.commands = require('telescope.builtin.internal').commands +builtin.quickfix = require('telescope.builtin.internal').quickfix +builtin.loclist = require('telescope.builtin.internal').loclist +builtin.oldfiles = require('telescope.builtin.internal').oldfiles +builtin.command_history = require('telescope.builtin.internal').command_history +builtin.vim_options = require('telescope.builtin.internal').vim_options +builtin.help_tags = require('telescope.builtin.internal').help_tags +builtin.man_pages = require('telescope.builtin.internal').man_pages +builtin.reloader = require('telescope.builtin.internal').reloader +builtin.buffers = require('telescope.builtin.internal').buffers +builtin.colorscheme = require('telescope.builtin.internal').colorscheme +builtin.marks = require('telescope.builtin.internal').marks +builtin.registers = require('telescope.builtin.internal').registers +builtin.keymaps = require('telescope.builtin.internal').keymaps +builtin.filetypes = require('telescope.builtin.internal').filetypes +builtin.highlights = require('telescope.builtin.internal').highlights + +builtin.lsp_references = require('telescope.builtin.lsp').references +builtin.lsp_document_symbols = require('telescope.builtin.lsp').document_symbols +builtin.lsp_code_actions = require('telescope.builtin.lsp').code_actions +builtin.lsp_workspace_symbols = require('telescope.builtin.lsp').workspace_symbols return builtin diff --git a/lua/telescope/builtin/internal.lua b/lua/telescope/builtin/internal.lua new file mode 100644 index 0000000..76bc969 --- /dev/null +++ b/lua/telescope/builtin/internal.lua @@ -0,0 +1,595 @@ +local actions = require('telescope.actions') +local finders = require('telescope.finders') +local make_entry = require('telescope.make_entry') +local path = require('telescope.path') +local pickers = require('telescope.pickers') +local previewers = require('telescope.previewers') +local sorters = require('telescope.sorters') +local utils = require('telescope.utils') + +local conf = require('telescope.config').values + +local filter = vim.tbl_filter + +local internal = {} + +-- TODO: What the heck should we do for accepting this. +-- vim.fn.setreg("+", "nnoremap $TODO :lua require('telescope.builtin').()") +-- TODO: Can we just do the names instead? +internal.builtin = function(opts) + opts.hide_filename = utils.get_default(opts.hide_filename, true) + opts.ignore_filename = utils.get_default(opts.ignore_filename, true) + + local objs = {} + + local modules = { + require('telescope.builtin.internal'), + require('telescope.builtin.git'), + require('telescope.builtin.lsp'), + require('telescope.builtin.files'), + } + + for _, m in ipairs(modules) do + for k, v in pairs(m) do + local debug_info = debug.getinfo(v) + + table.insert(objs, { + filename = string.sub(debug_info.source, 2), + text = k, + }) + end + end + + pickers.new(opts, { + prompt_title = 'Telescope Builtin', + finder = finders.new_table { + results = objs, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = previewers.builtin.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(_) + actions.goto_file_selection_edit:replace(actions.run_builtin) + return true + end + }):find() +end + +internal.planets = function(opts) + local show_pluto = opts.show_pluto or false + + local sourced_file = require('plenary.debug_utils').sourced_filepath() + local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h:h") + + local globbed_files = vim.fn.globpath(base_directory .. '/data/memes/planets/', '*', true, true) + local acceptable_files = {} + for _, v in ipairs(globbed_files) do + if not show_pluto and v:find("pluto") then + else + table.insert(acceptable_files,vim.fn.fnamemodify(v, ':t')) + end + end + + pickers.new { + prompt_title = 'Planets', + finder = finders.new_table { + results = acceptable_files, + entry_maker = function(line) + return { + ordinal = line, + display = line, + filename = base_directory .. '/data/memes/planets/' .. line, + } + end + }, + previewer = previewers.cat.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + actions.close(prompt_bufnr) + + print("Enjoy astronomy! You viewed:", selection.display) + end) + + return true + end, + }:find() +end + +internal.commands = function(opts) + pickers.new({}, { + prompt_title = 'Commands', + finder = finders.new_table { + results = (function() + local command_iter = vim.api.nvim_get_commands({}) + local commands = {} + + for _, cmd in pairs(command_iter) do + table.insert(commands, cmd) + end + + return commands + end)(), + entry_maker = function(line) + return { + valid = line ~= "", + value = line, + ordinal = line.name, + display = line.name + } + end + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + actions.close(prompt_bufnr) + local val = selection.value + local cmd = string.format([[:%s ]], val.name) + + if val.nargs == "0" then + vim.cmd(cmd) + else + vim.cmd [[stopinsert]] + vim.fn.feedkeys(cmd) + end + end) + + return true + end + }):find() +end + +internal.quickfix = function(opts) + local locations = vim.fn.getqflist() + + if vim.tbl_isempty(locations) then + return + end + + pickers.new(opts, { + prompt_title = 'Quickfix', + finder = finders.new_table { + results = locations, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = previewers.qflist.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +internal.loclist = function(opts) + local locations = vim.fn.getloclist(0) + local filename = vim.api.nvim_buf_get_name(0) + + for _, value in pairs(locations) do + value.filename = filename + end + + if vim.tbl_isempty(locations) then + return + end + + pickers.new(opts, { + prompt_title = 'Loclist', + finder = finders.new_table { + results = locations, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = previewers.qflist.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +internal.oldfiles = function(opts) + pickers.new(opts, { + prompt_title = 'Oldfiles', + finder = finders.new_table(vim.tbl_filter(function(val) + return 0 ~= vim.fn.filereadable(val) + end, vim.v.oldfiles)), + sorter = conf.file_sorter(opts), + previewer = previewers.cat.new(opts), + }):find() +end + +internal.command_history = function(opts) + local history_string = vim.fn.execute('history cmd') + local history_list = vim.split(history_string, "\n") + + local results = {} + for i = #history_list, 3, -1 do + local item = history_list[i] + local _, finish = string.find(item, "%d+ +") + table.insert(results, string.sub(item, finish + 1)) + end + + pickers.new(opts, { + prompt_title = 'Command History', + finder = finders.new_table(results), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(_, map) + map('i', '', actions.set_command_line) + + -- TODO: Find a way to insert the text... it seems hard. + -- map('i', '', actions.insert_value, { expr = true }) + + return true + end, + }):find() +end + +internal.vim_options = function(opts) + -- Load vim options. + local vim_opts = loadfile(utils.data_directory() .. path.separator .. 'options' .. path.separator .. 'options.lua')().options + + pickers.new(opts, { + prompt = 'options', + finder = finders.new_table { + results = vim_opts, + entry_maker = make_entry.gen_from_vimoptions(opts), + }, + -- TODO: previewer for Vim options + -- previewer = previewers.help.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function() + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + local esc = "" + + if vim.fn.mode() == "i" then + -- TODO: don't make this local + esc = vim.api.nvim_replace_termcodes("", true, false, true) + end + + -- TODO: Make this actually work. + + -- actions.close(prompt_bufnr) + -- vim.api.nvim_win_set_var(vim.fn.nvim_get_current_win(), "telescope", 1) + -- print(prompt_bufnr) + -- print(vim.fn.bufnr()) + -- vim.cmd([[ autocmd BufEnter ++nested ++once startinsert!]]) + -- print(vim.fn.winheight(0)) + + -- local prompt_winnr = vim.fn.getbufinfo(prompt_bufnr)[1].windows[1] + -- print(prompt_winnr) + + -- local float_opts = {} + -- float_opts.relative = "editor" + -- float_opts.anchor = "sw" + -- float_opts.focusable = false + -- float_opts.style = "minimal" + -- float_opts.row = vim.api.nvim_get_option("lines") - 2 -- TODO: include `cmdheight` and `laststatus` in this calculation + -- float_opts.col = 2 + -- float_opts.height = 10 + -- float_opts.width = string.len(selection.last_set_from)+15 + -- local buf = vim.fn.nvim_create_buf(false, true) + -- vim.fn.nvim_buf_set_lines(buf, 0, 0, false, {"default value: abcdef", "last set from: " .. selection.last_set_from}) + -- local status_win = vim.fn.nvim_open_win(buf, false, float_opts) + -- -- vim.api.nvim_win_set_option(status_win, "winblend", 100) + -- vim.api.nvim_win_set_option(status_win, "winhl", "Normal:PmenuSel") + -- -- vim.api.nvim_set_current_win(status_win) + -- vim.cmd[[redraw!]] + -- vim.cmd("autocmd CmdLineLeave : ++once echom 'beep'") + vim.api.nvim_feedkeys(string.format("%s:set %s=%s", esc, selection.name, selection.current_value), "m", true) + end) + + return true + end + }):find() +end + +internal.help_tags = function(opts) + local tags = {} + for _, file in pairs(vim.fn.findfile('doc/tags', vim.o.runtimepath, -1)) do + local f = assert(io.open(file, "rb")) + for line in f:lines() do + table.insert(tags, line) + end + f:close() + end + + pickers.new(opts, { + prompt_title = 'Help', + finder = finders.new_table { + results = tags, + entry_maker = make_entry.gen_from_taglist(opts), + }, + -- TODO: previewer for Vim help + previewer = previewers.help.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions._goto_file_selection:replace(function(_, cmd) + local selection = actions.get_selected_entry() + actions.close(prompt_bufnr) + if cmd == 'edit' or cmd == 'new' then + vim.cmd('help ' .. selection.value) + elseif cmd == 'vnew' then + vim.cmd('vert bo help ' .. selection.value) + elseif cmd == 'tabedit' then + vim.cmd('tab help ' .. selection.value) + end + end) + + return true + end + }):find() +end + +internal.man_pages = function(opts) + local cmd = opts.man_cmd or "apropos --sections=1 ''" + + local pages = utils.get_os_command_output(cmd) + + local lines = {} + for s in pages:gmatch("[^\r\n]+") do + table.insert(lines, s) + end + + pickers.new(opts, { + prompt_title = 'Man', + finder = finders.new_table { + results = lines, + entry_maker = make_entry.gen_from_apropos(opts), + }, + previewer = previewers.man.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions._goto_file_selection:replace(function(_, cmd) + local selection = actions.get_selected_entry() + + actions.close(prompt_bufnr) + if cmd == 'edit' or cmd == 'new' then + vim.cmd('Man ' .. selection.value) + elseif cmd == 'vnew' then + vim.cmd('vert bo Man ' .. selection.value) + elseif cmd == 'tabedit' then + vim.cmd('tab Man ' .. selection.value) + end + end) + + return true + end + }):find() +end + +internal.reloader = function(opts) + local package_list = vim.tbl_keys(package.loaded) + + -- filter out packages we don't want and track the longest package name + opts.column_len = 0 + for index, module_name in pairs(package_list) do + if type(require(module_name)) ~= 'table' or module_name:sub(1,1) == "_" or package.searchpath(module_name, package.path) == nil then + table.remove(package_list, index) + elseif #module_name > opts.column_len then + opts.column_len = #module_name + end + end + + pickers.new(opts, { + prompt_title = 'Packages', + finder = finders.new_table { + results = package_list, + entry_maker = make_entry.gen_from_packages(opts), + }, + -- previewer = previewers.vim_buffer.new(opts), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(prompt_bufnr) + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + + actions.close(prompt_bufnr) + require('plenary.reload').reload_module(selection.value) + print(string.format("[%s] - module reloaded", selection.value)) + end) + + return true + end + }):find() +end + +internal.buffers = function(opts) + local bufnrs = filter(function(b) + return + (opts.show_all_buffers + or vim.api.nvim_buf_is_loaded(b)) + and 1 == vim.fn.buflisted(b) + end, vim.api.nvim_list_bufs()) + + local buffers = {} + local default_selection_idx = 1 + for _, bufnr in ipairs(bufnrs) do + local flag = bufnr == vim.fn.bufnr('') and '%' or (bufnr == vim.fn.bufnr('#') and '#' or ' ') + + if opts.sort_lastused and flag == "#" then + default_selection_idx = 2 + end + + local element = { + bufnr = bufnr, + flag = flag, + info = vim.fn.getbufinfo(bufnr)[1], + } + + if opts.sort_lastused and (flag == "#" or flag == "%") then + local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1) + table.insert(buffers, idx, element) + else + table.insert(buffers, element) + end + end + + if not opts.bufnr_width then + local max_bufnr = math.max(unpack(bufnrs)) + opts.bufnr_width = #tostring(max_bufnr) + end + + pickers.new(opts, { + prompt_title = 'Buffers', + finder = finders.new_table { + results = buffers, + entry_maker = make_entry.gen_from_buffer(opts) + }, + previewer = previewers.vim_buffer.new(opts), + sorter = conf.generic_sorter(opts), + default_selection_index = default_selection_idx, + }):find() +end + +internal.colorscheme = function(opts) + local colors = vim.list_extend(opts.colors or {}, vim.fn.getcompletion('', 'color')) + + pickers.new(opts,{ + prompt = 'Change Colorscheme', + finder = finders.new_table { + results = colors + }, + -- TODO: better preview? + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + + actions.close(prompt_bufnr) + vim.cmd("colorscheme " .. selection.value) + end) + + return true + end + }):find() +end + +internal.marks = function(opts) + local marks = vim.api.nvim_exec("marks", true) + local marks_table = vim.fn.split(marks, "\n") + + -- Pop off the header. + table.remove(marks_table, 1) + + pickers.new(opts,{ + prompt = 'Marks', + finder = finders.new_table { + results = marks_table, + entry_maker = make_entry.gen_from_marks(opts), + }, + previewer = previewers.vimgrep.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +internal.registers = function(opts) + local registers_table = {"\"", "_", "#", "=", "_", "/", "*", "+", ":", ".", "%"} + + -- named + for i = 0, 9 do + table.insert(registers_table, tostring(i)) + end + + -- alphabetical + for i = 65, 90 do + table.insert(registers_table, string.char(i)) + end + + pickers.new(opts,{ + prompt_title = 'Registers', + finder = finders.new_table { + results = registers_table, + entry_maker = make_entry.gen_from_registers(opts), + }, + -- use levenshtein as n-gram doesn't support <2 char matches + sorter = sorters.get_levenshtein_sorter(), + attach_mappings = function(_, map) + actions.goto_file_selection_edit:replace(actions.paste_register) + map('i', '', actions.edit_register) + + return true + end, + }):find() +end + +-- find normal mode mappings +internal.keymaps = function(opts) + local modes = {"n", "i", "c"} + local keymaps_table = {} + for _, mode in pairs(modes) do + local keymaps_iter = vim.api.nvim_get_keymap(mode) + for _, keymap in pairs(keymaps_iter) do + table.insert(keymaps_table, keymap) + end + end + + pickers.new({}, { + prompt_title = 'Key Maps', + finder = finders.new_table { + results = keymaps_table, + entry_maker = function(line) + return { + valid = line ~= "", + value = line, + ordinal = line.lhs .. line.rhs, + display = line.mode .. ' ' .. utils.display_termcodes(line.lhs) .. ' ' .. line.rhs + } + end + }, + sorter = conf.generic_sorter(opts), + }):find() +end + +internal.filetypes = function(opts) + local filetypes = vim.fn.getcompletion('', 'filetype') + + pickers.new({}, { + prompt_title = 'Filetypes', + finder = finders.new_table { + results = filetypes, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + actions.close(prompt_bufnr) + vim.cmd('setfiletype ' .. selection[1]) + end) + return true + end + }):find() +end + +internal.highlights = function(opts) + local highlights = vim.fn.getcompletion('', 'highlight') + + pickers.new({}, { + prompt_title = 'Highlights', + finder = finders.new_table { + results = highlights, + entry_maker = make_entry.gen_from_highlights(opts) + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + actions.close(prompt_bufnr) + vim.cmd('hi ' .. selection.value) + end) + return true + end, + previewer = previewers.display_content.new(opts), + }):find() +end + + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod +end + +return apply_checks(internal) diff --git a/lua/telescope/builtin/lsp.lua b/lua/telescope/builtin/lsp.lua new file mode 100644 index 0000000..50acdc9 --- /dev/null +++ b/lua/telescope/builtin/lsp.lua @@ -0,0 +1,182 @@ +local actions = require('telescope.actions') +local finders = require('telescope.finders') +local make_entry = require('telescope.make_entry') +local pickers = require('telescope.pickers') +local previewers = require('telescope.previewers') +local utils = require('telescope.utils') + +local conf = require('telescope.config').values + +local lsp = {} + +lsp.references = function(opts) + opts.shorten_path = utils.get_default(opts.shorten_path, true) + + local params = vim.lsp.util.make_position_params() + params.context = { includeDeclaration = true } + + local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/references", params, opts.timeout or 10000) + local locations = {} + for _, server_results in pairs(results_lsp) do + if server_results.result then + vim.list_extend(locations, vim.lsp.util.locations_to_items(server_results.result) or {}) + end + end + + if vim.tbl_isempty(locations) then + return + end + + pickers.new(opts, { + prompt_title = 'LSP References', + finder = finders.new_table { + results = locations, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = previewers.qflist.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +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, opts.timeout or 10000) + + 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_title = 'LSP Document Symbols', + finder = finders.new_table { + results = locations, + entry_maker = make_entry.gen_from_quickfix(opts) + }, + previewer = previewers.qflist.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +lsp.code_actions = function(opts) + local params = vim.lsp.util.make_range_params() + + params.context = { + diagnostics = vim.lsp.diagnostic.get_line_diagnostics() + } + + local results_lsp, err = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, opts.timeout or 10000) + + if err then + print("ERROR: " .. err) + return + end + + if not results_lsp or vim.tbl_isempty(results_lsp) then + print("No results from textDocument/codeAction") + return + end + + local results = (results_lsp[1] or results_lsp[2]).result; + + if not results or #results == 0 then + print("No code actions available") + return + end + + for i,x in ipairs(results) do + x.idx = i + end + + pickers.new(opts, { + prompt_title = 'LSP Code Actions', + finder = finders.new_table { + results = results, + entry_maker = function(line) + return { + valid = line ~= nil, + value = line, + ordinal = line.idx .. line.title, + display = line.idx .. ': ' .. line.title + } + end + }, + attach_mappings = function(prompt_bufnr) + actions.goto_file_selection_edit:replace(function() + local selection = actions.get_selected_entry() + actions.close(prompt_bufnr) + local val = selection.value + + if val.edit or type(val.command) == "table" then + if val.edit then + vim.lsp.util.apply_workspace_edit(val.edit) + end + if type(val.command) == "table" then + vim.lsp.buf.execute_command(val.command) + end + else + vim.lsp.buf.execute_command(val) + end + end) + + return true + end, + sorter = conf.generic_sorter(opts), + }):find() +end + +lsp.workspace_symbols = function(opts) + opts.shorten_path = utils.get_default(opts.shorten_path, true) + + local params = {query = opts.query or ''} + local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params, opts.timeout or 10000) + + if not results_lsp or vim.tbl_isempty(results_lsp) then + print("No results from workspace/symbol") + return + end + + local locations = {} + for _, server_results in pairs(results_lsp) do + if server_results.result then + vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result, 0) or {}) + end + end + + if vim.tbl_isempty(locations) then + return + end + + pickers.new(opts, { + prompt_title = 'LSP Workspace Symbols', + finder = finders.new_table { + results = locations, + entry_maker = make_entry.gen_from_quickfix(opts) + }, + previewer = previewers.qflist.new(opts), + sorter = conf.generic_sorter(opts), + }):find() +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod +end + +return apply_checks(lsp) diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index fc8b569..2a4ae5c 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -329,7 +329,7 @@ function make_entry.gen_from_buffer(opts) bufnr = entry.bufnr, filename = bufname, - lnum = entry.info.lnum or 1, + lnum = entry.info.lnum and entry.info.lnum or 1, indicator = indicator, } end diff --git a/lua/telescope/previewers.lua b/lua/telescope/previewers.lua index ee98f1c..a51c17e 100644 --- a/lua/telescope/previewers.lua +++ b/lua/telescope/previewers.lua @@ -474,6 +474,7 @@ previewers.ctags = defaulter(function(_) vim.api.nvim_win_set_option(status.preview_win, 'scrolloff', 999) pcall(vim.fn.matchdelete, self.state.hl_id, self.state.hl_win) + vim.cmd "norm! gg" vim.fn.search(scode) self.state.hl_win = status.preview_win @@ -483,6 +484,45 @@ previewers.ctags = defaulter(function(_) } end, {}) +previewers.builtin = defaulter(function(_) + return previewers.new_buffer_previewer { + setup = function() + return {} + end, + + teardown = function(self) + if self.state and self.state.hl_id then + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.hl_win) + self.state.hl_id = nil + end + end, + + preview_fn = function(self, entry, status) + with_preview_window(status, nil, function() + local module_name = vim.fn.fnamemodify(entry.filename, ':t:r') + local text = module_name .. '\\.' .. entry.text + + local new_bufnr = vim.fn.bufnr(entry.filename, true) + vim.fn.bufload(new_bufnr) + + vim.api.nvim_win_set_buf(status.preview_win, new_bufnr) + vim.api.nvim_win_set_option(status.preview_win, 'wrap', false) + vim.api.nvim_win_set_option(status.preview_win, 'winhl', 'Normal:Normal') + vim.api.nvim_win_set_option(status.preview_win, 'signcolumn', 'no') + vim.api.nvim_win_set_option(status.preview_win, 'foldlevel', 100) + vim.api.nvim_win_set_option(status.preview_win, 'scrolloff', 999) + + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.hl_win) + vim.cmd "norm! gg" + vim.fn.search(text) + + self.state.hl_win = status.preview_win + self.state.hl_id = vim.fn.matchadd('Search', text) + end) + end + } +end, {}) + previewers.qflist = defaulter(function(opts) opts = opts or {}