[gi-docgen: 2/10] feat: implement fzy searching
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gi-docgen: 2/10] feat: implement fzy searching
- Date: Fri, 9 Apr 2021 18:19:37 +0000 (UTC)
commit 6b86c71343278236fb8b2aa7e6ab9b2ad0388554
Author: Rom Grk <romgrk cc gmail com>
Date: Thu Apr 1 01:45:34 2021 -0400
feat: implement fzy searching
gidocgen/templates/basic/base.html | 9 +-
gidocgen/templates/basic/main.js | 541 ++++++-------------------------------
gidocgen/templates/basic/search.js | 281 ++++++++++++++++++-
3 files changed, 359 insertions(+), 472 deletions(-)
---
diff --git a/gidocgen/templates/basic/base.html b/gidocgen/templates/basic/base.html
index ca3d3cb..d0bd361 100644
--- a/gidocgen/templates/basic/base.html
+++ b/gidocgen/templates/basic/base.html
@@ -42,6 +42,10 @@ SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
{% if CONFIG.urlmap_file %}
<script src="{{ CONFIG.urlmap_file }}"></script>
{% endif %}
+ {% if CONFIG.search_index %}
+ <script src="fzy.js"></script>
+ <script src="search.js"></script>
+ {% endif %}
<script src="stemmer.js"></script>
<script src="main.js"></script>
<!--[if IE]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
@@ -82,11 +86,6 @@ SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
<section id="search" class="content hidden"></section>
- {% if CONFIG.search_index %}
- <script src="fzy.js"></script>
- <script src="search.js"></script>
- {% endif %}
-
<footer>
{% block footer %}{% endblock %}
</footer>
diff --git a/gidocgen/templates/basic/main.js b/gidocgen/templates/basic/main.js
index 46f03fb..990bb8c 100644
--- a/gidocgen/templates/basic/main.js
+++ b/gidocgen/templates/basic/main.js
@@ -1,450 +1,19 @@
// SPDX-FileCopyrightText: 2021 GNOME Foundation
//
// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
+"use strict;"
-// eslint-disable-next-line no-unused-vars
-function hasClass(elem, className) {
- return elem && elem.classList && elem.classList.contains(className);
-}
-
-// eslint-disable-next-line no-unused-vars
-function addClass(elem, className) {
- if (!elem || !elem.classList) {
- return;
- }
- elem.classList.add(className);
-}
-
-// eslint-disable-next-line no-unused-vars
-function removeClass(elem, className) {
- if (!elem || !elem.classList) {
- return;
- }
- elem.classList.remove(className);
-}
-
-function insertAfter(newNode, referenceNode) {
- referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
-}
-
-function onEach(arr, func, reversed) {
- if (arr && arr.length > 0 && func) {
- var length = arr.length;
- var i;
- if (reversed !== true) {
- for (i = 0; i < length; ++i) {
- if (func(arr[i]) === true) {
- return true;
- }
- }
- } else {
- for (i = length - 1; i >= 0; --i) {
- if (func(arr[i]) === true) {
- return true;
- }
- }
- }
- }
- return false;
-}
-
-function onEachLazy(lazyArray, func, reversed) {
- return onEach(
- Array.prototype.slice.call(lazyArray),
- func,
- reversed);
-}
-
-// eslint-disable-next-line no-unused-vars
-function hasOwnProperty(obj, property) {
- return Object.prototype.hasOwnProperty.call(obj, property);
-}
-
-function getSearchElement() {
- return document.getElementById("search");
-}
-
-function getSearchInput() {
- return document.getElementsByClassName("search-input")[0];
-}
-
-function getQueryStringParams() {
- var params = {};
- window.location.search.substring(1).split('&').
- map(function(s) {
- var pair = s.split('=');
- params[decodeURIComponent(pair[0])] =
- typeof pair[1] === 'undefined' ? null : decodeURIComponent(pair[1]);
- });
- return params;
-}
-
-function getQuery(query) {
- var query_lower = query.toLowerCase(),
- query_split = query_lower.split('+');
-
- query_split = query_split.filter(function(chunk) { return chunk !== ""; });
-
- return {
- raw: query,
- terms: query_split,
- user: query_split.join(' '),
- }
-}
-
-window.initSearch = function(searchIndex) {
- var search_input = getSearchInput();
- var params = getQueryStringParams();
-
- var searchSymbols = searchIndex["symbols"];
- var searchTerms = searchIndex["terms"];
-
- if (search_input.value === "") {
- search_input.value === params.q || "";
- }
-
- function runQuery(query) {
- function getDocumentFromId(id) {
- if (typeof id === "number") {
- return searchSymbols[id];
- }
- return null;
- }
-
- function getLinkForDocument(doc) {
- switch (doc.type) {
- case "alias":
- return "alias." + doc.name + ".html";
- case "bitfield":
- return "flags." + doc.name + ".html";
- case "callback":
- return "callback." + doc.name + ".html";
- case "class":
- return "class." + doc.name + ".html";
- case "class_method":
- return "class_method." + doc.type_name + "." + doc.name + ".html";
- case "constant":
- return "const." + doc.name + ".html";
- case "ctor":
- return "ctor." + doc.type_name + "." + doc.name + ".html";
- case "domain":
- return "error." + doc.name + ".html";
- case "enum":
- return "enum." + doc.name + ".html";
- case "function":
- return "func." + doc.name + ".html";
- case "function_macro":
- return "func." + doc.name + ".html";
- case "interface":
- return "iface." + doc.name + ".html";
- case "method":
- return "method." + doc.type_name + "." + doc.name + ".html";
- case "property":
- return "property." + doc.type_name + "." + doc.name + ".html";
- case "record":
- return "struct." + doc.name + ".html";
- case "signal":
- return "signal." + doc.type_name + "." + doc.name + ".html";
- case "type_func":
- return "type_func." + doc.type_name + "." + doc.name + ".html";
- case "union":
- return "union." + doc.name + ".html";
- case "vfunc":
- return "vfunc." + doc.type_name + "." + doc.name + ".html";
- }
-
- return null;
- }
-
- function getTextForDocument(doc) {
- switch (doc.type) {
- case "alias":
- case "bitfield":
- case "class":
- case "domain":
- case "enum":
- case "interface":
- case "record":
- case "union":
- return doc.ctype;
- case "class_method":
- case "constant":
- case "ctor":
- case "function":
- case "function_macro":
- case "method":
- case "type_func":
- return doc.ident;
- case "property":
- return doc.type_name + ":" + doc.name;
- case "signal":
- return doc.type_name + "::" + doc.name;
- case "vfunc":
- return doc.type_name + "." + doc.name;
- case "callback":
- return doc.name;
- }
-
- return null;
- }
- var queryTypes = [
- "alias",
- "bitfield",
- "callback",
- "class",
- "constant",
- "ctor",
- "domain",
- "enum",
- "function_macro",
- "function",
- "interface",
- "method",
- "property",
- "record",
- "signal",
- "type_func",
- "union",
- "vfunc",
- ];
+const urlMap = new Map(typeof baseURLs !== 'undefined' ? baseURLs : []);
- var query_re = new RegExp("^(" + queryTypes.join('|') + ")\\s*:\\s*", 'i');
+window.addEventListener("hashchange", onDidHashChange);
+window.addEventListener("load", onDidLoad, false);
- function matchType(term) {
- var matches = term.match(query_re);
- if (matches) {
- var type = matches[1];
- var query = term.substring(matches[0].length);
- return {
- type: type,
- term: query,
- }
- }
-
- return {
- type: null,
- term: term,
- }
- }
-
- const PREDICATE_ALL = 0;
- const PREDICATE_ANY = 1;
-
- function uniqueResults(arrays) {
- var unique = [];
-
- for (var it = 0; it < arrays.length; it++) {
- arrays[it].forEach(function(e) {
- if (unique.findIndex((doc) => doc.type === e.type && doc.name === e.name) == -1) {
- unique.push(e);
- }
- });
- }
-
- return unique;
- }
-
- function mergeArrays(arrays, predicate) {
- if (predicate === PREDICATE_ANY) {
- return uniqueResults(arrays);
- }
-
- if (predicate === PREDICATE_ALL) {
- // find the smallest array
- var smallest = 0;
- for (var it = 1; it < arrays.length; it++) {
- if (arrays[it].length < smallest) {
- smallest = it;
- }
- }
-
- if (smallest > arrays.length) {
- return [];
- }
-
- var results = [];
-
- // check for elements present in all arrays
- arrays[smallest].forEach(function(e) {
- var found = [];
- for (var it = 0; it < arrays.length; it++) {
- if (it == smallest) {
- found.push(smallest);
- } else {
- if (arrays[it].findIndex((doc) => doc.type === e.type && doc.name ===
e.name) != -1) {
- found.push(it);
- }
- }
- }
- if (found.length === arrays.length) {
- results.push(e);
- }
- });
-
- return results;
- }
-
- return [];
- }
-
- var termIndex = 0;
- var results = [];
-
- query.terms.forEach(function(term) {
- var q = matchType(term);
- var stemmed_term = stemmer(q.term);
- if (searchTerms.hasOwnProperty(stemmed_term)) {
- var docs = searchTerms[stemmed_term];
-
- results[termIndex] = [];
-
- docs.forEach(function(id) {
- var doc = getDocumentFromId(id);
-
- if (doc !== null) {
- var res = {
- name: doc.name,
- type: doc.type,
- text: getTextForDocument(doc),
- href: getLinkForDocument(doc),
- summary: doc.summary,
- };
-
- if (q.type === null || (q.type === doc.type)) {
- results[termIndex].push(res);
- }
- }
- });
-
- termIndex += 1;
- }
- });
-
- if (termIndex == 0) {
- return {
- all: [],
- any: [],
- }
- } else if (termIndex == 1) {
- return {
- all: results[0],
- any: results[0],
- }
- } else {
- return {
- all: mergeArrays(results, PREDICATE_ALL),
- any: mergeArrays(results, PREDICATE_ANY),
- }
- }
- }
-
- function showSearchResults(search) {
- if (search === null || typeof search === 'undefined') {
- search = getSearchElement();
- }
-
- addClass(main, "hidden");
- removeClass(search, "hidden");
- }
-
- function hideSearchResults(search) {
- if (search === null || typeof search === 'undefined') {
- search = getSearchElement();
- }
-
- addClass(search, "hidden");
- removeClass(search, "hidden");
- }
-
- function addResults(results) {
- var output = "";
-
- if (results.length > 0) {
- output += "<table class=\"results\">" +
- "<tr><th>Name</th><th>Description</th></tr>";
-
- results.forEach(function(item) {
- output += "<tr>" +
- "<td class=\"result " + item.type + "\">" +
- "<a href=\"" + item.href + "\"><code>" + item.text + "</code></a>" +
- "</td>" +
- "<td>" + item.summary + "</td>" +
- "</tr>";
- });
-
- output += "</table>";
- } else {
- output = "No results found.";
- }
-
- return output;
- }
-
- function showResults(query, results) {
- var search = getSearchElement();
-
- var res = [];
- var len = 0;
- if (results.all.length > 0) {
- res = results.all;
- len = results.all.length;
- } else {
- res = results.any;
- len = results.any.length;
- }
-
- var output = "<h1>Results for "" + query.user + "" (" + len + ")</h1>" +
- "<div id=\"search-results\">" +
- addResults(res) +
- "</div>";
-
- search.innerHTML = output;
- showSearchResults(search);
- }
-
- function search() {
- var query = getQuery(getQueryStringParams().q);
-
- if (search_input.value === "" && query) {
- if (query.terms.length === 0) {
- return;
- }
-
- search_input.value = query.user;
- }
-
- window.title = "Results for: " + query.user;
-
- showResults(query, runQuery(query));
- }
-
- window.onpageshow = function() {
- var query = getQuery(getQueryStringParams().q);
- if (search_input.value === "" && query) {
- search_input.value = query.user;
- }
- search();
- };
-
- if (getQueryStringParams().q) {
- search();
- }
-};
-
-window.addEventListener("load", function() {
- "use strict;"
-
- var main = document.getElementById("main");
- var btnToTop = document.getElementById("btn-to-top");
-
- var searchInput = getSearchInput();
+function onDidLoad() {
+ const btnToTop = document.getElementById("btn-to-top");
function labelForToggleButton(isCollapsed) {
- if (isCollapsed) {
- return "+";
- }
- return "\u2212";
+ return isCollapsed ? "+" : "\u2212";
}
function createToggle(isCollapsed) {
@@ -479,10 +48,10 @@ window.addEventListener("load", function() {
}
onEachLazy(document.getElementsByClassName("toggle-wrapper"), function(e) {
- let sectionHeader = e.querySelector(".section-header");
- let fragmentMatches = sectionHeader !== null && location.hash === "#" +
sectionHeader.getAttribute('id');
- collapsedByDefault = hasClass(e, "default-hide") && !fragmentMatches;
- var toggle = createToggle(collapsedByDefault);
+ const sectionHeader = e.querySelector(".section-header");
+ const fragmentMatches = sectionHeader !== null && location.hash === "#" +
sectionHeader.getAttribute('id');
+ const collapsedByDefault = hasClass(e, "default-hide") && !fragmentMatches;
+ const toggle = createToggle(collapsedByDefault);
toggle.onclick = toggleClicked;
e.insertBefore(toggle, e.firstChild);
if (collapsedByDefault) {
@@ -510,26 +79,18 @@ window.addEventListener("load", function() {
}
function resolveNamespaceLink(namespace) {
- try {
- let urlMap = new Map(baseURLs);
- if (urlMap.has(namespace)) {
- return urlMap.get(namespace);
- }
- return '';
- } catch (e) {
- return '';
- }
+ return urlMap.get(namespace);
}
- window.onscroll = toggleScrollButton;
- btnToTop.onclick = scrollBackTop;
+ window.addEventListener('scroll', toggleScrollButton);
+ btnToTop.addEventListener('click', scrollBackTop);
onEachLazy(document.getElementsByClassName("external"), function(e) {
if (e.tagName == "A" && e.dataset.hasOwnProperty('namespace')) {
var data_namespace = e.dataset.namespace
var data_link = e.dataset.link
var base_url = resolveNamespaceLink(data_namespace)
- if (base_url !== '') {
+ if (base_url !== undefined) {
e.href = base_url + data_link;
} else {
e.title = "No reference to the " + data_namespace + " namespace";
@@ -553,14 +114,12 @@ window.addEventListener("load", function() {
});
}
- if (window.buildIndex) {
- window.buildIndex('index.json');
+ if (window.onInitSearch) {
+ window.onInitSearch()
}
-}, false);
-
-window.addEventListener("hashchange", function() {
- "use strict;"
+}
+function onDidHashChange() {
// When URL fragment changes to ID of a collapsible section,
// expand it when it is collapsed.
// This is useful for clicking section links in the sidebar on the index page.
@@ -574,4 +133,64 @@ window.addEventListener("hashchange", function() {
}
}
}
-});
+}
+
+// Helpers
+
+// eslint-disable-next-line no-unused-vars
+function hasClass(elem, className) {
+ return elem && elem.classList && elem.classList.contains(className);
+}
+
+// eslint-disable-next-line no-unused-vars
+function addClass(elem, className) {
+ if (!elem || !elem.classList) {
+ return;
+ }
+ elem.classList.add(className);
+}
+
+// eslint-disable-next-line no-unused-vars
+function removeClass(elem, className) {
+ if (!elem || !elem.classList) {
+ return;
+ }
+ elem.classList.remove(className);
+}
+
+function insertAfter(newNode, referenceNode) {
+ referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
+}
+
+function onEach(arr, func, reversed) {
+ if (arr && arr.length > 0 && func) {
+ var length = arr.length;
+ var i;
+ if (reversed !== true) {
+ for (i = 0; i < length; ++i) {
+ if (func(arr[i]) === true) {
+ return true;
+ }
+ }
+ } else {
+ for (i = length - 1; i >= 0; --i) {
+ if (func(arr[i]) === true) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+function onEachLazy(lazyArray, func, reversed) {
+ return onEach(
+ Array.prototype.slice.call(lazyArray),
+ func,
+ reversed);
+}
+
+// eslint-disable-next-line no-unused-vars
+function hasOwnProperty(obj, property) {
+ return Object.prototype.hasOwnProperty.call(obj, property);
+}
diff --git a/gidocgen/templates/basic/search.js b/gidocgen/templates/basic/search.js
index 7ff137d..c26efa5 100644
--- a/gidocgen/templates/basic/search.js
+++ b/gidocgen/templates/basic/search.js
@@ -2,18 +2,287 @@
//
// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
-window.buildIndex = function (rawSearchIndex) {
- let request = new XMLHttpRequest();
- request.open('GET', rawSearchIndex, true);
+(function() {
+
+const QUERY_TYPES = [
+ "alias",
+ "bitfield",
+ "callback",
+ "class",
+ "constant",
+ "ctor",
+ "domain",
+ "enum",
+ "function_macro",
+ "function",
+ "interface",
+ "method",
+ "property",
+ "record",
+ "signal",
+ "type_func",
+ "union",
+ "vfunc",
+];
+const QUERY_PATTERN = new RegExp("^(" + QUERY_TYPES.join('|') + ")\\s*:\\s*", 'i');
+
+
+const fzy = window.fzy;
+const searchParams = getSearchParams();
+
+let searchIndex = undefined;
+
+// Exports
+window.onInitSearch = onInitSearch;
+
+/* Event handlers */
+
+function onInitSearch() {
+ fetchJSON('index.json', onDidLoadSearchIndex);
+}
+
+function onDidLoadSearchIndex(data) {
+ const searchInput = getSearchInput();
+ const searchIndex = new SearchIndex(data)
+
+ if (searchInput.value === "") {
+ searchInput.value === searchParams.q || "";
+ }
+
+ function runQuery(query) {
+ const q = matchQuery(query);
+ const docs = searchIndex.searchDocs(q.term, q.type);
+
+ const results = docs.map(function(doc) {
+ return {
+ name: doc.name,
+ type: doc.type,
+ text: getTextForDocument(doc, searchIndex.meta),
+ href: getLinkForDocument(doc),
+ summary: doc.summary,
+ };
+ });
+
+ return results;
+ }
+
+ function search() {
+ const query = searchParams.q;
+ if (searchInput.value === "" && query) {
+ searchInput.value = query;
+ }
+ window.title = "Results for: " + query.user;
+ showResults(query, runQuery(query));
+ }
+
+ window.onpageshow = function() {
+ var query = getQuery(searchParams.q);
+ if (searchInput.value === "" && query) {
+ searchInput.value = query.user;
+ }
+ search();
+ };
+
+ if (searchParams.q) {
+ search();
+ }
+};
+
+
+/* Rendering */
+
+function showSearchResults(search) {
+ if (search === null || typeof search === 'undefined') {
+ search = getSearchElement();
+ }
+
+ addClass(main, "hidden");
+ removeClass(search, "hidden");
+
+}
+
+function hideSearchResults(search) {
+ if (search === null || typeof search === 'undefined') {
+ search = getSearchElement();
+ }
+
+ addClass(search, "hidden");
+ removeClass(search, "hidden");
+}
+
+function addResults(results) {
+ var output = "";
+
+ if (results.length > 0) {
+ output += "<table class=\"results\">" +
+ "<tr><th>Name</th><th>Description</th></tr>";
+
+ results.forEach(function(item) {
+ output += "<tr>" +
+ "<td class=\"result " + item.type + "\">" +
+ "<a href=\"" + item.href + "\"><code>" + item.text + "</code></a>" +
+ "</td>" +
+ "<td>" + item.summary + "</td>" +
+ "</tr>";
+ });
+
+ output += "</table>";
+ } else {
+ output = "No results found.";
+ }
+
+ return output;
+}
+
+function showResults(query, results) {
+ const search = getSearchElement();
+ const output =
+ "<h1>Results for "" + query + "" (" + results.length + ")</h1>" +
+ "<div id=\"search-results\">" +
+ addResults(results) +
+ "</div>";
+
+ search.innerHTML = output;
+ showSearchResults(search);
+}
+
+
+/* Search data instance */
+
+function SearchIndex(searchIndex) {
+ this.symbols = searchIndex.symbols;
+ this.meta = searchIndex.meta;
+}
+SearchIndex.prototype.searchDocs = function searchDocs(term, type) {
+ const filteredSymbols = type ? this.symbols.filter(s => s.type === type) : this.symbols;
+ const results = fzy.filter(term, filteredSymbols, doc => getTextForDocument(doc, this.meta))
+ return results.map(i => i.item)
+}
+SearchIndex.prototype.getDocumentFromId = function getDocumentFromId(id) {
+ if (typeof id === "number") {
+ return this.searchIndex.symbols[id];
+ }
+ return null;
+}
+
+
+/* Search metadata selectors */
+
+function getLinkForDocument(doc) {
+ switch (doc.type) {
+ case "alias": return "alias." + doc.name + ".html";
+ case "bitfield": return "flags." + doc.name + ".html";
+ case "callback": return "callback." + doc.name + ".html";
+ case "class": return "class." + doc.name + ".html";
+ case "class_method": return "class_method." + doc.type_name + "." + doc.name + ".html";
+ case "constant": return "const." + doc.name + ".html";
+ case "ctor": return "ctor." + doc.type_name + "." + doc.name + ".html";
+ case "domain": return "error." + doc.name + ".html";
+ case "enum": return "enum." + doc.name + ".html";
+ case "function": return "func." + doc.name + ".html";
+ case "function_macro": return "func." + doc.name + ".html";
+ case "interface": return "iface." + doc.name + ".html";
+ case "method": return "method." + doc.type_name + "." + doc.name + ".html";
+ case "property": return "property." + doc.type_name + "." + doc.name + ".html";
+ case "record": return "struct." + doc.name + ".html";
+ case "signal": return "signal." + doc.type_name + "." + doc.name + ".html";
+ case "type_func": return "type_func." + doc.type_name + "." + doc.name + ".html";
+ case "union": return "union." + doc.name + ".html";
+ case "vfunc": return "vfunc." + doc.type_name + "." + doc.name + ".html";
+ }
+ return null;
+}
+
+function getTextForDocument(doc, meta) {
+ switch (doc.type) {
+ case "alias":
+ case "bitfield":
+ case "class":
+ case "domain":
+ case "enum":
+ case "interface":
+ case "record":
+ case "union":
+ return doc.ctype;
+ case "class_method":
+ case "constant":
+ case "ctor":
+ case "function":
+ case "function_macro":
+ case "method":
+ case "type_func":
+ return doc.ident;
+
+ // NOTE: meta.ns added for more consistent results, otherwise
+ // searching for "Button" would return all signals, properties
+ // and vfuncs (eg "Button.clicked") before the actual object
+ // (eg "GtkButton") because "Button" matches higher with starting
+ // sequences.
+ case "property":
+ return meta.ns + doc.type_name + ":" + doc.name;
+ case "signal":
+ return meta.ns + doc.type_name + "::" + doc.name;
+ case "vfunc":
+ return meta.ns + doc.type_name + "." + doc.name;
+
+ case "callback":
+ return doc.name;
+ }
+
+ return null;
+}
+
+
+// Helpers
+
+function fetchJSON(url, callback) {
+ const request = new XMLHttpRequest();
+ request.open('GET', url, true);
request.onreadystatechange = function() {
if (request.readyState === XMLHttpRequest.DONE) {
- var status = request.status;
+ const status = request.status;
if (status === 0 || (status >= 200 && status < 400)) {
- var searchIndex = JSON.parse(request.responseText);
- window.initSearch(searchIndex);
+ callback(JSON.parse(request.responseText));
}
}
}
request.send(null);
}
+
+function getSearchElement() {
+ return document.getElementById("search");
+}
+
+function getSearchInput() {
+ return document.getElementsByClassName("search-input")[0];
+}
+
+function getSearchParams() {
+ const params = {};
+ window.location.search.substring(1).split('&')
+ .map(function(s) {
+ const pair = s.split('=');
+ params[decodeURIComponent(pair[0])] =
+ typeof pair[1] === 'undefined' ? null : decodeURIComponent(pair[1].replace(/\+/g, '%20'));
+ });
+ return params;
+}
+
+function matchQuery(input) {
+ let type = null
+ let term = input
+
+ const matches = term.match(QUERY_PATTERN);
+ if (matches) {
+ type = matches[1];
+ term = term.substring(matches[0].length);
+ }
+
+ // Remove all spaces, fzy will handle things gracefully.
+ term = term.replace(/\s+/g, '')
+
+ return { type: type, term: term }
+}
+
+})()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]