From 47f6547589294f5ef46b977d759ccb6ddb19dd58 Mon Sep 17 00:00:00 2001 From: James Addison Date: Thu, 24 Oct 2024 18:55:21 +0100 Subject: [PATCH 01/28] search: check that query terms exist as properties in term indices before accessing them --- sphinx/themes/basic/static/searchtools.js | 4 ++-- tests/js/fixtures/ecmascript/searchindex.js | 1 + tests/js/roots/ecmascript/conf.py | 0 tests/js/roots/ecmascript/index.rst | 5 +++++ 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 tests/js/fixtures/ecmascript/searchindex.js create mode 100644 tests/js/roots/ecmascript/conf.py create mode 100644 tests/js/roots/ecmascript/index.rst diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index aaf078d2b91..f13f5027934 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -514,8 +514,8 @@ const Search = { searchTerms.forEach((word) => { const files = []; const arr = [ - { files: terms[word], score: Scorer.term }, - { files: titleTerms[word], score: Scorer.title }, + { files: terms.hasOwnProperty(word) ? terms[word] : [], score: Scorer.term }, + { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : [], score: Scorer.title }, ]; // add support for partial matches if (word.length > 2) { diff --git a/tests/js/fixtures/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js new file mode 100644 index 00000000000..f878089d88d --- /dev/null +++ b/tests/js/fixtures/ecmascript/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles":{},"docnames":["index"],"envversion":{"sphinx":64,"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":{"object.__proto__ (object attribute)":[[0,"Object.__proto__",false]]},"objects":{"Object":[[0,0,1,"","__proto__"]]},"objnames":{"0":["js","attribute","JavaScript attribute"]},"objtypes":{"0":"js:attribute"},"terms":{"__proto__":0,"access":0,"aka":0,"an":0,"ecmascript":0,"engin":0,"fixtur":0,"gener":0,"i":0,"index":0,"instanc":0,"javascript":0,"object":0,"project":0,"prototyp":0,"sampl":0,"search":0,"thi":0,"us":0},"titles":["<no title>"],"titleterms":{}}) \ No newline at end of file diff --git a/tests/js/roots/ecmascript/conf.py b/tests/js/roots/ecmascript/conf.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/js/roots/ecmascript/index.rst b/tests/js/roots/ecmascript/index.rst new file mode 100644 index 00000000000..6f9e9886912 --- /dev/null +++ b/tests/js/roots/ecmascript/index.rst @@ -0,0 +1,5 @@ +This is a sample JavaScript (aka ``ECMAScript``) project used to generate a search engine index fixture. + +.. js:attribute:: Object.__proto__ + + Used to access the `prototype `_ of an object instance. From 713d6bbf9f2ea8c86f8c58131c5e93aca7a29ce9 Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 2 Nov 2024 02:19:52 +0000 Subject: [PATCH 02/28] Tests: fixtures: add document title for ECMAScript testroot --- tests/js/fixtures/ecmascript/searchindex.js | 2 +- tests/js/roots/ecmascript/index.rst | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/js/fixtures/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js index f878089d88d..48aba140210 100644 --- a/tests/js/fixtures/ecmascript/searchindex.js +++ b/tests/js/fixtures/ecmascript/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{},"docnames":["index"],"envversion":{"sphinx":64,"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":{"object.__proto__ (object attribute)":[[0,"Object.__proto__",false]]},"objects":{"Object":[[0,0,1,"","__proto__"]]},"objnames":{"0":["js","attribute","JavaScript attribute"]},"objtypes":{"0":"js:attribute"},"terms":{"__proto__":0,"access":0,"aka":0,"an":0,"ecmascript":0,"engin":0,"fixtur":0,"gener":0,"i":0,"index":0,"instanc":0,"javascript":0,"object":0,"project":0,"prototyp":0,"sampl":0,"search":0,"thi":0,"us":0},"titles":["<no title>"],"titleterms":{}}) \ No newline at end of file +Search.setIndex({"alltitles":{"ECMAScript":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":64,"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":{"object.__proto__ (object attribute)":[[0,"Object.__proto__",false]]},"objects":{"Object":[[0,0,1,"","__proto__"]]},"objnames":{"0":["js","attribute","JavaScript attribute"]},"objtypes":{"0":"js:attribute"},"terms":{"__proto__":0,"access":0,"aka":0,"an":0,"engin":0,"fixtur":0,"gener":0,"i":0,"index":0,"instanc":0,"javascript":0,"object":0,"project":0,"prototyp":0,"sampl":0,"search":0,"thi":0,"us":0},"titles":["ECMAScript"],"titleterms":{"ecmascript":0}}) \ No newline at end of file diff --git a/tests/js/roots/ecmascript/index.rst b/tests/js/roots/ecmascript/index.rst index 6f9e9886912..841ed60b031 100644 --- a/tests/js/roots/ecmascript/index.rst +++ b/tests/js/roots/ecmascript/index.rst @@ -1,3 +1,6 @@ +ECMAScript +---------- + This is a sample JavaScript (aka ``ECMAScript``) project used to generate a search engine index fixture. .. js:attribute:: Object.__proto__ From 0fff170a5193741f53b56c0304e1864b754fa81a Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 2 Nov 2024 02:20:51 +0000 Subject: [PATCH 03/28] Tests: add JavaScript test coverage for prototype-property-name query --- tests/js/searchtools.spec.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index cfe5fdcf7ed..b6c5913b5ae 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -209,6 +209,29 @@ describe('Basic html theme search', function() { }); + describe('can handle edge-case search queries', function() { + + it('can search for the javascript prototype property', function() { + eval(loadFixture("ecmascript/searchindex.js")); + + searchParameters = Search._parseQuery('__proto__'); + + hits = [ + [ + 'index', + 'Object.__proto__', + '#Object.__proto__', + 'JavaScript attribute, in ECMAScript', + 16, + 'index.rst', + 'object' + ] + ]; + expect(Search._performSearch(...searchParameters)).toEqual(hits); + }); + + }); + }); describe("htmlToText", function() { From 64d6e09db072f252d4014486627c2378f28b3321 Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 2 Nov 2024 02:23:49 +0000 Subject: [PATCH 04/28] Add CHANGES.rst entry [skip ci] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b47f417e9a1..b27b9d20af2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,9 @@ Bugs fixed * #13060: HTML Search: use ``Map`` to store per-file term scores. Patch by James Addison +* #13097: HTML Search: add a precautionary check for query term + presence in index properties before accessing them. + Patch by James Addison Testing ------- From 9bab17c8d97ae3ca7bbe63005aa9cf496c7eb71b Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 5 Nov 2024 22:07:06 +0000 Subject: [PATCH 05/28] Tests: add negative-test case The JS prototype property should _not_ be found in unrelated documents --- tests/js/searchtools.spec.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index b6c5913b5ae..2bc04771ff8 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -230,6 +230,15 @@ describe('Basic html theme search', function() { expect(Search._performSearch(...searchParameters)).toEqual(hits); }); + it('does not find the javascript prototype property in unrelated documents', function() { + eval(loadFixture("partial/searchindex.js")); + + searchParameters = Search._parseQuery('__proto__'); + + hits = []; + expect(Search._performSearch(...searchParameters)).toEqual(hits); + }); + }); }); From 9b1edac4069cf9d341f60941c6cc8703fb404718 Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 11 Nov 2024 17:27:59 +0000 Subject: [PATCH 06/28] search: add explanatory comment for document title/text term lookup --- sphinx/themes/basic/static/searchtools.js | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index f13f5027934..076dd33ec04 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -513,6 +513,7 @@ const Search = { // perform the search on the required terms searchTerms.forEach((word) => { const files = []; + // find documents, if any, containing the query word in their text/title term indices const arr = [ { files: terms.hasOwnProperty(word) ? terms[word] : [], score: Scorer.term }, { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : [], score: Scorer.title }, From 52b9de3b725a3308c92ab53eb37961dc2598126f Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 11 Nov 2024 17:29:27 +0000 Subject: [PATCH 07/28] search: retain `undefined` result instead of `[]` for non-matching terms --- sphinx/themes/basic/static/searchtools.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 076dd33ec04..41cb5f996f2 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -515,8 +515,8 @@ const Search = { const files = []; // find documents, if any, containing the query word in their text/title term indices const arr = [ - { files: terms.hasOwnProperty(word) ? terms[word] : [], score: Scorer.term }, - { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : [], score: Scorer.title }, + { files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term }, + { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title }, ]; // add support for partial matches if (word.length > 2) { From bcb02fe19f00c7216bc52108f9396d2b54230a6f Mon Sep 17 00:00:00 2001 From: James Addison Date: Mon, 11 Nov 2024 17:30:42 +0000 Subject: [PATCH 08/28] search: add explanatory comment regarding `hasOwnProperty` usage --- sphinx/themes/basic/static/searchtools.js | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 41cb5f996f2..91f4be57fc8 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -514,6 +514,7 @@ const Search = { searchTerms.forEach((word) => { const files = []; // find documents, if any, containing the query word in their text/title term indices + // use Object.hasOwnProperty to avoid mismatching against prototype properties const arr = [ { files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term }, { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title }, From 462c8595937f2034f86322c8650e3f76f81de905 Mon Sep 17 00:00:00 2001 From: James Addison Date: Thu, 14 Nov 2024 12:01:31 +0000 Subject: [PATCH 09/28] HTML search: encapsulate the search index content within a JSON string --- sphinx/search/__init__.py | 2 +- sphinx/themes/basic/static/searchtools.js | 2 +- tests/js/fixtures/cpp/searchindex.js | 2 +- tests/js/fixtures/ecmascript/searchindex.js | 2 +- tests/js/fixtures/multiterm/searchindex.js | 2 +- tests/js/fixtures/partial/searchindex.js | 2 +- tests/js/fixtures/titles/searchindex.js | 2 +- tests/js/roots/ecmascript/index.rst | 4 +--- tests/js/searchtools.spec.js | 10 +++++----- 9 files changed, 13 insertions(+), 15 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 3f19d3663a0..c75067706da 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -169,7 +169,7 @@ class _JavaScriptIndex: def dumps(self, data: Any) -> str: data_json = json.dumps(data, separators=(',', ':'), sort_keys=True) - return self.PREFIX + data_json + self.SUFFIX + return self.PREFIX + repr(data_json) + self.SUFFIX def loads(self, s: str) -> Any: data = s[len(self.PREFIX) : -len(self.SUFFIX)] diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 91f4be57fc8..6734dbfa538 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -219,7 +219,7 @@ const Search = { (document.body.appendChild(document.createElement("script")).src = url), setIndex: (index) => { - Search._index = index; + Search._index = JSON.parse(index); if (Search._queued_query !== null) { const query = Search._queued_query; Search._queued_query = null; diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index e5837e65d56..35b01f58b6e 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{},"docnames":["index"],"envversion":{"sphinx":64,"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":64,"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/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js index 48aba140210..713c3fa3dff 100644 --- a/tests/js/fixtures/ecmascript/searchindex.js +++ b/tests/js/fixtures/ecmascript/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles":{"ECMAScript":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":64,"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":{"object.__proto__ (object attribute)":[[0,"Object.__proto__",false]]},"objects":{"Object":[[0,0,1,"","__proto__"]]},"objnames":{"0":["js","attribute","JavaScript attribute"]},"objtypes":{"0":"js:attribute"},"terms":{"__proto__":0,"access":0,"aka":0,"an":0,"engin":0,"fixtur":0,"gener":0,"i":0,"index":0,"instanc":0,"javascript":0,"object":0,"project":0,"prototyp":0,"sampl":0,"search":0,"thi":0,"us":0},"titles":["ECMAScript"],"titleterms":{"ecmascript":0}}) \ No newline at end of file +Search.setIndex('{"alltitles":{"ECMAScript":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":64,"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":{"__proto__":0,"access":0,"aka":0,"an":0,"ani":0,"engin":0,"fixtur":0,"gener":0,"i":0,"index":0,"instanc":0,"javascript":0,"object":0,"project":0,"properti":0,"prototyp":0,"sampl":0,"search":0,"thi":0,"us":0},"titles":["ECMAScript"],"titleterms":{"ecmascript":0}}') \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index b3e2977792c..f57ab1bc417 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":64,"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":64,"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 ac024bf0c6e..0556fd48776 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":64,"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":64,"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/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 987be77992a..a218281d8de 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":64,"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":64,"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 diff --git a/tests/js/roots/ecmascript/index.rst b/tests/js/roots/ecmascript/index.rst index 841ed60b031..aa0f23efb44 100644 --- a/tests/js/roots/ecmascript/index.rst +++ b/tests/js/roots/ecmascript/index.rst @@ -3,6 +3,4 @@ ECMAScript This is a sample JavaScript (aka ``ECMAScript``) project used to generate a search engine index fixture. -.. js:attribute:: Object.__proto__ - - Used to access the `prototype `_ of an object instance. +Use the `__proto__` property to access the `prototype `_ (if any) of an object instance. diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 2bc04771ff8..3ea628c4692 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -219,12 +219,12 @@ describe('Basic html theme search', function() { hits = [ [ 'index', - 'Object.__proto__', - '#Object.__proto__', - 'JavaScript attribute, in ECMAScript', - 16, + 'ECMAScript', + '', + null, + 5, 'index.rst', - 'object' + 'text' ] ]; expect(Search._performSearch(...searchParameters)).toEqual(hits); From 443fd213c75d5ba7eb9b04869ccad8ca655f2393 Mon Sep 17 00:00:00 2001 From: James Addison Date: Thu, 14 Nov 2024 12:02:13 +0000 Subject: [PATCH 10/28] HTML search: use the `JSON.parse` `reviver` argument to freeze search index contents Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- sphinx/themes/basic/static/searchtools.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 6734dbfa538..e513761829d 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -219,7 +219,8 @@ const Search = { (document.body.appendChild(document.createElement("script")).src = url), setIndex: (index) => { - Search._index = JSON.parse(index); + const reviver = (k, v) => (typeof v === "object" && v !== null) ? Object.freeze(v) : v; + Search._index = JSON.parse(index, reviver); if (Search._queued_query !== null) { const query = Search._queued_query; Search._queued_query = null; From 3498755ff46af324557dc58f8f6381f42ab54aad Mon Sep 17 00:00:00 2001 From: James Addison Date: Thu, 14 Nov 2024 12:16:40 +0000 Subject: [PATCH 11/28] Tests: fixup: accommodate adjusted `setIndex` prefix/suffix lengths --- tests/test_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index 600f66cb9f6..fbec00674c3 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -67,7 +67,7 @@ def load_searchindex(path: Path) -> Any: assert searchindex.startswith('Search.setIndex(') assert searchindex.endswith(')') - return json.loads(searchindex[16:-1]) + return json.loads(searchindex[17:-2]) def is_registered_term(index: Any, keyword: str) -> bool: From f38ce20a395f465ad9069ffba34b92593e56659b Mon Sep 17 00:00:00 2001 From: James Addison Date: Thu, 14 Nov 2024 12:20:29 +0000 Subject: [PATCH 12/28] HTML search: replace `repr` with simple embedded single-quotes The `JSON.dumps` call escapes single-quotes within strings; all JSON strings are enclosed by double-quotes --- sphinx/search/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index c75067706da..58690edfe56 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -164,12 +164,12 @@ class _JavaScriptIndex: on the documentation search object to register the index. """ - PREFIX = 'Search.setIndex(' - SUFFIX = ')' + PREFIX = "Search.setIndex('" + SUFFIX = "')" def dumps(self, data: Any) -> str: data_json = json.dumps(data, separators=(',', ':'), sort_keys=True) - return self.PREFIX + repr(data_json) + self.SUFFIX + return self.PREFIX + data_json + self.SUFFIX def loads(self, s: str) -> Any: data = s[len(self.PREFIX) : -len(self.SUFFIX)] From 0d9a26c05d073fdd13c24820fbe2d3dcdf32da8b Mon Sep 17 00:00:00 2001 From: James Addison Date: Fri, 15 Nov 2024 19:00:49 +0000 Subject: [PATCH 13/28] Revert "HTML search: use the `JSON.parse` `reviver` argument to freeze search index contents" This reverts commit 443fd213c75d5ba7eb9b04869ccad8ca655f2393. --- sphinx/themes/basic/static/searchtools.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index e513761829d..6734dbfa538 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -219,8 +219,7 @@ const Search = { (document.body.appendChild(document.createElement("script")).src = url), setIndex: (index) => { - const reviver = (k, v) => (typeof v === "object" && v !== null) ? Object.freeze(v) : v; - Search._index = JSON.parse(index, reviver); + Search._index = JSON.parse(index); if (Search._queued_query !== null) { const query = Search._queued_query; Search._queued_query = null; From d4bba1ea1f6cd3730fc8891d9bcc65fb7f309f31 Mon Sep 17 00:00:00 2001 From: James Addison Date: Fri, 15 Nov 2024 19:23:50 +0000 Subject: [PATCH 14/28] WIP: HTML search: use JS `Map`s in serialized index [skip ci] --- sphinx/search/__init__.py | 15 +++++-- sphinx/themes/basic/static/searchtools.js | 45 +++++++++------------ tests/js/fixtures/cpp/searchindex.js | 2 +- tests/js/fixtures/ecmascript/searchindex.js | 2 +- tests/js/fixtures/multiterm/searchindex.js | 2 +- tests/js/fixtures/partial/searchindex.js | 2 +- tests/js/fixtures/titles/searchindex.js | 2 +- tests/test_search.py | 2 +- 8 files changed, 37 insertions(+), 35 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 58690edfe56..c69df0f7035 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -164,12 +164,19 @@ class _JavaScriptIndex: on the documentation search object to register the index. """ - PREFIX = "Search.setIndex('" - SUFFIX = "')" + PREFIX = 'Search.setIndex(' + SUFFIX = ')' def dumps(self, data: Any) -> str: - data_json = json.dumps(data, separators=(',', ':'), sort_keys=True) - return self.PREFIX + data_json + self.SUFFIX + assert all(k.isidentifier() for k in data) + js_indices = { + key: f'new Map({json.dumps([[key, value] for key, value in index.items()])})' + if isinstance(index, dict) + else json.dumps(index) + for key, index in sorted(data.items()) + } + data_js = '{' + ','.join(f'{k}: {v}' for k, v in js_indices.items()) + '}' + return self.PREFIX + data_js + self.SUFFIX def loads(self, s: str) -> Any: data = s[len(self.PREFIX) : -len(self.SUFFIX)] diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 6734dbfa538..25ae39e6bc0 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -219,7 +219,7 @@ const Search = { (document.body.appendChild(document.createElement("script")).src = url), setIndex: (index) => { - Search._index = JSON.parse(index); + Search._index = index; if (Search._queued_query !== null) { const query = Search._queued_query; Search._queued_query = null; @@ -333,7 +333,7 @@ const Search = { _removeChildren(document.getElementById("search-progress")); const queryLower = query.toLowerCase().trim(); - for (const [title, foundTitles] of Object.entries(allTitles)) { + for (const [title, foundTitles] of allTitles) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { const score = Math.round(Scorer.title * queryLower.length / title.length); @@ -352,7 +352,7 @@ const Search = { } // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { + for (const [entry, foundEntries] of indexEntries) { if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { for (const [file, id, isMain] of foundEntries) { const score = Math.round(100 * queryLower.length / entry.length); @@ -452,7 +452,7 @@ const Search = { else if (parts.slice(-1)[0].indexOf(object) > -1) score += Scorer.objPartialMatch; // matches in last name - const objName = objNames[match[1]][2]; + const objName = objNames.get(match[1])[2]; const title = titles[match[0]]; // If more than one term searched for, we require other words to be @@ -469,7 +469,7 @@ const Search = { let anchor = match[3]; if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + else if (anchor === "-") anchor = objNames.get(match[1])[1] + "-" + fullname; const descr = objName + _(", in ") + title; @@ -488,11 +488,7 @@ const Search = { SearchResultKind.object, ]); }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) - ); + for (const [prefix, [array]] of objects) objectSearchCallback(prefix, array); return results; }, @@ -514,25 +510,24 @@ const Search = { searchTerms.forEach((word) => { const files = []; // find documents, if any, containing the query word in their text/title term indices - // use Object.hasOwnProperty to avoid mismatching against prototype properties const arr = [ - { files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term }, - { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title }, + { files: terms.get(word), score: Scorer.term }, + { files: titleTerms.get(word), score: Scorer.title }, ]; // add support for partial matches if (word.length > 2) { const escapedWord = _escapeRegExp(word); - if (!terms.hasOwnProperty(word)) { - Object.keys(terms).forEach((term) => { + if (!terms.has(word)) { + for (const [term, files] of terms) { if (term.match(escapedWord)) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); + arr.push({ files: files, score: Scorer.partialTerm }); + } } - if (!titleTerms.hasOwnProperty(word)) { - Object.keys(titleTerms).forEach((term) => { + if (!titleTerms.has(word)) { + for (const [term, files] of titleTerms) { if (term.match(escapedWord)) - arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); - }); + arr.push({ files: files, score: Scorer.partialTitle }); + } } } @@ -581,10 +576,10 @@ const Search = { if ( [...excludedTerms].some( (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) + terms.get(term) === file || + titleTerms.get(term) === file || + (terms.get(term) || []).includes(file) || + (titleTerms.get(term) || []).includes(file) ) ) break; diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index 35b01f58b6e..1c15f554ba1 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex('{"alltitles":{},"docnames":["index"],"envversion":{"sphinx":64,"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: new Map([]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([["sphinx (c++ class)", [[0, "_CPPv46Sphinx", false]]]]),objects: new Map([["", [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]]]),objnames: new Map([[0, ["cpp", "class", "C++ class"]]]),objtypes: new Map([[0, "cpp:class"]]),terms: new Map([["thi", 0], ["i", 0], ["sampl", 0], ["c", 0], ["project", 0], ["us", 0], ["gener", 0], ["search", 0], ["engin", 0], ["index", 0], ["fixtur", 0], ["class", 0], ["sphinx", 0], ["The", 0], ["descript", 0], ["queri", 0], ["term", 0], ["can", 0], ["challeng", 0], ["becaus", 0], ["relat", 0], ["token", 0], ["often", 0], ["drop", 0], ["punctuat", 0], ["mathemat", 0], ["charact", 0], ["thei", 0], ["occur", 0], ["frequent", 0], ["web", 0], ["would", 0], ["inflat", 0], ["cardin", 0], ["size", 0]]),titles: ["<no title>"],titleterms: new Map([])}) \ No newline at end of file diff --git a/tests/js/fixtures/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js index 713c3fa3dff..d0eab82a4ad 100644 --- a/tests/js/fixtures/ecmascript/searchindex.js +++ b/tests/js/fixtures/ecmascript/searchindex.js @@ -1 +1 @@ -Search.setIndex('{"alltitles":{"ECMAScript":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":64,"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":{"__proto__":0,"access":0,"aka":0,"an":0,"ani":0,"engin":0,"fixtur":0,"gener":0,"i":0,"index":0,"instanc":0,"javascript":0,"object":0,"project":0,"properti":0,"prototyp":0,"sampl":0,"search":0,"thi":0,"us":0},"titles":["ECMAScript"],"titleterms":{"ecmascript":0}}') \ No newline at end of file +Search.setIndex({alltitles: new Map([["ECMAScript", [[0, null]]]]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["thi", 0], ["i", 0], ["sampl", 0], ["javascript", 0], ["aka", 0], ["project", 0], ["us", 0], ["gener", 0], ["search", 0], ["engin", 0], ["index", 0], ["fixtur", 0], ["__proto__", 0], ["properti", 0], ["access", 0], ["prototyp", 0], ["ani", 0], ["an", 0], ["object", 0], ["instanc", 0]]),titles: ["ECMAScript"],titleterms: new Map([["ecmascript", 0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index f57ab1bc417..cb1d0548e86 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":64,"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: new Map([["Main Page", [[0, null]]]]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["thi", 0], ["i", 0], ["multiterm", 0], ["test", 0], ["project", 0], ["document", 0], ["us", 0], ["fixtur", 0], ["check", 0], ["search", 0], ["function", 0], ["includ", 0], ["when", 0], ["ar", 0], ["built", 0], ["an", 0], ["html", 0], ["output", 0], ["format", 0], ["can", 0], ["successfulli", 0], ["match", 0], ["queri", 0], ["contain", 0], ["multipl", 0], ["term", 0], ["perform", 0], ["At", 0], ["time", 0], ["write", 0], ["messag", 0], ["applic", 0], ["doesn", 0], ["t", 0], ["support", 0], ["phrase", 0], ["requir", 0], ["all", 0], ["appear", 0], ["adjac", 0], ["each", 0], ["other", 0], ["same", 0], ["order", 0], ["perhap", 0], ["do", 0], ["futur", 0]]),titles: ["Main Page"],titleterms: new Map([["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 0556fd48776..ebe02871f3d 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":64,"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: new Map([["sphinx_utils module", [[0, null]]]]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["partial", 0], ["match", 0], ["document", 0], ["titl", 0], ["term", 0], ["should", 0], ["both", 0], ["possibl", 0], ["us", 0], ["javascript", 0], ["search", 0], ["function", 0], ["includ", 0], ["when", 0], ["html", 0], ["project", 0], ["ar", 0], ["built", 0], ["thi", 0], ["provid", 0], ["sampl", 0], ["restructuredtext", 0], ["input", 0], ["confirm", 0], ["i", 0]]),titles: ["sphinx_utils module"],titleterms: new Map([["sphinx_util", 0], ["modul", 0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index a218281d8de..3944beac13a 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":64,"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: new Map([["Main Page", [[0, null]]], ["Relevance", [[0, "relevance"], [1, null]]], ["Result Scoring", [[0, "result-scoring"]]]]),docnames: ["index", "relevance"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst", "relevance.rst"],indexentries: new Map([["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: new Map([["", [[0, 0, 0, "-", "relevance"]]], ["relevance", [[0, 1, 1, "", "Example"]]], ["relevance.Example", [[0, 2, 1, "", "relevance"]]]]),objnames: new Map([[0, ["py", "module", "Python module"]], [1, ["py", "class", "Python class"]], [2, ["py", "attribute", "Python attribute"]]]),objtypes: new Map([[0, "py:module"], [1, "py:class"], [2, "py:attribute"]]),terms: new Map([["thi", 0], ["i", [0, 1]], ["titl", 0], ["test", 0], ["project", 0], ["In", [0, 1]], ["particular", 0], ["intend", 0], ["demonstr", 0], ["how", 0], ["sphinx", 0], ["can", [0, 1]], ["handl", 0], ["queri", [0, 1]], ["match", 0], ["against", 0], ["document", [0, 1]], ["subsect", 0], ["head", 0], ["rel", 0], ["other", 0], ["term", [0, 1]], ["found", 0], ["within", 0], ["text", 0], ["object", 0], ["name", [0, 1]], ["extract", 0], ["from", 0], ["code", [0, 1]], ["context", 0], ["search", [0, 1]], ["engin", 0], ["we", [0, 1]], ["sai", 0], ["user", [0, 1]], ["", [0, 1]], ["when", 0], ["contain", 0], ["inform", 0], ["seem", 0], ["like", [0, 1]], ["help", 0], ["them", 0], ["find", 0], ["an", 0], ["answer", 0], ["question", 0], ["thei", 0], ["re", 0], ["ask", 0], ["improv", 0], ["knowledg", 0], ["subject", 0], ["area", 0], ["research", 0], ["class", 0], ["exampl", [0, 1]], ["attribut", 0], ["docstr", 0], ["mani", 0], ["assign", 0], ["numer", 0], ["dure", 0], ["retriev", 0], ["often", 0], ["us", 0], ["determin", [0, 1]], ["order", 0], ["which", 0], ["present", 0], ["For", [0, 1]], ["issu", [0, 1]], ["two", 0], ["word", 0], ["both", 0], ["would", [0, 1]], ["typic", 0], ["more", 0], ["highli", [0, 1]], ["than", [0, 1]], ["onli", [0, 1]], ["one", [0, 1]], ["By", 0], ["evalu", 0], ["collect", 0], ["feedback", 0], ["over", 0], ["time", 0], ["attempt", 0], ["align", 0], ["some", 1], ["domain", 1], ["straightforward", 1], ["whether", 1], ["result", 1], ["ar", 1], ["softwar", 1], ["program", 1], ["languag", 1], ["ha", 1], ["printf", 1], ["could", 1], ["consid", 1], ["corpu", 1], ["describ", 1], ["built", 1], ["function", 1], ["same", 1], ["A", 1], ["happen", 1], ["mention", 1], ["part", 1], ["appear", 1], ["page", 1], ["also", 1], ["less", 1], ["itself", 1], ["detail", 1]]),titles: ["Main Page", "Relevance"],titleterms: new Map([["main", 0], ["page", 0], ["relev", [0, 1]], ["result", 0], ["score", 0]])}) \ No newline at end of file diff --git a/tests/test_search.py b/tests/test_search.py index fbec00674c3..600f66cb9f6 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -67,7 +67,7 @@ def load_searchindex(path: Path) -> Any: assert searchindex.startswith('Search.setIndex(') assert searchindex.endswith(')') - return json.loads(searchindex[17:-2]) + return json.loads(searchindex[16:-1]) def is_registered_term(index: Any, keyword: str) -> bool: From d9dee52467a3e94de88f53f2b026a16c641a7900 Mon Sep 17 00:00:00 2001 From: James Addison Date: Fri, 15 Nov 2024 23:14:01 +0000 Subject: [PATCH 15/28] HTML search: fixup for object search Relates-to commit d4bba1ea1f6cd3730fc8891d9bcc65fb7f309f31. [skip ci] --- sphinx/themes/basic/static/searchtools.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 25ae39e6bc0..d986e476f2c 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -488,7 +488,9 @@ const Search = { SearchResultKind.object, ]); }; - for (const [prefix, [array]] of objects) objectSearchCallback(prefix, array); + for (const [prefix, arrays] of objects) { + arrays.forEach((array) => objectSearchCallback(prefix, array)); + } return results; }, From d056a8a0a26582478372a86af1570c6c71b337df Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:05:49 +0000 Subject: [PATCH 16/28] HTML search: ensure array-format index entries are sorted --- sphinx/search/__init__.py | 2 +- tests/js/fixtures/cpp/searchindex.js | 2 +- tests/js/fixtures/ecmascript/searchindex.js | 2 +- tests/js/fixtures/multiterm/searchindex.js | 2 +- tests/js/fixtures/partial/searchindex.js | 2 +- tests/js/fixtures/titles/searchindex.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index c69df0f7035..fa8066c349d 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -170,7 +170,7 @@ class _JavaScriptIndex: def dumps(self, data: Any) -> str: assert all(k.isidentifier() for k in data) js_indices = { - key: f'new Map({json.dumps([[key, value] for key, value in index.items()])})' + key: f'new Map({json.dumps([[key, value] for key, value in sorted(index.items())])})' if isinstance(index, dict) else json.dumps(index) for key, index in sorted(data.items()) diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index 1c15f554ba1..e6deac5298b 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([["sphinx (c++ class)", [[0, "_CPPv46Sphinx", false]]]]),objects: new Map([["", [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]]]),objnames: new Map([[0, ["cpp", "class", "C++ class"]]]),objtypes: new Map([[0, "cpp:class"]]),terms: new Map([["thi", 0], ["i", 0], ["sampl", 0], ["c", 0], ["project", 0], ["us", 0], ["gener", 0], ["search", 0], ["engin", 0], ["index", 0], ["fixtur", 0], ["class", 0], ["sphinx", 0], ["The", 0], ["descript", 0], ["queri", 0], ["term", 0], ["can", 0], ["challeng", 0], ["becaus", 0], ["relat", 0], ["token", 0], ["often", 0], ["drop", 0], ["punctuat", 0], ["mathemat", 0], ["charact", 0], ["thei", 0], ["occur", 0], ["frequent", 0], ["web", 0], ["would", 0], ["inflat", 0], ["cardin", 0], ["size", 0]]),titles: ["<no title>"],titleterms: new Map([])}) \ No newline at end of file +Search.setIndex({alltitles: new Map([]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([["sphinx (c++ class)", [[0, "_CPPv46Sphinx", false]]]]),objects: new Map([["", [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]]]),objnames: new Map([[0, ["cpp", "class", "C++ class"]]]),objtypes: new Map([[0, "cpp:class"]]),terms: new Map([["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: new Map([])}) \ No newline at end of file diff --git a/tests/js/fixtures/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js index d0eab82a4ad..1e8808c3ac6 100644 --- a/tests/js/fixtures/ecmascript/searchindex.js +++ b/tests/js/fixtures/ecmascript/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["ECMAScript", [[0, null]]]]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["thi", 0], ["i", 0], ["sampl", 0], ["javascript", 0], ["aka", 0], ["project", 0], ["us", 0], ["gener", 0], ["search", 0], ["engin", 0], ["index", 0], ["fixtur", 0], ["__proto__", 0], ["properti", 0], ["access", 0], ["prototyp", 0], ["ani", 0], ["an", 0], ["object", 0], ["instanc", 0]]),titles: ["ECMAScript"],titleterms: new Map([["ecmascript", 0]])}) \ No newline at end of file +Search.setIndex({alltitles: new Map([["ECMAScript", [[0, null]]]]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["__proto__", 0], ["access", 0], ["aka", 0], ["an", 0], ["ani", 0], ["engin", 0], ["fixtur", 0], ["gener", 0], ["i", 0], ["index", 0], ["instanc", 0], ["javascript", 0], ["object", 0], ["project", 0], ["properti", 0], ["prototyp", 0], ["sampl", 0], ["search", 0], ["thi", 0], ["us", 0]]),titles: ["ECMAScript"],titleterms: new Map([["ecmascript", 0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index cb1d0548e86..b5e06451361 100644 --- a/tests/js/fixtures/multiterm/searchindex.js +++ b/tests/js/fixtures/multiterm/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["Main Page", [[0, null]]]]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["thi", 0], ["i", 0], ["multiterm", 0], ["test", 0], ["project", 0], ["document", 0], ["us", 0], ["fixtur", 0], ["check", 0], ["search", 0], ["function", 0], ["includ", 0], ["when", 0], ["ar", 0], ["built", 0], ["an", 0], ["html", 0], ["output", 0], ["format", 0], ["can", 0], ["successfulli", 0], ["match", 0], ["queri", 0], ["contain", 0], ["multipl", 0], ["term", 0], ["perform", 0], ["At", 0], ["time", 0], ["write", 0], ["messag", 0], ["applic", 0], ["doesn", 0], ["t", 0], ["support", 0], ["phrase", 0], ["requir", 0], ["all", 0], ["appear", 0], ["adjac", 0], ["each", 0], ["other", 0], ["same", 0], ["order", 0], ["perhap", 0], ["do", 0], ["futur", 0]]),titles: ["Main Page"],titleterms: new Map([["main", 0], ["page", 0]])}) \ No newline at end of file +Search.setIndex({alltitles: new Map([["Main Page", [[0, null]]]]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["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: new Map([["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 ebe02871f3d..2ccea540216 100644 --- a/tests/js/fixtures/partial/searchindex.js +++ b/tests/js/fixtures/partial/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["sphinx_utils module", [[0, null]]]]),docnames: ["index"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst"],indexentries: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["partial", 0], ["match", 0], ["document", 0], ["titl", 0], ["term", 0], ["should", 0], ["both", 0], ["possibl", 0], ["us", 0], ["javascript", 0], ["search", 0], ["function", 0], ["includ", 0], ["when", 0], ["html", 0], ["project", 0], ["ar", 0], ["built", 0], ["thi", 0], ["provid", 0], ["sampl", 0], ["restructuredtext", 0], ["input", 0], ["confirm", 0], ["i", 0]]),titles: ["sphinx_utils module"],titleterms: new Map([["sphinx_util", 0], ["modul", 0]])}) \ No newline at end of file +Search.setIndex({alltitles: new Map([["sphinx_utils module", [[0, null]]]]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["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: new Map([["modul", 0], ["sphinx_util", 0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 3944beac13a..48c31428393 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["Main Page", [[0, null]]], ["Relevance", [[0, "relevance"], [1, null]]], ["Result Scoring", [[0, "result-scoring"]]]]),docnames: ["index", "relevance"],envversion: new Map([["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], ["sphinx", 64]]),filenames: ["index.rst", "relevance.rst"],indexentries: new Map([["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: new Map([["", [[0, 0, 0, "-", "relevance"]]], ["relevance", [[0, 1, 1, "", "Example"]]], ["relevance.Example", [[0, 2, 1, "", "relevance"]]]]),objnames: new Map([[0, ["py", "module", "Python module"]], [1, ["py", "class", "Python class"]], [2, ["py", "attribute", "Python attribute"]]]),objtypes: new Map([[0, "py:module"], [1, "py:class"], [2, "py:attribute"]]),terms: new Map([["thi", 0], ["i", [0, 1]], ["titl", 0], ["test", 0], ["project", 0], ["In", [0, 1]], ["particular", 0], ["intend", 0], ["demonstr", 0], ["how", 0], ["sphinx", 0], ["can", [0, 1]], ["handl", 0], ["queri", [0, 1]], ["match", 0], ["against", 0], ["document", [0, 1]], ["subsect", 0], ["head", 0], ["rel", 0], ["other", 0], ["term", [0, 1]], ["found", 0], ["within", 0], ["text", 0], ["object", 0], ["name", [0, 1]], ["extract", 0], ["from", 0], ["code", [0, 1]], ["context", 0], ["search", [0, 1]], ["engin", 0], ["we", [0, 1]], ["sai", 0], ["user", [0, 1]], ["", [0, 1]], ["when", 0], ["contain", 0], ["inform", 0], ["seem", 0], ["like", [0, 1]], ["help", 0], ["them", 0], ["find", 0], ["an", 0], ["answer", 0], ["question", 0], ["thei", 0], ["re", 0], ["ask", 0], ["improv", 0], ["knowledg", 0], ["subject", 0], ["area", 0], ["research", 0], ["class", 0], ["exampl", [0, 1]], ["attribut", 0], ["docstr", 0], ["mani", 0], ["assign", 0], ["numer", 0], ["dure", 0], ["retriev", 0], ["often", 0], ["us", 0], ["determin", [0, 1]], ["order", 0], ["which", 0], ["present", 0], ["For", [0, 1]], ["issu", [0, 1]], ["two", 0], ["word", 0], ["both", 0], ["would", [0, 1]], ["typic", 0], ["more", 0], ["highli", [0, 1]], ["than", [0, 1]], ["onli", [0, 1]], ["one", [0, 1]], ["By", 0], ["evalu", 0], ["collect", 0], ["feedback", 0], ["over", 0], ["time", 0], ["attempt", 0], ["align", 0], ["some", 1], ["domain", 1], ["straightforward", 1], ["whether", 1], ["result", 1], ["ar", 1], ["softwar", 1], ["program", 1], ["languag", 1], ["ha", 1], ["printf", 1], ["could", 1], ["consid", 1], ["corpu", 1], ["describ", 1], ["built", 1], ["function", 1], ["same", 1], ["A", 1], ["happen", 1], ["mention", 1], ["part", 1], ["appear", 1], ["page", 1], ["also", 1], ["less", 1], ["itself", 1], ["detail", 1]]),titles: ["Main Page", "Relevance"],titleterms: new Map([["main", 0], ["page", 0], ["relev", [0, 1]], ["result", 0], ["score", 0]])}) \ No newline at end of file +Search.setIndex({alltitles: new Map([["Main Page", [[0, null]]], ["Relevance", [[0, "relevance"], [1, null]]], ["Result Scoring", [[0, "result-scoring"]]]]),docnames: ["index", "relevance"],envversion: new Map([["sphinx", 64], ["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: new Map([["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: new Map([["", [[0, 0, 0, "-", "relevance"]]], ["relevance", [[0, 1, 1, "", "Example"]]], ["relevance.Example", [[0, 2, 1, "", "relevance"]]]]),objnames: new Map([[0, ["py", "module", "Python module"]], [1, ["py", "class", "Python class"]], [2, ["py", "attribute", "Python attribute"]]]),objtypes: new Map([[0, "py:module"], [1, "py:class"], [2, "py:attribute"]]),terms: new Map([["", [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: new Map([["main", 0], ["page", 0], ["relev", [0, 1]], ["result", 0], ["score", 0]])}) \ No newline at end of file From 15a7dfa775ab786febf6c87648d3436f94a7656b Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:14:49 +0000 Subject: [PATCH 17/28] HTML search: refactor-out redundant code --- sphinx/search/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index fa8066c349d..fa1b8def781 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -170,7 +170,7 @@ class _JavaScriptIndex: def dumps(self, data: Any) -> str: assert all(k.isidentifier() for k in data) js_indices = { - key: f'new Map({json.dumps([[key, value] for key, value in sorted(index.items())])})' + key: f'new Map({json.dumps(sorted(index.items()))})' if isinstance(index, dict) else json.dumps(index) for key, index in sorted(data.items()) From 6e670ead3a02d780e6530f49e44c81ff5b68b3da Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:15:33 +0000 Subject: [PATCH 18/28] HTML search: rectify variable names --- sphinx/search/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index fa1b8def781..6d39a447cd6 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -170,10 +170,10 @@ class _JavaScriptIndex: def dumps(self, data: Any) -> str: assert all(k.isidentifier() for k in data) js_indices = { - key: f'new Map({json.dumps(sorted(index.items()))})' - if isinstance(index, dict) - else json.dumps(index) - for key, index in sorted(data.items()) + name: f'new Map({json.dumps(sorted(entries.items()))})' + if isinstance(entries, dict) + else json.dumps(entries) + for name, entries in sorted(data.items()) } data_js = '{' + ','.join(f'{k}: {v}' for k, v in js_indices.items()) + '}' return self.PREFIX + data_js + self.SUFFIX From f2848ede807d7b3e9c2eea97b7e4cf5c1751f0ba Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:18:16 +0000 Subject: [PATCH 19/28] HTML search: compress index representation --- sphinx/search/__init__.py | 7 ++++--- tests/js/fixtures/cpp/searchindex.js | 2 +- tests/js/fixtures/ecmascript/searchindex.js | 2 +- tests/js/fixtures/multiterm/searchindex.js | 2 +- tests/js/fixtures/partial/searchindex.js | 2 +- tests/js/fixtures/titles/searchindex.js | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 6d39a447cd6..14fa9b45144 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -169,13 +169,14 @@ class _JavaScriptIndex: def dumps(self, data: Any) -> str: assert all(k.isidentifier() for k in data) + json_dump = lambda data: json.dumps(data, separators=(',', ':')) js_indices = { - name: f'new Map({json.dumps(sorted(entries.items()))})' + name: f'new Map({json_dump(sorted(entries.items()))})' if isinstance(entries, dict) - else json.dumps(entries) + else json_dump(entries) for name, entries in sorted(data.items()) } - data_js = '{' + ','.join(f'{k}: {v}' for k, v in js_indices.items()) + '}' + data_js = '{' + ','.join(f'{k}:{v}' for k, v in js_indices.items()) + '}' return self.PREFIX + data_js + self.SUFFIX def loads(self, s: str) -> Any: diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index e6deac5298b..3da7369deef 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([["sphinx (c++ class)", [[0, "_CPPv46Sphinx", false]]]]),objects: new Map([["", [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]]]),objnames: new Map([[0, ["cpp", "class", "C++ class"]]]),objtypes: new Map([[0, "cpp:class"]]),terms: new Map([["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: new Map([])}) \ No newline at end of file +Search.setIndex({alltitles:new Map([]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([["sphinx (c++ class)",[[0,"_CPPv46Sphinx",false]]]]),objects:new Map([["",[[0,0,1,"_CPPv46Sphinx","Sphinx"]]]]),objnames:new Map([[0,["cpp","class","C++ class"]]]),objtypes:new Map([[0,"cpp:class"]]),terms:new Map([["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:new Map([])}) \ No newline at end of file diff --git a/tests/js/fixtures/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js index 1e8808c3ac6..3b1341de2a7 100644 --- a/tests/js/fixtures/ecmascript/searchindex.js +++ b/tests/js/fixtures/ecmascript/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["ECMAScript", [[0, null]]]]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["__proto__", 0], ["access", 0], ["aka", 0], ["an", 0], ["ani", 0], ["engin", 0], ["fixtur", 0], ["gener", 0], ["i", 0], ["index", 0], ["instanc", 0], ["javascript", 0], ["object", 0], ["project", 0], ["properti", 0], ["prototyp", 0], ["sampl", 0], ["search", 0], ["thi", 0], ["us", 0]]),titles: ["ECMAScript"],titleterms: new Map([["ecmascript", 0]])}) \ No newline at end of file +Search.setIndex({alltitles:new Map([["ECMAScript",[[0,null]]]]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([]),objects:new Map([]),objnames:new Map([]),objtypes:new Map([]),terms:new Map([["__proto__",0],["access",0],["aka",0],["an",0],["ani",0],["engin",0],["fixtur",0],["gener",0],["i",0],["index",0],["instanc",0],["javascript",0],["object",0],["project",0],["properti",0],["prototyp",0],["sampl",0],["search",0],["thi",0],["us",0]]),titles:["ECMAScript"],titleterms:new Map([["ecmascript",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index b5e06451361..81469f756cf 100644 --- a/tests/js/fixtures/multiterm/searchindex.js +++ b/tests/js/fixtures/multiterm/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["Main Page", [[0, null]]]]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["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: new Map([["main", 0], ["page", 0]])}) \ No newline at end of file +Search.setIndex({alltitles:new Map([["Main Page",[[0,null]]]]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([]),objects:new Map([]),objnames:new Map([]),objtypes:new Map([]),terms:new Map([["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:new Map([["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 2ccea540216..3f54752219f 100644 --- a/tests/js/fixtures/partial/searchindex.js +++ b/tests/js/fixtures/partial/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["sphinx_utils module", [[0, null]]]]),docnames: ["index"],envversion: new Map([["sphinx", 64], ["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: new Map([]),objects: new Map([]),objnames: new Map([]),objtypes: new Map([]),terms: new Map([["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: new Map([["modul", 0], ["sphinx_util", 0]])}) \ No newline at end of file +Search.setIndex({alltitles:new Map([["sphinx_utils module",[[0,null]]]]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([]),objects:new Map([]),objnames:new Map([]),objtypes:new Map([]),terms:new Map([["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:new Map([["modul",0],["sphinx_util",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 48c31428393..5f885b7e1a2 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles: new Map([["Main Page", [[0, null]]], ["Relevance", [[0, "relevance"], [1, null]]], ["Result Scoring", [[0, "result-scoring"]]]]),docnames: ["index", "relevance"],envversion: new Map([["sphinx", 64], ["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: new Map([["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: new Map([["", [[0, 0, 0, "-", "relevance"]]], ["relevance", [[0, 1, 1, "", "Example"]]], ["relevance.Example", [[0, 2, 1, "", "relevance"]]]]),objnames: new Map([[0, ["py", "module", "Python module"]], [1, ["py", "class", "Python class"]], [2, ["py", "attribute", "Python attribute"]]]),objtypes: new Map([[0, "py:module"], [1, "py:class"], [2, "py:attribute"]]),terms: new Map([["", [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: new Map([["main", 0], ["page", 0], ["relev", [0, 1]], ["result", 0], ["score", 0]])}) \ No newline at end of file +Search.setIndex({alltitles:new Map([["Main Page",[[0,null]]],["Relevance",[[0,"relevance"],[1,null]]],["Result Scoring",[[0,"result-scoring"]]]]),docnames:["index","relevance"],envversion:new Map([["sphinx",64],["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:new Map([["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:new Map([["",[[0,0,0,"-","relevance"]]],["relevance",[[0,1,1,"","Example"]]],["relevance.Example",[[0,2,1,"","relevance"]]]]),objnames:new Map([[0,["py","module","Python module"]],[1,["py","class","Python class"]],[2,["py","attribute","Python attribute"]]]),objtypes:new Map([[0,"py:module"],[1,"py:class"],[2,"py:attribute"]]),terms:new Map([["",[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:new Map([["main",0],["page",0],["relev",[0,1]],["result",0],["score",0]])}) \ No newline at end of file From 798cb48b8180266730341b2114675224630126ff Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:19:12 +0000 Subject: [PATCH 20/28] HTML search: enclose index names in double-quotes --- sphinx/search/__init__.py | 2 +- tests/js/fixtures/cpp/searchindex.js | 2 +- tests/js/fixtures/ecmascript/searchindex.js | 2 +- tests/js/fixtures/multiterm/searchindex.js | 2 +- tests/js/fixtures/partial/searchindex.js | 2 +- tests/js/fixtures/titles/searchindex.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 14fa9b45144..5f6e5966609 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -171,7 +171,7 @@ def dumps(self, data: Any) -> str: assert all(k.isidentifier() for k in data) json_dump = lambda data: json.dumps(data, separators=(',', ':')) js_indices = { - name: f'new Map({json_dump(sorted(entries.items()))})' + json_dump(name): f'new Map({json_dump(sorted(entries.items()))})' if isinstance(entries, dict) else json_dump(entries) for name, entries in sorted(data.items()) diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index 3da7369deef..5922a5f33ac 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles:new Map([]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([["sphinx (c++ class)",[[0,"_CPPv46Sphinx",false]]]]),objects:new Map([["",[[0,0,1,"_CPPv46Sphinx","Sphinx"]]]]),objnames:new Map([[0,["cpp","class","C++ class"]]]),objtypes:new Map([[0,"cpp:class"]]),terms:new Map([["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:new Map([])}) \ No newline at end of file +Search.setIndex({"alltitles":new Map([]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([["sphinx (c++ class)",[[0,"_CPPv46Sphinx",false]]]]),"objects":new Map([["",[[0,0,1,"_CPPv46Sphinx","Sphinx"]]]]),"objnames":new Map([[0,["cpp","class","C++ class"]]]),"objtypes":new Map([[0,"cpp:class"]]),"terms":new Map([["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":new Map([])}) \ No newline at end of file diff --git a/tests/js/fixtures/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js index 3b1341de2a7..21aa5b30a80 100644 --- a/tests/js/fixtures/ecmascript/searchindex.js +++ b/tests/js/fixtures/ecmascript/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles:new Map([["ECMAScript",[[0,null]]]]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([]),objects:new Map([]),objnames:new Map([]),objtypes:new Map([]),terms:new Map([["__proto__",0],["access",0],["aka",0],["an",0],["ani",0],["engin",0],["fixtur",0],["gener",0],["i",0],["index",0],["instanc",0],["javascript",0],["object",0],["project",0],["properti",0],["prototyp",0],["sampl",0],["search",0],["thi",0],["us",0]]),titles:["ECMAScript"],titleterms:new Map([["ecmascript",0]])}) \ No newline at end of file +Search.setIndex({"alltitles":new Map([["ECMAScript",[[0,null]]]]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([]),"objects":new Map([]),"objnames":new Map([]),"objtypes":new Map([]),"terms":new Map([["__proto__",0],["access",0],["aka",0],["an",0],["ani",0],["engin",0],["fixtur",0],["gener",0],["i",0],["index",0],["instanc",0],["javascript",0],["object",0],["project",0],["properti",0],["prototyp",0],["sampl",0],["search",0],["thi",0],["us",0]]),"titles":["ECMAScript"],"titleterms":new Map([["ecmascript",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index 81469f756cf..20a95c7610c 100644 --- a/tests/js/fixtures/multiterm/searchindex.js +++ b/tests/js/fixtures/multiterm/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles:new Map([["Main Page",[[0,null]]]]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([]),objects:new Map([]),objnames:new Map([]),objtypes:new Map([]),terms:new Map([["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:new Map([["main",0],["page",0]])}) \ No newline at end of file +Search.setIndex({"alltitles":new Map([["Main Page",[[0,null]]]]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([]),"objects":new Map([]),"objnames":new Map([]),"objtypes":new Map([]),"terms":new Map([["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":new Map([["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 3f54752219f..9aca213a96c 100644 --- a/tests/js/fixtures/partial/searchindex.js +++ b/tests/js/fixtures/partial/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles:new Map([["sphinx_utils module",[[0,null]]]]),docnames:["index"],envversion:new Map([["sphinx",64],["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:new Map([]),objects:new Map([]),objnames:new Map([]),objtypes:new Map([]),terms:new Map([["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:new Map([["modul",0],["sphinx_util",0]])}) \ No newline at end of file +Search.setIndex({"alltitles":new Map([["sphinx_utils module",[[0,null]]]]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([]),"objects":new Map([]),"objnames":new Map([]),"objtypes":new Map([]),"terms":new Map([["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":new Map([["modul",0],["sphinx_util",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 5f885b7e1a2..ab6702ed447 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({alltitles:new Map([["Main Page",[[0,null]]],["Relevance",[[0,"relevance"],[1,null]]],["Result Scoring",[[0,"result-scoring"]]]]),docnames:["index","relevance"],envversion:new Map([["sphinx",64],["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:new Map([["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:new Map([["",[[0,0,0,"-","relevance"]]],["relevance",[[0,1,1,"","Example"]]],["relevance.Example",[[0,2,1,"","relevance"]]]]),objnames:new Map([[0,["py","module","Python module"]],[1,["py","class","Python class"]],[2,["py","attribute","Python attribute"]]]),objtypes:new Map([[0,"py:module"],[1,"py:class"],[2,"py:attribute"]]),terms:new Map([["",[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:new Map([["main",0],["page",0],["relev",[0,1]],["result",0],["score",0]])}) \ No newline at end of file +Search.setIndex({"alltitles":new Map([["Main Page",[[0,null]]],["Relevance",[[0,"relevance"],[1,null]]],["Result Scoring",[[0,"result-scoring"]]]]),"docnames":["index","relevance"],"envversion":new Map([["sphinx",64],["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":new Map([["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":new Map([["",[[0,0,0,"-","relevance"]]],["relevance",[[0,1,1,"","Example"]]],["relevance.Example",[[0,2,1,"","relevance"]]]]),"objnames":new Map([[0,["py","module","Python module"]],[1,["py","class","Python class"]],[2,["py","attribute","Python attribute"]]]),"objtypes":new Map([[0,"py:module"],[1,"py:class"],[2,"py:attribute"]]),"terms":new Map([["",[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":new Map([["main",0],["page",0],["relev",[0,1]],["result",0],["score",0]])}) \ No newline at end of file From af61293341136e2b4c8a8668d682b79fbd04d531 Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:22:37 +0000 Subject: [PATCH 21/28] Tests: HTML search: use `ast.literal_eval` to parse index contents --- tests/test_search.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index 600f66cb9f6..6deee6d08f6 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2,6 +2,7 @@ from __future__ import annotations +import ast import json import warnings from io import BytesIO @@ -67,7 +68,16 @@ def load_searchindex(path: Path) -> Any: assert searchindex.startswith('Search.setIndex(') assert searchindex.endswith(')') - return json.loads(searchindex[16:-1]) + indices = ast.literal_eval( + searchindex[16:-1] + .replace('new Map', '') + .replace('null', 'None') + .replace('false', 'False') + ) + return { + name: dict(entries) if entries and isinstance(entries[0], list) else entries + for name, entries in indices.items() + } def is_registered_term(index: Any, keyword: str) -> bool: From 2bcf7a6739909f4585014defb57657ee2db161e2 Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:23:14 +0000 Subject: [PATCH 22/28] Tests: HTML search: update test expectation --- tests/test_search.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_search.py b/tests/test_search.py index 6deee6d08f6..88d1819d7e6 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -162,8 +162,8 @@ def test_term_in_heading_and_section(app): # if search term is in the title of one doc and in the text of another # both documents should be a hit in the search index as a title, # respectively text hit - assert '"textinhead":2' in searchindex - assert '"textinhead":0' in searchindex + assert '["textinhead",2]' in searchindex + assert '["textinhead",0]' in searchindex @pytest.mark.sphinx('html', testroot='search') From 381c26fb431e2fa57b5762ad7123a48c5282ef9a Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:31:10 +0000 Subject: [PATCH 23/28] HTML search: cleanup: remove redundant `assert` statement --- sphinx/search/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 5f6e5966609..7c1f3b976ae 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -168,7 +168,6 @@ class _JavaScriptIndex: SUFFIX = ')' def dumps(self, data: Any) -> str: - assert all(k.isidentifier() for k in data) json_dump = lambda data: json.dumps(data, separators=(',', ':')) js_indices = { json_dump(name): f'new Map({json_dump(sorted(entries.items()))})' From ac281dc9e47dbb3910a17044e7c5dfc131e0c992 Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:34:34 +0000 Subject: [PATCH 24/28] HTML search: typing/lint fixups for `ruff` and `mypy` --- sphinx/search/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 7c1f3b976ae..5bf3bb33986 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -168,7 +168,9 @@ class _JavaScriptIndex: SUFFIX = ')' def dumps(self, data: Any) -> str: - json_dump = lambda data: json.dumps(data, separators=(',', ':')) + def json_dump(data: str | tuple | list) -> str: + return json.dumps(data, separators=(',', ':')) + js_indices = { json_dump(name): f'new Map({json_dump(sorted(entries.items()))})' if isinstance(entries, dict) From fae81a0a92e81618c8fc70b2db1f3f93bc0dccaa Mon Sep 17 00:00:00 2001 From: James Addison Date: Sat, 16 Nov 2024 11:59:09 +0000 Subject: [PATCH 25/28] Edit CHANGES.rst entry [skip ci] --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cf841a445bc..43a8edd5936 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,8 +23,8 @@ Bugs fixed * #13060: HTML Search: use ``Map`` to store per-file term scores. Patch by James Addison -* #13097: HTML Search: add a precautionary check for query term - presence in index properties before accessing them. +* #13097: HTML Search: represent index entries in ``searchindex.js`` + using JavaScript ``Map`` instances, to handle a query edge-case. Patch by James Addison Testing From 6d5a121d2fcdee2341d76c74f6840bcad963acac Mon Sep 17 00:00:00 2001 From: James Addison Date: Sun, 24 Nov 2024 12:54:47 +0000 Subject: [PATCH 26/28] HTML search: use JSON format for the search index --- CHANGES.rst | 4 +- sphinx/builders/html/__init__.py | 2 +- sphinx/search/__init__.py | 20 ++------ sphinx/themes/basic/search.html | 2 +- sphinx/themes/basic/static/searchindex.js | 4 ++ sphinx/themes/basic/static/searchtools.js | 47 ++++++++++--------- tests/js/fixtures/cpp/searchindex.js | 1 - tests/js/fixtures/cpp/searchindex.json | 1 + tests/js/fixtures/ecmascript/searchindex.js | 1 - tests/js/fixtures/ecmascript/searchindex.json | 1 + tests/js/fixtures/multiterm/searchindex.js | 1 - tests/js/fixtures/multiterm/searchindex.json | 1 + tests/js/fixtures/partial/searchindex.js | 1 - tests/js/fixtures/partial/searchindex.json | 1 + tests/js/fixtures/titles/searchindex.js | 1 - tests/js/fixtures/titles/searchindex.json | 1 + tests/js/jasmine-browser.mjs | 1 - tests/js/searchtools.spec.js | 28 +++++------ tests/test_search.py | 44 +++++++---------- utils/generate_js_fixtures.py | 4 +- 20 files changed, 72 insertions(+), 94 deletions(-) create mode 100644 sphinx/themes/basic/static/searchindex.js delete mode 100644 tests/js/fixtures/cpp/searchindex.js create mode 100644 tests/js/fixtures/cpp/searchindex.json delete mode 100644 tests/js/fixtures/ecmascript/searchindex.js create mode 100644 tests/js/fixtures/ecmascript/searchindex.json delete mode 100644 tests/js/fixtures/multiterm/searchindex.js create mode 100644 tests/js/fixtures/multiterm/searchindex.json delete mode 100644 tests/js/fixtures/partial/searchindex.js create mode 100644 tests/js/fixtures/partial/searchindex.json delete mode 100644 tests/js/fixtures/titles/searchindex.js create mode 100644 tests/js/fixtures/titles/searchindex.json diff --git a/CHANGES.rst b/CHANGES.rst index 43a8edd5936..05b725bc255 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,8 +23,8 @@ Bugs fixed * #13060: HTML Search: use ``Map`` to store per-file term scores. Patch by James Addison -* #13097: HTML Search: represent index entries in ``searchindex.js`` - using JavaScript ``Map`` instances, to handle a query edge-case. +* #13097: HTML Search: serialize search index in JSON format, to + handle a query edge-case. Patch by James Addison Testing diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 9a1001fceaf..d2d57e2b645 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -124,7 +124,7 @@ class StandaloneHTMLBuilder(Builder): supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', 'image/jpeg'] supported_remote_images = True supported_data_uri_images = True - searchindex_filename = 'searchindex.js' + searchindex_filename = 'searchindex.json' add_permalinks = True allow_sharp_as_current_path = True embedded = False # for things like HTML help or Qt help: suppresses sidebar diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 5bf3bb33986..abcb485ad9d 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -164,28 +164,14 @@ class _JavaScriptIndex: on the documentation search object to register the index. """ - PREFIX = 'Search.setIndex(' - SUFFIX = ')' - def dumps(self, data: Any) -> str: - def json_dump(data: str | tuple | list) -> str: - return json.dumps(data, separators=(',', ':')) - - js_indices = { - json_dump(name): f'new Map({json_dump(sorted(entries.items()))})' - if isinstance(entries, dict) - else json_dump(entries) - for name, entries in sorted(data.items()) - } - data_js = '{' + ','.join(f'{k}:{v}' for k, v in js_indices.items()) + '}' - return self.PREFIX + data_js + self.SUFFIX + return json.dumps(data, separators=(',', ':'), sort_keys=True) def loads(self, s: str) -> Any: - data = s[len(self.PREFIX) : -len(self.SUFFIX)] - if not data or not s.startswith(self.PREFIX) or not s.endswith(self.SUFFIX): + if not s: msg = 'invalid data' raise ValueError(msg) - return json.loads(data) + return json.loads(s) def dump(self, data: Any, f: IO[str]) -> None: f.write(self.dumps(data)) diff --git a/sphinx/themes/basic/search.html b/sphinx/themes/basic/search.html index 0ce54c43424..59730288ca6 100644 --- a/sphinx/themes/basic/search.html +++ b/sphinx/themes/basic/search.html @@ -7,7 +7,7 @@ {%- endblock %} {% block extrahead %} - + {{ super() }} {% endblock %} diff --git a/sphinx/themes/basic/static/searchindex.js b/sphinx/themes/basic/static/searchindex.js new file mode 100644 index 00000000000..f2cc75f4729 --- /dev/null +++ b/sphinx/themes/basic/static/searchindex.js @@ -0,0 +1,4 @@ +req = new XMLHttpRequest(); +req.open("GET", "searchindex.json", false); +req.send(null); +Search.setIndex(req.responseText); diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index d986e476f2c..7ee323fe905 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -219,7 +219,7 @@ const Search = { (document.body.appendChild(document.createElement("script")).src = url), setIndex: (index) => { - Search._index = index; + Search._index = JSON.parse(index); if (Search._queued_query !== null) { const query = Search._queued_query; Search._queued_query = null; @@ -333,7 +333,7 @@ const Search = { _removeChildren(document.getElementById("search-progress")); const queryLower = query.toLowerCase().trim(); - for (const [title, foundTitles] of allTitles) { + for (const [title, foundTitles] of Object.entries(allTitles)) { if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { for (const [file, id] of foundTitles) { const score = Math.round(Scorer.title * queryLower.length / title.length); @@ -352,7 +352,7 @@ const Search = { } // search for explicit entries in index directives - for (const [entry, foundEntries] of indexEntries) { + for (const [entry, foundEntries] of Object.entries(indexEntries)) { if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { for (const [file, id, isMain] of foundEntries) { const score = Math.round(100 * queryLower.length / entry.length); @@ -452,7 +452,7 @@ const Search = { else if (parts.slice(-1)[0].indexOf(object) > -1) score += Scorer.objPartialMatch; // matches in last name - const objName = objNames.get(match[1])[2]; + const objName = objNames[match[1]][2]; const title = titles[match[0]]; // If more than one term searched for, we require other words to be @@ -469,7 +469,7 @@ const Search = { let anchor = match[3]; if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames.get(match[1])[1] + "-" + fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; const descr = objName + _(", in ") + title; @@ -488,9 +488,11 @@ const Search = { SearchResultKind.object, ]); }; - for (const [prefix, arrays] of objects) { - arrays.forEach((array) => objectSearchCallback(prefix, array)); - } + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); return results; }, @@ -511,25 +513,24 @@ const Search = { // perform the search on the required terms searchTerms.forEach((word) => { const files = []; - // find documents, if any, containing the query word in their text/title term indices const arr = [ - { files: terms.get(word), score: Scorer.term }, - { files: titleTerms.get(word), score: Scorer.title }, + { files: terms.hasOwnProperty(word) ? terms[word] : [], score: Scorer.term }, + { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : [], score: Scorer.title }, ]; // add support for partial matches if (word.length > 2) { const escapedWord = _escapeRegExp(word); - if (!terms.has(word)) { - for (const [term, files] of terms) { + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { if (term.match(escapedWord)) - arr.push({ files: files, score: Scorer.partialTerm }); - } + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); } - if (!titleTerms.has(word)) { - for (const [term, files] of titleTerms) { + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { if (term.match(escapedWord)) - arr.push({ files: files, score: Scorer.partialTitle }); - } + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); } } @@ -578,10 +579,10 @@ const Search = { if ( [...excludedTerms].some( (term) => - terms.get(term) === file || - titleTerms.get(term) === file || - (terms.get(term) || []).includes(file) || - (titleTerms.get(term) || []).includes(file) + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) ) ) break; diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js deleted file mode 100644 index 5922a5f33ac..00000000000 --- a/tests/js/fixtures/cpp/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles":new Map([]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([["sphinx (c++ class)",[[0,"_CPPv46Sphinx",false]]]]),"objects":new Map([["",[[0,0,1,"_CPPv46Sphinx","Sphinx"]]]]),"objnames":new Map([[0,["cpp","class","C++ class"]]]),"objtypes":new Map([[0,"cpp:class"]]),"terms":new Map([["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":new Map([])}) \ No newline at end of file diff --git a/tests/js/fixtures/cpp/searchindex.json b/tests/js/fixtures/cpp/searchindex.json new file mode 100644 index 00000000000..188056c9fcf --- /dev/null +++ b/tests/js/fixtures/cpp/searchindex.json @@ -0,0 +1 @@ +{"alltitles":{},"docnames":["index"],"envversion":{"sphinx":64,"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/ecmascript/searchindex.js b/tests/js/fixtures/ecmascript/searchindex.js deleted file mode 100644 index 21aa5b30a80..00000000000 --- a/tests/js/fixtures/ecmascript/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles":new Map([["ECMAScript",[[0,null]]]]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([]),"objects":new Map([]),"objnames":new Map([]),"objtypes":new Map([]),"terms":new Map([["__proto__",0],["access",0],["aka",0],["an",0],["ani",0],["engin",0],["fixtur",0],["gener",0],["i",0],["index",0],["instanc",0],["javascript",0],["object",0],["project",0],["properti",0],["prototyp",0],["sampl",0],["search",0],["thi",0],["us",0]]),"titles":["ECMAScript"],"titleterms":new Map([["ecmascript",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/ecmascript/searchindex.json b/tests/js/fixtures/ecmascript/searchindex.json new file mode 100644 index 00000000000..aef1ae4e0fa --- /dev/null +++ b/tests/js/fixtures/ecmascript/searchindex.json @@ -0,0 +1 @@ +{"alltitles":{"ECMAScript":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":64,"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":{"__proto__":0,"access":0,"aka":0,"an":0,"ani":0,"engin":0,"fixtur":0,"gener":0,"i":0,"index":0,"instanc":0,"javascript":0,"object":0,"project":0,"properti":0,"prototyp":0,"sampl":0,"search":0,"thi":0,"us":0},"titles":["ECMAScript"],"titleterms":{"ecmascript":0}} \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js deleted file mode 100644 index 20a95c7610c..00000000000 --- a/tests/js/fixtures/multiterm/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles":new Map([["Main Page",[[0,null]]]]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([]),"objects":new Map([]),"objnames":new Map([]),"objtypes":new Map([]),"terms":new Map([["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":new Map([["main",0],["page",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.json b/tests/js/fixtures/multiterm/searchindex.json new file mode 100644 index 00000000000..e14c6d48e60 --- /dev/null +++ b/tests/js/fixtures/multiterm/searchindex.json @@ -0,0 +1 @@ +{"alltitles":{"Main Page":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":64,"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 deleted file mode 100644 index 9aca213a96c..00000000000 --- a/tests/js/fixtures/partial/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles":new Map([["sphinx_utils module",[[0,null]]]]),"docnames":["index"],"envversion":new Map([["sphinx",64],["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":new Map([]),"objects":new Map([]),"objnames":new Map([]),"objtypes":new Map([]),"terms":new Map([["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":new Map([["modul",0],["sphinx_util",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/partial/searchindex.json b/tests/js/fixtures/partial/searchindex.json new file mode 100644 index 00000000000..a0a50be326e --- /dev/null +++ b/tests/js/fixtures/partial/searchindex.json @@ -0,0 +1 @@ +{"alltitles":{"sphinx_utils module":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":64,"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/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js deleted file mode 100644 index ab6702ed447..00000000000 --- a/tests/js/fixtures/titles/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles":new Map([["Main Page",[[0,null]]],["Relevance",[[0,"relevance"],[1,null]]],["Result Scoring",[[0,"result-scoring"]]]]),"docnames":["index","relevance"],"envversion":new Map([["sphinx",64],["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":new Map([["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":new Map([["",[[0,0,0,"-","relevance"]]],["relevance",[[0,1,1,"","Example"]]],["relevance.Example",[[0,2,1,"","relevance"]]]]),"objnames":new Map([[0,["py","module","Python module"]],[1,["py","class","Python class"]],[2,["py","attribute","Python attribute"]]]),"objtypes":new Map([[0,"py:module"],[1,"py:class"],[2,"py:attribute"]]),"terms":new Map([["",[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":new Map([["main",0],["page",0],["relev",[0,1]],["result",0],["score",0]])}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.json b/tests/js/fixtures/titles/searchindex.json new file mode 100644 index 00000000000..e94d9c6f83c --- /dev/null +++ b/tests/js/fixtures/titles/searchindex.json @@ -0,0 +1 @@ +{"alltitles":{"Main Page":[[0,null]],"Relevance":[[0,"relevance"],[1,null]],"Result Scoring":[[0,"result-scoring"]]},"docnames":["index","relevance"],"envversion":{"sphinx":64,"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 diff --git a/tests/js/jasmine-browser.mjs b/tests/js/jasmine-browser.mjs index b84217fd8c5..17b0c04ad34 100644 --- a/tests/js/jasmine-browser.mjs +++ b/tests/js/jasmine-browser.mjs @@ -4,7 +4,6 @@ export default { 'sphinx/themes/basic/static/doctools.js', 'sphinx/themes/basic/static/searchtools.js', 'sphinx/themes/basic/static/sphinx_highlight.js', - 'tests/js/fixtures/**/*.js', 'tests/js/documentation_options.js', 'tests/js/language_data.js', ], diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 3ea628c4692..5a842604026 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -1,10 +1,10 @@ describe('Basic html theme search', function() { - function loadFixture(name) { + function loadIndex(name) { req = new XMLHttpRequest(); req.open("GET", `__src__/tests/js/fixtures/${name}`, false); req.send(null); - return req.responseText; + Search.setIndex(req.responseText); } function checkRanking(expectedRanking, results) { @@ -28,7 +28,7 @@ describe('Basic html theme search', function() { describe('terms search', function() { it('should find "C++" when in index', function() { - eval(loadFixture("cpp/searchindex.js")); + loadIndex("cpp/searchindex.json"); [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('C++'); @@ -45,7 +45,7 @@ describe('Basic html theme search', function() { }); it('should be able to search for multiple terms', function() { - eval(loadFixture("multiterm/searchindex.js")); + loadIndex("multiterm/searchindex.json"); [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('main page'); hits = [[ @@ -61,7 +61,7 @@ describe('Basic html theme search', function() { }); it('should partially-match "sphinx" when in title index', function() { - eval(loadFixture("partial/searchindex.js")); + loadIndex("partial/searchindex.json"); [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('sphinx'); @@ -78,7 +78,7 @@ describe('Basic html theme search', function() { }); it('should partially-match within "possible" when in term index', function() { - eval(loadFixture("partial/searchindex.js")); + loadIndex("partial/searchindex.json"); [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('ossibl'); terms = Search._index.terms; @@ -101,7 +101,7 @@ describe('Basic html theme search', function() { describe('aggregation of search results', function() { it('should combine document title and document term matches', function() { - eval(loadFixture("multiterm/searchindex.js")); + loadIndex("multiterm/searchindex.json"); searchParameters = Search._parseQuery('main page'); @@ -138,7 +138,7 @@ describe('Basic html theme search', function() { */ it('should score a code module match above a page-title match', function() { - eval(loadFixture("titles/searchindex.js")); + loadIndex("titles/searchindex.json"); expectedRanking = [ ['index', 'relevance', '#module-relevance'], /* py:module documentation */ @@ -152,7 +152,7 @@ describe('Basic html theme search', function() { }); it('should score a main-title match above an object member match', function() { - eval(loadFixture("titles/searchindex.js")); + loadIndex("titles/searchindex.json"); expectedRanking = [ ['relevance', 'Relevance', ''], /* main title */ @@ -166,7 +166,7 @@ describe('Basic html theme search', function() { }); it('should score a title match above a standard index entry match', function() { - eval(loadFixture("titles/searchindex.js")); + loadIndex("titles/searchindex.json"); expectedRanking = [ ['relevance', 'Relevance', ''], /* title */ @@ -180,7 +180,7 @@ describe('Basic html theme search', function() { }); it('should score a priority index entry match above a title match', function() { - eval(loadFixture("titles/searchindex.js")); + loadIndex("titles/searchindex.json"); expectedRanking = [ ['index', 'Main Page', '#index-0'], /* index entry */ @@ -194,7 +194,7 @@ describe('Basic html theme search', function() { }); it('should score a main-title match above a subheading-title match', function() { - eval(loadFixture("titles/searchindex.js")); + loadIndex("titles/searchindex.json"); expectedRanking = [ ['relevance', 'Relevance', ''], /* main title */ @@ -212,7 +212,7 @@ describe('Basic html theme search', function() { describe('can handle edge-case search queries', function() { it('can search for the javascript prototype property', function() { - eval(loadFixture("ecmascript/searchindex.js")); + loadIndex("ecmascript/searchindex.json"); searchParameters = Search._parseQuery('__proto__'); @@ -231,7 +231,7 @@ describe('Basic html theme search', function() { }); it('does not find the javascript prototype property in unrelated documents', function() { - eval(loadFixture("partial/searchindex.js")); + loadIndex("partial/searchindex.json"); searchParameters = Search._parseQuery('__proto__'); diff --git a/tests/test_search.py b/tests/test_search.py index 88d1819d7e6..c1e27be2afd 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -65,19 +65,7 @@ def get_objects(self) -> list[tuple[str, str, str, str, str, int]]: def load_searchindex(path: Path) -> Any: searchindex = path.read_text(encoding='utf8') - assert searchindex.startswith('Search.setIndex(') - assert searchindex.endswith(')') - - indices = ast.literal_eval( - searchindex[16:-1] - .replace('new Map', '') - .replace('null', 'None') - .replace('false', 'False') - ) - return { - name: dict(entries) if entries and isinstance(entries[0], list) else entries - for name, entries in indices.items() - } + return json.loads(searchindex) def is_registered_term(index: Any, keyword: str) -> bool: @@ -100,7 +88,7 @@ def is_registered_term(index: Any, keyword: str) -> bool: @pytest.mark.sphinx('html', testroot='ext-viewcode') def test_objects_are_escaped(app): app.build(force_all=True) - index = load_searchindex(app.outdir / 'searchindex.js') + index = load_searchindex(app.outdir / 'searchindex.json') for item in index.get('objects').get(''): if item[-1] == 'n::Array<T, d>': # n::Array is escaped break @@ -111,7 +99,7 @@ def test_objects_are_escaped(app): @pytest.mark.sphinx('html', testroot='search') def test_meta_keys_are_handled_for_language_en(app): app.build(force_all=True) - searchindex = load_searchindex(app.outdir / 'searchindex.js') + searchindex = load_searchindex(app.outdir / 'searchindex.json') assert not is_registered_term(searchindex, 'thisnoteith') assert is_registered_term(searchindex, 'thisonetoo') assert is_registered_term(searchindex, 'findthiskei') @@ -129,7 +117,7 @@ def test_meta_keys_are_handled_for_language_en(app): ) def test_meta_keys_are_handled_for_language_de(app): app.build(force_all=True) - searchindex = load_searchindex(app.outdir / 'searchindex.js') + searchindex = load_searchindex(app.outdir / 'searchindex.json') assert not is_registered_term(searchindex, 'thisnoteith') assert is_registered_term(searchindex, 'thisonetoo') assert not is_registered_term(searchindex, 'findthiskei') @@ -142,14 +130,14 @@ def test_meta_keys_are_handled_for_language_de(app): @pytest.mark.sphinx('html', testroot='search') def test_stemmer_does_not_remove_short_words(app): app.build(force_all=True) - searchindex = (app.outdir / 'searchindex.js').read_text(encoding='utf8') + searchindex = (app.outdir / 'searchindex.json').read_text(encoding='utf8') assert 'bat' in searchindex @pytest.mark.sphinx('html', testroot='search') def test_stemmer(app): app.build(force_all=True) - searchindex = load_searchindex(app.outdir / 'searchindex.js') + searchindex = load_searchindex(app.outdir / 'searchindex.json') print(searchindex) assert is_registered_term(searchindex, 'findthisstemmedkei') assert is_registered_term(searchindex, 'intern') @@ -158,18 +146,18 @@ def test_stemmer(app): @pytest.mark.sphinx('html', testroot='search') def test_term_in_heading_and_section(app): app.build(force_all=True) - searchindex = (app.outdir / 'searchindex.js').read_text(encoding='utf8') + searchindex = (app.outdir / 'searchindex.json').read_text(encoding='utf8') # if search term is in the title of one doc and in the text of another # both documents should be a hit in the search index as a title, # respectively text hit - assert '["textinhead",2]' in searchindex - assert '["textinhead",0]' in searchindex + assert '"textinhead":2' in searchindex + assert '"textinhead":0' in searchindex @pytest.mark.sphinx('html', testroot='search') def test_term_in_raw_directive(app): app.build(force_all=True) - searchindex = load_searchindex(app.outdir / 'searchindex.js') + searchindex = load_searchindex(app.outdir / 'searchindex.json') assert not is_registered_term(searchindex, 'raw') assert is_registered_term(searchindex, 'rawword') assert not is_registered_term(searchindex, 'latex_keyword') @@ -390,7 +378,7 @@ def test_IndexBuilder_lookup(): ) def test_search_index_gen_zh(app): app.build(force_all=True) - index = load_searchindex(app.outdir / 'searchindex.js') + index = load_searchindex(app.outdir / 'searchindex.json') assert 'chinesetest ' not in index['terms'] assert 'chinesetest' in index['terms'] assert 'chinesetesttwo' in index['terms'] @@ -404,7 +392,7 @@ def test_search_index_gen_zh(app): ) def test_nosearch(app): app.build() - index = load_searchindex(app.outdir / 'searchindex.js') + index = load_searchindex(app.outdir / 'searchindex.json') assert index['docnames'] == ['index', 'nosearch', 'tocitem'] assert 'latex' not in index['terms'] assert 'bat' in index['terms'] @@ -421,14 +409,14 @@ def test_nosearch(app): ) def test_parallel(app): app.build() - index = load_searchindex(app.outdir / 'searchindex.js') + index = load_searchindex(app.outdir / 'searchindex.json') assert index['docnames'] == ['index', 'nosearch', 'tocitem'] @pytest.mark.sphinx('html', testroot='search') def test_search_index_is_deterministic(app): app.build(force_all=True) - index = load_searchindex(app.outdir / 'searchindex.js') + index = load_searchindex(app.outdir / 'searchindex.json') # Pretty print the index. Only shown by pytest on failure. print(f'searchindex.js contents:\n\n{json.dumps(index, indent=2)}') assert_is_sorted(index, '') @@ -480,9 +468,9 @@ def test_check_js_search_indexes(make_app, sphinx_test_tempdir, directory): ) app.build() - fresh_searchindex = app.outdir / 'searchindex.js' + fresh_searchindex = app.outdir / 'searchindex.json' existing_searchindex = ( - TESTS_ROOT / 'js' / 'fixtures' / directory.name / 'searchindex.js' + TESTS_ROOT / 'js' / 'fixtures' / directory.name / 'searchindex.json' ) msg = ( diff --git a/utils/generate_js_fixtures.py b/utils/generate_js_fixtures.py index 4e126899026..79e378b3304 100755 --- a/utils/generate_js_fixtures.py +++ b/utils/generate_js_fixtures.py @@ -26,8 +26,8 @@ def build(srcdir: Path) -> None: for directory in TEST_JS_ROOTS: - searchindex = directory / '_build' / 'searchindex.js' - destination = TEST_JS_FIXTURES / directory.name / 'searchindex.js' + searchindex = directory / '_build' / 'searchindex.json' + destination = TEST_JS_FIXTURES / directory.name / 'searchindex.json' print(f'Building {directory} ... ', end='') build(directory) From 742893282dad27908cd5705f778b4450193bc3da Mon Sep 17 00:00:00 2001 From: James Addison Date: Sun, 24 Nov 2024 13:07:26 +0000 Subject: [PATCH 27/28] Tests: cleanup: remove unused `ast` import --- tests/test_search.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index c1e27be2afd..7b81c891c48 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2,7 +2,6 @@ from __future__ import annotations -import ast import json import warnings from io import BytesIO From 22acc49c1c11c536ab19986a9215e857f75b659b Mon Sep 17 00:00:00 2001 From: James Addison Date: Sun, 24 Nov 2024 13:31:31 +0000 Subject: [PATCH 28/28] HTML search: refactor loader to use Fetch API Ref: https://fetch.spec.whatwg.org/#fetch-api --- sphinx/themes/basic/static/searchindex.js | 5 +---- sphinx/themes/basic/static/searchtools.js | 2 +- tests/js/searchtools.spec.js | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sphinx/themes/basic/static/searchindex.js b/sphinx/themes/basic/static/searchindex.js index f2cc75f4729..c7cd483aaf7 100644 --- a/sphinx/themes/basic/static/searchindex.js +++ b/sphinx/themes/basic/static/searchindex.js @@ -1,4 +1 @@ -req = new XMLHttpRequest(); -req.open("GET", "searchindex.json", false); -req.send(null); -Search.setIndex(req.responseText); +window.fetch("searchindex.json").then(response => response.json()).then(Search.setIndex); diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 7ee323fe905..f13f5027934 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -219,7 +219,7 @@ const Search = { (document.body.appendChild(document.createElement("script")).src = url), setIndex: (index) => { - Search._index = JSON.parse(index); + Search._index = index; if (Search._queued_query !== null) { const query = Search._queued_query; Search._queued_query = null; diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 5a842604026..1a3b73cbe8d 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -4,7 +4,7 @@ describe('Basic html theme search', function() { req = new XMLHttpRequest(); req.open("GET", `__src__/tests/js/fixtures/${name}`, false); req.send(null); - Search.setIndex(req.responseText); + Search.setIndex(JSON.parse(req.responseText)); } function checkRanking(expectedRanking, results) {