diff --git a/.luacheckrc b/.luacheckrc index 0fb34a9..49c9bcf 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -18,6 +18,7 @@ globals = { "TelescopeCachedTails", "TelescopeCachedNgrams", "_TelescopeConfigurationValues", + "__TelescopeKeymapStore", } -- Global objects defined by the C code diff --git a/README.md b/README.md index ab05407..54dd401 100644 --- a/README.md +++ b/README.md @@ -409,6 +409,7 @@ Built-in functions. Ready to be bound to any key you like. :smile: | `builtin.lsp_workspace_symbols` | Searches in LSP all workspace symbols. | | `builtin.lsp_code_actions` | Lists LSP action to be trigged on enter. | | `builtin.lsp_range_code_actions` | Lists LSP range code action to be trigged on enter. | +| `builtin.lsp_diagnostics` | Lists LSP Diagnostics in the current document. | | .................................. | Your next awesome picker function here :D | ### Git Pickers diff --git a/doc/telescope.txt b/doc/telescope.txt index 518bc6e..b5bec93 100644 --- a/doc/telescope.txt +++ b/doc/telescope.txt @@ -252,16 +252,16 @@ previewers.Previewer() *previewers.Previewer()* -previewers.buffer_previewer_maker({opts}, {bufnr}, {filepath})*previewers.buffer_previewer_maker()* +previewers.buffer_previewer_maker({filepath}, {bufnr}, {opts})*previewers.buffer_previewer_maker()* A universal way of reading a file into a buffer previewer. It handles async reading, cache, highlighting, displaying directories and provides a callback which can be used, to jump to a line in the buffer. Parameters: ~ - {opts} (table) keys: `use_ft_detect`, `bufname` and `callback` - {bufnr} (number) Where the content will be written {filepath} (string) String to the filepath, will be expanded + {bufnr} (number) Where the content will be written + {opts} (table) keys: `use_ft_detect`, `bufname` and `callback` previewers.cat() *previewers.cat()* diff --git a/lua/telescope/builtin/init.lua b/lua/telescope/builtin/init.lua index 2d76c79..940d3a3 100644 --- a/lua/telescope/builtin/init.lua +++ b/lua/telescope/builtin/init.lua @@ -64,6 +64,7 @@ builtin.spell_suggest = require('telescope.builtin.internal').spell_suggest 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_diagnostics = require('telescope.builtin.lsp').diagnostics builtin.lsp_range_code_actions = require('telescope.builtin.lsp').range_code_actions builtin.lsp_workspace_symbols = require('telescope.builtin.lsp').workspace_symbols diff --git a/lua/telescope/builtin/lsp.lua b/lua/telescope/builtin/lsp.lua index 49bc698..8324cf1 100644 --- a/lua/telescope/builtin/lsp.lua +++ b/lua/telescope/builtin/lsp.lua @@ -64,7 +64,10 @@ lsp.document_symbols = function(opts) entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts) }, previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), + sorter = conf.prefilter_sorter{ + tag = "symbol_type", + sorter = conf.generic_sorter(opts) + } }):find() end @@ -180,7 +183,32 @@ lsp.workspace_symbols = function(opts) entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts) }, previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), + sorter = conf.prefilter_sorter{ + tag = "symbol_type", + sorter = conf.generic_sorter(opts) + } + }):find() +end + +lsp.diagnostics = function(opts) + local locations = utils.diagnostics_to_tbl(opts) + + if vim.tbl_isempty(locations) then + print('No diagnostics found') + return + end + + pickers.new(opts, { + prompt_title = 'LSP Diagnostics', + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_diagnostics(opts) + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter{ + tag = "type", + sorter = conf.generic_sorter(opts) + } }):find() end diff --git a/lua/telescope/config.lua b/lua/telescope/config.lua index 5e025e3..17c1e91 100644 --- a/lua/telescope/config.lua +++ b/lua/telescope/config.lua @@ -161,6 +161,7 @@ function config.set_defaults(defaults) set("default_mappings", nil) set("generic_sorter", sorters.get_generic_fuzzy_sorter) + set("prefilter_sorter", sorters.prefilter) set("file_sorter", sorters.get_fuzzy_file) set("file_ignore_patterns", nil) diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index 8c793d7..2b27328 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -27,6 +27,13 @@ local lsp_type_highlight = { ["Variable"] = "TelescopeResultsVariable", } +local lsp_type_diagnostic = { + [1] = "Error", + [2] = "Warning", + [3] = "Information", + [4] = "Hint" +} + local make_entry = {} do @@ -847,6 +854,77 @@ function make_entry.gen_from_ctags(opts) end end +function make_entry.gen_from_lsp_diagnostics(opts) + opts = opts or {} + opts.tail_path = utils.get_default(opts.tail_path, true) + + local signs = {} + for _, v in pairs(lsp_type_diagnostic) do + signs[v] = vim.trim(vim.fn.sign_getdefined("LspDiagnosticsSign" .. v)[1].text) + end + -- expose line width for longer msg if opts.hide_filename + local line_width = utils.get_default(opts.line_width, 48) + local displayer = entry_display.create { + separator = "▏", + items = { + { width = 11 }, + { width = line_width }, + { remaining = true } + } + } + + local make_display = function(entry) + local filename + if not opts.hide_filename then + filename = entry.filename + if opts.tail_path then + filename = utils.path_tail(filename) + elseif opts.shorten_path then + filename = utils.path_shorten(filename) + end + end + + -- add styling of entries + local pos = string.format("%3d:%2d", entry.lnum, entry.col) + local line_info = { + string.format("%s %s", signs[entry.type], pos), + string.format("LspDiagnosticsDefault%s", entry.type) + } + -- remove line break to avoid display issues + local text = string.format("%-" .. line_width .."s", entry.text:gsub(".* | ", ""):gsub("[\n]", "")) + + return displayer { + line_info, + text, + filename, + } + end + + return function(entry) + local filename = entry.filename or vim.api.nvim_buf_get_name(entry.bufnr) + + return { + valid = true, + + value = entry, + ordinal = ( + not opts.ignore_filename and filename + or '' + ) .. ' ' .. entry.text, + display = make_display, + bufnr = entry.bufnr, + filename = filename, + type = entry.type, + lnum = entry.lnum, + col = entry.col, + text = entry.text, + start = entry.start, + finish = entry.finish, + } + end +end + + function make_entry.gen_from_autocommands(_) local displayer = entry_display.create { separator = "▏", diff --git a/lua/telescope/sorters.lua b/lua/telescope/sorters.lua index a85a75b..6ae404d 100644 --- a/lua/telescope/sorters.lua +++ b/lua/telescope/sorters.lua @@ -39,6 +39,7 @@ function Sorter:new(opts) return setmetatable({ state = {}, + filter_function = opts.filter_function, scoring_function = opts.scoring_function, highlighter = opts.highlighter, discard = opts.discard, @@ -84,7 +85,13 @@ function Sorter:score(prompt, entry) return FILTERED end - local score = self:scoring_function(prompt or "", ordinal, entry) + local filter_score + if self.filter_function ~= nil then + filter_score, prompt = self:filter_function(prompt, entry) + end + + local score = (filter_score == FILTERED and FILTERED or + self:scoring_function(prompt or "", ordinal, entry)) if score == FILTERED then self:_mark_discarded(prompt, ordinal) @@ -467,4 +474,45 @@ sorters.get_substr_matcher = function() } end +local substr_matcher = function(_, prompt, line, _) + local display = line:lower() + local search_terms = util.max_split(prompt, "%s") + local matched = 0 + local total_search_terms = 0 + for _, word in pairs(search_terms) do + total_search_terms = total_search_terms + 1 + if display:find(word, 1, true) then + matched = matched + 1 + end + end + + return matched == total_search_terms and 0 or FILTERED +end + +local filter_function = function(opts) + local scoring_function = vim.F.if_nil(opts.filter_function, substr_matcher) + local tag = vim.F.if_nil(opts.tag, "ordinal") + local delimiter = vim.F.if_nil(opts.delimiter, ":") + + return function(_, prompt, entry) + local filter = "^(" .. delimiter .. "(%S+)" .. "[" .. delimiter .. "%s]" .. ")" + local matched = prompt:match(filter) + + if matched == nil then + return 0, prompt + end + -- clear prompt of tag + prompt = prompt:sub(#matched + 1, -1) + local query = vim.trim(matched:gsub(delimiter, "")) + return scoring_function(_, query, entry[tag], _), prompt + end +end + +sorters.prefilter = function(opts) + local sorter = opts.sorter + sorter.filter_function = filter_function(opts) + sorter._was_discarded = function() return false end + return sorter +end + return sorters diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua index 928b502..cd58c13 100644 --- a/lua/telescope/utils.lua +++ b/lua/telescope/utils.lua @@ -82,6 +82,41 @@ utils.quickfix_items_to_entries = function(locations) return results end +utils.diagnostics_to_tbl = function(opts) + opts = opts or {} + + local bufnr = vim.api.nvim_get_current_buf() + local filename = vim.api.nvim_buf_get_name(bufnr) + local buffer_diags = vim.lsp.diagnostic.get(bufnr, opts.client_id) + + local items = {} + local lsp_type_diagnostic = {[1] = "Error", [2] = "Warning", [3] = "Information", [4] = "Hint"} + local insert_diag = function(diag) + local start = diag.range['start'] + local finish = diag.range['end'] + local row = start.line + local col = start.character + + local line = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or {""})[1] + + table.insert(items, { + bufnr = bufnr, + filename = filename, + lnum = row + 1, + col = col + 1, + start = start, + finish = finish, + text = vim.trim(line .. " | " .. diag.message), + type = lsp_type_diagnostic[diag.severity] or lsp_type_diagnostic[1] + }) + end + + for _, diag in ipairs(buffer_diags) do insert_diag(diag) end + + table.sort(items, function(a, b) return a.lnum < b.lnum end) + return items +end + -- TODO: Figure out how to do this... could include in plenary :) -- NOTE: Don't use this yet. It will segfault sometimes. --