vuepress-plugin-full-text-search2
Version:
VuePress v2 plugin that adds full-text search box.
177 lines (176 loc) • 4.85 kB
JavaScript
// src/client/engine.ts
import {
searchIndex as searchIndexRaw,
UPD_NAME
} from "@internal/vuepress-plugin-full-text-search2-search-index";
import { computed, ref, watch } from "vue";
var searchIndex = ref(searchIndexRaw);
var pathToPage = computed(() => {
const map = /* @__PURE__ */ new Map();
for (const page of searchIndex.value) {
map.set(page.path, page);
}
return map;
});
if (
// @ts-expect-error -- ignore
import.meta.webpackHot || // @ts-expect-error -- ignore
import.meta.hot
) {
__VUE_HMR_RUNTIME__[UPD_NAME] = (data) => {
searchIndex.value = data;
};
}
function useSuggestions(query) {
const suggestions = ref([]);
let id = null;
watch(query, () => {
if (id) {
clearTimeout(id);
}
id = setTimeout(search, 100);
});
return suggestions;
function search() {
const queryStr = query.value.toLowerCase().trim();
if (!queryStr) {
suggestions.value = [];
return;
}
const suggestionResults = /* @__PURE__ */ new Map();
const suggestionSubTitles = /* @__PURE__ */ new Set();
for (const page of searchIndex.value) {
for (const suggest of extractSuggestions(page, queryStr)) {
suggestionSubTitles.add(suggest.parentPageTitle);
let list = suggestionResults.get(suggest.parentPageTitle);
if (!list) {
list = [];
suggestionResults.set(suggest.parentPageTitle, list);
}
list.push(suggest);
}
}
const sortedSuggestionSubTitles = [...suggestionSubTitles].sort((a, b) => {
const listA = suggestionResults.get(a);
const listB = suggestionResults.get(b);
return listB.length - listA.length;
});
suggestions.value = [...suggestionResults].flatMap(([, s]) => s).sort(
(a, b) => a.parentPagePriority - b.parentPagePriority || sortedSuggestionSubTitles.indexOf(a.parentPageTitle) - sortedSuggestionSubTitles.indexOf(b.parentPageTitle) || a.priority - b.priority
);
}
}
function* extractSuggestions(page, queryStr) {
const matchTitle = buildMatch(page.title, queryStr);
if (matchTitle) {
yield {
path: page.path,
parentPageTitle: getParentPageTitle(page),
title: page.title,
display: matchTitle,
page,
content: null,
parentPagePriority: 1,
priority: 1
};
return;
}
for (const content of page.contents) {
const matchHeader = buildMatch(content.header, queryStr);
if (matchHeader) {
yield {
path: page.path + (content.slug ? `#${content.slug}` : ""),
parentPageTitle: getParentPageTitle(page),
title: page.title,
display: matchHeader,
page,
content: null,
parentPagePriority: 10,
priority: 2
};
continue;
}
const matchContent = buildMatch(content.content, queryStr);
if (matchContent) {
yield {
path: page.path + (content.slug ? `#${content.slug}` : ""),
parentPageTitle: getParentPageTitle(page),
title: page.title,
display: [
{
type: "header",
str: `${content.header}
`
},
...matchContent
],
page,
content: null,
parentPagePriority: 10,
priority: 10
};
}
}
}
function getParentPageTitle(page) {
const pathParts = page.path.split("/");
let parentPagePath = "/";
if (pathParts[1])
parentPagePath = `/${pathParts[1]}/`;
const parentPage = pathToPage.value.get(parentPagePath) || page;
return parentPage.title;
}
function buildMatch(text, queryStr) {
const result = [];
let totalLength = 0;
const lower = text.toLowerCase().replace(/\s/gu, " ");
let start = 0;
let matchIndex = lower.indexOf(queryStr, start);
if (matchIndex < 0) {
return null;
}
while (matchIndex >= 0) {
const end = matchIndex + queryStr.length;
append(text.slice(start, matchIndex), "normal");
append(text.slice(matchIndex, end), "highlight");
start = end;
matchIndex = lower.indexOf(queryStr, start);
if (totalLength > 100)
break;
}
append(text.slice(start), "normal");
return result.filter((w) => w.str);
function append(s, type) {
let str = s;
if (type === "normal") {
if (str.length > 100) {
if (totalLength === 0) {
str = `\u2026 ${str.slice(-10)}`;
}
}
}
let needEllipsis = false;
if (totalLength + str.length > 100) {
if (result.some((w) => w.type === "ellipsis")) {
return;
}
str = str.slice(0, Math.max(100 - totalLength, 1));
needEllipsis = true;
}
result.push({
type,
str
});
totalLength += str.length;
if (needEllipsis) {
result.push({
type: "ellipsis",
str: " \u2026"
});
totalLength += 2;
}
}
}
export {
useSuggestions
};