Skip to content

Commit 1c8bd3c

Browse files
authored
Merge pull request #128 from machow/test-spec-interlinks
Lua interlinks filter
2 parents 830f140 + c4570dd commit 1c8bd3c

File tree

18 files changed

+754
-370
lines changed

18 files changed

+754
-370
lines changed

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
1+
EXAMPLE_INTERLINKS=quartodoc/tests/example_interlinks
2+
13
README.md: README.qmd
24
quarto render $<
35

6+
7+
# These 2 rules are used to generate the example_interlinks folder,
8+
# which contains a full example for the interlinks filter to be tested
9+
10+
$(EXAMPLE_INTERLINKS): scripts/filter-spec/generate_files.py
11+
python3 $<
12+
13+
$(EXAMPLE_INTERLINKS)/test.qmd: scripts/filter-spec/generate_test_qmd.py
14+
python3 $<
15+
16+
$(EXAMPLE_INTERLINKS)/test.md: $(EXAMPLE_INTERLINKS)/test.qmd _extensions/interlinks-experimental/interlinks.lua
17+
cd $(EXAMPLE_INTERLINKS) && quarto render test.qmd --to gfm
18+
19+
420
examples/%/_site: examples/%/_quarto.yml
521
cd examples/$* \
622
&& quarto add --no-prompt ../.. \
@@ -9,6 +25,7 @@ examples/%/_site: examples/%/_quarto.yml
925
cd examples/$* && quartodoc interlinks
1026
quarto render $(dir $<)
1127

28+
1229
docs/examples/%: examples/%/_site
1330
rm -rf docs/examples/$*
1431
cp -rv $< $@
@@ -21,5 +38,7 @@ docs-build: docs-build-examples
2138
cd docs && quartodoc interlinks
2239
quarto render docs
2340

41+
test-interlinks: quartodoc/tests/example_interlinks/test.md
42+
2443
requirements-dev.txt:
2544
pip-compile setup.cfg --extra dev -o $@

_extensions/interlinks/README.md

Lines changed: 0 additions & 30 deletions
This file was deleted.

_extensions/interlinks/_extension.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ version: 1.0.0
44
quarto-required: ">=1.2.0"
55
contributes:
66
filters:
7-
- interlinks.py
7+
- interlinks.lua

_extensions/interlinks/example.qmd

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
local function read_json(filename)
2+
local file = io.open(filename, "r")
3+
if file == nil then
4+
return nil
5+
end
6+
local str = file:read("a")
7+
file:close()
8+
return quarto.json.decode(str)
9+
end
10+
11+
local inventory = {}
12+
13+
function lookup(search_object)
14+
15+
local results = {}
16+
for ii, inventory in ipairs(inventory) do
17+
for jj, item in ipairs(inventory.items) do
18+
-- e.g. :external+<inv_name>:<domain>:<role>:`<name>`
19+
if item.inv_name and item.inv_name ~= search_object.inv_name then
20+
goto continue
21+
end
22+
23+
if item.name ~= search_object.name then
24+
goto continue
25+
end
26+
27+
if search_object.role and item.role ~= search_object.role then
28+
goto continue
29+
end
30+
31+
if search_object.domain and item.domain ~= search_object.domain then
32+
goto continue
33+
else
34+
table.insert(results, item)
35+
36+
goto continue
37+
end
38+
39+
::continue::
40+
end
41+
end
42+
43+
if #results == 1 then
44+
return results[1]
45+
end
46+
if #results > 1 then
47+
print("Found multiple matches for " .. search_object.name)
48+
quarto.utils.dump(results)
49+
return nil
50+
end
51+
if #results == 0 then
52+
print("Found no matches for object:")
53+
quarto.utils.dump(search_object)
54+
end
55+
56+
return nil
57+
end
58+
59+
function mysplit (inputstr, sep)
60+
if sep == nil then
61+
sep = "%s"
62+
end
63+
local t={}
64+
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
65+
table.insert(t, str)
66+
end
67+
return t
68+
end
69+
70+
local function normalize_role(role)
71+
if role == "func" then
72+
return "function"
73+
end
74+
return role
75+
end
76+
77+
local function build_search_object(str)
78+
local starts_with_colon = str:sub(1, 1) == ":"
79+
local search = {}
80+
if starts_with_colon then
81+
local t = mysplit(str, ":")
82+
if #t == 2 then
83+
-- e.g. :py:func:`my_func`
84+
search.role = normalize_role(t[1])
85+
search.name = t[2]:match("%%60(.*)%%60")
86+
elseif #t == 3 then
87+
-- e.g. :py:func:`my_func`
88+
search.domain = t[1]
89+
search.role = normalize_role(t[2])
90+
search.name = t[3]:match("%%60(.*)%%60")
91+
elseif #t == 4 then
92+
-- e.g. :ext+inv:py:func:`my_func`
93+
search.external = true
94+
95+
search.inv_name = t[1]:match("external%+(.*)")
96+
search.domain = t[2]
97+
search.role = normalize_role(t[3])
98+
search.name = t[4]:match("%%60(.*)%%60")
99+
else
100+
print("couldn't parse this link: " .. str)
101+
return {}
102+
end
103+
else
104+
search.name = str:match("%%60(.*)%%60")
105+
end
106+
107+
if search.name == nil then
108+
print("couldn't parse this link: " .. str)
109+
return {}
110+
end
111+
112+
if search.name:sub(1, 1) == "~" then
113+
search.shortened = true
114+
search.name = search.name:sub(2, -1)
115+
end
116+
return search
117+
end
118+
119+
function report_broken_link(link, search_object, replacement)
120+
-- TODO: how to unescape html elements like [?
121+
return pandoc.Code(pandoc.utils.stringify(link.content))
122+
end
123+
124+
function Link(link)
125+
-- do not process regular links ----
126+
if not link.target:match("%%60") then
127+
return link
128+
end
129+
130+
-- lookup item ----
131+
local search = build_search_object(link.target)
132+
local item = lookup(search)
133+
134+
-- determine replacement, used if no link text specified ----
135+
local original_text = pandoc.utils.stringify(link.content)
136+
local replacement = search.name
137+
if search.shortened then
138+
local t = mysplit(search.name, ".")
139+
replacement = t[#t]
140+
end
141+
142+
-- set link text ----
143+
if original_text == "" then
144+
link.content = replacement
145+
end
146+
147+
-- report broken links ----
148+
if item == nil then
149+
return report_broken_link(link, search)
150+
end
151+
link.target = item.uri:gsub("%$$", search.name)
152+
153+
154+
return link
155+
end
156+
157+
function fixup_json(json, prefix)
158+
for _, item in ipairs(json.items) do
159+
item.uri = prefix .. item.uri
160+
end
161+
table.insert(inventory, json)
162+
end
163+
164+
return {
165+
{
166+
Meta = function(meta)
167+
local json
168+
local prefix
169+
for k, v in pairs(meta.interlinks.sources) do
170+
json = read_json(quarto.project.offset .. "/_inv/" .. k .. "_objects.json")
171+
prefix = pandoc.utils.stringify(v.url)
172+
fixup_json(json, prefix)
173+
end
174+
json = read_json(quarto.project.offset .. "/objects.json")
175+
if json ~= nil then
176+
fixup_json(json, "/")
177+
end
178+
end
179+
},
180+
{
181+
Link = Link
182+
}
183+
}

0 commit comments

Comments
 (0)