# 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
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" +
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
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
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
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]+""")
# 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
"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,
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;
}
/* 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;
alias: alias.name,
url: alias.url,
flags: alias.flags,
+ cssClass: alias.cssClass,
+ typeName: alias.typeName,
suffixLength: suffixLength + resultSuffixLength};
}
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};
},
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) {
-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
</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">
</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">
</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">
</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>
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'
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()
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()
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()
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))
/* 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));
}
{
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);
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);
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 */
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);
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 */
{ 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 }], '']);
}
{
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 }], '']);
}
{
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 }], '()']);
}
{
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á']);
}
{
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']);
}
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
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]
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):
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]
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):
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
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
# 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)
</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">