Skip to content

Commit

Permalink
Optimizations.
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreasMatthias committed Apr 15, 2024
1 parent ecb0a5e commit 0aba4cb
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 80 deletions.
92 changes: 64 additions & 28 deletions src/luals2dox/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,38 @@

local cjson = require('cjson')
local fstring = require('F')
local path = require('pl.path')

local fs
local has_luaposix = pcall(require, 'posix')
if has_luaposix then
local posix = require('posix')
fs = {
realpath = posix.realpath,
getcwd = posix.getcwd,
lstat_mtime = function(file)
return posix.sys.stat.lstat(file).st_mtime
end,
isfile = function(file)
if not file then
return false
else
local stat = posix.stat(file)
return stat and stat.type == 'regular'
end
end,
}
else
local path = require('pl.path')
fs = {
realpath = path.abspath,
getcwd = path.currentdir,
lstat_mtime = function(file)
return path.attrib(file).modification
end,
isfile = path.isfile,
}
end


local lpeg = require('lpeg')
local ws = lpeg.S(' \n\t') ^ 0
Expand Down Expand Up @@ -127,6 +158,13 @@ function Doxy:init()
self:list_config()
return
end
if self:getOS() == 'Windows' then
self.file_scheme = '^file:///'
self.dev_null = 'null'
else
self.file_scheme = '^file://'
self.dev_null = '/dev/null'
end
self:set_lua_file()
self:update_json()
end
Expand All @@ -140,15 +178,26 @@ function Doxy:info(str)
end


---Return name of Operating System.
---@return string
function Doxy:getOS()
if package.config:sub(1, 1) == '\\' then
return 'Windows'
else
return io.popen('uname -s', 'r'):read()
end
end


---Set lua file (`self.lua_file`) with error checking.
---@return nil
function Doxy:set_lua_file()
if self.args['file.lua'] == '' then
self.arg_parser:error("missing argument 'file.lua'")
end
self.lua_file = path.abspath(self.args['file.lua']) --[[@as string]] --Lua file name.
self.lua_file = fs.realpath(self.args['file.lua']) --[[@as string]] --Lua file name.
self.lua_file = self:windows_drive_letter_lowercase(self.lua_file)
if not path.isfile(self.lua_file) then
if not fs.isfile(self.lua_file) then
self.arg_parser:error(string.format('Lua file \'%s\' not found.', self.args['file.lua']))
end
end
Expand All @@ -162,15 +211,15 @@ end
---@param file string # File name
---@return string
function Doxy:windows_drive_letter_lowercase(file)
return file:sub(1, 1):lower() .. file:sub(2)
return file and file:sub(1, 1):lower() .. file:sub(2)
end


---Set json file (`self.json_file`) with error checking.
---@return nil
function Doxy:set_json_file()
self.json_file = path.abspath(self.args['json']) --[[@as string]] --JSON file name.
if not path.isfile(self.json_file) then
self.json_file = fs.realpath(self.args['json']) --[[@as string]] --JSON file name.
if not fs.isfile(self.json_file) then
self.arg_parser:error(string.format('JSON file \'%s\' not found.', self.args['json']))
end
end
Expand All @@ -193,7 +242,7 @@ end
---@return nil
function Doxy:list_config()
self:print_config_item('Binary', self.args.lua_language_server)
self:print_config_item('Working directory', path.currentdir())
self:print_config_item('Working directory', fs.getcwd())
self:print_config_item('JSON file', self.json_file)
for _, section in ipairs(self.doc_json) do
if section.type == 'luals.config' then
Expand All @@ -217,12 +266,14 @@ end
---Update json file if currently processed lua-file is newer than json-file.
---@return nil
function Doxy:update_json()
local stat_lua = path.attrib(self.lua_file)
local stat_json = path.attrib(self.json_file)
if stat_json.modification < stat_lua.modification then
local stat_lua = fs.lstat_mtime(self.lua_file)
local stat_json = fs.lstat_mtime(self.json_file)
if stat_json < stat_lua then
os.rename(self.json_file, 'doc.json') -- LuaLS expects 'doc.json'.
local ok, state, errno =
os.execute(string.format('%s --doc_update > /dev/null', self.args.lua_language_server))
os.execute(string.format('%s --doc_update > %s',
self.args.lua_language_server,
self.dev_null))
if not ok then
os.rename('doc.json', self.json_file)
self.arg_parser:error(string.format(
Expand Down Expand Up @@ -303,28 +354,13 @@ function Doxy:render_section(section)
end


---Return name of Operating System.
---@return string
function Doxy:getOS()
if package.config:sub(1, 1) == '\\' then
return 'Windows'
else
return io.popen('uname -s', 'r'):read()
end
end


---Return `true` if `file` is the currently processed lua file.
---@param file string # file name.
---@return boolean
function Doxy:is_current_lua_file(file)
file = self:urldecode(file)
if self:getOS() == 'Windows' then
file = file:gsub('^file:///', '')
else
file = file:gsub('^file://', '')
end
file = path.abspath(file)
file = file:gsub(self.file_scheme, '')
file = fs.realpath(file)
if file == self.lua_file then
return true
else
Expand Down
90 changes: 66 additions & 24 deletions test/helper.lua
Original file line number Diff line number Diff line change
@@ -1,33 +1,75 @@
local M = {}

function M.copy_file(old, new)
local fd_old = io.open(old, 'rb')
local size_old, size_new
if not fd_old then
return false
end
local fd_new = io.open(new, 'wb')
if not fd_new then
return false
end
while true do
local block = fd_old:read(2^13)
if not block then
size_old = fd_old:seek('end')
break
end
fd_new:write(block)
end
fd_old:close()
size_new = fd_new:seek('end')
fd_new:close()
return size_new == size_old
end
-- function M.copy_file(old, new)
-- local fd_old = io.open(old, 'rb')
-- local size_old, size_new
-- if not fd_old then
-- return false
-- end
-- local fd_new = io.open(new, 'wb')
-- if not fd_new then
-- return false
-- end
-- while true do
-- local block = fd_old:read(2^13)
-- if not block then
-- size_old = fd_old:seek('end')
-- break
-- end
-- fd_new:write(block)
-- end
-- fd_old:close()
-- size_new = fd_new:seek('end')
-- fd_new:close()
-- return size_new == size_old
-- end


function M.sleep(s)
M.sleep = function(s)
local time = os.time() + s
repeat until os.time() > time
end


local function getOS()
if package.config:sub(1, 1) == '\\' then
return 'Windows'
else
return io.popen('uname -s', 'r'):read()
end
end

local function dos_fn(file)
return file:gsub('/', '\\')
end


if getOS() == 'Windows' then
M.copy_file = function(old, new)
os.execute(string.format('copy %s %s > null', dos_fn(old), dos_fn(new)))
end
M.link_file = function(target, link)
M.copy_file(dos_fn(target), dos_fn(link))
end
M.remove_file = function(file)
-- If we don't wait here, we'll get "The process cannot access the file
-- because it is being used by another process."
M.winsleep(1)
os.execute(string.format('if exist %s (del /f %s)', file, file))
end
M.winsleep = M.sleep
else
M.copy_file = function(old, new)
os.execute(string.format('cp %s %s', old, new))
end
M.link_file = function(target, link)
os.execute(string.format('ln %s %s', target, link))
end
M.remove_file = function(file)
os.execute(string.format('rm -f %s', file))
end
M.winsleep = function(s) end
end


return M
45 changes: 22 additions & 23 deletions test/test-cli.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,20 @@ end


describe('CLI arguments:', function()
it('XXXLua file missing/not found', function()
helper.copy_file('./json/doc.json', './doc.json')
it('Lua file missing/not found', function()
helper.remove_file('doc.json')
helper.link_file('json/doc.json', 'doc.json')
assert.has_error(
function() test({}) end,
"missing argument 'file.lua'")
assert.has_error(
function() test({'file_does_not_exist.lua'}) end,
"Lua file 'file_does_not_exist.lua' not found.")
helper.sleep(1)
assert(os.remove('./doc.json'))
end)

it('--list-config', function()
helper.copy_file('./json/empty.json', './doc.json')
helper.remove_file('doc.json')
helper.link_file('json/empty.json', 'doc.json')
local print_stub = stub(_G, 'print')
assert.has_error(
function() test({'-l'}) end,
Expand All @@ -61,23 +61,24 @@ describe('CLI arguments:', function()
function() test({'--list-config'}) end,
"Section 'luals.config' missing in JSON file.")

helper.copy_file('./json/doc.json', './doc.json')
helper.remove_file('doc.json')
helper.link_file('json/doc.json', 'doc.json')
assert.has_no_error(
function() test({'-l'}) end)
print_stub:revert()
helper.sleep(1)
assert(os.remove('./doc.json'))
end)

it('JSON file not found', function()
helper.remove_file('doc.json')
assert.has_error(
function() test({'./var/var-01.lua'}) end,
"JSON file './doc.json' not found.")
end)

it('Cannot open JSON file', function()
local function test_here()
helper.copy_file('json/doc.json', 'doc.json')
helper.remove_file('doc.json')
helper.link_file('json/doc.json', 'doc.json')
arg[1] = './var/var-01.lua'
-- Mock error function of argparse.
local args = require('luals2dox.args')
Expand All @@ -88,8 +89,7 @@ describe('CLI arguments:', function()
-- Run luals2dox.
local l2d = require('luals2dox')
local doxy = assert(l2d:new())
helper.sleep(1)
assert(os.remove('./doc.json'))
helper.remove_file('doc.json')
doxy:load_json_file()
end

Expand All @@ -101,29 +101,28 @@ describe('CLI arguments:', function()
it('Update JSON file', function()
-- For code coverage statistics we want to run the part of the code
-- that updated `doc.json`. This is the only reason for this test.
helper.copy_file('./json/doc.json', './doc.json')
local tmp_lua_file = './var/_tmp_file.lua'
io.open('./var/_tmp_file.lua', 'w'):close()
helper.remove_file('doc.json')
helper.link_file('json/doc.json', 'doc.json')
local tmp_lua_file = 'tmp_file.lua'
io.open(tmp_lua_file, 'w'):close()
assert.has_no_error(
function() test({tmp_lua_file}) end)
helper.sleep(1)
assert(os.remove(tmp_lua_file))
helper.sleep(1)
helper.remove_file(tmp_lua_file)
end)

it('--lua-language-server', function()
helper.copy_file('./json/doc.json', './doc.json')
local tmp_lua_file = './var/_tmp_file.lua'
helper.sleep(1)
local tmp_lua_file = 'var/tmp_file.lua'
helper.remove_file(tmp_lua_file)
helper.remove_file('doc.json')
helper.link_file('json/doc.json', 'doc.json')
helper.sleep(.5)
io.open(tmp_lua_file, 'w'):close()
assert.error_matches(
function()
test({'--lua-language-server', 'wrong-binary', tmp_lua_file})
end,
'Updating JSON file failed %(exit, %d+%).')
helper.sleep(1)
assert(os.remove(tmp_lua_file))
assert(os.remove('./doc.json'))
-- helper.remove_file(tmp_lua_file)
end)

it('--all-files', function()
Expand Down
4 changes: 2 additions & 2 deletions test/test-render.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ assert:register('assertion', 'strings_equal', strings_equal,


-- Copy json file.
os.remove('./doc.json')
helper.copy_file('./json/doc.json', './doc.json')
os.remove('doc.json')
helper.link_file('json/doc.json', 'doc.json')


---@param lua_filename string # Lua file name.
Expand Down
6 changes: 3 additions & 3 deletions test/test-unknown.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ local path = require('pl.path')

local function make_json_file(content)
-- Read JSON file
local fd = assert(io.open('./json/doc.json'))
local fd = assert(io.open('json/doc.json'))
local json = cjson.decode(fd:read('*a'))
fd:close()
-- Append JSON data
json[#json + 1] = content
-- Write JSON file
os.remove('./doc.json')
fd = assert(io.open('./doc.json', 'w'))
helper.remove_file('doc.json')
fd = assert(io.open('doc.json', 'w'))
fd:write(cjson.encode(json))
fd:close()
end
Expand Down

0 comments on commit 0aba4cb

Please sign in to comment.