summaryrefslogtreecommitdiff
path: root/server/src/workspace.lua
blob: d14ad5d8b61e54113c4134698fa68b6b19ef2059 (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
local fs = require 'bee.filesystem'
local async = require 'async'

local function uriDecode(uri)
    if uri:sub(1, 8) ~= 'file:///' then
        log.error('uri decode failed: ', uri)
        return nil
    end
    local names = {}
    for name in uri:sub(9):gmatch '[^%/]+' do
        names[#names+1] = name:gsub('%%([0-9a-fA-F][0-9a-fA-F])', function (hex)
            return string.char(tonumber(hex, 16))
        end)
    end
    if #names == 0 then
        log.error('uri decode failed: ', uri)
        return nil
    end
    -- 盘符后面加个斜杠
    local path = fs.path(names[1] .. '\\')
    for i = 2, #names do
        path = path / names[i]
    end
    return fs.absolute(path)
end

local function uriEncode(path)
    local names = {}
    local cur = fs.absolute(path)
    while true do
        local name = cur:filename():string():gsub([=[[^%w%-%_%.%~]]=], function (char)
            return '%' .. string.byte(char)
        end)
        table.insert(names, 1, name)
        if cur == cur:parent_path() then
            break
        end
        cur = cur:parent_path()
    end
    return 'file:///' .. table.concat(names, '/')
end

local mt = {}
mt.__index = mt

function mt:init(rootUri)
    self.root = uriDecode(rootUri)
    if not self.root then
        return
    end
    log.info('Workspace inited, root: ', self.root)
    async.call([[
        require 'utility'
        local fs = require 'bee.filesystem'
        local list = {}
        local ignore = {
            ['.git'] = true,
            ['node_modules'] = true,
        }
        for path in io.scan(fs.path(ROOT)) do
            if path:extension():string() == '.lua' then
                list[#list+1] = path:string()
            end
        end
        return list
    ]], {
        ROOT = self.root:string()
    }, function (list)
        log.info(('Found [%d] files'):format(#list))
        for _, filename in ipairs(list) do
            local path = fs.absolute(fs.path(filename))
            local name = path:string():lower()
            self.files[name] = uriEncode(path)
        end
        self:reset()
    end)
end

function mt:addFile(uri)
    if uri:sub(-4) == '.lua' then
        local name = uriDecode(uri):string():lower()
        self.files[name] = uri
    end
end

function mt:removeFile(uri)
    local name = uriDecode(uri):string():lower()
    self.files[name] = nil
end

function mt:searchPath(str)
    if self.loaded[str] then
        return self.loaded[str]
    end
    str = str:gsub('%.', '/')
    local searchers = {}
    for i, luapath in ipairs(self.luapath) do
        searchers[i] = luapath:gsub('%?', str):lower()
    end
    for filename, uri in pairs(self.files) do
        for _, searcher in ipairs(searchers) do
            if filename:sub(-#searcher) == searcher then
                self.loaded[str] = uri
                self.lsp:readText(uri, fs.path(filename))
                return uri
            end
        end
    end
    return nil
end

function mt:reset()
    self.laoded = {}
    self.lsp:reCompile()
end

return function (lsp, name, uri)
    local workspace = setmetatable({
        lsp = lsp,
        name = name,
        files = {},
        loaded = {},
        luapath = {
            '?.lua',
            '?/init.lua',
            '?/?.lua',
        },
    }, mt)
    workspace:init(uri)
    return workspace
end