summaryrefslogtreecommitdiff
path: root/script/progress.lua
blob: e1d6cbebed1ea36f337b3801f2372b4bea599ae7 (plain)
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
local proto = require 'proto.proto'
local util  = require 'utility'
local timer = require "timer"

local nextToken = util.counter()

local m = {}

m.map = {}

---@class progress
local mt = {}
mt.__index     = mt
mt._token      = nil
mt._title      = nil
mt._message    = nil
mt._removed    = false
mt._clock      = 0.0
mt._delay      = 0.0
mt._percentage = 0.0
mt._showed     = false
mt._dirty      = true
mt._updated    = 0.0
mt._onCancel   = nil

---移除进度条
function mt:remove()
    if self._removed then
        return
    end
    self._removed = true
    local token = self._token
    m.map[token] = nil
    if self._showed then
        self._showed = false
        proto.notify('$/progress', {
            token = token,
            value = {
                kind = 'end',
            }
        })
        log.info('Remove progress:', token, self._title)
    end
end

---设置描述
---@param message string # 描述
function mt:setMessage(message)
    if self._message == message then
        return
    end
    self._message = message
    self._dirty   = true
    self:_update()
end

---设置百分比
---@param per number # 百分比(1-100)
function mt:setPercentage(per)
    if self._percentage == per then
        return
    end
    self._percentage = math.floor(per)
    self._dirty      = true
    self:_update()
end

---取消事件
function mt:onCancel(callback)
    self._onCancel = callback
    self:_update()
end

function mt:_update()
    if self._removed then
        return
    end
    if not self._dirty then
        return
    end
    self._dirty = false
    if  not self._showed
    and self._clock + self._delay <= os.clock() then
        self._showed  = true
        self._updated = os.clock()
        proto.request('window/workDoneProgress/create', {
            token = self._token,
        })
        proto.notify('$/progress', {
            token = self._token,
            value = {
                kind        = 'begin',
                title       = self._title,
                cancellable = self._onCancel ~= nil,
                message     = self._message,
                percentage  = self._percentage,
            }
        })
        log.info('Create progress:', self._token, self._title)
        return
    end
    if not self._showed then
        return
    end
    if os.clock() - self._updated < 0.05 then
        return
    end
    self._updated = os.clock()
    proto.notify('$/progress', {
        token = self._token,
        value = {
            kind        = 'report',
            message     = self._message,
            percentage  = self._percentage,
        }
    })
    log.info('Report progress:', self._token, self._title, self._message, self._percentage)
end

function mt:__close()
    log.info('Close progress:', self._token, self._message)
    self:remove()
end

function m.update()
    ---@param prog progress
    for _, prog in pairs(m.map) do
        if prog._removed then
            goto CONTINUE
        end
        prog:_update()
        ::CONTINUE::
    end
end

---创建一个进度条
---@param title string # 标题
---@param delay number # 至少经过这么久之后才会显示出来
function m.create(title, delay)
    local prog = setmetatable({
        _token = nextToken(),
        _title = title,
        _clock = os.clock(),
        _delay = delay,
    }, mt)

    m.map[prog._token] = prog

    return prog
end

---取消一个进度条
function m.cancel(token)
    local prog = m.map[token]
    if not prog then
        return
    end
    xpcall(prog._onCancel, log.error, prog)
    prog:remove()
end

timer.loop(0.1, m.update)

return m