| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 | local M = {}M.methods = {}local has_words_before = function()  local line, col = unpack(vim.api.nvim_win_get_cursor(0))  return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match "%s" == nilendM.methods.has_words_before = has_words_before---@deprecated use M.methods.has_words_before insteadM.methods.check_backspace = function()  return not has_words_before()endlocal T = function(str)  return vim.api.nvim_replace_termcodes(str, true, true, true)endlocal function feedkeys(key, mode)  vim.api.nvim_feedkeys(T(key), mode, true)endM.methods.feedkeys = feedkeys---when inside a snippet, seeks to the nearest luasnip field if possible, and checks if it is jumpable---@param dir number 1 for forward, -1 for backward; defaults to 1---@return boolean true if a jumpable luasnip field is found while inside a snippetlocal function jumpable(dir)  local luasnip_ok, luasnip = pcall(require, "luasnip")  if not luasnip_ok then    return false  end  local win_get_cursor = vim.api.nvim_win_get_cursor  local get_current_buf = vim.api.nvim_get_current_buf  ---sets the current buffer's luasnip to the one nearest the cursor  ---@return boolean true if a node is found, false otherwise  local function seek_luasnip_cursor_node()    -- TODO(kylo252): upstream this    -- for outdated versions of luasnip    if not luasnip.session.current_nodes then      return false    end    local node = luasnip.session.current_nodes[get_current_buf()]    if not node then      return false    end    local snippet = node.parent.snippet    local exit_node = snippet.insert_nodes[0]    local pos = win_get_cursor(0)    pos[1] = pos[1] - 1    -- exit early if we're past the exit node    if exit_node then      local exit_pos_end = exit_node.mark:pos_end()      if (pos[1] > exit_pos_end[1]) or (pos[1] == exit_pos_end[1] and pos[2] > exit_pos_end[2]) then        snippet:remove_from_jumplist()        luasnip.session.current_nodes[get_current_buf()] = nil        return false      end    end    node = snippet.inner_first:jump_into(1, true)    while node ~= nil and node.next ~= nil and node ~= snippet do      local n_next = node.next      local next_pos = n_next and n_next.mark:pos_begin()      local candidate = n_next ~= snippet and next_pos and (pos[1] < next_pos[1])        or (pos[1] == next_pos[1] and pos[2] < next_pos[2])      -- Past unmarked exit node, exit early      if n_next == nil or n_next == snippet.next then        snippet:remove_from_jumplist()        luasnip.session.current_nodes[get_current_buf()] = nil        return false      end      if candidate then        luasnip.session.current_nodes[get_current_buf()] = node        return true      end      local ok      ok, node = pcall(node.jump_from, node, 1, true) -- no_move until last stop      if not ok then        snippet:remove_from_jumplist()        luasnip.session.current_nodes[get_current_buf()] = nil        return false      end    end    -- No candidate, but have an exit node    if exit_node then      -- to jump to the exit node, seek to snippet      luasnip.session.current_nodes[get_current_buf()] = snippet      return true    end    -- No exit node, exit from snippet    snippet:remove_from_jumplist()    luasnip.session.current_nodes[get_current_buf()] = nil    return false  end  if dir == -1 then    return luasnip.in_snippet() and luasnip.jumpable(-1)  else    return luasnip.in_snippet() and seek_luasnip_cursor_node() and luasnip.jumpable(1)  endendM.methods.jumpable = jumpableM.config = function()  local status_cmp_ok, cmp = pcall(require, "cmp")  if not status_cmp_ok then    return  end  local status_luasnip_ok, luasnip = pcall(require, "luasnip")  if not status_luasnip_ok then    return  end  lvim.builtin.cmp = {    active = true,    on_config_done = nil,    enabled = function()      local buftype = vim.api.nvim_buf_get_option(0, "buftype")      if buftype == "prompt" then        return false      end      return lvim.builtin.cmp.active    end,    confirm_opts = {      behavior = cmp.ConfirmBehavior.Replace,      select = false,    },    completion = {      ---@usage The minimum length of a word to complete on.      keyword_length = 1,    },    experimental = {      ghost_text = false,      native_menu = false,    },    formatting = {      fields = { "kind", "abbr", "menu" },      max_width = 0,      kind_icons = lvim.icons.kind,      source_names = {        nvim_lsp = "(LSP)",        emoji = "(Emoji)",        path = "(Path)",        calc = "(Calc)",        cmp_tabnine = "(Tabnine)",        vsnip = "(Snippet)",        luasnip = "(Snippet)",        buffer = "(Buffer)",        tmux = "(TMUX)",        copilot = "(Copilot)",        treesitter = "(TreeSitter)",      },      duplicates = {        buffer = 1,        path = 1,        nvim_lsp = 0,        luasnip = 1,      },      duplicates_default = 0,      format = function(entry, vim_item)        local max_width = lvim.builtin.cmp.formatting.max_width        if max_width ~= 0 and #vim_item.abbr > max_width then          vim_item.abbr = string.sub(vim_item.abbr, 1, max_width - 1) .. lvim.icons.ui.Ellipsis        end        if lvim.use_icons then          vim_item.kind = lvim.builtin.cmp.formatting.kind_icons[vim_item.kind]          if entry.source.name == "copilot" then            vim_item.kind = lvim.icons.git.Octoface            vim_item.kind_hl_group = "CmpItemKindCopilot"          end          if entry.source.name == "cmp_tabnine" then            vim_item.kind = lvim.icons.misc.Robot            vim_item.kind_hl_group = "CmpItemKindTabnine"          end          if entry.source.name == "crates" then            vim_item.kind = lvim.icons.misc.Package            vim_item.kind_hl_group = "CmpItemKindCrate"          end          if entry.source.name == "lab.quick_data" then            vim_item.kind = lvim.icons.misc.CircuitBoard            vim_item.kind_hl_group = "CmpItemKindConstant"          end          if entry.source.name == "emoji" then            vim_item.kind = lvim.icons.misc.Smiley            vim_item.kind_hl_group = "CmpItemKindEmoji"          end        end        vim_item.menu = lvim.builtin.cmp.formatting.source_names[entry.source.name]        vim_item.dup = lvim.builtin.cmp.formatting.duplicates[entry.source.name]          or lvim.builtin.cmp.formatting.duplicates_default        return vim_item      end,    },    snippet = {      expand = function(args)        require("luasnip").lsp_expand(args.body)      end,    },    window = {      completion = cmp.config.window.bordered(),      documentation = cmp.config.window.bordered(),    },    sources = {      {        name = "copilot",        -- keyword_length = 0,        max_item_count = 3,        trigger_characters = {          {            ".",            ":",            "(",            "'",            '"',            "[",            ",",            "#",            "*",            "@",            "|",            "=",            "-",            "{",            "/",            "\\",            "+",            "?",            " ",            -- "\t",            -- "\n",          },        },      },      {        name = "nvim_lsp",        entry_filter = function(entry, ctx)          local kind = require("cmp.types").lsp.CompletionItemKind[entry:get_kind()]          if kind == "Snippet" and ctx.prev_context.filetype == "java" then            return false          end          if kind == "Text" then            return false          end          return true        end,      },      { name = "path" },      { name = "luasnip" },      { name = "cmp_tabnine" },      { name = "nvim_lua" },      { name = "buffer" },      { name = "calc" },      { name = "emoji" },      { name = "treesitter" },      { name = "crates" },      { name = "tmux" },    },    mapping = cmp.mapping.preset.insert {      ["<C-k>"] = cmp.mapping(cmp.mapping.select_prev_item(), { "i", "c" }),      ["<C-j>"] = cmp.mapping(cmp.mapping.select_next_item(), { "i", "c" }),      ["<Down>"] = cmp.mapping(cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Select }, { "i" }),      ["<Up>"] = cmp.mapping(cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Select }, { "i" }),      ["<C-d>"] = cmp.mapping.scroll_docs(-4),      ["<C-f>"] = cmp.mapping.scroll_docs(4),      ["<C-y>"] = cmp.mapping {        i = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = false },        c = function(fallback)          if cmp.visible() then            cmp.confirm { behavior = cmp.ConfirmBehavior.Replace, select = false }          else            fallback()          end        end,      },      ["<Tab>"] = cmp.mapping(function(fallback)        if cmp.visible() then          cmp.select_next_item()        elseif luasnip.expand_or_locally_jumpable() then          luasnip.expand_or_jump()        elseif jumpable(1) then          luasnip.jump(1)        elseif has_words_before() then          -- cmp.complete()          fallback()        else          fallback()        end      end, { "i", "s" }),      ["<S-Tab>"] = cmp.mapping(function(fallback)        if cmp.visible() then          cmp.select_prev_item()        elseif luasnip.jumpable(-1) then          luasnip.jump(-1)        else          fallback()        end      end, { "i", "s" }),      ["<C-Space>"] = cmp.mapping.complete(),      ["<C-e>"] = cmp.mapping.abort(),      ["<CR>"] = cmp.mapping(function(fallback)        if cmp.visible() then          local confirm_opts = vim.deepcopy(lvim.builtin.cmp.confirm_opts) -- avoid mutating the original opts below          local is_insert_mode = function()            return vim.api.nvim_get_mode().mode:sub(1, 1) == "i"          end          if is_insert_mode() then -- prevent overwriting brackets            confirm_opts.behavior = cmp.ConfirmBehavior.Insert          end          if cmp.confirm(confirm_opts) then            return -- success, exit early          end        end        fallback() -- if not exited early, always fallback      end),    },    cmdline = {      enable = false,      options = {        {          type = ":",          sources = {            { name = "path" },            { name = "cmdline" },          },        },        {          type = { "/", "?" },          sources = {            { name = "buffer" },          },        },      },    },  }endfunction M.setup()  local cmp = require "cmp"  cmp.setup(lvim.builtin.cmp)  if lvim.builtin.cmp.cmdline.enable then    for _, option in ipairs(lvim.builtin.cmp.cmdline.options) do      cmp.setup.cmdline(option.type, {        mapping = cmp.mapping.preset.cmdline(),        sources = option.sources,      })    end  end    if lvim.builtin.cmp.on_config_done then    lvim.builtin.cmp.on_config_done(cmp)  endendreturn M
 |