summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2021-01-04 11:10:55 +0800
committer最萌小汐 <sumneko@hotmail.com>2021-01-04 11:10:55 +0800
commit83975e739d6ed07fee0bf64f7cbf0be00d5afcc7 (patch)
tree65047896839cb4b1831a1df1b83048d5d7a9c452
parent50a1df60f0916ff2a1cd68a37d501851b10d1da3 (diff)
downloadlua-language-server-83975e739d6ed07fee0bf64f7cbf0be00d5afcc7.zip
workspace: supports `.dll`(`.so`) in `require`
-rw-r--r--changelog.md3
-rw-r--r--script/core/completion.lua35
-rw-r--r--script/file-uri.lua6
-rw-r--r--script/files.lua128
-rw-r--r--script/workspace/workspace.lua75
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