diff --git a/lua/ivy/backends/lsp-workspace-symbols.lua b/lua/ivy/backends/lsp-workspace-symbols.lua new file mode 100644 index 0000000..c1c18d7 --- /dev/null +++ b/lua/ivy/backends/lsp-workspace-symbols.lua @@ -0,0 +1,40 @@ +local window = require "ivy.window" +local utils = require "ivy.utils" + +local previous_results = {} +local function set_items(items) + window.set_items(items) + previous_results = items +end + +local function items(input) + local buffer_number = window.origin_buffer + local cwd = vim.fn.getcwd() + local results = {} + vim.lsp.buf_request(buffer_number, "workspace/symbol", { query = input }, function(err, server_result, _, _) + if err ~= nil then + set_items { content = "-- There was an error with workspace/symbol --" } + return + end + local locations = vim.lsp.util.symbols_to_items(server_result or {}, buffer_number) or {} + for index = 1, #locations do + local item = locations[index] + local relative_path = item.filename:sub(#cwd + 2, -1) + table.insert(results, { content = relative_path .. ":" .. item.lnum .. ": " .. item.text }) + end + + set_items(results) + end) + + return previous_results +end + +local lsp_workspace_symbols = { + name = "WorkspaceSymbol", + command = "IvyWorkspaceSymbol", + description = "Search for workspace ymbol using the lsp workspace/symbol", + items = items, + callback = utils.vimgrep_action(), +} + +return lsp_workspace_symbols diff --git a/lua/ivy/window.lua b/lua/ivy/window.lua index 87c0856..c53d6e1 100644 --- a/lua/ivy/window.lua +++ b/lua/ivy/window.lua @@ -1,144 +1,147 @@ -- Constent options that will be used for the keymaps local opts = { noremap = true, silent = true, nowait = true } -- All of the base chars that will be used for an "input" operation on the -- prompt -- stylua: ignore local chars = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "<", ">", "`", "@", "#", "~", "!", "\"", "$", "%", "^", "&", "/", "(", ")", "=", "+", "*", "-", "_", ".", ",", ";", ":", "?", "\\", "|", "'", "{", "}", "[", "]", " ", } local function string_to_table(lines) local matches = {} for line in lines:gmatch "[^\r\n]+" do table.insert(matches, { content = line }) end return matches end local window = {} window.index = 0 window.origin = nil window.window = nil window.buffer = nil +window.origin_buffer = nil window.initialize = function() window.make_buffer() end window.make_buffer = function() window.origin = vim.api.nvim_get_current_win() + window.origin_buffer = vim.api.nvim_win_get_buf(0) vim.api.nvim_command "botright split new" window.buffer = vim.api.nvim_win_get_buf(0) window.window = vim.api.nvim_get_current_win() vim.api.nvim_win_set_option(window.window, "number", false) vim.api.nvim_win_set_option(window.window, "relativenumber", false) vim.api.nvim_win_set_option(window.window, "signcolumn", "no") vim.api.nvim_buf_set_option(window.buffer, "filetype", "ivy") vim.api.nvim_buf_set_var(window.buffer, "bufftype", "nofile") for index = 1, #chars do local char = chars[index] if char == "'" then char = "\\'" end if char == "\\" then char = "\\\\\\\\" end vim.api.nvim_buf_set_keymap(window.buffer, "n", chars[index], "lua vim.ivy.input('" .. char .. "')", opts) end vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.destroy()", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.search('')", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.next()", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.previous()", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.next(); vim.ivy.checkpoint()", opts) vim.api.nvim_buf_set_keymap( window.buffer, "n", "", "lua vim.ivy.previous(); vim.ivy.checkpoint()", opts ) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.complete(vim.ivy.action.EDIT)", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.complete(vim.ivy.action.VSPLIT)", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.complete(vim.ivy.action.SPLIT)", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('BACKSPACE')", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('LEFT')", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('RIGHT')", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('DELETE_WORD')", opts) end window.get_current_selection = function() local line = vim.api.nvim_buf_get_lines(window.buffer, window.index, window.index + 1, true) if line == nil then line = { "" } end return line[1] end window.get_buffer = function() if window.buffer == nil then window.make_buffer() end return window.buffer end window.update = function() vim.api.nvim_win_set_cursor(window.window, { window.index + 1, 0 }) end window.set_items = function(items) if type(items) == "string" then items = string_to_table(items) end -- TODO(ade): Validate the items are in the correct format. This also need to -- come with some descriptive messages and possible help. -- Display no items text if there are no items to dispaly if #items == 0 then items = { { content = "-- No Items --" } } end local items_length = #items window.index = items_length - 1 for index = 1, items_length do vim.api.nvim_buf_set_lines(window.buffer, index - 1, -1, false, { items[index].content }) end -- Limit the results window size to 10 so when there are lots of results the -- window does not take up the hole terminal local line_count = items_length if line_count > 10 then line_count = 10 end vim.api.nvim_win_set_height(window.window, line_count) window.update() end window.destroy = function() if type(window.buffer) == "number" then vim.api.nvim_buf_delete(window.buffer, { force = true }) end window.buffer = nil window.window = nil window.origin = nil + window.origin_buffer = nil window.index = 0 end return window diff --git a/plugin/ivy.lua b/plugin/ivy.lua index 3bf9a67..2834681 100644 --- a/plugin/ivy.lua +++ b/plugin/ivy.lua @@ -1,33 +1,34 @@ local controller = require "ivy.controller" -- Put the controller in to the vim global so we can access it in mappings -- better without requires. You can call controller commands like `vim.ivy.xxx`. -- luacheck: ignore vim.ivy = controller local register_backend = function(backend) assert(backend.command, "The backend must have a command") assert(backend.items, "The backend must have a items function") assert(backend.callback, "The backend must have a callback function") local user_command_options = { bang = true } if backend.description ~= nil then user_command_options.desc = backend.description end local name = backend.name or backend.command vim.api.nvim_create_user_command(backend.command, function() vim.ivy.run(name, backend.items, backend.callback) end, user_command_options) if backend.keymap ~= nil then vim.api.nvim_set_keymap("n", backend.keymap, "" .. backend.command .. "", { nowait = true, silent = true }) end end register_backend(require "ivy.backends.ag") register_backend(require "ivy.backends.buffers") register_backend(require "ivy.backends.files") register_backend(require "ivy.backends.lines") +register_backend(require "ivy.backends.lsp-workspace-symbols") vim.cmd "highlight IvyMatch cterm=bold gui=bold"