Initial commit of suggest-require
. A simple library for finding modules to require.
This commit is contained in:
commit
4fddf72901
149
suggest-require.lua
Normal file
149
suggest-require.lua
Normal file
|
@ -0,0 +1,149 @@
|
|||
|
||||
-- This is a tiny library to discover which modules can be imported using
|
||||
-- `require`. It's intended to be included in an auto-complete system for Lua.
|
||||
|
||||
-- Not sure if it works on a system without GNU find :/
|
||||
|
||||
---
|
||||
-- Constants
|
||||
|
||||
local SCAN_DIR_TEMPLATE, SCAN_DIR_SEP_PATTERN
|
||||
|
||||
if package.config:sub(1, 1) == '/' then
|
||||
-- On unix
|
||||
SCAN_DIR_TEMPLATE = 'find -L "%s" -maxdepth 1 -mindepth 1 -print0'
|
||||
SCAN_DIR_SEP_PATTERN = '%z'
|
||||
else
|
||||
-- On windows
|
||||
SCAN_DIR_TEMPLATE = 'dir "%s" /b /ad'
|
||||
SCAN_DIR_SEP_PATTERN = '\n'
|
||||
end
|
||||
|
||||
-- TODO: Improve Windows support
|
||||
-- TODO: Make sure works on MacOS.
|
||||
|
||||
--------------
|
||||
-- Util
|
||||
|
||||
local function get_module_paths (path_str)
|
||||
-- TODO: Add support for alternative package.config
|
||||
|
||||
-- Error check
|
||||
local path_str = path_str or package.path
|
||||
assert(type(package.config) == 'string')
|
||||
assert(type(path_str) == 'string')
|
||||
|
||||
-- Work work
|
||||
local paths = {}
|
||||
for path in path_str:gmatch '[^;]+' do
|
||||
paths[#paths+1] = path
|
||||
end
|
||||
-- Return
|
||||
return paths
|
||||
end
|
||||
|
||||
local function get_modules_fitting_path (root_path, module_names)
|
||||
assert(type(root_path) == 'string')
|
||||
assert(type(module_names) == 'table')
|
||||
|
||||
-- Use `find` to find files in folders below the given matching.
|
||||
local pfile = io.popen ('find -L "'..(root_path:match '^(.-)?' or root_path)..'" -type f -not -path \'*/\\.*\' -print0')
|
||||
local list_str = pfile:read '*all'
|
||||
pfile:close()
|
||||
-- Construct a pattern for the expected path of possible modules.
|
||||
local module_path_pattern = '^'
|
||||
.. root_path:gsub('[%^%(%)%.%[%]%*%+%-]', function (a) return '%' .. a end)
|
||||
:gsub('?', '(.+)')
|
||||
.. '$'
|
||||
-- Look through the file list, and find importable modules.
|
||||
for path in list_str:gmatch '[^%z]+' do
|
||||
|
||||
local matches = { path:match(module_path_pattern) }
|
||||
local identical = #matches > 0
|
||||
|
||||
for i = 1, #matches do if matches[1] ~= matches[i] then
|
||||
identical = false; break
|
||||
end end
|
||||
|
||||
|
||||
-- Check if match
|
||||
if identical then
|
||||
module_names[#module_names+1] = matches[1]:gsub('/', '.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--------------
|
||||
-- Finding Module Names
|
||||
|
||||
local function get_loaded_module_names ()
|
||||
-- Get the names of modules already loaded.
|
||||
local l = {}
|
||||
for k in pairs(package.loaded) do l[#l+1] = k end
|
||||
return l
|
||||
end
|
||||
|
||||
local function get_module_names_from_path ()
|
||||
local paths = get_module_paths(package.path)
|
||||
|
||||
local modules = {}
|
||||
for _, path in pairs(paths) do
|
||||
get_modules_fitting_path(path, modules)
|
||||
end
|
||||
|
||||
return modules
|
||||
end
|
||||
|
||||
local function get_c_module_names_from_path ()
|
||||
local paths = get_module_paths(package.cpath)
|
||||
|
||||
local modules = {}
|
||||
for _, path in pairs(paths) do
|
||||
get_modules_fitting_path(path, modules)
|
||||
end
|
||||
|
||||
return modules
|
||||
end
|
||||
|
||||
local function get_available_module_names ()
|
||||
assert(type(package) == 'table')
|
||||
|
||||
local names_loaded = get_loaded_module_names()
|
||||
local names_path = get_module_names_from_path()
|
||||
local names_cpath = get_c_module_names_from_path()
|
||||
|
||||
--
|
||||
|
||||
-- Dedub and Sort
|
||||
local dedub = {}
|
||||
for i = 1, #names_loaded do dedub[names_loaded[i]] = true end
|
||||
for i = 1, #names_path do dedub[names_path[i]] = true end
|
||||
for i = 1, #names_cpath do dedub[names_cpath[i]] = true end
|
||||
local module_names = {}
|
||||
for name in pairs(dedub) do module_names[#module_names+1] = name end
|
||||
table.sort(module_names)
|
||||
|
||||
-- Output assertions
|
||||
assert(type(module_names) == 'table')
|
||||
for i = 1, #module_names do
|
||||
assert(type(module_names[i]) == 'string')
|
||||
end
|
||||
|
||||
-- Return
|
||||
return module_names
|
||||
end
|
||||
|
||||
--------------
|
||||
|
||||
if ... then
|
||||
return get_available_module_names
|
||||
else
|
||||
local names = get_available_module_names()
|
||||
io.write 'Following modules are available: \n'
|
||||
for _, module_name in ipairs(names) do
|
||||
io.write ' - '
|
||||
io.write(module_name)
|
||||
io.write '\n'
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user