diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index dc4612c2..626afc5a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -45,7 +45,7 @@ body: - **OS**: Ubuntu 20.04 - **Flutter version**: 3.7.0 - **Is flutter in $PATH**: yes - - **neovim version**: 0.8.3 + - **neovim version**: 0.11.0 value: | - OS: - Flutter version: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 41b6cefb..f72acecd 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -42,7 +42,7 @@ jobs: uses: kdheepak/panvimdoc@main with: vimdoc: flutter-tools - version: "Neovim >= 0.8.0" + version: "Neovim >= 0.11" demojify: true treesitter: true - name: Push changes diff --git a/README.md b/README.md index c60fbe2c..8b6f7d95 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,7 @@ nnoremap K lua vim.lsp.buf.hover() nnoremap gd lua vim.lsp.buf.definition() " Open code actions using the default lsp UI, if you want to change this please see the plugins above nnoremap ca lua vim.lsp.buf.code_action() - " Open code actions for the selected visual range -xnoremap ca lua vim.lsp.buf.range_code_action() +xnoremap ca lua vim.lsp.buf.code_action() ``` Please note this is not a replacement for reading the documentation, this is only to show new users what @@ -41,7 +40,7 @@ some basic setup might look like. ## Prerequisites -- neovim 0.8.0+ +- Neovim 0.11+ ## Installation @@ -159,14 +158,21 @@ require("flutter-tools").setup {} -- use defaults - `FlutterDetach` - Ends a running session locally but keeps the process running on the device. - `FlutterOutlineToggle` - Toggle the outline window showing the widget tree for the given file. - `FlutterOutlineOpen` - Opens an outline window showing the widget tree for the given file. +- `FlutterVisualDebug` - Toggle Flutter visual debugging. +- `FlutterChangeTargetPlatform` - Cycle the target platform override for the running app. +- `FlutterToggleBrightness` - Toggle the app brightness override. - `FlutterDevTools` - Starts a Dart Dev Tools server. - `FlutterDevToolsActivate` - Activates a Dart Dev Tools server. +- `FlutterOpenDevTools` - Open the currently connected DevTools page. +- `FlutterInspectWidget` - Toggle widget inspection for the running app. - `FlutterCopyProfilerUrl` - Copies the profiler url to your system clipboard (+ register). Note that commands `FlutterRun` and `FlutterDevTools` must be executed first. +- `FlutterPubGet` - Run `flutter pub get` in the project root. +- `FlutterPubUpgrade [args]` - Run `flutter pub upgrade` with optional extra arguments. - `FlutterLspRestart` - This command restarts the dart language server, and is intended for situations where it begins to work incorrectly. - `FlutterSuper` - Go to super class, method using custom LSP method `dart/textDocument/super`. - `FlutterReanalyze` - Forces LSP server reanalyze using custom LSP method `dart/reanalyze`. -- `FlutterRename` - Renames and updates imports if `lsp.settings.renameFilesWithClasses == "always"` +- `FlutterRename` - Rename a symbol and update imports if `lsp.settings.renameFilesWithClasses == "always"`. - `FlutterLogClear` - Clears the log buffer. - `FlutterLogToggle` - Toggles the log buffer. @@ -270,14 +276,6 @@ require("flutter-tools").setup { auto_open = false -- if true this will open the outline automatically when it is first populated }, lsp = { - color = { -- show the derived colours for dart variables - enabled = false, -- whether or not to highlight color variables at all, only supported on flutter >= 2.10 - background = false, -- highlight the background - background_color = nil, -- required, when background is transparent (i.e. background_color = { r = 19, g = 17, b = 24},) - foreground = false, -- highlight the foreground - virtual_text = true, -- show the highlight using virtual text - virtual_text_str = "■", -- the virtual text character to highlight - }, on_attach = my_custom_on_attach, capabilities = my_custom_capabilities, -- e.g. lsp_status capabilities --- OR you can specify a function to deactivate or change or control how the config is created @@ -302,6 +300,30 @@ require("flutter-tools").setup { You can override any options available in the `lspconfig` setup, this call essentially wraps it and adds some extra `flutter` specific handlers and utilisation options. +### Document colors + +Plugin-managed `lsp.color` rendering is deprecated and will be removed when flutter-tools.nvim requires Neovim `0.12+`. + +On Neovim `0.12+`, use the built-in LSP document color support instead: + +```lua +vim.api.nvim_create_autocmd("LspAttach", { + callback = function(ev) + vim.lsp.document_color.enable(true, { bufnr = ev.buf }) + end, +}) +``` + +If you want to opt out of Neovim's built-in document colors for some buffers: + +```lua +vim.api.nvim_create_autocmd("LspAttach", { + callback = function(ev) + vim.lsp.document_color.enable(false, { bufnr = ev.buf }) + end, +}) +``` + **NOTE:** By default this plugin excludes analysis of the packages in the flutter SDK. If for example you jump to the definition of `StatelessWidget`, the lsp will not try and index the 100s (maybe 1000s) of diff --git a/doc/flutter-tools.txt b/doc/flutter-tools.txt index c9bd6d69..58ccdf8d 100644 --- a/doc/flutter-tools.txt +++ b/doc/flutter-tools.txt @@ -1,5 +1,5 @@ *flutter-tools.txt* - For Neovim >= 0.8.0 Last change: 2026 April 23 + For Neovim >= 0.11 Last change: 2026 May 03 ============================================================================== Table of Contents *flutter-tools-table-of-contents* @@ -62,9 +62,8 @@ A minimal native LSP configuration might look like: " Jump to definition nnoremap gd lua vim.lsp.buf.definition() " Open code actions using the default lsp UI, if you want to change this please see the plugins above - nnoremap ca lua vim.lsp.buf.code_action() - " Open code actions for the selected visual range - xnoremap ca lua vim.lsp.buf.range_code_action() + nnoremap ca lua vim.lsp.buf.code_action() + xnoremap ca lua vim.lsp.buf.code_action() < Please note this is not a replacement for reading the documentation, this is @@ -73,7 +72,7 @@ only to show new users what some basic setup might look like. PREREQUISITES *flutter-tools-flutter-tools.nvim-prerequisites* -- neovim 0.8.0+ +- Neovim 0.11+ INSTALLATION *flutter-tools-flutter-tools.nvim-installation* @@ -193,14 +192,21 @@ APP VERSION - `FlutterDetach` - Ends a running session locally but keeps the process running on the device. - `FlutterOutlineToggle` - Toggle the outline window showing the widget tree for the given file. - `FlutterOutlineOpen` - Opens an outline window showing the widget tree for the given file. +- `FlutterVisualDebug` - Toggle Flutter visual debugging. +- `FlutterChangeTargetPlatform` - Cycle the target platform override for the running app. +- `FlutterToggleBrightness` - Toggle the app brightness override. - `FlutterDevTools` - Starts a Dart Dev Tools server. - `FlutterDevToolsActivate` - Activates a Dart Dev Tools server. +- `FlutterOpenDevTools` - Open the currently connected DevTools page. +- `FlutterInspectWidget` - Toggle widget inspection for the running app. - `FlutterCopyProfilerUrl` - Copies the profiler url to your system clipboard (+ register). Note that commands `FlutterRun` and `FlutterDevTools` must be executed first. +- `FlutterPubGet` - Run `flutter pub get` in the project root. +- `FlutterPubUpgrade [args]` - Run `flutter pub upgrade` with optional extra arguments. - `FlutterLspRestart` - This command restarts the dart language server, and is intended for situations where it begins to work incorrectly. - `FlutterSuper` - Go to super class, method using custom LSP method `dart/textDocument/super`. - `FlutterReanalyze` - Forces LSP server reanalyze using custom LSP method `dart/reanalyze`. -- `FlutterRename` - Renames and updates imports if `lsp.settings.renameFilesWithClasses == "always"` +- `FlutterRename` - Rename a symbol and update imports if `lsp.settings.renameFilesWithClasses == "always"`. - `FlutterLogClear` - Clears the log buffer. - `FlutterLogToggle` - Toggles the log buffer. @@ -306,14 +312,6 @@ both are set. auto_open = false -- if true this will open the outline automatically when it is first populated }, lsp = { - color = { -- show the derived colours for dart variables - enabled = false, -- whether or not to highlight color variables at all, only supported on flutter >= 2.10 - background = false, -- highlight the background - background_color = nil, -- required, when background is transparent (i.e. background_color = { r = 19, g = 17, b = 24},) - foreground = false, -- highlight the foreground - virtual_text = true, -- show the highlight using virtual text - virtual_text_str = "■", -- the virtual text character to highlight - }, on_attach = my_custom_on_attach, capabilities = my_custom_capabilities, -- e.g. lsp_status capabilities --- OR you can specify a function to deactivate or change or control how the config is created @@ -339,6 +337,32 @@ You can override any options available in the `lspconfig` setup, this call essentially wraps it and adds some extra `flutter` specific handlers and utilisation options. + +DOCUMENT COLORS ~ + +Plugin-managed `lsp.color` rendering is deprecated and will be removed when +flutter-tools.nvim requires Neovim `0.12+`. + +On Neovim `0.12+`, use the built-in LSP document color support instead: + +>lua + vim.api.nvim_create_autocmd("LspAttach", { + callback = function(ev) + vim.lsp.document_color.enable(true, { bufnr = ev.buf }) + end, + }) +< + +If you want to opt out of Neovim's built-in document colors for some buffers: + +>lua + vim.api.nvim_create_autocmd("LspAttach", { + callback = function(ev) + vim.lsp.document_color.enable(false, { bufnr = ev.buf }) + end, + }) +< + **NOTE:** By default this plugin excludes analysis of the packages in the flutter SDK. If for example you jump to the definition of `StatelessWidget`, the lsp will not try and index the 100s (maybe 1000s) of files in that diff --git a/lua/flutter-tools/config.lua b/lua/flutter-tools/config.lua index dffd95ac..bbc42cab 100644 --- a/lua/flutter-tools/config.lua +++ b/lua/flutter-tools/config.lua @@ -65,17 +65,9 @@ local function validate_prefs(prefs) end ) end - if vim.fn.has("nvim-0.11") == 1 then - vim.validate("outline", prefs.outline, "table", true) - vim.validate("dev_log", prefs.dev_log, "table", true) - vim.validate("closing_tags", prefs.closing_tags, "table", true) - else - vim.validate({ - outline = { prefs.outline, "table", true }, - dev_log = { prefs.dev_log, "table", true }, - closing_tags = { prefs.closing_tags, "table", true }, - }) - end + vim.validate("outline", prefs.outline, "table", true) + vim.validate("dev_log", prefs.dev_log, "table", true) + vim.validate("closing_tags", prefs.closing_tags, "table", true) end ---Create a proportional split using a percentage specified as a float @@ -163,15 +155,47 @@ local deprecations = { fallback = "widget_guides", message = "please use 'widget_guides' instead", }, + lsp = { + nested = { + color = { + when = function() return vim.fn.has("nvim-0.12") == 1 end, + message = "plugin-managed document colors are deprecated and will be removed when flutter-tools.nvim requires Neovim 0.12+. On Neovim 0.12+, use vim.lsp.document_color.enable() instead", + }, + }, + }, } -local function handle_deprecation(key, value, conf) - local deprecation = deprecations[key] - if not deprecation then return end +local function notify_deprecation(key, message) vim.defer_fn( - function() ui.notify(fmt("%s is deprecated: %s", key, deprecation.message), ui.WARN) end, + function() ui.notify(fmt("%s is deprecated: %s", key, message), ui.WARN, { once = true }) end, 1000 ) +end + +local function should_notify_deprecation(deprecation) + return not deprecation.when or deprecation.when() +end + +local function handle_nested_deprecation(prefix, value, deprecation) + if type(value) ~= "table" or type(deprecation.nested) ~= "table" then return end + for key, nested_value in pairs(value) do + local nested_deprecation = deprecation.nested[key] + if nested_deprecation then + if nested_deprecation.message and should_notify_deprecation(nested_deprecation) then + notify_deprecation(("%s.%s"):format(prefix, key), nested_deprecation.message) + end + handle_nested_deprecation(("%s.%s"):format(prefix, key), nested_value, nested_deprecation) + end + end +end + +local function handle_deprecation(key, value, conf) + local deprecation = deprecations[key] + if not deprecation then return end + if deprecation.message and should_notify_deprecation(deprecation) then + notify_deprecation(key, deprecation.message) + end + handle_nested_deprecation(key, value, deprecation) if deprecation.fallback then conf[deprecation.fallback] = value end end diff --git a/lua/flutter-tools/dev_tools.lua b/lua/flutter-tools/dev_tools.lua index d94d07b9..4849b92c 100644 --- a/lua/flutter-tools/dev_tools.lua +++ b/lua/flutter-tools/dev_tools.lua @@ -207,7 +207,7 @@ end function M.stop() if devtools_pid then - local uv = vim.loop + local uv = vim.uv uv.kill(devtools_pid, uv.constants.SIGTERM) devtools_pid = nil devtools_url = nil diff --git a/lua/flutter-tools/devices.lua b/lua/flutter-tools/devices.lua index 8f11d781..4264a835 100644 --- a/lua/flutter-tools/devices.lua +++ b/lua/flutter-tools/devices.lua @@ -64,7 +64,18 @@ function M.to_selection_entries(result, device_type) if not result or #result < 1 then return {} end if not device_type then device_type = DEVICE end local devices = get_devices(result, device_type) - if #devices == 0 then vim.tbl_map(function(item) return { text = item } end, result) end + if #devices == 0 then + return vim.tbl_map( + function(item) + return { + text = item, + type = ui.entry_type.INFO, + data = nil, + } + end, + result + ) + end return vim.tbl_map(function(device) local has_platform = device.platform and device.platform ~= "" return { diff --git a/lua/flutter-tools/executable.lua b/lua/flutter-tools/executable.lua index 64dec39c..48f3de4f 100644 --- a/lua/flutter-tools/executable.lua +++ b/lua/flutter-tools/executable.lua @@ -30,7 +30,7 @@ local Job = require("plenary.job") local fn = vim.fn local fs = vim.fs -local luv = vim.loop +local uv = vim.uv local M = {} @@ -124,13 +124,13 @@ end local function flutter_bin_from_fvm() local fvm_root = - fs.dirname(fs.find(".fvm", { path = luv.cwd(), upward = true, type = "directory" })[1]) + fs.dirname(fs.find(".fvm", { path = uv.cwd(), upward = true, type = "directory" })[1]) local binary_name = path.is_windows and "flutter.bat" or "flutter" local flutter_bin_symlink = path.join(fvm_root, ".fvm", "flutter_sdk", "bin", binary_name) flutter_bin_symlink = fn.exepath(flutter_bin_symlink) - local flutter_bin = luv.fs_realpath(flutter_bin_symlink) + local flutter_bin = uv.fs_realpath(flutter_bin_symlink) if path.exists(flutter_bin_symlink) and path.exists(flutter_bin) then return flutter_bin end end diff --git a/lua/flutter-tools/lsp/color/utils.lua b/lua/flutter-tools/lsp/color/utils.lua index 333eacc3..2d15b992 100644 --- a/lua/flutter-tools/lsp/color/utils.lua +++ b/lua/flutter-tools/lsp/color/utils.lua @@ -32,32 +32,15 @@ end ---@return RGB rgb_table --- FIXME: this currently does not support transparent backgrounds. Need a replacement for bg_rgb function M.rgba_to_rgb(rgba, bg_rgb) - if vim.fn.has("nvim-0.11") == 1 then - vim.validate("rgba", rgba, "table", true) - vim.validate("bg_rgb", bg_rgb, "table", false) - vim.validate("r", rgba.r, "number", true) - vim.validate("g", rgba.g, "number", true) - vim.validate("b", rgba.b, "number", true) - vim.validate("a", rgba.a, "number", true) - vim.validate("bg_r", bg_rgb.r, "number", true) - vim.validate("bg_g", bg_rgb.g, "number", true) - vim.validate("bg_b", bg_rgb.b, "number", true) - else - vim.validate({ - rgba = { rgba, "table", true }, - bg_rgb = { bg_rgb, "table", false }, - r = { rgba.r, "number", true }, - g = { rgba.g, "number", true }, - b = { rgba.b, "number", true }, - a = { rgba.a, "number", true }, - }) - - vim.validate({ - bg_r = { bg_rgb.r, "number", true }, - bg_g = { bg_rgb.g, "number", true }, - bg_b = { bg_rgb.b, "number", true }, - }) - end + vim.validate("rgba", rgba, "table", true) + vim.validate("bg_rgb", bg_rgb, "table", false) + vim.validate("r", rgba.r, "number", true) + vim.validate("g", rgba.g, "number", true) + vim.validate("b", rgba.b, "number", true) + vim.validate("a", rgba.a, "number", true) + vim.validate("bg_r", bg_rgb.r, "number", true) + vim.validate("bg_g", bg_rgb.g, "number", true) + vim.validate("bg_b", bg_rgb.b, "number", true) local r = rgba.r * rgba.a + bg_rgb.r * (1 - rgba.a) local g = rgba.g * rgba.a + bg_rgb.g * (1 - rgba.a) @@ -70,19 +53,10 @@ end ---@param rgb RGB with keys 'r', 'g', 'b' in [0,255] ---@return string 6 digit hex representing the rgb params local function rgb_to_hex(rgb) - if vim.fn.has("nvim-0.11") == 1 then - vim.validate("rgb", rgb, "table", false) - vim.validate("r", rgb.r, "number", false) - vim.validate("g", rgb.g, "number", false) - vim.validate("b", rgb.b, "number", false) - else - vim.validate({ - rgb = { rgb, "table", false }, - r = { rgb.r, "number", false }, - g = { rgb.g, "number", false }, - b = { rgb.b, "number", false }, - }) - end + vim.validate("rgb", rgb, "table", false) + vim.validate("r", rgb.r, "number", false) + vim.validate("g", rgb.g, "number", false) + vim.validate("b", rgb.b, "number", false) return tohex(bor(lshift(rgb.r, 16), lshift(rgb.g, 8), rgb.b), 6) end @@ -98,11 +72,7 @@ function M.rgba_to_hex(rgba, bg_rgb) return rgb_to_hex(M.rgba_to_rgb(rgba, bg_rg ---@param rgb_24bit number 24-bit RGB value ---@return RGB function M.decode_24bit_rgb(rgb_24bit) - if vim.fn.has("nvim-0.11") == 1 then - vim.validate("rgb_24bit", rgb_24bit, "number", true) - else - vim.validate({ rgb_24bit = { rgb_24bit, "number", true } }) - end + vim.validate("rgb_24bit", rgb_24bit, "number", true) local r = band(rshift(rgb_24bit, 16), 255) local g = band(rshift(rgb_24bit, 8), 255) local b = band(rgb_24bit, 255) @@ -219,15 +189,8 @@ end ---@param color_infos table of `ColorInformation` objects to highlight. -- See https://microsoft.github.io/language-server-protocol/specification#textDocument_documentColor function M.buf_color(client_id, bufnr, color_infos, _) - if vim.fn.has("nvim-0.11") == 1 then - vim.validate("bufnr", bufnr, "number", true) - vim.validate("color_infos", color_infos, "table", true) - else - vim.validate({ - bufnr = { bufnr, "number", true }, - color_infos = { color_infos, "table", true }, - }) - end + vim.validate("bufnr", bufnr, "number", true) + vim.validate("color_infos", color_infos, "table", true) if not color_infos or not bufnr then return end local c = config.lsp.color @@ -250,15 +213,8 @@ end ---@param client_id number client id ---@param bufnr number buffer id function M.buf_clear_color(client_id, bufnr) - if vim.fn.has("nvim-0.11") == 1 then - vim.validate("client_id", client_id, "number", true) - vim.validate("bufnr", bufnr, "number", true) - else - vim.validate({ - client_id = { client_id, "number", true }, - bufnr = { bufnr, "number", true }, - }) - end + vim.validate("client_id", client_id, "number", true) + vim.validate("bufnr", bufnr, "number", true) if api.nvim_buf_is_valid(bufnr) then api.nvim_buf_clear_namespace(bufnr, CLIENT_NS, 0, -1) end end diff --git a/lua/flutter-tools/lsp/utils.lua b/lua/flutter-tools/lsp/utils.lua index 2e421dac..afde68cd 100644 --- a/lua/flutter-tools/lsp/utils.lua +++ b/lua/flutter-tools/lsp/utils.lua @@ -4,8 +4,7 @@ local lsp = vim.lsp M.SERVER_NAME = "dartls" --- TODO: Remove after compatibility with Neovim=0.9 is dropped -local get_clients = vim.fn.has("nvim-0.10") == 1 and lsp.get_clients or lsp.get_active_clients +local get_clients = lsp.get_clients ---@param bufnr number? ---@return vim.lsp.Client? diff --git a/lua/flutter-tools/resolve_url.lua b/lua/flutter-tools/resolve_url.lua index 76e523b1..0d0c80c4 100644 --- a/lua/flutter-tools/resolve_url.lua +++ b/lua/flutter-tools/resolve_url.lua @@ -2,9 +2,11 @@ -- https://github.com/dart-lang/dart-vim-plugin/blob/4bdc04e2540edf90fda2812434c11d19dc04bc8f/autoload/dart.vim#L94 local M = {} +local uv = vim.uv + local function resolve(path) - -- Use vim.loop.fs_realpath to resolve symbolic links and get the absolute path - local real_path = vim.loop.fs_realpath(path) + -- Use vim.uv.fs_realpath to resolve symbolic links and get the absolute path + local real_path = uv.fs_realpath(path) -- If the path cannot be resolved, return the original return real_path or path end diff --git a/lua/flutter-tools/runners/debugger_runner.lua b/lua/flutter-tools/runners/debugger_runner.lua index 3dad6d90..855447a7 100644 --- a/lua/flutter-tools/runners/debugger_runner.lua +++ b/lua/flutter-tools/runners/debugger_runner.lua @@ -146,7 +146,7 @@ local function handle_inspect_event(isolate_id) if location and location.file and location.line then local file = location.file:gsub("^file://", "") - if vim.loop.os_uname().sysname == "Windows_NT" then + if vim.uv.os_uname().sysname == "Windows_NT" then -- On Windows, the file URI may start with an extra slash file = file:gsub("^/", "") end diff --git a/lua/flutter-tools/ui.lua b/lua/flutter-tools/ui.lua index 8a3c28f2..8067be1d 100644 --- a/lua/flutter-tools/ui.lua +++ b/lua/flutter-tools/ui.lua @@ -5,6 +5,7 @@ local fmt = string.format local entry_type = { CODE_ACTION = 1, DEVICE = 2, + INFO = 3, } ---@generic T @@ -109,6 +110,12 @@ local function get_telescope_picker_config(items, title, on_select) hint = data.platform, command = function() on_select(data) end, } + elseif item.type == entry_type.INFO then + return { + id = item.text, + label = item.text, + command = function() end, + } end end, filtered), { title = title } @@ -129,7 +136,7 @@ function M.select(opts) -- custom key for dressing.nvim telescope = get_telescope_picker_config(lines, title, on_select), }, function(item) - if not item then return end + if not item or item.data == nil then return end on_select(item.data) end) end diff --git a/lua/flutter-tools/utils/config_utils.lua b/lua/flutter-tools/utils/config_utils.lua index 551b62cf..401fcf10 100644 --- a/lua/flutter-tools/utils/config_utils.lua +++ b/lua/flutter-tools/utils/config_utils.lua @@ -11,7 +11,7 @@ local lsp = lazy.require("flutter-tools.lsp") ---@module "flutter-tools.utils" function M.get_cwd(project_conf) if project_conf and project_conf.cwd then local resolved_path = path.get_absolute_path(project_conf.cwd) - if not vim.loop.fs_stat(resolved_path) then + if not vim.uv.fs_stat(resolved_path) then return ui.notify("Provided cwd does not exist: " .. resolved_path, ui.ERROR) end return resolved_path diff --git a/lua/flutter-tools/utils/init.lua b/lua/flutter-tools/utils/init.lua index 7dc1902f..29c69d54 100644 --- a/lua/flutter-tools/utils/init.lua +++ b/lua/flutter-tools/utils/init.lua @@ -146,9 +146,7 @@ function M.emit_event(event, opts) api.nvim_exec_autocmds("User", { pattern = event, data = data }) end --- TODO: Remove after compatibility with Neovim=0.9 is dropped -M.islist = vim.fn.has("nvim-0.10") == 1 and vim.islist or vim.tbl_islist -local flatten = function(t) return vim.iter(t):flatten():totable() end -M.flatten = vim.fn.has("nvim-0.11") == 1 and flatten or vim.tbl_flatten +M.islist = vim.islist +M.flatten = function(t) return vim.iter(t):flatten():totable() end return M diff --git a/lua/flutter-tools/utils/path.lua b/lua/flutter-tools/utils/path.lua index 94480504..1f5d226e 100644 --- a/lua/flutter-tools/utils/path.lua +++ b/lua/flutter-tools/utils/path.lua @@ -2,12 +2,12 @@ local lazy = require("flutter-tools.lazy") local utils = lazy.require("flutter-tools.utils") ---@module "flutter-tools.utils" -local luv = vim.loop +local uv = vim.uv local api = vim.api local M = {} function M.exists(filename) - local stat = luv.fs_stat(filename) + local stat = uv.fs_stat(filename) return stat and stat.type or false end @@ -15,7 +15,7 @@ function M.is_dir(filename) return M.exists(filename) == "directory" end function M.is_file(filename) return M.exists(filename) == "file" end -local uname = luv.os_uname() +local uname = uv.os_uname() M.is_mac = uname.sysname == "Darwin" M.is_linux = uname.sysname == "Linux" ---@type boolean @@ -60,7 +60,7 @@ end -- Traverse the path calling cb along the way. function M.traverse_parents(path, cb) - path = luv.fs_realpath(path) + path = uv.fs_realpath(path) local dir = path -- Just in case our algo is buggy, don't infinite loop. for _ = 1, 100 do @@ -74,7 +74,7 @@ end -- Iterate the path until we find the rootdir. function M.iterate_parents(path) - path = luv.fs_realpath(path) + path = uv.fs_realpath(path) local function it(_, v) if not v then return end if is_fs_root(v) then return end @@ -97,11 +97,7 @@ function M.is_descendant(root, path) end function M.search_ancestors(startpath, func) - if vim.fn.has("nvim-0.11") == 1 then - vim.validate("func", func, "function") - else - vim.validate({ func = { func, "function" } }) - end + vim.validate("func", func, "function") if func(startpath) then return startpath end for path in M.iterate_parents(startpath) do if func(path) then return path end diff --git a/lua/flutter-tools/vm_service.lua b/lua/flutter-tools/vm_service.lua index b1af4d5d..bd3850bc 100644 --- a/lua/flutter-tools/vm_service.lua +++ b/lua/flutter-tools/vm_service.lua @@ -2,7 +2,7 @@ --- WebSocket framing per RFC 6455: --- Client frames must be masked with a 4-byte key --- Frame format: [FIN/opcode][mask/length][mask-key][payload] -local uv = vim.uv or vim.loop +local uv = vim.uv local M = {} diff --git a/tests/config_spec.lua b/tests/config_spec.lua new file mode 100644 index 00000000..ad059891 --- /dev/null +++ b/tests/config_spec.lua @@ -0,0 +1,80 @@ +describe("config", function() + local config + local ui + local notifications + local original_has + + before_each(function() + notifications = {} + original_has = vim.fn.has + + package.loaded["flutter-tools.config"] = nil + package.loaded["flutter-tools.ui"] = nil + + ui = require("flutter-tools.ui") + ui.notify = function(msg, level) table.insert(notifications, { msg = msg, level = level }) end + + config = require("flutter-tools.config") + end) + + after_each(function() + vim.fn.has = original_has + package.loaded["flutter-tools.config"] = nil + package.loaded["flutter-tools.ui"] = nil + end) + + it("warns when lsp.color is configured on Neovim 0.12+", function() + vim.fn.has = function(feature) + if feature == "nvim-0.12" then return 1 end + return original_has(feature) + end + + config.set({ + lsp = { + color = { + enabled = true, + }, + }, + }) + + vim.wait(1200) + + assert.equal(1, #notifications) + assert.equal( + "lsp.color is deprecated: plugin-managed document colors are deprecated and will be removed when flutter-tools.nvim requires Neovim 0.12+. On Neovim 0.12+, use vim.lsp.document_color.enable() instead", + notifications[1].msg + ) + assert.equal(ui.WARN, notifications[1].level) + end) + + it("does not warn when lsp.color is configured before Neovim 0.12", function() + vim.fn.has = function(feature) + if feature == "nvim-0.12" then return 0 end + return original_has(feature) + end + + config.set({ + lsp = { + color = { + enabled = true, + }, + }, + }) + + vim.wait(1200) + + assert.same({}, notifications) + end) + + it("does not warn when lsp.color is omitted", function() + config.set({ + lsp = { + debug = config.debug_levels.DEBUG, + }, + }) + + vim.wait(1200) + + assert.same({}, notifications) + end) +end) diff --git a/tests/devices_spec.lua b/tests/devices_spec.lua index ec8819cb..1c266e13 100644 --- a/tests/devices_spec.lua +++ b/tests/devices_spec.lua @@ -1,7 +1,8 @@ ---@diagnostic disable: need-check-nil describe("Devices - ", function() describe("parsing tests - ", function() - local parse = require("flutter-tools.devices").parse + local devices = require("flutter-tools.devices") + local parse = devices.parse it("should correctly parse flutter emulators output", function() local output = parse("apple_ios_simulator • iOS Simulator • Apple • ios", 1) assert.equal(output.id, "apple_ios_simulator") @@ -27,5 +28,32 @@ INFO | Storing crashdata in: /tmp/android-ts/emu-crash-34.2.14.db, detection ) assert.is_nil(output) end) + + it("should build selection entries for parsed devices", function() + local entries = devices.to_selection_entries({ "linux • Linux • linux-x64 • linux" }) + + assert.equal(1, #entries) + assert.equal(" linux • linux-x64 ", entries[1].text) + assert.equal("Linux", entries[1].data.id) + end) + + it("should fall back to raw output when no devices are parsed", function() + local result = { + "No supported devices connected.", + "Run 'flutter emulators' to list and start any available device emulators.", + } + local entries = devices.to_selection_entries(result) + + assert.equal(2, #entries) + assert.equal(result[1], entries[1].text) + assert.is_nil(entries[1].data) + assert.equal(result[2], entries[2].text) + assert.is_nil(entries[2].data) + end) + + it("should return an empty list when there is no output", function() + assert.same({}, devices.to_selection_entries({})) + assert.same({}, devices.to_selection_entries(nil)) + end) end) end) diff --git a/tests/minimal_init.lua b/tests/minimal_init.lua index 885e5f34..fd7fffd0 100644 --- a/tests/minimal_init.lua +++ b/tests/minimal_init.lua @@ -1,5 +1,8 @@ local M = {} +vim.g.loaded_netrw = 1 +vim.g.loaded_netrwPlugin = 1 + function M.root(root) local f = debug.getinfo(1, "S").source:sub(2) return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (root or "") @@ -9,7 +12,7 @@ end function M.load(plugin) local name = plugin:match(".*/(.*)") local package_root = M.root(".tests/site/pack/deps/start/") - if not vim.loop.fs_stat(package_root .. name) then + if not vim.uv.fs_stat(package_root .. name) then print("Installing " .. plugin) vim.fn.mkdir(package_root, "p") vim.fn.system({ diff --git a/tests/path_spec.lua b/tests/path_spec.lua index d636ea39..731dfa02 100644 --- a/tests/path_spec.lua +++ b/tests/path_spec.lua @@ -11,7 +11,7 @@ describe("path.find_root", function() -- Use realpath to normalize (handles /var -> /private/var symlink on macOS) local temp_base = vim.fn.tempname() vim.fn.mkdir(temp_base, "p") - test_dir = vim.loop.fs_realpath(temp_base) + test_dir = vim.uv.fs_realpath(temp_base) workspace_root = test_dir .. "/workspace" package_a = workspace_root .. "/packages/package_a" package_b = workspace_root .. "/packages/package_b"