Skip to content

Commit

Permalink
Move to localStorage for search highlighting
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed Sep 24, 2022
1 parent c7c0e40 commit e2b3b3f
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 45 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -32,6 +32,8 @@ Features added
* #6692: HTML Search: Include explicit :rst:dir:`index` directive index entries
in the search index and search results. Patch by Adam Turner
* #10816: imgmath: Allow embedding images in HTML as base64
* #10854: HTML Search: Use browser localstorage for highlight control, stop
storing highlight parameters in URL query strings. Patch by Adam Turner.

Bugs fixed
----------
Expand Down
29 changes: 12 additions & 17 deletions sphinx/themes/basic/static/searchtools.js
Expand Up @@ -57,7 +57,7 @@ const _removeChildren = (element) => {
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string

const _displayItem = (item, highlightTerms, searchTerms) => {
const _displayItem = (item, searchTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
Expand All @@ -82,10 +82,8 @@ const _displayItem = (item, highlightTerms, searchTerms) => {
requestUrl = docUrlRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
const params = new URLSearchParams();
params.set("highlight", [...highlightTerms].join(" "));
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + "?" + params.toString() + anchor;
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = title;
if (descr)
Expand All @@ -97,7 +95,7 @@ const _displayItem = (item, highlightTerms, searchTerms) => {
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms, highlightTerms)
Search.makeSearchSummary(data, searchTerms)
);
});
Search.output.appendChild(listItem);
Expand All @@ -117,15 +115,14 @@ const _finishSearch = (resultCount) => {
const _displayNextItem = (
results,
resultCount,
highlightTerms,
searchTerms
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), highlightTerms, searchTerms);
_displayItem(results.pop(), searchTerms);
setTimeout(
() => _displayNextItem(results, resultCount, highlightTerms, searchTerms),
() => _displayNextItem(results, resultCount, searchTerms),
5
);
}
Expand Down Expand Up @@ -271,6 +268,10 @@ const Search = {
}
});

if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
}

// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
Expand Down Expand Up @@ -359,7 +360,7 @@ const Search = {
// console.info("search results:", Search.lastresults);

// print the results
_displayNextItem(results, results.length, highlightTerms, searchTerms);
_displayNextItem(results, results.length, searchTerms);
},

/**
Expand Down Expand Up @@ -538,11 +539,9 @@ const Search = {
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, highlightWords is the list of normal, unstemmed
* words. the first one is used to find the occurrence, the
* latter for highlighting it.
* of stemmed words.
*/
makeSearchSummary: (htmlText, keywords, highlightWords) => {
makeSearchSummary: (htmlText, keywords) => {
const text = Search.htmlToText(htmlText);
if (text === "") return null;

Expand All @@ -560,10 +559,6 @@ const Search = {
summary.classList.add("context");
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;

highlightWords.forEach((highlightWord) =>
_highlightText(summary, highlightWord, "highlighted")
);

return summary;
},
};
Expand Down
55 changes: 27 additions & 28 deletions sphinx/themes/basic/static/sphinx_highlight.js
@@ -1,6 +1,8 @@
/* Highlighting utilities for Sphinx HTML documentation. */
"use strict";

const SPHINX_HIGHLIGHT_ENABLED = true

/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
Expand Down Expand Up @@ -68,11 +70,22 @@ const _highlightText = (thisNode, text, className) => {
const SphinxHighlight = {

/**
* highlight the search words provided in the url in the text
* highlight the search words provided in localstorage in the text
*/
highlightSearchWords: () => {
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight

// get and clear terms from localstorage
const url = new URL(window.location);
const highlight =
new URLSearchParams(window.location.search).get("highlight") || "";
localStorage.getItem("sphinx_highlight_terms")
|| url.searchParams.get("highlight")
|| "";
localStorage.removeItem("sphinx_highlight_terms")
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);

// get individual terms from highlight string
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do

Expand All @@ -91,7 +104,7 @@ const SphinxHighlight = {
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
Documentation.gettext("Hide Search Matches") +
_("Hide Search Matches") +
"</a></p>"
)
);
Expand All @@ -107,39 +120,25 @@ const SphinxHighlight = {
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
const url = new URL(window.location);
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
localStorage.removeItem("sphinx_highlight_terms")
},

initOnKeyListeners: () => {
initEscapeListener: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;

const blacklistedElements = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
document.addEventListener("keydown", (event) => {
if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements
if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys

if (!event.shiftKey) {
switch (event.key) {
case "Escape":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
});
},
};

_ready(SphinxHighlight.highlightSearchWords);
_ready(SphinxHighlight.initOnKeyListeners);
_ready(SphinxHighlight.initEscapeListener);

0 comments on commit e2b3b3f

Please sign in to comment.