about summary refs log tree commit diff
path: root/src/librustdoc/html/static/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/static/main.js')
-rw-r--r--src/librustdoc/html/static/main.js938
1 files changed, 723 insertions, 215 deletions
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index da4430d8a15..cea510de692 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -37,26 +37,42 @@
                      "associatedtype",
                      "constant",
                      "associatedconstant",
-                     "union"];
+                     "union",
+                     "foreigntype"];
+
+    // On the search screen, so you remain on the last tab you opened.
+    //
+    // 0 for "Types/modules"
+    // 1 for "As parameters"
+    // 2 for "As return value"
+    var currentTab = 0;
 
     function hasClass(elem, className) {
         if (elem && className && elem.className) {
             var elemClass = elem.className;
             var start = elemClass.indexOf(className);
-            if (start == -1) {
+            if (start === -1) {
                 return false;
-            } else if (elemClass.length == className.length) {
+            } else if (elemClass.length === className.length) {
                 return true;
             } else {
-                if (start > 0 && elemClass[start - 1] != ' ') {
+                if (start > 0 && elemClass[start - 1] !== ' ') {
                     return false;
                 }
                 var end = start + className.length;
-                if (end < elemClass.length && elemClass[end] != ' ') {
+                if (end < elemClass.length && elemClass[end] !== ' ') {
                     return false;
                 }
                 return true;
             }
+            if (start > 0 && elemClass[start - 1] !== ' ') {
+                return false;
+            }
+            var end = start + className.length;
+            if (end < elemClass.length && elemClass[end] !== ' ') {
+                return false;
+            }
+            return true;
         }
         return false;
     }
@@ -115,6 +131,7 @@
     }
 
     function highlightSourceLines(ev) {
+        var search = document.getElementById("search");
         var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
         if (match) {
             from = parseInt(match[1], 10);
@@ -129,7 +146,7 @@
                 if (x) {
                     x.scrollIntoView();
                 }
-            };
+            }
             onEach(document.getElementsByClassName('line-numbers'), function(e) {
                 onEach(e.getElementsByTagName('span'), function(i_e) {
                     removeClass(i_e, 'line-highlighted');
@@ -138,6 +155,17 @@
             for (i = from; i <= to; ++i) {
                 addClass(document.getElementById(i), 'line-highlighted');
             }
+        } else if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
+            addClass(search, "hidden");
+            removeClass(document.getElementById("main"), "hidden");
+            var hash = ev.newURL.slice(ev.newURL.indexOf('#') + 1);
+            if (browserSupportsHistoryApi()) {
+                history.replaceState(hash, "", "?search=#" + hash);
+            }
+            var elem = document.getElementById(hash);
+            if (elem) {
+                elem.scrollIntoView();
+            }
         }
     }
     highlightSourceLines(null);
@@ -163,6 +191,20 @@
         return String.fromCharCode(c);
     }
 
+    function displayHelp(display, ev) {
+        if (display === true) {
+            if (hasClass(help, "hidden")) {
+                ev.preventDefault();
+                removeClass(help, "hidden");
+                addClass(document.body, "blur");
+            }
+        } else if (!hasClass(help, "hidden")) {
+            ev.preventDefault();
+            addClass(help, "hidden");
+            removeClass(document.body, "blur");
+        }
+    }
+
     function handleShortcut(ev) {
         if (document.activeElement.tagName === "INPUT")
             return;
@@ -174,11 +216,10 @@
         var help = document.getElementById("help");
         switch (getVirtualKey(ev)) {
         case "Escape":
+            hideModal();
             var search = document.getElementById("search");
             if (!hasClass(help, "hidden")) {
-                ev.preventDefault();
-                addClass(help, "hidden");
-                removeClass(document.body, "blur");
+                displayHelp(false, ev);
             } else if (!hasClass(search, "hidden")) {
                 ev.preventDefault();
                 addClass(search, "hidden");
@@ -188,20 +229,22 @@
 
         case "s":
         case "S":
+            displayHelp(false, ev);
+            hideModal();
             ev.preventDefault();
             focusSearchBar();
             break;
 
         case "+":
+        case "-":
             ev.preventDefault();
             toggleAllDocs();
             break;
 
         case "?":
-            if (ev.shiftKey && hasClass(help, "hidden")) {
-                ev.preventDefault();
-                removeClass(help, "hidden");
-                addClass(document.body, "blur");
+            if (ev.shiftKey) {
+                hideModal();
+                displayHelp(true, ev);
             }
             break;
         }
@@ -309,6 +352,7 @@
     function initSearch(rawSearchIndex) {
         var currentResults, index, searchIndex;
         var MAX_LEV_DISTANCE = 3;
+        var MAX_RESULTS = 200;
         var params = getQueryStringParams();
 
         // Populate search bar with query string search term when provided,
@@ -331,15 +375,327 @@
             var valLower = query.query.toLowerCase(),
                 val = valLower,
                 typeFilter = itemTypeFromName(query.type),
-                results = [],
+                results = {}, results_in_args = {}, results_returned = {},
                 split = valLower.split("::");
 
-            // remove empty keywords
-            for (var j = 0; j < split.length; ++j) {
-                split[j].toLowerCase();
-                if (split[j] === "") {
-                    split.splice(j, 1);
+            for (var z = 0; z < split.length; ++z) {
+                if (split[z] === "") {
+                    split.splice(z, 1);
+                    z -= 1;
+                }
+            }
+
+            function transformResults(results, isType) {
+                var out = [];
+                for (i = 0; i < results.length; ++i) {
+                    if (results[i].id > -1) {
+                        var obj = searchIndex[results[i].id];
+                        obj.lev = results[i].lev;
+                        if (isType !== true || obj.type) {
+                            out.push(obj);
+                        }
+                    }
+                    if (out.length >= MAX_RESULTS) {
+                        break;
+                    }
+                }
+                return out;
+            }
+
+            function sortResults(results, isType) {
+                var ar = [];
+                for (var entry in results) {
+                    if (results.hasOwnProperty(entry)) {
+                        ar.push(results[entry]);
+                    }
+                }
+                results = ar;
+                var nresults = results.length;
+                for (var i = 0; i < nresults; ++i) {
+                    results[i].word = searchWords[results[i].id];
+                    results[i].item = searchIndex[results[i].id] || {};
+                }
+                // if there are no results then return to default and fail
+                if (results.length === 0) {
+                    return [];
+                }
+
+                results.sort(function(aaa, bbb) {
+                    var a, b;
+
+                    // Sort by non levenshtein results and then levenshtein results by the distance
+                    // (less changes required to match means higher rankings)
+                    a = (aaa.lev);
+                    b = (bbb.lev);
+                    if (a !== b) { return a - b; }
+
+                    // sort by crate (non-current crate goes later)
+                    a = (aaa.item.crate !== window.currentCrate);
+                    b = (bbb.item.crate !== window.currentCrate);
+                    if (a !== b) { return a - b; }
+
+                    // sort by exact match (mismatch goes later)
+                    a = (aaa.word !== valLower);
+                    b = (bbb.word !== valLower);
+                    if (a !== b) { return a - b; }
+
+                    // sort by item name length (longer goes later)
+                    a = aaa.word.length;
+                    b = bbb.word.length;
+                    if (a !== b) { return a - b; }
+
+                    // sort by item name (lexicographically larger goes later)
+                    a = aaa.word;
+                    b = bbb.word;
+                    if (a !== b) { return (a > b ? +1 : -1); }
+
+                    // sort by index of keyword in item name (no literal occurrence goes later)
+                    a = (aaa.index < 0);
+                    b = (bbb.index < 0);
+                    if (a !== b) { return a - b; }
+                    // (later literal occurrence, if any, goes later)
+                    a = aaa.index;
+                    b = bbb.index;
+                    if (a !== b) { return a - b; }
+
+                    // special precedence for primitive pages
+                    if ((aaa.item.ty === TY_PRIMITIVE) && (bbb.item.ty !== TY_PRIMITIVE)) {
+                        return -1;
+                    }
+                    if ((bbb.item.ty === TY_PRIMITIVE) && (aaa.item.ty !== TY_PRIMITIVE)) {
+                        return 1;
+                    }
+
+                    // sort by description (no description goes later)
+                    a = (aaa.item.desc === '');
+                    b = (bbb.item.desc === '');
+                    if (a !== b) { return a - b; }
+
+                    // sort by type (later occurrence in `itemTypes` goes later)
+                    a = aaa.item.ty;
+                    b = bbb.item.ty;
+                    if (a !== b) { return a - b; }
+
+                    // sort by path (lexicographically larger goes later)
+                    a = aaa.item.path;
+                    b = bbb.item.path;
+                    if (a !== b) { return (a > b ? +1 : -1); }
+
+                    // que sera, sera
+                    return 0;
+                });
+
+                for (var i = 0; i < results.length; ++i) {
+                    var result = results[i];
+
+                    // this validation does not make sense when searching by types
+                    if (result.dontValidate) {
+                        continue;
+                    }
+                    var name = result.item.name.toLowerCase(),
+                        path = result.item.path.toLowerCase(),
+                        parent = result.item.parent;
+
+                    if (isType !== true &&
+                        validateResult(name, path, split, parent) === false)
+                    {
+                        result.id = -1;
+                    }
                 }
+                return transformResults(results);
+            }
+
+            function extractGenerics(val) {
+                val = val.toLowerCase();
+                if (val.indexOf('<') !== -1) {
+                    var values = val.substring(val.indexOf('<') + 1, val.lastIndexOf('>'));
+                    return {
+                        name: val.substring(0, val.indexOf('<')),
+                        generics: values.split(/\s*,\s*/),
+                    };
+                }
+                return {
+                    name: val,
+                    generics: [],
+                };
+            }
+
+            function checkGenerics(obj, val) {
+                // The names match, but we need to be sure that all generics kinda
+                // match as well.
+                var lev_distance = MAX_LEV_DISTANCE + 1;
+                if (val.generics.length > 0) {
+                    if (obj.generics && obj.generics.length >= val.generics.length) {
+                        var elems = obj.generics.slice(0);
+                        var total = 0;
+                        var done = 0;
+                        // We need to find the type that matches the most to remove it in order
+                        // to move forward.
+                        for (var y = 0; y < val.generics.length; ++y) {
+                            var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1};
+                            for (var x = 0; x < elems.length; ++x) {
+                                var tmp_lev = levenshtein(elems[x], val.generics[y]);
+                                if (tmp_lev < lev.lev) {
+                                    lev.lev = tmp_lev;
+                                    lev.pos = x;
+                                }
+                            }
+                            if (lev.pos !== -1) {
+                                elems.splice(lev.pos, 1);
+                                lev_distance = Math.min(lev.lev, lev_distance);
+                                total += lev.lev;
+                                done += 1;
+                            } else {
+                                return MAX_LEV_DISTANCE + 1;
+                            }
+                        }
+                        return lev_distance;//Math.ceil(total / done);
+                    }
+                }
+                return MAX_LEV_DISTANCE + 1;
+            }
+
+            // Check for type name and type generics (if any).
+            function checkType(obj, val, literalSearch) {
+                var lev_distance = MAX_LEV_DISTANCE + 1;
+                if (obj.name === val.name) {
+                    if (literalSearch === true) {
+                        if (val.generics.length !== 0) {
+                            if (obj.generics && obj.length >= val.generics.length) {
+                                var elems = obj.generics.slice(0);
+                                var allFound = true;
+                                var x;
+
+                                for (var y = 0; allFound === true && y < val.generics.length; ++y) {
+                                    allFound = false;
+                                    for (x = 0; allFound === false && x < elems.length; ++x) {
+                                        allFound = elems[x] === val.generics[y];
+                                    }
+                                    if (allFound === true) {
+                                        elems.splice(x - 1, 1);
+                                    }
+                                }
+                                if (allFound === true) {
+                                    return true;
+                                }
+                            } else {
+                                return false;
+                            }
+                        }
+                        return true;
+                    }
+                    // If the type has generics but don't match, then it won't return at this point.
+                    // Otherwise, `checkGenerics` will return 0 and it'll return.
+                    if (obj.generics && obj.generics.length !== 0) {
+                        var tmp_lev = checkGenerics(obj, val);
+                        if (tmp_lev <= MAX_LEV_DISTANCE) {
+                            return tmp_lev;
+                        }
+                    } else {
+                        return 0;
+                    }
+                }
+                // Names didn't match so let's check if one of the generic types could.
+                if (literalSearch === true) {
+                     if (obj.generics.length > 0) {
+                        for (var x = 0; x < obj.generics.length; ++x) {
+                            if (obj.generics[x] === val.name) {
+                                return true;
+                            }
+                        }
+                    }
+                    return false;
+                }
+                var lev_distance = Math.min(levenshtein(obj.name, val.name), lev_distance);
+                if (lev_distance <= MAX_LEV_DISTANCE) {
+                    lev_distance = Math.min(checkGenerics(obj, val), lev_distance);
+                } else if (obj.generics && obj.generics.length > 0) {
+                    // We can check if the type we're looking for is inside the generics!
+                    for (var x = 0; x < obj.generics.length; ++x) {
+                        lev_distance = Math.min(levenshtein(obj.generics[x], val.name),
+                                                lev_distance);
+                    }
+                }
+                // Now whatever happens, the returned distance is "less good" so we should mark it
+                // as such, and so we add 1 to the distance to make it "less good".
+                return lev_distance + 1;
+            }
+
+            function findArg(obj, val, literalSearch) {
+                var lev_distance = MAX_LEV_DISTANCE + 1;
+
+                if (obj && obj.type && obj.type.inputs.length > 0) {
+                    for (var i = 0; i < obj.type.inputs.length; i++) {
+                        var tmp = checkType(obj.type.inputs[i], val, literalSearch);
+                        if (literalSearch === true && tmp === true) {
+                            return true;
+                        }
+                        lev_distance = Math.min(tmp, lev_distance);
+                        if (lev_distance === 0) {
+                            return 0;
+                        }
+                    }
+                }
+                return literalSearch === true ? false : lev_distance;
+            }
+
+            function checkReturned(obj, val, literalSearch) {
+                var lev_distance = MAX_LEV_DISTANCE + 1;
+
+                if (obj && obj.type && obj.type.output) {
+                    var tmp = checkType(obj.type.output, val, literalSearch);
+                    if (literalSearch === true && tmp === true) {
+                        return true;
+                    }
+                    lev_distance = Math.min(tmp, lev_distance);
+                    if (lev_distance === 0) {
+                        return 0;
+                    }
+                }
+                return literalSearch === true ? false : lev_distance;
+            }
+
+            function checkPath(startsWith, lastElem, ty) {
+                var ret_lev = MAX_LEV_DISTANCE + 1;
+                var path = ty.path.split("::");
+
+                if (ty.parent && ty.parent.name) {
+                    path.push(ty.parent.name.toLowerCase());
+                }
+
+                if (startsWith.length > path.length) {
+                    return MAX_LEV_DISTANCE + 1;
+                }
+                for (var i = 0; i < path.length; ++i) {
+                    if (i + startsWith.length > path.length) {
+                        break;
+                    }
+                    var lev_total = 0;
+                    var aborted = false;
+                    for (var x = 0; x < startsWith.length; ++x) {
+                        var lev = levenshtein(path[i + x], startsWith[x]);
+                        if (lev > MAX_LEV_DISTANCE) {
+                            aborted = true;
+                            break;
+                        }
+                        lev_total += lev;
+                    }
+                    if (aborted === false) {
+                        var extra = MAX_LEV_DISTANCE + 1;
+                        if (i + startsWith.length < path.length) {
+                            extra = levenshtein(path[i + startsWith.length], lastElem);
+                        }
+                        if (extra > MAX_LEV_DISTANCE) {
+                            extra = levenshtein(ty.name, lastElem);
+                        }
+                        if (extra < MAX_LEV_DISTANCE + 1) {
+                            lev_total += extra;
+                            ret_lev = Math.min(ret_lev,
+                                               Math.round(lev_total / (startsWith.length + 1)));
+                        }
+                    }
+                }
+                return ret_lev;
             }
 
             function typePassesFilter(filter, type) {
@@ -364,189 +720,238 @@
                 return false;
             }
 
+            function generateId(ty) {
+                if (ty.parent && ty.parent.name) {
+                    return itemTypes[ty.ty] + ty.path + ty.parent.name + ty.name;
+                }
+                return itemTypes[ty.ty] + ty.path + ty.name;
+            }
+
             // quoted values mean literal search
             var nSearchWords = searchWords.length;
             if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
                 val.charAt(val.length - 1) === val.charAt(0))
             {
-                val = val.substr(1, val.length - 2);
+                val = extractGenerics(val.substr(1, val.length - 2));
                 for (var i = 0; i < nSearchWords; ++i) {
-                    if (searchWords[i] === val) {
+                    var in_args = findArg(searchIndex[i], val, true);
+                    var returned = checkReturned(searchIndex[i], val, true);
+                    var ty = searchIndex[i];
+                    var fullId = generateId(ty);
+
+                    if (searchWords[i] === val.name) {
                         // filter type: ... queries
-                        if (typePassesFilter(typeFilter, searchIndex[i].ty)) {
-                            results.push({id: i, index: -1});
+                        if (typePassesFilter(typeFilter, searchIndex[i].ty) &&
+                            results[fullId] === undefined)
+                        {
+                            results[fullId] = {id: i, index: -1};
+                        }
+                    } else if ((in_args === true || returned === true) &&
+                               typePassesFilter(typeFilter, searchIndex[i].ty)) {
+                        if (in_args === true || returned === true) {
+                            if (in_args === true) {
+                                results_in_args[fullId] = {
+                                    id: i,
+                                    index: -1,
+                                    dontValidate: true,
+                                };
+                            }
+                            if (returned === true) {
+                                results_returned[fullId] = {
+                                    id: i,
+                                    index: -1,
+                                    dontValidate: true,
+                                };
+                            }
+                        } else {
+                            results[fullId] = {
+                                id: i,
+                                index: -1,
+                                dontValidate: true,
+                            };
                         }
-                    }
-                    if (results.length === max) {
-                        break;
                     }
                 }
+                query.inputs = [val];
+                query.output = val;
+                query.search = val;
             // searching by type
             } else if (val.search("->") > -1) {
                 var trimmer = function (s) { return s.trim(); };
                 var parts = val.split("->").map(trimmer);
                 var input = parts[0];
                 // sort inputs so that order does not matter
-                var inputs = input.split(",").map(trimmer).sort().toString();
-                var output = parts[1];
+                var inputs = input.split(",").map(trimmer).sort();
+                for (var i = 0; i < inputs.length; ++i) {
+                    inputs[i] = extractGenerics(inputs[i]);
+                }
+                var output = extractGenerics(parts[1]);
 
                 for (var i = 0; i < nSearchWords; ++i) {
                     var type = searchIndex[i].type;
+                    var ty = searchIndex[i];
                     if (!type) {
                         continue;
                     }
-
-                    // sort index inputs so that order does not matter
-                    var typeInputs = type.inputs.map(function (input) {
-                        return input.name;
-                    }).sort();
+                    var fullId = generateId(ty);
 
                     // allow searching for void (no output) functions as well
                     var typeOutput = type.output ? type.output.name : "";
-                    if ((inputs === "*" || inputs === typeInputs.toString()) &&
-                        (output === "*" || output == typeOutput)) {
-                        results.push({id: i, index: -1, dontValidate: true});
-                    }
-                }
-            } else {
-                // gather matching search results up to a certain maximum
-                val = val.replace(/\_/g, "");
-                for (var i = 0; i < split.length; ++i) {
-                    for (var j = 0; j < nSearchWords; ++j) {
-                        var lev_distance;
-                        if (searchWords[j].indexOf(split[i]) > -1 ||
-                            searchWords[j].indexOf(val) > -1 ||
-                            searchWords[j].replace(/_/g, "").indexOf(val) > -1)
-                        {
-                            // filter type: ... queries
-                            if (typePassesFilter(typeFilter, searchIndex[j].ty)) {
-                                results.push({
-                                    id: j,
-                                    index: searchWords[j].replace(/_/g, "").indexOf(val),
-                                    lev: 0,
-                                });
-                            }
-                        } else if (
-                            (lev_distance = levenshtein(searchWords[j], val)) <=
-                                MAX_LEV_DISTANCE) {
-                            if (typePassesFilter(typeFilter, searchIndex[j].ty)) {
-                                results.push({
-                                    id: j,
-                                    index: 0,
-                                    // we want lev results to go lower than others
-                                    lev: lev_distance,
-                                });
+                    var returned = checkReturned(ty, output, true);
+                    if (output.name === "*" || returned === true) {
+                        var in_args = false;
+                        var module = false;
+
+                        if (input === "*") {
+                            module = true;
+                        } else {
+                            var allFound = true;
+                            for (var it = 0; allFound === true && it < inputs.length; it++) {
+                                allFound = checkType(type, inputs[it], true);
                             }
+                            in_args = allFound;
                         }
-                        if (results.length === max) {
-                            break;
+                        if (in_args === true) {
+                            results_in_args[fullId] = {
+                                id: i,
+                                index: -1,
+                                dontValidate: true,
+                            };
+                        }
+                        if (returned === true) {
+                            results_returned[fullId] = {
+                                id: i,
+                                index: -1,
+                                dontValidate: true,
+                            };
+                        }
+                        if (module === true) {
+                            results[fullId] = {
+                                id: i,
+                                index: -1,
+                                dontValidate: true,
+                            };
                         }
                     }
                 }
-            }
+                query.inputs = inputs.map(function(input) {
+                    return input.name;
+                });
+                query.output = output.name;
+            } else {
+                query.inputs = [val];
+                query.output = val;
+                query.search = val;
+                // gather matching search results up to a certain maximum
+                val = val.replace(/\_/g, "");
 
-            var nresults = results.length;
-            for (var i = 0; i < nresults; ++i) {
-                results[i].word = searchWords[results[i].id];
-                results[i].item = searchIndex[results[i].id] || {};
-            }
-            // if there are no results then return to default and fail
-            if (results.length === 0) {
-                return [];
-            }
+                var valGenerics = extractGenerics(val);
 
-            results.sort(function sortResults(aaa, bbb) {
-                var a, b;
-
-                // Sort by non levenshtein results and then levenshtein results by the distance
-                // (less changes required to match means higher rankings)
-                a = (aaa.lev);
-                b = (bbb.lev);
-                if (a !== b) { return a - b; }
-
-                // sort by crate (non-current crate goes later)
-                a = (aaa.item.crate !== window.currentCrate);
-                b = (bbb.item.crate !== window.currentCrate);
-                if (a !== b) { return a - b; }
-
-                // sort by exact match (mismatch goes later)
-                a = (aaa.word !== valLower);
-                b = (bbb.word !== valLower);
-                if (a !== b) { return a - b; }
-
-                // sort by item name length (longer goes later)
-                a = aaa.word.length;
-                b = bbb.word.length;
-                if (a !== b) { return a - b; }
-
-                // sort by item name (lexicographically larger goes later)
-                a = aaa.word;
-                b = bbb.word;
-                if (a !== b) { return (a > b ? +1 : -1); }
-
-                // sort by index of keyword in item name (no literal occurrence goes later)
-                a = (aaa.index < 0);
-                b = (bbb.index < 0);
-                if (a !== b) { return a - b; }
-                // (later literal occurrence, if any, goes later)
-                a = aaa.index;
-                b = bbb.index;
-                if (a !== b) { return a - b; }
-
-                // special precedence for primitive pages
-                if ((aaa.item.ty === TY_PRIMITIVE) && (bbb.item.ty !== TY_PRIMITIVE)) {
-                    return -1;
-                }
-                if ((bbb.item.ty === TY_PRIMITIVE) && (aaa.item.ty !== TY_PRIMITIVE)) {
-                    return 1;
+                var paths = valLower.split("::");
+                var j;
+                for (j = 0; j < paths.length; ++j) {
+                    if (paths[j] === "") {
+                        paths.splice(j, 1);
+                        j -= 1;
+                    }
                 }
+                val = paths[paths.length - 1];
+                var startsWith = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
 
-                // sort by description (no description goes later)
-                a = (aaa.item.desc === '');
-                b = (bbb.item.desc === '');
-                if (a !== b) { return a - b; }
-
-                // sort by type (later occurrence in `itemTypes` goes later)
-                a = aaa.item.ty;
-                b = bbb.item.ty;
-                if (a !== b) { return a - b; }
-
-                // sort by path (lexicographically larger goes later)
-                a = aaa.item.path;
-                b = bbb.item.path;
-                if (a !== b) { return (a > b ? +1 : -1); }
+                for (j = 0; j < nSearchWords; ++j) {
+                    var lev_distance;
+                    var ty = searchIndex[j];
+                    if (!ty) {
+                        continue;
+                    }
+                    var lev_add = 0;
+                    if (paths.length > 1) {
+                        var lev = checkPath(startsWith, paths[paths.length - 1], ty);
+                        if (lev > MAX_LEV_DISTANCE) {
+                            continue;
+                        } else if (lev > 0) {
+                            lev_add = 1;
+                        }
+                    }
 
-                // que sera, sera
-                return 0;
-            });
+                    var returned = MAX_LEV_DISTANCE + 1;
+                    var in_args = MAX_LEV_DISTANCE + 1;
+                    var index = -1;
+                    // we want lev results to go lower than others
+                    var lev = MAX_LEV_DISTANCE + 1;
+                    var fullId = generateId(ty);
+
+                    if (searchWords[j].indexOf(split[i]) > -1 ||
+                        searchWords[j].indexOf(val) > -1 ||
+                        searchWords[j].replace(/_/g, "").indexOf(val) > -1)
+                    {
+                        // filter type: ... queries
+                        if (typePassesFilter(typeFilter, ty) && results[fullId] === undefined) {
+                            index = searchWords[j].replace(/_/g, "").indexOf(val);
+                        }
+                    }
+                    if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
+                        if (typePassesFilter(typeFilter, ty) === false) {
+                            lev = MAX_LEV_DISTANCE + 1;
+                        } else {
+                            lev += 1;
+                        }
+                    }
+                    if ((in_args = findArg(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
+                        if (typePassesFilter(typeFilter, ty) === false) {
+                            in_args = MAX_LEV_DISTANCE + 1;
+                        }
+                    }
+                    if ((returned = checkReturned(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
+                        if (typePassesFilter(typeFilter, ty) === false) {
+                            returned = MAX_LEV_DISTANCE + 1;
+                        }
+                    }
 
-            // remove duplicates, according to the data provided
-            for (var i = results.length - 1; i > 0; i -= 1) {
-                if (results[i].word === results[i - 1].word &&
-                    results[i].item.ty === results[i - 1].item.ty &&
-                    results[i].item.path === results[i - 1].item.path &&
-                    (results[i].item.parent || {}).name === (results[i - 1].item.parent || {}).name)
-                {
-                    results[i].id = -1;
+                    lev += lev_add;
+                    if (in_args <= MAX_LEV_DISTANCE) {
+                        if (results_in_args[fullId] === undefined) {
+                            results_in_args[fullId] = {
+                                id: j,
+                                index: index,
+                                lev: in_args,
+                            };
+                        }
+                        results_in_args[fullId].lev =
+                            Math.min(results_in_args[fullId].lev, in_args);
+                    }
+                    if (returned <= MAX_LEV_DISTANCE) {
+                        if (results_returned[fullId] === undefined) {
+                            results_returned[fullId] = {
+                                id: j,
+                                index: index,
+                                lev: returned,
+                            };
+                        }
+                        results_returned[fullId].lev =
+                            Math.min(results_returned[fullId].lev, returned);
+                    }
+                    if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
+                        if (index !== -1) {
+                            lev = 0;
+                        }
+                        if (results[fullId] === undefined) {
+                            results[fullId] = {
+                                id: j,
+                                index: index,
+                                lev: lev,
+                            };
+                        }
+                        results[fullId].lev = Math.min(results[fullId].lev, lev);
+                    }
                 }
             }
-            for (var i = 0; i < results.length; ++i) {
-                var result = results[i],
-                    name = result.item.name.toLowerCase(),
-                    path = result.item.path.toLowerCase(),
-                    parent = result.item.parent;
-
-                // this validation does not make sense when searching by types
-                if (result.dontValidate) {
-                    continue;
-                }
 
-                var valid = validateResult(name, path, split, parent);
-                if (!valid) {
-                    result.id = -1;
-                }
-            }
-            return results;
+            return {
+                'in_args': sortResults(results_in_args, true),
+                'returned': sortResults(results_returned, true),
+                'others': sortResults(results),
+            };
         }
 
         /**
@@ -569,15 +974,14 @@
                 // each check is for validation so we negate the conditions and invalidate
                 if (!(
                     // check for an exact name match
-                    name.toLowerCase().indexOf(keys[i]) > -1 ||
+                    name.indexOf(keys[i]) > -1 ||
                     // then an exact path match
-                    path.toLowerCase().indexOf(keys[i]) > -1 ||
+                    path.indexOf(keys[i]) > -1 ||
                     // next if there is a parent, check for exact parent match
                     (parent !== undefined &&
                         parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
                     // lastly check to see if the name was a levenshtein match
-                    levenshtein(name.toLowerCase(), keys[i]) <=
-                        MAX_LEV_DISTANCE)) {
+                    levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
                     return false;
                 }
             }
@@ -647,41 +1051,56 @@
             });
 
             var search_input = document.getElementsByClassName('search-input')[0];
-            search_input.onkeydown = null;
             search_input.onkeydown = function(e) {
-                var actives = [];
+                // "actives" references the currently highlighted item in each search tab.
+                // Each array in "actives" represents a tab.
+                var actives = [[], [], []];
+                // "current" is used to know which tab we're looking into.
+                var current = 0;
                 onEach(document.getElementsByClassName('search-results'), function(e) {
-                    onEach(document.getElementsByClassName('highlighted'), function(e) {
-                        actives.push(e);
+                    onEach(e.getElementsByClassName('highlighted'), function(e) {
+                        actives[current].push(e);
                     });
+                    current += 1;
                 });
 
                 if (e.which === 38) { // up
-                    if (!actives.length || !actives[0].previousElementSibling) {
+                    if (!actives[currentTab].length ||
+                        !actives[currentTab][0].previousElementSibling) {
                         return;
                     }
 
-                    addClass(actives[0].previousElementSibling, 'highlighted');
-                    removeClass(actives[0], 'highlighted');
+                    addClass(actives[currentTab][0].previousElementSibling, 'highlighted');
+                    removeClass(actives[currentTab][0], 'highlighted');
                 } else if (e.which === 40) { // down
-                    if (!actives.length) {
+                    if (!actives[currentTab].length) {
                         var results = document.getElementsByClassName('search-results');
                         if (results.length > 0) {
-                            var res = results[0].getElementsByClassName('result');
+                            var res = results[currentTab].getElementsByClassName('result');
                             if (res.length > 0) {
                                 addClass(res[0], 'highlighted');
                             }
                         }
-                    } else if (actives[0].nextElementSibling) {
-                        addClass(actives[0].nextElementSibling, 'highlighted');
-                        removeClass(actives[0], 'highlighted');
+                    } else if (actives[currentTab][0].nextElementSibling) {
+                        addClass(actives[currentTab][0].nextElementSibling, 'highlighted');
+                        removeClass(actives[currentTab][0], 'highlighted');
                     }
                 } else if (e.which === 13) { // return
-                    if (actives.length) {
-                        document.location.href = actives[0].getElementsByTagName('a')[0].href;
+                    if (actives[currentTab].length) {
+                        document.location.href =
+                            actives[currentTab][0].getElementsByTagName('a')[0].href;
+                    }
+                } else if (e.which === 9) { // tab
+                    if (e.shiftKey) {
+                        printTab(currentTab > 0 ? currentTab - 1 : 2);
+                    } else {
+                        printTab(currentTab > 1 ? 0 : currentTab + 1);
                     }
-                } else if (actives.length > 0) {
-                    removeClass(actives[0], 'highlighted');
+                    e.preventDefault();
+                } else if (e.which === 16) { // shift
+                    // Does nothing, it's just to avoid losing "focus" on the highlighted element.
+                } else if (actives[currentTab].length > 0) {
+                    removeClass(actives[currentTab][0], 'highlighted');
                 }
             };
         }
@@ -692,18 +1111,18 @@
             return h1.innerHTML;
         }
 
-        function showResults(results) {
-            var output, shown, query = getQuery();
-
-            currentResults = query.id;
-            output = '<h1>Results for ' + escape(query.query) +
-                (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>';
-            output += '<table class="search-results">';
+        function addTab(array, query, display) {
+            var extraStyle = '';
+            if (display === false) {
+                extraStyle = ' style="display: none;"';
+            }
 
-            if (results.length > 0) {
-                shown = [];
+            var output = '';
+            if (array.length > 0) {
+                output = '<table class="search-results"' + extraStyle + '>';
+                var shown = [];
 
-                results.forEach(function(item) {
+                array.forEach(function(item) {
                     var name, type, href, displayPath;
 
                     if (shown.indexOf(item) !== -1) {
@@ -752,13 +1171,41 @@
                               '<span class="desc">' + escape(item.desc) +
                               '&nbsp;</span></a></td></tr>';
                 });
+                output += '</table>';
             } else {
-                output += 'No results :( <a href="https://duckduckgo.com/?q=' +
+                output = '<div class="search-failed"' + extraStyle + '>No results :(<br/>' +
+                    'Try on <a href="https://duckduckgo.com/?q=' +
                     encodeURIComponent('rust ' + query.query) +
-                    '">Try on DuckDuckGo?</a>';
+                    '">DuckDuckGo</a>?</div>';
+            }
+            return output;
+        }
+
+        function makeTabHeader(tabNb, text, nbElems) {
+            if (currentTab === tabNb) {
+                return '<div class="selected">' + text +
+                       ' <div class="count">(' + nbElems + ')</div></div>';
             }
+            return '<div>' + text + ' <div class="count">(' + nbElems + ')</div></div>';
+        }
+
+        function showResults(results) {
+            var output, query = getQuery();
+
+            currentResults = query.id;
+            output = '<h1>Results for ' + escape(query.query) +
+                (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>' +
+                '<div id="titles">' +
+                makeTabHeader(0, "Types/modules", results['others'].length) +
+                makeTabHeader(1, "As parameters", results['in_args'].length) +
+                makeTabHeader(2, "As return value", results['returned'].length) +
+                '</div><div id="results">';
+
+            output += addTab(results['others'], query);
+            output += addTab(results['in_args'], query, false);
+            output += addTab(results['returned'], query, false);
+            output += '</div>';
 
-            output += "</p>";
             addClass(document.getElementById('main'), 'hidden');
             var search = document.getElementById('search');
             removeClass(search, 'hidden');
@@ -773,14 +1220,17 @@
                 e.style.width = width + 'px';
             });
             initSearchNav();
+            var elems = document.getElementById('titles').childNodes;
+            elems[0].onclick = function() { printTab(0); };
+            elems[1].onclick = function() { printTab(1); };
+            elems[2].onclick = function() { printTab(2); };
+            printTab(currentTab);
         }
 
         function search(e) {
             var query,
-                filterdata = [],
                 obj, i, len,
-                results = [],
-                maxResults = 200,
+                results = {"in_args": [], "returned": [], "others": []},
                 resultIndex;
             var params = getQueryStringParams();
 
@@ -806,25 +1256,15 @@
                 }
             }
 
-            resultIndex = execQuery(query, 20000, index);
-            len = resultIndex.length;
-            for (i = 0; i < len; ++i) {
-                if (resultIndex[i].id > -1) {
-                    obj = searchIndex[resultIndex[i].id];
-                    filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
-                    results.push(obj);
-                }
-                if (results.length >= maxResults) {
-                    break;
-                }
-            }
-
+            results = execQuery(query, 20000, index);
             showResults(results);
         }
 
         function itemTypeFromName(typename) {
             for (var i = 0; i < itemTypes.length; ++i) {
-                if (itemTypes[i] === typename) { return i; }
+                if (itemTypes[i] === typename) {
+                    return i;
+                }
             }
             return -1;
         }
@@ -915,7 +1355,7 @@
             var search_input = document.getElementsByClassName("search-input")[0];
             search_input.onkeyup = callback;
             search_input.oninput = callback;
-            document.getElementsByClassName("search-form")[0].onsubmit = function(e){
+            document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
                 e.preventDefault();
                 clearTimeout(searchTimeout);
                 search();
@@ -991,7 +1431,9 @@
 
             var crates = [];
             for (var crate in rawSearchIndex) {
-                if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
+                if (!rawSearchIndex.hasOwnProperty(crate)) {
+                    continue;
+                }
                 crates.push(crate);
             }
             crates.sort();
@@ -1071,6 +1513,7 @@
         block("trait", "Traits");
         block("fn", "Functions");
         block("type", "Type Definitions");
+        block("foreigntype", "Foreign Types");
     }
 
     window.initSidebarItems = initSidebarItems;
@@ -1290,6 +1733,30 @@
         return wrapper;
     }
 
+    // In the search display, allows to switch between tabs.
+    function printTab(nb) {
+        if (nb === 0 || nb === 1 || nb === 2) {
+            currentTab = nb;
+        }
+        var nb_copy = nb;
+        onEach(document.getElementById('titles').childNodes, function(elem) {
+            if (nb_copy === 0) {
+                addClass(elem, 'selected');
+            } else {
+                removeClass(elem, 'selected');
+            }
+            nb_copy -= 1;
+        });
+        onEach(document.getElementById('results').childNodes, function(elem) {
+            if (nb === 0) {
+                elem.style.display = '';
+            } else {
+                elem.style.display = 'none';
+            }
+            nb -= 1;
+        });
+    }
+
     onEach(document.getElementById('main').getElementsByTagName('pre'), function(e) {
         onEach(e.getElementsByClassName('attributes'), function(i_e) {
             i_e.parentNode.insertBefore(createToggleWrapper(), i_e);
@@ -1314,6 +1781,47 @@
             });
         }
     });
+
+    function showModal(content) {
+        var modal = document.createElement('div');
+        modal.id = "important";
+        addClass(modal, 'modal');
+        modal.innerHTML = '<div class="modal-content"><div class="close" id="modal-close">✕</div>' +
+                          '<div class="whiter"></div><span class="docblock">' + content +
+                          '</span></div>';
+        document.getElementsByTagName('body')[0].appendChild(modal);
+        document.getElementById('modal-close').onclick = hideModal;
+        modal.onclick = hideModal;
+    }
+
+    function hideModal() {
+        var modal = document.getElementById("important");
+        if (modal) {
+            modal.parentNode.removeChild(modal);
+        }
+    }
+
+    onEach(document.getElementsByClassName('important-traits'), function(e) {
+        e.onclick = function() {
+            showModal(e.lastElementChild.innerHTML);
+        };
+    });
+
+    var search_input = document.getElementsByClassName("search-input")[0];
+
+    if (search_input) {
+        search_input.onfocus = function() {
+            if (search_input.value !== "") {
+                addClass(document.getElementById("main"), "hidden");
+                removeClass(document.getElementById("search"), "hidden");
+                if (browserSupportsHistoryApi()) {
+                    history.replaceState(search_input.value,
+                                         "",
+                                         "?search=" + encodeURIComponent(search_input.value));
+                }
+            }
+        };
+    }
 }());
 
 // Sets the focus on the search bar at the top of the page