From b20b1757a9f9e553ce3a221f046024bd8f277adb Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 7 Nov 2024 19:13:35 +0000 Subject: [PATCH] Add footnote backreferences, and update styling This makes several changes to how footnotes are rendered: - Backlinks are now included, which links back to the reference so you can continue reading where you left off. - Footnotes are moved to the bottom of the page. This helps with the implementation of numbering, and is a style some have requested. I waffled a lot on this change, but supporting the in-place style was just adding too much complexity. - Footnotes are now highlighted when you click on a reference. - Some of the spacing for elements within a footnote has now been fixed (such as supporting multiple paragraphs). - Footnote navigation now scrolls to the middle of the page. This is an alternative to https://github.com/rust-lang/mdBook/pull/2475 Closes https://github.com/rust-lang/mdBook/issues/1927 Closes https://github.com/rust-lang/mdBook/issues/2169 Closes https://github.com/rust-lang/mdBook/issues/2595 --- src/theme/css/general.css | 53 +- src/theme/css/variables.css | 10 + src/utils/mod.rs | 151 ++++- tests/dummy_book/src/first/markdown.md | 30 +- tests/rendered_output.rs | 36 +- tests/searchindex_fixture.json | 730 +++++++++++++++++++++++-- 6 files changed, 929 insertions(+), 81 deletions(-) diff --git a/src/theme/css/general.css b/src/theme/css/general.css index 49e72673..9946cfc0 100644 --- a/src/theme/css/general.css +++ b/src/theme/css/general.css @@ -200,18 +200,53 @@ sup { line-height: 0; } -:not(.footnote-definition) + .footnote-definition { - margin-block-start: 2em; -} -.footnote-definition:not(:has(+ .footnote-definition)) { - margin-block-end: 2em; -} .footnote-definition { font-size: 0.9em; - margin: 0.5em 0; } -.footnote-definition p { - display: inline; +/* The default spacing for a list is a little too large. */ +.footnote-definition ul, +.footnote-definition ol { + padding-left: 20px; +} +.footnote-definition > li { + /* Required to position the ::before target */ + position: relative; +} +.footnote-definition > li:target { + scroll-margin-top: 50vh; +} +.footnote-reference:target { + scroll-margin-top: 50vh; +} +/* Draws a border around the footnote (including the marker) when it is selected. + TODO: If there are multiple linkbacks, highlight which one you just came + from so you know which one to click. +*/ +.footnote-definition > li:target::before { + border: 2px solid var(--footnote-highlight); + border-radius: 6px; + position: absolute; + top: -8px; + right: -8px; + bottom: -8px; + left: -32px; + pointer-events: none; + content: ""; +} +/* Pulses the footnote reference so you can quickly see where you left off reading. + This could use some improvement. +*/ +@media not (prefers-reduced-motion) { + .footnote-reference:target { + animation: fn-highlight 0.8s; + border-radius: 2px; + } + + @keyframes fn-highlight { + from { + background-color: var(--footnote-highlight); + } + } } .tooltiptext { diff --git a/src/theme/css/variables.css b/src/theme/css/variables.css index 57977155..6a692ae7 100644 --- a/src/theme/css/variables.css +++ b/src/theme/css/variables.css @@ -61,6 +61,8 @@ --copy-button-filter: invert(45%) sepia(6%) saturate(621%) hue-rotate(198deg) brightness(99%) contrast(85%); /* Same as `--sidebar-active` */ --copy-button-filter-hover: invert(68%) sepia(55%) saturate(531%) hue-rotate(341deg) brightness(104%) contrast(101%); + + --footnote-highlight: #2668a6; } .coal { @@ -110,6 +112,8 @@ --copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%); /* Same as `--sidebar-active` */ --copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%); + + --footnote-highlight: #4079ae; } .light, html:not(.js) { @@ -159,6 +163,8 @@ --copy-button-filter: invert(45.49%); /* Same as `--sidebar-active` */ --copy-button-filter-hover: invert(14%) sepia(93%) saturate(4250%) hue-rotate(243deg) brightness(99%) contrast(130%); + + --footnote-highlight: #7e7eff; } .navy { @@ -208,6 +214,8 @@ --copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%); /* Same as `--sidebar-active` */ --copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%); + + --footnote-highlight: #4079ae; } .rust { @@ -255,6 +263,8 @@ --copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%); /* Same as `--sidebar-active` */ --copy-button-filter-hover: invert(77%) sepia(16%) saturate(1798%) hue-rotate(328deg) brightness(98%) contrast(83%); + + --footnote-highlight: #d3a17a; } @media (prefers-color-scheme: dark) { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index a53f79c0..b9174b53 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -212,18 +212,156 @@ pub fn render_markdown_with_path( smart_punctuation: bool, path: Option<&Path>, ) -> String { - let mut s = String::with_capacity(text.len() * 3 / 2); - let p = new_cmark_parser(text, smart_punctuation); - let events = p + let mut body = String::with_capacity(text.len() * 3 / 2); + + // Based on + // https://github.com/pulldown-cmark/pulldown-cmark/blob/master/pulldown-cmark/examples/footnote-rewrite.rs + + // This handling of footnotes is a two-pass process. This is done to + // support linkbacks, little arrows that allow you to jump back to the + // footnote reference. The first pass collects the footnote definitions. + // The second pass modifies those definitions to include the linkbacks, + // and inserts the definitions back into the `events` list. + + // This is a map of name -> (number, count) + // `name` is the name of the footnote. + // `number` is the footnote number displayed in the output. + // `count` is the number of references to this footnote (used for multiple + // linkbacks, and checking for unused footnotes). + let mut footnote_numbers = HashMap::new(); + // This is a list of (name, Vec) + // `name` is the name of the footnote. + // The events list is the list of events needed to build the footnote definition. + let mut footnote_defs = Vec::new(); + + // The following are used when currently processing a footnote definition. + // + // This is the name of the footnote (escaped). + let mut in_footnote_name = String::new(); + // This is the list of events to build the footnote definition. + let mut in_footnote = Vec::new(); + + let events = new_cmark_parser(text, smart_punctuation) .map(clean_codeblock_headers) .map(|event| adjust_links(event, path)) .flat_map(|event| { let (a, b) = wrap_tables(event); a.into_iter().chain(b) + }) + // Footnote rewriting must go last to ensure inner definition contents + // are processed (since they get pulled out of the initial stream). + .filter_map(|event| { + match event { + Event::Start(Tag::FootnoteDefinition(name)) => { + if !in_footnote.is_empty() { + log::warn!("internal bug: nested footnote not expected in {path:?}"); + } + in_footnote_name = special_escape(&name); + None + } + Event::End(TagEnd::FootnoteDefinition) => { + let def_events = std::mem::take(&mut in_footnote); + let name = std::mem::take(&mut in_footnote_name); + footnote_defs.push((name, def_events)); + None + } + Event::FootnoteReference(name) => { + let name = special_escape(&name); + let len = footnote_numbers.len() + 1; + let (n, count) = footnote_numbers.entry(name.clone()).or_insert((len, 0)); + *count += 1; + let html = Event::Html( + format!( + "\ + {n}\ + " + ) + .into(), + ); + if in_footnote_name.is_empty() { + Some(html) + } else { + // While inside a footnote, we need to accumulate. + in_footnote.push(html); + None + } + } + // While inside a footnote, accumulate all events into a local. + _ if !in_footnote_name.is_empty() => { + in_footnote.push(event); + None + } + _ => Some(event), + } }); - html::push_html(&mut s, events); - s + html::push_html(&mut body, events); + + if !footnote_defs.is_empty() { + add_footnote_defs(&mut body, path, footnote_defs, &footnote_numbers); + } + + body +} + +/// Adds all footnote definitions into `body`. +fn add_footnote_defs( + body: &mut String, + path: Option<&Path>, + mut defs: Vec<(String, Vec>)>, + numbers: &HashMap, +) { + // Remove unused. + defs.retain(|(name, _)| { + if !numbers.contains_key(name) { + log::warn!( + "footnote `{name}` in `{}` is defined but not referenced", + path.map_or_else(|| Cow::from(""), |p| p.to_string_lossy()) + ); + false + } else { + true + } + }); + + defs.sort_by_cached_key(|(name, _)| numbers[name].0); + + body.push_str( + "
\n\ +
    ", + ); + + // Insert the backrefs to the definition, and put the definitions in the output. + for (name, mut fn_events) in defs { + let count = numbers[&name].1; + fn_events.insert( + 0, + Event::Html(format!("
  1. ").into()), + ); + // Generate the linkbacks. + for usage in 1..=count { + let nth = if usage == 1 { + String::new() + } else { + usage.to_string() + }; + let backlink = + Event::Html(format!(" ↩{nth}").into()); + if matches!(fn_events.last(), Some(Event::End(TagEnd::Paragraph))) { + // Put the linkback at the end of the last paragraph instead + // of on a line by itself. + fn_events.insert(fn_events.len() - 1, backlink); + } else { + // Not a clear place to put it in this circumstance, so put it + // at the end. + fn_events.push(backlink); + } + } + fn_events.push(Event::Html("
  2. \n".into())); + html::push_html(body, fn_events.into_iter()); + } + + body.push_str("
"); } /// Wraps tables in a `.table-wrapper` class to apply overflow-x rules to. @@ -267,13 +405,14 @@ pub fn log_backtrace(e: &Error) { pub(crate) fn special_escape(mut s: &str) -> String { let mut escaped = String::with_capacity(s.len()); - let needs_escape: &[char] = &['<', '>', '\'', '\\', '&']; + let needs_escape: &[char] = &['<', '>', '\'', '"', '\\', '&']; while let Some(next) = s.find(needs_escape) { escaped.push_str(&s[..next]); match s.as_bytes()[next] { b'<' => escaped.push_str("<"), b'>' => escaped.push_str(">"), b'\'' => escaped.push_str("'"), + b'"' => escaped.push_str("""), b'\\' => escaped.push_str("\"), b'&' => escaped.push_str("&"), _ => unreachable!(), diff --git a/tests/dummy_book/src/first/markdown.md b/tests/dummy_book/src/first/markdown.md index d65d3d38..ec2958e0 100644 --- a/tests/dummy_book/src/first/markdown.md +++ b/tests/dummy_book/src/first/markdown.md @@ -15,8 +15,34 @@ Footnote example[^1], or with a word[^word]. [^1]: This is a footnote. [^word]: A longer footnote. - With multiple lines. - Third line. + With multiple lines. [Link to unicode](unicode.md). + With a reference inside.[^1] + +There are multiple references to word[^word]. + +Footnote without a paragraph[^para] + +[^para]: + 1. Item one + 1. Sub-item + 2. Item two + +Footnote with multiple paragraphs[^multiple] + +[^define-before-use]: This is defined before it is referred to. + + +[^multiple]:

One

Two

Three

+ +[^unused]: This footnote is defined by not used. + +Footnote name with wacky characters[^"wacky"] + +[^"wacky"]: Testing footnote id with special characters. + +Testing when referring to something earlier.[^define-before-use] ## Strikethrough diff --git a/tests/rendered_output.rs b/tests/rendered_output.rs index c2725c53..beb83ebd 100644 --- a/tests/rendered_output.rs +++ b/tests/rendered_output.rs @@ -545,10 +545,38 @@ fn markdown_options() { assert_contains_strings( &path, &[ - r##"1"##, - r##"2"##, - r##"
1"##, - r##"
2"##, + r##"1"##, + r##"2"##, + r##"2"##, + r##"
+
  1. +

    This is a footnote. ↩2

    +
  2. +
  3. +

    A longer footnote. +With multiple lines. Link to unicode. +With a reference inside.1 ↩2

    +
  4. +
  5. +
      +
    1. Item one +
        +
      1. Sub-item
      2. +
      +
    2. +
    3. Item two
    4. +
    +
  6. +
  7. One

    Two

    Three

    +
  8. +
  9. +

    Testing footnote id with special characters.

    +
  10. +
  11. +

    This is defined before it is referred to.

    +
  12. +
+"##, ], ); assert_contains_strings(&path, &["strikethrough example"]); diff --git a/tests/searchindex_fixture.json b/tests/searchindex_fixture.json index 06bfb2ab..3a6dd657 100644 --- a/tests/searchindex_fixture.json +++ b/tests/searchindex_fixture.json @@ -65,7 +65,7 @@ "title": 1 }, "14": { - "body": 12, + "body": 55, "breadcrumbs": 4, "title": 1 }, @@ -223,7 +223,7 @@ "title": "Tables" }, "14": { - "body": "Footnote example [1] , or with a word [2] . This is a footnote. A longer footnote. With multiple lines. Third line.", + "body": "Footnote example [1] , or with a word [2] . This is a footnote. A longer footnote. With multiple lines. Link to unicode . With a reference inside. [1] There are multiple references to word [2] . Footnote without a paragraph [3] Item one Sub-item Item two Footnote with multiple paragraphs [4] This is defined before it is referred to. OneTwoThree This footnote is defined by not used. Footnote name with wacky characters [7] Testing footnote id with special characters. Testing when referring to something earlier. [5]", "breadcrumbs": "First Chapter » Markdown » Footnotes", "id": "14", "title": "Footnotes" @@ -382,7 +382,7 @@ "df": 2, "docs": { "14": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "17": { "tf": 1.0 @@ -390,6 +390,38 @@ } }, "2": { + "df": 1, + "docs": { + "14": { + "tf": 1.4142135623730951 + } + } + }, + "3": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "4": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "5": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "7": { "df": 1, "docs": { "14": { @@ -645,6 +677,22 @@ "e": { "df": 0, "docs": {}, + "f": { + "df": 0, + "docs": {}, + "o": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + }, "h": { "a": { "df": 0, @@ -957,8 +1005,11 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.4142135623730951 + }, "17": { "tf": 2.23606797749979 } @@ -1148,6 +1199,26 @@ "tf": 1.0 } }, + "e": { + "df": 0, + "docs": {}, + "f": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "n": { + "df": 1, + "docs": { + "14": { + "tf": 1.4142135623730951 + } + } + } + } + } + }, "u": { "df": 0, "docs": {}, @@ -1198,6 +1269,34 @@ "df": 0, "docs": {}, "e": { + "a": { + "df": 0, + "docs": {}, + "r": { + "df": 0, + "docs": {}, + "l": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + } + } + } + }, "d": { "df": 0, "docs": {}, @@ -1456,7 +1555,7 @@ "df": 1, "docs": { "14": { - "tf": 2.0 + "tf": 3.0 } } } @@ -1675,8 +1774,11 @@ }, "i": { "d": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "25": { "tf": 1.0 } @@ -1788,6 +1890,18 @@ } } } + }, + "i": { + "d": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "df": 0, + "docs": {} } }, "t": { @@ -1869,6 +1983,22 @@ "df": 0, "docs": {} } + }, + "t": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "m": { + "df": 1, + "docs": { + "14": { + "tf": 1.7320508075688772 + } + } + } + } } }, "j": { @@ -1916,7 +2046,7 @@ "df": 3, "docs": { "14": { - "tf": 1.4142135623730951 + "tf": 1.0 }, "26": { "tf": 1.0 @@ -1927,8 +2057,11 @@ } }, "k": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "27": { "tf": 2.23606797749979 } @@ -2108,7 +2241,7 @@ "df": 1, "docs": { "14": { - "tf": 1.0 + "tf": 1.7320508075688772 } } } @@ -2119,6 +2252,22 @@ } }, "n": { + "a": { + "df": 0, + "docs": {}, + "m": { + "df": 0, + "docs": {}, + "e": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + }, "df": 0, "docs": {}, "e": { @@ -2168,6 +2317,50 @@ "o": { "df": 0, "docs": {}, + "n": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + }, + "e": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "w": { + "df": 0, + "docs": {}, + "o": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 0, + "docs": {}, + "r": { + "df": 0, + "docs": {}, + "e": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + } + } + } + } + } + } + }, "u": { "df": 0, "docs": {}, @@ -2229,6 +2422,34 @@ } }, "r": { + "a": { + "df": 0, + "docs": {}, + "g": { + "df": 0, + "docs": {}, + "r": { + "a": { + "df": 0, + "docs": {}, + "p": { + "df": 0, + "docs": {}, + "h": { + "df": 1, + "docs": { + "14": { + "tf": 1.4142135623730951 + } + } + } + } + }, + "df": 0, + "docs": {} + } + } + }, "df": 0, "docs": {}, "t": { @@ -2434,6 +2655,22 @@ }, "df": 0, "docs": {}, + "f": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "14": { + "tf": 2.0 + } + } + } + } + }, "g": { "df": 0, "docs": {}, @@ -2818,7 +3055,19 @@ } }, "df": 0, - "docs": {} + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } } } }, @@ -2874,8 +3123,11 @@ "df": 0, "docs": {}, "l": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "6": { "tf": 1.0 } @@ -2971,6 +3223,14 @@ } }, "u": { + "b": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, "c": { "df": 0, "docs": {}, @@ -3093,11 +3353,14 @@ "df": 0, "docs": {} }, - "df": 4, + "df": 5, "docs": { "12": { "tf": 1.4142135623730951 }, + "14": { + "tf": 1.4142135623730951 + }, "17": { "tf": 1.0 }, @@ -3138,22 +3401,6 @@ "h": { "df": 0, "docs": {}, - "i": { - "df": 0, - "docs": {}, - "r": { - "d": { - "df": 1, - "docs": { - "14": { - "tf": 1.0 - } - } - }, - "df": 0, - "docs": {} - } - }, "r": { "df": 0, "docs": {}, @@ -3175,8 +3422,11 @@ "df": 0, "docs": {}, "o": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "17": { "tf": 1.0 } @@ -3196,11 +3446,14 @@ "docs": {}, "o": { "d": { - "df": 2, + "df": 3, "docs": { "10": { "tf": 1.0 }, + "14": { + "tf": 1.0 + }, "17": { "tf": 1.0 } @@ -3225,6 +3478,14 @@ } } } + }, + "s": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } } }, "v": { @@ -3253,6 +3514,22 @@ }, "w": { "a": { + "c": { + "df": 0, + "docs": {}, + "k": { + "df": 0, + "docs": {}, + "i": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + }, "df": 0, "docs": {}, "y": { @@ -3266,6 +3543,34 @@ }, "df": 0, "docs": {}, + "i": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 0, + "docs": {}, + "o": { + "df": 0, + "docs": {}, + "u": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + } + } + } + }, "o": { "df": 0, "docs": {}, @@ -3274,7 +3579,7 @@ "df": 1, "docs": { "14": { - "tf": 1.0 + "tf": 1.4142135623730951 } } }, @@ -3341,7 +3646,7 @@ "df": 2, "docs": { "14": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "17": { "tf": 1.0 @@ -3349,6 +3654,38 @@ } }, "2": { + "df": 1, + "docs": { + "14": { + "tf": 1.4142135623730951 + } + } + }, + "3": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "4": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "5": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "7": { "df": 1, "docs": { "14": { @@ -3610,6 +3947,22 @@ "e": { "df": 0, "docs": {}, + "f": { + "df": 0, + "docs": {}, + "o": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + }, "h": { "a": { "df": 0, @@ -3985,8 +4338,11 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.4142135623730951 + }, "17": { "tf": 2.23606797749979 } @@ -4176,6 +4532,26 @@ "tf": 1.0 } }, + "e": { + "df": 0, + "docs": {}, + "f": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "n": { + "df": 1, + "docs": { + "14": { + "tf": 1.4142135623730951 + } + } + } + } + } + }, "u": { "df": 0, "docs": {}, @@ -4235,6 +4611,34 @@ "df": 0, "docs": {}, "e": { + "a": { + "df": 0, + "docs": {}, + "r": { + "df": 0, + "docs": {}, + "l": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + } + } + } + }, "d": { "df": 0, "docs": {}, @@ -4553,7 +4957,7 @@ "df": 1, "docs": { "14": { - "tf": 2.23606797749979 + "tf": 3.1622776601683795 } } } @@ -4775,8 +5179,11 @@ }, "i": { "d": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "25": { "tf": 1.4142135623730951 } @@ -4888,6 +5295,18 @@ } } } + }, + "i": { + "d": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, + "df": 0, + "docs": {} } }, "t": { @@ -4969,6 +5388,22 @@ "df": 0, "docs": {} } + }, + "t": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "m": { + "df": 1, + "docs": { + "14": { + "tf": 1.7320508075688772 + } + } + } + } } }, "j": { @@ -5016,7 +5451,7 @@ "df": 3, "docs": { "14": { - "tf": 1.4142135623730951 + "tf": 1.0 }, "26": { "tf": 1.0 @@ -5027,8 +5462,11 @@ } }, "k": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "27": { "tf": 2.449489742783178 } @@ -5220,7 +5658,7 @@ "df": 1, "docs": { "14": { - "tf": 1.0 + "tf": 1.7320508075688772 } } } @@ -5231,6 +5669,22 @@ } }, "n": { + "a": { + "df": 0, + "docs": {}, + "m": { + "df": 0, + "docs": {}, + "e": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + }, "df": 0, "docs": {}, "e": { @@ -5298,6 +5752,50 @@ "o": { "df": 0, "docs": {}, + "n": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + }, + "e": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "w": { + "df": 0, + "docs": {}, + "o": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 0, + "docs": {}, + "r": { + "df": 0, + "docs": {}, + "e": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + } + } + } + } + } + } + }, "u": { "df": 0, "docs": {}, @@ -5359,6 +5857,34 @@ } }, "r": { + "a": { + "df": 0, + "docs": {}, + "g": { + "df": 0, + "docs": {}, + "r": { + "a": { + "df": 0, + "docs": {}, + "p": { + "df": 0, + "docs": {}, + "h": { + "df": 1, + "docs": { + "14": { + "tf": 1.4142135623730951 + } + } + } + } + }, + "df": 0, + "docs": {} + } + } + }, "df": 0, "docs": {}, "t": { @@ -5567,6 +6093,22 @@ }, "df": 0, "docs": {}, + "f": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "14": { + "tf": 2.0 + } + } + } + } + }, "g": { "df": 0, "docs": {}, @@ -5957,7 +6499,19 @@ } }, "df": 0, - "docs": {} + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } } } }, @@ -6013,8 +6567,11 @@ "df": 0, "docs": {}, "l": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "6": { "tf": 1.4142135623730951 } @@ -6110,6 +6667,14 @@ } }, "u": { + "b": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + }, "c": { "df": 0, "docs": {}, @@ -6232,11 +6797,14 @@ "df": 0, "docs": {} }, - "df": 4, + "df": 5, "docs": { "12": { "tf": 1.7320508075688772 }, + "14": { + "tf": 1.4142135623730951 + }, "17": { "tf": 1.4142135623730951 }, @@ -6277,22 +6845,6 @@ "h": { "df": 0, "docs": {}, - "i": { - "df": 0, - "docs": {}, - "r": { - "d": { - "df": 1, - "docs": { - "14": { - "tf": 1.0 - } - } - }, - "df": 0, - "docs": {} - } - }, "r": { "df": 0, "docs": {}, @@ -6314,8 +6866,11 @@ "df": 0, "docs": {}, "o": { - "df": 1, + "df": 2, "docs": { + "14": { + "tf": 1.0 + }, "17": { "tf": 1.0 } @@ -6335,11 +6890,14 @@ "docs": {}, "o": { "d": { - "df": 2, + "df": 3, "docs": { "10": { "tf": 1.0 }, + "14": { + "tf": 1.0 + }, "17": { "tf": 1.7320508075688772 } @@ -6364,6 +6922,14 @@ } } } + }, + "s": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } } }, "v": { @@ -6392,6 +6958,22 @@ }, "w": { "a": { + "c": { + "df": 0, + "docs": {}, + "k": { + "df": 0, + "docs": {}, + "i": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + }, "df": 0, "docs": {}, "y": { @@ -6405,6 +6987,34 @@ }, "df": 0, "docs": {}, + "i": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 0, + "docs": {}, + "o": { + "df": 0, + "docs": {}, + "u": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "14": { + "tf": 1.0 + } + } + } + } + } + } + } + }, "o": { "df": 0, "docs": {}, @@ -6413,7 +7023,7 @@ "df": 1, "docs": { "14": { - "tf": 1.0 + "tf": 1.4142135623730951 } } },