nomsu/files.moon

159 lines
5.2 KiB
Plaintext
Raw Normal View History

-- Some file utilities for searching for files recursively and using package.nomsupath
lpeg = require 'lpeg'
re = require 're'
Files = {}
assert package.nomsupath, "No package.nomsupath was found"
_SPOOFED_FILES = {}
_FILE_CACHE = setmetatable {}, __index:_SPOOFED_FILES
_BROWSE_CACHE = {}
-- Create a fake file and put it in the cache
Files.spoof = (filename, contents)->
_SPOOFED_FILES[filename] = contents
return contents
-- Read a file's contents (searching first locally, then in the nomsupath)
Files.read = (filename)->
if file_contents = _FILE_CACHE[filename]
return file_contents
if filename == 'stdin'
return Files.spoof('stdin', io.read('*a'))
file = io.open(filename)
return nil unless file
contents = file\read("*a")
file\close!
_FILE_CACHE[filename] = contents
return contents
-- `walk` returns an iterator over all files matching a path.
2018-06-23 17:24:28 -07:00
{:match, :gsub} = string
2018-07-17 16:13:35 -07:00
-- TODO: improve sanitization
sanitize = (path)->
path = gsub(path,"\\","\\\\")
path = gsub(path,"`","")
path = gsub(path,'"','\\"')
path = gsub(path,"$","")
return path
Files.exists = (path)->
return true if _SPOOFED_FILES[path]
2018-07-17 16:13:35 -07:00
return true unless io.popen("ls #{sanitize(path)}")\close!
for nomsupath in package.nomsupath\gmatch("[^;]+")
return true unless io.popen("ls #{nomsupath}/#{sanitize(path)}")\close!
2018-07-17 16:13:35 -07:00
return false
browse = (path)->
unless _BROWSE_CACHE[path]
local files
_BROWSE_CACHE[path] = if _SPOOFED_FILES[path]
{path}
else
result = false
for nomsupath in package.nomsupath\gmatch("[^;]+")
f = io.popen('find -L "'..nomsupath..'/'..path..'" -not -path "*/\\.*" -type f -name "*.nom"')
files = [line for line in f\lines!]
if f\close!
result = files
break
result
return _BROWSE_CACHE[path]
2018-07-17 16:13:35 -07:00
ok, lfs = pcall(require, "lfs")
if ok
2018-07-09 16:58:46 -07:00
raw_file_exists = (filename)->
mode = lfs.attributes(filename, 'mode')
2018-07-09 19:22:40 -07:00
return if mode == 'file' or mode == 'directory' or mode == 'link' then true else false
Files.exists = (path)->
return true if _SPOOFED_FILES[path]
2018-07-09 16:58:46 -07:00
return true if path == 'stdin' or raw_file_exists(path)
for nomsupath in package.nomsupath\gmatch("[^;]+")
return true if raw_file_exists(nomsupath.."/"..path)
2018-07-09 16:58:46 -07:00
return false
2018-07-09 19:22:40 -07:00
2018-07-17 16:13:35 -07:00
export browse
browse = (filename)->
unless _BROWSE_CACHE[filename]
_BROWSE_CACHE[filename] = if _SPOOFED_FILES[filename]
{filename}
else
file_type, err = lfs.attributes(filename, 'mode')
if file_type == 'file'
if match(filename, "%.nom$") or match(filename, "%.lua$")
{filename}
else false
elseif file_type == 'char device'
{filename}
elseif file_type == 'directory' or file_type == 'link'
files = {}
for subfile in lfs.dir(filename)
continue if subfile == "." or subfile == ".."
for f in *(browse(filename.."/"..subfile) or {})
files[#files+1] = f
files
else false
return _BROWSE_CACHE[filename]
else
if io.popen('find . -maxdepth 0')\close!
error "Could not find 'luafilesystem' module and couldn't run system command `find` (this might happen on Windows). Please install `luafilesystem` (which can be found at: http://keplerproject.github.io/luafilesystem/ or `luarocks install luafilesystem`)", 0
2018-07-09 16:58:46 -07:00
Files.walk = (path, flush_cache=false)->
2018-07-17 16:13:35 -07:00
if flush_cache
export _BROWSE_CACHE
_BROWSE_CACHE = {}
local files
for nomsupath in package.nomsupath\gmatch("[^;]+")
if files = browse(nomsupath.."/"..path) then break
iter = (files, i)->
return unless files
2018-07-17 16:13:35 -07:00
i += 1
if f = files[i]
2018-07-17 16:13:35 -07:00
return i, f
return iter, files, 0
line_counter = re.compile([[
lines <- {| line (%nl line)* |}
line <- {} (!%nl .)*
]], nl:lpeg.P("\r")^-1 * lpeg.P("\n"))
-- LINE_STARTS is a mapping from strings to a table that maps line number to character positions
_LINE_STARTS = {}
Files.get_line_starts = (str)->
if type(str) != 'string'
str = tostring(str)
if starts = _LINE_STARTS[str]
return starts
line_starts = line_counter\match(str)
_LINE_STARTS[str] = line_starts
return line_starts
2018-07-17 16:13:35 -07:00
log = {}
Files.get_line_number = (str, pos)->
line_starts = Files.get_line_starts(str)
-- Binary search for line number of position
lo, hi = 1, #line_starts
while lo <= hi
mid = math.floor((lo+hi)/2)
if line_starts[mid] > pos
hi = mid-1
else lo = mid+1
return hi
Files.get_line = (str, line_no)->
line_starts = Files.get_line_starts(str)
start = line_starts[line_no]
return unless start
stop = line_starts[line_no+1]
return unless stop
return str\sub(start, stop - 2)
2018-07-17 16:13:35 -07:00
get_lines = re.compile([[
lines <- {| line (%nl line)* |}
line <- {[^%nl]*}
]], nl:lpeg.P("\r")^-1 * lpeg.P("\n"))
Files.get_lines = (str)-> get_lines\match(str)
return Files