chiark / gitweb /
documentation: search data format version 2.
authorVladimír Vondruš <mosra@centrum.cz>
Wed, 17 Jul 2019 16:44:32 +0000 (18:44 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Thu, 18 Jul 2019 11:34:34 +0000 (13:34 +0200)
Instead of having a hardcoded mapping from flags to type names and
associated classes on the JavaScript side, which was very C++-specific,
the new format now moves this mapping directly into the search data.

It's a new section at the end, for each type index containing CSS class
ID (which is still hardcoded, but that's not language specific anyway)
and an UTF-8 name that's displayed in the label. For C++ this makes the
search data file size 107 bytes larger, which isn't significant by any
means.

19 files changed:
documentation/_search.py
documentation/doxygen.py
documentation/search.js
documentation/test_doxygen/js-test-data/empty.bin
documentation/test_doxygen/js-test-data/nested.bin
documentation/test_doxygen/js-test-data/searchdata.b85
documentation/test_doxygen/js-test-data/searchdata.bin
documentation/test_doxygen/js-test-data/unicode.bin
documentation/test_doxygen/js-test-data/wrong-magic.bin
documentation/test_doxygen/js-test-data/wrong-version.bin
documentation/test_doxygen/layout/pages.html
documentation/test_doxygen/layout_generated_doxyfile/index.html
documentation/test_doxygen/layout_minimal/index.html
documentation/test_doxygen/layout_search_opensearch/index.html
documentation/test_doxygen/populate-js-test-data.py
documentation/test_doxygen/test-search.js
documentation/test_doxygen/test_search.py
documentation/test_doxygen/test_undocumented.py
documentation/test_python/layout/index.html

index 1e58b4a098f56a0bba9959fa7f3e837bf580beca..25c0be1ba7b0011c0d28fdbb885830a0cc2d4b8f 100644 (file)
 # doxygen.py. But `from _search import bla` works. Ugh.
 
 import base64
+import enum
 import struct
-from enum import Flag
 from types import SimpleNamespace as Empty
+from typing import List, Tuple
 
-searchdata_format_version = 0
+# Version 0 was without the type map
+searchdata_format_version = 1
 searchdata_filename = f'searchdata-v{searchdata_format_version}.bin'
 searchdata_filename_b85 = f'searchdata-v{searchdata_format_version}.js'
 
-class ResultFlag(Flag):
+class CssClass(enum.Enum):
+    DEFAULT = 0
+    PRIMARY = 1
+    SUCCESS = 2
+    WARNING = 3
+    DANGER = 4
+    INFO = 5
+    DIM = 6
+
+class ResultFlag(enum.Flag):
     @staticmethod
     def from_type(flag: 'ResultFlag', type) -> 'ResultFlag':
         assert not flag & ResultFlag._TYPE
@@ -360,13 +371,49 @@ class Trie:
         self.root_offset_struct.pack_into(output, 0, self._serialize(hashtable, output, merge_subtrees=merge_subtrees))
         return output
 
-search_data_header_struct = struct.Struct('<3sBHI')
+#     type 1     |     type 2     |     |         |        | type 1 |
+# class |  name  | class |  name  | ... | padding |  end   |  name  | ...
+#   ID  | offset |   ID  | offset |     |         | offset |  data  |
+#   8b  |   8b   |   8b  |   8b   |     |    8b   |   8b   |        |
+type_map_entry_struct = struct.Struct('<BB')
+
+def serialize_type_map(map: List[Tuple[CssClass, str]]) -> bytearray:
+    serialized = bytearray()
+    names = bytearray()
+
+    # There's just 16 bits for the type and we're using one for aliases, so
+    # that makes at most 15 values left. See ResultFlag for details.
+    assert len(map) <= 15
+
+    # Initial name offset is after all the offset entries plus the final one
+    initial_name_offset = (len(map) + 1)*type_map_entry_struct.size
+
+    # Add all entries (and the final offset), encode the names separately,
+    # concatenate at the end
+    for css_class, name in map:
+        serialized += type_map_entry_struct.pack(css_class.value, initial_name_offset + len(names))
+        names += name.encode('utf-8')
+    serialized += type_map_entry_struct.pack(0, initial_name_offset + len(names))
+    assert len(serialized) == initial_name_offset
+
+    return serialized + names
 
-def serialize_search_data(trie: Trie, map: ResultMap, symbol_count, merge_subtrees=True, merge_prefixes=True) -> bytearray:
+# magic  | version | symbol | result |  type  |
+# header |         | count  |  map   |  map   |
+#        |         |        | offset | offset |
+#  24b   |   8b    |  16b   |  32b   |  32b   |
+search_data_header_struct = struct.Struct('<3sBHII')
+
+def serialize_search_data(trie: Trie, map: ResultMap, type_map: List[Tuple[CssClass, str]], symbol_count, *, merge_subtrees=True, merge_prefixes=True) -> bytearray:
     serialized_trie = trie.serialize(merge_subtrees=merge_subtrees)
     serialized_map = map.serialize(merge_prefixes=merge_prefixes)
-    # magic header, version, symbol count, offset of result map
-    return search_data_header_struct.pack(b'MCS', searchdata_format_version, symbol_count, len(serialized_trie) + 10) + serialized_trie + serialized_map
+    serialized_type_map = serialize_type_map(type_map)
+
+    preamble = search_data_header_struct.pack(b'MCS',
+        searchdata_format_version, symbol_count,
+        search_data_header_struct.size + len(serialized_trie),
+        search_data_header_struct.size + len(serialized_trie) + len(serialized_map))
+    return preamble + serialized_trie + serialized_map + serialized_type_map
 
 def base85encode_search_data(data: bytearray) -> bytearray:
     return (b"/* Generated by https://mcss.mosra.cz/documentation/doxygen/. Do not edit. */\n" +
@@ -491,11 +538,25 @@ def pretty_print_map(serialized: bytes, *, entryTypeClass, colors=False):
         offset = next_offset
     return out
 
+def pretty_print_type_map(serialized: bytes, *, entryTypeClass):
+    # Unpack until we aren't at EOF
+    i = 0
+    out = ''
+    class_id, offset = type_map_entry_struct.unpack_from(serialized, 0)
+    while offset < len(serialized):
+        if i: out += ',\n'
+        next_class_id, next_offset = type_map_entry_struct.unpack_from(serialized, (i + 1)*type_map_entry_struct.size)
+        out += "({}, {}, '{}')".format(entryTypeClass(i + 1), CssClass(class_id), serialized[offset:next_offset].decode('utf-8'))
+        i += 1
+        class_id, offset = next_class_id, next_offset
+    return out
+
 def pretty_print(serialized: bytes, *, entryTypeClass, show_merged=False, show_lookahead_barriers=True, colors=False):
-    magic, version, symbol_count, map_offset = search_data_header_struct.unpack_from(serialized)
+    magic, version, symbol_count, map_offset, type_map_offset = search_data_header_struct.unpack_from(serialized)
     assert magic == b'MCS'
     assert version == searchdata_format_version
 
     pretty_trie, stats = pretty_print_trie(serialized[search_data_header_struct.size:map_offset], show_merged=show_merged, show_lookahead_barriers=show_lookahead_barriers, colors=colors)
-    pretty_map = pretty_print_map(serialized[map_offset:], entryTypeClass=entryTypeClass, colors=colors)
-    return '{} symbols\n'.format(symbol_count) + pretty_trie + '\n' + pretty_map, stats
+    pretty_map = pretty_print_map(serialized[map_offset:type_map_offset], entryTypeClass=entryTypeClass, colors=colors)
+    pretty_type_map = pretty_print_type_map(serialized[type_map_offset:], entryTypeClass=entryTypeClass)
+    return '{} symbols\n'.format(symbol_count) + pretty_trie + '\n' + pretty_map + '\n' + pretty_type_map, stats
index e2c73dc3c14dc0f25a10f19bffdc07ad38e4fd9d..4e4ac2a0d6d7f9059a496f63ef02073b20cec65e 100755 (executable)
@@ -47,7 +47,7 @@ from pygments import highlight
 from pygments.formatters import HtmlFormatter
 from pygments.lexers import TextLexer, BashSessionLexer, get_lexer_by_name, find_lexer_class_for_filename
 
-from _search import ResultFlag, ResultMap, Trie, serialize_search_data, base85encode_search_data, searchdata_filename, searchdata_filename_b85, searchdata_format_version
+from _search import CssClass, ResultFlag, ResultMap, Trie, serialize_search_data, base85encode_search_data, searchdata_filename, searchdata_filename_b85, searchdata_format_version
 
 sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../plugins'))
 import dot2svg
@@ -56,6 +56,8 @@ import latex2svgextra
 import ansilexer
 
 class EntryType(enum.Enum):
+    # Order must match the search_type_map below; first value is reserved for
+    # ResultFlag.ALIAS
     PAGE = 1
     NAMESPACE = 2
     GROUP = 3
@@ -71,6 +73,24 @@ class EntryType(enum.Enum):
     ENUM_VALUE = 13
     VAR = 14
 
+# Order must match the EntryType above
+search_type_map = [
+    (CssClass.SUCCESS, "page"),
+    (CssClass.PRIMARY, "namespace"),
+    (CssClass.SUCCESS, "group"),
+    (CssClass.PRIMARY, "class"),
+    (CssClass.PRIMARY, "struct"),
+    (CssClass.PRIMARY, "union"),
+    (CssClass.PRIMARY, "typedef"),
+    (CssClass.WARNING, "dir"),
+    (CssClass.WARNING, "file"),
+    (CssClass.INFO, "func"),
+    (CssClass.INFO, "define"),
+    (CssClass.PRIMARY, "enum"),
+    (CssClass.DEFAULT, "enum val"),
+    (CssClass.DEFAULT, "var")
+]
+
 xref_id_rx = re.compile(r"""(.*)_1(_[a-z-]+[0-9]+|@)$""")
 slugify_nonalnum_rx = re.compile(r"""[^\w\s-]""")
 slugify_hyphens_rx = re.compile(r"""[-\s]+""")
@@ -2354,7 +2374,7 @@ def build_search_data(state: State, merge_subtrees=True, add_lookahead_barriers=
     # order by default
     trie.sort(map)
 
-    return serialize_search_data(trie, map, symbol_count, merge_subtrees=merge_subtrees, merge_prefixes=merge_prefixes)
+    return serialize_search_data(trie, map, search_type_map, symbol_count, merge_subtrees=merge_subtrees, merge_prefixes=merge_prefixes)
 
 def parse_xml(state: State, xml: str):
     # Reset counter for unique math formulas
index 43b336c33123a1fc69ca5ff26e1f6121eb0e68fc..cbd6ed0313991a8699712782550a4b13c7a02487 100644 (file)
 "use strict"; /* it summons the Cthulhu in a proper way, they say */
 
 var Search = {
-    formatVersion: 0, /* the data filename contains this number too */
+    formatVersion: 1, /* the data filename contains this number too */
 
     trie: null,
     map: null,
+    typeMap: null,
     dataSize: 0,
     symbolCount: 0,
     maxResults: 0,
@@ -54,8 +55,9 @@ var Search = {
     init: function(buffer, maxResults) {
         let view = new DataView(buffer);
 
-        /* The file is too short to contain at least the headers */
-        if(view.byteLength < 20) {
+        /* The file is too short to contain at least the headers and empty
+           sections */
+        if(view.byteLength < 26) {
             console.error("Search data too short");
             return false;
         }
@@ -74,8 +76,10 @@ var Search = {
 
         /* Separate the data into the trie and the result map */
         let mapOffset = view.getUint32(6, true);
-        this.trie = new DataView(buffer, 10, mapOffset - 10);
-        this.map = new DataView(buffer, mapOffset);
+        let typeMapOffset = view.getUint32(10, true);
+        this.trie = new DataView(buffer, 14, mapOffset - 14);
+        this.map = new DataView(buffer, mapOffset, typeMapOffset - mapOffset);
+        this.typeMap = new DataView(buffer, typeMapOffset);
 
         /* Set initial properties */
         this.dataSize = buffer.byteLength;
@@ -396,6 +400,8 @@ var Search = {
                     alias: alias.name,
                     url: alias.url,
                     flags: alias.flags,
+                    cssClass: alias.cssClass,
+                    typeName: alias.typeName,
                     suffixLength: suffixLength + resultSuffixLength};
         }
 
@@ -405,10 +411,40 @@ var Search = {
             url += String.fromCharCode(this.map.getUint8(j));
         }
 
-        /* Keeping in UTF-8, as we need that for proper slicing (and concatenating) */
+        /* This is an alias, return what we have, without parsed CSS class and
+           type name as those are retrieved from the final target type */
+        if(!(flags >> 4))
+            return {name: name,
+                    url: url,
+                    flags: flags & 0x0f,
+                    suffixLength: suffixLength + resultSuffixLength};
+
+        /* Otherwise, get CSS class and type name for the result label */
+        let typeMapIndex = (flags >> 4) - 1;
+        let cssClass = [
+            /* Keep in sync with _search.py */
+            'm-default',
+            'm-primary',
+            'm-success',
+            'm-warning',
+            'm-danger',
+            'm-info',
+            'm-dim'
+        ][this.typeMap.getUint8(typeMapIndex*2)];
+        let typeNameOffset = this.typeMap.getUint8(typeMapIndex*2 + 1);
+        let nextTypeNameOffset = this.typeMap.getUint8((typeMapIndex + 1)*2 + 1);
+        let typeName = '';
+        for(let j = typeNameOffset; j != nextTypeNameOffset; ++j)
+            typeName += String.fromCharCode(this.typeMap.getUint8(j));
+
+        /* Keeping in UTF-8, as we need that for proper slicing (and
+           concatenating). Strip the type from the flags, as it's now expressed
+           directly. */
         return {name: name,
                 url: url,
-                flags: flags,
+                flags: flags & 0x0f,
+                cssClass: cssClass,
+                typeName: typeName,
                 suffixLength: suffixLength + resultSuffixLength};
     },
 
@@ -451,70 +487,8 @@ var Search = {
 
             let list = '';
             for(let i = 0; i != results.length; ++i) {
-                let type = '';
-                let color = '';
-                switch(results[i].flags >> 4) {
-                    /* Keep in sync with doxygen.py */
-                    case 1:
-                        type = 'page';
-                        color = 'm-success';
-                        break;
-                    case 2:
-                        type = 'namespace';
-                        color = 'm-primary';
-                        break;
-                    case 3:
-                        type = 'group';
-                        color = 'm-success';
-                        break;
-                    case 4:
-                        type = 'class';
-                        color = 'm-primary';
-                        break;
-                    case 5:
-                        type = 'struct';
-                        color = 'm-primary';
-                        break;
-                    case 6:
-                        type = 'union';
-                        color = 'm-primary';
-                        break;
-                    case 7:
-                        type = 'typedef';
-                        color = 'm-primary';
-                        break;
-                    case 8:
-                        type = 'dir';
-                        color = 'm-warning';
-                        break;
-                    case 9:
-                        type = 'file';
-                        color = 'm-warning';
-                        break;
-                    case 10:
-                        type = 'func';
-                        color = 'm-info';
-                        break;
-                    case 11:
-                        type = 'define';
-                        color = 'm-info';
-                        break;
-                    case 12:
-                        type = 'enum';
-                        color = 'm-primary';
-                        break;
-                    case 13:
-                        type = 'enum val';
-                        color = 'm-default';
-                        break;
-                    case 14:
-                        type = 'var';
-                        color = 'm-default';
-                        break;
-                }
-
                 /* Labels + */
-                list += '<li' + (i ? '' : ' id="search-current"') + '><a href="' + results[i].url + '" onmouseover="selectResult(event)" data-md-link-title="' + this.escape(results[i].name.substr(results[i].name.length - value.length - results[i].suffixLength)) + '"><div class="m-label m-flat ' + color + '">' + type + '</div>' + (results[i].flags & 2 ? '<div class="m-label m-danger">deprecated</div>' : '') + (results[i].flags & 4 ? '<div class="m-label m-danger">deleted</div>' : '');
+                list += '<li' + (i ? '' : ' id="search-current"') + '><a href="' + results[i].url + '" onmouseover="selectResult(event)" data-md-link-title="' + this.escape(results[i].name.substr(results[i].name.length - value.length - results[i].suffixLength)) + '"><div class="m-label m-flat ' + results[i].cssClass + '">' + results[i].typeName + '</div>' + (results[i].flags & 2 ? '<div class="m-label m-danger">deprecated</div>' : '') + (results[i].flags & 4 ? '<div class="m-label m-danger">deleted</div>' : '');
 
                 /* Render the alias (cut off from the right) */
                 if(results[i].alias) {
index 67dc8acd8bab56f383e3fdd70215edb1172a03ab..36e30edcedd56ce0b203ee32f441a8b676543277 100644 (file)
Binary files a/documentation/test_doxygen/js-test-data/empty.bin and b/documentation/test_doxygen/js-test-data/empty.bin differ
index 8cc87696caeda8bc060ad80d89af3644f9b71d8a..39d332c92eb788904e29b24c6b0b5093bdbe0c84 100644 (file)
Binary files a/documentation/test_doxygen/js-test-data/nested.bin and b/documentation/test_doxygen/js-test-data/nested.bin differ
index a8ca35847b861c3048b7111463e782215dcb9e53..3239298ae56a7e3098f8d65ad053e2ebb302c0dc 100644 (file)
@@ -1 +1 @@
-O+!-v2LONp003kG000310RR921ONaj009U904M+f4gdgd009&L0BHdL0{{R4AOHX<00ATb04M+fDgXd(00A%n0BHaLHUI!^00BGz06GBy0suk)fI0vHNB{tG00B?{0B-;RRsaBW00CS80Am0FVgLYT0RRO600C|Q04V?gasU7*00DRa0B!&QegFVz00D#m0BryPiU0sQ0RaR6kN|)>00EW&0A&CHo&W%600E=`0B!&QssI3C00SBT0BvXh0Cund0CE5Uwg3P+0RaF2!~lRg00GJX0B8UK(f|N-0{{U40{{g800G_r04V?g<^TXF00Ha(0B!&R*Z=@w@&Ev70RRU8009C40A&CH1_1zU009gE0A~OJ5&-~i0RadA7y$rb00ABW0CWHWCIJ9r00OE20AVZv0A&FH1^@s7JOKb@00BS&0A~OJMgag}00B$^0B`^SQUL&B00CG50CfNa_y7QHXaE3qG64W`UI74eC;$K;KL7x!R{#J?djJ5bkpKWlvj70C$p8Ql<p2Nx@Bjb_{r~^~O<{Cs0B&JzWpi+0V`WWYbZ9PUbZu+^01^l~I&EogC~0nVEFfuabSVHMZE0=*0025VR%K&!Z*l-*Y+-YAO<{CsUol@XR%K&!Z*neZbZu+`02l^3I&EogC@COgZ*FsR03&T_ZU6uPIyzEeZf9ixV{Bn_b4_7%XkRg3F;Zb}XJsyEbZu+|02c;2I&EogC@COgZ*FsR03&T_Zct%oWgx=4AX9Z>aA9X<0CRO>aA9X<E@*UZYy<#OWn*+<Zf9&|1ONvB00KHXQe|UwC@BB{
\ No newline at end of file
+O+!-w2LOZt004pl003kG000310RR921ONaj009U904M+f4gdgd009&L0BHdL0{{R4AOHX<00ATb04M+fDgXd(00A%n0BHaLHUI!^00BGz06GBy0suk)fI0vHNB{tG00B?{0B-;RRsaBW00CS80Am0FVgLYT0RRO600C|Q04V?gasU7*00DRa0B!&QegFVz00D#m0BryPiU0sQ0RaR6kN|)>00EW&0A&CHo&W%600E=`0B!&QssI3C00SBT0BvXh0Cund0CE5Uwg3P+0RaF2!~lRg00GJX0B8UK(f|N-0{{U40{{g800G_r04V?g<^TXF00Ha(0B!&R*Z=@w@&Ev70RRU8009C40A&CH1_1zU009gE0A~OJ5&-~i0RadA7y$rb00ABW0CWHWCIJ9r00OE20AVZv0A&FH1^@s7JOKb@00BS&0A~OJMgag}00B$^0B`^SQUL&B00CG50CfNa_y7QHXaE3qG64W`UI74eC;$K;KL7x!R{#J?djJ5bkpKWlvj70C$p8Ql<p2Nx@Bjb_{r~^~O<{Cs0B&JzWpi+0V`WWYbZ9PUbZu+^01^l~I&EogC~0nVEFfuabSVHMZE0=*0025VR%K&!Z*l-*Y+-YAO<{CsUol@XR%K&!Z*neZbZu+`02l^3I&EogC@COgZ*FsR03&T_ZU6uPIyzEeZf9ixV{Bn_b4_7%XkRg3F;Zb}XJsyEbZu+|02c;2I&EogC@COgZ*FsR03&T_Zct%oWgx=4AX9Z>aA9X<0CRO>aA9X<E@*UZYy<#OWn*+<Zf9&|1ONvB00KHXQe|UwC@BIS0U`n`0WbkI0XqRe14jc&1x^K00ayTB0Av7XaA9X<ZeeX@b8ul}WoL44b#P;BVRLhHbaHiLbaifNZ*FvXaAjm=W@Kq{W@&6?W_503WMyV)Ze?X|b!}yCb!{MaVQh9`asU7T
\ No newline at end of file
index 884306d7b429c60c09dc3cd5130bf6e2f5be42e1..319ef59a772e7ed876620be5c2a7106ad281b2c3 100644 (file)
Binary files a/documentation/test_doxygen/js-test-data/searchdata.bin and b/documentation/test_doxygen/js-test-data/searchdata.bin differ
index ce221155b31d62475deb291744e3696daf8d7111..a4facc452d2c7ca159d9013fafa8114279b76b73 100644 (file)
Binary files a/documentation/test_doxygen/js-test-data/unicode.bin and b/documentation/test_doxygen/js-test-data/unicode.bin differ
index 253fe0fba18bf510f484924531b2ebbbac76c688..6a3dd03eb7cd398f2b0b43a2b7234fd00cff1f77 100644 (file)
Binary files a/documentation/test_doxygen/js-test-data/wrong-magic.bin and b/documentation/test_doxygen/js-test-data/wrong-magic.bin differ
index b2c67d7784f05d917ae5ef497425a0d07e434ca4..9b66517fd264a88dd8c3adcc63b62610134239ee 100644 (file)
Binary files a/documentation/test_doxygen/js-test-data/wrong-version.bin and b/documentation/test_doxygen/js-test-data/wrong-version.bin differ
index 57019fed6bbb5754640cf232f21d763595c8d59a..663c778a419bc0b86dbaaf521518632b5927bb90 100644 (file)
   </div>
 </div>
 <script src="search.js"></script>
-<script src="searchdata-v0.js" async="async"></script>
+<script src="searchdata-v1.js" async="async"></script>
 <footer><nav>
   <div class="m-container">
     <div class="m-row">
index d099c574ffb80828587c640feda3e8ae4ef6bedc..dd26202286cd0e58013edbf610787b627eeee079 100644 (file)
@@ -85,7 +85,7 @@
   </div>
 </div>
 <script src="search.js"></script>
-<script src="searchdata-v0.js" async="async"></script>
+<script src="searchdata-v1.js" async="async"></script>
 <footer><nav>
   <div class="m-container">
     <div class="m-row">
index d099c574ffb80828587c640feda3e8ae4ef6bedc..dd26202286cd0e58013edbf610787b627eeee079 100644 (file)
@@ -85,7 +85,7 @@
   </div>
 </div>
 <script src="search.js"></script>
-<script src="searchdata-v0.js" async="async"></script>
+<script src="searchdata-v1.js" async="async"></script>
 <footer><nav>
   <div class="m-container">
     <div class="m-row">
index 2d402f3f046e24072c195574def9061ca294050e..60f336fc2cdd48ee16463f9f4407a22e3edf5ccf 100644 (file)
@@ -71,6 +71,6 @@
   </div>
 </div>
 <script src="search.js"></script>
-<script src="searchdata-v0.js" async="async"></script>
+<script src="searchdata-v1.js" async="async"></script>
 </body>
 </html>
index 3d39bb32faeeb484d38e870371339fabdb5c7973..9ca5f91bfbd4563f156db815bcd3ea6112c59115 100755 (executable)
@@ -30,7 +30,7 @@ import sys
 import pathlib
 sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
 
-from doxygen import EntryType
+from doxygen import EntryType, search_type_map
 from _search import Trie, ResultMap, ResultFlag, serialize_search_data
 
 basedir = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))/'js-test-data'
@@ -38,11 +38,11 @@ basedir = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))/'js-test-dat
 with open(basedir/'short.bin', 'wb') as f:
     f.write(b'')
 with open(basedir/'wrong-magic.bin', 'wb') as f:
-    f.write(b'MOS\0                ')
+    f.write(b'MOS\1                      ')
 with open(basedir/'wrong-version.bin', 'wb') as f:
-    f.write(b'MCS\1                ')
+    f.write(b'MCS\0                      ')
 with open(basedir/'empty.bin', 'wb') as f:
-    f.write(serialize_search_data(Trie(), ResultMap(), 0))
+    f.write(serialize_search_data(Trie(), ResultMap(), [], 0))
 
 trie = Trie()
 map = ResultMap()
@@ -71,9 +71,9 @@ trie.insert("rectangle", map.add("Rectangle", "", alias=range_index))
 trie.insert("rect", map.add("Rectangle::Rect()", "", suffix_length=2, alias=range_index))
 
 with open(basedir/'searchdata.bin', 'wb') as f:
-    f.write(serialize_search_data(trie, map, 7))
+    f.write(serialize_search_data(trie, map, search_type_map, 7))
 with open(basedir/'searchdata.b85', 'wb') as f:
-    f.write(base64.b85encode(serialize_search_data(trie, map, 7), True))
+    f.write(base64.b85encode(serialize_search_data(trie, map, search_type_map, 7), True))
 
 trie = Trie()
 map = ResultMap()
@@ -82,7 +82,7 @@ trie.insert("hýždě", map.add("Hýždě", "#a", flags=ResultFlag.from_type(Res
 trie.insert("hárá", map.add("Hárá", "#b", flags=ResultFlag.from_type(ResultFlag.NONE, EntryType.PAGE)))
 
 with open(basedir/'unicode.bin', 'wb') as f:
-    f.write(serialize_search_data(trie, map, 2))
+    f.write(serialize_search_data(trie, map, search_type_map, 2))
 
 trie = Trie()
 map = ResultMap()
@@ -92,4 +92,4 @@ trie.insert("geometry", map.add("Magnum::Math::Geometry", "namespaceMagnum_1_1Ma
 trie.insert("range", map.add("Magnum::Math::Range", "classMagnum_1_1Math_1_1Range.html", flags=ResultFlag.from_type(ResultFlag.NONE, EntryType.CLASS)))
 
 with open(basedir/'nested.bin', 'wb') as f:
-    f.write(serialize_search_data(trie, map, 4))
+    f.write(serialize_search_data(trie, map, search_type_map, 4))
index eca92af47e4121334ec7826f024a0267252dc617..d95c31f5cc8a89e3ddb65105d3d5a2d6ba6905ce 100644 (file)
@@ -79,7 +79,7 @@ const { StringDecoder } = require('string_decoder');
 /* Verify that base85-decoded file is equivalent to the binary */
 {
     let binary = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.bin"));
-    assert.equal(binary.byteLength, 638);
+    assert.equal(binary.byteLength, 745);
     let b85 = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.b85"), {encoding: 'utf-8'});
     assert.deepEqual(new DataView(binary.buffer.slice(binary.byteOffset, binary.byteOffset + binary.byteLength)), new DataView(Search.base85decode(b85), 0, binary.byteLength));
 }
@@ -114,7 +114,7 @@ const { StringDecoder } = require('string_decoder');
 {
     let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.bin"));
     assert.ok(Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
-    assert.equal(Search.dataSize, 638);
+    assert.equal(Search.dataSize, 745);
     assert.equal(Search.symbolCount, 7);
     assert.equal(Search.maxResults, 100);
 
@@ -122,19 +122,27 @@ const { StringDecoder } = require('string_decoder');
     let resultsForM = [[
         { name: 'Math',
           url: 'namespaceMath.html',
-          flags: 32,
+          flags: 0,
+          cssClass: 'm-primary',
+          typeName: 'namespace',
           suffixLength: 3 },
         { name: 'Math::min(int, int)',
           url: 'namespaceMath.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 12 },
         { name: 'Math::Vector::min() const',
           url: 'classMath_1_1Vector.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 10 },
         { name: 'Math::Range::min() const',
           url: 'classMath_1_1Range.html#min',
-          flags: 173,
+          flags: 13, /* has prefix + suffix, deleted */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 10 }], ''];
     assert.deepEqual(Search.search('m'), resultsForM);
 
@@ -142,15 +150,21 @@ const { StringDecoder } = require('string_decoder');
     assert.deepEqual(Search.search('min'), [[
         { name: 'Math::min(int, int)',
           url: 'namespaceMath.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 10 },
         { name: 'Math::Vector::min() const',
           url: 'classMath_1_1Vector.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 8 },
         { name: 'Math::Range::min() const',
           url: 'classMath_1_1Range.html#min',
-          flags: 173,
+          flags: 13, /* has prefix + suffix, deleted */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 8 }], '()']);
 
     /* Go back, get the same thing */
@@ -160,7 +174,9 @@ const { StringDecoder } = require('string_decoder');
     let resultsForVec = [[
         { name: 'Math::Vector',
           url: 'classMath_1_1Vector.html',
-          flags: 72|2, /* Deprecated */
+          flags: 10, /* has prefix + deprecated */
+          cssClass: 'm-primary',
+          typeName: 'class',
           suffixLength: 3 }], 'tor'];
     assert.deepEqual(Search.search('vec'), resultsForVec);
 
@@ -174,7 +190,9 @@ const { StringDecoder } = require('string_decoder');
     assert.deepEqual(Search.search('su'), [[
         { name: Search.toUtf8('Page » Subpage'),
           url: 'subpage.html',
-          flags: 16,
+          flags: 0,
+          cssClass: 'm-success',
+          typeName: 'page',
           suffixLength: 5 }], 'bpage']);
 
     /* Alias */
@@ -182,16 +200,22 @@ const { StringDecoder } = require('string_decoder');
         { name: 'Rectangle::Rect()',
           alias: 'Math::Range',
           url: 'classMath_1_1Range.html',
-          flags: 72,
+          flags: 8, /* has prefix */
+          cssClass: 'm-primary',
+          typeName: 'class',
           suffixLength: 5 },
         { name: 'Math::Range',
           url: 'classMath_1_1Range.html',
-          flags: 72,
+          flags: 8, /* has prefix */
+          cssClass: 'm-primary',
+          typeName: 'class',
           suffixLength: 4 },
         { name: 'Rectangle',
           alias: 'Math::Range',
           url: 'classMath_1_1Range.html',
-          flags: 72,
+          flags: 8, /* has prefix */
+          cssClass: 'm-primary',
+          typeName: 'class',
           suffixLength: 8 }], '']);
 }
 
@@ -199,21 +223,27 @@ const { StringDecoder } = require('string_decoder');
 {
     let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.bin"));
     assert.ok(Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), 3));
-    assert.equal(Search.dataSize, 638);
+    assert.equal(Search.dataSize, 745);
     assert.equal(Search.symbolCount, 7);
     assert.equal(Search.maxResults, 3);
     assert.deepEqual(Search.search('m'), [[
         { name: 'Math',
           url: 'namespaceMath.html',
-          flags: 32,
+          flags: 0,
+          cssClass: 'm-primary',
+          typeName: 'namespace',
           suffixLength: 3 },
         { name: 'Math::min(int, int)',
           url: 'namespaceMath.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 12 },
         { name: 'Math::Vector::min() const',
           url: 'classMath_1_1Vector.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 10 }], '']);
 }
 
@@ -221,21 +251,27 @@ const { StringDecoder } = require('string_decoder');
 {
     let b85 = fs.readFileSync(path.join(__dirname, "js-test-data/searchdata.b85"), {encoding: 'utf-8'});
     assert.ok(Search.load(b85));
-    assert.equal(Search.dataSize, 640); /* some padding on the end, that's okay */
+    assert.equal(Search.dataSize, 748); /* some padding on the end, that's okay */
     assert.equal(Search.symbolCount, 7);
     assert.equal(Search.maxResults, 100);
     assert.deepEqual(Search.search('min'), [[
         { name: 'Math::min(int, int)',
           url: 'namespaceMath.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 10 },
         { name: 'Math::Vector::min() const',
           url: 'classMath_1_1Vector.html#min',
-          flags: 169,
+          flags: 9, /* has prefix + suffix */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 8 },
         { name: 'Math::Range::min() const',
           url: 'classMath_1_1Range.html#min',
-          flags: 173,
+          flags: 13, /* has prefix + suffix, deleted */
+          cssClass: 'm-info',
+          typeName: 'func',
           suffixLength: 8 }], '()']);
 }
 
@@ -243,29 +279,37 @@ const { StringDecoder } = require('string_decoder');
 {
     let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/unicode.bin"));
     assert.ok(Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
-    assert.equal(Search.dataSize, 124);
+    assert.equal(Search.dataSize, 231);
     assert.equal(Search.symbolCount, 2);
     /* Both "Hýždě" and "Hárá" have common autocompletion to "h\xA1", which is
        not valid UTF-8, so it has to get truncated */
     assert.deepEqual(Search.search('h'), [[
         { name: Search.toUtf8('Hárá'),
           url: '#b',
-          flags: 16,
+          flags: 0,
+          cssClass: 'm-success',
+          typeName: 'page',
           suffixLength: 5 },
         { name: Search.toUtf8('Hýždě'),
           url: '#a',
-          flags: 16,
+          flags: 0,
+          cssClass: 'm-success',
+          typeName: 'page',
           suffixLength: 7 }], '']);
     /* These autocompletions are valid UTF-8, so nothing gets truncated */
     assert.deepEqual(Search.search('hý'), [[
         { name: Search.toUtf8('Hýždě'),
           url: '#a',
-          flags: 16,
+          flags: 0,
+          cssClass: 'm-success',
+          typeName: 'page',
           suffixLength: 5 }], 'ždě']);
     assert.deepEqual(Search.search('há'), [[
         { name: Search.toUtf8('Hárá'),
           url: '#b',
-          flags: 16,
+          flags: 0,
+          cssClass: 'm-success',
+          typeName: 'page',
           suffixLength: 3 }], 'rá']);
 }
 
@@ -273,18 +317,22 @@ const { StringDecoder } = require('string_decoder');
 {
     let buffer = fs.readFileSync(path.join(__dirname, "js-test-data/nested.bin"));
     assert.ok(Search.init(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)));
-    assert.equal(Search.dataSize, 295);
+    assert.equal(Search.dataSize, 402);
     assert.equal(Search.symbolCount, 4);
     assert.deepEqual(Search.search('geo'), [[
         { name: 'Magnum::Math::Geometry',
           url: 'namespaceMagnum_1_1Math_1_1Geometry.html',
-          flags: 40,
+          flags: 8, /* has prefix */
+          cssClass: 'm-primary',
+          typeName: 'namespace',
           suffixLength: 5 }], 'metry']);
 
     assert.deepEqual(Search.search('ra'), [[
         { name: 'Magnum::Math::Range',
           url: 'classMagnum_1_1Math_1_1Range.html',
-          flags: 72,
+          flags: 8, /* has prefix */
+          cssClass: 'm-primary',
+          typeName: 'class',
           suffixLength: 3 }], 'nge']);
 }
 
index 69ba9b3df8be7874bd489d2360502e9c2d97530a..707c748e1212ff2f49b62257fb9da37325ed72de 100755 (executable)
@@ -30,7 +30,7 @@ import sys
 import unittest
 from types import SimpleNamespace as Empty
 
-from doxygen import EntryType
+from doxygen import EntryType, search_type_map
 from _search import Trie, ResultMap, ResultFlag, serialize_search_data, pretty_print_trie, pretty_print_map, pretty_print, searchdata_filename
 
 from test_doxygen import IntegrationTestCase
@@ -222,7 +222,7 @@ class Serialization(unittest.TestCase):
         trie.insert("math::range", index)
         trie.insert("range", index)
 
-        serialized = serialize_search_data(trie, map, 3)
+        serialized = serialize_search_data(trie, map, search_type_map, 3)
         self.compare(serialized, """
 3 symbols
 math [0]
@@ -233,8 +233,22 @@ range [2]
 0: Math [type=NAMESPACE] -> namespaceMath.html
 1: ::Vector [prefix=0[:0], type=CLASS] -> classMath_1_1Vector.html
 2: ::Range [prefix=0[:0], type=CLASS] -> classMath_1_1Range.html
+(EntryType.PAGE, CssClass.SUCCESS, 'page'),
+(EntryType.NAMESPACE, CssClass.PRIMARY, 'namespace'),
+(EntryType.GROUP, CssClass.SUCCESS, 'group'),
+(EntryType.CLASS, CssClass.PRIMARY, 'class'),
+(EntryType.STRUCT, CssClass.PRIMARY, 'struct'),
+(EntryType.UNION, CssClass.PRIMARY, 'union'),
+(EntryType.TYPEDEF, CssClass.PRIMARY, 'typedef'),
+(EntryType.DIR, CssClass.WARNING, 'dir'),
+(EntryType.FILE, CssClass.WARNING, 'file'),
+(EntryType.FUNC, CssClass.INFO, 'func'),
+(EntryType.DEFINE, CssClass.INFO, 'define'),
+(EntryType.ENUM, CssClass.PRIMARY, 'enum'),
+(EntryType.ENUM_VALUE, CssClass.DEFAULT, 'enum val'),
+(EntryType.VAR, CssClass.DEFAULT, 'var')
 """)
-        self.assertEqual(len(serialized), 241)
+        self.assertEqual(len(serialized), 348)
 
 class Search(IntegrationTestCase):
     def __init__(self, *args, **kwargs):
@@ -247,7 +261,7 @@ class Search(IntegrationTestCase):
             serialized = f.read()
             search_data_pretty = pretty_print(serialized, entryTypeClass=EntryType)[0]
         #print(search_data_pretty)
-        self.assertEqual(len(serialized), 4695)
+        self.assertEqual(len(serialized), 4802)
         self.assertEqual(search_data_pretty, """
 53 symbols
 deprecated_macro [0]
@@ -411,6 +425,20 @@ union [59]
 58: ::DeprecatedUnion [prefix=39[:0], deprecated, type=UNION] -> unionDeprecatedNamespace_1_1DeprecatedUnion.html
 59: ::Union [prefix=50[:0], type=UNION] -> unionNamespace_1_1Union.html
 60: glUnion() [alias=59] ->
+(EntryType.PAGE, CssClass.SUCCESS, 'page'),
+(EntryType.NAMESPACE, CssClass.PRIMARY, 'namespace'),
+(EntryType.GROUP, CssClass.SUCCESS, 'group'),
+(EntryType.CLASS, CssClass.PRIMARY, 'class'),
+(EntryType.STRUCT, CssClass.PRIMARY, 'struct'),
+(EntryType.UNION, CssClass.PRIMARY, 'union'),
+(EntryType.TYPEDEF, CssClass.PRIMARY, 'typedef'),
+(EntryType.DIR, CssClass.WARNING, 'dir'),
+(EntryType.FILE, CssClass.WARNING, 'file'),
+(EntryType.FUNC, CssClass.INFO, 'func'),
+(EntryType.DEFINE, CssClass.INFO, 'define'),
+(EntryType.ENUM, CssClass.PRIMARY, 'enum'),
+(EntryType.ENUM_VALUE, CssClass.DEFAULT, 'enum val'),
+(EntryType.VAR, CssClass.DEFAULT, 'var')
 """.strip())
 
 class SearchLongSuffixLength(IntegrationTestCase):
@@ -424,7 +452,7 @@ class SearchLongSuffixLength(IntegrationTestCase):
             serialized = f.read()
             search_data_pretty = pretty_print(serialized, entryTypeClass=EntryType)[0]
         #print(search_data_pretty)
-        self.assertEqual(len(serialized), 382)
+        self.assertEqual(len(serialized), 489)
         # The parameters get cut off with an ellipsis
         self.assertEqual(search_data_pretty, """
 2 symbols
@@ -439,6 +467,20 @@ averylongfunctionname [0]
 0: ::aVeryLongFunctionName(const std::reference_wrapper<const std::vector<std::string>>&, c…) [prefix=2[:12], suffix_length=69, type=FUNC] -> #a1e9a11887275938ef5541070955c9d9c
 1:  [prefix=0[:46], suffix_length=67, type=FUNC] ->
 2: File.h [type=FILE] -> File_8h.html
+(EntryType.PAGE, CssClass.SUCCESS, 'page'),
+(EntryType.NAMESPACE, CssClass.PRIMARY, 'namespace'),
+(EntryType.GROUP, CssClass.SUCCESS, 'group'),
+(EntryType.CLASS, CssClass.PRIMARY, 'class'),
+(EntryType.STRUCT, CssClass.PRIMARY, 'struct'),
+(EntryType.UNION, CssClass.PRIMARY, 'union'),
+(EntryType.TYPEDEF, CssClass.PRIMARY, 'typedef'),
+(EntryType.DIR, CssClass.WARNING, 'dir'),
+(EntryType.FILE, CssClass.WARNING, 'file'),
+(EntryType.FUNC, CssClass.INFO, 'func'),
+(EntryType.DEFINE, CssClass.INFO, 'define'),
+(EntryType.ENUM, CssClass.PRIMARY, 'enum'),
+(EntryType.ENUM_VALUE, CssClass.DEFAULT, 'enum val'),
+(EntryType.VAR, CssClass.DEFAULT, 'var')
 """.strip())
 
 if __name__ == '__main__': # pragma: no cover
index 9bc0052adcd594550a1b6208171fdc7b682bd1ef..496db2266e2126204fcf09901a795016baf19d12 100644 (file)
@@ -56,5 +56,5 @@ class Test(IntegrationTestCase):
         # TODO: reuse the search data deserialization API once done
         with open(os.path.join(self.path, 'html', searchdata_filename), 'rb') as f:
             serialized = f.read()
-            magic, version, symbol_count, map_offset = search_data_header_struct.unpack_from(serialized)
+            magic, version, symbol_count, map_offset, type_map_offset = search_data_header_struct.unpack_from(serialized)
             self.assertEqual(symbol_count, 44)
index 8fe9e0197630ed0a70d444488448efd2af70cdd8..543ae9a7ea7f3a9c34543b17ca0dbcf8641aa90f 100644 (file)
@@ -91,7 +91,7 @@
   </div>
 </div>
 <script src="search.js"></script>
-<script src="searchdata-v0.js" async="async"></script>
+<script src="searchdata-v1.js" async="async"></script>
 <footer><nav>
   <div class="m-container">
     <div class="m-row">