Skip to content

Extensible spinner framework for Neovim plugins and UI.

License

Notifications You must be signed in to change notification settings

xieyonn/spinner.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

178 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

spinner.nvim

Extensible spinner framework for Neovim plugins and UI.

coverage release LuaRocks Requires Neovim 0.11+ license

Features

  • Multiple UI locations:
    • Pre-defined statusline, tabline, winbar, cursor, extmark, cmdline, window-title and window-footer.
    • Any place you can render a text. see Extend
  • LSP integration: show spinners for LspProgress and LspRequest.
  • Extensible API: start / stop / pause a spinner in your plugins and configurations.
  • Configurable spinner patterns: Built-in presets with 70+ patterns

Getting Started

Neovim's UI components do NOT refresh automatically.

So there are 2 ways to make the spinner animate:

  • Passive Refresh: Insert a function into the UI component, then refreshes this UI component at regular intervals. eg: statusline/tabline/winbar spinner.
  • Active Refresh: Periodically call the API to update the text content of the UI component. eg: cursor/extmark spinner.

spinner.nvim manages the internal state of the spinner and determines when to refresh the UI. Each spinner is identified by a unique id, with option kind to indicate how spinner.nvim refresh the UI.

kind type refresh method
statusline passive vim.cmd("redrawstatus)
tabline passive vim.cmd("redrawtabline)
winbar passive vim.cmd("redrawstatus)
extmark active vim.api.nvim_buf_set_extmarks()
cursor active vim.api.nvim_win_open() + vim.api.nvim_buf_set_lines()
cmdline active vim.cmd("echo 'text'")
window-title active vim.api.nvim_win_set_config()
window-footer active vim.api.nvim_win_set_config()
custom you tell you tell how, see Extend
local spinner = require("spinner")

-- Setup a spinner with a unique id.
spinner.config("id", opts)

-- Control spinner as need.
spinner.start("id") -- Start a spinner
spinner.stop("id") -- Stop a spinner
spinner.pause("id") -- Pause a spinner
spinner.reset("id") -- Reset a spinner
spinner.fail("id") -- Fail a spinner (stop & mark as failed)

Installation

Using lazy.nvim:

{
  "xieyonn/spinner.nvim",
  config = function()
    ---@type spinner
    local sp = require("spinner")

    -- NO need to call setup() if you are fine with defaults.
    sp.setup()
  end
}

Using rocks.nvim

:Rocks install spinner.nvim

Preview

Type in command line:

:= require("spinner.demo").open()

Will open a window displaying all built-in spinners.

Setup

Setup defaults for spinners.

require("spinner").setup({
  -- Pre-defined pattern key name in
  -- https://github.com/xieyonn/spinner.nvim/blob/main/lua/spinner/pattern.lua
  -- or be a table { intervals = 80, frames = { "1", "2" } }
  pattern = "dots",

  -- Time-to-live in milliseconds since the most recent start, after which the
  -- spinner stops, preventing it from running indefinitely.
  ttl_ms = 0,

  -- Milliseconds to wait after startup before showing the spinner.
  -- This helps prevent the spinner from briefly flashing for short-lived tasks.
  initial_delay_ms = 0,

  -- Text displayed when the spinner is idle/stoped.
  --
  -- true: show an empty string, with length equal to spinner frames.
  -- false: equals to "".
  -- or string value, eg: show âś” when lsp progress finished.
  -- or a table value, eg: { init = "", stopped = "", failed = "" }
  placeholder = false,

  -- Highlight group for text, use fg of `Comment` by default.
  -- or be a table { init = "", running = "", paused = "", stopped = "", failed = ""}
  hl_group = "Spinner",

  cursor_spinner = {
    -- CursorSpinner window option.
    winblend = 60,

    -- CursorSpinner window option.
    zindex = 50,

    -- CursorSpinner window position, relative to cursor.
    -- row = -1 col = 1 means Above-Right.
    row = -1,
    col = 1,
  }
})

Example

Lsp Spinner

  • Display lsp client name (lua_ls) and lsp progress in statusline.
  • Display spinner right above cursor when press K (lsp_hover)
Click to expand
  1. setup a statusline spinner with id lsp_progress and attach to LspProgress
require("spinner").config("lsp_progress", {
  kind = "statusline", -- spinner kind.
  placeholder = "âś”", -- a nice symbol to indicate lsp is ready.
  attach = {
    lsp = {
      progress = true, -- attach to LspProgress event.
    },
  },
})
  1. create a global function to concat lsp client names and render spinner text.

if you setup statusline by vim.o.statusline, you need a global function. or if you use a statusline plugin, you can set this function local.

function lsp_progress()
  local client_names = {}
  local seen = {}

  -- need to remove duplicate because somehow a buffer may attached to multiple
  -- clients with same name.
  for _, client in ipairs(vim.lsp.get_clients({ bufnr = 0 })) do
    local name = client and client.name or ""
    if name ~= "" and not seen[name] then
      table.insert(client_names, name)
      seen[name] = true
    end
  end
  -- if no active lsp clients, leave it empty
  if #client_names == 0 then
    return ""
  end

  local spinner = require("spinner").render("lsp_progress")

  return table.concat(client_names, " ") .. " " .. spinner
end
  1. setup statusline
vim.o.statusline = vim.o.statusline .. "%{v:lua.lsp_progress()%}"
  1. setup a cursor spinner with id cursor and attach to LspRequest
require("spinner").config("cursor", {
  kind = "cursor", -- kind cursor
  attach = {
    lsp = {
      request = {
        -- select the methods you're interested in. For a complete list: `:h lsp-method`
        -- "textDocument/definition", -- for GoToDefinition (shortcut `C-]`)
        "textDocument/hover", -- for hover (shortcut `K`)
      },
    },
  },
})

Task List

  1. Display a TODO like list in buffer.
  2. Tracking list status.
Click to expand
local sp = require("spinner")

-- 1. Create a scratch buffer
local bufnr = vim.api.nvim_create_buf(false, true)
vim.bo[bufnr].buftype = "nofile"
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].swapfile = false
vim.bo[bufnr].undofile = false

-- 2. Open a window
local width = math.floor(vim.o.columns * 0.4)
local height = math.floor(vim.o.lines * 0.6)
local row = math.floor((vim.o.lines - height) / 2)
local col = math.floor((vim.o.columns - width) / 2)
local win = vim.api.nvim_open_win(bufnr, true, {
  relative = "editor",
  row = row,
  col = col,
  width = width,
  height = 8,
  style = "minimal",
  focusable = true,
  border = "rounded",
  noautocmd = false,
})

-- 3. close window on q or esc
for _, key in ipairs({ "q", "<Esc>" }) do
  vim.keymap.set("n", key, function()
    if vim.api.nvim_win_is_valid(win) then
      vim.api.nvim_win_close(win, true)
    end
  end, {
    buffer = bufnr,
    nowait = true,
    silent = true,
  })
end

-- 4. render a list
local todolist = {
  "   Item 1",
  "   Item 2",
  "   Item 3",
  "   Item 4",
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, todolist)
for i = 1, #todolist do
  sp.config("todo" .. i, {
    kind = "extmark",
    pattern = "blink",
    placeholder = "â—Ź",
    bufnr = bufnr,
    row = i - 1,
    col = 1,
    ttl_ms = 4000,
    hl_group = {
      init = "white", -- init hl_group
      stopped = "green", -- stopped hl_group
    },
    virt_text_pos = "overlay",
  })
end

-- 5. start spinner
for i = 1, #todolist do
  vim.defer_fn(function()
    sp.start("todo" .. i)
  end, i * 1000)
end

Usage

Statusline

  1. Configure a statusline spinner with id my_spinner.
require("spinner").config("my_spinner", {
  kind = "statusline",
})
  1. Set vim.o.statusline.
-- Need a global function here.
function my_spinner()
  return require("spinner").render("my_spinner")
end

vim.o.statusline = vim.o.statusline .. "%{v:lua.my_spinner()%}"
  1. Start/stop/pause the spinner where needed.
-- start spinner
require("spinner").start("my_spinner")

-- stop spinner
require("spinner").stop("my_spinner")

-- spinner.nvim internally tracks the number of start/stop calls for the same
-- spinner. It only stops spinning when the number of stop calls >= start calls.
-- use a second param `true` to force stop a spinner.
require("spinner").stop("my_spinner", true)

-- pause a spinner
require("spinner").pause("my_spinner")

By default, spinner.nvim refreshes the statusline using vim.cmd("redrawstatus"). If you use a statusline plugin, it may take over the refresh mechanism, causing the spinner to behave incorrectly due to refresh/frame-rate issues.

You can use the on_update_ui option to let your statusline plugin handle updates properly.

eg: if you use lualine.nvim

require("spinner").config("my_spinner", {
  kind = "statusline",
  on_update_ui = function()
    require("lualine").refresh() -- use lualine's refresh method
  end,
})

lualine.nvim is auto-detect and support by default.

Tabline

  1. Configure a tabline spinner with id my_spinner.
require("spinner").config("my_spinner", {
  kind = "tabline",
})
  1. Set vim.o.tabline.
-- this function need to be a global function
function my_spinner()
  return require("spinner").render("my_spinner")
end

vim.o.tabline = vim.o.tabline .. "%{v:lua.my_spinner()%}"

spinner.nvim use vim.cmd("redrawtabline") to refresh tabline. if you use a plugin to setup tabline, you may need to provide a on_update_ui option to refresh tabline. See Statusline.

Winbar

  1. Configure a winbar spinner with id my_spinner.
require("spinner").config("my_spinner", {
  kind = "winbar",
})
  1. Set vim.o.winbar.
-- this function need to be a global function
function my_spinner()
  return require("spinner").render("my_spinner")
end

vim.o.winbar = vim.o.winbar .. "%{v:lua.my_spinner()%}"

spinner.nvim use vim.cmd("redrawstatus") to refresh winbar. if you use a plugin to setup winbar, you may need to provide a on_update_ui option to refresh tabline. See Statusline.

Cursor

spinner.nvim use a float window relative to cursor to displaying spinner. create the float window when spinner start/pause, close the float window when stop.

If you want to show multiple cursor spinners, be careful with id.

Configure a cursor spinner with id.

require("spinner").config("cursor", {
  kind = "cursor",

  -- CursorSpinner window option.
  winblend = 60, -- optional

  -- CursorSpinner window option.
  zindex = 50, --optional

  -- CursorSpinner window position, relative to cursor.
  row = -1, --optional

  -- CursorSpinner window position, relative to cursor.
  col = 1, --optional
})

row and col means the position relative to cursor.

  • { row = -1, col = 1 } Above-Right
  • { row = -1, col = -1 } Above-Left
  • { row = 1, col = 1 } Below-Right
  • { row = 1, col = -1 } Below-Left

Extmark

spinner.nvim uses Neovim extmarks (see h: extmark) to attach spinners to buffer positions.

Extmarks automatically track positions as the text changes, ensuring the spinner stays correctly aligned even when you edit the buffer, like diagnostic messages.

If you want to show multiple spinners at the same time, be careful with the spinner id.

Configure an extmark spinner with id.

local bufnr, row, col
local id = string.format("extmark-spinner-%d-%d-%d", bufnr, row, col)
require("spinner").config(id, {
  kind = "extmark",
  bufnr = bufnr, -- must be provided
  row = row, -- must be provided, which line, 0-based
  col = col, -- must be provided, which col, 0-based

  ns = 0, -- namespace, optional
  virt_text_pos = "eol" -- options for `vim.api.nvim_buf_set_extmark`, optional
  virt_text_win_col = nil -- options for `vim.api.nvim_buf_set_extmarks`, optional
})

virt_text_pos and virt_text_win_col determine spinner position.

virt_text_pos virt_text_win_col Description
overlay nil Draws at the col anchor
overlay integer Draws at the specified window column, ignoring col
eol nil Draws at the end of the line
eol integer virt_text_win_col is ignored
right_align nil Aligns text to the right of the window
right_align integer virt_text_win_col is ignored
inline nil Inserts at the specified col
inline integer virt_text_win_col is ignored

See h: nvim_buf_set_extmarks().

Cmdline

Configure a cmdline spinner with id my_spinner.

require("spinner").config("my_spinner", {
  kind = "cmdline"

  fmt = function(event)
    local text = event.text
    return text .. " Loading .."
  end
})

A preview

Window Title

Configure a window-title spinner:

local win = nil -- target win
local id = string.format("window-title:%d", win)
require("spinner").config(id, {
  kind = "window-title",
  win = win, -- target win, must provided

  pos = "center", -- optional, can be "left", "center", "right", default is "center"

  -- optional, use fmt function to add extra text.
  fmt = function(event)
    local text = event.text
    return text .. " This is a title"
  end,
})

Spinner will stop when window close.

A preview:

Window Footer

Configure a window-footer spinner:

local win = nil -- target win
local id = string.format("window-footer:%d", win)
require("spinner").config(id, {
  kind = "window-footer",
  win = win, -- target win, must provided

  pos = "center", -- optional, can be "left", "center", "right", default is "center"

  -- optional, use fmt function to add extra text.
  fmt = function(event)
    local text = event.text
    return text .. " This is a footer"
  end,
})

Spinner will stop when window close.

A preview:

Extend

spinner.nvim decides when to refresh the UI, you decide where and how.

Extend a spinner using the on_update_ui option, which is a function that gets called every time the UI needs to be refreshed, including:

  • init: when config/reset
  • running: When spinning
  • pause: When paused
  • stop: When stopped
  • fail: When failed

if spinner has initial_delay_ms option, only refresh ui when timeout expires.

local id = "my_spinner"
require("spinner").config(id, {
  kind = "custom",

  -- must provide, called when refresh UI.
  on_update_ui = function(event)
    local status = event.status -- current status
    local text = event.text -- current text (including placeholder)
    local hl_group = event.hl_group -- current hl_group if provided

    -- do what you want
  end,

  -- optional, used to improve performance, take spinner id by default
  ui_scope = id,
})

Option ui_scope defines the scope for batching UI updates.

Spinners with the same ui_scope will have their UI updates combined within a short period of time to improve performance.

  • All statusline spinners share the statusline scope and update together.
  • All tabline spinners share the tabline scope and update together.
  • Custom spinners with the same ui_scope will update together.

Here is a example shows how to display spinner in a window title. (only float window can set title)

local bufnr = vim.api.nvim_create_buf(false, true)
local win = vim.api.nvim_open_win(bufnr, true, {})
local spinner_id = string.format("win-%d", win)
require("spinner").config(spinner_id, {
  kind = "custom",
  ui_scope = spinner_id, -- Tell spinner.nvim not to merge refreshes with other spinners.
  on_update_ui = function(event)
    if not (win and vim.api.nvim_win_is_valid(win)) then
      return
    end

    vim.api.nvim_win_set_config(win, {
      title = event.text,
      title_pos = "center",
    })
  end,
})

-- start spinner
require("spinner").start(spinner_id)

This is how the built-in window-title spinner is implemented.

Options

Lsp Integration

Configure spinner with attach option to make it listen to LspProgress or LspRequest.

require("spinner").config("cursor", {
  kind = "cursor",
  attach = {
    lsp = {
      progress = true, -- listen to LspProgress

      request = {
        -- Select the methods you're interested in. For a complete list: `:h lsp-method`
        -- "textDocument/definition", -- for GoToDefinition (shortcut `C-]`)
        "textDocument/hover", -- for hover (shortcut `K`)
      },

      -- Optional, select lsp names you're interested in.
      client_names = {
        "lua_ls",
      }
    },
  },
})

Pattern

spinner.nvim provides a large number of built-in patterns. See patterns

Or you can set up a custom pattern:

require("spinner").config("my_spinner", {
  kind = "statusline",
  pattern = {
    interval = 80, -- in milliseconds
    frames = { "â ‹", "â ™", "â ą", "â ¸", "â Ľ", "â ´", "â ¦", "â §", "â ‡", "â Ź" },
  },
})

TTL

Tasks may fail or abort unexpectedly, causing the stop signal to never be received. You can set a ttl_ms to prevent the spinner from spinning indefinitely.

require("spinner").config("my_spinner", {
  kind = "statusline",
  ttl_ms = 10000, -- 10 seconds
})

Initial Delay

Some asynchronous tasks are too short: the spinner stops shortly after starting, causing flickering. Use initial_delay_ms to delay the spinner startup and avoid flickering.

require("spinner").config("my_spinner", {
  kind = "statusline",
  initial_delay_ms = 200, -- in milliseconds
})

Placeholder

Set a default string to display when the spinner is idle (init/stopped).

require("spinner").config("my_spinner", {
  kind = "statusline",

  -- eg: show âś” when lsp progress is ready.
  placeholder = "âś”",
})
  • placeholder = true render an empty string with length == len(frames[1]).
  • placeholder = false disable placeholder.
  • placeholder = "" equals to false.

You can also set it to a table to control the placeholder displayed during initialization and stop of the spinner.

require("spinner").config("my_spinner", {
  kind = "statusline"

  -- eg: show âś” when lsp progress is ready.
  placeholder = {
    init = " ", -- show empty if not started.
    stopped = "âś”", -- show âś” when success.
    failed = "âś—" -- show âś— when failed.
  },
})

Formatting

You can customize the spinner text format using the fmt option. This function receives the current text and status and returns the formatted string.

require("spinner").config("my_spinner", {
  kind = "statusline",
  fmt = function(event)
    local text = event.text
    -- local status = event.status
    -- local hl_group = event.hl_group
    return "[" .. text .. "]"
  end
})

event.text will contains hl_group ONLY in statusline/tabline/winbar/cmdline spinner. You are free to add other hl_group for text in other spinners.

Highlight

You can customize the spinner text color by using the hl_group option.

require("spinner").config("my_spinner", {
  kind = "statusline",
  hl_group = "Spinner"
})

Or set a global default value in setup()

require("spinner").setup({
  hl_group = "Spinner",
})

You can set it to a table to display different colors for different spinner states.

require("spinner").config("my_spinner", {
  kind = "statusline",
  hl_group = {
    init = "", -- after spinner config
    running = "", -- running
    paused = "", -- paused
    stopped = "", -- stopped
    failed = "", -- failed
  }
})

eg: show a gree âś” to indicate lsp is ready.

statusline / tabline / winbar will wrap text in format %#HL_GROUP#...%* and requires explicit setting in config() rather then setup() for backward compatibility

Commands

spinner.nvim provides a command Spinner:

:Spinner start my_spinner    " Start a spinner
:Spinner stop my_spinner     " Stop a spinner
:Spinner pause my_spinner    " Pause a spinner
:Spinner reset my_spinner    " Reset a spinner
:Spinner fail my_spinner     " Fail a spinner

With tab completion for spinner IDs.

Use it to test your configuration.

API Reference

Click to expand
---@class spinner
---@field start fun(id: string) -- Start spinner.
---@field stop fun(id: string, force?: boolean) -- Stop spinner.
---@field pause fun(id: string) -- Pause spinner.
---@field config fun(id: string, opts?: spinner.Opts) -- Setup spinner.
---@field render fun(id: string): string -- Render spinner.
---@field reset fun(id: string) -- Reset spinner.
---@field fail fun(id: string) -- Fail spinner, stop & mark status as failed.
---@field setup fun(opts?: spinner.Config) -- Setup global configuration.

---@alias spinner.UIScope -- Used to combine UI updates.
---| "statusline"
---| "tabline"
---| "cursor"
---| string

---@alias spinner.Kind
---| "custom" -- Custom UI kind
---| "statusline" -- Statusline spinner
---| "tabline" -- Tabline spinner
---| "winbar" -- Winbar spinner
---| "cursor" -- Cursor spinner
---| "extmark" -- Extmark spinner
---| "cmdline" -- CommandLine spinner
---| "window-title" -- WindowTitle spinner
---| "window-footer" -- WindowFooter spinner

---@alias spinner.Opts
---| spinner.CoreOpts -- Core options
---| spinner.CustomOpts -- Custom options
---| spinner.StatuslineOpts -- Statusline options
---| spinner.TablineOpts -- Tabline options
---| spinner.WinbarOpts -- Winbar options
---| spinner.CursorOpts -- Cursor options
---| spinner.ExtmarkOpts -- Extmark options
---| spinner.CmdlineOpts -- CommandLine options
---| spinner.WindowTitleOpts -- WindowTitle options
---| spinner.WindowFooterOpts -- WindowFooter options
---
---@class spinner.CoreOpts
---@field kind? spinner.Kind -- Spinner kind
---@field pattern? string|spinner.Pattern -- Animation pattern
---@field ttl_ms? integer -- Time to live in ms
---@field initial_delay_ms? integer -- Initial delay in ms
---@field placeholder? string|boolean|spinner.Placeholder -- Placeholder text
---@field attach? spinner.Event -- Event attachment
---@field hl_group? string|spinner.HighlightGroup
---@field on_update_ui? fun(event: spinner.OnChangeEvent) -- UI update callback
---@field ui_scope? string custom ui_scope, used to improve UI refresh performance
---@field fmt? fun(event: spinner.OnChangeEvent): string -- Format function
---
---@class spinner.Placeholder
---@field init? string -- when status == init (new create)
---@field stopped? string -- when status == stopped
---@field failed? string -- when status == failed
---
---@class spinner.StatuslineOpts: spinner.CoreOpts
---@field kind "statusline" -- Statusline kind
---
---@class spinner.TablineOpts: spinner.CoreOpts
---@field kind "tabline" -- Tabline kind
---
---@class spinner.WinbarOpts: spinner.CoreOpts
---@field kind "winbar" -- Winbar kind
---
---@class spinner.CursorOpts: spinner.CoreOpts
---@field kind "cursor" -- Cursor kind
---@field row? integer -- Position relative to cursor
---@field col? integer -- Position relative to cursor
---@field zindex? integer -- Z-index
---@field winblend? integer -- Window blend
---
---@class spinner.HighlightGroup
---@field init? string -- used in init status
---@field running? string -- used in running status
---@field paused? string -- used in paused status
---@field stopped? string -- used in stopped status
---@field failed? string -- used in failed status
---
---@class spinner.ExtmarkOpts: spinner.CoreOpts
---@field kind "extmark" -- Extmark kind
---@field bufnr integer -- Buffer number
---@field row integer -- Line position 0-based
---@field col integer -- Column position 0-based
---@field ns? integer -- Namespace
---@field virt_text_pos? string -- options for vim.api.nvim_buf_set_extmark
---@field virt_text_win_col? integer -- options for `vim.api.nvim_buf_set_extmarks`
---
---@class spinner.WindowTitleOpts: spinner.CoreOpts
---@field kind "window-title"
---@field win integer -- target win id
---@field pos? string -- position, can be on of "left", "center" or "right"
---
---@class spinner.WindowFooterOpts: spinner.CoreOpts
---@field kind "window-footer"
---@field win integer -- target win id
---@field pos? string -- position, can be on of "left", "center" or "right"
---
---@class spinner.CmdlineOpts: spinner.CoreOpts
---@field kind "cmdline" -- CommandLine kind
---
---@class spinner.CustomOpts: spinner.CoreOpts
---@field kind "custom"
---@field on_update_ui fun(event: spinner.OnChangeEvent) -- UI update callback
---@field ui_scope? string custom ui_scope, use spinner id by default
---
---@class spinner.OnChangeEvent
---@field status spinner.Status -- Current status
---@field text string -- Current text
---@field hl_group? string -- Current hl_group

---@enum spinner.Status
local STATUS = {
  INIT = "init", -- Initialized but not started status
  DELAYED = "delayed", -- Delayed status
  RUNNING = "running", -- Running status
  PAUSED = "paused", -- Paused status
  STOPPED = "stopped", -- Stopped status
  FAILED = "failed", -- Failed state
}

---@class spinner.Pattern
---@field interval integer -- Animation interval
---@field frames string[] -- Animation frames

---@class spinner.Event
---@field lsp? spinner.Event.Lsp -- LSP event attachment
---
---@class spinner.Event.Lsp
---@field client_names? string[] -- Client names filter
---@field progress? boolean -- Progress event
---@field request? spinner.LspRequest[] -- Request events
---
---@alias spinner.LspRequest
---| 'callHierarchy/incomingCalls'
---| 'callHierarchy/outgoingCalls'
---| 'codeAction/resolve'
---| 'codeLens/resolve'
---| 'completionItem/resolve'
---| 'documentLink/resolve'
---| 'initialize'
---| 'inlayHint/resolve'
---| 'shutdown'
---| 'textDocument/codeAction'
---| 'textDocument/codeLens'
---| 'textDocument/colorPresentation'
---| 'textDocument/completion'
---| 'textDocument/declaration'
---| 'textDocument/definition'
---| 'textDocument/diagnostic'
---| 'textDocument/documentColor'
---| 'textDocument/documentHighlight'
---| 'textDocument/documentLink'
---| 'textDocument/documentSymbol'
---| 'textDocument/foldingRange'
---| 'textDocument/formatting'
---| 'textDocument/hover'
---| 'textDocument/implementation'
---| 'textDocument/inlayHint'
---| 'textDocument/inlineCompletion'
---| 'textDocument/inlineValue'
---| 'textDocument/linkedEditingRange'
---| 'textDocument/moniker'
---| 'textDocument/onTypeFormatting'
---| 'textDocument/prepareCallHierarchy'
---| 'textDocument/prepareRename'
---| 'textDocument/prepareTypeHierarchy'
---| 'textDocument/rangeFormatting'
---| 'textDocument/rangesFormatting'
---| 'textDocument/references'
---| 'textDocument/rename'
---| 'textDocument/selectionRange'
---| 'textDocument/semanticTokens/full'
---| 'textDocument/semanticTokens/full/delta'
---| 'textDocument/semanticTokens/range'
---| 'textDocument/signatureHelp'
---| 'textDocument/typeDefinition'
---| 'textDocument/willSaveWaitUntil'
---| 'typeHierarchy/subtypes'
---| 'typeHierarchy/supertypes'
---| 'workspaceSymbol/resolve'
---| 'workspace/diagnostic'
---| 'workspace/executeCommand'
---| 'workspace/symbol'
---| 'workspace/textDocumentContent'
---| 'workspace/willCreateFiles'
---| 'workspace/willDeleteFiles'
---| 'workspace/willRenameFiles'

---@class spinner.Config
---@field pattern? string|spinner.Pattern -- Default pattern
---@field ttl_ms? integer -- Default TTL
---@field initial_delay_ms? integer -- Default delay
---@field placeholder? string|boolean|spinner.Placeholder -- Placeholder text
---@field hl_group? string|spinner.HighlightGroup
---@field cursor_spinner? spinner.CursorSpinnerConfig -- Default cursor config
---
---@class spinner.CursorSpinnerConfig
---@field winblend? integer -- Default window blend
---@field zindex? integer -- Default z-index
---@field row? integer -- Default row offset 0-based
---@field col? integer -- Default column offset 0-based

Contributing

Please see CONTRIBUTING.md for detailed guidelines on how to contribute to this project.

Thanks

  • cli-spinners Adopted a lot of spinner patterns from there.
  • panvimdoc Use this plugin to generate vim docs.
  • nvim-dap Borrow splitstr function used in cmdline command completion.

About

Extensible spinner framework for Neovim plugins and UI.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •