chinese search support

This commit is contained in:
Zhou Yue 2021-04-04 03:50:41 +08:00
parent 94f7578576
commit fe1927f344
7 changed files with 129 additions and 19 deletions

View File

@ -44,7 +44,7 @@ tokio = { version = "0.2.18", features = ["macros"], optional = true }
warp = { version = "0.2.2", default-features = false, features = ["websocket"], optional = true } warp = { version = "0.2.2", default-features = false, features = ["websocket"], optional = true }
# Search feature # Search feature
elasticlunr-rs = { version = "2.3", optional = true, default-features = false } elasticlunr-rs = { version = "2.3", optional = true }
ammonia = { version = "3", optional = true } ammonia = { version = "3", optional = true }
[dev-dependencies] [dev-dependencies]

View File

@ -550,7 +550,7 @@ impl Renderer for HtmlHandlebars {
{ {
let search = html_config.search.unwrap_or_default(); let search = html_config.search.unwrap_or_default();
if search.enable { if search.enable {
super::search::create_files(&search, &destination, &book)?; super::search::create_files(&search, &ctx.config.book.language, &destination, &book)?;
} }
} }

View File

@ -12,8 +12,23 @@ use crate::theme::searcher;
use crate::utils; use crate::utils;
/// Creates all files required for search. /// Creates all files required for search.
pub fn create_files(search_config: &Search, destination: &Path, book: &Book) -> Result<()> { pub fn create_files(
let mut index = Index::new(&["title", "body", "breadcrumbs"]); search_config: &Search,
lang: &Option<String>,
destination: &Path,
book: &Book,
) -> Result<()> {
let mut index = match lang {
Some(lang_str) => match lang_str.to_lowercase().as_str() {
"zh" => Index::with_language(
elasticlunr::Language::Chinese,
&["title", "body", "breadcrumbs"],
),
_ => Index::new(&["title", "body", "breadcrumbs"]),
},
None => Index::new(&["title", "body", "breadcrumbs"]),
};
let mut doc_urls = Vec::with_capacity(book.sections.len()); let mut doc_urls = Vec::with_capacity(book.sections.len());
for item in book.iter() { for item in book.iter() {
@ -36,6 +51,7 @@ pub fn create_files(search_config: &Search, destination: &Path, book: &Book) ->
utils::fs::write_file(destination, "searcher.js", searcher::JS)?; utils::fs::write_file(destination, "searcher.js", searcher::JS)?;
utils::fs::write_file(destination, "mark.min.js", searcher::MARK_JS)?; utils::fs::write_file(destination, "mark.min.js", searcher::MARK_JS)?;
utils::fs::write_file(destination, "elasticlunr.min.js", searcher::ELASTICLUNR_JS)?; utils::fs::write_file(destination, "elasticlunr.min.js", searcher::ELASTICLUNR_JS)?;
utils::fs::write_file(destination, "lunr.zh.js", searcher::LUNR_ZH_JS)?;
debug!("Copying search files ✓"); debug!("Copying search files ✓");
} }

View File

@ -271,6 +271,7 @@
{{#if search_js}} {{#if search_js}}
<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script> <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}lunr.zh.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script> <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script> <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
{{/if}} {{/if}}

View File

@ -0,0 +1,89 @@
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(factory)
} else if (typeof exports === 'object') {
/**
* Node. Does not work with strict CommonJS, but
* only CommonJS-like environments that support module.exports,
* like Node.
*/
module.exports = factory()
} else {
// Browser globals (root is window)
factory()(root.lunr);
}
}(this, function () {
return function (lunr) {
if ('undefined' === typeof lunr) {
throw new Error('Lunr is not present. Please include / require Lunr before this script.');
}
/* register specific locale function */
lunr.zh = function () {
this.pipeline.reset();
this.pipeline.add(
lunr.zh.trimmer,
lunr.zh.stopWordFilter,
lunr.zh.stemmer
);
// for lunr version 2
// this is necessary so that every searched word is also stemmed before
// in lunr <= 1 this is not needed, as it is done using the normal pipeline
if (this.searchPipeline) {
this.searchPipeline.reset();
this.searchPipeline.add(lunr.zh.stemmer)
}
};
lunr.zh.tokenizer = function (str) {
if (!arguments.length || str === null || str === undefined) return [];
if (Array.isArray(str)) {
var arr = str.filter(function (token) {
if (token === null || token === undefined) {
return false;
}
return true;
});
arr = arr.map(function (t) {
return lunr.utils.toString(t);
});
var out = [];
arr.forEach(function (item) {
var tokens = item.split(lunr.tokenizer.seperator);
out = out.concat(tokens);
}, this);
return out;
}
return str.toString().trim().split(lunr.tokenizer.seperator);
};
/* lunr trimmer function */
lunr.zh.trimmer = function (_token) {
return _token;
}
lunr.Pipeline.registerFunction(lunr.zh.trimmer, 'trimmer-zh');
/* lunr stemmer function */
lunr.zh.stemmer = (function () {
/* and return a function that stems a word for the current locale */
return function (token) {
return token;
}
})();
lunr.Pipeline.registerFunction(lunr.zh.stemmer, 'stemmer-zh');
lunr.zh.stopWordFilter = function (token) {
return token;
};
lunr.Pipeline.registerFunction(lunr.zh.stopWordFilter, 'stopWordFilter-zh');
};
}))

View File

@ -4,3 +4,4 @@
pub static JS: &[u8] = include_bytes!("searcher.js"); pub static JS: &[u8] = include_bytes!("searcher.js");
pub static MARK_JS: &[u8] = include_bytes!("mark.min.js"); pub static MARK_JS: &[u8] = include_bytes!("mark.min.js");
pub static ELASTICLUNR_JS: &[u8] = include_bytes!("elasticlunr.min.js"); pub static ELASTICLUNR_JS: &[u8] = include_bytes!("elasticlunr.min.js");
pub static LUNR_ZH_JS: &[u8] = include_bytes!("lunr.zh.js");

View File

@ -257,6 +257,9 @@ window.search = window.search || {};
search_options = config.search_options; search_options = config.search_options;
searchbar_outer = config.searchbar_outer; searchbar_outer = config.searchbar_outer;
doc_urls = config.doc_urls; doc_urls = config.doc_urls;
if (config.index.lang == "Chinese") {
elasticlunr.tokenizer = elasticlunr.zh.tokenizer
}
searchindex = elasticlunr.Index.load(config.index); searchindex = elasticlunr.Index.load(config.index);
// Set up events // Set up events
@ -338,8 +341,8 @@ window.search = window.search || {};
unfocusSearchbar(); unfocusSearchbar();
searchresults.firstElementChild.classList.add("focus"); searchresults.firstElementChild.classList.add("focus");
} else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE } else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE
|| e.keyCode === UP_KEYCODE || e.keyCode === UP_KEYCODE
|| e.keyCode === SELECT_KEYCODE)) { || e.keyCode === SELECT_KEYCODE)) {
// not `:focus` because browser does annoying scrolling // not `:focus` because browser does annoying scrolling
var focused = searchresults.querySelector("li.focus"); var focused = searchresults.querySelector("li.focus");
if (!focused) return; if (!focused) return;