···5050 .iter_mut()
5151 .flat_map(|(base, page)| page.rel_links.iter_mut().map(move |link| (base, link)))
5252 {
5353- let url = if link.link.starts_with('/') {
5353+ let file = if link.link.starts_with('/') {
5454 self.vcs_root.join(&link.link[1..])
5555 } else {
5656 base.join(&link.link)
···5858 .context("couldn't derive url")
5959 .tap_err(log_debug!());
60606161- let Ok(url) = url else {
6161+ let Ok(file_url) = file else {
6262 link.status = LinkStatus::Ignored;
6363 continue;
6464 };
65656666 let env = self;
6767+ let page_url = base.as_ref();
6768 let fragments = &fragments;
68696970 Resolver {
7071 link,
7171- url,
7272+ page_url,
7373+ file_url,
7274 env,
7375 fragments,
7476 }
···95979698#[must_use]
9799struct Resolver<'a, 'r> {
9898- url: Url,
100100+ file_url: Url,
101101+ page_url: &'a Url,
99102 link: &'a mut RelativeLink<'r>,
100103 env: &'a Environment,
101104 fragments: &'a Fragments,
···103106104107impl Resolver<'_, '_> {
105108 fn resolve(self) {
106106- if self.url.scheme() == "file" {
109109+ if self.file_url.scheme() == "file" {
107110 self.resolve_file()
108111 } else if let Some(book) = &self.env.config.book_url {
109109- if let Some(page) = book.0.make_relative(&self.url) {
112112+ if let Some(page) = book.0.make_relative(&self.file_url) {
110113 self.resolve_page(page);
111114 } else {
112115 self.link.status = LinkStatus::Ignored;
···119122 fn resolve_file(self) {
120123 let Self {
121124 link,
122122- url,
125125+ page_url,
126126+ file_url,
123127 env,
124128 fragments,
125129 } = self;
126130127127- let Ok(path) = url.to_file_path() else {
131131+ let Ok(path) = file_url.to_file_path() else {
128132 link.status = LinkStatus::Ignored;
129133 return;
130134 };
···141145142146 let Ok(rel) = env
143147 .book_src
144144- .make_relative(&url)
148148+ .make_relative(&file_url)
145149 .context("url is from a different origin")
146150 .tap_err(log_debug!())
147151 else {
···152156 .config
153157 .always_link
154158 .iter()
155155- .any(|suffix| url.path().ends_with(suffix));
159159+ .any(|suffix| file_url.path().ends_with(suffix));
156160157157- if !rel.starts_with("../") && !always_link {
158158- link.status = LinkStatus::Published;
161161+ if !always_link && !rel.starts_with("../") {
162162+ if link.link.starts_with('/') {
163163+ // mdbook doesn't support absolute paths like VS Code does
164164+ link.link = page_url.make_relative(&file_url).unwrap().into();
165165+ link.status = LinkStatus::Rewritten
166166+ } else {
167167+ link.status = LinkStatus::Published;
168168+ }
159169 Self {
160170 link,
161161- url,
171171+ page_url,
172172+ file_url,
162173 env,
163174 fragments,
164175 }
···168179169180 let Ok(rel) = env
170181 .vcs_root
171171- .make_relative(&url)
182182+ .make_relative(&file_url)
172183 .context("url is from a different origin")
173184 .tap_err(log_debug!())
174185 else {
···191202192203 fn resolve_page(self, page: String) {
193204 let Self {
194194- url,
205205+ file_url,
206206+ page_url,
195207 link,
196208 env,
197209 fragments,
···236248 .tap_err(log_debug!());
237249238250 if matches!(exists, Ok(true)) {
239239- let url = {
251251+ let file_url = {
240252 let mut file = file;
241241- file.set_query(url.query());
242242- file.set_fragment(url.fragment());
253253+ file.set_query(file_url.query());
254254+ file.set_fragment(file_url.fragment());
243255 file
244256 };
245257246246- link.link = url.to_string().into();
258258+ link.link = file_url.to_string().into();
247259 link.status = LinkStatus::Published;
248260249261 Self {
250262 link,
251251- url,
263263+ page_url,
264264+ file_url,
252265 env,
253266 fragments,
254267 }
···266279267280 fn resolve_fragment(self) {
268281 let Self {
282282+ mut file_url,
269283 link,
270270- mut url,
271284 fragments,
272285 ..
273286 } = self;
274287275275- let Some(fragment) = url
288288+ let Some(fragment) = file_url
276289 .fragment()
277290 .and_then(|f| percent_decode_str(f).decode_utf8().ok().or(Some(f.into())))
278291 .map(|f| f.into_owned())
···280293 return;
281294 };
282295283283- url.set_fragment(None);
296296+ file_url.set_fragment(None);
284297285298 let found = fragments
286299 .0
287287- .get(&url)
300300+ .get(&file_url)
288301 .map(|f| f.contains(&fragment))
289302 .unwrap_or(false);
290303291291- url.set_fragment(Some(&fragment));
304304+ file_url.set_fragment(Some(&fragment));
292305293306 if !found {
294307 link.status = LinkStatus::NoSuchFragment;
···323336 Ignored,
324337 /// Link to a file under src/
325338 Published,
339339+ /// Link to a file under src/ but was rewritten
340340+ Rewritten,
326341 /// Link to a file under source control
327342 Permalink,
328343 /// Link to a file outside source control
···483498 self.rel_links
484499 .iter()
485500 .filter_map(|link| {
486486- if !matches!(link.status, LinkStatus::Permalink) {
501501+ if !matches!(link.status, LinkStatus::Permalink | LinkStatus::Rewritten) {
487502 return None;
488503 }
489504 let start = match link.usage {
+7
crates/mdbookkit/src/lib.rs
···11+//! Toolkit for [`mdbook`].
22+//!
33+//! This is the lib documentation. If you are looking for the mdBook [preprocessors]
44+//! that this crate provides, visit <https://tonywu6.github.io/mdbookkit/> instead.
55+//!
66+//! [preprocessors]: https://rust-lang.github.io/mdBook/format/configuration/preprocessors.html
77+18#[cfg(feature = "common-logger")]
29pub mod diagnostics;
310pub mod env;
···75757676</figure>
77777878+> [!NOTE]
7979+>
8080+> `book-url` only enables validation, and is only for links to your book, not to GitHub.
8181+7882<!-- prettier-ignore-start -->
79838084[mdbook-include]: https://rust-lang.github.io/mdBook/format/mdbook.html#including-files