if vim.g.loaded_ember then return end vim.g.loaded_ember = true ---@type table local handlers = {} local ms = vim.lsp.protocol.Methods local chars = {} for i = 32, 126 do table.insert(chars, string.char(i)) end ---@type lsp.InitializeResult local initializeResult = { capabilities = { hoverProvider = true, definitionProvider = true, referencesProvider = true, completionProvider = { triggerCharacters = chars, }, }, serverInfo = { name = "ember_ls", version = "0.0.1", }, } handlers[ms.initialize] = function(_, callback) callback(nil, initializeResult) end -- hover ====================================================================== ---@param _ lsp.HoverParams ---@param callback fun(err?: lsp.ResponseError, result: lsp.Hover) handlers[ms.textDocument_hover] = function(_, callback) local word = vim.fn.expand("") local url_format = "https://api.dictionaryapi.dev/api/v2/entries/en/%s" vim.system( { "curl", url_format:format(word) }, vim.schedule_wrap(function(out) local contents if out.code ~= 0 then contents = "word fetch failed" else local ok, decoded = pcall(vim.json.decode, out.stdout) if ok and decoded and decoded[1] then contents = decoded[1].meanings[1].definitions[1].definition else contents = decoded.message -- this api gives a nice message if no result end end callback(nil, { contents = contents }) end) ) end -- completion ================================================================= ---@param params lsp.CompletionParams ---@return string word_to_complete local get_word_to_complete = function(params) local col = params.position.character + 1 local line = vim.api.nvim_get_current_line() local line_to_cursor = line:sub(1, col) local regex = vim.regex("\\k*$") return line:sub(regex:match_str(line_to_cursor) + 1, col) end ---@return vim.lsp.inline_completion.Item[] local function completion_buffer(params) local word = get_word_to_complete(params) local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) local matches = vim.fn.matchstrlist(lines, [[\<\k\+\>]]) vim.list.unique(matches, function(x) ---@diagnostic disable-next-line: undefined-field return x.text end) matches = vim.fn.matchfuzzy(matches, word, { key = "text" }) return vim .iter(matches) :map(function(v) -- dont match current word if v.text == word then return end return { label = v.text, kind = vim.lsp.protocol.CompletionItemKind["Text"], } end) :totable() end local get_spell_candidates = vim.func._memoize("concat", function(w) local entries = vim.fn.spellsuggest(w) entries = vim.fn.matchfuzzy(entries, w, { limit = 5 }) return vim .iter(entries) :map(function(v) if string.find(v, "[ ']") ~= nil then return end return { label = v, kind = vim.lsp.protocol.CompletionItemKind["Text"], } end) :totable() end) ---@return vim.lsp.inline_completion.Item[] local function completion_spell(params) local word = get_word_to_complete(params) return get_spell_candidates(word) end ---@param params lsp.CompletionParams ---@param callback fun(err?: lsp.ResponseError, result: lsp.CompletionItem[]) handlers[ms.textDocument_completion] = function(params, callback) local items = {} if vim.wo.spell then vim.list_extend(items, completion_spell(params)) end vim.list_extend(items, completion_buffer(params)) callback(nil, { items = items, isIncomplete = #items > 0, }) end -- init ======================================================================= ---@type vim.lsp.ClientConfig local cfg = { name = "ember_ls", cmd = function() return { request = function(method, params, callback) if handlers[method] then handlers[method](params, callback) end end, notify = function() end, is_closing = function() end, terminate = function() end, } end, } vim.lsp.config("ember_ls", cfg)