diff options
| author | William Boman <william@redwill.se> | 2022-07-08 18:34:38 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-08 18:34:38 +0200 |
| commit | 976aa4fbee8a070f362cab6f6ec84e9251a90cf9 (patch) | |
| tree | 5e8d9c9c59444a25c7801b8f39763c4ba6e1f76d /lua/mason-core/spawn.lua | |
| parent | feat: add gotests, gomodifytags, impl (#28) (diff) | |
| download | mason-976aa4fbee8a070f362cab6f6ec84e9251a90cf9.tar mason-976aa4fbee8a070f362cab6f6ec84e9251a90cf9.tar.gz mason-976aa4fbee8a070f362cab6f6ec84e9251a90cf9.tar.bz2 mason-976aa4fbee8a070f362cab6f6ec84e9251a90cf9.tar.lz mason-976aa4fbee8a070f362cab6f6ec84e9251a90cf9.tar.xz mason-976aa4fbee8a070f362cab6f6ec84e9251a90cf9.tar.zst mason-976aa4fbee8a070f362cab6f6ec84e9251a90cf9.zip | |
refactor: add mason-schemas and mason-core modules (#29)
* refactor: add mason-schemas and move generated filetype map to mason-lspconfig
* refactor: add mason-core module
Diffstat (limited to 'lua/mason-core/spawn.lua')
| -rw-r--r-- | lua/mason-core/spawn.lua | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/lua/mason-core/spawn.lua b/lua/mason-core/spawn.lua new file mode 100644 index 00000000..6b783492 --- /dev/null +++ b/lua/mason-core/spawn.lua @@ -0,0 +1,112 @@ +local a = require "mason-core.async" +local _ = require "mason-core.functional" +local Result = require "mason-core.result" +local process = require "mason-core.process" +local platform = require "mason-core.platform" +local log = require "mason-core.log" + +---@alias JobSpawn table<string, async fun(opts: JobSpawnOpts): Result> +---@type JobSpawn +local spawn = { + _aliases = { + npm = platform.is_win and "npm.cmd" or "npm", + gem = platform.is_win and "gem.cmd" or "gem", + composer = platform.is_win and "composer.bat" or "composer", + gradlew = platform.is_win and "gradlew.bat" or "gradlew", + -- for hererocks installations + luarocks = (platform.is_win and vim.fn.executable "luarocks.bat" == 1) and "luarocks.bat" or "luarocks", + rebar3 = platform.is_win and "rebar3.cmd" or "rebar3", + }, + _flatten_cmd_args = _.compose(_.filter(_.complement(_.equals(vim.NIL))), _.flatten), +} + +local function Failure(err, cmd) + return Result.failure(setmetatable(err, { + __tostring = function() + return ("spawn: %s failed with exit code %s and signal %s. %s"):format( + cmd, + err.exit_code or "-", + err.signal or "-", + err.stderr or "" + ) + end, + })) +end + +local is_executable = _.memoize(function(cmd) + if vim.in_fast_event() then + a.scheduler() + end + return vim.fn.executable(cmd) == 1 +end, _.identity) + +---@class SpawnArgs +---@field with_paths string[] @Optional. Paths to add to the PATH environment variable. +---@field env table<string, string> @Optional. Example { SOME_ENV = "value", SOME_OTHER_ENV = "some_value" } +---@field env_raw string[] @Optional. Example: { "SOME_ENV=value", "SOME_OTHER_ENV=some_value" } +---@field stdio_sink StdioSink @Optional. If provided, will be used to write to stdout and stderr. +---@field cwd string @Optional +---@field on_spawn fun(handle: luv_handle, stdio: luv_pipe[]) @Optional. Will be called when the process successfully spawns. +---@field check_executable boolean @Optional. Whether to check if the provided command is executable (defaults to true). + +setmetatable(spawn, { + ---@param normalized_cmd string + __index = function(self, normalized_cmd) + ---@param args SpawnArgs + return function(args) + local cmd_args = self._flatten_cmd_args(args) + local env = args.env + + if args.with_paths then + env = env or {} + env.PATH = process.extend_path(args.with_paths) + end + + ---@type JobSpawnOpts + local spawn_args = { + stdio_sink = args.stdio_sink, + cwd = args.cwd, + env = env and process.graft_env(env) or args.env_raw, + args = cmd_args, + } + + local stdio + if not spawn_args.stdio_sink then + stdio = process.in_memory_sink() + spawn_args.stdio_sink = stdio.sink + end + + local cmd = self._aliases[normalized_cmd] or normalized_cmd + + if (env and env.PATH) == nil and args.check_executable ~= false and not is_executable(cmd) then + log.fmt_debug("%s is not executable", cmd) + return Failure({ + stderr = ("%s is not executable"):format(cmd), + }, cmd) + end + + local _, exit_code, signal = a.wait(function(resolve) + local handle, stdio, pid = process.spawn(cmd, spawn_args, resolve) + if args.on_spawn and handle and stdio and pid then + args.on_spawn(handle, stdio, pid) + end + end) + + if exit_code == 0 and signal == 0 then + return Result.success { + stdout = stdio and table.concat(stdio.buffers.stdout, "") or nil, + stderr = stdio and table.concat(stdio.buffers.stderr, "") or nil, + } + else + return Failure({ + exit_code = exit_code, + signal = signal, + stdout = stdio and table.concat(stdio.buffers.stdout, "") or nil, + stderr = stdio and table.concat(stdio.buffers.stderr, "") or nil, + }, cmd) + end + end + end, +}) + +return spawn |
