Skip to content

Commit

Permalink
feat(config): add project configuration (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
akinsho authored Apr 11, 2023
1 parent d4ef9ef commit f898ac2
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 37 deletions.
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,45 @@ You cannot/should not edit the files in the sdk directly so diagnostic analysis
To ignore packages installed with pub, consider adding `vim.fn.expand("$HOME/AppData/Local/Pub/Cache")` to
`analysisExcludedFolders` if you are using PowerShell.

#### Project Configuration

It is possible to configure how each project is run using neovim's `exrc` functionality (see `:help exrc`).
This allows you to create an exrc file e.g. `.nvim.lua` and put the project configurations inside it.
This is similar _conceptually_ to vscode's `launch.json` file.

```lua
-- .nvim.lua
-- If you have more than one setup configured you will be prompted when you run
-- your app to select which one you want to use
require('flutter-tools').setup_project({
{
name = 'Development', -- an arbitrary name that you provide so you can recognise this config
flavor = 'DevFlavor', -- your flavour
device = 'pixel6pro', -- the device ID, which you can get by running `flutter devices`
dart_defines = {
API_URL = 'https://dev.example.com/api',
IS_DEV = true,
}
},
{
name = 'Web',
device = 'chrome',
flavor = 'WebApp'
}
})
```

you can also specify the configuration as an object if there is only one

```lua
require('flutter-tools').setup_project({
name = 'Development',
flavor = 'DevFlavor',
device = 'pixel6pro',
dart_defines = { ... }
})
```

#### Flutter binary

In order to run flutter commands you _might_ need to pass either a _path_ or a _command_ to the plugin so it can find your
Expand All @@ -308,9 +347,10 @@ was added, you can set your `flutter_path` to `"<INSERT-HOME-DIRECTORY>/snap/flu
which is where this is usually installed by `snap`.

### Highlights

Highlight groups that are user configurable to change the appearance of certain UI elements.

* `FlutterToolsOutlineIndentGuides` - indent guides for the outline window
- `FlutterToolsOutlineIndentGuides` - indent guides for the outline window

#### Widget guides

Expand Down
3 changes: 3 additions & 0 deletions lua/flutter-tools.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ local command = function(name, callback, opts)
api.nvim_create_user_command(name, callback, opts or {})
end

---@param opts flutter.ProjectConfig
function M.setup_project(opts) config.setup_project(opts) end

local function setup_commands()
-- Commands
command("FlutterRun", function(data) commands.run_command(data.args) end, { nargs = "*" })
Expand Down
90 changes: 65 additions & 25 deletions lua/flutter-tools/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@ local dev_log = lazy.require("flutter-tools.log") ---@module "flutter-tools.log"

local M = {}

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

---@type table?
local current_device = nil

---@class FlutterRunner
---@field is_running fun(runner: FlutterRunner):boolean
---@field run fun(runner: FlutterRunner, paths:table, args:table, cwd:string, on_run_data:fun(is_err:boolean, data:string), on_run_exit:fun(data:string[], args: table))
---@field cleanup fun(funner: FlutterRunner)
---@field send fun(runner: FlutterRunner, cmd:string, quiet: boolean?)
---@class flutter.Runner
---@field is_running fun(runner: flutter.Runner):boolean
---@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))
---@field cleanup fun(funner: flutter.Runner)
---@field send fun(runner: flutter.Runner, cmd:string, quiet: boolean?)

---@type FlutterRunner?
---@type flutter.Runner?
local runner = nil

function M.use_debugger_runner()
local function use_debugger_runner()
local dap_ok, dap = pcall(require, "dap")
if not config.debugger.run_via_dap then return false end
if dap_ok then return true end
Expand Down Expand Up @@ -98,31 +100,64 @@ function M.run_command(args)
M.run({ args = args })
end

---Run the flutter application
---@param opts table
function M.run(opts)
if M.is_running() then return ui.notify("Flutter is already running!") end
opts = opts or {}
local device = opts.device
local cmd_args = opts.args
local cli_args = opts.cli_args
executable.get(function(paths)
local args = cli_args or {}
if not cli_args then
if not M.use_debugger_runner() then vim.list_extend(args, { "run" }) end
if not cmd_args and device and device.id then vim.list_extend(args, { "-d", device.id }) end

if cmd_args then vim.list_extend(args, cmd_args) end
---@param callback fun(project_config: flutter.ProjectConfig?)
local function select_project_config(callback)
local project_config = config.project --[=[@as flutter.ProjectConfig[]]=]
if #project_config <= 1 then return callback(project_config[1]) end
vim.ui.select(project_config, {
prompt = "Select a project configuration",
format_item = function(item)
if item.name then return item.name end
return vim.inspect(item)
end,
}, function(selected)
if selected then callback(selected) end
end)
end

local dev_url = dev_tools.get_url()
if dev_url then vim.list_extend(args, { "--devtools-server-address", dev_url }) end
---@param opts RunOpts
---@param conf flutter.ProjectConfig?
---@return string[]
local function get_run_args(opts, conf)
local args = {}
local cmd_args = opts.args
local device = conf and conf.device or (opts.device and opts.device.id)
local flavor = conf and conf.flavor
local dart_defines = conf and conf.dart_define
local dev_url = dev_tools.get_url()

if not use_debugger_runner() then vim.list_extend(args, { "run" }) end
if not cmd_args and device then vim.list_extend(args, { "-d", device }) end
if cmd_args then vim.list_extend(args, cmd_args) end
if flavor then vim.list_extend(args, { "--flavor", flavor }) end
if dart_defines then
for key, value in pairs(dart_defines) do
vim.list_extend(args, { "--dart-define", ("%s=%s"):format(key, value) })
end
end
if dev_url then vim.list_extend(args, { "--devtools-server-address", dev_url }) end
return args
end

---@param opts RunOpts
---@param project_conf flutter.ProjectConfig?
local function run(opts, project_conf)
opts = opts or {}
executable.get(function(paths)
local args = opts.cli_args or get_run_args(opts, project_conf)
ui.notify("Starting flutter project...")
runner = M.use_debugger_runner() and debugger_runner or job_runner
runner = use_debugger_runner() and debugger_runner or job_runner
runner:run(paths, args, lsp.get_lsp_root_dir(), on_run_data, on_run_exit)
end)
end

---Run the flutter application
---@param opts RunOpts
function M.run(opts)
if M.is_running() then return ui.notify("Flutter is already running!") end
select_project_config(function(project_conf) run(opts, project_conf) end)
end

---@param cmd string
---@param quiet boolean?
---@param on_send function|nil
Expand Down Expand Up @@ -331,4 +366,9 @@ function M.fvm_use(sdk_name)
end
end

if __TEST then
M.__run = run
M.__get_run_args = get_run_args
end

return M
24 changes: 18 additions & 6 deletions lua/flutter-tools/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@ local lazy = require("flutter-tools.lazy")
local path = lazy.require("flutter-tools.utils.path") ---@module "flutter-tools.utils.path"
local ui = lazy.require("flutter-tools.ui") ---@module "flutter-tools.ui"

---@class flutter.ProjectConfig
---@field name string?
---@field device string
---@field flavor string
---@field dart_define {[string]: string}

local M = {}

---@type flutter.ProjectConfig[]
local project_config = {}

local fn = vim.fn
local fmt = string.format

Expand Down Expand Up @@ -138,11 +147,10 @@ local function handle_deprecation(key, value, conf)
if deprecation.fallback then conf[deprecation.fallback] = value end
end

---Get the configuration or just a key of the config
---@param key string?
function M.get(key)
if key then return config[key] end
return config
---@param project flutter.ProjectConfig | flutter.ProjectConfig[]
M.setup_project = function(project)
if not vim.tbl_islist(project) then project = { project } end
project_config = project
end

function M.set(user_config)
Expand All @@ -155,6 +163,10 @@ function M.set(user_config)
return config
end

---@module "flutter-tools.config"
return setmetatable(M, {
__index = function(_, k) return M.get(k) end,
__index = function(_, k)
if k == "project" then return project_config end
return config[k]
end,
})
2 changes: 1 addition & 1 deletion lua/flutter-tools/runners/debugger_runner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ local api = vim.api

local fmt = string.format

---@type FlutterRunner
---@type flutter.Runner
local DebuggerRunner = {}

local service_extensions_isolateid = {}
Expand Down
2 changes: 1 addition & 1 deletion lua/flutter-tools/runners/job_runner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ local ui = require("flutter-tools.ui")
local dev_tools = require("flutter-tools.dev_tools")
local api = vim.api

---@type FlutterRunner
---@type flutter.Runner
local JobRunner = {}

---@type Job
Expand Down
11 changes: 8 additions & 3 deletions lua/flutter-tools/utils/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,14 @@ function M.highlight(name, opts)
api.nvim_create_autocmd("ColorScheme", { callback = hl, group = colorscheme_group })
end

function M.fold(accumulator, callback, list)
for _, v in ipairs(list) do
accumulator = callback(accumulator, v)
---@generic T, S
---@param accumulator S
---@param callback fun(accumulator: S, item: T, index: number|string): S
---@param list T[]
---@return S
function M.fold(callback, list, accumulator)
for k, v in ipairs(list) do
accumulator = callback(accumulator, v, k)
end
return accumulator
end
Expand Down
49 changes: 49 additions & 0 deletions tests/commands_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
local utils = require("flutter-tools.utils")

describe("commands", function()
local commands
before_each(function() commands = require("flutter-tools.commands") end)
after_each(function()
commands = nil
package.loaded["flutter-tools.commands"] = nil
end)
it(
"should add project config options correctly",
function()
assert.are.same(
{ "run", "--flavor", "Production" },
commands.__get_run_args({}, { flavor = "Production" })
)
end
)

it(
"should add 'dart_defines' options correctly",
function()
assert.are.same(
{ "run", "--flavor", "Production", "--dart-define", "ENV=prod" },
commands.__get_run_args({}, { flavor = "Production", dart_define = { ENV = "prod" } })
)
end
)

it("should add multiple dart_defines", function()
local args = commands.__get_run_args({}, {
flavor = "Production",
dart_define = { ENV = "prod", KEY = "VALUE" },
})
local result = utils.fold(function(acc, v)
acc[v] = acc[v] and acc[v] + 1 or 1
return acc
end, args, {})

assert.are.same(result, {
["run"] = 1,
["--flavor"] = 1,
["Production"] = 1,
["--dart-define"] = 2,
["ENV=prod"] = 1,
["KEY=VALUE"] = 1,
})
end)
end)

0 comments on commit f898ac2

Please sign in to comment.