diff --git a/README.md b/README.md index fa62174..f6d7df9 100644 --- a/README.md +++ b/README.md @@ -398,6 +398,8 @@ which is where this is usually installed by `snap`. Highlight groups that are user configurable to change the appearance of certain UI elements. - `FlutterToolsOutlineIndentGuides` - indent guides for the outline window +- `FlutterToolsDimmed` - de-emphasised text: closing tags, outline return types and menu hints. + Links to Nvim 0.13's built-in `Dimmed` group where available, and to `Comment` otherwise. #### Widget guides diff --git a/ftplugin/dart/init.lua b/ftplugin/dart/init.lua index 3220e12..23ec60e 100644 --- a/ftplugin/dart/init.lua +++ b/ftplugin/dart/init.lua @@ -9,7 +9,7 @@ local path = require("flutter-tools.utils.path") vim.opt_local.comments = [[sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://]] vim.opt_local.commentstring = [[//%s]] -vim.opt.includeexpr = "v:lua.require('flutter-tools.resolve_url').resolve_url(v:fname)" +vim.opt_local.includeexpr = "v:lua.require('flutter-tools.resolve_url').resolve_url(v:fname)" local full_path = vim.fn.expand("%:p") -- Prevent writes to files in the pub cache and FVM folder. diff --git a/lua/flutter-tools/config.lua b/lua/flutter-tools/config.lua index bbc42ca..6d1b6de 100644 --- a/lua/flutter-tools/config.lua +++ b/lua/flutter-tools/config.lua @@ -115,7 +115,7 @@ local config = { register_configurations = nil, }, closing_tags = { - highlight = "Comment", + highlight = ui.DIM_HL, prefix = "// ", priority = 10, enabled = true, diff --git a/lua/flutter-tools/dev_tools.lua b/lua/flutter-tools/dev_tools.lua index 584cc93..6fb26b6 100644 --- a/lua/flutter-tools/dev_tools.lua +++ b/lua/flutter-tools/dev_tools.lua @@ -45,19 +45,8 @@ end ---@param url string local function open_dev_tools(url) - local open_command, args = utils.open_command() - if not open_command then - return ui.notify( - "Sorry your Operating System is not supported, please raise an issue", - ui.ERROR - ) - end - table.insert(args, url) - Job:new({ - command = open_command, - args = args, - detached = true, - }):start() + local _, err = vim.ui.open(url) + if err then return ui.notify(err, ui.ERROR) end end local function start_browser() diff --git a/lua/flutter-tools/lsp/color/init.lua b/lua/flutter-tools/lsp/color/init.lua index 746dbbd..46e4fee 100644 --- a/lua/flutter-tools/lsp/color/init.lua +++ b/lua/flutter-tools/lsp/color/init.lua @@ -10,7 +10,7 @@ function M.document_color() local client = lsp_utils.get_dartls_client() if client and client.server_capabilities.colorProvider then - client.request("textDocument/documentColor", params, nil, 0) + client:request("textDocument/documentColor", params, nil, 0) end end diff --git a/lua/flutter-tools/lsp/color/utils.lua b/lua/flutter-tools/lsp/color/utils.lua index 2d15b99..db5c68c 100644 --- a/lua/flutter-tools/lsp/color/utils.lua +++ b/lua/flutter-tools/lsp/color/utils.lua @@ -109,7 +109,7 @@ end ---@param start_pos number ---@param end_pos number local function hl_range(bufnr, namespace, hlname, start_pos, end_pos) - local hl = vim.highlight + local hl = vim.hl hl.range(bufnr, namespace, hlname, start_pos, end_pos, { priority = hl.priorities.user }) end diff --git a/lua/flutter-tools/lsp/commands.lua b/lua/flutter-tools/lsp/commands.lua index 22a7c97..b2f4fc1 100644 --- a/lua/flutter-tools/lsp/commands.lua +++ b/lua/flutter-tools/lsp/commands.lua @@ -19,7 +19,7 @@ function M.refactor_perform(command, ctx) prompt = "Enter a name for the variable: " default = "newVariable" else - client.request("workspace/executeCommand", command) + client:request("workspace/executeCommand", command) return end @@ -34,7 +34,7 @@ function M.refactor_perform(command, ctx) -- see more: https://github.com/dart-lang/sdk/blob/e995cb5f7cd67d39c1ee4bdbe95c8241db36725f/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart#L53 local optionsIndex = 6 command.arguments[optionsIndex] = { name = name } - client.request("workspace/executeCommand", command) + client:request("workspace/executeCommand", command) end) end diff --git a/lua/flutter-tools/lsp/init.lua b/lua/flutter-tools/lsp/init.lua index e95672d..7132b0c 100644 --- a/lua/flutter-tools/lsp/init.lua +++ b/lua/flutter-tools/lsp/init.lua @@ -119,15 +119,9 @@ local function get_defaults(opts) handlers = { -- TODO: can this be replaced with the initialized capability ["$/progress"] = handle_progress, - ["dart/textDocument/publishClosingLabels"] = utils.lsp_handler( - require("flutter-tools.labels").closing_tags - ), - ["dart/textDocument/publishOutline"] = utils.lsp_handler( - require("flutter-tools.outline").document_outline - ), - ["dart/textDocument/publishFlutterOutline"] = utils.lsp_handler( - require("flutter-tools.guides").widget_guides - ), + ["dart/textDocument/publishClosingLabels"] = require("flutter-tools.labels").closing_tags, + ["dart/textDocument/publishOutline"] = require("flutter-tools.outline").document_outline, + ["dart/textDocument/publishFlutterOutline"] = require("flutter-tools.guides").widget_guides, ["textDocument/documentColor"] = require("flutter-tools.lsp.color").on_document_color, ["dart/reanalyze"] = function() end, -- returns: None ["dart/textDocument/super"] = handle_super, @@ -156,13 +150,15 @@ end function M.restart() local client = lsp_utils.get_dartls_client() - if client then - local bufs = lsp.get_buffers_by_client_id(client.id) - lsp.stop_client(client.id) - local client_id = lsp.start_client(client.config) - for _, buf in pairs(bufs) do - if client_id then lsp.buf_attach_client(buf, client_id) end - end + if not client then return end + local client_config = client.config + local bufs = vim.tbl_keys(client.attached_buffers) + client:stop() + -- `Client:stop()` marks the client as stopping synchronously, so the default + -- `reuse_client` predicate will not hand the dying client back to us here. The + -- first `start` spawns the replacement, the rest attach to it. + for _, buf in ipairs(bufs) do + if api.nvim_buf_is_valid(buf) then lsp.start(client_config, { bufnr = buf }) end end end @@ -215,7 +211,7 @@ function M.dart_lsp_super() character = lsp_col, -- 0-based character position }, } - client.request("dart/textDocument/super", params, nil, 0) + client:request("dart/textDocument/super", params, nil, 0) end function M.dart_reanalyze() lsp.buf_request(0, "dart/reanalyze") end @@ -254,11 +250,7 @@ local function get_server_config(user_config, callback) config.commands = merge_config(defaults.commands, config.commands) config.on_init = function(client, _) - if vim.fn.has("nvim-0.12") == 0 then - return client.notify("workspace/didChangeConfiguration", { settings = config.settings }) - else - return client:notify("workspace/didChangeConfiguration", { settings = config.settings }) - end + return client:notify("workspace/didChangeConfiguration", { settings = config.settings }) end callback(config) end) diff --git a/lua/flutter-tools/lsp/rename.lua b/lua/flutter-tools/lsp/rename.lua index 6b2e90a..b44a3f2 100644 --- a/lua/flutter-tools/lsp/rename.lua +++ b/lua/flutter-tools/lsp/rename.lua @@ -67,7 +67,7 @@ function M.rename(new_name, opts) local params = util.make_position_params(win, client.offset_encoding) --[[@as lsp.RenameParams]] params.newName = name local handler = client.handlers["textDocument/rename"] or lsp.handlers["textDocument/rename"] - client.request("textDocument/rename", params, function(...) + client:request("textDocument/rename", params, function(...) handler(...) if result then lsp.util.apply_workspace_edit(result, client.offset_encoding) end @@ -93,7 +93,7 @@ function M.rename(new_name, opts) }, } - client.request("workspace/willRenameFiles", params, function(err, result) + client:request("workspace/willRenameFiles", params, function(err, result) if err then ui.notify(err.message or "Error on getting lsp rename results!", ui.ERROR) return @@ -106,9 +106,9 @@ function M.rename(new_name, opts) end -- Try to use prepare rename first - if client.supports_method("textDocument/prepareRename") then + if client:supports_method("textDocument/prepareRename") then local params = util.make_position_params(win, client.offset_encoding) - client.request("textDocument/prepareRename", params, function(err, result) + client:request("textDocument/prepareRename", params, function(err, result) if err or result == nil then if err then ui.notify(("Error on prepareRename: %s"):format(err.message), ui.ERROR) @@ -142,7 +142,7 @@ function M.rename(new_name, opts) end) end, bufnr) else - assert(client.supports_method("textDocument/rename"), "Client must support textDocument/rename") + assert(client:supports_method("textDocument/rename"), "Client must support textDocument/rename") if new_name then rename_fix_imports(new_name) return diff --git a/lua/flutter-tools/menu.lua b/lua/flutter-tools/menu.lua index 943c7df..5e5adac 100644 --- a/lua/flutter-tools/menu.lua +++ b/lua/flutter-tools/menu.lua @@ -39,7 +39,7 @@ local function command_entry_maker(max_width) }) local items = { { en.label, "Type" } } - if has_hint then table.insert(items, { en.hint, "Comment" }) end + if has_hint then table.insert(items, { en.hint, ui.DIM_HL }) end return displayer(items) end return function(entry) diff --git a/lua/flutter-tools/outline.lua b/lua/flutter-tools/outline.lua index 6581124..1feb982 100644 --- a/lua/flutter-tools/outline.lua +++ b/lua/flutter-tools/outline.lua @@ -56,7 +56,7 @@ local icon_highlights = { [icons.GETTER] = { name = "Getter", link = "Function" }, [icons.ENUM] = { name = "Enum", link = "Type" }, [icons.ENUM_CONSTANT] = { name = "EnumConstant", link = "Type" }, - [icons.DEFAULT] = { name = "Default", link = "Comment" }, + [icons.DEFAULT] = { name = "Default", link = ui.DIM_HL }, } api.nvim_set_hl(0, MARKER_HL, { default = true, link = "NonText" }) @@ -149,7 +149,7 @@ local function parse_outline(result, node, indent, marker) local length = #table.concat(display_str, " ") local return_type = element.returnType and element.returnType .. " " - length = add_segment(text, hl, return_type, "Comment", length) + length = add_segment(text, hl, return_type, ui.DIM_HL, length) length = add_segment(text, hl, element.name, "None", length) length = add_segment(text, hl, element.typeParameters, "Type", length) length = add_segment(text, hl, element.parameters, "Bold", length) diff --git a/lua/flutter-tools/ui.lua b/lua/flutter-tools/ui.lua index 8067be1..99ee37a 100644 --- a/lua/flutter-tools/ui.lua +++ b/lua/flutter-tools/ui.lua @@ -30,6 +30,16 @@ local api = vim.api local namespace_id = api.nvim_create_namespace("flutter_tools_popups") M.entry_type = entry_type +---Highlight group for de-emphasised text (closing tags, outline chrome, menu hints). +---Nvim 0.13 ships a core `Dimmed` group for exactly this; below that we fall back to +---`Comment`, which is what `Dimmed` itself links to by default. Declared with +---`default = true` so a colorscheme or user definition always wins. +M.DIM_HL = "FlutterToolsDimmed" +utils.highlight(M.DIM_HL, { + default = true, + link = vim.fn.has("nvim-0.13") == 1 and "Dimmed" or "Comment", +}) + function M.clear_highlights(buf_id, ns_id, line_start, line_end) line_start = line_start or 0 line_end = line_end or -1 @@ -44,14 +54,14 @@ function M.add_highlights(buf_id, lines, ns_id) ns_id = ns_id or namespace_id if not lines then return end for _, line in ipairs(lines) do - api.nvim_buf_add_highlight( - buf_id, - ns_id, - line.highlight, - line.line_number, - line.column_start, - line.column_end - ) + -- NOTE: `strict = false` so an end column past the end of the line is clamped + -- rather than raising, matching the behaviour callers relied on previously. + api.nvim_buf_set_extmark(buf_id, ns_id, line.line_number, line.column_start, { + end_row = line.line_number, + end_col = line.column_end, + hl_group = line.highlight, + strict = false, + }) end end diff --git a/lua/flutter-tools/utils/init.lua b/lua/flutter-tools/utils/init.lua index 29c69d5..0a5e213 100644 --- a/lua/flutter-tools/utils/init.lua +++ b/lua/flutter-tools/utils/init.lua @@ -3,7 +3,6 @@ local fn = vim.fn local api = vim.api local lazy = require("flutter-tools.lazy") -local path = lazy.require("flutter-tools.utils.path") ---@module "flutter-tools.utils.path" --- if every item in a table is an empty value return true function M.list_is_empty(tbl) @@ -82,54 +81,13 @@ function M.executable(bin) return fn.executable(bin) > 0 end ---Get the attribute value of a specified highlight ---@param name string ---@param attribute string ----@return string? -function M.get_hl(name, attribute) - if api.nvim_get_hl then - local hl = api.nvim_get_hl(0, { name = name }) - return hl[attribute] - else - local ok, hl = pcall(api.nvim_get_hl_by_name, name, true) - if not ok then return end - hl.foreground = hl.foreground and "#" .. bit.tohex(hl.foreground, 6) - hl.background = hl.background and "#" .. bit.tohex(hl.background, 6) - local attr = ({ bg = "background", fg = "foreground" })[attribute] or attribute - return hl[attr] - end -end - -function M.open_command() - if path.is_mac then return "open", {} end - if path.is_linux then return "xdg-open", {} end - if path.is_windows then return "cmd.exe", { "/c", "start" } end - return nil, nil -end +---@return string|integer|boolean|nil +function M.get_hl(name, attribute) return api.nvim_get_hl(0, { name = name })[attribute] end ---@param lines string[] ---@return string function M.join(lines) return table.concat(lines, "\n") end ----Create an lsp handler compatible with the new handler signature ----see: https://github.com/neovim/neovim/pull/15504/ ----@param func function ----@return function -function M.lsp_handler(func) - return function(...) - local config_or_client_id = select(4, ...) - local is_new = type(config_or_client_id) ~= "number" - if is_new then - func(...) - else - local err = select(1, ...) - local method = select(2, ...) - local result = select(3, ...) - local client_id = select(4, ...) - local bufnr = select(5, ...) - local config = select(6, ...) - func(err, result, { method = method, client_id = client_id, bufnr = bufnr }, config) - end - end -end - ---@enum Events M.events = { PROJECT_CONFIG_CHANGED = "FlutterToolsProjectConfigChanged",