chiark / gitweb /
doxygen: support for aliases in search.
authorVladimír Vondruš <mosra@centrum.cz>
Sun, 4 Feb 2018 22:54:59 +0000 (23:54 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Sun, 4 Feb 2018 23:13:18 +0000 (00:13 +0100)
Not yet wired to the complete doxygen extraction, though.

doxygen/dox2html5.py
doxygen/search.js
doxygen/test/js-test-data/searchdata.b85
doxygen/test/js-test-data/searchdata.bin
doxygen/test/populate-js-test-data.py
doxygen/test/test-search.js
doxygen/test/test_search.py

index ab9a7b32e1911f89ed20fc37d548cce563b3ff3f..5165d5ff1c3b1c9f2495dbdbdb434646d2b1b3c1 100755 (executable)
@@ -132,6 +132,7 @@ class ResultFlag(Flag):
     DELETED = 1 << 2
 
     _TYPE = 0xf << 4
+    ALIAS = 0 << 4
     NAMESPACE = 1 << 4
     CLASS = 2 << 4
     STRUCT = 3 << 4
@@ -176,21 +177,31 @@ class ResultMap:
     # id + len | length | suffix |    |
     # 16b + 8b |   8b   |        | 8b |
     #
+    # alias item (flags & 0xf0 == 0x00):
+    #
+    # alias |     | alias
+    #  id   | ... | name
+    #  16b  |     |
+    #
     offset_struct = struct.Struct('<I')
     flags_struct = struct.Struct('<B')
     prefix_struct = struct.Struct('<HB')
     suffix_length_struct = struct.Struct('<B')
+    alias_struct = struct.Struct('<H')
 
     def __init__(self):
         self.entries = []
 
-    def add(self, name, url, suffix_length=0, flags=ResultFlag(0)) -> int:
+    def add(self, name, url, alias=None, suffix_length=0, flags=ResultFlag(0)) -> int:
         if suffix_length: flags |= ResultFlag.HAS_SUFFIX
+        if alias is not None:
+            assert flags & ResultFlag._TYPE == ResultFlag.ALIAS
 
         entry = Empty()
         entry.name = name
         entry.url = url
         entry.flags = flags
+        entry.alias = alias
         entry.prefix = 0
         entry.prefix_length = 0
         entry.suffix_length = suffix_length
@@ -260,6 +271,7 @@ class ResultMap:
                     entry.name = e.name[len(self.entries[longest_prefix.results[0]].name):]
                     entry.url = e.url[max_prefix[1]:]
                     entry.flags = e.flags|ResultFlag.HAS_PREFIX
+                    entry.alias = e.alias
                     entry.prefix = max_prefix[0]
                     entry.prefix_length = max_prefix[1]
                     entry.suffix_length = e.suffix_length
@@ -279,6 +291,10 @@ class ResultMap:
             output += self.offset_struct.pack(offset)
             self.flags_struct.pack_into(output, len(output) - 1, e.flags.value)
 
+            # The entry is an alias, extra field for alias index
+            if e.flags & ResultFlag._TYPE == ResultFlag.ALIAS:
+                offset += 2
+
             # Extra field for prefix index and length
             if e.flags & ResultFlag.HAS_PREFIX:
                 offset += 3
@@ -300,6 +316,10 @@ class ResultMap:
 
         # Write the entries themselves
         for e in self.entries:
+            if e.flags & ResultFlag._TYPE == ResultFlag.ALIAS:
+                assert not e.alias is None
+                assert not e.url
+                output += self.alias_struct.pack(e.alias)
             if e.flags & ResultFlag.HAS_PREFIX:
                 output += self.prefix_struct.pack(e.prefix, e.prefix_length)
             if e.flags & ResultFlag.HAS_SUFFIX:
index 8186ade337d732ec940b317df322f6a4ee2f047a..35060dd6fcda9f301f8fc0b7b218eba653f4b7b5 100644 (file)
@@ -264,6 +264,13 @@ var Search = {
         let flags = this.map.getUint8(index*4 + 3);
         let resultOffset = this.map.getUint32(index*4, true) & 0x00ffffff;
 
+        /* The result is an alias, parse the aliased prefix */
+        let aliasedIndex = null;
+        if((flags & 0xf0) == 0x00) {
+            aliasedIndex = this.map.getUint16(resultOffset, true);
+            resultOffset += 2;
+        }
+
         /* The result has a prefix, parse that first, recursively */
         let name = '';
         let url = '';
@@ -301,10 +308,25 @@ var Search = {
             name += String.fromCharCode(c); /* eheh. IS THIS FAST?! */
         }
 
-        /* Extract URL */
-        let max = Math.min(j + maxUrlPrefix, nextResultOffset);
-        for(; j != max; ++j) {
-            url += String.fromCharCode(this.map.getUint8(j));
+        /* The result is an alias and we're not deep inside resolving a prefix,
+           extract the aliased name and URL */
+        /* TODO: this abuses 0xffffff to guess how the call stack is deep and
+           that's just wrong, fix! */
+        if(aliasedIndex != null && maxUrlPrefix == 0xffffff) {
+            let alias = this.gatherResult(aliasedIndex, 0 /* ignored */, 0xffffff); /* should be enough haha */
+            name += ': ' + alias.name;
+            url = alias.url;
+            flags = alias.flags;
+
+            /* Result suffix length: add the whole aliased name + the `: ` */
+            resultSuffixLength += 2 + alias.name.length;
+
+        /* Otherwise extract URL from here */
+        } else {
+            let max = Math.min(j + maxUrlPrefix, nextResultOffset);
+            for(; j != max; ++j) {
+                url += String.fromCharCode(this.map.getUint8(j));
+            }
         }
 
         /* Keeping in UTF-8, as we need that for proper slicing (and concatenating) */
index 773bb0ea609737bf02af1973e26a1070481429ef..7353fb94be870904bba2013b0f2cadc5ff0333d8 100644 (file)
@@ -1 +1 @@
-O+!-vL;(N*Dggih0RRC2009I504V?g2mk;m009mF0B!&Q6aWBe0RRI400AHX04V?gBme*?00Alh0B!&QFaQ8)00A}t0BryPJOBVX0RaL4LI8j|00Bq<0CE5UPyhgL00CA20CWHWTmS%L00CkE0A&FH1poj6ZU6u&00D9U04M+fcmM!y00Djg0BHaLga80-00D{s06GBy1OSi#fI0vHmH+@{00Eu=0A~OJqyPYJ00F810AT<F8UO%oXaE3qumAvZ00FiD06GBy006`QfI0vH$^Zap00Ghf0CWQY0RRI41poj6-T(k800HIz04M+f>;M3600P(m0Aca~0BHdL1^@s70s#PJ009O80A~OJ3;_UP009yK0B`^S7y$rc00ABW0CfNa_y7QHXaE3qumAvZBmn?(AOHXmHvj->PXGWaa{vHoi2wj7s{jCP!2kfj-2eapO<{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@*UZYybcN
\ No newline at end of file
+O+!-viU9xsZ~*`S0RRC2009I504V?g2mk;m009mF0B!&Q6aWBe0RRI400AHX04V?gBme*?00Alh0B!&QFaQ8)00A}t0BryPJOBVX0RaL4LI8j|00Bq<0CE5UPyhgL00CA20CWHWTmS%L00CkE0A&FH1poj6ZU6u&00D9U04M+fcmM!y00Djg0BHaLga80-00D{s06GBy1OSi#fI0vHmH+@{00Eu=0A~OJqyPYJ00F810AT<F8UO%oXaE3qumAvZ00FiD06GBy006`QfI0vH$^Zap00Ghf0CWQY0RRI41poj6-T(k800HIz04M+f>;M3600P(m0Aca~0BHdL2LJ#80s#PJ009O80BisO3;_UV009yK0B!*Q2mk;99svL;00I~R0AV5l04M+fC;<R;00A-q0Am0GssI3CH~|1<0RRR700Bq=0A&CHOaTCA00B|~0AT<DSOEZV00CYB0Ac_EWB~wm00j5|0BvXh0CqkB0CH*p0COk+01!U_0BKhM04jR`0BMl`04TEn0By+t0Knw{008g+015s8002#4bZ7u>VQpn|aA9L*O<{CsE@*UZYybcf2s%1#X>KTKZgealX>N2W03&T_ZU6uPIyzQmV{~tF0Ap-nb8}5$bZB2OUolo?V{~tFE@*UZYyton20A)zX>KSfAY*TCb94YBZE0=*0025VQekdqWdLJrVRLg$VRUF;F<&uKVQyz-E@*UZYy<!o20A)zX>KSfAY*TCb94YBZE0>$VP|C^!n+_-bz*Q~XJr6$bz*Q~XJsyEbZu+|08(XRbYX61Y-I!h2LJ#9IyzEiV{|Af
\ No newline at end of file
index 6ab162d541b6fca34a57ea06f74f13b0e1ba5720..f1bed6261f7ccaa7a92ed5e2d394331836ca08f0 100644 (file)
Binary files a/doxygen/test/js-test-data/searchdata.bin and b/doxygen/test/js-test-data/searchdata.bin differ
index fb848414711fea8bce42113db8e51c17db24faf9..062803719bc41fc93e386c91c7d086b19cd8b551 100755 (executable)
@@ -57,15 +57,18 @@ index = map.add("Math::Vector::min() const", "classMath_1_1Vector.html#min", suf
 trie.insert("math::vector::min()", index, lookahead_barriers=[4, 12])
 trie.insert("vector::min()", index, lookahead_barriers=[6])
 trie.insert("min()", index)
-index = map.add("Math::Range", "classMath_1_1Range.html", flags=ResultFlag.CLASS)
-trie.insert("math::range", index)
-trie.insert("range", index)
+range_index = map.add("Math::Range", "classMath_1_1Range.html", flags=ResultFlag.CLASS)
+trie.insert("math::range", range_index)
+trie.insert("range", range_index)
 index = map.add("Math::Range::min() const", "classMath_1_1Range.html#min", suffix_length=6, flags=ResultFlag.FUNC|ResultFlag.DELETED)
 trie.insert("math::range::min()", index, lookahead_barriers=[4, 11])
 trie.insert("range::min()", index, lookahead_barriers=[5])
 trie.insert("min()", index)
 trie.insert("subpage", map.add("Page » Subpage", "subpage.html", flags=ResultFlag.PAGE))
 
+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))
 with open(basedir/'searchdata.b85', 'wb') as f:
index 427b9f4773196c56ad7a429eb002cf5b3abf2fbe..56b0c47b7f142d3b17685d1b56daa5fc973f3058 100644 (file)
@@ -70,7 +70,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, 545);
+    assert.equal(binary.byteLength, 648);
     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));
 }
@@ -105,8 +105,8 @@ 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, 545);
-    assert.equal(Search.symbolCount, 7);
+    assert.equal(Search.dataSize, 648);
+    assert.equal(Search.symbolCount, 9);
     assert.equal(Search.maxResults, 100);
 
     /* Blow up */
@@ -167,14 +167,29 @@ const { StringDecoder } = require('string_decoder');
           url: 'subpage.html',
           flags: 192,
           suffixLength: 5 }]);
+
+    /* Alias */
+    assert.deepEqual(Search.search('r'), [
+        { name: 'Math::Range',
+          url: 'classMath_1_1Range.html',
+          flags: 40,
+          suffixLength: 4 },
+        { name: 'Rectangle::Rect(): Math::Range',
+          url: 'classMath_1_1Range.html',
+          flags: 40,
+          suffixLength: 20 },
+        { name: 'Rectangle: Math::Range',
+          url: 'classMath_1_1Range.html',
+          flags: 40,
+          suffixLength: 21 }]);
 }
 
 /* Search, limiting the results to 3 */
 {
     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, 545);
-    assert.equal(Search.symbolCount, 7);
+    assert.equal(Search.dataSize, 648);
+    assert.equal(Search.symbolCount, 9);
     assert.equal(Search.maxResults, 3);
     assert.deepEqual(Search.search('m'), [
         { name: 'Math',
@@ -195,8 +210,8 @@ 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, 548); /* some padding on the end, that's okay */
-    assert.equal(Search.symbolCount, 7);
+    assert.equal(Search.dataSize, 648); /* some padding on the end, that's okay */
+    assert.equal(Search.symbolCount, 9);
     assert.equal(Search.maxResults, 100);
     assert.deepEqual(Search.search('min'), [
         { name: 'Math::min(int, int)',
index 7a43c346046623f19c46786c69f0ac7b96bf7dbc..9b0b0ca31afd2f5ed2134fc6218ff900ea7116ed 100755 (executable)
@@ -132,6 +132,9 @@ def pretty_print_map(serialized: bytes, colors=False):
         if i: out += '\n'
         flags = ResultFlag(ResultMap.flags_struct.unpack_from(serialized, i*4 + 3)[0])
         extra = []
+        if flags & ResultFlag._TYPE == ResultFlag.ALIAS:
+            extra += ['alias={}'.format(ResultMap.alias_struct.unpack_from(serialized, offset)[0])]
+            offset += ResultMap.alias_struct.size
         if flags & ResultFlag.HAS_PREFIX:
             extra += ['prefix={}[:{}]'.format(*ResultMap.prefix_struct.unpack_from(serialized, offset))]
             offset += ResultMap.prefix_struct.size
@@ -310,6 +313,8 @@ class MapSerialization(unittest.TestCase):
         self.assertEqual(map.add("Math::Range", "classMath_1_1Range.html", flags=ResultFlag.CLASS), 2)
         self.assertEqual(map.add("Math::min()", "namespaceMath.html#abcdef2875", flags=ResultFlag.FUNC), 3)
         self.assertEqual(map.add("Math::max(int, int)", "namespaceMath.html#abcdef1234", suffix_length=8, flags=ResultFlag.FUNC|ResultFlag.DEPRECATED|ResultFlag.DELETED), 4)
+        self.assertEqual(map.add("Rectangle", "", alias=2), 5)
+        self.assertEqual(map.add("Rectangle::Rect()", "", suffix_length=2, alias=2), 6)
 
         serialized = map.serialize()
         self.compare(serialized, """
@@ -318,8 +323,10 @@ class MapSerialization(unittest.TestCase):
 2: ::Range [prefix=0[:0], type=CLASS] -> classMath_1_1Range.html
 3: ::min() [prefix=0[:18], type=FUNC] -> #abcdef2875
 4: ::max(int, int) [prefix=0[:18], suffix_length=8, deprecated, deleted, type=FUNC] -> #abcdef1234
+5: Rectangle [alias=2] ->
+6: ::Rect() [alias=2, prefix=5[:0], suffix_length=2] ->
 """)
-        self.assertEqual(len(serialized), 170)
+        self.assertEqual(len(serialized), 203)
 
 class Serialization(unittest.TestCase):
     def __init__(self, *args, **kwargs):