support web search, fixes #55
This commit is contained in:
parent
8abcdfcf4a
commit
bf951bf0c8
8 changed files with 107 additions and 8 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -2358,6 +2358,12 @@ version = "0.2.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
|
@ -2892,6 +2898,7 @@ dependencies = [
|
|||
"thiserror 2.0.12",
|
||||
"toml",
|
||||
"tree_magic_mini",
|
||||
"urlencoding",
|
||||
"which",
|
||||
"wl-clipboard-rs",
|
||||
]
|
||||
|
|
|
@ -9,17 +9,13 @@ Worf has more features than wofi though, so there won't be 100% compatibility.
|
|||
Broken compatibility with wofi is not considered a bug, but issues regarding that will be accepted if you
|
||||
would rather use worf than wofi.
|
||||
|
||||
**While the main branch in general is stable and I am using this on a daily basis, it is not ready yet
|
||||
to be used as a library, a lot of things are still moving around.**
|
||||
|
||||
It supports various modes:
|
||||
* Math
|
||||
* DRun
|
||||
* File
|
||||
* Ssh
|
||||
* Run
|
||||
* Emoji
|
||||
* // WebSearch --> tracked by https://github.com/alexmohr/worf/issues/55
|
||||
* Emoji
|
||||
* Auto
|
||||
|
||||
Auto mode tries to detect the desired mode automatically, to achieve this some modes require a prefix in the search.
|
||||
|
|
|
@ -53,3 +53,4 @@ emoji = "0.2.1"
|
|||
wl-clipboard-rs = "0.9.2"
|
||||
notify-rust="4.11.7"
|
||||
thiserror = "2.0.12"
|
||||
urlencoding = "2.1.3"
|
||||
|
|
|
@ -86,6 +86,9 @@ pub enum Mode {
|
|||
|
||||
/// Emoji browser
|
||||
Emoji,
|
||||
|
||||
/// Open search engine.
|
||||
WebSearch,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
@ -136,6 +139,7 @@ impl FromStr for Mode {
|
|||
"math" => Ok(Mode::Math),
|
||||
"ssh" => Ok(Mode::Ssh),
|
||||
"emoji" => Ok(Mode::Emoji),
|
||||
"websearch" => Ok(Mode::WebSearch),
|
||||
"auto" => Ok(Mode::Auto),
|
||||
_ => Err(Error::InvalidArgument(
|
||||
format!("{s} is not a valid argument, see help for details").to_owned(),
|
||||
|
@ -412,6 +416,11 @@ pub struct Config {
|
|||
/// See `KeyDetectionType` for details.
|
||||
#[clap(long = "key-detection-type")]
|
||||
key_detection_type: Option<KeyDetectionType>,
|
||||
|
||||
/// Defines the search query to use.
|
||||
/// Defaults to `<https://duckduckgo.com/?q=>`
|
||||
#[clap(long = "search-query")]
|
||||
search_query: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -499,6 +508,7 @@ impl Config {
|
|||
Mode::Auto => "auto".to_owned(),
|
||||
Mode::Ssh => "ssh".to_owned(),
|
||||
Mode::Emoji => "emoji".to_owned(),
|
||||
Mode::WebSearch => "websearch".to_owned(),
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -646,6 +656,13 @@ impl Config {
|
|||
pub fn dynamic_lines_limit(&self) -> bool {
|
||||
self.dynamic_lines_limit.unwrap_or(true)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn search_query(&self) -> String {
|
||||
self.search_query
|
||||
.clone()
|
||||
.unwrap_or_else(|| "https://duckduckgo.com/?q=".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
fn default_false() -> bool {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use regex::Regex;
|
||||
|
||||
use crate::modes::search::SearchProvider;
|
||||
use crate::{
|
||||
Error,
|
||||
config::Config,
|
||||
|
@ -20,7 +21,7 @@ enum AutoRunType {
|
|||
DRun,
|
||||
File,
|
||||
Ssh,
|
||||
// WebSearch,
|
||||
WebSearch,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -29,6 +30,7 @@ struct AutoItemProvider {
|
|||
file: FileItemProvider<AutoRunType>,
|
||||
math: MathProvider<AutoRunType>,
|
||||
ssh: SshProvider<AutoRunType>,
|
||||
search: SearchProvider<AutoRunType>,
|
||||
last_mode: Option<AutoRunType>,
|
||||
}
|
||||
|
||||
|
@ -44,6 +46,7 @@ impl AutoItemProvider {
|
|||
file: FileItemProvider::new(AutoRunType::File, config.sort_order()),
|
||||
math: MathProvider::new(AutoRunType::Math),
|
||||
ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()),
|
||||
search: SearchProvider::new(AutoRunType::WebSearch, config.search_query()),
|
||||
last_mode: None,
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +69,7 @@ impl AutoItemProvider {
|
|||
|
||||
fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
|
||||
// Regex for function names (word boundaries to match whole words)
|
||||
let math_functions = r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e)\b";
|
||||
let math_functions = r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e|0x|0b|\||&|<<|>>|\^)\b";
|
||||
|
||||
// Regex for strings that start with a number (including decimals)
|
||||
let starts_with_number = r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)";
|
||||
|
@ -90,6 +93,13 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
|
|||
(AutoRunType::File, self.file.get_elements(search_opt))
|
||||
} else if search.starts_with("ssh") {
|
||||
(AutoRunType::Ssh, self.ssh.get_elements(search_opt))
|
||||
} else if search.starts_with('?') {
|
||||
let re = Regex::new(r"^\?\s*").unwrap();
|
||||
let query = re.replace(search, "");
|
||||
(
|
||||
AutoRunType::WebSearch,
|
||||
self.search.get_elements(Some(&query)),
|
||||
)
|
||||
} else {
|
||||
return self.default_auto_elements(search_opt);
|
||||
};
|
||||
|
@ -131,7 +141,7 @@ pub fn show(config: &Config) -> Result<(), Error> {
|
|||
provider.clone(),
|
||||
true,
|
||||
Some(
|
||||
vec!["ssh", "emoji", "^\\$\\w+"]
|
||||
vec!["ssh", "emoji", "^\\$\\w+", "^\\?\\s*"]
|
||||
.into_iter()
|
||||
.map(|s| Regex::new(s).unwrap())
|
||||
.collect(),
|
||||
|
@ -160,6 +170,12 @@ pub fn show(config: &Config) -> Result<(), Error> {
|
|||
ssh::launch(&selection_result, config)?;
|
||||
break;
|
||||
}
|
||||
AutoRunType::WebSearch => {
|
||||
if let Some(action) = selection_result.action {
|
||||
spawn_fork(&action, None)?;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if selection_result.label.starts_with("ssh") {
|
||||
selection_result.label = selection_result.label.chars().skip(4).collect();
|
||||
|
|
|
@ -9,6 +9,7 @@ pub mod emoji;
|
|||
pub mod file;
|
||||
pub mod math;
|
||||
pub mod run;
|
||||
pub mod search;
|
||||
pub mod ssh;
|
||||
|
||||
pub(crate) fn load_cache(cache_path: Option<PathBuf>) -> (Option<PathBuf>, HashMap<String, i64>) {
|
||||
|
|
60
worf/src/lib/modes/search.rs
Normal file
60
worf/src/lib/modes/search.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use urlencoding::encode;
|
||||
|
||||
use crate::desktop::spawn_fork;
|
||||
use crate::{
|
||||
Error,
|
||||
config::Config,
|
||||
gui::{self, ItemProvider, MenuItem},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct SearchProvider<T: Clone> {
|
||||
search_query: String,
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> SearchProvider<T> {
|
||||
pub fn new(data: T, search_query: String) -> Self {
|
||||
Self {
|
||||
search_query,
|
||||
data: data.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> ItemProvider<T> for SearchProvider<T> {
|
||||
fn get_elements(&mut self, query: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
|
||||
if let Some(query) = query {
|
||||
let url = format!("{}{}", self.search_query, encode(query));
|
||||
let run_search = MenuItem::new(
|
||||
format!("Search {query}"),
|
||||
None,
|
||||
Some(format!("xdg-open {url}")),
|
||||
vec![],
|
||||
None,
|
||||
0.0,
|
||||
Some(self.data.clone()),
|
||||
);
|
||||
(true, vec![run_search])
|
||||
} else {
|
||||
(false, vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, Option<Vec<MenuItem<T>>>) {
|
||||
(false, None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows the emoji mode
|
||||
/// # Errors
|
||||
///
|
||||
/// Forwards errors from the gui. See `gui::show` for details.
|
||||
pub fn show(config: &Config) -> Result<(), Error> {
|
||||
let provider = SearchProvider::new(String::new(), config.search_query());
|
||||
let selection_result = gui::show(config.clone(), provider, true, None, None)?;
|
||||
match selection_result.menu.action {
|
||||
None => Err(Error::MissingAction),
|
||||
Some(action) => spawn_fork(&action, None),
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ fn main() -> anyhow::Result<()> {
|
|||
Mode::Ssh => modes::ssh::show(&config),
|
||||
Mode::Emoji => modes::emoji::show(&config),
|
||||
Mode::Auto => modes::auto::show(&config),
|
||||
Mode::WebSearch => modes::search::show(&config),
|
||||
};
|
||||
|
||||
if let Err(err) = result {
|
||||
|
|
Loading…
Add table
Reference in a new issue