diff --git a/docs/content/docs/config-options.md b/docs/content/docs/config-options.md index b3eedce..d6f063f 100644 --- a/docs/content/docs/config-options.md +++ b/docs/content/docs/config-options.md @@ -63,7 +63,7 @@ exclude_selectors: - "[id^='prefix-']" ``` -All children will also be ignored, so using a `#my_navigation *` selector is not required — in other words, the semantics are the same as the [data-pagefind-ignore](/docs/indexing/#removing-individual-elements-from-the-index) attribute. +All children will also be excluded, so using a `#my_navigation *` selector is not required. In other words, the semantics are the same as the [data-pagefind-ignore="all"](/docs/indexing/#removing-individual-elements-from-the-index) attribute: matched elements are skipped for metadata and filters as well as for content. Note that currently Pagefind only supports lists of options via configuration files. If using the `--exclude-selectors` CLI flag or the `PAGEFIND_EXCLUDE_SELECTORS` environment variable, only one selector may be supplied. The selector may be a comma-separated CSS selector though, so the above example can be passed as `--exclude-selectors "#my_navigation, blockquote > span, [id^='prefix-']"`. diff --git a/pagefind/src/fossick/parser.rs b/pagefind/src/fossick/parser.rs index a1f5d88..7793083 100644 --- a/pagefind/src/fossick/parser.rs +++ b/pagefind/src/fossick/parser.rs @@ -63,6 +63,9 @@ struct DomParserData { has_html_element: bool, has_old_bundle_reference: bool, has_default_ui_reference: bool, + // Set when an --exclude-selectors match fires, and taken by the handler that + // builds the node for that same element. + exclude_element: bool, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -134,17 +137,28 @@ impl<'a> DomParser<'a> { pub fn new(options: &'a SearchOptions) -> Self { let data = Rc::new(RefCell::new(DomParserData::default())); let root = format!("{}, {} *", options.root_selector, options.root_selector); - let mut custom_exclusions = options.exclude_selectors.clone(); - custom_exclusions.extend(REMOVE_SELECTORS.iter().map(|s| s.to_string())); - let custom_exclusions = custom_exclusions - .iter() - .map(|e| format!("{} {}", options.root_selector, e)) - .collect::>() - .join(", "); + let scope_to_root = |selectors: &mut dyn Iterator| { + selectors + .map(|e| format!("{} {}", options.root_selector, e)) + .collect::>() + .join(", ") + }; + let user_exclusions = + scope_to_root(&mut options.exclude_selectors.iter().map(String::as_str)); + let auto_exclusions = scope_to_root(&mut REMOVE_SELECTORS.iter().copied()); let mut anchor_counter = 0; + // Registered ahead of the handler below so that the element's own status + // is known before any of its metadata is read. + let user_exclusion_handler = (!user_exclusions.is_empty()).then(|| { + enclose! { (data) element!(user_exclusions, move |_el| { + data.borrow_mut().exclude_element = true; + Ok(()) + })} + }); + let rewriter = HtmlRewriter::new( - vec![ + user_exclusion_handler.into_iter().chain(vec![ enclose! { (data) element!("html", move |el| { let mut data = data.borrow_mut(); data.has_html_element = true; @@ -154,6 +168,7 @@ impl<'a> DomParser<'a> { Ok(()) })}, enclose! { (data) element!(root, move |el| { + let excluded_by_selector = std::mem::take(&mut data.borrow_mut().exclude_element); let explicit_ignore_flag = el.get_attribute("data-pagefind-ignore").map(|attr| { match attr.to_ascii_lowercase().as_str() { "" | "index" | "true" => NodeStatus::Ignored, @@ -175,7 +190,9 @@ impl<'a> DomParser<'a> { let index_attrs: Option> = el.get_attribute("data-pagefind-index-attrs").map(|attr| attr.split(',').map(|a| a.trim().to_string()).collect()); let tag_name = el.tag_name(); - let status = if treat_as_body { + let status = if excluded_by_selector { + NodeStatus::Excluded + } else if treat_as_body { NodeStatus::Body } else if let Some(explicit_ignore_flag) = explicit_ignore_flag { explicit_ignore_flag @@ -495,8 +512,9 @@ impl<'a> DomParser<'a> { } Ok(()) })}, - // If we hit a selector that should be excluded, mark whatever the current node is as such - enclose! { (data) element!(custom_exclusions, move |_el| { + // Elements we always skip stay "ignored" rather than "excluded", + // so that metadata tagged inside a or a