| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 | local M = {}M.methods = {}---checks if the character preceding the cursor is a space character---@return boolean true if it is a space character, false otherwiselocal check_backspace = function()  local col = vim.fn.col "." - 1  return col == 0 or vim.fn.getline("."):sub(col, col):match "%s"endM.methods.check_backspace = check_backspacelocal function T(str)  return vim.api.nvim_replace_termcodes(str, true, true, true)end---wraps vim.fn.feedkeys while replacing key codes with escape codes---Ex: feedkeys("<CR>", "n") becomes feedkeys("^M", "n")---@param key string---@param mode stringlocal function feedkeys(key, mode)  vim.fn.feedkeys(T(key), mode)endM.methods.feedkeys = feedkeys---checks if emmet_ls is available and active in the buffer---@return boolean true if available, false otherwiselocal is_emmet_active = function()  local clients = vim.lsp.buf_get_clients()  for _, client in pairs(clients) do    if client.name == "emmet_ls" then      return true    end  end  return falseendM.methods.is_emmet_active = is_emmet_active---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  end  local win_get_cursor = vim.api.nvim_win_get_cursor  local get_current_buf = vim.api.nvim_get_current_buf  local function inside_snippet()    -- 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 snip_begin_pos, snip_end_pos = node.parent.snippet.mark:pos_begin_end()    local pos = win_get_cursor(0)    pos[1] = pos[1] - 1 -- LuaSnip is 0-based not 1-based like nvim for rows    return pos[1] >= snip_begin_pos[1] and pos[1] <= snip_end_pos[1]  end  ---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()    -- for outdated versions of luasnip    if not luasnip.session.current_nodes then      return false    end    local pos = win_get_cursor(0)    pos[1] = pos[1] - 1    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]    -- 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 inside_snippet() and luasnip.jumpable(-1)  else    return inside_snippet() and seek_luasnip_cursor_node() and luasnip.jumpable()  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 = {    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 = true,      native_menu = false,    },    formatting = {      fields = { "kind", "abbr", "menu" },      kind_icons = {        Class = " ",        Color = " ",        Constant = "ﲀ ",        Constructor = " ",        Enum = "練",        EnumMember = " ",        Event = " ",        Field = " ",        File = "",        Folder = " ",        Function = " ",        Interface = "ﰮ ",        Keyword = " ",        Method = " ",        Module = " ",        Operator = "",        Property = " ",        Reference = " ",        Snippet = " ",        Struct = " ",        Text = " ",        TypeParameter = " ",        Unit = "塞",        Value = " ",        Variable = " ",      },      source_names = {        nvim_lsp = "(LSP)",        emoji = "(Emoji)",        path = "(Path)",        calc = "(Calc)",        cmp_tabnine = "(Tabnine)",        vsnip = "(Snippet)",        luasnip = "(Snippet)",        buffer = "(Buffer)",      },      duplicates = {        buffer = 1,        path = 1,        nvim_lsp = 0,        luasnip = 1,      },      duplicates_default = 0,      format = function(entry, vim_item)        vim_item.kind = lvim.builtin.cmp.formatting.kind_icons[vim_item.kind]        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,    },    documentation = {      border = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" },    },    sources = {      { name = "nvim_lsp" },      { name = "path" },      { name = "luasnip" },      { name = "cmp_tabnine" },      { name = "nvim_lua" },      { name = "buffer" },      { name = "calc" },      { name = "emoji" },      { name = "treesitter" },      { name = "crates" },    },    mapping = {      ["<C-k>"] = cmp.mapping.select_prev_item(),      ["<C-j>"] = cmp.mapping.select_next_item(),      ["<C-d>"] = cmp.mapping.scroll_docs(-4),      ["<C-f>"] = cmp.mapping.scroll_docs(4),      -- TODO: potentially fix emmet nonsense      ["<Tab>"] = cmp.mapping(function(fallback)        if cmp.visible() then          cmp.select_next_item()        elseif luasnip.expandable() then          luasnip.expand()        elseif jumpable() then          luasnip.jump(1)        elseif check_backspace() then          fallback()        elseif is_emmet_active() then          return vim.fn["cmp#complete"]()        else          fallback()        end      end, {        "i",        "s",      }),      ["<S-Tab>"] = cmp.mapping(function(fallback)        if cmp.visible() then          cmp.select_prev_item()        elseif 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() and cmp.confirm(lvim.builtin.cmp.confirm_opts) then          if jumpable() then            luasnip.jump(1)          end          return        end        if jumpable() then          if not luasnip.jump(1) then            fallback()          end        else          fallback()        end      end),    },  }endfunction M.setup()  require("cmp").setup(lvim.builtin.cmp)endreturn M
 |