// Searching the OCAML API. // Copyright 2019-2020 San VU NGOC // Permission to use, copy, modify, and/or distribute this software // for any purpose with or without fee is hereby granted, provided // that the above copyright notice and this permission notice appear // in all copies. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS // OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // Thanks @steinuil for help on deferred loading. // Thanks @osener, @UnixJunkie, @Armael for very helpful suggestions // Thanks to all testers! const MAX_RESULTS = 20; const MAX_ERROR = 10; const DESCR_INDEX = 4; // index of HTML description in index.js const SIG_INDEX = 6; // index of HTML signature in index.js const ERR_INDEX = 8; // length of each line in index.js. This is used // for storing the computed error, except if we // don't want description and type signature, // then ERR_INDEX becomes DESCR_INDEX. let indexState = 'NOT_LOADED'; // return true if we are loading the index file function loadingIndex (includeDescr) { switch (indexState) { case 'NOT_LOADED': indexState = 'LOADING'; const script = document.createElement('script'); script.src = 'index.js'; script.addEventListener('load', () => { indexState = 'HAS_LOADED'; mySearch(includeDescr); }); document.head.appendChild(script); return true; case 'LOADING': return true; case 'HAS_LOADED': return false; } } // line is a string array. We check if sub is a substring of one of // the elements of the array. The start/end of the string s are marked // by "^" and "$", and hence these chars can be used in sub to refine // the search. Case sensitive is better for OCaml modules. Searching // within line.join() is slightly more efficient that iterating 'line' // with .findIndex (my benchmarks show about 15% faster; except if we // search for the value at the beginning of line). However it might // use more memory. function hasSubString (sub, line) { let lineAll = "^" + line.join("$^") + "$"; return (lineAll.includes(sub)); } // Check if one of the strings in subs is a substring of one of the // strings in line. function hasSubStrings (subs, line) { let lineAll = "^" + line.join("$^") + "$"; return (subs.findIndex(function (sub) { return (lineAll.includes(sub))}) !== -1); } // Error of sub being a substring of s. Best if starts at 0. Except // for strings containing "->", which is then best if the substring is // at the most right-hand position (representing the "return type"). // markers "^" and "$" for start/end of string can be used: if they // are not satisfied, the MAX_ERROR is returned. function subError (sub, s) { let StartOnly = false; let EndOnly = false; if (sub.length>1) { if (sub[0] == "^") { StartOnly = true; sub = sub.substring(1); } if (sub[sub.length - 1] == "$") { EndOnly = true; sub = sub.substring(0, sub.length - 1); } } let err = s.indexOf(sub); if (err == -1 || (StartOnly && err != 0) || (EndOnly && err != s.length - sub.length)) { err = MAX_ERROR; } else { if ( sub.includes("->") ) { err = Math.min(s.length - sub.length - err,1); // 0 or 1 // err = 0 if the substring is right-aligned } else { err = Math.min(err,1); // 0 or 1 // err = 0 if the substring } err += Math.abs((s.length - sub.length) / s.length);} return (err) // between 0 and 2, except if MAX_ERROR } // Minimal substring error. In particular, it returns 0 if the string // 'sub' has an exact match with one of the strings in 'line'. function subMinError (sub, line) { let errs = line.map(function (s) { return subError (sub, s); }); return Math.min(...errs); // destructuring assignment } function add (acc, a) { return acc + a; } // for each sub we compute the minimal error within 'line', and then // take the average over all 'subs'. Thus it returns 0 if each sub has // an exact match with one of the strings in 'line'. function subsAvgMinError (subs, line) { let errs = subs.map(function (sub) { return subMinError (sub, line); }); return errs.reduce(add,0) / subs.length; } function formatLine (line) { let li = '
${line[0]}.${line[2]}
`;
if (line.length > 5) {
if ( line[ERR_INDEX] == 0 ) {
li = '${html} : ${line[SIG_INDEX]}${line[DESCR_INDEX]}`; } return (li + html + "