Skip to content
Open
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
117 changes: 110 additions & 7 deletions lua/crates/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ local M = {
---@field diagnostics vim.Diagnostic[]
---@field search_transaction SearchTransaction?
---@field line_state table<integer,LineState>
---@field loading_timer uv.uv_timer_t?
---@field loading_frame integer
---@field loading_lines table<integer,boolean>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can get rid of the separate table of loading lines, and instead just use the line_state and check if it's LOADING OR SEARCHING.


---@class SearchTransaction
---@field id number
Expand All @@ -25,13 +28,28 @@ local LineState = {
UPDATE = 3,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed the UPDATE state is and was never used, so could you also remove it?

}

local LOADING_FRAMES = {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can become the default frames in the config.

"⠋",
"⠙",
"⠹",
"⠸",
"⠼",
"⠴",
"⠦",
"⠧",
"⠇",
"⠏",
}

---@param buf integer
---@return BufUiState
function M.get_or_init(buf)
local buf_state = M.state[buf] or {
custom_diagnostics = {},
diagnostics = {},
line_state = {},
loading_frame = 1,
loading_lines = {},
}
M.state[buf] = buf_state
return buf_state
Expand All @@ -40,8 +58,83 @@ end
---@type integer
local CUSTOM_NS = vim.api.nvim_create_namespace("crates.nvim")
---@type integer
local LOADING_NS = vim.api.nvim_create_namespace("crates.nvim.loading")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the mentioned changes I'd rename this to SPINNER_NS

---@type integer
local DIAGNOSTIC_NS = vim.api.nvim_create_namespace("crates.nvim.diagnostic")

---@param text string
---@param frame string
---@return string
local function loading_text(text, frame)
local replaced = text:gsub("^(%s*)%S+", "%1" .. frame, 1)
return replaced
end

---@param buf_state BufUiState
local function stop_loading_timer(buf_state)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would become stop_spin_timer

if buf_state.loading_timer then
buf_state.loading_timer:stop()
buf_state.loading_timer:close()
buf_state.loading_timer = nil
end
end

---@param buf integer
---@param buf_state BufUiState
---@param line integer
local function hide_loading_indicator(buf, buf_state, line)
if buf_state.line_state[line] == LineState.LOADING then
buf_state.line_state[line] = nil
buf_state.loading_lines[line] = nil
vim.api.nvim_buf_clear_namespace(buf, LOADING_NS, line, line + 1)
end
end

---@param buf integer
---@param buf_state BufUiState
local function render_loading(buf, buf_state)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would become render_spinners

if not next(buf_state.loading_lines) then
stop_loading_timer(buf_state)
return
end

local frame = LOADING_FRAMES[buf_state.loading_frame] or LOADING_FRAMES[1]
buf_state.loading_frame = (buf_state.loading_frame % #LOADING_FRAMES) + 1

vim.api.nvim_buf_clear_namespace(buf, LOADING_NS, 0, -1)
for line, _ in pairs(buf_state.loading_lines) do
vim.api.nvim_buf_set_extmark(buf, LOADING_NS, line, -1, {
virt_text = { { loading_text(state.cfg.text.loading, frame), state.cfg.highlight.loading } },
virt_text_pos = "eol",
hl_mode = "combine",
})
end
end

---@param buf integer
---@param buf_state BufUiState
local function ensure_loading_timer(buf, buf_state)
if buf_state.loading_timer then
return
end

local timer = assert(vim.loop.new_timer())
buf_state.loading_timer = timer
timer:start(120, 120, vim.schedule_wrap(function()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make the update rate configurable?
I think a new section called loading with the following fields would be nice.

  • show_indicator: boolean
  • frames: {string}
  • rate: integer // 0 should be handled specially and not start the timer at all

The text.searching and text.loading strings should be changed to include a %s format specifier where the loading spinner will be inserted.

The loading_indicator options should be deprecated like this in config/init.lua

deprecated = {
    new_field = { "loading", "show_indicator" },
},

local current = M.state[buf]
if not current or current.loading_timer ~= timer then
return
end

if not state.visible or not vim.api.nvim_buf_is_loaded(buf) or not next(current.loading_lines) then
stop_loading_timer(current)
return
end

render_loading(buf, current)
end))
end

---@param buf integer
---@param d CratesDiagnostic
---@return vim.Diagnostic
Expand Down Expand Up @@ -89,7 +182,10 @@ function M.display_crate_info(buf, infos)
return
end

local buf_state = M.get_or_init(buf)
for _, info in ipairs(infos) do
hide_loading_indicator(buf, buf_state, info.vers_line)

local virt_text = {}
if info.vers_match then
table.insert(virt_text, {
Expand Down Expand Up @@ -123,6 +219,10 @@ function M.display_crate_info(buf, infos)
hl_mode = "combine",
})
end

if not next(buf_state.loading_lines) then
stop_loading_timer(buf_state)
end
end

---@param buf integer
Expand All @@ -137,14 +237,11 @@ function M.display_loading(buf, crates)
for _, crate in ipairs(crates) do
local vers_line = crate.vers and crate.vers.line or crate.lines.s
buf_state.line_state[vers_line] = LineState.LOADING

local virt_text = { { state.cfg.text.loading, state.cfg.highlight.loading } }
vim.api.nvim_buf_set_extmark(buf, CUSTOM_NS, vers_line, -1, {
virt_text = virt_text,
virt_text_pos = "eol",
hl_mode = "combine",
})
buf_state.loading_lines[vers_line] = true
end

render_loading(buf, buf_state)
ensure_loading_timer(buf, buf_state)
end

---@param buf integer
Expand Down Expand Up @@ -214,9 +311,15 @@ end

---@param buf integer
function M.clear(buf)
local buf_state = M.state[buf]
if buf_state then
stop_loading_timer(buf_state)
end

M.state[buf] = nil

vim.api.nvim_buf_clear_namespace(buf, CUSTOM_NS, 0, -1)
vim.api.nvim_buf_clear_namespace(buf, LOADING_NS, 0, -1)
vim.diagnostic.reset(CUSTOM_NS, buf)
vim.diagnostic.reset(DIAGNOSTIC_NS, buf)
end
Expand Down