diff options
Diffstat (limited to 'support/digestif/digestif/Cache.lua')
-rw-r--r-- | support/digestif/digestif/Cache.lua | 129 |
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 |