Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into pyflakes
Browse files Browse the repository at this point in the history
* origin/main:
  feat(registry): add cmake-lint (williamboman#1050)
  feat(registry): add snakefmt (williamboman#1048)
  chore: autogenerate (williamboman#1065)
  feat(ui): display registries in help window (williamboman#1062)
  fix(package): emit registry event on abnormal failures (williamboman#1061)
  refactor(package): lazy-require modules (williamboman#1060)
  feat: add semver module (williamboman#1058)
  chore(ci): bump selene and stylua versions (williamboman#1059)
  refactor(powershell): remove .script and fix fast API error (williamboman#1057)
  refactor(expr): better handling of nil values (williamboman#1056)
  feat(InstallContext): add strict_mode flag (williamboman#1055)
  chore: autogenerate (williamboman#1051)
  • Loading branch information
williamboman committed Mar 5, 2023
2 parents 001f4a0 + dd9ef3f commit 73d3aa3
Show file tree
Hide file tree
Showing 28 changed files with 424 additions and 161 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/selene.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ jobs:
# token is needed because the action allegedly downloads binary from github releases
token: ${{ secrets.GITHUB_TOKEN }}
args: lua/ tests/ scripts/
version: 0.20.0
version: 0.24.0
2 changes: 1 addition & 1 deletion .github/workflows/stylua.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
# CLI arguments
args: --check .
version: 0.15.0
version: 0.16.1
7 changes: 7 additions & 0 deletions lua/mason-core/fs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ local function make_module(uv)
uv.fs_symlink(path, new_path)
end

---@param path string
---@param mode integer
function M.chmod(path, mode)
log.trace("fs: chmod", path, mode)
uv.fs_chmod(path, mode)
end

return M
end

Expand Down
49 changes: 37 additions & 12 deletions lua/mason-core/installer/context.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ local Optional = require "mason-core.optional"
local _ = require "mason-core.functional"

---@class ContextualSpawn
---@field strict_mode boolean Whether spawn failures should raise an exception rather then return a Result.
---@field cwd CwdManager
---@field handle InstallHandle
---@field [string] async fun(opts: SpawnArgs): Result
local ContextualSpawn = {}

---@param cwd CwdManager
---@param handle InstallHandle
function ContextualSpawn.new(cwd, handle)
return setmetatable({ cwd = cwd, handle = handle }, ContextualSpawn)
---@param strict_mode boolean
function ContextualSpawn.new(cwd, handle, strict_mode)
return setmetatable({ cwd = cwd, handle = handle, strict_mode = strict_mode }, ContextualSpawn)
end

function ContextualSpawn.__index(self, cmd)
---@param cmd string
function ContextualSpawn:__index(cmd)
---@param args JobSpawnOpts
return function(args)
args.cwd = args.cwd or self.cwd:get()
args.stdio_sink = args.stdio_sink or self.handle.stdio.sink
Expand All @@ -36,9 +41,12 @@ function ContextualSpawn.__index(self, cmd)
self.handle:deregister_spawn_handle(captured_handle)
end
end
-- We get_or_throw() here for convenience reasons.
-- Almost every time spawn is called via context we want the command to succeed.
return spawn[cmd](args):on_success(pop_spawn_stack):on_failure(pop_spawn_stack):get_or_throw()
local result = spawn[cmd](args):on_success(pop_spawn_stack):on_failure(pop_spawn_stack)
if self.strict_mode then
return result:get_or_throw()
else
return result
end
end
end

Expand Down Expand Up @@ -104,9 +112,22 @@ function ContextualFs:rename(old_path, new_path)
end

---@async
---@param dirpath string
function ContextualFs:mkdir(dirpath)
return fs.async.mkdir(path.concat { self.cwd:get(), dirpath })
---@param dir_path string
function ContextualFs:mkdir(dir_path)
return fs.async.mkdir(path.concat { self.cwd:get(), dir_path })
end

---@async
---@param file_path string
---@param mode integer
function ContextualFs:chmod(file_path, mode)
return fs.async.chmod(path.concat { self.cwd:get(), file_path }, mode)
end

---@async
---@param file_path string
function ContextualFs:fstat(file_path)
return fs.async.fstat(path.concat { self.cwd:get(), file_path })
end

---@class CwdManager
Expand Down Expand Up @@ -142,7 +163,7 @@ end
---@field public receipt InstallReceiptBuilder
---@field public requested_version Optional
---@field public fs ContextualFs
---@field public spawn JobSpawn
---@field public spawn ContextualSpawn
---@field public handle InstallHandle
---@field public package Package
---@field public cwd CwdManager
Expand All @@ -158,7 +179,7 @@ function InstallContext.new(handle, opts)
local cwd_manager = CwdManager.new(path.install_prefix())
return setmetatable({
cwd = cwd_manager,
spawn = ContextualSpawn.new(cwd_manager, handle),
spawn = ContextualSpawn.new(cwd_manager, handle, handle.package.spec.schema ~= "registry+v1"),
handle = handle,
package = handle.package, -- for convenience
fs = ContextualFs.new(cwd_manager),
Expand Down Expand Up @@ -246,7 +267,11 @@ end
function InstallContext:write_pyvenv_exec_wrapper(new_executable_rel_path, module)
local pip3 = require "mason-core.managers.pip3"
local module_exists, module_err = pcall(function()
self.spawn.python { "-c", ("import %s"):format(module), with_paths = { pip3.venv_path(self.cwd:get()) } }
local result =
self.spawn.python { "-c", ("import %s"):format(module), with_paths = { pip3.venv_path(self.cwd:get()) } }
if self.spawn.strict_mode then
result:get_or_throw()
end
end)
if not module_exists then
log.fmt_error("Failed to find module %q for package %q. %s", module, self.package, module_err)
Expand Down
26 changes: 10 additions & 16 deletions lua/mason-core/installer/registry/expr.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,30 @@ local function shallow_clone(tbl)
return res
end

---@param expr string
---@param ctx table<string, any>
local function eval(expr, ctx)
return setfenv(assert(loadstring("return " .. expr), ("Failed to parse expression: %q"):format(expr)), ctx)()
end

---@param str string
---@param ctx table<string, any>
function M.interpolate(str, ctx)
ctx = shallow_clone(ctx)
setmetatable(ctx, { __index = FILTERS })
return Result.pcall(function()
setmetatable(ctx, { __index = FILTERS })
return _.gsub("{{([^}]+)}}", function(expr)
local components = parse_expr(expr)

local value = assert(
setfenv(
assert(
loadstring("return " .. components.value_expr),
("Failed to parse value: %q"):format(components.value_expr)
),
ctx
)(),
("Value is nil: %q"):format(components.value_expr)
)
local value = eval(components.value_expr, ctx)

local filters = _.map(function(filter_expr)
local filter = setfenv(
assert(loadstring("return " .. filter_expr), ("Failed to parse filter: %q"):format(filter_expr)),
ctx
)()
local filter = eval(filter_expr, ctx)
assert(type(filter) == "function", ("Invalid filter expression: %q"):format(filter_expr))
return filter
end, components.filters)

return _.reduce(_.apply_to, value, filters)
return _.reduce(_.apply_to, value, filters) or ""
end, str)
end)
end
Expand Down
40 changes: 14 additions & 26 deletions lua/mason-core/managers/powershell/init.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
local a = require "mason-core.async"
local async_uv = require "mason-core.async.uv"
local spawn = require "mason-core.spawn"
local process = require "mason-core.process"
local _ = require "mason-core.functional"

local M = {}

Expand All @@ -11,40 +11,28 @@ local PWSHOPT = {
error_action_preference = [[ $ErrorActionPreference = "Stop"; ]],
}

local powershell = vim.fn.executable "pwsh" == 1 and "pwsh" or "powershell"

---@param script string
---@param opts SpawnArgs?
---@param custom_spawn JobSpawn?
function M.script(script, opts, custom_spawn)
opts = opts or {}
---@type JobSpawn
local spawner = custom_spawn or spawn
return spawner[powershell](vim.tbl_extend("keep", {
"-NoProfile",
on_spawn = a.scope(function(_, stdio)
local stdin = stdio[1]
async_uv.write(stdin, PWSHOPT.error_action_preference)
async_uv.write(stdin, PWSHOPT.progress_preference)
async_uv.write(stdin, PWSHOPT.security_protocol)
async_uv.write(stdin, script)
async_uv.write(stdin, "\n")
async_uv.shutdown(stdin)
async_uv.close(stdin)
end),
env_raw = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
}, opts))
end
local powershell = _.lazy(function()
if vim.in_fast_event() then
a.scheduler()
end
if vim.fn.executable "pwsh" == 1 then
return "pwsh"
else
return "powershell"
end
end)

---@async
---@param command string
---@param opts SpawnArgs?
---@param custom_spawn JobSpawn?
function M.command(command, opts, custom_spawn)
opts = opts or {}
---@type JobSpawn
local spawner = custom_spawn or spawn
return spawner[powershell](vim.tbl_extend("keep", {
return spawner[powershell()](vim.tbl_extend("keep", {
"-NoProfile",
"-NonInteractive",
"-Command",
PWSHOPT.error_action_preference .. PWSHOPT.progress_preference .. PWSHOPT.security_protocol .. command,
env_raw = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
Expand Down
29 changes: 17 additions & 12 deletions lua/mason-core/package/init.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
local registry = require "mason-registry"
local a = require "mason-core.async"
local _ = require "mason-core.functional"
local installer = require "mason-core.installer"
local InstallationHandle = require "mason-core.installer.handle"
local Optional = require "mason-core.optional"
local log = require "mason-core.log"
local EventEmitter = require "mason-core.EventEmitter"
local receipt = require "mason-core.receipt"
local fs = require "mason-core.fs"
local path = require "mason-core.path"
local linker = require "mason-core.installer.linker"

local version_checks = require "mason-core.package.version-check"

---@class Package : EventEmitter
---@field name string
Expand Down Expand Up @@ -78,6 +72,7 @@ function Package:new_handle()
assert(handle:is_closed(), "Cannot create new handle because existing handle is not closed.")
end)
log.fmt_trace("Creating new handle for %s", self)
local InstallationHandle = require "mason-core.installer.handle"
local handle = InstallationHandle.new(self)
self.handle = handle

Expand Down Expand Up @@ -107,19 +102,25 @@ function Package:install(opts)
end)
:or_else_get(function()
local handle = self:new_handle()
-- This function is not expected to be run in async scope, so we create
-- a new scope here and handle the result callback-style.
a.run(
function(...)
-- we wrap installer.execute for testing purposes (to allow spy objects)
return installer.execute(...)
end,
require("mason-core.installer").execute,
---@param success boolean
---@param result Result
function(success, result)
if not success then
-- Installer failed abnormally (i.e. unexpected exception in the installer code itself).
log.error("Unexpected error", result)
handle.stdio.sink.stderr(tostring(result))
handle.stdio.sink.stderr "\nInstallation failed abnormally. Please report this error."
self:emit("install:failed", handle)
registry:emit("package:install:failed", self, handle)

-- We terminate _after_ emitting failure events because [termination -> failed] have different
-- meaning than [failed -> terminate] ([termination -> failed] is interpreted as a triggered
-- termination).
if not handle:is_closed() and not handle.is_terminated then
handle:terminate()
end
return
end
result
Expand Down Expand Up @@ -153,6 +154,7 @@ function Package:unlink()
local install_path = self:get_install_path()
-- 1. Unlink
self:get_receipt():if_present(function(receipt)
local linker = require "mason-core.installer.linker"
linker.unlink(self, receipt):get_or_throw()
end)

Expand Down Expand Up @@ -180,6 +182,7 @@ end
function Package:get_receipt()
local receipt_path = path.concat { self:get_install_path(), "mason-receipt.json" }
if fs.sync.file_exists(receipt_path) then
local receipt = require "mason-core.receipt"
return Optional.of(receipt.InstallReceipt.from_json(vim.json.decode(fs.sync.read_file(receipt_path))))
end
return Optional.empty()
Expand All @@ -189,6 +192,7 @@ end
function Package:get_installed_version(callback)
a.run(function()
local receipt = self:get_receipt():or_else_throw "Unable to get receipt."
local version_checks = require "mason-core.package.version-check"
return version_checks.get_installed_version(receipt, self:get_install_path()):get_or_throw()
end, callback)
end
Expand All @@ -197,6 +201,7 @@ end
function Package:check_new_version(callback)
a.run(function()
local receipt = self:get_receipt():or_else_throw "Unable to get receipt."
local version_checks = require "mason-core.package.version-check"
return version_checks.get_new_version(receipt, self:get_install_path()):get_or_throw()
end, callback)
end
Expand Down
12 changes: 12 additions & 0 deletions lua/mason-core/semver.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
local semver = require "mason-vendor.semver"
local Result = require "mason-core.result"

local M = {}

---@param version string
function M.parse(version)
version = version:gsub("^v", "")
return Result.pcall(semver, version)
end

return M
11 changes: 11 additions & 0 deletions lua/mason-registry/index/cmake-lint/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local Pkg = require "mason-core.package"
local pip3 = require "mason-core.managers.pip3"

return Pkg.new {
name = "cmake-lint",
desc = [[cmakelint parses CMake files and reports style issues]],
homepage = "https://github.com/cmake-lint/cmake-lint",
languages = { Pkg.Lang.Python },
categories = { Pkg.Cat.Linter },
install = pip3.packages { "cmakelint", bin = { "cmakelint" } },
}
2 changes: 2 additions & 0 deletions lua/mason-registry/index/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ return {
["clarity-lsp"] = "mason-registry.index.clarity-lsp",
["clojure-lsp"] = "mason-registry.index.clojure-lsp",
["cmake-language-server"] = "mason-registry.index.cmake-language-server",
["cmake-lint"] = "mason-registry.index.cmake-lint",
cmakelang = "mason-registry.index.cmakelang",
codelldb = "mason-registry.index.codelldb",
codeql = "mason-registry.index.codeql",
Expand Down Expand Up @@ -227,6 +228,7 @@ return {
["shopify-theme-check"] = "mason-registry.index.shopify-theme-check",
["slint-lsp"] = "mason-registry.index.slint-lsp",
["smithy-language-server"] = "mason-registry.index.smithy-language-server",
snakefmt = "mason-registry.index.snakefmt",
solang = "mason-registry.index.solang",
solargraph = "mason-registry.index.solargraph",
solhint = "mason-registry.index.solhint",
Expand Down
11 changes: 11 additions & 0 deletions lua/mason-registry/index/snakefmt/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local Pkg = require "mason-core.package"
local pip3 = require "mason-core.managers.pip3"

return Pkg.new {
name = "snakefmt",
desc = "The uncompromising Snakemake code formatter",
homepage = "https://github.com/snakemake/snakefmt",
languages = { Pkg.Lang.Snakemake },
categories = { Pkg.Cat.Formatter },
install = pip3.packages { "snakefmt", bin = { "snakefmt" } },
}
1 change: 1 addition & 0 deletions lua/mason-registry/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ local sources = require "mason-registry.sources"
---@class RegistrySource
---@field get_package fun(self: RegistrySource, pkg_name: string): Package?
---@field get_all_package_names fun(self: RegistrySource): string[]
---@field get_display_name fun(self: RegistrySource): string
---@field is_installed fun(self: RegistrySource): boolean
---@field install async fun(self: RegistrySource): Result

Expand Down
Loading

0 comments on commit 73d3aa3

Please sign in to comment.