From 0449e4825b30b0974b57fc237bf0eae73f866662 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sat, 13 Sep 2025 11:15:00 +0200 Subject: [PATCH 01/18] Fix that negated search words are filtered out in the splitQuery. --- sphinx/themes/basic/static/searchtools.js | 22 +++++++++++++++------- tests/js/searchtools.spec.js | 10 ++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 5a7628a18a2..6aa4f216fda 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -171,15 +171,23 @@ const _orderResultsByScoreThenName = (a, b) => { * Default splitQuery function. Can be overridden in ``sphinx.search`` with a * custom function per language. * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + * The `consecutiveLetters` regular expression works by matching consecutive characters + * that are Unicode letters, numbers, underscores, or emoji characters. + * + * The `searchWords` regular expression works by matching a word like structure + * that matches the `consecutiveLetters` with or without a leading hyphen '-' which is + * used to exclude search terms later on. */ if (typeof splitQuery === "undefined") { - var splitQuery = (query) => - query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter((term) => term); // remove remaining empty strings + var splitQuery = (query) => { + const consecutiveLetters = /[\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu; + const searchWords = new RegExp(`(${consecutiveLetters.source})|\\s(-${consecutiveLetters.source})`, "gu"); + return Array.from( + query.matchAll(searchWords) + .map((results) => results[1] ?? results[2]) // select one of the possible groups (e.g. "word" or "-word"). + .filter((term) => term) // remove remaining empty strings. + ); + } } /** diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index d00689c907c..3007cf23757 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -295,6 +295,16 @@ describe("splitQuery regression tests", () => { expect(parts).toEqual(["Pin", "Code"]); }); + it("can keep underscores in words", () => { + const parts = splitQuery("python_function"); + expect(parts).toEqual(["python_function"]); + }); + + it("can maintain negated search words", () => { + const parts = splitQuery("Pin -Code"); + expect(parts).toEqual(["Pin", "-Code"]); + }); + it("can split Chinese characters", () => { const parts = splitQuery("Hello from 中国 上海"); expect(parts).toEqual(["Hello", "from", "中国", "上海"]); From dbfbd46a513e7d0dd6ceeae20fc26c54081c46e5 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 14 Sep 2025 11:04:59 +0200 Subject: [PATCH 02/18] Fix that negated search words create a SyntaxError: Invalid or unexpected token. --- sphinx/themes/basic/static/searchtools.js | 12 +++++++----- tests/js/searchtools.spec.js | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 6aa4f216fda..fc89a54cd5e 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -636,11 +636,13 @@ const Search = { // ensure that none of the excluded terms is in the search result if ( [...excludedTerms].some( - (term) => - terms[term] === file - || titleTerms[term] === file - || (terms[term] || []).includes(file) - || (titleTerms[term] || []).includes(file), + (excludedTerm) => { + // Both mappings will contain either a single integer or a list of integers. + // Converting them to lists makes the comparison more readable. + let excludedTermFiles = [].concat(terms[excludedTerm]); + let excludedTitleFiles = [].concat(titleTerms[excludedTerm]); + return excludedTermFiles.includes(file) || excludedTitleFiles.includes(file); + } ) ) break; diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 3007cf23757..900f8d9a81e 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -66,6 +66,25 @@ describe("Basic html theme search", function () { expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); + it("should be able to exclude search terms", function () { + eval(loadFixture("titles/searchindex.js")); + + [_searchQuery, searchterms, excluded, ..._remainingItems] = + Search._parseQuery("main page -function"); + // prettier-ignore + hits = [[ + 'index', + 'Main Page', + '', + null, + 15, + 'index.rst', + 'text' + ]]; + expect(excluded).toEqual(new Set(["function"])); + expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); + }); + it('should partially-match "sphinx" when in title index', function () { eval(loadFixture("partial/searchindex.js")); From 7a8d46ee881b3f45786101a10850ba0ffc94fdca Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Mon, 15 Sep 2025 10:22:32 +0200 Subject: [PATCH 03/18] Fix that excluded words would abort the entire search if matched in one page. --- sphinx/themes/basic/static/searchtools.js | 2 +- tests/js/searchtools.spec.js | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index fc89a54cd5e..8edd6d04b5e 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -645,7 +645,7 @@ const Search = { } ) ) - break; + continue; // select one (max) score for the file. const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 900f8d9a81e..8a73748a08f 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -66,22 +66,29 @@ describe("Basic html theme search", function () { expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); - it("should be able to exclude search terms", function () { + it("should find results when excluded terms are used", function () { + // This fixture already existed and has the right data to make this test work. + // Replace with another matching fixture if necessary. eval(loadFixture("titles/searchindex.js")); + // It's important that the searchterm is included in multiple pages while the + // excluded term is not included in all pages. + // In this case the ``for`` is included in the two existing pages while the ``ask`` + // is only included in one page. [_searchQuery, searchterms, excluded, ..._remainingItems] = - Search._parseQuery("main page -function"); + Search._parseQuery("for -ask"); + // prettier-ignore hits = [[ - 'index', - 'Main Page', + 'relevance', + 'Relevance', '', null, - 15, - 'index.rst', + 2, + 'relevance.rst', 'text' ]]; - expect(excluded).toEqual(new Set(["function"])); + expect(excluded).toEqual(new Set(["ask"])); expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); From cfdfda47b216fed6802ce69f7f91874022e72214 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Mon, 15 Sep 2025 11:27:35 +0200 Subject: [PATCH 04/18] Fix format and add contribution information. --- AUTHORS.rst | 1 + CHANGES.rst | 1 + sphinx/themes/basic/static/searchtools.js | 36 +++++++++++++---------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 5bcd74c943b..1299c1a4748 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -83,6 +83,7 @@ Contributors * Lars Hupfeldt Nielsen - OpenSSL FIPS mode md5 bug fix * Louis Maddox -- better docstrings * Łukasz Langa -- partial support for autodoc +* Lukas Wieg -- JavaScript search improvement * Marco Buttu -- doctest extension (pyversion option) * Mark Ostroth -- semantic HTML contributions * Martin Hans -- autodoc improvements diff --git a/CHANGES.rst b/CHANGES.rst index 64f94e14ec3..53970ceb2d1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -118,6 +118,7 @@ Bugs fixed for objects documented as ``:py:data:`` to be hyperlinked in function signatures. * #13858: doctest: doctest blocks are now correctly added to a group defined by the configuration variable ``doctest_test_doctest_blocks``. +* #13892: search: support word exclusion in the search by prefixing words with hyphen. Testing diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 8edd6d04b5e..926e79e0bde 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -180,14 +180,19 @@ const _orderResultsByScoreThenName = (a, b) => { */ if (typeof splitQuery === "undefined") { var splitQuery = (query) => { - const consecutiveLetters = /[\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu; - const searchWords = new RegExp(`(${consecutiveLetters.source})|\\s(-${consecutiveLetters.source})`, "gu"); + const consecutiveLetters = + /[\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu; + const searchWords = new RegExp( + `(${consecutiveLetters.source})|\\s(-${consecutiveLetters.source})`, + "gu", + ); return Array.from( - query.matchAll(searchWords) - .map((results) => results[1] ?? results[2]) // select one of the possible groups (e.g. "word" or "-word"). - .filter((term) => term) // remove remaining empty strings. + query + .matchAll(searchWords) + .map((results) => results[1] ?? results[2]) // select one of the possible groups (e.g. "word" or "-word"). + .filter((term) => term), // remove remaining empty strings. ); - } + }; } /** @@ -635,15 +640,16 @@ const Search = { // ensure that none of the excluded terms is in the search result if ( - [...excludedTerms].some( - (excludedTerm) => { - // Both mappings will contain either a single integer or a list of integers. - // Converting them to lists makes the comparison more readable. - let excludedTermFiles = [].concat(terms[excludedTerm]); - let excludedTitleFiles = [].concat(titleTerms[excludedTerm]); - return excludedTermFiles.includes(file) || excludedTitleFiles.includes(file); - } - ) + [...excludedTerms].some((excludedTerm) => { + // Both mappings will contain either a single integer or a list of integers. + // Converting them to lists makes the comparison more readable. + let excludedTermFiles = [].concat(terms[excludedTerm]); + let excludedTitleFiles = [].concat(titleTerms[excludedTerm]); + return ( + excludedTermFiles.includes(file) + || excludedTitleFiles.includes(file) + ); + }) ) continue; From 80d6ad1b938b117484db6f6beb75a0a221c2cb24 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Mon, 15 Sep 2025 11:29:38 +0200 Subject: [PATCH 05/18] Fix long line. --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 53970ceb2d1..a57ea60e8a8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -118,7 +118,7 @@ Bugs fixed for objects documented as ``:py:data:`` to be hyperlinked in function signatures. * #13858: doctest: doctest blocks are now correctly added to a group defined by the configuration variable ``doctest_test_doctest_blocks``. -* #13892: search: support word exclusion in the search by prefixing words with hyphen. +* #13892: search: support word exclusion in the search by prefixing words with "-". Testing From 9b06fd581a189e35328ffea1e1c7e17abaac788e Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Fri, 19 Sep 2025 22:11:17 +0200 Subject: [PATCH 06/18] Add a dedicated fixture for testing excluded words in searches. --- tests/js/roots/search_exclusion/conf.py | 0 tests/js/roots/search_exclusion/excluded.rst | 4 ++++ tests/js/roots/search_exclusion/index.rst | 12 ++++++++++++ tests/js/searchtools.spec.js | 15 ++++++++------- 4 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 tests/js/roots/search_exclusion/conf.py create mode 100644 tests/js/roots/search_exclusion/excluded.rst create mode 100644 tests/js/roots/search_exclusion/index.rst diff --git a/tests/js/roots/search_exclusion/conf.py b/tests/js/roots/search_exclusion/conf.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/js/roots/search_exclusion/excluded.rst b/tests/js/roots/search_exclusion/excluded.rst new file mode 100644 index 00000000000..62f8485153b --- /dev/null +++ b/tests/js/roots/search_exclusion/excluded.rst @@ -0,0 +1,4 @@ +Excluded Page +============= + +This is a page with the special word penguin. diff --git a/tests/js/roots/search_exclusion/index.rst b/tests/js/roots/search_exclusion/index.rst new file mode 100644 index 00000000000..94520c59db9 --- /dev/null +++ b/tests/js/roots/search_exclusion/index.rst @@ -0,0 +1,12 @@ +Main Page +========= + +This is the main page of the ``search_exclusion`` test project. + +This document is used as a test fixture to check that search results can be +filtered in the query by specifying excluded terms. + +A term which starts with a hypen will be used as excluded term. + +Include a second page which can be excluded in the search: +:index:`excluded` \ No newline at end of file diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 8a73748a08f..b57d23b499b 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -69,26 +69,27 @@ describe("Basic html theme search", function () { it("should find results when excluded terms are used", function () { // This fixture already existed and has the right data to make this test work. // Replace with another matching fixture if necessary. - eval(loadFixture("titles/searchindex.js")); + eval(loadFixture("search_exclusion/searchindex.js")); // It's important that the searchterm is included in multiple pages while the // excluded term is not included in all pages. // In this case the ``for`` is included in the two existing pages while the ``ask`` // is only included in one page. [_searchQuery, searchterms, excluded, ..._remainingItems] = - Search._parseQuery("for -ask"); + Search._parseQuery("page -penguin"); // prettier-ignore hits = [[ - 'relevance', - 'Relevance', + 'index', + 'Main Page', '', null, - 2, - 'relevance.rst', + 15, + 'index.rst', 'text' ]]; - expect(excluded).toEqual(new Set(["ask"])); + + expect(excluded).toEqual(new Set(["penguin"])); expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); From 11d2e2e4e9b25f42b556aa0b9daca42b5e42994c Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Fri, 19 Sep 2025 22:21:51 +0200 Subject: [PATCH 07/18] Is the pipeline flaky? --- tests/js/roots/search_exclusion/index.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/js/roots/search_exclusion/index.rst b/tests/js/roots/search_exclusion/index.rst index 94520c59db9..0a47d842169 100644 --- a/tests/js/roots/search_exclusion/index.rst +++ b/tests/js/roots/search_exclusion/index.rst @@ -9,4 +9,6 @@ filtered in the query by specifying excluded terms. A term which starts with a hypen will be used as excluded term. Include a second page which can be excluded in the search: -:index:`excluded` \ No newline at end of file +:index:`excluded` + +is the pipeline flaky? \ No newline at end of file From 6cc4055bc4870064b5e615825b2e87f94f86c37b Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Fri, 19 Sep 2025 22:28:12 +0200 Subject: [PATCH 08/18] Add missing file. --- tests/js/fixtures/search_exclusion/searchindex.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/js/fixtures/search_exclusion/searchindex.js diff --git a/tests/js/fixtures/search_exclusion/searchindex.js b/tests/js/fixtures/search_exclusion/searchindex.js new file mode 100644 index 00000000000..8e21d1001d2 --- /dev/null +++ b/tests/js/fixtures/search_exclusion/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles":{"Excluded Page":[[0,null]],"Main Page":[[1,null]]},"docnames":["excluded","index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["excluded.rst","index.rst"],"indexentries":{"excluded":[[1,"index-0",false]]},"objects":{},"objnames":{},"objtypes":{},"terms":{"A":1,"This":[0,1],"can":1,"check":1,"document":1,"exclud":1,"filter":1,"fixtur":1,"hypen":1,"includ":1,"penguin":0,"project":1,"queri":1,"result":1,"search":1,"search_exclus":1,"second":1,"special":0,"specifi":1,"start":1,"term":1,"test":1,"use":1,"will":1,"word":0},"titles":["Excluded Page","Main Page"],"titleterms":{"exclud":0,"main":1,"page":[0,1]}}) \ No newline at end of file From ffc193b2aea3bfc314156387f4c911a74a47e205 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Fri, 19 Sep 2025 22:28:40 +0200 Subject: [PATCH 09/18] Remove debug change. --- tests/js/roots/search_exclusion/index.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/js/roots/search_exclusion/index.rst b/tests/js/roots/search_exclusion/index.rst index 0a47d842169..29f3a13012d 100644 --- a/tests/js/roots/search_exclusion/index.rst +++ b/tests/js/roots/search_exclusion/index.rst @@ -10,5 +10,3 @@ A term which starts with a hypen will be used as excluded term. Include a second page which can be excluded in the search: :index:`excluded` - -is the pipeline flaky? \ No newline at end of file From a4764b1cfac26898b41cd279d2d1ed88b9921e94 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Tue, 7 Oct 2025 20:45:14 +0200 Subject: [PATCH 10/18] Reduce line length to fix doclinter. --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 069d91f7fdd..c400ec32574 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -120,7 +120,7 @@ Bugs fixed configuration variable ``doctest_test_doctest_blocks``. * #13885: Coverage builder: Fix TypeError when warning about missing modules. Patch by Damien Ayers. -* #13892: HTML search: support word exclusion in the search by prefixing words with "-". +* #13892: HTML search: fix word exclusion in the search by prefixing words with "-". Testing From 8e41004e378b52c6735b23afab2c750587908142 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 2 Nov 2025 14:52:58 +0100 Subject: [PATCH 11/18] Add regression test for TypeError in excluded words comparison. --- .../search_multiple_exclusions/searchindex.js | 1 + .../roots/search_multiple_exclusions/conf.py | 0 .../search_multiple_exclusions/first.rst | 4 ++ .../search_multiple_exclusions/index.rst | 13 +++++++ .../search_multiple_exclusions/second.rst | 4 ++ tests/js/searchtools.spec.js | 37 +++++++++++++++++++ 6 files changed, 59 insertions(+) create mode 100644 tests/js/fixtures/search_multiple_exclusions/searchindex.js create mode 100644 tests/js/roots/search_multiple_exclusions/conf.py create mode 100644 tests/js/roots/search_multiple_exclusions/first.rst create mode 100644 tests/js/roots/search_multiple_exclusions/index.rst create mode 100644 tests/js/roots/search_multiple_exclusions/second.rst diff --git a/tests/js/fixtures/search_multiple_exclusions/searchindex.js b/tests/js/fixtures/search_multiple_exclusions/searchindex.js new file mode 100644 index 00000000000..6751a9690a7 --- /dev/null +++ b/tests/js/fixtures/search_multiple_exclusions/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"First Page": [[0, null]], "Main Page": [[1, null]], "Second Page": [[2, null]]}, "docnames": ["first", "index", "second"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["first.rst", "index.rst", "second.rst"], "indexentries": {"first": [[1, "index-0", false]], "second": [[1, "index-1", false]]}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"A": 1, "can": 1, "check": 1, "document": 1, "exclud": 1, "excluded2": [], "filter": 1, "first": 1, "fixtur": 1, "hypen": 1, "i": [0, 1, 2], "includ": 1, "jumanji": 2, "penguin": 0, "project": 1, "queri": 1, "result": 1, "search": 1, "search_exclus": 1, "second": 1, "special": [0, 2], "specifi": 1, "start": 1, "term": 1, "test": 1, "thi": [0, 1, 2], "us": 1, "which": 1, "word": [0, 2]}, "titles": ["First Page", "Main Page", "Second Page"], "titleterms": {"exclud": [], "first": 0, "main": 1, "page": [0, 1, 2], "second": 2}}) \ No newline at end of file diff --git a/tests/js/roots/search_multiple_exclusions/conf.py b/tests/js/roots/search_multiple_exclusions/conf.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/js/roots/search_multiple_exclusions/first.rst b/tests/js/roots/search_multiple_exclusions/first.rst new file mode 100644 index 00000000000..8f7a81860f2 --- /dev/null +++ b/tests/js/roots/search_multiple_exclusions/first.rst @@ -0,0 +1,4 @@ +First Page +============= + +This is a page with the special word penguin. diff --git a/tests/js/roots/search_multiple_exclusions/index.rst b/tests/js/roots/search_multiple_exclusions/index.rst new file mode 100644 index 00000000000..71f011a7292 --- /dev/null +++ b/tests/js/roots/search_multiple_exclusions/index.rst @@ -0,0 +1,13 @@ +Main Page +========= + +This is the main page of the ``search_exclusion`` test project. + +This document is used as a test fixture to check that search results can be +filtered in the query by specifying excluded terms. + +A term which starts with a hypen will be used as excluded term. + +Include pages which can be excluded in the search: +:index:`first` +:index:`second` \ No newline at end of file diff --git a/tests/js/roots/search_multiple_exclusions/second.rst b/tests/js/roots/search_multiple_exclusions/second.rst new file mode 100644 index 00000000000..fa7c2ff2f28 --- /dev/null +++ b/tests/js/roots/search_multiple_exclusions/second.rst @@ -0,0 +1,4 @@ +Second Page +============= + +This is a page with the special word jumanji. diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index b57d23b499b..65b1089f4ec 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -93,6 +93,43 @@ describe("Basic html theme search", function () { expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); + it("should exclude results where the file index is above 0.", function () { + // This is a very constructed test case for fixing the issue that + // `(terms[term] || []).includes(file)` raises an error when `terms[term]` is an + // int above 0. The condition would be convterted to `(1).includes(3)` which + // results in this type error: + // TypeError: (terms[term] || []).includes is not a function + eval(loadFixture("search_multiple_exclusions/searchindex.js")); + + [_searchQuery, searchterms, excluded, ..._remainingItems] = + Search._parseQuery("page -jumanji"); + + // prettier-ignore + hits = [ + [ + 'first', + 'First Page', + '', + null, + 15, + 'first.rst', + 'text' + ], + [ + 'index', + 'Main Page', + '', + null, + 15, + 'index.rst', + 'text' + ] + ]; + + expect(excluded).toEqual(new Set(["jumanji"])); + expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); + }); + it('should partially-match "sphinx" when in title index', function () { eval(loadFixture("partial/searchindex.js")); From da297e52d05b46660b9ae62193481d1d1bfa5d40 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 2 Nov 2025 14:53:38 +0100 Subject: [PATCH 12/18] Fix wording of test. --- tests/js/searchtools.spec.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 65b1089f4ec..07b14dfc9f4 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -66,14 +66,12 @@ describe("Basic html theme search", function () { expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); - it("should find results when excluded terms are used", function () { - // This fixture already existed and has the right data to make this test work. - // Replace with another matching fixture if necessary. + it("should find results when a excluded term is used", function () { eval(loadFixture("search_exclusion/searchindex.js")); // It's important that the searchterm is included in multiple pages while the // excluded term is not included in all pages. - // In this case the ``for`` is included in the two existing pages while the ``ask`` + // In this case the ``page`` is included in the two existing pages while the ``penguin`` // is only included in one page. [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery("page -penguin"); From 1fd12ab739628a282fc1584ec1077b0ac28e6732 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 2 Nov 2025 14:55:11 +0100 Subject: [PATCH 13/18] Update searchindex by running `python .\utils\generate_js_fixtures.py` No change in functionality intended. Only update the automatically generated files which might be forgotten. --- tests/js/fixtures/cpp/searchindex.js | 2 +- tests/js/fixtures/multiterm/searchindex.js | 2 +- tests/js/fixtures/partial/searchindex.js | 2 +- tests/js/fixtures/search_exclusion/searchindex.js | 2 +- tests/js/fixtures/titles/searchindex.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index 10ed84d7ff0..46f48244741 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{},"docnames":["index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{"sphinx (c++ class)":[[0,"_CPPv46Sphinx",false]]},"objects":{"":[[0,0,1,"_CPPv46Sphinx","Sphinx"]]},"objnames":{"0":["cpp","class","C++ class"]},"objtypes":{"0":"cpp:class"},"terms":{"The":0,"This":0,"becaus":0,"c":0,"can":0,"cardin":0,"challeng":0,"charact":0,"class":0,"descript":0,"drop":0,"engin":0,"fixtur":0,"frequent":0,"generat":0,"index":0,"inflat":0,"mathemat":0,"occur":0,"often":0,"project":0,"punctuat":0,"queri":0,"relat":0,"sampl":0,"search":0,"size":0,"sphinx":0,"term":0,"token":0,"use":0,"web":0},"titles":["<no title>"],"titleterms":{}}) \ No newline at end of file +Search.setIndex({"alltitles": {}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {"sphinx (c++ class)": [[0, "_CPPv46Sphinx", false]]}, "objects": {"": [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]}, "objnames": {"0": ["cpp", "class", "C++ class"]}, "objtypes": {"0": "cpp:class"}, "terms": {"The": 0, "becaus": 0, "c": 0, "can": 0, "cardin": 0, "challeng": 0, "charact": 0, "class": 0, "descript": 0, "drop": 0, "engin": 0, "fixtur": 0, "frequent": 0, "gener": 0, "i": 0, "index": 0, "inflat": 0, "mathemat": 0, "occur": 0, "often": 0, "project": 0, "punctuat": 0, "queri": 0, "relat": 0, "sampl": 0, "search": 0, "size": 0, "sphinx": 0, "term": 0, "thei": 0, "thi": 0, "token": 0, "us": 0, "web": 0, "would": 0}, "titles": ["<no title>"], "titleterms": {}}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index bd732522b3d..a868eb6bdcb 100644 --- a/tests/js/fixtures/multiterm/searchindex.js +++ b/tests/js/fixtures/multiterm/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{"Main Page":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"At":0,"This":0,"adjac":0,"appear":0,"applic":0,"built":0,"can":0,"check":0,"contain":0,"document":0,"doesn":0,"fixtur":0,"format":0,"function":0,"futur":0,"html":0,"includ":0,"match":0,"messag":0,"multipl":0,"multiterm":0,"order":0,"output":0,"perform":0,"perhap":0,"phrase":0,"project":0,"queri":0,"requir":0,"search":0,"success":0,"support":0,"t":0,"term":0,"test":0,"time":0,"use":0,"will":0,"write":0},"titles":["Main Page"],"titleterms":{"main":0,"page":0}}) \ No newline at end of file +Search.setIndex({"alltitles": {"Main Page": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"At": 0, "adjac": 0, "all": 0, "an": 0, "appear": 0, "applic": 0, "ar": 0, "built": 0, "can": 0, "check": 0, "contain": 0, "do": 0, "document": 0, "doesn": 0, "each": 0, "fixtur": 0, "format": 0, "function": 0, "futur": 0, "html": 0, "i": 0, "includ": 0, "match": 0, "messag": 0, "multipl": 0, "multiterm": 0, "order": 0, "other": 0, "output": 0, "perform": 0, "perhap": 0, "phrase": 0, "project": 0, "queri": 0, "requir": 0, "same": 0, "search": 0, "successfulli": 0, "support": 0, "t": 0, "term": 0, "test": 0, "thi": 0, "time": 0, "us": 0, "when": 0, "write": 0}, "titles": ["Main Page"], "titleterms": {"main": 0, "page": 0}}) \ No newline at end of file diff --git a/tests/js/fixtures/partial/searchindex.js b/tests/js/fixtures/partial/searchindex.js index 8a65718e2ea..18fa232b9b1 100644 --- a/tests/js/fixtures/partial/searchindex.js +++ b/tests/js/fixtures/partial/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{"sphinx_utils module":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"This":0,"built":0,"confirm":0,"document":0,"function":0,"html":0,"includ":0,"input":0,"javascript":0,"match":0,"partial":0,"possibl":0,"project":0,"provid":0,"restructuredtext":0,"sampl":0,"search":0,"term":0,"titl":0,"use":0},"titles":["sphinx_utils module"],"titleterms":{"modul":0,"sphinx_util":0}}) \ No newline at end of file +Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"ar": 0, "both": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "match": 0, "partial": 0, "possibl": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "term": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}}) \ No newline at end of file diff --git a/tests/js/fixtures/search_exclusion/searchindex.js b/tests/js/fixtures/search_exclusion/searchindex.js index 8e21d1001d2..678be1d4b91 100644 --- a/tests/js/fixtures/search_exclusion/searchindex.js +++ b/tests/js/fixtures/search_exclusion/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{"Excluded Page":[[0,null]],"Main Page":[[1,null]]},"docnames":["excluded","index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["excluded.rst","index.rst"],"indexentries":{"excluded":[[1,"index-0",false]]},"objects":{},"objnames":{},"objtypes":{},"terms":{"A":1,"This":[0,1],"can":1,"check":1,"document":1,"exclud":1,"filter":1,"fixtur":1,"hypen":1,"includ":1,"penguin":0,"project":1,"queri":1,"result":1,"search":1,"search_exclus":1,"second":1,"special":0,"specifi":1,"start":1,"term":1,"test":1,"use":1,"will":1,"word":0},"titles":["Excluded Page","Main Page"],"titleterms":{"exclud":0,"main":1,"page":[0,1]}}) \ No newline at end of file +Search.setIndex({"alltitles": {"Excluded Page": [[0, null]], "Main Page": [[1, null]]}, "docnames": ["excluded", "index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["excluded.rst", "index.rst"], "indexentries": {"excluded": [[1, "index-0", false]]}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"A": 1, "can": 1, "check": 1, "document": 1, "exclud": 1, "filter": 1, "fixtur": 1, "hypen": 1, "i": [0, 1], "includ": 1, "penguin": 0, "project": 1, "queri": 1, "result": 1, "search": 1, "search_exclus": 1, "second": 1, "special": 0, "specifi": 1, "start": 1, "term": 1, "test": 1, "thi": [0, 1], "us": 1, "which": 1, "word": 0}, "titles": ["Excluded Page", "Main Page"], "titleterms": {"exclud": 0, "main": 1, "page": [0, 1]}}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index fe325c7742d..6e4034e4977 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{"Main Page":[[0,null]],"Relevance":[[0,"relevance"],[1,null]],"Result Scoring":[[0,"result-scoring"]]},"docnames":["index","relevance"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst","relevance.rst"],"indexentries":{"example (class in relevance)":[[0,"relevance.Example",false]],"module":[[0,"module-relevance",false]],"relevance":[[0,"index-1",false],[0,"module-relevance",false]],"relevance (relevance.example attribute)":[[0,"relevance.Example.relevance",false]],"scoring":[[0,"index-0",true]]},"objects":{"":[[0,0,0,"-","relevance"]],"relevance":[[0,1,1,"","Example"]],"relevance.Example":[[0,2,1,"","relevance"]]},"objnames":{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","attribute","Python attribute"]},"objtypes":{"0":"py:module","1":"py:class","2":"py:attribute"},"terms":{"A":1,"By":0,"For":[0,1],"In":[0,1],"This":0,"align":0,"also":1,"answer":0,"appear":1,"area":0,"ask":0,"assign":0,"attempt":0,"attribut":0,"built":1,"can":[0,1],"class":0,"code":[0,1],"collect":0,"consid":1,"contain":0,"context":0,"corpus":1,"demonstr":0,"describ":1,"detail":1,"determin":[0,1],"docstr":0,"document":[0,1],"domain":1,"dure":0,"engin":0,"evalu":0,"exampl":[0,1],"extract":0,"feedback":0,"find":0,"found":0,"function":1,"handl":0,"happen":1,"head":0,"help":0,"high":[0,1],"improv":0,"inform":0,"intend":0,"issu":[0,1],"knowledg":0,"languag":1,"less":1,"like":[0,1],"mani":0,"match":0,"mention":1,"name":[0,1],"numer":0,"object":0,"often":0,"one":[0,1],"onli":[0,1],"order":0,"page":1,"part":1,"particular":0,"present":0,"printf":1,"program":1,"project":0,"queri":[0,1],"question":0,"re":0,"relat":0,"research":0,"result":1,"retriev":0,"s":[0,1],"say":0,"search":[0,1],"seem":0,"softwar":1,"sphinx":0,"straightforward":1,"subject":0,"subsect":0,"term":[0,1],"test":0,"text":0,"time":0,"titl":0,"two":0,"typic":0,"use":0,"user":[0,1],"whether":1,"will":0,"within":0,"word":0},"titles":["Main Page","Relevance"],"titleterms":{"main":0,"page":0,"relev":[0,1],"result":0,"score":0}}) \ No newline at end of file +Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]], "Result Scoring": [[0, "result-scoring"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "index-1", false], [0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]], "scoring": [[0, "index-0", true]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "By": 0, "For": [0, 1], "In": [0, 1], "against": 0, "align": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "assign": 0, "attempt": 0, "attribut": 0, "both": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "collect": 0, "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": [0, 1], "docstr": 0, "document": [0, 1], "domain": 1, "dure": 0, "engin": 0, "evalu": 0, "exampl": [0, 1], "extract": 0, "feedback": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": [0, 1], "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": [0, 1], "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "mani": 0, "match": 0, "mention": 1, "more": 0, "name": [0, 1], "numer": 0, "object": 0, "often": 0, "one": [0, 1], "onli": [0, 1], "order": 0, "other": 0, "over": 0, "page": 1, "part": 1, "particular": 0, "present": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "retriev": 0, "sai": 0, "same": 1, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": [0, 1], "thei": 0, "them": 0, "thi": 0, "time": 0, "titl": 0, "two": 0, "typic": 0, "us": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "which": 0, "within": 0, "word": 0, "would": [0, 1]}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1], "result": 0, "score": 0}}) \ No newline at end of file From 577098df78788feef4cf621c34a8f8bacaa4c981 Mon Sep 17 00:00:00 2001 From: Lukas Wieg <48561400+cglukas@users.noreply.github.com> Date: Sun, 2 Nov 2025 15:13:58 +0100 Subject: [PATCH 14/18] Simplify regex code Use lookbehind to split words only if a hyphon is contained inside a word. Co-authored-by: James Addison <55152140+jayaddison@users.noreply.github.com> --- sphinx/themes/basic/static/searchtools.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 926e79e0bde..31beb2cdeba 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -179,20 +179,10 @@ const _orderResultsByScoreThenName = (a, b) => { * used to exclude search terms later on. */ if (typeof splitQuery === "undefined") { - var splitQuery = (query) => { - const consecutiveLetters = - /[\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu; - const searchWords = new RegExp( - `(${consecutiveLetters.source})|\\s(-${consecutiveLetters.source})`, - "gu", - ); - return Array.from( - query - .matchAll(searchWords) - .map((results) => results[1] ?? results[2]) // select one of the possible groups (e.g. "word" or "-word"). - .filter((term) => term), // remove remaining empty strings. - ); - }; + var splitQuery = (query) => + query + .split(/(? term); // remove remaining empty strings } /** From 8a21a565e9013e93bc8868590a2c2e88f3e0e1fa Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 9 Nov 2025 14:37:38 +0100 Subject: [PATCH 15/18] Fxi searchindex.js of fixtures by rerunning generation with the newest updates from main. --- tests/js/fixtures/cpp/searchindex.js | 2 +- tests/js/fixtures/multiterm/searchindex.js | 2 +- tests/js/fixtures/partial/searchindex.js | 2 +- tests/js/fixtures/search_exclusion/searchindex.js | 2 +- tests/js/fixtures/search_multiple_exclusions/searchindex.js | 2 +- tests/js/fixtures/titles/searchindex.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index 46f48244741..10ed84d7ff0 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {"sphinx (c++ class)": [[0, "_CPPv46Sphinx", false]]}, "objects": {"": [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]}, "objnames": {"0": ["cpp", "class", "C++ class"]}, "objtypes": {"0": "cpp:class"}, "terms": {"The": 0, "becaus": 0, "c": 0, "can": 0, "cardin": 0, "challeng": 0, "charact": 0, "class": 0, "descript": 0, "drop": 0, "engin": 0, "fixtur": 0, "frequent": 0, "gener": 0, "i": 0, "index": 0, "inflat": 0, "mathemat": 0, "occur": 0, "often": 0, "project": 0, "punctuat": 0, "queri": 0, "relat": 0, "sampl": 0, "search": 0, "size": 0, "sphinx": 0, "term": 0, "thei": 0, "thi": 0, "token": 0, "us": 0, "web": 0, "would": 0}, "titles": ["<no title>"], "titleterms": {}}) \ No newline at end of file +Search.setIndex({"alltitles":{},"docnames":["index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{"sphinx (c++ class)":[[0,"_CPPv46Sphinx",false]]},"objects":{"":[[0,0,1,"_CPPv46Sphinx","Sphinx"]]},"objnames":{"0":["cpp","class","C++ class"]},"objtypes":{"0":"cpp:class"},"terms":{"The":0,"This":0,"becaus":0,"c":0,"can":0,"cardin":0,"challeng":0,"charact":0,"class":0,"descript":0,"drop":0,"engin":0,"fixtur":0,"frequent":0,"generat":0,"index":0,"inflat":0,"mathemat":0,"occur":0,"often":0,"project":0,"punctuat":0,"queri":0,"relat":0,"sampl":0,"search":0,"size":0,"sphinx":0,"term":0,"token":0,"use":0,"web":0},"titles":["<no title>"],"titleterms":{}}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index a868eb6bdcb..bd732522b3d 100644 --- a/tests/js/fixtures/multiterm/searchindex.js +++ b/tests/js/fixtures/multiterm/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Main Page": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"At": 0, "adjac": 0, "all": 0, "an": 0, "appear": 0, "applic": 0, "ar": 0, "built": 0, "can": 0, "check": 0, "contain": 0, "do": 0, "document": 0, "doesn": 0, "each": 0, "fixtur": 0, "format": 0, "function": 0, "futur": 0, "html": 0, "i": 0, "includ": 0, "match": 0, "messag": 0, "multipl": 0, "multiterm": 0, "order": 0, "other": 0, "output": 0, "perform": 0, "perhap": 0, "phrase": 0, "project": 0, "queri": 0, "requir": 0, "same": 0, "search": 0, "successfulli": 0, "support": 0, "t": 0, "term": 0, "test": 0, "thi": 0, "time": 0, "us": 0, "when": 0, "write": 0}, "titles": ["Main Page"], "titleterms": {"main": 0, "page": 0}}) \ No newline at end of file +Search.setIndex({"alltitles":{"Main Page":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"At":0,"This":0,"adjac":0,"appear":0,"applic":0,"built":0,"can":0,"check":0,"contain":0,"document":0,"doesn":0,"fixtur":0,"format":0,"function":0,"futur":0,"html":0,"includ":0,"match":0,"messag":0,"multipl":0,"multiterm":0,"order":0,"output":0,"perform":0,"perhap":0,"phrase":0,"project":0,"queri":0,"requir":0,"search":0,"success":0,"support":0,"t":0,"term":0,"test":0,"time":0,"use":0,"will":0,"write":0},"titles":["Main Page"],"titleterms":{"main":0,"page":0}}) \ No newline at end of file diff --git a/tests/js/fixtures/partial/searchindex.js b/tests/js/fixtures/partial/searchindex.js index 18fa232b9b1..8a65718e2ea 100644 --- a/tests/js/fixtures/partial/searchindex.js +++ b/tests/js/fixtures/partial/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"ar": 0, "both": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "match": 0, "partial": 0, "possibl": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "term": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}}) \ No newline at end of file +Search.setIndex({"alltitles":{"sphinx_utils module":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"This":0,"built":0,"confirm":0,"document":0,"function":0,"html":0,"includ":0,"input":0,"javascript":0,"match":0,"partial":0,"possibl":0,"project":0,"provid":0,"restructuredtext":0,"sampl":0,"search":0,"term":0,"titl":0,"use":0},"titles":["sphinx_utils module"],"titleterms":{"modul":0,"sphinx_util":0}}) \ No newline at end of file diff --git a/tests/js/fixtures/search_exclusion/searchindex.js b/tests/js/fixtures/search_exclusion/searchindex.js index 678be1d4b91..8e21d1001d2 100644 --- a/tests/js/fixtures/search_exclusion/searchindex.js +++ b/tests/js/fixtures/search_exclusion/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Excluded Page": [[0, null]], "Main Page": [[1, null]]}, "docnames": ["excluded", "index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["excluded.rst", "index.rst"], "indexentries": {"excluded": [[1, "index-0", false]]}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"A": 1, "can": 1, "check": 1, "document": 1, "exclud": 1, "filter": 1, "fixtur": 1, "hypen": 1, "i": [0, 1], "includ": 1, "penguin": 0, "project": 1, "queri": 1, "result": 1, "search": 1, "search_exclus": 1, "second": 1, "special": 0, "specifi": 1, "start": 1, "term": 1, "test": 1, "thi": [0, 1], "us": 1, "which": 1, "word": 0}, "titles": ["Excluded Page", "Main Page"], "titleterms": {"exclud": 0, "main": 1, "page": [0, 1]}}) \ No newline at end of file +Search.setIndex({"alltitles":{"Excluded Page":[[0,null]],"Main Page":[[1,null]]},"docnames":["excluded","index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["excluded.rst","index.rst"],"indexentries":{"excluded":[[1,"index-0",false]]},"objects":{},"objnames":{},"objtypes":{},"terms":{"A":1,"This":[0,1],"can":1,"check":1,"document":1,"exclud":1,"filter":1,"fixtur":1,"hypen":1,"includ":1,"penguin":0,"project":1,"queri":1,"result":1,"search":1,"search_exclus":1,"second":1,"special":0,"specifi":1,"start":1,"term":1,"test":1,"use":1,"will":1,"word":0},"titles":["Excluded Page","Main Page"],"titleterms":{"exclud":0,"main":1,"page":[0,1]}}) \ No newline at end of file diff --git a/tests/js/fixtures/search_multiple_exclusions/searchindex.js b/tests/js/fixtures/search_multiple_exclusions/searchindex.js index 6751a9690a7..73e4a7c1446 100644 --- a/tests/js/fixtures/search_multiple_exclusions/searchindex.js +++ b/tests/js/fixtures/search_multiple_exclusions/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"First Page": [[0, null]], "Main Page": [[1, null]], "Second Page": [[2, null]]}, "docnames": ["first", "index", "second"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["first.rst", "index.rst", "second.rst"], "indexentries": {"first": [[1, "index-0", false]], "second": [[1, "index-1", false]]}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"A": 1, "can": 1, "check": 1, "document": 1, "exclud": 1, "excluded2": [], "filter": 1, "first": 1, "fixtur": 1, "hypen": 1, "i": [0, 1, 2], "includ": 1, "jumanji": 2, "penguin": 0, "project": 1, "queri": 1, "result": 1, "search": 1, "search_exclus": 1, "second": 1, "special": [0, 2], "specifi": 1, "start": 1, "term": 1, "test": 1, "thi": [0, 1, 2], "us": 1, "which": 1, "word": [0, 2]}, "titles": ["First Page", "Main Page", "Second Page"], "titleterms": {"exclud": [], "first": 0, "main": 1, "page": [0, 1, 2], "second": 2}}) \ No newline at end of file +Search.setIndex({"alltitles":{"First Page":[[0,null]],"Main Page":[[1,null]],"Second Page":[[2,null]]},"docnames":["first","index","second"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["first.rst","index.rst","second.rst"],"indexentries":{"first":[[1,"index-0",false]],"second":[[1,"index-1",false]]},"objects":{},"objnames":{},"objtypes":{},"terms":{"A":1,"This":[0,1,2],"can":1,"check":1,"document":1,"exclud":1,"filter":1,"first":1,"fixtur":1,"hypen":1,"includ":1,"jumanji":2,"penguin":0,"project":1,"queri":1,"result":1,"search":1,"search_exclus":1,"second":1,"special":[0,2],"specifi":1,"start":1,"term":1,"test":1,"use":1,"will":1,"word":[0,2]},"titles":["First Page","Main Page","Second Page"],"titleterms":{"first":0,"main":1,"page":[0,1,2],"second":2}}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 6e4034e4977..fe325c7742d 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]], "Result Scoring": [[0, "result-scoring"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "index-1", false], [0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]], "scoring": [[0, "index-0", true]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "By": 0, "For": [0, 1], "In": [0, 1], "against": 0, "align": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "assign": 0, "attempt": 0, "attribut": 0, "both": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "collect": 0, "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": [0, 1], "docstr": 0, "document": [0, 1], "domain": 1, "dure": 0, "engin": 0, "evalu": 0, "exampl": [0, 1], "extract": 0, "feedback": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": [0, 1], "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": [0, 1], "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "mani": 0, "match": 0, "mention": 1, "more": 0, "name": [0, 1], "numer": 0, "object": 0, "often": 0, "one": [0, 1], "onli": [0, 1], "order": 0, "other": 0, "over": 0, "page": 1, "part": 1, "particular": 0, "present": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "retriev": 0, "sai": 0, "same": 1, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": [0, 1], "thei": 0, "them": 0, "thi": 0, "time": 0, "titl": 0, "two": 0, "typic": 0, "us": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "which": 0, "within": 0, "word": 0, "would": [0, 1]}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1], "result": 0, "score": 0}}) \ No newline at end of file +Search.setIndex({"alltitles":{"Main Page":[[0,null]],"Relevance":[[0,"relevance"],[1,null]],"Result Scoring":[[0,"result-scoring"]]},"docnames":["index","relevance"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst","relevance.rst"],"indexentries":{"example (class in relevance)":[[0,"relevance.Example",false]],"module":[[0,"module-relevance",false]],"relevance":[[0,"index-1",false],[0,"module-relevance",false]],"relevance (relevance.example attribute)":[[0,"relevance.Example.relevance",false]],"scoring":[[0,"index-0",true]]},"objects":{"":[[0,0,0,"-","relevance"]],"relevance":[[0,1,1,"","Example"]],"relevance.Example":[[0,2,1,"","relevance"]]},"objnames":{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","attribute","Python attribute"]},"objtypes":{"0":"py:module","1":"py:class","2":"py:attribute"},"terms":{"A":1,"By":0,"For":[0,1],"In":[0,1],"This":0,"align":0,"also":1,"answer":0,"appear":1,"area":0,"ask":0,"assign":0,"attempt":0,"attribut":0,"built":1,"can":[0,1],"class":0,"code":[0,1],"collect":0,"consid":1,"contain":0,"context":0,"corpus":1,"demonstr":0,"describ":1,"detail":1,"determin":[0,1],"docstr":0,"document":[0,1],"domain":1,"dure":0,"engin":0,"evalu":0,"exampl":[0,1],"extract":0,"feedback":0,"find":0,"found":0,"function":1,"handl":0,"happen":1,"head":0,"help":0,"high":[0,1],"improv":0,"inform":0,"intend":0,"issu":[0,1],"knowledg":0,"languag":1,"less":1,"like":[0,1],"mani":0,"match":0,"mention":1,"name":[0,1],"numer":0,"object":0,"often":0,"one":[0,1],"onli":[0,1],"order":0,"page":1,"part":1,"particular":0,"present":0,"printf":1,"program":1,"project":0,"queri":[0,1],"question":0,"re":0,"relat":0,"research":0,"result":1,"retriev":0,"s":[0,1],"say":0,"search":[0,1],"seem":0,"softwar":1,"sphinx":0,"straightforward":1,"subject":0,"subsect":0,"term":[0,1],"test":0,"text":0,"time":0,"titl":0,"two":0,"typic":0,"use":0,"user":[0,1],"whether":1,"will":0,"within":0,"word":0},"titles":["Main Page","Relevance"],"titleterms":{"main":0,"page":0,"relev":[0,1],"result":0,"score":0}}) \ No newline at end of file From f1e5e8bc789e413164e418ec179204cb315d3ee6 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 9 Nov 2025 15:10:00 +0100 Subject: [PATCH 16/18] Test commit to re-trigger the actions on github. --- tests/js/roots/search_multiple_exclusions/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/js/roots/search_multiple_exclusions/conf.py b/tests/js/roots/search_multiple_exclusions/conf.py index e69de29bb2d..8b137891791 100644 --- a/tests/js/roots/search_multiple_exclusions/conf.py +++ b/tests/js/roots/search_multiple_exclusions/conf.py @@ -0,0 +1 @@ + From 76b375e3145b8481d28e73aef8356b6cb1469ec7 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 9 Nov 2025 15:20:56 +0100 Subject: [PATCH 17/18] Revert "Test commit to re-trigger the actions on github." This reverts commit f1e5e8bc789e413164e418ec179204cb315d3ee6. --- tests/js/roots/search_multiple_exclusions/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/js/roots/search_multiple_exclusions/conf.py b/tests/js/roots/search_multiple_exclusions/conf.py index 8b137891791..e69de29bb2d 100644 --- a/tests/js/roots/search_multiple_exclusions/conf.py +++ b/tests/js/roots/search_multiple_exclusions/conf.py @@ -1 +0,0 @@ - From 35232a3108b7b589b82f6477ce80c39dce121b80 Mon Sep 17 00:00:00 2001 From: Lukas Wieg Date: Sun, 9 Nov 2025 15:10:00 +0100 Subject: [PATCH 18/18] Test commit to re-trigger the actions on github. --- tests/js/roots/search_multiple_exclusions/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/js/roots/search_multiple_exclusions/conf.py b/tests/js/roots/search_multiple_exclusions/conf.py index e69de29bb2d..8b137891791 100644 --- a/tests/js/roots/search_multiple_exclusions/conf.py +++ b/tests/js/roots/search_multiple_exclusions/conf.py @@ -0,0 +1 @@ +