Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions lua/flutter-tools/banner.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
local lazy = require("flutter-tools.lazy")
local executable = lazy.require("flutter-tools.executable") ---@module "flutter-tools.executable"
local Job = require("plenary.job") ---@module "plenary.job"

---@class flutter.DetectedBanners
---
--- True, if the banner that matches `PATTERNS.FLUTTER_NEW_VERSION` has been
--- detected.
---
--- This banner will be detected if the file
--- `$FLUTTER_SDK/bin/cache/flutter_version_check.stamp` does not exist, or
--- reached a certain age. (Assuming `$FLUTTER_SDK` is the path to the directory
--- that contains your Flutter SDK).
---
--- See the [version.dart from the Flutter SDK](https://github.com/flutter/flutter/blob/3.35.7/packages/flutter_tools/lib/src/version.dart#L1303).
---@field has_flutter_new_version boolean
---
--- True, if the banner that matches `PATTERNS.FLUTTER_WELCOME` has been
--- detected.
---
--- This banner will be detected if the file `$HOME/.config/flutter/tool_state`
--- does not exist, or you changed your Flutter SDK to one with a different
--- welcome banner.
---
--- See the [first_run.dart from the Flutter SDK](https://github.com/flutter/flutter/blob/3.35.7/packages/flutter_tools/lib/src/reporting/first_run.dart#L13).
---@field has_flutter_welcome boolean

---@private
---@alias flutter.internal.OnBannersClearedListener fun(detect_banners: flutter.DetectedBanners):nil

---@type flutter.DetectedBanners?
local cached_banners = nil

---@type flutter.internal.OnBannersClearedListener[]
local on_cleared_listeners = {}

local has_started_cleansing = false

local M = {
PATTERNS = {
FLUTTER_NEW_VERSION = "A new version of Flutter is available!",
FLUTTER_WELCOME = "Welcome to Flutter!",
},
}

---@param lines string[]
---@return flutter.DetectedBanners
local function detect_banners(lines)
---@type flutter.DetectedBanners
local banners = {
has_flutter_new_version = false,
has_flutter_welcome = false,
}

for _, line in ipairs(lines) do
if nil ~= line:match(M.PATTERNS.FLUTTER_NEW_VERSION) then
banners.has_flutter_new_version = true
end

if nil ~= line:match(M.PATTERNS.FLUTTER_WELCOME) then banners.has_flutter_welcome = true end
end

return banners
end

--- Calls every listener from `on_cleared_listeners`, once `do_clear_banners` is
--- done. Internally caches all listeners and then resets `on_cleared_listeners`
--- to an empty table.
---
---@param detected_banners flutter.DetectedBanners
local function on_cleared_banners(detected_banners)
local listeners = vim.deepcopy(on_cleared_listeners)
on_cleared_listeners = {}
vim.schedule(function()
for _, cb in ipairs(listeners) do
cb(detected_banners)
end
end)
end

local function do_clear_banners(is_flutter_project)
assert(nil == cached_banners)
assert(not has_started_cleansing)

has_started_cleansing = true

executable.get(function(paths)
if is_flutter_project then
Job:new({
command = paths.flutter_bin,
args = { "--version" },
enable_recording = true,
on_exit = function(self, code, _)
-- Exit code should always be 0.
assert(0 == code)

-- 'flutter --version' writes everything to STDOUT including the
-- "Welcome" and "New Flutter Version" banner.
---@type string[]
local lines = self:result()

local banners = detect_banners(lines)
cached_banners = banners

on_cleared_banners(cached_banners)
end,
}):start()
else
-- Only flutter CLI shows startup banners that interfer with the
-- Debug-/Jobrunner.
--
-- dart CLI does currently not show anything, maybe there will
-- be a banner in the future.
cached_banners = {
has_flutter_welcome = false,
has_flutter_new_version = false,
}

on_cleared_banners(cached_banners)
end
end)
end

--- Clear and detect any startup banners from the Flutter or Dart CLI tool.
--- `on_cleared` is called, after all banners have been cleared.
---
---@param is_flutter_project boolean
---@param on_cleared fun(detected_banners: flutter.DetectedBanners)
function M.clear_startup_banners(is_flutter_project, on_cleared)
if nil ~= cached_banners then
vim.schedule(function() on_cleared(cached_banners) end)
return
end

table.insert(on_cleared_listeners, on_cleared)

if not has_started_cleansing then do_clear_banners(is_flutter_project) end
end

--- Reset the internally cached banners.
function M.reset_cache()
cached_banners = nil
on_cleared_listeners = {}
has_started_cleansing = false
end

return M
44 changes: 30 additions & 14 deletions lua/flutter-tools/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ local job_runner = lazy.require("flutter-tools.runners.job_runner") ---@module "
local debugger_runner = lazy.require("flutter-tools.runners.debugger_runner") ---@module "flutter-tools.runners.debugger_runner"
local path = lazy.require("flutter-tools.utils.path") ---@module "flutter-tools.utils.path"
local dev_log = lazy.require("flutter-tools.log") ---@module "flutter-tools.log"
local banner = lazy.require("flutter-tools.banner") ---@module "flutter-tools.banner"
local parser = lazy.require("flutter-tools.utils.yaml_parser")
local config_utils = lazy.require("flutter-tools.utils.config_utils") ---@module "flutter-tools.utils.config_utils"

Expand All @@ -34,6 +35,8 @@ local runner = nil

local fvm_name = path.is_windows and "fvm.bat" or "fvm"

local has_notified_new_flutter_version = false

local function use_debugger_runner(force_debug)
if force_debug or config.debugger.enabled then
local dap_ok, _ = pcall(require, "dap")
Expand Down Expand Up @@ -249,10 +252,14 @@ local function run(opts, project_conf, launch_config)
project_conf.pre_run_callback(callback_args)
end
end

local cwd = config_utils.get_cwd(project_conf)
-- To determinate if the project is a flutter project we need to check if the pubspec.yaml
-- file has a flutter dependency in it. We need to get cwd first to pick correct pubspec.yaml file.

-- To determinate if the project is a flutter project we need to check if
-- the pubspec.yaml file has a flutter dependency in it. We need to get
-- cwd first to pick correct pubspec.yaml file.
local is_flutter_project = has_flutter_dependency_in_pubspec(cwd)

local default_run_args = config.default_run_args
local run_args
if is_flutter_project then
Expand All @@ -262,25 +269,34 @@ local function run(opts, project_conf, launch_config)
ui.notify("Starting dart project...")
if default_run_args then run_args = default_run_args.dart end
end

if run_args then
if type(run_args) == "string" then
vim.list_extend(args, vim.split(run_args, " "))
elseif type(run_args) == "table" then
vim.list_extend(args, run_args)
end
end
runner = use_debugger_runner(opts.force_debug) and debugger_runner or job_runner
runner:run(
opts,
paths,
args,
cwd,
on_run_data,
on_run_exit,
is_flutter_project,
project_conf,
launch_config
)

banner.clear_startup_banners(is_flutter_project, function(detected_banners)
if detected_banners.has_flutter_new_version and not has_notified_new_flutter_version then
has_notified_new_flutter_version = true
ui.notify(banner.PATTERNS.FLUTTER_NEW_VERSION, ui.INFO)
end

runner = use_debugger_runner(opts.force_debug) and debugger_runner or job_runner
runner:run(
opts,
paths,
args,
cwd,
on_run_data,
on_run_exit,
is_flutter_project,
project_conf,
launch_config
)
end)
end)
end

Expand Down
Loading
Loading