diff --git a/lua/ivy/controller.lua b/lua/ivy/controller.lua index ff11426..be93ef6 100644 --- a/lua/ivy/controller.lua +++ b/lua/ivy/controller.lua @@ -1,80 +1,81 @@ local window = require "ivy.window" local prompt = require "ivy.prompt" local controller = {} controller.items = nil controller.callback = nil controller.run = function(name, items, callback) controller.callback = callback controller.items = items window.initialize() - window.set_items { "-- Loading ---" } + + window.set_items { { content = "-- Loading ---" } } vim.api.nvim_buf_set_name(window.get_buffer(), name) controller.input "" end controller.input = function(char) prompt.input(char) controller.update(prompt.text()) end controller.search = function(value) prompt.set(value) controller.update(prompt.text()) end controller.update = function(text) vim.schedule(function() window.set_items(controller.items(text)) vim.cmd("syntax clear IvyMatch") vim.cmd("syntax match IvyMatch '[(" .. text .. ")]'") end) end controller.complete = function() controller.checkpoint() controller.destroy() end controller.checkpoint = function() vim.api.nvim_set_current_win(window.origin) controller.callback(window.get_current_selection()) vim.api.nvim_set_current_win(window.window) end controller.next = function() local max = vim.api.nvim_buf_line_count(window.buffer) - 1 if window.index == max then return end window.index = window.index + 1 window.update() end controller.previous = function() if window.index == 0 then return end window.index = window.index - 1 window.update() end controller.origin = function() return vim.api.nvim_win_get_buf(window.origin) end controller.destroy = function() controller.items = nil controller.callback = nil window.destroy() prompt.destroy() end return controller diff --git a/lua/ivy/window.lua b/lua/ivy/window.lua index addefc1..5db6d20 100644 --- a/lua/ivy/window.lua +++ b/lua/ivy/window.lua @@ -1,144 +1,142 @@ -- 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 items = {} + local matches = {} for line in lines:gmatch "[^\r\n]+" do - table.insert(items, line) + table.insert(matches, { content = line }) end - return items -end - -local function set_items_string(buffer, lines) - vim.api.nvim_buf_set_lines(buffer, 0, 9999, false, string_to_table(lines)) -end - -local function set_items_array(buffer, lines) - if type(lines[1]) == "string" then - vim.api.nvim_buf_set_lines(buffer, 0, 9999, false, lines) - else - for i = 1, #lines do - vim.api.nvim_buf_set_lines(buffer, i - 1, 9999, false, { lines[i][2] }) - end - end + return matches end local window = {} window.index = 0 window.origin = nil window.window = nil window.buffer = nil window.initialize = function() window.make_buffer() end window.make_buffer = function() window.origin = vim.api.nvim_get_current_win() 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()", 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 - vim.api.nvim_buf_set_lines(window.get_buffer(), 0, 9999, false, { "-- No Items --" }) - elseif type(items) == "string" then - set_items_string(window.get_buffer(), items) - elseif type(items) == "table" then - set_items_array(window.get_buffer(), items) + 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 - local line_count = vim.api.nvim_buf_line_count(window.buffer) - window.index = line_count - 1 + -- 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 - 1 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.index = 0 end return window diff --git a/plugin/ivy.lua b/plugin/ivy.lua index e5f1233..e6351d3 100644 --- a/plugin/ivy.lua +++ b/plugin/ivy.lua @@ -1,72 +1,72 @@ local controller = require "ivy.controller" local utils = require "ivy.utils" local libivy = require "ivy.libivy" -- 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 vim.api.nvim_create_user_command("IvyAg", function() vim.ivy.run("AG", utils.command_finder "ag", utils.vimgrep_action()) end, { bang = true, desc = "Run ag to search for content in files" }) vim.api.nvim_create_user_command("IvyFd", function() vim.ivy.run("Files", function(term) return libivy.ivy_files(term, vim.fn.getcwd()) end, utils.file_action()) end, { bang = true, desc = "Find files in the project" }) vim.api.nvim_create_user_command("IvyBuffers", function() vim.ivy.run("Buffers", function(input) local list = {} local buffers = vim.api.nvim_list_bufs() for index = 1, #buffers do local buffer = buffers[index] -- Get the relative path from the current working directory. We need to -- substring +2 to remove the `/` from the start of the path to give us a -- true relative path local buffer_name = vim.api.nvim_buf_get_name(buffer):sub(#vim.fn.getcwd() + 2, -1) if vim.api.nvim_buf_is_loaded(buffer) and #buffer_name > 0 then local score = libivy.ivy_match(input, buffer_name) if score > -200 or #input == 0 then - table.insert(list, { score, buffer_name }) + table.insert(list, { score = score, content = buffer_name }) end end end table.sort(list, function(a, b) - return a[1] < b[1] + return a.score < b.score end) return list end, utils.file_action()) end, { bang = true, desc = "List all of the current open buffers" }) vim.api.nvim_create_user_command("IvyLines", function() vim.ivy.run("Lines", function(input) local list = {} local lines = vim.api.nvim_buf_get_lines(vim.ivy.origin(), 0, -1, false) for index = 1, #lines do local line = lines[index] local score = libivy.ivy_match(input, line) if score > -200 then local prefix = string.rep(" ", 4 - #tostring(index)) .. index .. ": " table.insert(list, { score, prefix .. line }) end end table.sort(list, function(a, b) return a[1] < b[1] end) return list end, utils.line_action()) end, { bang = true, desc = "List all of the current open buffers" }) vim.api.nvim_set_keymap("n", "b", "IvyBuffers", { nowait = true, silent = true }) vim.api.nvim_set_keymap("n", "p", "IvyFd", { nowait = true, silent = true }) vim.api.nvim_set_keymap("n", "/", "IvyAg", { nowait = true, silent = true }) vim.cmd "highlight IvyMatch cterm=bold"