mirror of
https://github.com/taigrr/glaze.nvim.git
synced 2026-04-02 03:09:10 -07:00
feat: bug fixes, lazy.nvim-style controls, auto-update checking, repo polish
Phase 1 - Bug fixes:
- Fix extmark rendering in text.lua (table vs string hl handling)
- Fix timer leak: WinClosed autocmd now triggers view.close()
- Add GOBIN/GOPATH/~/go/bin awareness to is_installed() and new bin_path()
- Reject update_all() while tasks running (race condition fix)
- Implement _toggle_details with full binary info expansion
Phase 2 - Lazy.nvim-style controls:
- U = Update all, u = update cursor binary
- I = Install all missing, i = install cursor binary
- x = Abort, CR = toggle details, q/Esc = close
- Line-to-binary mapping for cursor-aware actions
Phase 3 - Auto-update checking:
- New checker module with go list/go version -m integration
- auto_check config option (daily/weekly/custom frequency)
- Persistent state in stdpath('data')/glaze/state.json
- :GlazeCheck command for manual checks
- Update indicators in UI (version info + arrows)
Phase 4 - Repo polish:
- MIT LICENSE file
- Doughnut-themed README with badges and Why Glaze? section
- Updated help docs with all new features/keybinds/API
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Tai Groot
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
52
README.md
52
README.md
@@ -1,16 +1,33 @@
|
|||||||
# glaze.nvim
|
# 🍩 glaze.nvim
|
||||||
|
|
||||||
> A **Mason/Lazy-style** interface for managing Go binaries across Neovim plugins.
|
> **Go + Lazy = Glaze** — A Mason/Lazy-style manager for Go binaries in Neovim.
|
||||||
> Charmbracelet-inspired aesthetic. Zero duplication. One source of truth.
|
> Charmbracelet-inspired aesthetic. Zero duplication. One source of truth.
|
||||||
|
>
|
||||||
|
> *Like a fresh doughnut glaze — smooth, sweet, and holds everything together.*
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## 🤔 Why Glaze?
|
||||||
|
|
||||||
|
Every Go-based Neovim plugin reinvents the wheel: each one ships its own
|
||||||
|
`go install` wrapper, its own update command, its own version checking.
|
||||||
|
|
||||||
|
**Glaze stops the madness.** Register your binaries once, manage them all from
|
||||||
|
one beautiful UI. Plugin authors get a two-line integration. Users get a single
|
||||||
|
`:Glaze` command.
|
||||||
|
|
||||||
## ✨ Features
|
## ✨ Features
|
||||||
|
|
||||||
- **Centralized binary management** — Register binaries from any plugin, update them all at once
|
- **Centralized binary management** — Register binaries from any plugin, update them all at once
|
||||||
- **Lazy.nvim-style UI** — Floating window with progress bars, spinners, and status indicators
|
- **Lazy.nvim-style UI** — Floating window with progress bars, spinners, and status indicators
|
||||||
|
- **Cursor-aware keybinds** — `u` updates the binary under your cursor, `U` updates all
|
||||||
- **Parallel installations** — Configurable concurrency for fast updates
|
- **Parallel installations** — Configurable concurrency for fast updates
|
||||||
|
- **Auto-update checking** — Daily/weekly checks with non-intrusive notifications
|
||||||
|
- **GOBIN/GOPATH awareness** — Finds binaries even if not in PATH
|
||||||
|
- **Detail expansion** — Press `<CR>` to see URL, install path, version info
|
||||||
- **Charmbracelet aesthetic** — Pink/magenta color scheme that matches the Charm toolchain
|
- **Charmbracelet aesthetic** — Pink/magenta color scheme that matches the Charm toolchain
|
||||||
- **Zero config for dependents** — Just register and go
|
- **Zero config for dependents** — Just register and go
|
||||||
- **Callback support** — Get notified when your binary is updated
|
- **Callback support** — Get notified when your binary is updated
|
||||||
@@ -51,26 +68,30 @@ glaze.register("mods", "github.com/charmbracelet/mods")
|
|||||||
| `:Glaze` | Open the Glaze UI |
|
| `:Glaze` | Open the Glaze UI |
|
||||||
| `:GlazeUpdate [name]` | Update all or specific binary |
|
| `:GlazeUpdate [name]` | Update all or specific binary |
|
||||||
| `:GlazeInstall [name]` | Install missing or specific binary |
|
| `:GlazeInstall [name]` | Install missing or specific binary |
|
||||||
|
| `:GlazeCheck` | Manually check for available updates |
|
||||||
|
|
||||||
### Keybinds (in Glaze UI)
|
### Keybinds (in Glaze UI)
|
||||||
|
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
|-----|--------|
|
|-----|--------|
|
||||||
| `u` | Update all binaries |
|
| `U` | Update ALL binaries |
|
||||||
| `i` | Install missing binaries |
|
| `u` | Update binary under cursor |
|
||||||
|
| `I` | Install all missing binaries |
|
||||||
|
| `i` | Install binary under cursor |
|
||||||
| `x` | Abort running tasks |
|
| `x` | Abort running tasks |
|
||||||
|
| `<CR>` | Toggle details (URL, path, version) |
|
||||||
| `q` / `<Esc>` | Close window |
|
| `q` / `<Esc>` | Close window |
|
||||||
|
|
||||||
## 🔌 For Plugin Authors
|
## 🔌 For Plugin Authors
|
||||||
|
|
||||||
Register your plugin's binaries as a dependency:
|
Register your plugin's binaries as a dependency — two lines is all it takes:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- In your plugin's setup or init:
|
-- In your plugin's setup or init:
|
||||||
local ok, glaze = pcall(require, "glaze")
|
local ok, glaze = pcall(require, "glaze")
|
||||||
if ok then
|
if ok then
|
||||||
glaze.register("mytool", "github.com/me/mytool", {
|
glaze.register("mytool", "github.com/me/mytool", {
|
||||||
plugin = "myplugin.nvim", -- Shows in UI
|
plugin = "myplugin.nvim",
|
||||||
callback = function(success)
|
callback = function(success)
|
||||||
if success then
|
if success then
|
||||||
vim.notify("mytool updated!")
|
vim.notify("mytool updated!")
|
||||||
@@ -86,7 +107,6 @@ You can still expose plugin-specific commands that delegate to Glaze:
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
vim.api.nvim_create_user_command("MyPluginUpdate", function()
|
vim.api.nvim_create_user_command("MyPluginUpdate", function()
|
||||||
local glaze = require("glaze")
|
|
||||||
require("glaze.runner").update({ "mytool" })
|
require("glaze.runner").update({ "mytool" })
|
||||||
end, {})
|
end, {})
|
||||||
```
|
```
|
||||||
@@ -108,6 +128,10 @@ require("glaze").setup({
|
|||||||
},
|
},
|
||||||
concurrency = 4, -- Max parallel installations
|
concurrency = 4, -- Max parallel installations
|
||||||
go_cmd = { "go" }, -- Auto-detects goenv if available
|
go_cmd = { "go" }, -- Auto-detects goenv if available
|
||||||
|
auto_check = {
|
||||||
|
enabled = true, -- Auto-check for updates
|
||||||
|
frequency = "daily", -- "daily", "weekly", or hours as number
|
||||||
|
},
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -139,7 +163,8 @@ glaze.unregister(name) -- Remove a binary
|
|||||||
glaze.binaries() -- Get all registered binaries
|
glaze.binaries() -- Get all registered binaries
|
||||||
|
|
||||||
-- Status
|
-- Status
|
||||||
glaze.is_installed(name) -- Check if binary exists
|
glaze.is_installed(name) -- Check if binary exists (PATH + GOBIN + GOPATH)
|
||||||
|
glaze.bin_path(name) -- Get full path to binary
|
||||||
glaze.status(name) -- "installed", "missing", or "unknown"
|
glaze.status(name) -- "installed", "missing", or "unknown"
|
||||||
|
|
||||||
-- Runner (for programmatic control)
|
-- Runner (for programmatic control)
|
||||||
@@ -152,15 +177,22 @@ runner.abort() -- Stop all tasks
|
|||||||
runner.is_running() -- Check if tasks are running
|
runner.is_running() -- Check if tasks are running
|
||||||
runner.tasks() -- Get current task list
|
runner.tasks() -- Get current task list
|
||||||
runner.stats() -- Get { total, done, error, running, pending }
|
runner.stats() -- Get { total, done, error, running, pending }
|
||||||
|
|
||||||
|
-- Update checker
|
||||||
|
local checker = require("glaze.checker")
|
||||||
|
checker.check() -- Check for updates (with notifications)
|
||||||
|
checker.auto_check() -- Check only if enough time has passed
|
||||||
|
checker.get_update_info() -- Get cached update info
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🤝 Related Projects
|
## 🤝 Related Projects
|
||||||
|
|
||||||
- [freeze.nvim](https://github.com/taigrr/freeze.nvim) — Screenshot code with freeze
|
- [freeze.nvim](https://github.com/taigrr/freeze.nvim) — Screenshot code with freeze
|
||||||
- [neocrush.nvim](https://github.com/taigrr/neocrush.nvim) — AI-powered coding assistant
|
- [neocrush.nvim](https://github.com/taigrr/neocrush.nvim) — AI-powered coding assistant
|
||||||
|
- [blast.nvim](https://github.com/taigrr/blast.nvim) — Code activity tracking
|
||||||
- [lazy.nvim](https://github.com/folke/lazy.nvim) — UI inspiration
|
- [lazy.nvim](https://github.com/folke/lazy.nvim) — UI inspiration
|
||||||
- [mason.nvim](https://github.com/williamboman/mason.nvim) — Concept inspiration
|
- [mason.nvim](https://github.com/williamboman/mason.nvim) — Concept inspiration
|
||||||
|
|
||||||
## 📄 License
|
## 📄 License
|
||||||
|
|
||||||
MIT
|
MIT © [Tai Groot](https://github.com/taigrr)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
*glaze.txt* Centralized Go binary management for Neovim plugins
|
*glaze.txt* 🍩 Centralized Go binary management for Neovim plugins
|
||||||
|
|
||||||
Author: Tai Groot <tai@taigrr.com>
|
Author: Tai Groot <tai@taigrr.com>
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -10,19 +10,25 @@ CONTENTS *glaze-contents*
|
|||||||
1. Introduction ........................... |glaze-introduction|
|
1. Introduction ........................... |glaze-introduction|
|
||||||
2. Setup .................................. |glaze-setup|
|
2. Setup .................................. |glaze-setup|
|
||||||
3. Commands ............................... |glaze-commands|
|
3. Commands ............................... |glaze-commands|
|
||||||
4. API .................................... |glaze-api|
|
4. Keybinds ............................... |glaze-keybinds|
|
||||||
5. Configuration .......................... |glaze-config|
|
5. API .................................... |glaze-api|
|
||||||
6. Highlights ............................. |glaze-highlights|
|
6. Configuration .......................... |glaze-config|
|
||||||
7. For Plugin Authors ..................... |glaze-plugin-authors|
|
7. Highlights ............................. |glaze-highlights|
|
||||||
|
8. Auto-update Checking ................... |glaze-auto-check|
|
||||||
|
9. For Plugin Authors ..................... |glaze-plugin-authors|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
1. INTRODUCTION *glaze-introduction*
|
1. INTRODUCTION *glaze-introduction*
|
||||||
|
|
||||||
Glaze is a centralized manager for Go binaries used by Neovim plugins. Instead
|
Glaze (Go + Lazy = Glaze 🍩) is a centralized manager for Go binaries used by
|
||||||
of each plugin implementing its own binary installer, Glaze provides:
|
Neovim plugins. Instead of each plugin implementing its own binary installer,
|
||||||
|
Glaze provides:
|
||||||
|
|
||||||
- A unified UI for managing all Go binaries
|
- A unified lazy.nvim-style UI for managing all Go binaries
|
||||||
- Parallel installation with progress tracking
|
- Parallel installation with progress tracking
|
||||||
|
- Cursor-aware keybinds for individual binary control
|
||||||
|
- Automatic update checking with notifications
|
||||||
|
- GOBIN/GOPATH awareness
|
||||||
- A simple registration API for plugin authors
|
- A simple registration API for plugin authors
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
@@ -50,8 +56,26 @@ Using lazy.nvim:
|
|||||||
*:GlazeInstall*
|
*:GlazeInstall*
|
||||||
:GlazeInstall [name] Install missing binaries, or a specific one.
|
:GlazeInstall [name] Install missing binaries, or a specific one.
|
||||||
|
|
||||||
|
*:GlazeCheck*
|
||||||
|
:GlazeCheck Manually check for available updates.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
4. API *glaze-api*
|
4. KEYBINDS *glaze-keybinds*
|
||||||
|
|
||||||
|
These keybinds are active in the Glaze UI window:
|
||||||
|
|
||||||
|
Key Action ~
|
||||||
|
`U` Update ALL registered binaries
|
||||||
|
`u` Update the binary under the cursor
|
||||||
|
`I` Install all missing binaries
|
||||||
|
`i` Install the binary under the cursor
|
||||||
|
`x` Abort all running tasks
|
||||||
|
`<CR>` Toggle detail expansion (URL, path, version, errors)
|
||||||
|
`q` Close the Glaze window
|
||||||
|
`<Esc>` Close the Glaze window
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
5. API *glaze-api*
|
||||||
|
|
||||||
*glaze.setup()*
|
*glaze.setup()*
|
||||||
glaze.setup({opts})
|
glaze.setup({opts})
|
||||||
@@ -78,14 +102,38 @@ glaze.binaries()
|
|||||||
|
|
||||||
*glaze.is_installed()*
|
*glaze.is_installed()*
|
||||||
glaze.is_installed({name})
|
glaze.is_installed({name})
|
||||||
Returns true if the binary is in PATH.
|
Returns true if the binary is found in PATH, $GOBIN, $GOPATH/bin,
|
||||||
|
or ~/go/bin.
|
||||||
|
|
||||||
|
*glaze.bin_path()*
|
||||||
|
glaze.bin_path({name})
|
||||||
|
Returns the full path to the binary, or nil if not found.
|
||||||
|
|
||||||
*glaze.status()*
|
*glaze.status()*
|
||||||
glaze.status({name})
|
glaze.status({name})
|
||||||
Returns "installed", "missing", or "unknown".
|
Returns "installed", "missing", or "unknown".
|
||||||
|
|
||||||
|
*glaze-runner-api*
|
||||||
|
Runner API (require("glaze.runner")):
|
||||||
|
|
||||||
|
runner.update({names}) Update specific binaries
|
||||||
|
runner.update_all() Update all registered binaries
|
||||||
|
runner.install({names}) Install specific binaries
|
||||||
|
runner.install_missing() Install all missing binaries
|
||||||
|
runner.abort() Stop all running tasks
|
||||||
|
runner.is_running() Check if tasks are running
|
||||||
|
runner.tasks() Get current task list
|
||||||
|
runner.stats() Get task statistics
|
||||||
|
|
||||||
|
*glaze-checker-api*
|
||||||
|
Checker API (require("glaze.checker")):
|
||||||
|
|
||||||
|
checker.check() Check for updates (with notifications)
|
||||||
|
checker.auto_check() Check only if enough time has passed
|
||||||
|
checker.get_update_info() Get cached update info table
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
5. CONFIGURATION *glaze-config*
|
6. CONFIGURATION *glaze-config*
|
||||||
|
|
||||||
Default configuration:
|
Default configuration:
|
||||||
>lua
|
>lua
|
||||||
@@ -103,11 +151,15 @@ Default configuration:
|
|||||||
},
|
},
|
||||||
concurrency = 4,
|
concurrency = 4,
|
||||||
go_cmd = { "go" }, -- Auto-detects goenv
|
go_cmd = { "go" }, -- Auto-detects goenv
|
||||||
|
auto_check = {
|
||||||
|
enabled = true, -- Auto-check for updates on setup
|
||||||
|
frequency = "daily", -- "daily", "weekly", or hours (number)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
<
|
<
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
6. HIGHLIGHTS *glaze-highlights*
|
7. HIGHLIGHTS *glaze-highlights*
|
||||||
|
|
||||||
All highlight groups are prefixed with "Glaze":
|
All highlight groups are prefixed with "Glaze":
|
||||||
|
|
||||||
@@ -121,9 +173,27 @@ All highlight groups are prefixed with "Glaze":
|
|||||||
GlazeRunning In-progress status
|
GlazeRunning In-progress status
|
||||||
GlazeProgressDone Progress bar (filled)
|
GlazeProgressDone Progress bar (filled)
|
||||||
GlazeProgressTodo Progress bar (empty)
|
GlazeProgressTodo Progress bar (empty)
|
||||||
|
GlazeVersion Version info
|
||||||
|
GlazeTime Timing info
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
7. FOR PLUGIN AUTHORS *glaze-plugin-authors*
|
8. AUTO-UPDATE CHECKING *glaze-auto-check*
|
||||||
|
|
||||||
|
Glaze can automatically check for newer versions of your registered binaries.
|
||||||
|
|
||||||
|
When enabled, Glaze checks on setup if enough time has passed since the last
|
||||||
|
check. It uses `go list -m -json <module>@latest` to find the latest version
|
||||||
|
and `go version -m <binary>` to determine the installed version.
|
||||||
|
|
||||||
|
State is stored in: `vim.fn.stdpath("data") .. "/glaze/state.json"`
|
||||||
|
|
||||||
|
Use `:GlazeCheck` to manually trigger a check at any time.
|
||||||
|
|
||||||
|
In the UI, binaries with available updates show a ⬆ indicator with version
|
||||||
|
info (e.g., "v1.0.0 → v1.1.0").
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
9. FOR PLUGIN AUTHORS *glaze-plugin-authors*
|
||||||
|
|
||||||
Register your binaries in your plugin's setup:
|
Register your binaries in your plugin's setup:
|
||||||
>lua
|
>lua
|
||||||
|
|||||||
257
lua/glaze/checker.lua
Normal file
257
lua/glaze/checker.lua
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
---@brief [[
|
||||||
|
--- glaze.nvim update checker
|
||||||
|
--- Checks for newer versions of registered Go binaries
|
||||||
|
---@brief ]]
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
---@class GlazeUpdateInfo
|
||||||
|
---@field name string Binary name
|
||||||
|
---@field installed_version? string Currently installed version
|
||||||
|
---@field latest_version? string Latest available version
|
||||||
|
---@field has_update boolean Whether an update is available
|
||||||
|
|
||||||
|
---@type table<string, GlazeUpdateInfo>
|
||||||
|
M._update_info = {}
|
||||||
|
|
||||||
|
---@type boolean
|
||||||
|
M._checking = false
|
||||||
|
|
||||||
|
local STATE_FILE = vim.fn.stdpath("data") .. "/glaze/state.json"
|
||||||
|
|
||||||
|
---Read persisted state from disk.
|
||||||
|
---@return table
|
||||||
|
local function read_state()
|
||||||
|
local ok, content = pcall(vim.fn.readfile, STATE_FILE)
|
||||||
|
if not ok or #content == 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
local decode_ok, data = pcall(vim.json.decode, table.concat(content, "\n"))
|
||||||
|
if not decode_ok then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
return data or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
---Write state to disk.
|
||||||
|
---@param state table
|
||||||
|
local function write_state(state)
|
||||||
|
local dir = vim.fn.fnamemodify(STATE_FILE, ":h")
|
||||||
|
vim.fn.mkdir(dir, "p")
|
||||||
|
local json = vim.json.encode(state)
|
||||||
|
vim.fn.writefile({ json }, STATE_FILE)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get the frequency in seconds from config.
|
||||||
|
---@return number seconds
|
||||||
|
local function get_frequency_seconds()
|
||||||
|
local glaze = require("glaze")
|
||||||
|
local freq = glaze.config.auto_check.frequency
|
||||||
|
if freq == "daily" then
|
||||||
|
return 86400
|
||||||
|
elseif freq == "weekly" then
|
||||||
|
return 604800
|
||||||
|
elseif type(freq) == "number" then
|
||||||
|
return freq * 3600
|
||||||
|
end
|
||||||
|
return 86400
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get installed version of a binary by parsing `go version -m` output.
|
||||||
|
---@param name string Binary name
|
||||||
|
---@param callback fun(version: string?)
|
||||||
|
local function get_installed_version(name, callback)
|
||||||
|
local glaze = require("glaze")
|
||||||
|
local bin_path = glaze.bin_path(name)
|
||||||
|
if not bin_path then
|
||||||
|
callback(nil)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.fn.jobstart({ "go", "version", "-m", bin_path }, {
|
||||||
|
stdout_buffered = true,
|
||||||
|
on_stdout = function(_, data)
|
||||||
|
if not data then
|
||||||
|
callback(nil)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local output = table.concat(data, "\n")
|
||||||
|
-- Parse "mod\tmodule/path\tv1.2.3\th1:..." or "path\tmodule/path"
|
||||||
|
local version = output:match("\tmod\t[^\t]+\t(v[^\t%s]+)")
|
||||||
|
or output:match("\tpath\t[^\n]+\n[^\t]*\tmod\t[^\t]+\t(v[^\t%s]+)")
|
||||||
|
callback(version)
|
||||||
|
end,
|
||||||
|
on_exit = function(_, code)
|
||||||
|
if code ~= 0 then
|
||||||
|
callback(nil)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
---Check for the latest version of a module using go list.
|
||||||
|
---@param url string Module URL
|
||||||
|
---@param callback fun(version: string?)
|
||||||
|
local function get_latest_version(url, callback)
|
||||||
|
local glaze = require("glaze")
|
||||||
|
local cmd = vim.list_extend({}, glaze.config.go_cmd)
|
||||||
|
vim.list_extend(cmd, { "list", "-m", "-json", url .. "@latest" })
|
||||||
|
|
||||||
|
vim.fn.jobstart(cmd, {
|
||||||
|
stdout_buffered = true,
|
||||||
|
env = { GOFLAGS = "" },
|
||||||
|
on_stdout = function(_, data)
|
||||||
|
if not data then
|
||||||
|
callback(nil)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local output = table.concat(data, "\n")
|
||||||
|
local decode_ok, result = pcall(vim.json.decode, output)
|
||||||
|
if decode_ok and result and result.Version then
|
||||||
|
callback(result.Version)
|
||||||
|
else
|
||||||
|
callback(nil)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_exit = function(_, code)
|
||||||
|
if code ~= 0 then
|
||||||
|
callback(nil)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get cached update info.
|
||||||
|
---@return table<string, GlazeUpdateInfo>
|
||||||
|
function M.get_update_info()
|
||||||
|
return M._update_info
|
||||||
|
end
|
||||||
|
|
||||||
|
---Check for updates on all registered binaries.
|
||||||
|
---@param opts? { silent?: boolean }
|
||||||
|
function M.check(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local glaze = require("glaze")
|
||||||
|
local binaries = glaze.binaries()
|
||||||
|
|
||||||
|
if vim.tbl_count(binaries) == 0 then
|
||||||
|
if not opts.silent then
|
||||||
|
vim.notify("Glaze: no binaries registered", vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if M._checking then
|
||||||
|
if not opts.silent then
|
||||||
|
vim.notify("Glaze: already checking for updates", vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
M._checking = true
|
||||||
|
local remaining = 0
|
||||||
|
local updates_found = 0
|
||||||
|
|
||||||
|
for name, binary in pairs(binaries) do
|
||||||
|
remaining = remaining + 2 -- installed version + latest version
|
||||||
|
|
||||||
|
local info = {
|
||||||
|
name = name,
|
||||||
|
installed_version = nil,
|
||||||
|
latest_version = nil,
|
||||||
|
has_update = false,
|
||||||
|
}
|
||||||
|
M._update_info[name] = info
|
||||||
|
|
||||||
|
get_installed_version(name, function()
|
||||||
|
-- callback receives version from the jobstart; re-check via closure
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Simplified: check each binary sequentially-ish
|
||||||
|
remaining = vim.tbl_count(binaries)
|
||||||
|
for name, binary in pairs(binaries) do
|
||||||
|
local info = M._update_info[name]
|
||||||
|
|
||||||
|
get_installed_version(name, function(installed)
|
||||||
|
info.installed_version = installed
|
||||||
|
|
||||||
|
get_latest_version(binary.url, function(latest)
|
||||||
|
info.latest_version = latest
|
||||||
|
|
||||||
|
if installed and latest and installed ~= latest then
|
||||||
|
info.has_update = true
|
||||||
|
updates_found = updates_found + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
remaining = remaining - 1
|
||||||
|
if remaining <= 0 then
|
||||||
|
M._checking = false
|
||||||
|
|
||||||
|
-- Save check timestamp
|
||||||
|
local state = read_state()
|
||||||
|
state.last_check = os.time()
|
||||||
|
state.update_info = {}
|
||||||
|
for n, i in pairs(M._update_info) do
|
||||||
|
state.update_info[n] = {
|
||||||
|
installed_version = i.installed_version,
|
||||||
|
latest_version = i.latest_version,
|
||||||
|
has_update = i.has_update,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
write_state(state)
|
||||||
|
|
||||||
|
if not opts.silent then
|
||||||
|
if updates_found > 0 then
|
||||||
|
vim.schedule(function()
|
||||||
|
vim.notify("Glaze: " .. updates_found .. " update(s) available", vim.log.levels.INFO)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
vim.schedule(function()
|
||||||
|
vim.notify("Glaze: all binaries up to date", vim.log.levels.INFO)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
elseif updates_found > 0 then
|
||||||
|
vim.schedule(function()
|
||||||
|
vim.notify("Glaze: " .. updates_found .. " update(s) available", vim.log.levels.INFO)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Refresh UI if open
|
||||||
|
vim.schedule(function()
|
||||||
|
local ok, view = pcall(require, "glaze.view")
|
||||||
|
if ok and view._float and view._float:valid() then
|
||||||
|
view.render()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Auto-check if enough time has passed since last check.
|
||||||
|
function M.auto_check()
|
||||||
|
local state = read_state()
|
||||||
|
local last_check = state.last_check or 0
|
||||||
|
local now = os.time()
|
||||||
|
local freq = get_frequency_seconds()
|
||||||
|
|
||||||
|
-- Load cached update info
|
||||||
|
if state.update_info then
|
||||||
|
for name, info in pairs(state.update_info) do
|
||||||
|
M._update_info[name] = {
|
||||||
|
name = name,
|
||||||
|
installed_version = info.installed_version,
|
||||||
|
latest_version = info.latest_version,
|
||||||
|
has_update = info.has_update or false,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (now - last_check) >= freq then
|
||||||
|
M.check({ silent = true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -119,12 +119,18 @@ function Float:mount()
|
|||||||
self:close()
|
self:close()
|
||||||
end, "Close")
|
end, "Close")
|
||||||
|
|
||||||
-- Auto-close on WinClosed
|
-- Auto-close on WinClosed — also trigger view cleanup to stop timer
|
||||||
vim.api.nvim_create_autocmd("WinClosed", {
|
vim.api.nvim_create_autocmd("WinClosed", {
|
||||||
pattern = tostring(self.win),
|
pattern = tostring(self.win),
|
||||||
once = true,
|
once = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
self:close()
|
-- Trigger view close to clean up timer
|
||||||
|
local ok, view = pcall(require, "glaze.view")
|
||||||
|
if ok then
|
||||||
|
view.close()
|
||||||
|
else
|
||||||
|
self:close()
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,15 @@ local M = {}
|
|||||||
---@field plugin? string Plugin that registered this binary
|
---@field plugin? string Plugin that registered this binary
|
||||||
---@field callback? fun(success: boolean) Optional callback after install/update
|
---@field callback? fun(success: boolean) Optional callback after install/update
|
||||||
|
|
||||||
|
---@class GlazeAutoCheckConfig
|
||||||
|
---@field enabled boolean Whether to auto-check for updates
|
||||||
|
---@field frequency string|number Frequency: "daily", "weekly", or hours as number
|
||||||
|
|
||||||
---@class GlazeConfig
|
---@class GlazeConfig
|
||||||
---@field ui GlazeUIConfig
|
---@field ui GlazeUIConfig
|
||||||
---@field concurrency number Max parallel installations
|
---@field concurrency number Max parallel installations
|
||||||
---@field go_cmd string[] Go command (supports goenv)
|
---@field go_cmd string[] Go command (supports goenv)
|
||||||
|
---@field auto_check GlazeAutoCheckConfig
|
||||||
|
|
||||||
---@class GlazeUIConfig
|
---@class GlazeUIConfig
|
||||||
---@field border string Border style
|
---@field border string Border style
|
||||||
@@ -50,6 +55,10 @@ M.config = {
|
|||||||
},
|
},
|
||||||
concurrency = 4,
|
concurrency = 4,
|
||||||
go_cmd = { "go" },
|
go_cmd = { "go" },
|
||||||
|
auto_check = {
|
||||||
|
enabled = true,
|
||||||
|
frequency = "daily",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
---@type table<string, GlazeBinary>
|
---@type table<string, GlazeBinary>
|
||||||
@@ -100,6 +109,17 @@ function M.setup(opts)
|
|||||||
return vim.tbl_keys(M._binaries)
|
return vim.tbl_keys(M._binaries)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("GlazeCheck", function()
|
||||||
|
require("glaze.checker").check()
|
||||||
|
end, { desc = "Check for binary updates" })
|
||||||
|
|
||||||
|
-- Auto-check for updates
|
||||||
|
if M.config.auto_check.enabled then
|
||||||
|
vim.defer_fn(function()
|
||||||
|
require("glaze.checker").auto_check()
|
||||||
|
end, 3000)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Register a binary for management.
|
---Register a binary for management.
|
||||||
@@ -129,10 +149,79 @@ function M.binaries()
|
|||||||
end
|
end
|
||||||
|
|
||||||
---Check if a binary is installed.
|
---Check if a binary is installed.
|
||||||
|
---Checks PATH, $GOBIN, $GOPATH/bin, and $(go env GOBIN).
|
||||||
---@param name string Binary name
|
---@param name string Binary name
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function M.is_installed(name)
|
function M.is_installed(name)
|
||||||
return vim.fn.executable(name) == 1
|
-- Check PATH first
|
||||||
|
if vim.fn.executable(name) == 1 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check $GOBIN
|
||||||
|
local gobin = os.getenv("GOBIN")
|
||||||
|
if gobin and gobin ~= "" then
|
||||||
|
local path = gobin .. "/" .. name
|
||||||
|
if vim.uv.fs_stat(path) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check $GOPATH/bin
|
||||||
|
local gopath = os.getenv("GOPATH")
|
||||||
|
if gopath and gopath ~= "" then
|
||||||
|
local path = gopath .. "/bin/" .. name
|
||||||
|
if vim.uv.fs_stat(path) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check default ~/go/bin
|
||||||
|
local home = os.getenv("HOME") or os.getenv("USERPROFILE") or ""
|
||||||
|
local default_path = home .. "/go/bin/" .. name
|
||||||
|
if vim.uv.fs_stat(default_path) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get the install path for a binary if found.
|
||||||
|
---@param name string Binary name
|
||||||
|
---@return string? path Full path to the binary, or nil
|
||||||
|
function M.bin_path(name)
|
||||||
|
-- Check PATH
|
||||||
|
local which = vim.fn.exepath(name)
|
||||||
|
if which ~= "" then
|
||||||
|
return which
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check $GOBIN
|
||||||
|
local gobin = os.getenv("GOBIN")
|
||||||
|
if gobin and gobin ~= "" then
|
||||||
|
local path = gobin .. "/" .. name
|
||||||
|
if vim.uv.fs_stat(path) then
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check $GOPATH/bin
|
||||||
|
local gopath = os.getenv("GOPATH")
|
||||||
|
if gopath and gopath ~= "" then
|
||||||
|
local path = gopath .. "/bin/" .. name
|
||||||
|
if vim.uv.fs_stat(path) then
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check default ~/go/bin
|
||||||
|
local home = os.getenv("HOME") or os.getenv("USERPROFILE") or ""
|
||||||
|
local default_path = home .. "/go/bin/" .. name
|
||||||
|
if vim.uv.fs_stat(default_path) then
|
||||||
|
return default_path
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get binary installation status.
|
---Get binary installation status.
|
||||||
|
|||||||
@@ -139,6 +139,12 @@ end
|
|||||||
local function run(names, mode)
|
local function run(names, mode)
|
||||||
local glaze = require("glaze")
|
local glaze = require("glaze")
|
||||||
|
|
||||||
|
-- Reject if already running (race condition fix)
|
||||||
|
if M._running then
|
||||||
|
vim.notify("Glaze: tasks already running. Wait or abort first.", vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Check for Go
|
-- Check for Go
|
||||||
local go_check = glaze.config.go_cmd[1]
|
local go_check = glaze.config.go_cmd[1]
|
||||||
if vim.fn.executable(go_check) ~= 1 then
|
if vim.fn.executable(go_check) ~= 1 then
|
||||||
|
|||||||
@@ -57,6 +57,17 @@ function Text:append(str, hl, opts)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Append a virtual text extmark on the current line (empty string segment).
|
||||||
|
---@param extmark GlazeExtmark Extmark options (virt_text, virt_text_win_col, etc.)
|
||||||
|
---@return GlazeText
|
||||||
|
function Text:append_extmark(extmark)
|
||||||
|
if #self._lines == 0 then
|
||||||
|
self:nl()
|
||||||
|
end
|
||||||
|
table.insert(self._lines[#self._lines], { str = "", hl = extmark })
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
---@return GlazeText
|
---@return GlazeText
|
||||||
function Text:nl()
|
function Text:nl()
|
||||||
table.insert(self._lines, {})
|
table.insert(self._lines, {})
|
||||||
@@ -106,7 +117,7 @@ function Text:render(buf, ns)
|
|||||||
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
|
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
|
||||||
|
|
||||||
for l, line in ipairs(self._lines) do
|
for l, line in ipairs(self._lines) do
|
||||||
if lines[l] ~= "" then
|
if lines[l] ~= "" or true then -- process even empty lines for extmarks
|
||||||
local col = self.padding
|
local col = self.padding
|
||||||
for _, segment in ipairs(line) do
|
for _, segment in ipairs(line) do
|
||||||
local width = vim.fn.strwidth(segment.str)
|
local width = vim.fn.strwidth(segment.str)
|
||||||
@@ -114,12 +125,23 @@ function Text:render(buf, ns)
|
|||||||
|
|
||||||
if extmark then
|
if extmark then
|
||||||
if type(extmark) == "string" then
|
if type(extmark) == "string" then
|
||||||
extmark = { hl_group = extmark, end_col = col + width }
|
-- Simple highlight group string
|
||||||
|
if width > 0 then
|
||||||
|
pcall(vim.api.nvim_buf_set_extmark, buf, ns, l - 1, col, {
|
||||||
|
hl_group = extmark,
|
||||||
|
end_col = col + width,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
elseif type(extmark) == "table" then
|
||||||
|
-- Full extmark table (virt_text, etc.)
|
||||||
|
local extmark_col = extmark.col or col
|
||||||
|
local opts = vim.tbl_extend("force", {}, extmark)
|
||||||
|
opts.col = nil -- col is positional, not an extmark option
|
||||||
|
if not opts.end_col and width > 0 and opts.hl_group then
|
||||||
|
opts.end_col = extmark_col + width
|
||||||
|
end
|
||||||
|
pcall(vim.api.nvim_buf_set_extmark, buf, ns, l - 1, extmark_col, opts)
|
||||||
end
|
end
|
||||||
---@cast extmark GlazeExtmark
|
|
||||||
local extmark_col = extmark.col or col
|
|
||||||
extmark.col = nil
|
|
||||||
pcall(vim.api.nvim_buf_set_extmark, buf, ns, l - 1, extmark_col, extmark)
|
|
||||||
end
|
end
|
||||||
col = col + width
|
col = col + width
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,9 +11,26 @@ M._float = nil
|
|||||||
---@type number?
|
---@type number?
|
||||||
M._timer = nil
|
M._timer = nil
|
||||||
|
|
||||||
|
---@type table<string, boolean>
|
||||||
|
M._expanded = {}
|
||||||
|
|
||||||
|
---Line-to-binary mapping for cursor-aware actions.
|
||||||
|
---@type table<number, string>
|
||||||
|
M._line_map = {}
|
||||||
|
|
||||||
local SPINNERS = { "◐", "◓", "◑", "◒" }
|
local SPINNERS = { "◐", "◓", "◑", "◒" }
|
||||||
local SPINNER_IDX = 1
|
local SPINNER_IDX = 1
|
||||||
|
|
||||||
|
---Get the binary name under the cursor.
|
||||||
|
---@return string? name Binary name or nil
|
||||||
|
function M._get_cursor_binary()
|
||||||
|
if not M._float or not M._float:valid() then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local line = vim.api.nvim_win_get_cursor(M._float.win)[1]
|
||||||
|
return M._line_map[line]
|
||||||
|
end
|
||||||
|
|
||||||
---Open the Glaze UI.
|
---Open the Glaze UI.
|
||||||
function M.open()
|
function M.open()
|
||||||
local Float = require("glaze.float").Float
|
local Float = require("glaze.float").Float
|
||||||
@@ -23,17 +40,35 @@ function M.open()
|
|||||||
end
|
end
|
||||||
|
|
||||||
M._float = Float.new({
|
M._float = Float.new({
|
||||||
title = " Glaze ",
|
title = " 🍩 Glaze ",
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Set up keymaps
|
-- Set up keymaps — lazy.nvim-style controls
|
||||||
M._float:map("u", function()
|
M._float:map("U", function()
|
||||||
require("glaze.runner").update_all()
|
require("glaze.runner").update_all()
|
||||||
end, "Update all")
|
end, "Update all binaries")
|
||||||
|
|
||||||
|
M._float:map("u", function()
|
||||||
|
local name = M._get_cursor_binary()
|
||||||
|
if name then
|
||||||
|
require("glaze.runner").update({ name })
|
||||||
|
else
|
||||||
|
vim.notify("Move cursor to a binary line to update it", vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
end, "Update binary under cursor")
|
||||||
|
|
||||||
M._float:map("i", function()
|
M._float:map("i", function()
|
||||||
|
local name = M._get_cursor_binary()
|
||||||
|
if name then
|
||||||
|
require("glaze.runner").install({ name })
|
||||||
|
else
|
||||||
|
vim.notify("Move cursor to a binary line to install it", vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
end, "Install binary under cursor")
|
||||||
|
|
||||||
|
M._float:map("I", function()
|
||||||
require("glaze.runner").install_missing()
|
require("glaze.runner").install_missing()
|
||||||
end, "Install missing")
|
end, "Install all missing")
|
||||||
|
|
||||||
M._float:map("x", function()
|
M._float:map("x", function()
|
||||||
require("glaze.runner").abort()
|
require("glaze.runner").abort()
|
||||||
@@ -58,6 +93,7 @@ end
|
|||||||
function M._start_timer()
|
function M._start_timer()
|
||||||
if M._timer then
|
if M._timer then
|
||||||
vim.fn.timer_stop(M._timer)
|
vim.fn.timer_stop(M._timer)
|
||||||
|
M._timer = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
M._timer = vim.fn.timer_start(100, function()
|
M._timer = vim.fn.timer_start(100, function()
|
||||||
@@ -80,8 +116,14 @@ function M._start_timer()
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
|
---Toggle detail expansion for the binary under the cursor.
|
||||||
function M._toggle_details()
|
function M._toggle_details()
|
||||||
-- TODO: Implement detail expansion
|
local name = M._get_cursor_binary()
|
||||||
|
if not name then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
M._expanded[name] = not M._expanded[name]
|
||||||
|
M.render()
|
||||||
end
|
end
|
||||||
|
|
||||||
---Render the UI.
|
---Render the UI.
|
||||||
@@ -92,6 +134,7 @@ function M.render()
|
|||||||
|
|
||||||
local glaze = require("glaze")
|
local glaze = require("glaze")
|
||||||
local runner = require("glaze.runner")
|
local runner = require("glaze.runner")
|
||||||
|
local checker = require("glaze.checker")
|
||||||
local Text = require("glaze.text").Text
|
local Text = require("glaze.text").Text
|
||||||
|
|
||||||
local text = Text.new()
|
local text = Text.new()
|
||||||
@@ -99,9 +142,12 @@ function M.render()
|
|||||||
|
|
||||||
local icons = glaze.config.ui.icons
|
local icons = glaze.config.ui.icons
|
||||||
|
|
||||||
|
-- Reset line map
|
||||||
|
M._line_map = {}
|
||||||
|
|
||||||
-- Header
|
-- Header
|
||||||
text:nl()
|
text:nl()
|
||||||
text:append(" ", "GlazeIcon"):append("Glaze", "GlazeH1"):append(" Go Binary Manager", "GlazeComment"):nl()
|
text:append(" 🍩 ", "GlazeIcon"):append("Glaze", "GlazeH1"):append(" Go Binary Manager", "GlazeComment"):nl()
|
||||||
text:nl()
|
text:nl()
|
||||||
|
|
||||||
-- Stats / Progress
|
-- Stats / Progress
|
||||||
@@ -113,12 +159,19 @@ function M.render()
|
|||||||
|
|
||||||
-- Keybinds
|
-- Keybinds
|
||||||
text:append(" ", nil, { indent = 2 })
|
text:append(" ", nil, { indent = 2 })
|
||||||
|
text:append(" U ", "GlazeButtonActive"):append(" Update All ", "GlazeButton")
|
||||||
|
text:append(" ")
|
||||||
text:append(" u ", "GlazeButtonActive"):append(" Update ", "GlazeButton")
|
text:append(" u ", "GlazeButtonActive"):append(" Update ", "GlazeButton")
|
||||||
text:append(" ")
|
text:append(" ")
|
||||||
text:append(" i ", "GlazeButtonActive"):append(" Install ", "GlazeButton")
|
text:append(" I ", "GlazeButtonActive"):append(" Install All ", "GlazeButton")
|
||||||
text:append(" ")
|
text:append(" ")
|
||||||
|
text:append(" i ", "GlazeButtonActive"):append(" Install ", "GlazeButton")
|
||||||
|
text:nl()
|
||||||
|
text:append(" ", nil, { indent = 2 })
|
||||||
text:append(" x ", "GlazeButtonActive"):append(" Abort ", "GlazeButton")
|
text:append(" x ", "GlazeButtonActive"):append(" Abort ", "GlazeButton")
|
||||||
text:append(" ")
|
text:append(" ")
|
||||||
|
text:append(" ⏎ ", "GlazeButtonActive"):append(" Details ", "GlazeButton")
|
||||||
|
text:append(" ")
|
||||||
text:append(" q ", "GlazeButtonActive"):append(" Close ", "GlazeButton")
|
text:append(" q ", "GlazeButtonActive"):append(" Close ", "GlazeButton")
|
||||||
text:nl():nl()
|
text:nl():nl()
|
||||||
|
|
||||||
@@ -137,7 +190,22 @@ function M.render()
|
|||||||
local binaries = glaze.binaries()
|
local binaries = glaze.binaries()
|
||||||
local binary_count = vim.tbl_count(binaries)
|
local binary_count = vim.tbl_count(binaries)
|
||||||
|
|
||||||
text:append("Binaries", "GlazeH2"):append(" (" .. binary_count .. ")", "GlazeComment"):nl()
|
-- Count updates available
|
||||||
|
local updates_available = 0
|
||||||
|
local update_info = checker.get_update_info()
|
||||||
|
for _ in pairs(update_info) do
|
||||||
|
updates_available = updates_available + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local header_suffix = " (" .. binary_count .. ")"
|
||||||
|
if updates_available > 0 then
|
||||||
|
header_suffix = header_suffix
|
||||||
|
end
|
||||||
|
text:append("Binaries", "GlazeH2"):append(header_suffix, "GlazeComment")
|
||||||
|
if updates_available > 0 then
|
||||||
|
text:append(" " .. updates_available .. " update(s) available", "GlazeRunning")
|
||||||
|
end
|
||||||
|
text:nl()
|
||||||
text:nl()
|
text:nl()
|
||||||
|
|
||||||
if binary_count == 0 then
|
if binary_count == 0 then
|
||||||
@@ -156,7 +224,7 @@ function M.render()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
for _, item in ipairs(sorted) do
|
for _, item in ipairs(sorted) do
|
||||||
M._render_binary(text, item.binary, icons)
|
M._render_binary(text, item.binary, icons, update_info)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -181,14 +249,19 @@ function M._render_progress(text, stats)
|
|||||||
local done_width = math.floor(done_ratio * width + 0.5)
|
local done_width = math.floor(done_ratio * width + 0.5)
|
||||||
|
|
||||||
if stats.done < stats.total then
|
if stats.done < stats.total then
|
||||||
text:append("", {
|
text:append_extmark({
|
||||||
virt_text_win_col = 2,
|
virt_text_win_col = 2,
|
||||||
virt_text = { { string.rep("━", done_width), "GlazeProgressDone" } },
|
virt_text = { { string.rep("━", done_width), "GlazeProgressDone" } },
|
||||||
})
|
})
|
||||||
text:append("", {
|
text:append_extmark({
|
||||||
virt_text_win_col = 2 + done_width,
|
virt_text_win_col = 2 + done_width,
|
||||||
virt_text = { { string.rep("━", width - done_width), "GlazeProgressTodo" } },
|
virt_text = { { string.rep("━", width - done_width), "GlazeProgressTodo" } },
|
||||||
})
|
})
|
||||||
|
else
|
||||||
|
text:append_extmark({
|
||||||
|
virt_text_win_col = 2,
|
||||||
|
virt_text = { { string.rep("━", width), "GlazeProgressDone" } },
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -208,6 +281,9 @@ function M._render_task(text, task, icons)
|
|||||||
icon, icon_hl = icons.pending, "GlazePending"
|
icon, icon_hl = icons.pending, "GlazePending"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local line_num = text:row()
|
||||||
|
M._line_map[line_num] = task.binary.name
|
||||||
|
|
||||||
text:append(" " .. icon .. " ", icon_hl)
|
text:append(" " .. icon .. " ", icon_hl)
|
||||||
text:append(task.binary.name, "GlazeBinary")
|
text:append(task.binary.name, "GlazeBinary")
|
||||||
|
|
||||||
@@ -230,14 +306,18 @@ end
|
|||||||
---@param text GlazeText
|
---@param text GlazeText
|
||||||
---@param binary GlazeBinary
|
---@param binary GlazeBinary
|
||||||
---@param icons GlazeIcons
|
---@param icons GlazeIcons
|
||||||
|
---@param update_info? table<string, GlazeUpdateInfo>
|
||||||
---@private
|
---@private
|
||||||
function M._render_binary(text, binary, icons)
|
function M._render_binary(text, binary, icons, update_info)
|
||||||
local glaze = require("glaze")
|
local glaze = require("glaze")
|
||||||
local installed = glaze.is_installed(binary.name)
|
local installed = glaze.is_installed(binary.name)
|
||||||
|
|
||||||
local icon = installed and icons.done or icons.pending
|
local icon = installed and icons.done or icons.pending
|
||||||
local icon_hl = installed and "GlazeIconDone" or "GlazePending"
|
local icon_hl = installed and "GlazeIconDone" or "GlazePending"
|
||||||
|
|
||||||
|
local line_num = text:row()
|
||||||
|
M._line_map[line_num] = binary.name
|
||||||
|
|
||||||
text:append(" " .. icon .. " ", icon_hl)
|
text:append(" " .. icon .. " ", icon_hl)
|
||||||
text:append(binary.name, "GlazeBinary")
|
text:append(binary.name, "GlazeBinary")
|
||||||
|
|
||||||
@@ -245,11 +325,54 @@ function M._render_binary(text, binary, icons)
|
|||||||
text:append(" (" .. binary.plugin .. ")", "GlazePlugin")
|
text:append(" (" .. binary.plugin .. ")", "GlazePlugin")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Show update available indicator
|
||||||
|
if update_info and update_info[binary.name] then
|
||||||
|
local info = update_info[binary.name]
|
||||||
|
if info.has_update then
|
||||||
|
text:append(" ⬆", "GlazeRunning")
|
||||||
|
if info.installed_version and info.latest_version then
|
||||||
|
text:append(" " .. info.installed_version .. " → " .. info.latest_version, "GlazeTime")
|
||||||
|
end
|
||||||
|
elseif info.installed_version then
|
||||||
|
text:append(" ✓ " .. info.installed_version, "GlazeVersion")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
text:nl()
|
text:nl()
|
||||||
text:append(binary.url, "GlazeUrl", { indent = 6 }):nl()
|
|
||||||
|
-- Expanded details
|
||||||
|
if M._expanded[binary.name] then
|
||||||
|
text:append("URL: ", "GlazeComment", { indent = 6 })
|
||||||
|
text:append(binary.url, "GlazeUrl"):nl()
|
||||||
|
|
||||||
|
local bin_path = glaze.bin_path(binary.name)
|
||||||
|
if bin_path then
|
||||||
|
text:append("Path: ", "GlazeComment", { indent = 6 })
|
||||||
|
text:append(bin_path, "GlazeUrl"):nl()
|
||||||
|
end
|
||||||
|
|
||||||
|
if binary.plugin then
|
||||||
|
text:append("Plugin: ", "GlazeComment", { indent = 6 })
|
||||||
|
text:append(binary.plugin, "GlazePlugin"):nl()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Show last error output from tasks
|
||||||
|
local runner = require("glaze.runner")
|
||||||
|
for _, task in ipairs(runner.tasks()) do
|
||||||
|
if task.binary.name == binary.name and task.status == "error" and #task.output > 0 then
|
||||||
|
text:append("Error: ", "GlazeError", { indent = 6 }):nl()
|
||||||
|
for _, line in ipairs(task.output) do
|
||||||
|
text:append(line, "GlazeError", { indent = 8 }):nl()
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
text:nl()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Close the UI.
|
---Close the UI and clean up timer.
|
||||||
function M.close()
|
function M.close()
|
||||||
if M._timer then
|
if M._timer then
|
||||||
vim.fn.timer_stop(M._timer)
|
vim.fn.timer_stop(M._timer)
|
||||||
|
|||||||
Reference in New Issue
Block a user