Skip to content

Commit

Permalink
feat: add FlutterAttach command
Browse files Browse the repository at this point in the history
  • Loading branch information
sidlatau committed Jan 2, 2025
1 parent a526c30 commit b2801e0
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ require("flutter-tools").setup {} -- use defaults
- `FlutterReload` - Reload the running project.
- `FlutterRestart` - Restart the current project.
- `FlutterQuit` - Ends a running session.
- `FlutterAttach` - Attach to a running app.
- `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.
Expand Down
1 change: 1 addition & 0 deletions lua/flutter-tools.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ local function setup_commands()
command("FlutterRun", function(data) commands.run_command(data.args) end, { nargs = "*" })
command("FlutterDebug", function(data) commands.run_command(data.args) end, { nargs = "*" })
command("FlutterLspRestart", lsp.restart)
command("FlutterAttach", commands.attach)
command("FlutterDetach", commands.detach)
command("FlutterReload", commands.reload)
command("FlutterRestart", commands.restart)
Expand Down
23 changes: 23 additions & 0 deletions lua/flutter-tools/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local parser = lazy.require("flutter-tools.utils.yaml_parser")
local M = {}

---@alias RunOpts {cli_args: string[]?, args: string[]?, device: Device?, force_debug: boolean?}
---@alias AttachOpts {cli_args: string[]?, args: string[]?, device: Device?}

---@type table?
local current_device = nil
Expand All @@ -25,6 +26,7 @@ local current_device = nil
---@field run fun(runner: flutter.Runner, paths:table, args:table, cwd:string, on_run_data:fun(is_err:boolean, data:string), on_run_exit:fun(data:string[], args: table, project_conf: flutter.ProjectConfig?,launch_config: dap.Configuration?), is_flutter_project: boolean, project_conf: flutter.ProjectConfig?, launch_config: dap.Configuration?)
---@field cleanup fun(funner: flutter.Runner)
---@field send fun(runner: flutter.Runner, cmd:string, quiet: boolean?)
---@field attach fun(runner: flutter.Runner, paths:table, args:table, cwd:string, on_run_data:fun(is_err:boolean, data:string), on_run_exit:fun(data:string[], args: table, project_conf: flutter.ProjectConfig?,launch_config: dap.Configuration?))

---@type flutter.Runner?
local runner = nil
Expand Down Expand Up @@ -306,6 +308,27 @@ function M.run(opts, project_conf, launch_config)
end
end

---@param opts AttachOpts
local function attach(opts)
opts = opts or {}
executable.get(function(paths)
local args = opts.cli_args or {}
if not use_debugger_runner() then vim.list_extend(args, { "attach" }) end

local cwd = get_cwd()
ui.notify("Attaching flutter project...")
runner = use_debugger_runner() and debugger_runner or job_runner
runner:attach(paths, args, cwd, on_run_data, on_run_exit)
end)
end

--- Attach to a running app
---@param opts AttachOpts
function M.attach(opts)
if M.is_running() then return ui.notify("Flutter is already running!") end
attach(opts)
end

---@param cmd string
---@param quiet boolean?
---@param on_send function|nil
Expand Down
109 changes: 86 additions & 23 deletions lua/flutter-tools/runners/debugger_runner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,35 +102,15 @@ local function register_default_configurations(paths, is_flutter_project, projec
end
end

function DebuggerRunner:run(
paths,
args,
cwd,
on_run_data,
on_run_exit,
is_flutter_project,
project_config,
last_launch_config
)
---@type dap.Configuration
local selected_launch_config = nil

local function register_dap_listeners(on_run_data, on_run_exit)
local started = false
local before_start_logs = {}
vm_service_extensions.reset()
dap.listeners.after["event_output"][plugin_identifier] = function(_, body)
if body and body.output then
for line in body.output:gmatch("[^\r\n]+") do
if not started then table.insert(before_start_logs, line) end
on_run_data(body.category == "sterr", line)
end
end
on_run_data(started, before_start_logs, body)
end

local handle_termination = function()
if next(before_start_logs) ~= nil then
on_run_exit(before_start_logs, args, project_config, selected_launch_config)
end
if next(before_start_logs) ~= nil then on_run_exit(before_start_logs) end
end

dap.listeners.before["event_exited"][plugin_identifier] = function(_, _) handle_termination() end
Expand Down Expand Up @@ -160,6 +140,35 @@ function DebuggerRunner:run(
vm_service_extensions.set_service_extensions_state(body.extension, body.value)
end
end
end

function DebuggerRunner:run(
paths,
args,
cwd,
on_run_data,
on_run_exit,
is_flutter_project,
project_config,
last_launch_config
)
vm_service_extensions.reset()
---@type dap.Configuration
local selected_launch_config = nil

register_dap_listeners(
function(started, before_start_logs, body)
if body and body.output then
for line in body.output:gmatch("[^\r\n]+") do
if not started then table.insert(before_start_logs, line) end
on_run_data(body.category == "sterr", line)
end
end
end,
function(before_start_logs)
on_run_exit(before_start_logs, args, project_config, selected_launch_config)
end
)

register_debug_adapter(paths, is_flutter_project)
local launch_configurations = {}
Expand Down Expand Up @@ -212,6 +221,60 @@ function DebuggerRunner:run(
end
end

function DebuggerRunner:attach(paths, args, cwd, on_run_data, on_run_exit)
vm_service_extensions.reset()
register_dap_listeners(function(started, before_start_logs, body)
if body and body.output then
for line in body.output:gmatch("[^\r\n]+") do
if not started then table.insert(before_start_logs, line) end
on_run_data(body.category == "sterr", line)
end
end
end, function(before_start_logs) on_run_exit(before_start_logs, args) end)

register_debug_adapter(paths, true)
local launch_configurations = {}
local launch_configuration_count = 0
register_default_configurations(paths, true)
if config.debugger.register_configurations then config.debugger.register_configurations(paths) end
local all_configurations = require("dap").configurations.dart
if not all_configurations then
ui.notify("No launch configuration for DAP found", ui.ERROR)
return
end
for _, c in ipairs(all_configurations) do
if c.request == "attach" then
table.insert(launch_configurations, c)
launch_configuration_count = launch_configuration_count + 1
end
end

if launch_configuration_count == 0 then
ui.notify("No launch configuration for DAP found", ui.ERROR)
return
else
require("dap.ui").pick_if_many(
launch_configurations,
"Select launch configuration",
function(item)
return fmt("%s : %s | %s", item.name, item.program or item.cwd, vim.inspect(item.args))
end,
function(launch_config)
if not launch_config then return end
launch_config = vim.deepcopy(launch_config)
if not launch_config.cwd then launch_config.cwd = cwd end
launch_config.args = vim.list_extend(launch_config.args or {}, args or {})
launch_config.dartSdkPath = paths.dart_sdk
launch_config.flutterSdkPath = paths.flutter_sdk
if config.debugger.evaluate_to_string_in_debug_views then
launch_config.evaluateToStringInDebugViews = true
end
dap.run(launch_config)
end
)
end
end

function DebuggerRunner:send(cmd, quiet)
if cmd == "open_dev_tools" then
dev_tools.open_dev_tools()
Expand Down
19 changes: 19 additions & 0 deletions lua/flutter-tools/runners/job_runner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,23 @@ end

function JobRunner:cleanup() run_job = nil end

function JobRunner:attach(paths, args, cwd, on_run_data, on_run_exit)
local command = paths.flutter_bin
local command_args = args

run_job = Job:new({
command = command,
args = command_args,
cwd = cwd,
on_start = function() utils.emit_event(utils.events.APP_STARTED) end,
on_stdout = vim.schedule_wrap(function(_, data, _)
on_run_data(false, data)
dev_tools.handle_log(data)
end),
on_stderr = vim.schedule_wrap(function(_, data, _) on_run_data(true, data) end),
on_exit = vim.schedule_wrap(function(j, _) on_run_exit(j:result(), args) end),
})
run_job:start()
end

return JobRunner

0 comments on commit b2801e0

Please sign in to comment.