From 25e9cb52aab7c63bc501ade3b0a2d6f1a7adabf9 Mon Sep 17 00:00:00 2001 From: aarondill Date: Fri, 12 Jan 2024 12:54:04 -0600 Subject: [PATCH 1/9] fix: remove global in select_range this diff is large due to the removal of a duplicate select_range function that seems to be a copy/paste error. This adds a parameter and simplifies the logic for normalizing the v_table values --- lua/tabnine/utils.lua | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index af9292c..3ba2a3a 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -111,39 +111,17 @@ function M.set(array) return uniqueValues end -function M.select_range(range) +---Selects a given range of text +---@param range table +---@param selection_mode? 'charwise'|'linewise'|'blockwise'|'v'|'V'|'' +function M.select_range(range, selection_mode) local start_row, start_col, end_row, end_col = range[1][1], range[1][2], range[2][1], range[2][2] local v_table = { charwise = "v", linewise = "V", blockwise = "" } selection_mode = selection_mode or "charwise" -- Normalise selection_mode - if vim.tbl_contains(vim.tbl_keys(v_table), selection_mode) then selection_mode = v_table[selection_mode] end - - -- enter visual mode if normal or operator-pending (no) mode - -- Why? According to https://learnvimscriptthehardway.stevelosh.com/chapters/15.html - -- If your operator-pending mapping ends with some text visually selected, Vim will operate on that text. - -- Otherwise, Vim will operate on the text between the original cursor position and the new position. - local mode = api.nvim_get_mode() - if mode.mode ~= selection_mode then - -- Call to `nvim_replace_termcodes()` is needed for sending appropriate command to enter blockwise mode - selection_mode = vim.api.nvim_replace_termcodes(selection_mode, true, true, true) - api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {}) - end - - api.nvim_win_set_cursor(0, { start_row, start_col - 1 }) - vim.cmd("normal! o") - api.nvim_win_set_cursor(0, { end_row, end_col - 1 }) -end - -function M.select_range(range) - local start_row, start_col, end_row, end_col = range[1][1], range[1][2], range[2][1], range[2][2] - - local v_table = { charwise = "v", linewise = "V", blockwise = "" } - selection_mode = selection_mode or "charwise" - - -- Normalise selection_mode - if vim.tbl_contains(vim.tbl_keys(v_table), selection_mode) then selection_mode = v_table[selection_mode] end + selection_mode = v_table[selection_mode] or selection_mode -- enter visual mode if normal or operator-pending (no) mode -- Why? According to https://learnvimscriptthehardway.stevelosh.com/chapters/15.html From a2457b5752eba01052dcb92284f54816b3a21a94 Mon Sep 17 00:00:00 2001 From: aarondill Date: Fri, 12 Jan 2024 13:02:16 -0600 Subject: [PATCH 2/9] fix: allow nil arguments to debounce function --- lua/tabnine/utils.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index 3ba2a3a..d364bd6 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -4,13 +4,16 @@ local api = vim.api local M = {} local last_changedtick = vim.b.changedtick +---@param func fun(...: unknown): any the callback +---@param delay integer delay in milliseconds +---@return fun(...: unknown) function M.debounce(func, delay) local timer_id return function(...) if timer_id then fn.timer_stop(timer_id) end - local args = { ... } + local args = table.pack(...) timer_id = fn.timer_start(delay, function() - func(unpack(args)) + return func(unpack(args, 1, args.n)) end) end end From 4adca46d4cfa770db3d129d93cac3beaa937c5bf Mon Sep 17 00:00:00 2001 From: aarondill Date: Fri, 12 Jan 2024 13:09:02 -0600 Subject: [PATCH 3/9] refactor: add some basic types --- lua/tabnine/utils.lua | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index d364bd6..dfca3be 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -18,19 +18,29 @@ function M.debounce(func, delay) end end +---@param str string +---@return string[] function M.str_to_lines(str) return fn.split(str, "\n") end +---@param lines string[] +---@return string function M.lines_to_str(lines) return fn.join(lines, "\n") end +---@param str string +---@param suffix string +---@return string function M.remove_matching_suffix(str, suffix) if not M.ends_with(str, suffix) then return str end return str:sub(1, -#suffix - 1) end +---@param str string +---@param prefix string +---@return string function M.remove_matching_prefix(str, prefix) if not M.starts_with(str, prefix) then return str end return str:sub(#prefix) @@ -45,19 +55,21 @@ function M.script_path() return str:match("(.*/)") .. "../.." end -function M.prequire(...) - local status, lib = pcall(require, ...) +---pcall require. Returns nil if the module is not found. +---Note: use @module to manage types. +---@param modname string +---@return unknown? +function M.prequire(modname) + local status, lib = pcall(require, modname) if status then return lib end return nil end +---@return boolean function M.pumvisible() local cmp = M.prequire("cmp") - if cmp then - return cmp.visible() - else - return vim.fn.pumvisible() > 0 - end + if cmp then return cmp.visible() end + return vim.fn.pumvisible() > 0 end function M.current_position() From e2579aef281ecdb021a35e796bee3d6c44c66cbc Mon Sep 17 00:00:00 2001 From: aarondill Date: Fri, 12 Jan 2024 13:25:16 -0600 Subject: [PATCH 4/9] refactor!: remove utils.script_path and replace with utils.module_dir This more clearly describes what it returns. script_path is replaced with script_dir which returns a value based on the currently running file note: module_dir relies on utils.lua's location, and will break if the file is moved --- lua/tabnine/binary.lua | 2 +- lua/tabnine/chat/binary.lua | 2 +- lua/tabnine/chat/init.lua | 4 ++-- lua/tabnine/status.lua | 2 +- lua/tabnine/utils.lua | 13 +++++++++++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lua/tabnine/binary.lua b/lua/tabnine/binary.lua index b8c7662..fa77619 100644 --- a/lua/tabnine/binary.lua +++ b/lua/tabnine/binary.lua @@ -8,7 +8,7 @@ local TabnineBinary = {} local config = require("tabnine.config") local api_version = "4.4.223" -local binaries_path = utils.script_path() .. "/binaries" +local binaries_path = utils.module_dir() .. "/binaries" local function arch_and_platform() local os_uname = uv.os_uname() diff --git a/lua/tabnine/chat/binary.lua b/lua/tabnine/chat/binary.lua index fbf9f52..fff0bd3 100644 --- a/lua/tabnine/chat/binary.lua +++ b/lua/tabnine/chat/binary.lua @@ -13,7 +13,7 @@ local function binary_name() end end -local binary_path = utils.script_path() .. "/../chat/target/release/" .. binary_name() +local binary_path = utils.module_dir() .. "/chat/target/release/" .. binary_name() function ChatBinary:available() return vim.fn.executable(binary_path) == 1 diff --git a/lua/tabnine/chat/init.lua b/lua/tabnine/chat/init.lua index 3b36647..4156fb9 100644 --- a/lua/tabnine/chat/init.lua +++ b/lua/tabnine/chat/init.lua @@ -7,8 +7,8 @@ local config = require("tabnine.config") local M = { enabled = false } -local CHAT_STATE_FILE = utils.script_path() .. "/../chat_state.json" -local CHAT_SETTINGS_FILE = utils.script_path() .. "/../chat_settings.json" +local CHAT_STATE_FILE = utils.module_dir() .. "/chat_state.json" +local CHAT_SETTINGS_FILE = utils.module_dir() .. "/chat_settings.json" local chat_state = nil local chat_settings = nil diff --git a/lua/tabnine/status.lua b/lua/tabnine/status.lua index 513f651..93f0333 100644 --- a/lua/tabnine/status.lua +++ b/lua/tabnine/status.lua @@ -3,7 +3,7 @@ local fn = vim.fn local utils = require("tabnine.utils") local M = {} -local DISABLED_FILE = utils.script_path() .. "/.disabled" +local DISABLED_FILE = utils.module_dir() .. "/.disabled" local config = require("tabnine.config") local state = require("tabnine.state") local tabnine_binary = require("tabnine.binary") diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index dfca3be..a9ee875 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -50,9 +50,18 @@ function M.subset(tbl, from, to) return { unpack(tbl, from, to) } end -function M.script_path() +---returns the directory of the running script +---@return string +function M.script_dir() local str = debug.getinfo(2, "S").source:sub(2) - return str:match("(.*/)") .. "../.." + return str:match("(.*/)") or "./" +end + +---returns the directory of the root of the module +---@return string +function M.module_dir() + -- HACK: This only works if this file is not moved! + return M.script_dir() .. "../.." end ---pcall require. Returns nil if the module is not found. From 5280143aceb4de69d25079849d107e1b6a1dc702 Mon Sep 17 00:00:00 2001 From: aarondill Date: Fri, 12 Jan 2024 13:34:36 -0600 Subject: [PATCH 5/9] refactor!: current_position now returns two values use { current_position() } for the old behavior. This is unused in the code base. should it be removed? --- lua/tabnine/utils.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index a9ee875..fe57d21 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -81,8 +81,11 @@ function M.pumvisible() return vim.fn.pumvisible() > 0 end +---Return the position of the cursor +---@return number line, number column function M.current_position() - return { fn.line("."), fn.col(".") } + ---@diagnostic disable-next-line: return-type-mismatch -- This is fine, they return non-nil values + return fn.line("."), fn.col(".") end function M.ends_with(str, suffix) From a687158ba8acac730382aabdb15e07197ab1f44c Mon Sep 17 00:00:00 2001 From: aarondill Date: Fri, 12 Jan 2024 13:40:23 -0600 Subject: [PATCH 6/9] refactor!: ends_with and starts_with types. Remove special case for str=='' This special case seems to be unused in the codebase and is very unintuitive. --- lua/tabnine/utils.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index fe57d21..4e729ad 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -88,15 +88,19 @@ function M.current_position() return fn.line("."), fn.col(".") end +---Returns true if str ends with suffix. +---@param str string +---@param suffix string +---@return boolean function M.ends_with(str, suffix) - if str == "" then return true end - return str:sub(-#suffix) == suffix end +---Returns true if str starts with prefix. +---@param str string +---@param prefix string +---@return boolean function M.starts_with(str, prefix) - if str == "" then return true end - return str:sub(1, #prefix) == prefix end From e5452ac65c11dbc5bf8530f35bc72da2803b70fd Mon Sep 17 00:00:00 2001 From: aarondill Date: Fri, 12 Jan 2024 13:50:15 -0600 Subject: [PATCH 7/9] refactor: finish off types Slight code changes, no behavior changes. --- lua/tabnine/utils.lua | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index 4e729ad..16bfff6 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -2,7 +2,6 @@ local fn = vim.fn local api = vim.api local M = {} -local last_changedtick = vim.b.changedtick ---@param func fun(...: unknown): any the callback ---@param delay integer delay in milliseconds @@ -104,33 +103,50 @@ function M.starts_with(str, prefix) return str:sub(1, #prefix) == prefix end +---Returns true if the current cursor position is the end of the current line +---@return boolean function M.is_end_of_line() return fn.col(".") == fn.col("$") end +---Returns the text after the current cursor position to the end of the current line +---@return string function M.end_of_line() return api.nvim_buf_get_text(0, fn.line(".") - 1, fn.col(".") - 1, fn.line(".") - 1, fn.col("$"), {})[1] end -function M.document_changed() - local current_changedtick = last_changedtick - last_changedtick = vim.b.changedtick - return last_changedtick > current_changedtick +do + ---The last time we checked for a document change + local last_changedtick = vim.b.changedtick + ---Returns true if the document has changed since the last call + ---@return boolean + function M.document_changed() + local current_changedtick = last_changedtick + last_changedtick = vim.b.changedtick + return last_changedtick > current_changedtick + end end +---Returns the currently selected text. If no in visual mode, returns the empty string. +---@return string function M.selected_text() - local mode = vim.fn.mode() + local mode = vim.fn.mode() ---@type string if mode ~= "v" and mode ~= "V" and mode ~= "" then return "" end - local a_orig = vim.fn.getreg("a") + local a_orig = vim.fn.getreg("a", 1) vim.cmd([[silent! normal! "aygv]]) - local text = vim.fn.getreg("a") + ---@diagnostic disable-next-line: assign-type-mismatch -- This is fine. it returns a string + local text = vim.fn.getreg("a") ---@type string vim.fn.setreg("a", a_orig) return text end +---gets the unique values from the array +---@generic T +---@param array T[] +---@return T[] function M.set(array) - local set = {} - local uniqueValues = {} + local set = {} ---@type table + local uniqueValues = {} ---@type unknown[] for _, value in ipairs(array) do if not set[value] then From 1c329f63a80e41cec118995843264eec44801011 Mon Sep 17 00:00:00 2001 From: aarondill Date: Mon, 15 Jan 2024 21:47:42 -0600 Subject: [PATCH 8/9] refactor: add types to subset table.pack can't be used here because neovim functions will error if a `n` key is present. --- lua/tabnine/utils.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index 16bfff6..dbd02f0 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -1,5 +1,6 @@ local fn = vim.fn local api = vim.api +local unpack = table.unpack or unpack local M = {} @@ -45,7 +46,15 @@ function M.remove_matching_prefix(str, prefix) return str:sub(#prefix) end +---@generic T +---@param tbl T[] | {n?: integer} The table to get a subset from +---@param from integer? defaults to 1 +---@param to integer? defaults to tbl.n or #tbl. +---@return T[] function M.subset(tbl, from, to) + to = to or tbl.n or #tbl -- support table.pack values if no end given + -- We can't use table.pack here because nvim_buf_set_extmark will error if non-integer keys are present + -- Ideally, implementations would ignore string keys in an 'array'. return { unpack(tbl, from, to) } end From 1b523d3ff119e8dbe895cb902e570bcfff83d601 Mon Sep 17 00:00:00 2001 From: aarondill Date: Tue, 6 Feb 2024 10:05:46 -0600 Subject: [PATCH 9/9] fix: table.pack may not be defined --- lua/tabnine/utils.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/tabnine/utils.lua b/lua/tabnine/utils.lua index dbd02f0..fec79fe 100644 --- a/lua/tabnine/utils.lua +++ b/lua/tabnine/utils.lua @@ -1,7 +1,7 @@ local fn = vim.fn local api = vim.api local unpack = table.unpack or unpack - +local pack = table.pack or vim.F.pack_len local M = {} ---@param func fun(...: unknown): any the callback @@ -11,7 +11,7 @@ function M.debounce(func, delay) local timer_id return function(...) if timer_id then fn.timer_stop(timer_id) end - local args = table.pack(...) + local args = pack(...) timer_id = fn.timer_start(delay, function() return func(unpack(args, 1, args.n)) end)