Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion scripts/panvimdoc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ local DOC_MAPPING_PROJECT = true
local DATE = nil
local TITLE_DATE_PATTERN = "%Y %B %d"

local ANCHORS = {}

-- Compare link text to a heading loosely, ignoring case, spacing and markup.
local function normalize(text)
return (text:lower():gsub("[^%w]", ""))
end

local CURRENT_HEADER = nil
local SOFTBREAK_TO_HARDBREAK = "space"

Expand Down Expand Up @@ -212,6 +219,28 @@ Writer.Pandoc = function(doc, opts)
HEADER_COUNT = HEADER_COUNT + doc.meta.incrementheadinglevelby
DATE = doc.meta.date
TITLE_DATE_PATTERN = doc.meta.titledatepattern
ANCHORS = {}
local section = nil
doc:walk({
Header = function(h)
local slug = string.lower(string.gsub(stringify(h), "%s", "-"))
local tag
if h.level == 1 then
section = slug
tag = string.format("%s-%s", PROJECT, slug)
elseif h.level == 2 then
if DEDUP_SUBHEADINGS and section then
tag = string.format("%s-%s-%s", PROJECT, section, slug)
else
tag = string.format("%s-%s", PROJECT, slug)
end
end
-- Deeper levels are rendered without a tag, so they stay unresolvable.
if tag and h.identifier and #h.identifier > 0 then
ANCHORS[h.identifier] = { tag = tag, text = stringify(h) }
end
end,
})
local d = blocks(doc.blocks)
local notes = renderNotes()
local toc = renderToc()
Expand Down Expand Up @@ -480,7 +509,17 @@ Writer.Inline.Link = function(el)
if string.starts_with(tgt, "https://neovim.io/doc/") then
return "|" .. s .. "|"
elseif string.starts_with(tgt, "#") then
return "|" .. PROJECT .. "-" .. s:lower():gsub("%s", "-") .. "|"
local anchor = ANCHORS[tgt:sub(2)]
if anchor then
-- Keep wording that adds something; drop it when it just repeats the
-- heading, which the tag already spells out.
if normalize(s) == normalize(anchor.text) then
return "|" .. anchor.tag .. "|"
end
return s .. " |" .. anchor.tag .. "|"
end
io.stderr:write(string.format("panvimdoc: '%s' has no tagged heading\n", tgt))
return s
elseif string.starts_with(s, "http") then
return "<" .. s .. ">"
else
Expand Down
9 changes: 9 additions & 0 deletions tests/fixtures/render/internal-links_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Setup

See [Options](#options) and [the options below](#options).

Also [`Options`](#options).

# Options

Text.
25 changes: 25 additions & 0 deletions tests/fixtures/render/internal-links_1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
*test.txt* Test Description
For NVIM v0.8.0 Last change: 2026 April 12

==============================================================================
Table of Contents *test-table-of-contents*

1. Setup |test-setup|
2. Options |test-options|

==============================================================================
1. Setup *test-setup*

See |test-options| and the options below |test-options|.

Also |test-options|.


==============================================================================
2. Options *test-options*

Text.

Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>

vim:tw=78:ts=8:noet:ft=help:norl:
9 changes: 9 additions & 0 deletions tests/fixtures/render/internal-links_2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Usage

Read the [Lua API](#lua-api) section.

# Reference

## Lua API

Text.
27 changes: 27 additions & 0 deletions tests/fixtures/render/internal-links_2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
*test.txt* Test Description
For NVIM v0.8.0 Last change: 2026 April 12

==============================================================================
Table of Contents *test-table-of-contents*

1. Usage |test-usage|
2. Reference |test-reference|
- Lua API |test-reference-lua-api|

==============================================================================
1. Usage *test-usage*

Read the |test-reference-lua-api| section.


==============================================================================
2. Reference *test-reference*


LUA API *test-reference-lua-api*

Text.

Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>

vim:tw=78:ts=8:noet:ft=help:norl:
36 changes: 36 additions & 0 deletions tests/fixtures/render_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,42 @@
"doc_mapping_project_name": true
}
},
{
"name": "internal-links_1",
"input": "fixtures/render/internal-links_1.md",
"expected": "fixtures/render/internal-links_1.txt",
"options": {
"toc": true,
"dedup_subheadings": false,
"treesitter": true,
"demojify": false,
"description": "Test Description",
"vimversion": "NVIM v0.8.0",
"ignore_rawblocks": true,
"shift_heading_level_by": 0,
"increment_heading_level_by": 0,
"doc_mapping": true,
"doc_mapping_project_name": true
}
},
{
"name": "internal-links_2",
"input": "fixtures/render/internal-links_2.md",
"expected": "fixtures/render/internal-links_2.txt",
"options": {
"toc": true,
"dedup_subheadings": true,
"treesitter": true,
"demojify": false,
"description": "Test Description",
"vimversion": "NVIM v0.8.0",
"ignore_rawblocks": true,
"shift_heading_level_by": 0,
"increment_heading_level_by": 0,
"doc_mapping": true,
"doc_mapping_project_name": true
}
},
{
"name": "mapping_1",
"input": "fixtures/render/mapping_1.md",
Expand Down