diff --git a/lua/yaml-companion/context/init.lua b/lua/yaml-companion/context/init.lua index e22809f..53e545f 100644 --- a/lua/yaml-companion/context/init.lua +++ b/lua/yaml-companion/context/init.lua @@ -9,67 +9,88 @@ M.default_schema = function() end M.ctxs = {} +M.initialized_client_ids = {} -M.setup = function(bufnr, client) - local timer = vim.loop.new_timer() +M.store_initialized_handler = function(_, _, ctx, _) + local client_id = ctx.client_id + M.initialized_client_ids[client_id] = true + + local client = vim.lsp.get_client_by_id(client_id) + local buffers = vim.lsp.get_buffers_by_client_id(client_id) + + -- The store for this client_id has been initialized, we must check + -- all existing buffers and update then accordingly. + for _, bufnr in ipairs(buffers) do + if M.ctxs[bufnr] and M.ctxs[bufnr].executed == false then + M.autodiscover(bufnr, client) + end + end +end + +M.autodiscover = function(bufnr, client) + if not M.initialized_client_ids[client.id] then + return + end + + local schema = lsp.get_jsonschema(bufnr, client) + local options = require("yaml-companion.config").options + + if schema and schema.result and schema.result[1] and schema.result[1].uri then + -- if LSP returns a name that means it came from SchemaStore + -- and we can use it right away + if schema.result[1].name then + M.ctxs[bufnr].schema = schema + M.ctxs[bufnr].executed = true + + -- if it returned something without a name it means it came from our own + -- internal schema table and we have to loop through it to get the name + else + for _, option_schema in ipairs(options.schemas.result) do + if option_schema.uri == schema.result[1].uri then + M.ctxs[bufnr].schema = { + result = { + { name = option_schema.name, uri = option_schema.uri }, + }, + } + M.ctxs[bufnr].executed = true + end + end + end + -- if LSP is not using any schema, use registered matchers + else + for _, matcher in pairs(matchers) do + local result = matcher.match(bufnr) + if result then + M.schema(bufnr, { + result = { + { name = result.name, uri = result.uri }, + }, + }) + M.ctxs[bufnr].executed = true + end + end + end + + -- No schema matched + M.ctxs[bufnr].executed = true +end + +M.setup = function(bufnr, client) local state = { bufnr = bufnr, client = client, schema = default_schema, - timer = timer, + executed = false, } - -- This timer runs periodically until - -- it updates the context state for the buffer - timer:start( - 1000, - 1000, - vim.schedule_wrap(function() - -- if we get an schema from the LSP - local schema = lsp.get_jsonschema(bufnr) - local options = require("yaml-companion.config").options - - if schema and schema.result and schema.result[1] and schema.result[1].uri then - -- if LSP returns a name that means it came from SchemaStore - -- and we can use it right away - if schema.result[1].name then - M.ctxs[bufnr].schema = schema - timer:close() - - -- if it returned something without a name it means it came from our own - -- internal schema table and we have to loop through it to get the name - else - for _, option_schema in ipairs(options.schemas.result) do - if option_schema.uri == schema.result[1].uri then - M.ctxs[bufnr].schema = { - result = { - { name = option_schema.name, uri = option_schema.uri }, - }, - } - timer:close() - end - end - end + M.ctxs[bufnr] = state - -- if LSP is not using any schema, use registered matchers - else - for _, matcher in pairs(matchers) do - local result = matcher.match(bufnr) - if result then - M.schema(bufnr, { - result = { - { name = result.name, uri = result.uri }, - }, - }) - timer:close() - end - end - end - end) - ) + -- The first time this won't work because the client is not initialized yet + -- but it will be called once per client from the initialized_handler when it is. + M.autodiscover(bufnr, client) - M.ctxs[bufnr] = state + return lsp.support_schema_selection(bufnr, client) end M.schema = function(bufnr, schema) diff --git a/lua/yaml-companion/init.lua b/lua/yaml-companion/init.lua index ec911a5..2afbbff 100644 --- a/lua/yaml-companion/init.lua +++ b/lua/yaml-companion/init.lua @@ -8,6 +8,7 @@ M.setup = function(opts) config.setup(opts, function(client, bufnr) ctx.setup(bufnr, client) end) + vim.lsp.handlers["yaml/schema/store/initialized"] = ctx.store_initialized_handler return config.options.lspconfig end diff --git a/lua/yaml-companion/lsp/requests.lua b/lua/yaml-companion/lsp/requests.lua index ea8deee..eaed91c 100644 --- a/lua/yaml-companion/lsp/requests.lua +++ b/lua/yaml-companion/lsp/requests.lua @@ -1,12 +1,33 @@ local M = {} +local lsp = vim.lsp local sync_timeout = 1000 +-- let the yamlls attached to {bufnr} know that we support +-- schema selection and it should fire a 'yaml/schema/store/initialized' +-- when ready to use it +---@param bufnr number +---@return table +M.support_schema_selection = function(bufnr, client) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + client = client or lsp.get_active_clients({ name = "yamlls", bufnr = bufnr })[1] + + if client then + return client.notify("yaml/supportSchemaSelection") + end +end + -- get all known schemas by the yamlls attached to {bufnr} ---@param bufnr number ---@return table -M.get_all_jsonschemas = function(bufnr) - local client = require("yaml-companion.lsp.util").client(bufnr) +M.get_all_jsonschemas = function(bufnr, client) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + client = client or lsp.get_active_clients({ name = "yamlls", bufnr = bufnr })[1] + if client then return client.request_sync( "yaml/get/all/jsonSchemas", @@ -20,8 +41,12 @@ end -- get schemas used for {bufnr} by the yamlls attached to it ---@param bufnr number ---@return table -M.get_jsonschema = function(bufnr) - local client = require("yaml-companion.lsp.util").client(bufnr) +M.get_jsonschema = function(bufnr, client) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + client = client or lsp.get_active_clients({ name = "yamlls", bufnr = bufnr })[1] + if client then local schemas = client.request_sync( "yaml/get/jsonSchema", diff --git a/lua/yaml-companion/lsp/util.lua b/lua/yaml-companion/lsp/util.lua index fc576b2..4ec6716 100644 --- a/lua/yaml-companion/lsp/util.lua +++ b/lua/yaml-companion/lsp/util.lua @@ -1,23 +1,12 @@ local M = {} -local nvim_lsp = vim.lsp -local yaml_lsp = require("yaml-companion.lsp.requests") +local lsp = require("yaml-companion.lsp.requests") local matchers = require("yaml-companion._matchers")._loaded --- returns the yamlls client attached to {bufnr} if it has an active yamlls attached -M.client = function(bufnr) - local clients = nvim_lsp.buf_get_clients(bufnr) - for _, value in pairs(clients) do - if value.name == "yamlls" then - return value - end - end -end - --- Get all of the yaml schemas currently available to the server. ---- @return table schemas: merged list of user-defined and server-provided yaml schemas +--- @return table schemas: merged list of user-defined, server-provided, and matcher-provided yaml schemas M.get_all_yaml_schemas = function() - local schemas = yaml_lsp.get_all_jsonschemas(0) + local schemas = lsp.get_all_jsonschemas(0) if schemas == nil then return