|
1 | 1 | use std::cell::RefCell;
|
2 | 2 | use std::default::Default;
|
3 |
| -use std::fmt; |
4 | 3 | use std::hash::Hash;
|
5 |
| -use std::iter; |
6 | 4 | use std::lazy::SyncOnceCell as OnceCell;
|
7 | 5 | use std::path::PathBuf;
|
8 | 6 | use std::rc::Rc;
|
9 | 7 | use std::sync::Arc;
|
10 |
| -use std::vec; |
| 8 | +use std::{cmp, fmt, iter}; |
11 | 9 |
|
12 | 10 | use arrayvec::ArrayVec;
|
13 | 11 |
|
@@ -55,6 +53,9 @@ crate use self::Type::{
|
55 | 53 | };
|
56 | 54 | crate use self::Visibility::{Inherited, Public};
|
57 | 55 |
|
| 56 | +#[cfg(test)] |
| 57 | +mod tests; |
| 58 | + |
58 | 59 | crate type ItemIdSet = FxHashSet<ItemId>;
|
59 | 60 |
|
60 | 61 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
@@ -1028,6 +1029,86 @@ crate fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String {
|
1028 | 1029 | acc
|
1029 | 1030 | }
|
1030 | 1031 |
|
| 1032 | +/// Removes excess indentation on comments in order for the Markdown |
| 1033 | +/// to be parsed correctly. This is necessary because the convention for |
| 1034 | +/// writing documentation is to provide a space between the /// or //! marker |
| 1035 | +/// and the doc text, but Markdown is whitespace-sensitive. For example, |
| 1036 | +/// a block of text with four-space indentation is parsed as a code block, |
| 1037 | +/// so if we didn't unindent comments, these list items |
| 1038 | +/// |
| 1039 | +/// /// A list: |
| 1040 | +/// /// |
| 1041 | +/// /// - Foo |
| 1042 | +/// /// - Bar |
| 1043 | +/// |
| 1044 | +/// would be parsed as if they were in a code block, which is likely not what the user intended. |
| 1045 | +fn unindent_doc_fragments(docs: &mut Vec<DocFragment>) { |
| 1046 | + // `add` is used in case the most common sugared doc syntax is used ("/// "). The other |
| 1047 | + // fragments kind's lines are never starting with a whitespace unless they are using some |
| 1048 | + // markdown formatting requiring it. Therefore, if the doc block have a mix between the two, |
| 1049 | + // we need to take into account the fact that the minimum indent minus one (to take this |
| 1050 | + // whitespace into account). |
| 1051 | + // |
| 1052 | + // For example: |
| 1053 | + // |
| 1054 | + // /// hello! |
| 1055 | + // #[doc = "another"] |
| 1056 | + // |
| 1057 | + // In this case, you want "hello! another" and not "hello! another". |
| 1058 | + let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind) |
| 1059 | + && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc) |
| 1060 | + { |
| 1061 | + // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to |
| 1062 | + // "decide" how much the minimum indent will be. |
| 1063 | + 1 |
| 1064 | + } else { |
| 1065 | + 0 |
| 1066 | + }; |
| 1067 | + |
| 1068 | + // `min_indent` is used to know how much whitespaces from the start of each lines must be |
| 1069 | + // removed. Example: |
| 1070 | + // |
| 1071 | + // /// hello! |
| 1072 | + // #[doc = "another"] |
| 1073 | + // |
| 1074 | + // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum |
| 1075 | + // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4 |
| 1076 | + // (5 - 1) whitespaces. |
| 1077 | + let Some(min_indent) = docs |
| 1078 | + .iter() |
| 1079 | + .map(|fragment| { |
| 1080 | + fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { |
| 1081 | + if line.chars().all(|c| c.is_whitespace()) { |
| 1082 | + min_indent |
| 1083 | + } else { |
| 1084 | + // Compare against either space or tab, ignoring whether they are |
| 1085 | + // mixed or not. |
| 1086 | + let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); |
| 1087 | + cmp::min(min_indent, whitespace) |
| 1088 | + + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add } |
| 1089 | + } |
| 1090 | + }) |
| 1091 | + }) |
| 1092 | + .min() |
| 1093 | + else { |
| 1094 | + return; |
| 1095 | + }; |
| 1096 | + |
| 1097 | + for fragment in docs { |
| 1098 | + if fragment.doc == kw::Empty { |
| 1099 | + continue; |
| 1100 | + } |
| 1101 | + |
| 1102 | + let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { |
| 1103 | + min_indent - add |
| 1104 | + } else { |
| 1105 | + min_indent |
| 1106 | + }; |
| 1107 | + |
| 1108 | + fragment.indent = min_indent; |
| 1109 | + } |
| 1110 | +} |
| 1111 | + |
1031 | 1112 | /// A link that has not yet been rendered.
|
1032 | 1113 | ///
|
1033 | 1114 | /// This link will be turned into a rendered link by [`Item::links`].
|
@@ -1119,6 +1200,8 @@ impl Attributes {
|
1119 | 1200 | }
|
1120 | 1201 | }
|
1121 | 1202 |
|
| 1203 | + unindent_doc_fragments(&mut doc_strings); |
| 1204 | + |
1122 | 1205 | Attributes { doc_strings, other_attrs }
|
1123 | 1206 | }
|
1124 | 1207 |
|
|
0 commit comments