diff --git a/lua/rustaceanvim/config/json.lua b/lua/rustaceanvim/config/json.lua index 351e409f..e5423435 100644 --- a/lua/rustaceanvim/config/json.lua +++ b/lua/rustaceanvim/config/json.lua @@ -1,10 +1,37 @@ local M = {} -local function tbl_set(tbl, keys, value) +local warnings = {} +local errors = {} +local is_json_config_loaded = false + +---@param warning string +local function add_warning(warning) + table.insert(warnings, warning) +end + +---@param err string +local function add_error(err) + table.insert(errors, err) +end + +---@param field_name string | nil +---@param tbl unknown +---@param keys string[] +---@param value unknown +local function tbl_set(field_name, tbl, keys, value) local next = table.remove(keys, 1) + if type(tbl) ~= 'table' then + add_warning(([[ +Ignored field '%s' of invalid type '%s': %s +Please refer to the rust-analyzer documentation at +https://rust-analyzer.github.io/manual.html#%s +]]):format(field_name, type(value), vim.inspect(value), field_name)) + return + end if #keys > 0 then tbl[next] = tbl[next] or {} - tbl_set(tbl[next], keys, value) + field_name = (field_name and field_name .. '.' or '') .. next + tbl_set(field_name, tbl[next], keys, value) else tbl[next] = value end @@ -15,14 +42,17 @@ end ---@param json_value unknown local function override_tbl_values(tbl, json_key, json_value) local keys = vim.split(json_key, '%.') - tbl_set(tbl, keys, json_value) + tbl_set(nil, tbl, keys, json_value) end ---@param json_content string ---@return table function M.silent_decode(json_content) + warnings = {} + errors = {} local ok, json_tbl = pcall(vim.json.decode, json_content) if not ok or type(json_tbl) ~= 'table' then + add_error(('Failed to decode json: %s'):format(json_tbl or 'unknown error')) return {} end return json_tbl @@ -32,6 +62,7 @@ end ---@param json_tbl { [string]: unknown } ---@param key_predicate? fun(string): boolean function M.override_with_json_keys(tbl, json_tbl, key_predicate) + is_json_config_loaded = true for json_key, value in pairs(json_tbl) do if not key_predicate or key_predicate(json_key) then override_tbl_values(tbl, json_key, value) @@ -47,4 +78,16 @@ function M.override_with_rust_analyzer_json_keys(tbl, json_tbl) end) end +function M.is_json_config_loaded() + return is_json_config_loaded +end + +function M.get_warnings() + return warnings +end + +function M.get_errors() + return errors +end + return M diff --git a/lua/rustaceanvim/health.lua b/lua/rustaceanvim/health.lua index c0489fc3..851b66a4 100644 --- a/lua/rustaceanvim/health.lua +++ b/lua/rustaceanvim/health.lua @@ -148,6 +148,25 @@ local function check_tree_sitter() end end +local function check_json_config() + local json = require('rustaceanvim.config.json') + if json.is_json_config_loaded() then + local errors = json.get_errors() + if #errors > 0 then + h.warn('.vscode/settings.json failed to load.') + vim.iter(errors):each(h.error) + return + end + local warnings = json.get_warnings() + if #warnings == 0 then + h.ok('.vscode/settings.json loaded without errors.') + else + h.warn('.vscode/settings.json loaded with warnings.') + vim.iter(warnings):each(h.warn) + end + end +end + function health.check() local types = require('rustaceanvim.types.internal') local config = require('rustaceanvim.config.internal') @@ -281,6 +300,7 @@ function health.check() check_config(config) check_for_conflicts() check_tree_sitter() + check_json_config() end return health diff --git a/spec/json_spec.lua b/spec/json_spec.lua index 838d75ae..446b6122 100644 --- a/spec/json_spec.lua +++ b/spec/json_spec.lua @@ -42,4 +42,47 @@ describe('Decode rust-analyzer settings from json', function() }, }, tbl) end) + it('persists warnings on invalid config', function() + local invalid_json_content = [[ +{ + "rust-analyzer.checkOnSave.overrideCommand": [ + "cargo", + "check", + "-p", + "service_b", + "--message-format=json" + ], + "rust-analyzer.foo.enable": true, + "rust-analyzer.foo.bar.enable": true, + "rust-analyzer.foo.bar.baz.bat": "something deeply nested", + "some-other-key.foo.bar.baz.bat": "should not be included" +} +]] + local tbl = { + ['rust-analyzer'] = { + checkOnSave = true, + }, + } + local json_tbl = json.silent_decode(invalid_json_content) + json.override_with_rust_analyzer_json_keys(tbl, json_tbl) + assert.same({ + [[ +Ignored field 'rust-analyzer.checkOnSave' of invalid type 'table': { "cargo", "check", "-p", "service_b", "--message-format=json" } +Please refer to the rust-analyzer documentation at +https://rust-analyzer.github.io/manual.html#rust-analyzer.checkOnSave +]], + }, json.get_warnings()) + end) + it('persists warnings about config parse errors', function() + local unsupported_json_content = [[ +{ + // This is a json5 comment + "rust-analyzer.foo.enable": true, +} +]] + json.silent_decode(unsupported_json_content) + assert.same({ + 'Failed to decode json: Expected object key string but found invalid token at character 5', + }, json.get_errors()) + end) end)