use crate::{
config, override_keybindings, ui, view_with_theme,
wiki::search::{
SearchBuilder, SearchMetadata, SearchProperties, SearchResult, SearchSortOrder,
},
};
use anyhow::{Context, Result};
use cursive::view::{Nameable, Resizable, Scrollable};
use cursive::views::{Button, Dialog, EditView, LinearLayout, SelectView, TextView};
use cursive::{align::HAlign, utils::markup::StyledString, Cursive};
fn build_search() -> SearchBuilder {
SearchBuilder::new()
.info(SearchMetadata::new().total_hits())
.prop(SearchProperties::new().snippet())
.sort(SearchSortOrder::JustMatch)
}
pub fn on_search(siv: &mut Cursive, search_query: String) {
log::info!("on_search was called");
log::info!("searching for '{}'", search_query);
let search = match build_search().query(search_query.clone()).search() {
Ok(search) => search,
Err(error) => {
log::warn!("{}", error);
siv.add_layer(
Dialog::info(
"A Problem occurred while searching. \nCheck the logs for further information",
)
.title("Error")
.title_position(HAlign::Center),
);
log::info!("on_search failed to finish");
return;
}
};
log::debug!("clearing the search bar");
siv.call_on_name("search_bar", |view: &mut EditView| {
view.set_content("");
});
log::info!(
"displaying '{}' out of '{}' search results",
search.results().count(),
search.info().total_hits().unwrap_or(&-1),
);
let mut search_results_view = SelectView::<SearchResult>::new()
.on_select(on_result_select)
.on_submit(ui::article::on_article_submit);
let search_continue_button = {
let query = search_query.to_string();
let offset = search.search_offset().to_owned();
Button::new("Show more results...", move |s| {
if let Err(error) = continue_search(s, &query, &offset) {
log::warn!("{:?}", error);
}
})
.with_name("search_continue_button")
};
let search_results_preview = TextView::empty()
.h_align(cursive::align::HAlign::Left)
.with_name("search_results_preview")
.fixed_width(50);
let mut search_info_view = TextView::empty();
log::debug!("created the search results view, the search continue button, the search results preview and the search info view");
if let Some(total_hits) = search.info().total_hits() {
search_info_view.set_content(format!(
"Found {} articles matching your search",
total_hits
));
}
let first_result = search.results().next().cloned();
log::debug!("adding the results to the search results view");
for search_result in search.results() {
search_results_view.add_item(search_result.title().to_string(), search_result.to_owned())
}
let search_results_layout = LinearLayout::horizontal()
.child(view_with_theme!(
config::CONFIG.theme.search_results,
Dialog::around(override_keybindings!(LinearLayout::vertical()
.child(
search_results_view
.with_name("search_results_view")
.scrollable()
.min_height(10)
)
.child(search_continue_button)),)
))
.child(view_with_theme!(
config::CONFIG.theme.search_preview,
Dialog::around(search_results_preview)
));
log::debug!("created the search results layout");
siv.add_layer(
Dialog::around(
LinearLayout::vertical()
.child(search_results_layout)
.child(search_info_view),
)
.title(format!("Results for \"{}\"", search_query))
.dismiss_button("Back")
.button("Quit", Cursive::quit)
.max_height(20),
);
log::debug!("added the search view to the screen");
log::debug!("sending the callback to select the first search result");
if let Err(error) = siv.cb_sink().send(Box::new(|s| {
if let Some(search_result) = first_result {
on_result_select(s, &search_result);
}
})) {
log::warn!("{:?}", error);
log::info!("on_search failed to finish");
return;
}
log::info!("on_search finished successfully");
}
fn on_result_select(siv: &mut Cursive, item: &SearchResult) {
log::info!(
"on_result_select was called with the item '{}', page id: '{}'",
item.title(),
item.page_id()
);
log::debug!("generating the preview");
let mut preview = StyledString::new();
log::debug!("adding the title to the preview");
preview.append_plain(format!("{}\n", item.title()));
if let Some(snippet) = item.snippet() {
log::debug!("found a snippet for the result, adding it to the preview now");
let splitted_snippet: Vec<&str> = snippet.split(r#"<span class="searchmatch">"#).collect();
for slice in splitted_snippet {
if slice.contains("</span>") {
let split_slice: Vec<&str> = slice.split("</span>").collect();
preview.append(StyledString::styled(
split_slice[0],
config::CONFIG.theme.search_match,
));
preview.append_plain(split_slice[1]);
} else {
preview.append_plain(slice);
}
}
preview.append_plain("...");
}
log::debug!("displaying the generated preivew");
let result = siv.call_on_name("search_results_preview", |view: &mut TextView| {
view.set_content(preview);
});
if result.is_none() {
log::warn!("couldn't find the search results preview view");
log::info!("on_result_select failed to finish");
return;
}
log::info!("on_result_select finished successfully");
}
fn continue_search(siv: &mut Cursive, search_query: &str, search_offset: &usize) -> Result<()> {
log::info!(
"continue_search was called for the query '{}' with the offset '{}'",
search_query,
search_offset
);
log::info!("fetching more results");
let search = build_search()
.query(search_query.to_string())
.offset(*search_offset)
.search()?;
log::debug!("getting the search results view");
let mut search_results_views = siv
.find_name::<SelectView<SearchResult>>("search_results_view")
.with_context(|| {
log::info!("continue_search failed to finish");
"Couldn't find the search results view"
})?;
log::info!(
"adding '{}' results to the search results view",
search.results().count()
);
for search_result in search.results() {
search_results_views.add_item(search_result.title(), search_result.clone())
}
log::debug!("modifying the callback of the search continue button");
let mut search_continue_button = siv
.find_name::<Button>("search_continue_button")
.with_context(|| {
log::info!("continue_search failed to finish");
"Couldn't find the search continue button"
})?;
{
let query = search_query.to_string();
search_continue_button.set_callback(move |s| {
if let Err(error) = continue_search(s, &query, search.search_offset()) {
log::warn!("{:?}", error);
}
});
}
siv.focus_name("search_results_view").with_context(|| {
log::info!("continue_search failed to finish");
"Failed to focus the search results view"
})?;
log::debug!("focussed the search results view");
log::info!("continue_search finished successfully");
Ok(())
}