From f8dd0f1d0c09c689eaea082de1428c43be6d7e91 Mon Sep 17 00:00:00 2001 From: Vladimir Kolchurin <18503099+kolchurinvv@users.noreply.github.com> Date: Thu, 23 Apr 2026 20:33:28 +0200 Subject: [PATCH] barebones implementation of avd emulators --- lua/flutter-tools.lua | 1 + lua/flutter-tools/devices.lua | 73 +++++++++++++++++++++++++++++--- lua/flutter-tools/executable.lua | 12 ++++++ lua/flutter-tools/menu.lua | 6 +++ 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/lua/flutter-tools.lua b/lua/flutter-tools.lua index 3f0a5c5d..78dc0134 100644 --- a/lua/flutter-tools.lua +++ b/lua/flutter-tools.lua @@ -36,6 +36,7 @@ local function setup_commands() -- Lists command("FlutterDevices", devices.list_devices) command("FlutterEmulators", devices.list_emulators) + command("FlutterEmulatorsAvds", devices.list_emulators_avds) --- Outline command("FlutterOutlineOpen", outline.open) command("FlutterOutlineToggle", outline.toggle) diff --git a/lua/flutter-tools/devices.lua b/lua/flutter-tools/devices.lua index 8f11d781..1fad06ec 100644 --- a/lua/flutter-tools/devices.lua +++ b/lua/flutter-tools/devices.lua @@ -18,10 +18,17 @@ local DEVICE = 2 ---@param result string[] ---@param type integer -local function get_devices(result, type) +---@param is_avds boolean +local function get_devices(result, type, is_avds) + local parse_avds = is_avds == true local devices = {} for _, line in pairs(result) do - local device = M.parse(line, type) + local device + if parse_avds then + device = M.parse_avds(line, type) + else + device = M.parse(line, type) + end if device then table.insert(devices, device) if type == EMULATOR and device.system and device.system == "android" then @@ -34,6 +41,24 @@ local function get_devices(result, type) return devices end +---@param line string +---@param device_type number +---@return Device? +function M.parse_avds(line, device_type) + local parts = vim.split(line, "•") + local name_index = 1 + local id_index = 1 + return { + name = vim.trim(parts[name_index]), + id = vim.trim(parts[id_index]), + platform = "Android", + system = "dunno", + -- platform = vim.trim(parts[3]), + -- system = vim.trim(parts[4]), + type = device_type, + } +end + ---@param line string ---@param device_type number ---@return Device? @@ -59,11 +84,12 @@ end --- return the parsed list and the found devices if any ---@param result string[] ---@param device_type integer? +---@param is_avds boolean ---@return SelectionEntry[] -function M.to_selection_entries(result, device_type) +function M.to_selection_entries(result, device_type, is_avds) 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) + local devices = get_devices(result, device_type, is_avds) if #devices == 0 then vim.tbl_map(function(item) return { text = item } end, result) end return vim.tbl_map(function(device) local has_platform = device.platform and device.platform ~= "" @@ -86,6 +112,17 @@ function M.close_emulator() if M.emulator_job then M.emulator_job:shutdown() end end +---@param emulator table +function M.launch_emulator_avd(emulator) + if not emulator then return end + executable.emulator(function(cmd) + args = { "@" .. emulator.id, "-gpu", "host", "-accel", "on" } + if emulator.cold_boot then table.insert(args, "-no-snapshot-load") end + M.emulator_job = Job:new({ command = cmd, args = args }) + M.emulator_job:after_success(vim.schedule_wrap(handle_launch)) + M.emulator_job:start() + end) +end ---@param emulator table function M.launch_emulator(emulator) if not emulator then return end @@ -99,17 +136,39 @@ function M.launch_emulator(emulator) end ---@param result string[] -local function show_emulators(result) - local lines = M.to_selection_entries(result, EMULATOR) +---@param is_avds boolean +local function show_emulators(result, is_avds) + local lines = M.to_selection_entries(result, EMULATOR, is_avds) if #lines > 0 then + local on_select + if is_avds then + on_select = function(emulator) M.launch_emulator_avd(emulator) end + else + on_select = function(emulator) M.launch_emulator(emulator) end + end ui.select({ title = "Flutter emulators", lines = lines, - on_select = function(emulator) M.launch_emulator(emulator) end, + on_select = on_select, }) + else + print("no emulators") end end +function M.list_emulators_avds() + executable.emulator(function(cmd) + local job = Job:new({ command = cmd, args = { "-list-avds" } }) + job:after_success(vim.schedule_wrap(function(j) show_emulators(j:result(), true) end)) + job:after_failure( + vim.schedule_wrap( + function(j) return ui.notify(utils.join(j:stderr_result()), ui.ERROR, { timeout = 5000 }) end + ) + ) + job:start() + end) +end + function M.list_emulators() executable.flutter(function(cmd) local job = Job:new({ command = cmd, args = { "emulators" } }) diff --git a/lua/flutter-tools/executable.lua b/lua/flutter-tools/executable.lua index 64dec39c..71e90024 100644 --- a/lua/flutter-tools/executable.lua +++ b/lua/flutter-tools/executable.lua @@ -21,6 +21,7 @@ local Job = require("plenary.job") --- --- True if fvm provides the Flutter SDK, otherwise nil or false. ---@field fvm boolean? +---@field emulator_bin string ---@private ---@class flutter.internal.Paths @@ -83,6 +84,7 @@ local function get_default_binaries() flutter_bin = flutter_bin, dart_bin = fn.resolve(fn.exepath("dart")), flutter_sdk = flutter_sdk_root(flutter_bin), + emulator_bin = fn.resolve(fn.exepath("emulator")), } end @@ -151,6 +153,7 @@ function M.get(callback) -- Provide default values to make the linter happy. dart_sdk = "", dart_bin = "", + emulator_bin = fn.exepath("emulator") or "", } cached_paths.dart_sdk = dart_sdk_root(cached_paths) cached_paths.dart_bin = flutter_sdk_dart_bin(cached_paths.flutter_sdk) @@ -166,6 +169,7 @@ function M.get(callback) -- Provide default values to make the linter happy. dart_sdk = "", dart_bin = "", + emulator_bin = fn.exepath("emulator") or "", } cached_paths.dart_sdk = dart_sdk_root(cached_paths) cached_paths.dart_bin = flutter_sdk_dart_bin(cached_paths.flutter_sdk) @@ -179,6 +183,7 @@ function M.get(callback) flutter_sdk = paths.flutter_sdk, dart_bin = paths.dart_bin, dart_sdk = dart_sdk_root(paths), + emulator_bin = fn.exepath("emulator") or "", } callback(paths) end) @@ -192,6 +197,7 @@ function M.get(callback) flutter_sdk = internal_paths.flutter_sdk, dart_bin = internal_paths.dart_bin, dart_sdk = dart_sdk_root(internal_paths), + emulator_bin = fn.exepath("emulator") or "", } if cached_paths.flutter_sdk then cached_paths.dart_bin = flutter_sdk_dart_bin(cached_paths.flutter_sdk) @@ -208,6 +214,12 @@ function M.flutter(callback) M.get(function(paths) callback(paths.flutter_bin) end) end +--- Fetch the path to the users androidSdk -> emulator installation. +---@param callback fun(emulator_bin: string):nil +function M.emulator(callback) + M.get(function(paths) callback(paths.emulator_bin) end) +end + --- Fetch the path to the users dart installation. ---@param callback fun(dart_bin: string):nil function M.dart(callback) diff --git a/lua/flutter-tools/menu.lua b/lua/flutter-tools/menu.lua index 943c7dfa..d7b04bf4 100644 --- a/lua/flutter-tools/menu.lua +++ b/lua/flutter-tools/menu.lua @@ -199,6 +199,12 @@ function M.commands(opts) hint = "Show the available emulator devices", command = require("flutter-tools.devices").list_emulators, }, + { + id = "flutter-tools-list-emulators-avd", + label = "List Emulator avds", + hint = "Show the available avds", + command = require("flutter-tools.devices").list_emulators_avds, + }, { id = "flutter-tools-open-outline", label = "Open Outline",