summaryrefslogtreecommitdiff
path: root/support/digestif/digestif/Cache.lua
diff options
context:
space:
mode:
Diffstat (limited to 'support/digestif/digestif/Cache.lua')
-rw-r--r--support/digestif/digestif/Cache.lua129
1 files changed, 129 insertions, 0 deletions
diff --git a/support/digestif/digestif/Cache.lua b/support/digestif/digestif/Cache.lua
new file mode 100644
index 0000000000..d7e3e16f85
--- /dev/null
+++ b/support/digestif/digestif/Cache.lua
@@ -0,0 +1,129 @@
+-- A class to store transient file contents and manage the creation of
+-- Manuscript objects.
+
+local Manuscript = require "digestif.Manuscript"
+local lpeg = require "lpeg"
+local util = require "digestif.util"
+
+local S, C = lpeg.S, lpeg.C
+local sequence, gobble_until = util.sequence, util.gobble_until
+local path_split, path_join = util.path_split, util.path_join
+local path_normalize = util.path_normalize
+local weak_values = {__mode = "v"}
+local io_open = io.open
+
+local Cache = util.class()
+
+function Cache:__init(tbl)
+ self.store = setmetatable({}, weak_values)
+ self.persist = {} -- a place to keep an extra reference to things
+ -- that shouldn't vanish
+ if tbl then
+ for name, src in pairs(tbl) do
+ self:put(name, src)
+ end
+ end
+end
+
+-- Set the contents of a file in the cache.
+function Cache:put(filename, str)
+ local props = self.store[filename]
+ if not props then
+ props = {}
+ self.store[filename] = props
+ else
+ props.rootname = nil
+ end
+ props.contents = str
+ self.persist[filename] = props
+end
+
+-- Return the contents of a file, as stored with the put method, or
+-- attempt to read the file from disk. If neither succeeds, return
+-- nil. The second return value is a cookie. Values read from disk
+-- will be garbage collected when no reference to the cookie exists.
+-- Values stored by the put method are not subject to garbage
+-- collection until an explicit call to Cache.forget.
+--
+function Cache:__call(filename)
+ local props = self.store[filename]
+ if not props then
+ local file, str = io_open(filename), nil
+ if file then
+ str = file:read("*all")
+ file:close()
+ end
+ props = {contents = str}
+ self.store[filename] = props
+ end
+ return props.contents, props
+end
+
+-- Remove a file from the cache.
+function Cache:forget(filename)
+ self.store[filename] = nil
+ self.persist[filename] = nil
+end
+
+local space = S" \t\r"
+local magic_comment_patt = sequence(
+ (space^0 * "%")^1,
+ space^0 * "!" * util.case_fold("tex"),
+ space^1 * "root",
+ space^0 * "=",
+ space^0 * C(gobble_until(space^0 * "\n")))
+local search_magic_comment_patt = util.choice(
+ magic_comment_patt,
+ util.search("\n" * magic_comment_patt))
+
+-- Determine the root path of a document from magic comments.
+function Cache:rootname(filename)
+ local _, cookie = self(filename) -- warm up
+ local props = assert(self.store[filename])
+ local rootname = props.rootname
+ if rootname == nil then
+ local src = self(filename)
+ local val = search_magic_comment_patt:match(src:sub(1, 1000))
+ if val then
+ rootname = path_normalize(path_join(path_split(filename), val))
+ else
+ rootname = false
+ end
+ props.rootname = rootname
+ end
+ return rootname
+end
+
+-- Produce a Manuscript object. This method ensures that the result
+-- belongs to a tree of Manuscript objects representing a multi-file
+-- TeX document. If the rootname argument is omitted, try to infer it
+-- using magic comments.
+--
+-- This method reuses as much information as possible from the cache.
+--
+-- Arguments:
+-- filename (string): the path of the manuscript
+-- format (string): the TeX format
+-- rootname (string, optional): the root path of the TeX document
+--
+-- Returns:
+-- a Manuscript object
+--
+function Cache:manuscript(filename, format, rootname)
+ rootname = rootname or self:rootname(filename) or filename
+ local root = self.store[rootname] and self.store[rootname][format]
+ if not root or not root:is_current() then
+ root = Manuscript{
+ filename = rootname,
+ format = format,
+ files = self
+ }
+ self.store[rootname][format] = root
+ end
+ local script = root:find_manuscript(filename)
+ or self:manuscript(filename, format, filename) -- root does not refer back
+ self.store[filename][format] = script
+ return script
+end
+
+return Cache