summaryrefslogtreecommitdiff
path: root/server/src/plugin.lua
blob: eba224d6fe8a6f450796306dc15adc5b3bb67b8d (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
local fs        = require 'bee.filesystem'
local rpc       = require 'rpc'
local config    = require 'config'
local glob      = require 'glob'
local platform  = require 'bee.platform'
local sandbox   = require 'sandbox'

local Plugins

local function showError(msg)
    local traceback = log.error(msg)
    rpc:notify('window/showMessage', {
        type = 3,
        message = traceback,
    })
    return traceback
end

local function showWarn(msg)
    log.warn(msg)
    rpc:notify('window/showMessage', {
        type = 3,
        message = msg,
    })
    return msg
end

local function scan(path, callback)
    if fs.is_directory(path) then
        for p in path:list_directory() do
            scan(p, callback)
        end
    else
        callback(path)
    end
end

local function loadPluginFrom(path, root)
    log.info('Load plugin from:', path:string())
    local env = setmetatable({}, { __index = _G })
    sandbox(path:filename():string(), root:string(), io.open, package.loaded, env)
    Plugins[#Plugins+1] = env
end

local function load(workspace)
    Plugins = nil

    if not config.config.plugin.enable then
        return
    end
    local suc, path = xpcall(fs.path, showWarn, config.config.plugin.path)
    if not suc then
        return
    end

    Plugins = {}
    local pluginPath
    if workspace then
        pluginPath = fs.absolute(workspace.root / path)
    else
        pluginPath = fs.absolute(path)
    end
    if not fs.is_directory(pluginPath) then
        pluginPath = pluginPath:parent_path()
    end

    local pattern = {config.config.plugin.path}
    local options = {
        ignoreCase = platform.OS == 'Windows'
    }
    local parser = glob.glob(pattern, options)

    scan(pluginPath:parent_path(), function (filePath)
        if parser(filePath:string()) then
            loadPluginFrom(filePath, pluginPath)
        end
    end)
end

local function call(name, ...)
    if not Plugins then
        return nil
    end
    for _, plugin in ipairs(Plugins) do
        if type(plugin[name]) == 'function' then
            local suc, res = xpcall(plugin[name], showError, ...)
            if suc and res ~= nil then
                return res
            end
        end
    end
    return nil
end

return {
    load = load,
    call = call,
}