From 47f6547589294f5ef46b977d759ccb6ddb19dd58 Mon Sep 17 00:00:00 2001 From: James Addison Date: Thu, 24 Oct 2024 18:55:21 +0100 Subject: [PATCH 01/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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