-
Notifications
You must be signed in to change notification settings - Fork 109
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Did you check existing requests?
- I have searched the existing issues
Describe the feature
Thanks for the amazing work.
I use nvim as my man pager, and aerial.nvim helps me a lot in navigation and having an overview of strange manuals. But the current implementation (simple pattern match) just ignores many useful sections. Hence, I made such code below to include more sections.
Unlike the original implementation, I use indent as a checkpoint to judge whether to create a new section (or item in the original code), and handle example blocks which, in some manuals (e.g. tmux), may have useful information.
CODE
M.fetch_symbols_sync = function(bufnr)
bufnr = bufnr or 0
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
local root_sections = {}
local current_section = nil
local prev_line = {} -- Use to create sections
local prepre_line = {} -- Use to finish sections
local example_line = {} -- Use to mark example sections
-- Helper: Get indentation level
local function get_indent(line)
local padding = line:match("^(%s*)")
return padding:len()
end
-- Helper: Truncate to maximum 5 words
local function truncate_name(text)
local words = {}
for word in text:gmatch("%S+") do
table.insert(words, word)
if #words >= 5 then
break
end
end
if words == {} then
return "NO_NAME_FOUND"
end
return table.concat(words, " ")
end
-- Helper: Close sections until reaching target indent
local function try_finish_sections(target_indent, end_lnum, end_col)
while current_section and current_section.col >= target_indent do
-- Only finish example sections when target indent is smaller than example_line.indent
local is_example_section = example_line.is_section
and current_section.lnum == example_line.lnum
if is_example_section and target_indent == example_line.indent then
return
end
current_section.end_lnum = end_lnum
current_section.end_col = end_col
current_section = current_section.parent
end
if example_line.lnum and target_indent <= example_line.indent then
example_line = {}
end
end
-- Core: Create a new section
local function create_section(line, lnum, indent)
local name = truncate_name(line:gsub("^%s+", ""))
local new_section = {
kind = "Interface",
name = name,
level = current_section and current_section.level + 1 or 0,
lnum = lnum,
col = indent,
}
-- DO NOT know what this statement does
-- Just keep same with original code
if
not config.post_parse_symbol
or config.post_parse_symbol(bufnr, new_section, {
backend_name = "man",
lang = "man",
})
~= false
then
if current_section then
current_section.children = current_section.children or {}
new_section.parent = current_section
table.insert(current_section.children, new_section)
else
table.insert(root_sections, new_section)
end
end
current_section = new_section
end
local function should_create_section(curr_indent)
if not prev_line.lnum then
return false
end
if example_line.lnum then
return example_line.is_section and curr_indent == example_line.indent
end
return curr_indent > prev_line.indent
end
local function should_finish_sections(curr_indent)
-- current_section.col can not be used to judge whether to finish simple example sections which
-- is not created as a valid section based on rule
if example_line.lnum and not example_line.is_section and curr_indent == example_line.indent then
return true
end
return current_section and curr_indent <= current_section.col and prepre_line.lnum
end
local function shift_lines(curr_line, curr_lnum, curr_indent)
prepre_line = prev_line
prev_line = {
line = curr_line,
lnum = curr_lnum,
indent = curr_indent,
length = curr_line:len(),
}
end
local function handle_example_line(curr_indent)
local is_example_line = example_line.lnum and prev_line.lnum == example_line.lnum
if is_example_line then
-- In example sections, children have same indent with example_line
local same_indent = (curr_indent == example_line.indent)
local has_keyword = false
local lowerStr = string.lower(example_line.line)
-- Define the words to search for
local requiredWords = { "available", "follow", "all" }
-- Check if each word is present in the string
for _, word in ipairs(requiredWords) do
if string.find(lowerStr, word, 1, true) then
has_keyword = true
break
end
end
if same_indent and has_keyword then
-- The order in this if statement is important
-- create_section() uses example_line.is_section
create_section(example_line.line, example_line.lnum, example_line.indent)
example_line.is_section = true
end
end
end
-- Main loop
for lnum, line in ipairs(lines) do
-- Skip first line (title)
if lnum > 1 then
local is_empty = line:match("^%s*$")
if not is_empty then
local curr_indent = get_indent(line)
DEBUG = 196 <= lnum and lnum <= 300
handle_example_line(curr_indent)
if should_finish_sections(curr_indent) then
try_finish_sections(curr_indent, prepre_line.lnum, prepre_line.length)
end
if should_create_section(curr_indent) then
if example_line.is_section then
create_section(line, lnum, curr_indent)
else
create_section(prev_line.line, prev_line.lnum, prev_line.indent)
end
end
shift_lines(line, lnum, curr_indent)
if line:sub(-1) == ":" and not example_line.lnum then
example_line = {
line = line,
lnum = lnum,
indent = curr_indent,
is_section = false,
}
end
end
end
end
backends.set_symbols(bufnr, root_sections, { backend_name = "man", lang = "man" })
endI tested it in some manuals and I'm happy to make a PR but I'm not sure what else needs to be done
Provide background
No response
What is the significance of this feature?
strongly desired
Additional details
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request