diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2021-01-04 11:10:55 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2021-01-04 11:10:55 +0800 |
commit | 83975e739d6ed07fee0bf64f7cbf0be00d5afcc7 (patch) | |
tree | 65047896839cb4b1831a1df1b83048d5d7a9c452 | |
parent | 50a1df60f0916ff2a1cd68a37d501851b10d1da3 (diff) | |
download | lua-language-server-83975e739d6ed07fee0bf64f7cbf0be00d5afcc7.zip |
workspace: supports `.dll`(`.so`) in `require`
-rw-r--r-- | changelog.md | 3 | ||||
-rw-r--r-- | script/core/completion.lua | 35 | ||||
-rw-r--r-- | script/file-uri.lua | 6 | ||||
-rw-r--r-- | script/files.lua | 128 | ||||
-rw-r--r-- | script/workspace/workspace.lua | 75 |
5 files changed, 200 insertions, 47 deletions
diff --git a/changelog.md b/changelog.md index 99ba013d..00757ca6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ # changelog -## 1.9.1 +## 1.10.0 +* `NEW` workspace: supports `.dll`(`.so`) in `require` * `CHG` supports `~` in command line * `FIX` [#339](https://github.com/sumneko/lua-language-server/issues/339) diff --git a/script/core/completion.lua b/script/core/completion.lua index 4ffcfcd3..e9564df3 100644 --- a/script/core/completion.lua +++ b/script/core/completion.lua @@ -625,6 +625,20 @@ local function checkCommon(word, text, offset, results) end end end + for uri in files.eachDll() do + local words = files.getDllWords(uri) or {} + for _, str in ipairs(words) do + if not used[str] and str ~= word then + used[str] = true + if matchKey(word, str) then + results[#results+1] = { + label = str, + kind = define.CompletionItemKind.Text, + } + end + end + end + end else for str in text:gmatch '([%a_][%w_]*)' do if not used[str] and str ~= word then @@ -848,6 +862,27 @@ local function checkUri(ast, text, offset, results) end ::CONTINUE:: end + for uri in files.eachDll() do + local opens = files.getDllOpens(uri) or {} + local path = workspace.getRelativePath(uri) + for _, open in ipairs(opens) do + if matchKey(literal, open) then + if not collect[open] then + collect[open] = { + textEdit = { + start = source.start + #source[2], + finish = source.finish - #source[2], + newText = open, + } + } + end + collect[open][#collect[open]+1] = ([=[* [%s](%s)]=]):format( + path, + uri + ) + end + end + end elseif libName == 'dofile' or libName == 'loadfile' then for uri in files.eachFile() do diff --git a/script/file-uri.lua b/script/file-uri.lua index ba44f2e7..d1c495b4 100644 --- a/script/file-uri.lua +++ b/script/file-uri.lua @@ -1,5 +1,7 @@ local platform = require 'bee.platform' +---@class uri: string + local escPatt = '[^%w%-%.%_%~%/]' local function esc(c) @@ -20,7 +22,7 @@ local m = {} --- path -> uri ---@param path string ----@return string uri +---@return uri uri function m.encode(path) local authority = '' if platform.OS == 'Windows' then @@ -62,7 +64,7 @@ end -- file://server/share/some/path --> \\server\share\some\path --- uri -> path ----@param uri string +---@param uri uri ---@return string path function m.decode(uri) local scheme, authority, path = uri:match('([^:]*):?/?/?([^/]*)(.*)') diff --git a/script/files.lua b/script/files.lua index 9ffcd2a1..22ffab95 100644 --- a/script/files.lua +++ b/script/files.lua @@ -10,19 +10,20 @@ local timer = require 'timer' local m = {} -m.openMap = {} -m.libraryMap = {} -m.fileMap = {} -m.watchList = {} -m.notifyCache = {} -m.assocVersion = -1 -m.assocMatcher = nil +m.openMap = {} +m.libraryMap = {} +m.fileMap = {} +m.dllMap = {} +m.watchList = {} +m.notifyCache = {} +m.assocVersion = -1 +m.assocMatcher = nil m.globalVersion = 0 m.linesMap = setmetatable({}, { __mode = 'v' }) m.astMap = setmetatable({}, { __mode = 'v' }) --- 打开文件 ----@param uri string +---@param uri uri function m.open(uri) local originUri = uri if platform.OS == 'Windows' then @@ -33,7 +34,7 @@ function m.open(uri) end --- 关闭文件 ----@param uri string +---@param uri uri function m.close(uri) local originUri = uri if platform.OS == 'Windows' then @@ -44,7 +45,7 @@ function m.close(uri) end --- 是否打开 ----@param uri string +---@param uri uri ---@return boolean function m.isOpen(uri) if platform.OS == 'Windows' then @@ -98,7 +99,7 @@ function m.asKey(uri) end --- 设置文件文本 ----@param uri string +---@param uri uri ---@param text string function m.setText(uri, text) if not text then @@ -147,7 +148,7 @@ function m.getVersion(uri) end --- 获取文件文本 ----@param uri string +---@param uri uri ---@return string text function m.getText(uri) if platform.OS == 'Windows' then @@ -161,7 +162,7 @@ function m.getText(uri) end --- 移除文件 ----@param uri string +---@param uri uri function m.remove(uri) local originUri = uri if platform.OS == 'Windows' then @@ -218,6 +219,16 @@ function m.eachFile() return pairs(map) end +--- Pairs dll files +---@return function +function m.eachDll() + local map = {} + for uri, file in pairs(m.dllMap) do + map[uri] = file + end + return pairs(map) +end + function m.compileAst(uri, text) if not m.isOpen(uri) and #text >= config.config.workspace.preloadFileSize * 1000 then if not m.notifyCache['preloadFileSize'] then @@ -267,7 +278,7 @@ function m.compileAst(uri, text) end --- 获取文件语法树 ----@param uri string +---@param uri uri ---@return table ast function m.getAst(uri) if platform.OS == 'Windows' then @@ -290,7 +301,7 @@ function m.getAst(uri) end --- 获取文件行信息 ----@param uri string +---@param uri uri ---@return table lines function m.getLines(uri) if platform.OS == 'Windows' then @@ -313,7 +324,7 @@ function m.getOriginUri(uri) if platform.OS == 'Windows' then uri = uri:lower() end - local file = m.fileMap[uri] + local file = m.fileMap[uri] or m.dllMap[uri] if not file then return nil end @@ -369,7 +380,7 @@ function m.getAssoc() end --- 判断是否是Lua文件 ----@param uri string +---@param uri uri ---@return boolean function m.isLua(uri) local ext = uri:match '%.([^%.%/%\\]+)$' @@ -384,6 +395,89 @@ function m.isLua(uri) return matcher(path) end +--- Does the uri look like a `Dynamic link library` ? +---@param uri uri +---@return boolean +function m.isDll(uri) + local ext = uri:match '%.([^%.%/%\\]+)$' + if not ext then + return false + end + if platform.OS == 'Windows' then + if m.eq(ext, 'dll') then + return true + end + else + if m.eq(ext, 'so') then + return true + end + end + return false +end + +--- Save dll, makes opens and words, discard content +---@param uri uri +---@param content string +function m.saveDll(uri, content) + if not content then + return + end + local luri = uri + if platform.OS == 'Windows' then + luri = uri:lower() + end + local file = { + uri = uri, + opens = {}, + words = {}, + } + for word in content:gmatch 'luaopen_([%w_]+)' do + file.opens[#file.opens+1] = word:gsub('_', '.') + end + if #file.opens == 0 then + return + end + local mark = {} + for word in content:gmatch '(%a[%w_]+)\0' do + if word:sub(1, 3) ~= 'lua' then + if not mark[word] then + mark[word] = true + file.words[#file.words+1] = word + end + end + end + + m.dllMap[luri] = file +end + +--- +---@param uri uri +---@return string[]|nil +function m.getDllOpens(uri) + if platform.OS == 'Windows' then + uri = uri:lower() + end + local file = m.dllMap[uri] + if not file then + return nil + end + return file.opens +end + +--- +---@param uri uri +---@return string[]|nil +function m.getDllWords(uri) + if platform.OS == 'Windows' then + uri = uri:lower() + end + local file = m.dllMap[uri] + if not file then + return nil + end + return file.words +end + --- 注册事件 function m.watch(callback) m.watchList[#m.watchList+1] = callback diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua index de96b569..57bdc00f 100644 --- a/script/workspace/workspace.lua +++ b/script/workspace/workspace.lua @@ -172,36 +172,48 @@ end local function loadFileFactory(root, progress, isLibrary) return function (path) local uri = furi.encode(path) - if not files.isLua(uri) then - return - end - if not isLibrary and progress.preload >= config.config.workspace.maxPreload then - if not m.hasHitMaxPreload then - m.hasHitMaxPreload = true - proto.notify('window/showMessage', { - type = 3, - message = lang.script('MWS_MAX_PRELOAD', config.config.workspace.maxPreload), - }) + if files.isLua(uri) then + if not isLibrary and progress.preload >= config.config.workspace.maxPreload then + if not m.hasHitMaxPreload then + m.hasHitMaxPreload = true + proto.notify('window/showMessage', { + type = 3, + message = lang.script('MWS_MAX_PRELOAD', config.config.workspace.maxPreload), + }) + end + return end - return - end - if not isLibrary then - progress.preload = progress.preload + 1 + if not isLibrary then + progress.preload = progress.preload + 1 + end + progress.max = progress.max + 1 + pub.task('loadFile', uri, function (text) + progress.read = progress.read + 1 + if text then + log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #text / 1000.0)) + if isLibrary then + log.info('++++As library of:', root) + files.setLibraryPath(uri, root) + end + files.setText(uri, text) + else + files.remove(uri) + end + end) end - progress.max = progress.max + 1 - pub.task('loadFile', uri, function (text) - progress.read = progress.read + 1 - if text then - log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #text / 1000.0)) - if isLibrary then - log.info('++++As library of:', root) - files.setLibraryPath(uri, root) + if files.isDll(uri) then + progress.max = progress.max + 1 + pub.task('loadFile', uri, function (content) + progress.read = progress.read + 1 + if content then + log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #content / 1000.0)) + if isLibrary then + log.info('++++As library of:', root) + end + files.saveDll(uri, content) end - files.setText(uri, text) - else - files.remove(uri) - end - end) + end) + end end end @@ -316,6 +328,15 @@ function m.findUrisByRequirePath(path) local results = {} local mark = {} local searchers = {} + for uri in files.eachDll() do + local opens = files.getDllOpens(uri) or {} + for _, open in ipairs(opens) do + if open == path then + results[#results+1] = uri + end + end + end + local input = path:gsub('%.', '/') :gsub('%%', '%%%%') for _, luapath in ipairs(config.config.runtime.path) do |