Skip to content

Instantly share code, notes, and snippets.

@MrAsynchronous
Created October 27, 2023 17:55
Show Gist options
  • Select an option

  • Save MrAsynchronous/b6a857743d56ddc1e4cdae42d5c20492 to your computer and use it in GitHub Desktop.

Select an option

Save MrAsynchronous/b6a857743d56ddc1e4cdae42d5c20492 to your computer and use it in GitHub Desktop.
Configurable plugin for RobloxLSP that adds autocomplete to require-by-string importers
local fs = require("bee.filesystem")
local fsu = require("fs-utility")
local furi = require("file-uri")
local workspace = require("workspace")
--[[
This section is configurable. Here are some examples of how to configure it:
-- the Nevermore pattern
local Rx = require("Rx")
requires clause "local require =.+(loader)'"
and import regex '()local%s+[%w_]+%s=%srequire%("+([%w_]+)"%)'
-- the Orion pattern
local Promise = Import("Promise")
requires clause 'local Import =.+(Orion.Import)'
and import regex '()local%s+[%w_]+%s=%sImport%("([%w_]+)"%)'
The import clause is used for determining if a file needs to be scanned for imports.
The regex is used for extracting the name of the file the clause is attemping to fetch.
]]
local IMPORT_CLAUSE = "local require =.+(loader)'"
local IMPORT_REGEX = '()local%s+[%w_]+%s=%srequire%("+([%w_]+)"%)'
local foundFileCache = {}
local function relativePathToDotPath(relPath)
-- Convert posix path to dots, and remove file extension.
return tostring(relPath):gsub(".lua", ""):gsub("\\", "."):gsub("/", ".")
end
function buildFileMap()
local projectRoot = fs.path(workspace.path)
fsu.scanDirectory(projectRoot / "", function(path)
if path:extension():string() ~= ".lua" then return end
local uri = furi.encode(path:string())
-- Check that this file isn't ignored, and isn't a library.
-- if (not workspace.isValidLuaUri(uri)) then return end
-- Get the relative pat to the workspace.
local relativePath = workspace.getRelativePath(uri)
local scriptName = path:stem():string()
if scriptName == "init" then
scriptName = path:parent_path():stem():string()
end
foundFileCache[scriptName] = relativePath
end)
end
function getLuaFile(fileName)
local path = foundFileCache[fileName]
if (path) and (fs.exists(fs.path(path))) then
return relativePathToDotPath(path)
end
end
function OnSetText(_uri, text)
-- Only evaluate requires if nevermore is used as a module loader.
if (not text:match(IMPORT_CLAUSE)) then return end
local hasRebuiltMapThisSession = false
local diffs = {}
for localPos, moduleName in text:gmatch(IMPORT_REGEX) do
local relativeModuleDotPath = getLuaFile(moduleName)
if (not relativeModuleDotPath) and (not hasRebuiltMapThisSession) then
hasRebuiltMapThisSession = true
buildFileMap()
relativeModuleDotPath = getLuaFile(moduleName)
end
if (relativeModuleDotPath) then
diffs[#diffs + 1] = {
start = localPos,
finish = localPos - 1,
text = ("---@module %s\n"):format(relativeModuleDotPath),
}
end
end
if (#diffs == 0) then return end
return diffs
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment