implement listing functions for helpdb

develop
myk002 2022-07-06 22:26:06 -07:00
parent 64aba7a8a3
commit 35a4d19ac9
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
1 changed files with 102 additions and 50 deletions

@ -69,7 +69,7 @@ local function update_entry(entry, iterator, opts)
for line in iterator do for line in iterator do
if not short_help_found and opts.first_line_is_short_help then if not short_help_found and opts.first_line_is_short_help then
local _,_,text = line:trim():find('^%-%-%s*(.*)') local _,_,text = line:trim():find('^%-%-%s*(.*)')
if text[#text] ~= '.' then if not text:endswith('.') then
text = text .. '.' text = text .. '.'
end end
entry.short_help = text entry.short_help = text
@ -92,10 +92,11 @@ local function update_entry(entry, iterator, opts)
if not header_found and line:find('%w') then if not header_found and line:find('%w') then
header_found = true header_found = true
elseif not tags_found and line:find('^Tags: [%w, ]+$') then elseif not tags_found and line:find('^Tags: [%w, ]+$') then
-- tags must appear before the help text begins
local _,_,tags = line:trim():find('Tags: (.*)') local _,_,tags = line:trim():find('Tags: (.*)')
entry.tags = tags:split('[ ,]+') entry.tags = {}
table.sort(entry.tags) for _,tag in ipairs(tags:split('[ ,]+')) do
entry.tags[tag] = true
end
tags_found = true tags_found = true
elseif not short_help_found and not line:find('^Keybinding:') and elseif not short_help_found and not line:find('^Keybinding:') and
line:find('%w') then line:find('%w') then
@ -260,11 +261,11 @@ local function initialize_tags()
end end
-- ensures the db is up to date by scanning all help sources. does not do -- ensures the db is up to date by scanning all help sources. does not do
-- anything if it has already been run within the last second. -- anything if it has already been run within the last 10 seconds.
last_refresh_ms = last_refresh_ms or 0 last_refresh_ms = last_refresh_ms or 0
local function ensure_db() local function ensure_db()
local now_ms = dfhack.getTickCount() local now_ms = dfhack.getTickCount()
if now_ms - last_refresh_ms < 1000 then return end if now_ms - last_refresh_ms < 60000 then return end
last_refresh_ms = now_ms last_refresh_ms = now_ms
local old_db = db local old_db = db
@ -293,9 +294,18 @@ function get_entry_long_help(entry)
return get_db_property(entry, 'long_help') return get_db_property(entry, 'long_help')
end end
-- returns the list of tags associated with the entry local function set_to_sorted_list(set)
local list = {}
for item in pairs(set) do
table.insert(list, item)
end
table.sort(list)
return list
end
-- returns the list of tags associated with the entry, in alphabetical order
function get_entry_tags(entry) function get_entry_tags(entry)
return get_db_property(entry, 'tags') return set_to_sorted_list(get_db_property(entry, 'tags'))
end end
local function chunk_for_sorting(str) local function chunk_for_sorting(str)
@ -326,76 +336,109 @@ local function sort_by_basename(a, b)
return false return false
end end
local function add_if_matched(commands, command, strs, runnable) local function matches(command, filter)
if runnable and db[command].unrunnable then local db_entry = db[command]
return if filter.runnable and db_entry.unrunnable then
return false
end
if filter.tag then
local matched = false
for _,tag in ipairs(filter.tag) do
if db_entry.tags[tag] then
matched = true
break
end
end
if not matched then
return false
end
end end
if strs then if filter.str then
local matched = false local matched = false
for _,str in ipairs(strs) do for _,str in ipairs(filter.str) do
if command:find(str, 1, true) then if command:find(str, 1, true) then
matched = true matched = true
break break
end end
end end
if not matched then if not matched then
return return false
end end
end end
table.insert(commands, command) return true
end
local function normalize_string_list(l)
if not l then return nil end
if type(l) == 'string' then
return {l}
end
return l
end
local function normalize_filter(f)
if not f then return nil end
local filter = {}
filter.str = normalize_string_list(f.str)
filter.tag = normalize_string_list(f.tag)
filter.runnable = f.runnable
if not filter.str and not filter.tag and not filter.runnable then
return nil
end
return filter
end end
-- returns a list of identifiers, alphabetized by their last path component -- returns a list of identifiers, alphabetized by their last path component
-- (e.g. gui/autobutcher will immediately follow autobutcher). -- (e.g. gui/autobutcher will immediately follow autobutcher).
-- the optional filter element is a map with the following elements: -- the optional include and exclude filter params are maps with the following
-- elements:
-- str - if a string, filters by the given substring. if a table of strings, -- str - if a string, filters by the given substring. if a table of strings,
-- includes commands that match any of the given substrings. -- includes commands that match any of the given substrings.
-- tag - if a string, filters by the given tag name. if a table of strings, -- tag - if a string, filters by the given tag name. if a table of strings,
-- includes commands that match any of the given tags. -- includes commands that match any of the given tags.
-- runnable - if true, filters out plugin names that do no correspond to -- runnable - if true, matches only runnable commands, not plugin names.
-- runnable commands. function get_entries(include, exclude)
function list_entries(filter)
ensure_db() ensure_db()
filter = filter or {} include = normalize_filter(include)
exclude = normalize_filter(exclude)
local commands = {} local commands = {}
local strs = filter.str for command in pairs(db) do
if filter.str then if (not include or matches(command, include)) and
if type(strs) == 'string' then strs = {strs} end (not exclude or not matches(command, exclude)) then
end table.insert(commands, command)
local runnable = filter.runnable
if not filter.tag then
for command in pairs(db) do
add_if_matched(commands, command, strs, runnable)
end
else
local command_set = {}
local tags = filter.tag
if type(tags) == 'string' then tags = {tags} end
for _,tag in ipairs(tags) do
if not tag_index[tag] then
error('invalid tag: ' .. tag)
end
for _,command in ipairs(tag_index[tag]) do
command_set[command] = true
end
end
for command in pairs(command_set) do
add_if_matched(commands, command, strs, runnable)
end end
end end
table.sort(commands, sort_by_basename) table.sort(commands, sort_by_basename)
return commands return commands
end end
local function get_max_width(list, min_width)
local width = min_width or 0
for _,item in ipairs(list) do
width = math.max(width, #item)
end
return width
end
-- prints the requested entries to the console. include and exclude filters are
-- as in get_entries above.
function list_entries(include_tags, include, exclude)
local entries = get_entries(include, exclude)
local width = get_max_width(entries, 10)
for _,entry in ipairs(entries) do
print((' %-'..width..'s %s'):format(
entry, get_entry_short_help(entry)))
if include_tags then
print((' '..(' '):rep(width)..' tags(%s)'):format(
table.concat(get_entry_tags(entry), ',')))
end
end
end
-- returns the defined tags in alphabetical order -- returns the defined tags in alphabetical order
function list_tags() function get_tags()
ensure_db() ensure_db()
local tags = {} return set_to_sorted_list(tag_index)
for tag in pairs(tag_index) do
table.insert(tags, tag)
end
table.sort(tags)
return tags
end end
-- returns the description associated with the given tag -- returns the description associated with the given tag
@ -407,4 +450,13 @@ function get_tag_description(tag)
return tag_index[tag].description return tag_index[tag].description
end end
-- prints the defined tags and their descriptions to the console
function list_tags()
local tags = get_tags()
local width = get_max_width(tags, 10)
for _,tag in ipairs(tags) do
print((' %-'..width..'s %s'):format(tag, get_tag_description(tag)))
end
end
return _ENV return _ENV