1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Wiki internal links handling.
19 import markdown
::wikilinks
23 # Looks up a WikiEntry by its `name`.
26 # 1. Looks in the current section
27 # 2. Looks in the current section children
28 # 3. Looks in the current section parent
29 # 4. Looks up to wiki root
31 # Returns `null` if no article can be found.
32 fun lookup_entry_by_name
(context
: WikiEntry, name
: String): nullable WikiEntry do
33 var section
: nullable WikiEntry = context
.parent
or else context
34 var res
= section
.lookup_entry_by_name
(name
)
35 if res
!= null then return res
36 while section
!= null do
37 if section
.name
== name
then return section
38 if section
.children
.has_key
(name
) then return section
.children
[name
]
39 section
= section
.parent
44 # Looks up a WikiEntry by its `title`.
47 # 1. Looks in the current section
48 # 2. Looks in the current section children
49 # 3. Looks in the current section parent
50 # 4. Looks up to wiki root
52 # Returns `null` if no article can be found.
53 fun lookup_entry_by_title
(context
: WikiEntry, title
: String): nullable WikiEntry do
54 var section
: nullable WikiEntry = context
.parent
or else context
55 var res
= section
.lookup_entry_by_title
(title
)
56 if res
!= null then return res
57 while section
!= null do
58 if section
.title
.to_lower
== title
.to_lower
then return section
59 for child
in section
.children
.values
do
60 if child
.title
.to_lower
== title
.to_lower
then return child
62 section
= section
.parent
67 # Looks up a WikiEntry by its `path`.
69 # Path can be relative from `context` like `context/entry`.
70 # Or absolute like `/entry1/entry2`.
72 # Returns `null` if no article can be found.
73 fun lookup_entry_by_path
(context
: WikiEntry, path
: String): nullable WikiEntry do
74 var entry
= context
.parent
or else context
75 var parts
= path
.split_with
("/")
76 if path
.has_prefix
("/") then
78 if parts
.is_empty
then return root_section
.index
81 while not parts
.is_empty
do
82 var name
= parts
.shift
83 if name
.is_empty
then continue
84 if entry
.name
== name
then continue
85 if not entry
.children
.has_key
(name
) then return null
86 entry
= entry
.children
[name
]
91 # Trails between pages
93 # Trails are represented as a forest of entries.
94 # This way it is possible to represent a flat-trail as a visit of a tree.
95 var trails
= new OrderedTree[WikiEntry]
100 # Relative path to `self` from the target root_url
101 fun href
: String do return breadcrumbs
.join
("/")
103 # Relative path to the directory `self` from the target root_url
104 fun dir_href
: String do return href
.dirname
106 # A relative `href` to `self` from the page `context`.
108 # Should be used to navigate between documents.
109 fun href_from
(context
: WikiEntry): String
111 var res
= context
.dir_href
.relpath
(href
)
115 # A relative hyperlink <a> to `self` from the page `context`.
117 # If `text` is not given, `title` will be used instead.
118 fun a_from
(context
: WikiEntry, text
: nullable Text): Writable
120 var title
= title
.html_escape
121 if text
== null then text
= title
else text
= text
.html_escape
122 var href
= href_from
(context
)
123 return """<a href="{{{href}}}" title="{{{title}}}">{{{text}}}</a>"""
128 if not is_dirty
and not wiki
.force_render
then return
129 render_sidebar_wikilinks
132 # Search in `self` then `self.children` if an entry has the name `name`.
133 fun lookup_entry_by_name
(name
: String): nullable WikiEntry do
134 if children
.has_key
(name
) then return children
[name
]
135 for child
in children
.values
do
136 var res
= child
.lookup_entry_by_name
(name
)
137 if res
!= null then return res
142 # Search in `self` then `self.children` if an entry has the title `title`.
143 fun lookup_entry_by_title
(title
: String): nullable WikiEntry do
144 for child
in children
.values
do
145 if child
.title
.to_lower
== title
.to_lower
then return child
147 for child
in children
.values
do
148 var res
= child
.lookup_entry_by_title
(title
)
149 if res
!= null then return res
154 private var md_proc
: NitiwikiMdProcessor is lazy
do
155 return new NitiwikiMdProcessor(wiki
, self)
158 # Process wikilinks from sidebar.
159 private fun render_sidebar_wikilinks
do
160 var blocks
= sidebar
.blocks
161 for i
in [0..blocks
.length
[ do
162 blocks
[i
] = md_proc
.process
(blocks
[i
].to_s
).write_to_string
163 md_proc
.emitter
.decorator
.headlines
.clear
168 redef class WikiSection
170 # The index page for this section.
172 # If no file `index.md` exists for this section,
173 # a summary is generated using contained articles.
174 var index
: WikiArticle is lazy
do
175 for child
in children
.values
do
176 if child
isa WikiArticle and child
.is_index
then return child
178 return new WikiSectionIndex(wiki
, "index", self)
181 redef fun dir_href
do return href
184 redef class WikiArticle
186 # Headlines ids and titles.
187 var headlines
= new ArrayMap[String, HeadLine]
189 # Is `self` an index page?
191 # Checks if `self.name == "index"`.
192 fun is_index
: Bool do return name
== "index"
195 if parent
== null then
198 return parent
.href
.join_path
("{name}.html")
204 if not is_dirty
and not wiki
.force_render
or not has_source
then return
205 content
= md_proc
.process
(md
.as(not null))
206 headlines
.recover_with
(md_proc
.emitter
.decorator
.headlines
)
210 # A `WikiArticle` that contains the section index tree.
211 class WikiSectionIndex
214 # The section described by `self`.
215 var section
: WikiSection
217 redef fun title
do return section
.title
219 redef fun href
do return section
.href
221 redef fun dir_href
do return section
.dir_href
224 # A MarkdownProcessor able to parse wiki links.
225 class NitiwikiMdProcessor
226 super MarkdownProcessor
228 # Wiki used to resolve links.
231 # Article parsed by `self`.
233 # Used to contextualize links.
234 var context
: WikiEntry
237 emitter
= new MarkdownEmitter(self)
238 emitter
.decorator
= new NitiwikiDecorator(wiki
, context
)
242 # The decorator associated to `MarkdownProcessor`.
243 class NitiwikiDecorator
246 # Wiki used to resolve links.
249 # Article used to contextualize links.
250 var context
: WikiEntry
252 redef fun add_wikilink
(v
, token
) do
253 var wiki
= v
.processor
.as(NitiwikiMdProcessor).wiki
254 var target
: nullable WikiEntry = null
255 var anchor
: nullable String = null
256 var link
= token
.link
257 if link
== null then return
258 var name
= token
.name
260 if not link
.has_prefix
("http://") and not link
.has_prefix
("https://") then
261 # Extract commands from the link.
263 var command_split
= link
.split_once_on
(":")
264 if command_split
.length
> 1 then
265 command
= command_split
[0].trim
266 link
= command_split
[1].trim
269 if link
.has
("#") then
270 var parts
= link
.split_with
("#")
272 anchor
= parts
.subarray
(1, parts
.length
- 1).join
("#")
274 if link
.has
("/") then
275 target
= wiki
.lookup_entry_by_path
(context
, link
.to_s
)
277 target
= wiki
.lookup_entry_by_name
(context
, link
.to_s
)
278 if target
== null then
279 target
= wiki
.lookup_entry_by_title
(context
, link
.to_s
)
282 if target
!= null then
283 if name
== null then name
= target
.title
284 link
= target
.href_from
(context
)
286 if command
== "trail" then
287 if target
isa WikiSection then target
= target
.index
288 wiki
.trails
.add
(context
, target
)
291 wiki
.message
("Warning: unknown wikilink `{link}` (in {context.src_path.as(not null)})", 0)
292 v
.add
"class=\"broken\
" "
296 append_value(v, link)
297 if anchor != null then append_value(v, "#{anchor}")
299 var comment = token.comment
300 if comment != null and not comment.is_empty then
302 append_value
(v
, comment
)
306 if name == null then name = link