1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
local time = require("99.time")
local Consts = require("99.consts")
local throb_icons = {
{ "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" },
{ "◐", "◓", "◑", "◒" },
{ "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷" },
{ "◰", "◳", "◲", "◱" },
{ "◜", "◠", "◝", "◞", "◡", "◟" },
}
--- @alias _99.Throbber.ThrobFN fun(perc: number): string
--- @alias _99.Throbber.EaseFN fun(perc: number): number
--- @param ease_fn _99.Throbber.EaseFN
--- @return _99.Throbber.ThrobFN
local function create_throbber(ease_fn)
ease_fn = ease_fn or function(p)
return p
end
local icon_set = throb_icons[math.random(#throb_icons)]
return function(percent)
local eased = ease_fn(percent)
local index = math.floor(eased * #icon_set) + 1
return icon_set[math.min(index, #icon_set)]
end
end
--- @param percent number
--- @return number
--- @diagnostic disable-next-line
local function linear(percent)
return percent
end
--- @param percent number
--- @return number
--- @diagnostic disable-next-line
local function ease_in_ease_out_quadratic(percent)
if percent < 0.5 then
return 2 * percent * percent
else
local f = percent - 1
return 1 - 2 * f * f
end
end
--- @param percent number
--- @return number
--- @diagnostic disable-next-line
local function ease_in_ease_out_cubic(percent)
if percent < 0.5 then
return 4 * percent * percent * percent
else
local f = (2 * percent) - 2
return 1 - (f * f * f / 2)
end
end
--- @class _99.Throbber
--- @field start_time number
--- @field section_time number
--- @field state "init" | "throbbing" | "cooldown" | "stopped"
--- @field throb_fn _99.Throbber.ThrobFN
--- @field opts _99.Throbber.Opts
--- @field cb fun(str: string): nil
local Throbber = {}
Throbber.__index = Throbber
--- @class _99.Throbber.Opts
--- @field throb_time number
--- @field cooldown_time number
--- @field tick_time number
--- @param cb fun(str: string): nil
--- @param opts _99.Throbber.Opts?
--- @return _99.Throbber
function Throbber.new(cb, opts)
opts = opts or {}
opts.throb_time = opts.throb_time or Consts.throbber_throb_time
opts.cooldown_time = opts.cooldown_time or Consts.throbber_cooldown_time
opts.tick_time = opts.tick_time or Consts.throbber_tick_time
return setmetatable({
state = "init",
start_time = 0,
section_time = 0,
opts = opts,
cb = cb,
throb_fn = create_throbber(linear),
}, Throbber)
end
function Throbber:_run()
if self.state ~= "throbbing" and self.state ~= "cooldown" then
return
end
local elapsed = time.now() - self.start_time
local percent = math.min(1, elapsed / self.section_time)
local icon = self.throb_fn(self.state == "throbbing" and percent or 1)
if percent == 1 then
self.state = self.state == "cooldown" and "throbbing" or "cooldown"
self.start_time = time.now()
self.section_time = self.state == "cooldown" and self.opts.cooldown_time
or self.opts.throb_time
end
self.cb(icon)
vim.defer_fn(function()
self:_run()
end, self.opts.tick_time)
end
function Throbber:start()
self.start_time = time.now()
self.section_time = self.opts.throb_time
self.state = "throbbing"
self:_run()
end
function Throbber:stop()
self.state = "stopped"
end
Throbber._icons = throb_icons
return Throbber
|