Repository: rust-lang/mdBook (language: rust)

Issue:
Custom section (header) ID
It would be useful if the section IDs can be specified manually (such as `# About foo {#foo}`, supported by many processors including [zola](https://github.com/getzola/zola/issues/433)).

For example, it would be useful when:

* Some of section names are very long, but wants IDs to be short enough.
* Attempt to prevent future link breakage, which can be caused by sections renaming or mdbook algorithm change.
* Providing multiple sites with different languages, but wants URIs (especially paths and fragments) to be coherent English.

Users can already control paths by setting filename, then it is natural if users can also control fragment part of URIs (`/user/specified/path.html#THIS-PART`).

Is this feature simply not implemented or considered yet, or are there some reasons to object to this feature?

Maybe related: #880


Discussion / hints:
I think such a change would be best made as an extension in [pulldown-cmark](https://github.com/raphlinus/pulldown-cmark), the markdown processor mdbook uses.  Perhaps ask over there if it would be considered?

I usually just add `a` tags like `<a id="example-fragment"></a>` just before the header, but is certainly not ideal.

pulldown-cmark v0.9.0 now has [`Options::ENABLE_HEADING_ATTRIBUTES`](https://docs.rs/pulldown-cmark/0.9.0/pulldown_cmark/struct.Options.html#associatedconstant.ENABLE_HEADING_ATTRIBUTES) extension, which enables users to write custom ID and classes such as  `# Examples {#examples-foo .example}`.
@ehuss This is easy to work on, should I make this a default option or configurable?

Also I should document it on the mdbook, right?
Thanks for taking a look!

I think it should be fine to have it always be on. I think the risk of it causing issues is low.

Just make sure to test that things like the search index work correctly.

And yea, the documentation would be listed as an extension at https://github.com/rust-lang/mdBook/blob/master/guide/src/format/markdown.md.

Fix patch:
diff --git a/guide/src/format/markdown.md b/guide/src/format/markdown.md
index 963a1538c2..8a92870b67 100644
--- a/guide/src/format/markdown.md
+++ b/guide/src/format/markdown.md
@@ -220,3 +220,16 @@ To enable it, see the [`output.html.curly-quotes`] config option.
 [tables]: https://github.github.com/gfm/#tables-extension-
 [task list extension]: https://github.github.com/gfm/#task-list-items-extension-
 [`output.html.curly-quotes`]: configuration/renderers.md#html-renderer-options
+
+### Heading attributes
+
+Headings can have a custom HTML ID and classes. This let's you maintain the same ID even if you change the heading's text, it also let's you add multiple classes in the heading.
+
+Example:
+```md
+# Example heading { #first .class1 .class2 }
+```
+
+This makes the level 1 heading with the content `Example heading`, ID `first`, and classes `class1` and `class2`. Note that the attributes should be space-separated.
+
+More information can be found in the [heading attrs spec page](https://github.com/raphlinus/pulldown-cmark/blob/master/specs/heading_attrs.txt).
diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs
index ee27fd1376..e753dc2e14 100644
--- a/src/renderer/html_handlebars/hbs_renderer.rs
+++ b/src/renderer/html_handlebars/hbs_renderer.rs
@@ -789,8 +789,10 @@ fn make_data(
 /// Goes through the rendered HTML, making sure all header tags have
 /// an anchor respectively so people can link to sections directly.
 fn build_header_links(html: &str) -> String {
-    static BUILD_HEADER_LINKS: Lazy<Regex> =
-        Lazy::new(|| Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap());
+    static BUILD_HEADER_LINKS: Lazy<Regex> = Lazy::new(|| {
+        Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
+    });
+    static IGNORE_CLASS: &[&str] = &["menu-title"];
 
     let mut id_counter = HashMap::new();
 
@@ -800,7 +802,22 @@ fn build_header_links(html: &str) -> String {
                 .parse()
                 .expect("Regex should ensure we only ever get numbers here");
 
-            insert_link_into_header(level, &caps[2], &mut id_counter)
+            // Ignore .menu-title because now it's getting detected by the regex.
+            if let Some(classes) = caps.get(3) {
+                for class in classes.as_str().split(" ") {
+                    if IGNORE_CLASS.contains(&class) {
+                        return caps[0].to_string();
+                    }
+                }
+            }
+
+            insert_link_into_header(
+                level,
+                &caps[4],
+                caps.get(2).map(|x| x.as_str().to_string()),
+                caps.get(3).map(|x| x.as_str().to_string()),
+                &mut id_counter,
+            )
         })
         .into_owned()
 }
@@ -810,15 +827,21 @@ fn build_header_links(html: &str) -> String {
 fn insert_link_into_header(
     level: usize,
     content: &str,
+    id: Option<String>,
+    classes: Option<String>,
     id_counter: &mut HashMap<String, usize>,
 ) -> String {
-    let id = utils::unique_id_from_content(content, id_counter);
+    let id = id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter));
+    let classes = classes
+        .map(|s| format!(" class=\"{s}\""))
+        .unwrap_or_default();
 
     format!(
-        r##"<h{level} id="{id}"><a class="header" href="#{id}">{text}</a></h{level}>"##,
+        r##"<h{level} id="{id}"{classes}><a class="header" href="#{id}">{text}</a></h{level}>"##,
         level = level,
         id = id,
-        text = content
+        text = content,
+        classes = classes
     )
 }
 
@@ -1015,6 +1038,21 @@ mod tests {
                 "<h1>Foo</h1><h3>Foo</h3>",
                 r##"<h1 id="foo"><a class="header" href="#foo">Foo</a></h1><h3 id="foo-1"><a class="header" href="#foo-1">Foo</a></h3>"##,
             ),
+            // id only
+            (
+                r##"<h1 id="foobar">Foo</h1>"##,
+                r##"<h1 id="foobar"><a class="header" href="#foobar">Foo</a></h1>"##,
+            ),
+            // class only
+            (
+                r##"<h1 class="class1 class2">Foo</h1>"##,
+                r##"<h1 id="foo" class="class1 class2"><a class="header" href="#foo">Foo</a></h1>"##,
+            ),
+            // both id and class
+            (
+                r##"<h1 id="foobar" class="class1 class2">Foo</h1>"##,
+                r##"<h1 id="foobar" class="class1 class2"><a class="header" href="#foobar">Foo</a></h1>"##,
+            ),
         ];
 
         for (src, should_be) in inputs {
diff --git a/src/renderer/html_handlebars/search.rs b/src/renderer/html_handlebars/search.rs
index a9e2f5ca61..24d62fda2f 100644
--- a/src/renderer/html_handlebars/search.rs
+++ b/src/renderer/html_handlebars/search.rs
@@ -138,9 +138,11 @@ fn render_item(
 
                 in_heading = true;
             }
-            Event::End(Tag::Heading(i, ..)) if i as u32 <= max_section_depth => {
+            Event::End(Tag::Heading(i, id, _classes)) if i as u32 <= max_section_depth => {
                 in_heading = false;
-                section_id = Some(utils::unique_id_from_content(&heading, &mut id_counter));
+                section_id = id
+                    .map(|id| id.to_string())
+                    .or_else(|| Some(utils::unique_id_from_content(&heading, &mut id_counter)));
                 breadcrumbs.push(heading.clone());
             }
             Event::Start(Tag::FootnoteDefinition(name)) => {
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 9f67deda70..9156916ea6 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -183,6 +183,7 @@ pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> {
     opts.insert(Options::ENABLE_FOOTNOTES);
     opts.insert(Options::ENABLE_STRIKETHROUGH);
     opts.insert(Options::ENABLE_TASKLISTS);
+    opts.insert(Options::ENABLE_HEADING_ATTRIBUTES);
     if curly_quotes {
         opts.insert(Options::ENABLE_SMART_PUNCTUATION);
     }


Test patch:
diff --git a/test_book/src/individual/heading.md b/test_book/src/individual/heading.md
index df96d74c30..f9f4d5b2ea 100644
--- a/test_book/src/individual/heading.md
+++ b/test_book/src/individual/heading.md
@@ -13,3 +13,9 @@
 ##### Really Small Heading
 
 ###### Is it even a heading anymore - heading
+
+## Custom id {#example-id}
+
+## Custom class {.class1 .class2}
+
+## Both id and class {#example-id2 .class1 .class2}
diff --git a/tests/dummy_book/src/SUMMARY.md b/tests/dummy_book/src/SUMMARY.md
index 49b64a5455..f310508b24 100644
--- a/tests/dummy_book/src/SUMMARY.md
+++ b/tests/dummy_book/src/SUMMARY.md
@@ -14,6 +14,7 @@
     - [Unicode](first/unicode.md)
     - [No Headers](first/no-headers.md)
     - [Duplicate Headers](first/duplicate-headers.md)
+    - [Heading Attributes](first/heading-attributes.md)
 - [Second Chapter](second.md)
     - [Nested Chapter](second/nested.md)
 
diff --git a/tests/dummy_book/src/first/heading-attributes.md b/tests/dummy_book/src/first/heading-attributes.md
new file mode 100644
index 0000000000..a09a22b6b8
--- /dev/null
+++ b/tests/dummy_book/src/first/heading-attributes.md
@@ -0,0 +1,5 @@
+# Heading Attributes {#attrs}
+
+## Heading with classes {.class1 .class2}
+
+## Heading with id and classes {#both .class1 .class2}
diff --git a/tests/rendered_output.rs b/tests/rendered_output.rs
index 813f70fd8f..7626b9e8ac 100644
--- a/tests/rendered_output.rs
+++ b/tests/rendered_output.rs
@@ -35,6 +35,7 @@ const TOC_SECOND_LEVEL: &[&str] = &[
     "1.5. Unicode",
     "1.6. No Headers",
     "1.7. Duplicate Headers",
+    "1.8. Heading Attributes",
     "2.1. Nested Chapter",
 ];
 
@@ -754,6 +755,7 @@ mod search {
         let no_headers = get_doc_ref("first/no-headers.html");
         let duplicate_headers_1 = get_doc_ref("first/duplicate-headers.html#header-text-1");
         let conclusion = get_doc_ref("conclusion.html#conclusion");
+        let heading_attrs = get_doc_ref("first/heading-attributes.html#both");
 
         let bodyidx = &index["index"]["index"]["body"]["root"];
         let textidx = &bodyidx["t"]["e"]["x"]["t"];
@@ -766,7 +768,7 @@ mod search {
         assert_eq!(docs[&some_section]["body"], "");
         assert_eq!(
             docs[&summary]["body"],
-            "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode No Headers Duplicate Headers Second Chapter Nested Chapter Conclusion"
+            "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode No Headers Duplicate Headers Heading Attributes Second Chapter Nested Chapter Conclusion"
         );
         assert_eq!(
             docs[&summary]["breadcrumbs"],
@@ -785,6 +787,10 @@ mod search {
             docs[&no_headers]["body"],
             "Capybara capybara capybara. Capybara capybara capybara. ThisLongWordIsIncludedSoWeCanCheckThatSufficientlyLongWordsAreOmittedFromTheSearchIndex."
         );
+        assert_eq!(
+            docs[&heading_attrs]["breadcrumbs"],
+            "First Chapter » Heading Attributes » Heading with id and classes"
+        );
     }
 
     // Setting this to `true` may cause issues with `cargo watch`,
@@ -946,3 +952,19 @@ fn custom_fonts() {
         &["fonts.css", "myfont.woff"]
     );
 }
+
+#[test]
+fn custom_header_attributes() {
+    let temp = DummyBook::new().build().unwrap();
+    let md = MDBook::load(temp.path()).unwrap();
+    md.build().unwrap();
+
+    let contents = temp.path().join("book/first/heading-attributes.html");
+
+    let summary_strings = &[
+        r##"<h1 id="attrs"><a class="header" href="#attrs">Heading Attributes</a></h1>"##,
+        r##"<h2 id="heading-with-classes" class="class1 class2"><a class="header" href="#heading-with-classes">Heading with classes</a></h2>"##,
+        r##"<h2 id="both" class="class1 class2"><a class="header" href="#both">Heading with id and classes</a></h2>"##,
+    ];
+    assert_contains_strings(&contents, summary_strings);
+}
diff --git a/tests/searchindex_fixture.json b/tests/searchindex_fixture.json
index 3d7062d237..8546302895 100644
--- a/tests/searchindex_fixture.json
+++ b/tests/searchindex_fixture.json
@@ -23,6 +23,9 @@
     "first/duplicate-headers.html#header-text",
     "first/duplicate-headers.html#header-text-1",
     "first/duplicate-headers.html#header-text-2",
+    "first/heading-attributes.html#attrs",
+    "first/heading-attributes.html#heading-with-classes",
+    "first/heading-attributes.html#both",
     "second.html#second-chapter",
     "second/nested.html#testing-relative-links-for-the-print-page",
     "second/nested.html#some-section",
@@ -42,7 +45,7 @@
           "title": 1
         },
         "10": {
-          "body": 19,
+          "body": 21,
           "breadcrumbs": 4,
           "title": 1
         },
@@ -112,21 +115,36 @@
           "title": 2
         },
         "23": {
+          "body": 0,
+          "breadcrumbs": 6,
+          "title": 2
+        },
+        "24": {
+          "body": 0,
+          "breadcrumbs": 6,
+          "title": 2
+        },
+        "25": {
+          "body": 0,
+          "breadcrumbs": 7,
+          "title": 3
+        },
+        "26": {
           "body": 20,
           "breadcrumbs": 4,
           "title": 2
         },
-        "24": {
+        "27": {
           "body": 18,
           "breadcrumbs": 9,
           "title": 5
         },
-        "25": {
+        "28": {
           "body": 0,
           "breadcrumbs": 5,
           "title": 1
         },
-        "26": {
+        "29": {
           "body": 3,
           "breadcrumbs": 2,
           "title": 1
@@ -181,7 +199,7 @@
           "title": "Introduction"
         },
         "10": {
-          "body": "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode No Headers Duplicate Headers Second Chapter Nested Chapter Conclusion",
+          "body": "Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode No Headers Duplicate Headers Heading Attributes Second Chapter Nested Chapter Conclusion",
           "breadcrumbs": "First Chapter » Includes » Summary",
           "id": "10",
           "title": "Summary"
@@ -265,27 +283,45 @@
           "title": "header-text"
         },
         "23": {
+          "body": "",
+          "breadcrumbs": "First Chapter » Heading Attributes » Heading Attributes",
+          "id": "23",
+          "title": "Heading Attributes"
+        },
+        "24": {
+          "body": "",
+          "breadcrumbs": "First Chapter » Heading Attributes » Heading with classes",
+          "id": "24",
+          "title": "Heading with classes"
+        },
+        "25": {
+          "body": "",
+          "breadcrumbs": "First Chapter » Heading Attributes » Heading with id and classes",
+          "id": "25",
+          "title": "Heading with id and classes"
+        },
+        "26": {
           "body": "This makes sure you can insert runnable Rust files. fn main() { println!(\"Hello World!\");\n#\n# // You can even hide lines! :D\n# println!(\"I am hidden! Expand the code snippet to see me\");\n}",
           "breadcrumbs": "Second Chapter » Second Chapter",
-          "id": "23",
+          "id": "26",
           "title": "Second Chapter"
         },
-        "24": {
+        "27": {
           "body": "When we link to the first section , it should work on both the print page and the non-print page. A fragment link should work. Link outside . Some image HTML Link",
           "breadcrumbs": "Second Chapter » Nested Chapter » Testing relative links for the print page",
-          "id": "24",
+          "id": "27",
           "title": "Testing relative links for the print page"
         },
-        "25": {
+        "28": {
           "body": "",
           "breadcrumbs": "Second Chapter » Nested Chapter » Some section",
-          "id": "25",
+          "id": "28",
           "title": "Some section"
         },
-        "26": {
+        "29": {
           "body": "I put &lt;HTML&gt; in here!",
           "breadcrumbs": "Conclusion » Conclusion",
-          "id": "26",
+          "id": "29",
           "title": "Conclusion"
         },
         "3": {
@@ -331,7 +367,7 @@
           "title": "Includes"
         }
       },
-      "length": 27,
+      "length": 30,
       "save": true
     },
     "fields": [
@@ -498,6 +534,41 @@
                   }
                 }
               }
+            },
+            "t": {
+              "df": 0,
+              "docs": {},
+              "t": {
+                "df": 0,
+                "docs": {},
+                "r": {
+                  "df": 0,
+                  "docs": {},
+                  "i": {
+                    "b": {
+                      "df": 0,
+                      "docs": {},
+                      "u": {
+                        "df": 0,
+                        "docs": {},
+                        "t": {
+                          "df": 2,
+                          "docs": {
+                            "10": {
+                              "tf": 1.0
+                            },
+                            "23": {
+                              "tf": 1.0
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "df": 0,
+                    "docs": {}
+                  }
+                }
+              }
             }
           },
           "b": {
@@ -619,7 +690,7 @@
                 "h": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -821,7 +892,7 @@
                           "2": {
                             "tf": 1.0
                           },
-                          "23": {
+                          "26": {
                             "tf": 1.0
                           },
                           "4": {
@@ -856,6 +927,29 @@
               "df": 0,
               "docs": {}
             },
+            "l": {
+              "a": {
+                "df": 0,
+                "docs": {},
+                "s": {
+                  "df": 0,
+                  "docs": {},
+                  "s": {
+                    "df": 2,
+                    "docs": {
+                      "24": {
+                        "tf": 1.0
+                      },
+                      "25": {
+                        "tf": 1.0
+                      }
+                    }
+                  }
+                }
+              },
+              "df": 0,
+              "docs": {}
+            },
             "o": {
               "d": {
                 "df": 0,
@@ -863,7 +957,7 @@
                 "e": {
                   "df": 2,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "4": {
@@ -930,7 +1024,7 @@
                           "10": {
                             "tf": 1.0
                           },
-                          "26": {
+                          "29": {
                             "tf": 1.0
                           }
                         }
@@ -1002,7 +1096,7 @@
           "d": {
             "df": 1,
             "docs": {
-              "23": {
+              "26": {
                 "tf": 1.0
               }
             },
@@ -1115,7 +1209,7 @@
                 "n": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -1156,7 +1250,7 @@
                     "d": {
                       "df": 1,
                       "docs": {
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         }
                       }
@@ -1225,7 +1319,7 @@
                     "0": {
                       "tf": 1.0
                     },
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "4": {
@@ -1261,7 +1355,7 @@
                       "2": {
                         "tf": 1.0
                       },
-                      "24": {
+                      "27": {
                         "tf": 1.0
                       }
                     }
@@ -1272,7 +1366,7 @@
             "n": {
               "df": 3,
               "docs": {
-                "23": {
+                "26": {
                   "tf": 1.0
                 },
                 "7": {
@@ -1346,7 +1440,7 @@
                         "t": {
                           "df": 1,
                           "docs": {
-                            "24": {
+                            "27": {
                               "tf": 1.0
                             }
                           }
@@ -1386,8 +1480,21 @@
             "e": {
               "a": {
                 "d": {
-                  "df": 0,
-                  "docs": {},
+                  "df": 4,
+                  "docs": {
+                    "10": {
+                      "tf": 1.0
+                    },
+                    "23": {
+                      "tf": 1.0
+                    },
+                    "24": {
+                      "tf": 1.0
+                    },
+                    "25": {
+                      "tf": 1.0
+                    }
+                  },
                   "e": {
                     "df": 0,
                     "docs": {},
@@ -1451,7 +1558,7 @@
                     "0": {
                       "tf": 1.0
                     },
-                    "26": {
+                    "29": {
                       "tf": 1.0
                     }
                   }
@@ -1469,7 +1576,7 @@
                     "n": {
                       "df": 2,
                       "docs": {
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         },
                         "7": {
@@ -1484,7 +1591,7 @@
                 "e": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -1502,7 +1609,7 @@
                 "l": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -1511,6 +1618,14 @@
             }
           },
           "i": {
+            "d": {
+              "df": 1,
+              "docs": {
+                "25": {
+                  "tf": 1.0
+                }
+              }
+            },
             "df": 0,
             "docs": {},
             "m": {
@@ -1520,7 +1635,7 @@
                 "g": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -1592,7 +1707,7 @@
                     "t": {
                       "df": 1,
                       "docs": {
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         }
                       }
@@ -1728,7 +1843,7 @@
                     "14": {
                       "tf": 1.4142135623730951
                     },
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "6": {
@@ -1739,7 +1854,7 @@
                 "k": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 2.23606797749979
                     }
                   }
@@ -1793,7 +1908,7 @@
                             "t": {
                               "df": 1,
                               "docs": {
-                                "26": {
+                                "29": {
                                   "tf": 1.0
                                 }
                               }
@@ -1821,7 +1936,7 @@
                 "n": {
                   "df": 3,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "7": {
@@ -1839,7 +1954,7 @@
                 "e": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -1968,7 +2083,7 @@
               "n": {
                 "df": 1,
                 "docs": {
-                  "24": {
+                  "27": {
                     "tf": 1.0
                   }
                 }
@@ -2007,7 +2122,7 @@
                     "d": {
                       "df": 1,
                       "docs": {
-                        "24": {
+                        "27": {
                           "tf": 1.0
                         }
                       }
@@ -2032,7 +2147,7 @@
                     "19": {
                       "tf": 1.0
                     },
-                    "24": {
+                    "27": {
                       "tf": 1.7320508075688772
                     }
                   }
@@ -2145,7 +2260,7 @@
                   "t": {
                     "df": 1,
                     "docs": {
-                      "24": {
+                      "27": {
                         "tf": 1.7320508075688772
                       }
                     },
@@ -2173,7 +2288,7 @@
                                       "o": {
                                         "df": 1,
                                         "docs": {
-                                          "23": {
+                                          "26": {
                                             "tf": 1.0
                                           }
                                         }
@@ -2185,7 +2300,7 @@
                               "i": {
                                 "df": 1,
                                 "docs": {
-                                  "23": {
+                                  "26": {
                                     "tf": 1.0
                                   }
                                 }
@@ -2211,7 +2326,7 @@
               "t": {
                 "df": 1,
                 "docs": {
-                  "26": {
+                  "29": {
                     "tf": 1.0
                   }
                 }
@@ -2247,7 +2362,7 @@
               "l": {
                 "df": 1,
                 "docs": {
-                  "24": {
+                  "27": {
                     "tf": 1.0
                   }
                 }
@@ -2359,7 +2474,7 @@
                       "l": {
                         "df": 1,
                         "docs": {
-                          "23": {
+                          "26": {
                             "tf": 1.0
                           }
                         }
@@ -2397,7 +2512,7 @@
                   },
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -2422,7 +2537,7 @@
                         "10": {
                           "tf": 1.0
                         },
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         }
                       }
@@ -2443,10 +2558,10 @@
                       "n": {
                         "df": 4,
                         "docs": {
-                          "24": {
+                          "27": {
                             "tf": 1.0
                           },
-                          "25": {
+                          "28": {
                             "tf": 1.0
                           },
                           "3": {
@@ -2466,7 +2581,7 @@
               "e": {
                 "df": 1,
                 "docs": {
-                  "23": {
+                  "26": {
                     "tf": 1.0
                   }
                 }
@@ -2490,7 +2605,7 @@
                       "t": {
                         "df": 1,
                         "docs": {
-                          "23": {
+                          "26": {
                             "tf": 1.0
                           }
                         }
@@ -2781,7 +2896,7 @@
                 "e": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -2867,7 +2982,7 @@
                     "17": {
                       "tf": 1.0
                     },
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     },
                     "6": {
@@ -3049,7 +3164,7 @@
                 "k": {
                   "df": 2,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.4142135623730951
                     },
                     "8": {
@@ -3064,7 +3179,7 @@
                       "11": {
                         "tf": 4.69041575982343
                       },
-                      "23": {
+                      "26": {
                         "tf": 1.0
                       }
                     }
@@ -3259,6 +3374,47 @@
                   }
                 }
               }
+            },
+            "t": {
+              "df": 0,
+              "docs": {},
+              "t": {
+                "df": 0,
+                "docs": {},
+                "r": {
+                  "df": 0,
+                  "docs": {},
+                  "i": {
+                    "b": {
+                      "df": 0,
+                      "docs": {},
+                      "u": {
+                        "df": 0,
+                        "docs": {},
+                        "t": {
+                          "df": 4,
+                          "docs": {
+                            "10": {
+                              "tf": 1.0
+                            },
+                            "23": {
+                              "tf": 1.7320508075688772
+                            },
+                            "24": {
+                              "tf": 1.0
+                            },
+                            "25": {
+                              "tf": 1.0
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "df": 0,
+                    "docs": {}
+                  }
+                }
+              }
             }
           },
           "b": {
@@ -3380,7 +3536,7 @@
                 "h": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -3568,7 +3724,7 @@
                       "df": 0,
                       "docs": {},
                       "r": {
-                        "df": 24,
+                        "df": 27,
                         "docs": {
                           "10": {
                             "tf": 2.23606797749979
@@ -3613,12 +3769,21 @@
                             "tf": 1.0
                           },
                           "23": {
-                            "tf": 1.7320508075688772
+                            "tf": 1.0
                           },
                           "24": {
-                            "tf": 1.4142135623730951
+                            "tf": 1.0
                           },
                           "25": {
+                            "tf": 1.0
+                          },
+                          "26": {
+                            "tf": 1.7320508075688772
+                          },
+                          "27": {
+                            "tf": 1.4142135623730951
+                          },
+                          "28": {
                             "tf": 1.4142135623730951
                           },
                           "3": {
@@ -3671,6 +3836,29 @@
               "df": 0,
               "docs": {}
             },
+            "l": {
+              "a": {
+                "df": 0,
+                "docs": {},
+                "s": {
+                  "df": 0,
+                  "docs": {},
+                  "s": {
+                    "df": 2,
+                    "docs": {
+                      "24": {
+                        "tf": 1.4142135623730951
+                      },
+                      "25": {
+                        "tf": 1.4142135623730951
+                      }
+                    }
+                  }
+                }
+              },
+              "df": 0,
+              "docs": {}
+            },
             "o": {
               "d": {
                 "df": 0,
@@ -3678,7 +3866,7 @@
                 "e": {
                   "df": 2,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "4": {
@@ -3745,7 +3933,7 @@
                           "10": {
                             "tf": 1.0
                           },
-                          "26": {
+                          "29": {
                             "tf": 1.7320508075688772
                           }
                         }
@@ -3817,7 +4005,7 @@
           "d": {
             "df": 1,
             "docs": {
-              "23": {
+              "26": {
                 "tf": 1.0
               }
             },
@@ -3939,7 +4127,7 @@
                 "n": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -3980,7 +4168,7 @@
                     "d": {
                       "df": 1,
                       "docs": {
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         }
                       }
@@ -4049,7 +4237,7 @@
                     "0": {
                       "tf": 1.0
                     },
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "4": {
@@ -4071,7 +4259,7 @@
                   "df": 0,
                   "docs": {},
                   "t": {
-                    "df": 22,
+                    "df": 25,
                     "docs": {
                       "10": {
                         "tf": 1.4142135623730951
@@ -4115,9 +4303,18 @@
                       "22": {
                         "tf": 1.0
                       },
+                      "23": {
+                        "tf": 1.0
+                      },
                       "24": {
                         "tf": 1.0
                       },
+                      "25": {
+                        "tf": 1.0
+                      },
+                      "27": {
+                        "tf": 1.0
+                      },
                       "3": {
                         "tf": 1.0
                       },
@@ -4147,7 +4344,7 @@
             "n": {
               "df": 3,
               "docs": {
-                "23": {
+                "26": {
                   "tf": 1.0
                 },
                 "7": {
@@ -4221,7 +4418,7 @@
                         "t": {
                           "df": 1,
                           "docs": {
-                            "24": {
+                            "27": {
                               "tf": 1.0
                             }
                           }
@@ -4261,8 +4458,21 @@
             "e": {
               "a": {
                 "d": {
-                  "df": 0,
-                  "docs": {},
+                  "df": 4,
+                  "docs": {
+                    "10": {
+                      "tf": 1.0
+                    },
+                    "23": {
+                      "tf": 1.7320508075688772
+                    },
+                    "24": {
+                      "tf": 1.7320508075688772
+                    },
+                    "25": {
+                      "tf": 1.7320508075688772
+                    }
+                  },
                   "e": {
                     "df": 0,
                     "docs": {},
@@ -4329,7 +4539,7 @@
                     "0": {
                       "tf": 1.0
                     },
-                    "26": {
+                    "29": {
                       "tf": 1.0
                     }
                   }
@@ -4347,7 +4557,7 @@
                     "n": {
                       "df": 2,
                       "docs": {
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         },
                         "7": {
@@ -4362,7 +4572,7 @@
                 "e": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -4380,7 +4590,7 @@
                 "l": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -4389,6 +4599,14 @@
             }
           },
           "i": {
+            "d": {
+              "df": 1,
+              "docs": {
+                "25": {
+                  "tf": 1.4142135623730951
+                }
+              }
+            },
             "df": 0,
             "docs": {},
             "m": {
@@ -4398,7 +4616,7 @@
                 "g": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -4470,7 +4688,7 @@
                     "t": {
                       "df": 1,
                       "docs": {
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         }
                       }
@@ -4606,7 +4824,7 @@
                     "14": {
                       "tf": 1.4142135623730951
                     },
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "6": {
@@ -4617,7 +4835,7 @@
                 "k": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 2.449489742783178
                     }
                   }
@@ -4671,7 +4889,7 @@
                             "t": {
                               "df": 1,
                               "docs": {
-                                "26": {
+                                "29": {
                                   "tf": 1.0
                                 }
                               }
@@ -4699,7 +4917,7 @@
                 "n": {
                   "df": 3,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     },
                     "7": {
@@ -4717,7 +4935,7 @@
                 "e": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -4833,10 +5051,10 @@
                     "10": {
                       "tf": 1.4142135623730951
                     },
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     },
-                    "25": {
+                    "28": {
                       "tf": 1.0
                     },
                     "4": {
@@ -4876,7 +5094,7 @@
               "n": {
                 "df": 1,
                 "docs": {
-                  "24": {
+                  "27": {
                     "tf": 1.0
                   }
                 }
@@ -4915,7 +5133,7 @@
                     "d": {
                       "df": 1,
                       "docs": {
-                        "24": {
+                        "27": {
                           "tf": 1.0
                         }
                       }
@@ -4940,7 +5158,7 @@
                     "19": {
                       "tf": 1.0
                     },
-                    "24": {
+                    "27": {
                       "tf": 2.0
                     }
                   }
@@ -5053,7 +5271,7 @@
                   "t": {
                     "df": 1,
                     "docs": {
-                      "24": {
+                      "27": {
                         "tf": 2.0
                       }
                     },
@@ -5081,7 +5299,7 @@
                                       "o": {
                                         "df": 1,
                                         "docs": {
-                                          "23": {
+                                          "26": {
                                             "tf": 1.0
                                           }
                                         }
@@ -5093,7 +5311,7 @@
                               "i": {
                                 "df": 1,
                                 "docs": {
-                                  "23": {
+                                  "26": {
                                     "tf": 1.0
                                   }
                                 }
@@ -5119,7 +5337,7 @@
               "t": {
                 "df": 1,
                 "docs": {
-                  "26": {
+                  "29": {
                     "tf": 1.0
                   }
                 }
@@ -5158,7 +5376,7 @@
               "l": {
                 "df": 1,
                 "docs": {
-                  "24": {
+                  "27": {
                     "tf": 1.4142135623730951
                   }
                 }
@@ -5270,7 +5488,7 @@
                       "l": {
                         "df": 1,
                         "docs": {
-                          "23": {
+                          "26": {
                             "tf": 1.0
                           }
                         }
@@ -5308,7 +5526,7 @@
                   },
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -5333,13 +5551,13 @@
                         "10": {
                           "tf": 1.0
                         },
-                        "23": {
+                        "26": {
                           "tf": 1.7320508075688772
                         },
-                        "24": {
+                        "27": {
                           "tf": 1.0
                         },
-                        "25": {
+                        "28": {
                           "tf": 1.0
                         }
                       }
@@ -5360,10 +5578,10 @@
                       "n": {
                         "df": 4,
                         "docs": {
-                          "24": {
+                          "27": {
                             "tf": 1.0
                           },
-                          "25": {
+                          "28": {
                             "tf": 1.4142135623730951
                           },
                           "3": {
@@ -5383,7 +5601,7 @@
               "e": {
                 "df": 1,
                 "docs": {
-                  "23": {
+                  "26": {
                     "tf": 1.0
                   }
                 }
@@ -5407,7 +5625,7 @@
                       "t": {
                         "df": 1,
                         "docs": {
-                          "23": {
+                          "26": {
                             "tf": 1.0
                           }
                         }
@@ -5698,7 +5916,7 @@
                 "e": {
                   "df": 1,
                   "docs": {
-                    "23": {
+                    "26": {
                       "tf": 1.0
                     }
                   }
@@ -5784,7 +6002,7 @@
                     "17": {
                       "tf": 1.4142135623730951
                     },
-                    "24": {
+                    "27": {
                       "tf": 1.4142135623730951
                     },
                     "6": {
@@ -5966,7 +6184,7 @@
                 "k": {
                   "df": 2,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.4142135623730951
                     },
                     "8": {
@@ -5981,7 +6199,7 @@
                       "11": {
                         "tf": 4.69041575982343
                       },
-                      "23": {
+                      "26": {
                         "tf": 1.0
                       }
                     }
@@ -6061,6 +6279,38 @@
               },
               "df": 0,
               "docs": {}
+            },
+            "t": {
+              "df": 0,
+              "docs": {},
+              "t": {
+                "df": 0,
+                "docs": {},
+                "r": {
+                  "df": 0,
+                  "docs": {},
+                  "i": {
+                    "b": {
+                      "df": 0,
+                      "docs": {},
+                      "u": {
+                        "df": 0,
+                        "docs": {},
+                        "t": {
+                          "df": 1,
+                          "docs": {
+                            "23": {
+                              "tf": 1.0
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "df": 0,
+                    "docs": {}
+                  }
+                }
+              }
             }
           },
           "b": {
@@ -6139,7 +6389,7 @@
                           "2": {
                             "tf": 1.0
                           },
-                          "23": {
+                          "26": {
                             "tf": 1.0
                           },
                           "4": {
@@ -6154,6 +6404,29 @@
               "df": 0,
               "docs": {}
             },
+            "l": {
+              "a": {
+                "df": 0,
+                "docs": {},
+                "s": {
+                  "df": 0,
+                  "docs": {},
+                  "s": {
+                    "df": 2,
+                    "docs": {
+                      "24": {
+                        "tf": 1.0
+                      },
+                      "25": {
+                        "tf": 1.0
+                      }
+                    }
+                  }
+                }
+              },
+              "df": 0,
+              "docs": {}
+            },
             "o": {
               "df": 0,
               "docs": {},
@@ -6194,7 +6467,7 @@
                       "s": {
                         "df": 1,
                         "docs": {
-                          "26": {
+                          "29": {
                             "tf": 1.0
                           }
                         }
@@ -6332,8 +6605,18 @@
             "e": {
               "a": {
                 "d": {
-                  "df": 0,
-                  "docs": {},
+                  "df": 3,
+                  "docs": {
+                    "23": {
+                      "tf": 1.0
+                    },
+                    "24": {
+                      "tf": 1.0
+                    },
+                    "25": {
+                      "tf": 1.0
+                    }
+                  },
                   "e": {
                     "df": 0,
                     "docs": {},
@@ -6388,6 +6671,14 @@
             }
           },
           "i": {
+            "d": {
+              "df": 1,
+              "docs": {
+                "25": {
+                  "tf": 1.0
+                }
+              }
+            },
             "df": 0,
             "docs": {},
             "n": {
@@ -6468,7 +6759,7 @@
                 "k": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -6542,7 +6833,7 @@
                 "e": {
                   "df": 1,
                   "docs": {
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }
@@ -6575,7 +6866,7 @@
                   "t": {
                     "df": 1,
                     "docs": {
-                      "24": {
+                      "27": {
                         "tf": 1.0
                       }
                     }
@@ -6593,7 +6884,7 @@
               "l": {
                 "df": 1,
                 "docs": {
-                  "24": {
+                  "27": {
                     "tf": 1.0
                   }
                 }
@@ -6657,7 +6948,7 @@
                     "d": {
                       "df": 1,
                       "docs": {
-                        "23": {
+                        "26": {
                           "tf": 1.0
                         }
                       }
@@ -6678,7 +6969,7 @@
                       "n": {
                         "df": 3,
                         "docs": {
-                          "25": {
+                          "28": {
                             "tf": 1.0
                           },
                           "3": {
@@ -6883,7 +7174,7 @@
                     "17": {
                       "tf": 1.0
                     },
-                    "24": {
+                    "27": {
                       "tf": 1.0
                     }
                   }