summaryrefslogtreecommitdiff
path: root/Master/texmf/scripts/texdoc/search.tlu
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf/scripts/texdoc/search.tlu')
-rw-r--r--Master/texmf/scripts/texdoc/search.tlu364
1 files changed, 238 insertions, 126 deletions
diff --git a/Master/texmf/scripts/texdoc/search.tlu b/Master/texmf/scripts/texdoc/search.tlu
index fcb044d5189..fa35cce2754 100644
--- a/Master/texmf/scripts/texdoc/search.tlu
+++ b/Master/texmf/scripts/texdoc/search.tlu
@@ -9,25 +9,115 @@ local L = {}
load_env(L, {
'export_symbols',
'os', 'string', 'table', 'lfs', 'kpse', 'io',
- 'ipairs', 'assert', 'tonumber', 'type', 'print', 'tostring',
- 'err_print', 'win32_hook', 'parse_zip',
- 'config', 'alias', 'C',
+ 'ipairs', 'assert', 'error', 'tostring', 'setmetatable',
+ 'deb_print', 'err_print', 'win32_hook', 'parse_zip',
+ 'get_patterns', 'sort_doclist', 'docfile_quality',
+ 'config', 'C',
})
----------------------------- the docfiles list -----------------------------
+-- shared by all functions in this file
+local s_doclist
--- shared by all functions below
-local s_doc_files
+----------------------- docfile and doclist objects ------------------------
--- structure of the s_docfiles variable
--- s_docfiles = {
--- [1] = docfile1, docfiles2, ...,
+-- doclist = {
+-- [1] = docfile1, [2] = docfiles2, ...,
+-- inv = { realpath1 = index1, ... }
-- }
+--
+-- The inv subtable is such that for all i
+-- doclist.inv(doclist[i].realpath) == i
+
+local Doclist = {}
+Doclist.__index = Doclist
+
+-- create a new list of docfiles
+function Doclist:new()
+ local dl = { inv = {} }
+ setmetatable(dl, self)
+ return dl
+end
+
+-- add a docfile to a list
+function Doclist:add(df)
+ local index = self.inv[df.realpath]
+ if index then
+ self[index]:mergein(df)
+ else
+ local newindex = #self + 1
+ self[newindex] = df
+ self.inv[df.realpath] = newindex
+ end
+end
+
+-- stops a doclist
+function Doclist:stop()
+ self.inv = nil
+end
+
-- docfile = {
--- name = filename relative to tree, absolute if tree == nil,
--- tree = number of the tree in doc_roots,
--- exact = <boolean> does pattern match exactly,
+-- name = filename (used for scoring only)
+-- tree = code of the tree, see below
+-- matches = {pattern1, pattern2, ...}
-- }
+-- if tree > 1, this is the index of the tree in TEXDOCS
+-- if tree = 0, then name is relative to TLROOT (file found from tlpdb only)
+-- tree = - 1 if and only if file is a sty file. Here name is absolute.
+
+local Docfile = {}
+Docfile.__index = Docfile
+
+-- create a new docfile objet using initilisation info
+-- fields : name (relative to tree), tree, pattern
+function Docfile:new(info)
+ df = {}
+ setmetatable(df, self)
+ -- get realpath, tree and prepare name
+ df.tree = info.tree
+ if info.tree > 0 then
+ df.realpath = texdocs_tree_to_path(info.tree, info.name)
+ elseif info.tree == 0 then
+ error('Unimplemented')
+ df.realpath = get_tlroot()..'/'..info.name
+ info.name = string.gsub(info.name, '^texmf(-dist)?/doc/', '', 1)
+ elseif info.tree == -1 then
+ df.realpath = info.name
+ else
+ error('Internal error: bad tree number')
+ end
+ -- remove first component of name if at least two directory levels
+ if info.tree > -1 then
+ local name = string.match(info.name, '^..-/(.+/.+)$')
+ if name then
+ df.name = '/'..name
+ else
+ df.name = info.name
+ end
+ else
+ df.name = info.name
+ end
+ -- initialise the list of matches
+ if info.pattern then
+ df.matches = { info.pattern }
+ else
+ df.matches = {}
+ end
+ return df
+end
+
+-- merge a second docfile objet into self
+function Docfile:mergein(df)
+ if df.tree > self.tree then
+ self.name = df.name
+ self.tree = df.tree
+ end
+ for _, m in ipairs(df.matches) do
+ table.insert(self.matches, m)
+ end
+end
+
+-- from score.tlu
+Docfile.quality = docfile_quality
------------------ get results from TEXDOCS (à la kpse) -------------------
@@ -49,71 +139,57 @@ function get_texdocs ()
local kpse_texdocs = kpse.expand_var("$TEXDOCS")
-- expand the path and turn it into a lua list
local raw_doc_roots = string.explode(kpse.expand_braces(kpse_texdocs), sep)
- err_print('Search paths:', 'debug3')
- for i, dir in ipairs(raw_doc_roots) do
- doc_roots[i] = {}
+ local max = #raw_doc_roots + 1
+ for j, dir in ipairs(raw_doc_roots) do
+ local i = max - j
+ local dr = {}
local n
+ -- get path, !! and // values
dir, n = string.gsub (dir, '//$', '')
- doc_roots[i].recursion_allowed = (n == 1)
- doc_roots[i].path, n = string.gsub (dir, '^!!', '')
- doc_roots[i].index_mandatory = (n == 1)
- err_print(string.format('%s (index_mandatory=%s, recursion_allowed=%s)',
- doc_roots[i].path,
- tostring(doc_roots[i].index_mandatory),
- tostring(doc_roots[i].recursion_allowed)),
- 'debug3')
+ dr.recursion_allowed = (n == 1)
+ dr.path, n = string.gsub (dir, '^!!', '')
+ dr.index_mandatory = (n == 1)
+ deb_print('texdocs', string.format(
+ 'texdocs[%d] = %s (index_mandatory=%s, recursion_allowed=%s)',
+ i, dr.path,
+ tostring(dr.index_mandatory),
+ tostring(dr.recursion_allowed)))
+ -- decide if we should use a ls-R index, the filesystem, or do nothing
+ local root, shift = lsr_root(dr.path)
+ if root and shift and dr.recursion_allowed then
+ dr.lsr = root
+ dr.lsr_shift = shift
+ deb_print('texdocs', string.format(
+ 'texdocs[%d] using index: %s (shift=%s)', i, root, shift))
+ elseif not dr.index_mandatory and lfs.isdir(dr.path) then
+ dr.searchfs = true
+ deb_print('texdocs', string.format(
+ 'texdocs[%d] using filesystem search', i))
+ end
+ -- register this in docroots
+ doc_roots[i] = dr
end
end
--- return the real path of a docfile
-function real_path(docfile)
- if docfile.tree == nil then return docfile.name end
+-- return the real path from a texdocs tree number + relative path
+function texdocs_tree_to_path(tree, rel)
if doc_roots == nil then get_texdocs() end
- return win32_hook(doc_roots[docfile.tree].path..'/'..docfile.name)
+ return win32_hook(doc_roots[tree].path..'/'..rel)
end
-- find docfiles in texdocs directories
-function get_docfiles_texdocs (pattern)
- s_docfiles = {}
+function get_doclist_texdocs(patlist)
if doc_roots == nil then get_texdocs() end
- for code, doc_root in ipairs (doc_roots) do
- root, shift = lsr_root (doc_root.path)
- if root and shift and doc_root.recursion_allowed then
- err_print("Looking in tree '"..doc_root.path
- .."' using ls-R file'" ..root.."/ls-R'.", 'debug4')
- scan_lsr(root, code, shift, pattern)
- elseif (not doc_root.index_mandatory)
- and lfs.isdir(doc_root.path) then
- err_print("Looking in tree '"..doc_root.path
- .."' using filesystem.", 'debug4')
- scan_tree(code, doc_root.path, '',
- pattern, doc_root.recursion_allowed)
+ for code, dr in ipairs(doc_roots) do
+ if dr.lsr then
+ scan_lsr(patlist, code, dr.lsr, dr.lsr_shift)
+ elseif dr.searchfs then
+ scan_tree(patlist, code, dr.path, '', dr.recursion_allowed)
end
end
- return s_docfiles
-end
-
--- merge two components of a path, taking care of empty components
-function merge_path (a, b)
- return ((a == '') or (b == '')) and a..b or a..'/'..b
end
--- scan a tree without ls-R file
-function scan_tree (code, base, cwd, pattern, recurse)
- err_print("Entering directory: "..cwd, 'debug4')
- for file in lfs.dir(base..'/'..cwd) do
- if file ~= '.' and file ~= '..' then
- local f = (cwd == '') and file or cwd..'/'..file
- if lfs.isdir(base..'/'..f) then
- if recurse then scan_tree(code, base, f, pattern, recurse) end
- else
- local df = process_file(file, f, code, pattern, true)
- if df then table.insert(s_docfiles, df) end
- end
- end
- end
- err_print("Leaving directory: "..cwd, 'debug4')
-end
+end -- scope of doc_roots
-- find a ls-R file in a parent directory an return it or nil
function lsr_root (path)
@@ -131,10 +207,27 @@ function lsr_root (path)
end
end
+-- scan a tree without ls-R file
+function scan_tree (patlist, code, base, cwd, recurse)
+ deb_print('filesea', "Entering directory: "..cwd)
+ for file in lfs.dir(base..'/'..cwd) do
+ if file ~= '.' and file ~= '..' then
+ local f = (cwd == '') and file or cwd..'/'..file
+ if lfs.isdir(base..'/'..f) then
+ if recurse then scan_tree(patlist, code, base, f, recurse) end
+ else
+ local df = process_file(patlist, file, f, code, true)
+ if df then s_doclist:add(df) end
+ end
+ end
+ end
+ deb_print('filesea', "Leaving directory: "..cwd)
+end
+
-- scan a ls-R file
-function scan_lsr (cwd, code, shift, pattern)
+function scan_lsr(patlist, code, cwd, shift)
local is_dir = {} -- is_dir[path] = true iff path is a dir
- local results = {}
+ local results = Doclist:new()
local isdoc = false
local current_dir
local l = #shift
@@ -152,106 +245,125 @@ function scan_lsr (cwd, code, shift, pattern)
isdoc = true
current_dir = string.sub (dir_line, l+1)
is_dir[current_dir] = true
- err_print('Scanning directory: '..current_dir, 'debug4')
+ deb_print('lsrsea', 'Scanning directory: '..current_dir)
elseif isdoc then
- err_print("Finished scanning: "..shift, 'debug4')
+ deb_print('lsrsea', "Finished scanning: "..shift)
break -- we're exiting the ./doc (or shift) dir, so it's over
end
elseif isdoc then
- local df = process_file(
- line, merge_path(current_dir, line), code, pattern)
- if df then table.insert(results, df) end
+ local df = process_file(patlist, line, current_dir..'/'..line, code)
+ if df then results:add(df) end
end
end
lsr:close()
-- add non-directories to the list
for _, df in ipairs(results) do
if not is_dir[df.name] then
- table.insert(s_docfiles, df)
+ s_doclist:add(df)
end
end
end
-end -- scope of doc_roots
-
------------------------------- select results ------------------------------
-
-- says if file has a 'good' extenstion according to ext_list
-function check_ext(file, pattern)
- local good_ext, exact_match = false, false
+function check_ext(file)
+ file = string.lower(file)
-- remove zipext if applicable
file = parse_zip(file)
-- then do the normal thing
- local l, pat = string.len(pattern) + 1, pattern..'.'
for _, e in ipairs(config.ext_list) do
if e == '*' then
- good_ext = true
- if string.sub(file, 1, l) == pat then exact_match = true end
+ return true
elseif (e == '') then
- if not string.find(file, '.', 1, true) then good_ext = true end
- if file == pattern then exact_match = true end
+ if not string.find(file, '.', 1, true) then
+ return true
+ end
else
- if string.sub(file, -string.len(e)) == e then good_ext = true end
- if file == pattern..'.'..e then exact_match = true end
+ local dot_e = '.'..e
+ if string.sub(file, -string.len(dot_e)) == dot_e then
+ return true
+ end
end
end
- return good_ext, exact_match
+ return false
end
--- return a docfile entry if it "matches", nil ortherwise
-function process_file (file, pathfile, code, pattern)
- err_print('Processing file: '..pathfile, 'debug5')
- file = string.lower(file)
- local base, ext = string.match(file, '^(.*)%.(.*)$')
- if string.find(string.lower(pathfile), pattern,
- 1, config.regex ~= 'regex') then
- local good_ext, exact_match = check_ext(file, pattern)
- err_print(string.format("File '%s' matches; good_ext=%s, exact=%s",
- pathfile, tostring(good_ext), tostring(exact_match)), 'debug5')
- if good_ext then
- return {
- name = pathfile,
- tree = code,
- exact = exact_match,
- }
+-- return a docfile object if file "matches", nil ortherwise
+function process_file(patlist, file, pathfile, code)
+ deb_print('kpse', 'Processing file: '..pathfile)
+ local docfile
+ local pattern
+ for _, pattern in ipairs(patlist) do
+ if string.find(string.lower(pathfile), string.lower(pattern.name),
+ 1, config.mode ~= 'regex') then
+ local good_ext = check_ext(file)
+ deb_print('kpse', string.format(
+ "File '%s' matches '%s'; good_ext=%s",
+ pathfile, pattern.name, tostring(good_ext)))
+ if good_ext then
+ local info = {
+ name = pathfile,
+ tree = code,
+ pattern = pattern,
+ }
+ if docfile then
+ docfile:mergein(Docfile:new(info))
+ else
+ docfile = Docfile:new(info)
+ end
+ end
end
end
- return nil
+ return docfile
end
------------------------- get results from elsewhere ------------------------
+---------------------------- look for sty files ----------------------------
--- for sty files, we obviously don't want to look in TEXDOCS...
-function get_docfiles_sty (styname)
- return {{
- name = kpse.find_file(styname) ,
- exact = true,
- tree = nil,
- }}
+-- add doclist entries for sty files in patlist
+function get_doclist_sty(patlist)
+ for _, pat in ipairs(patlist) do
+ local file = kpse.find_file(pat.name)
+ if file then
+ local df = Docfile:new({
+ name = file,
+ tree = -1,
+ pattern = pat,
+ })
+ s_doclist:add(df)
+ end
+ end
end
------------------------------ main function -------------------------------
-- find docfiles according to pattern
-function get_docfiles(pattern)
- local no_regex = (config.mode ~= 'regex')
- -- apply aliases if relevant
- if no_regex and config.alias_switch and alias[pattern] then
- err_print (pattern.." aliased to "..alias[pattern], 'info')
- pattern = alias[pattern]
- end
- -- search using the appropriate function
- if string.match(string.lower(pattern), '%.([^/.]*)$') == 'sty'
- and no_regex then
- return get_docfiles_sty(pattern)
- else
- pattern = string.lower(pattern)
- return get_docfiles_texdocs(pattern)
+function get_doclist(pattern)
+ -- get patterns (inc. aliases)
+ local normal, sty = normal_vs_sty(get_patterns(pattern))
+ -- initialise result list
+ s_doclist = Doclist:new()
+ -- get results
+ get_doclist_sty(sty)
+ get_doclist_texdocs(normal)
+ -- finally, sort results
+ sort_doclist(s_doclist, pattern)
+ return s_doclist
+end
+
+-- separate sty patterns from the rest
+function normal_vs_sty(list)
+ if config.mode == 'regex' then return list, {} end
+ local normal, sty = {}, {}
+ for _, p in ipairs(list) do
+ if string.match(string.lower(p.name), '%.([^/.]*)$') == 'sty' then
+ table.insert(sty, p)
+ else
+ table.insert(normal, p)
+ end
end
+ return normal, sty
end
-- finally export a few symbols
export_symbols(L, {
- 'real_path',
- 'get_docfiles',
+ 'get_doclist',
})