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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -2892,6 +2898,7 @@ dependencies = [
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"toml",
|
"toml",
|
||||||
"tree_magic_mini",
|
"tree_magic_mini",
|
||||||
|
"urlencoding",
|
||||||
"which",
|
"which",
|
||||||
"wl-clipboard-rs",
|
"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
|
Broken compatibility with wofi is not considered a bug, but issues regarding that will be accepted if you
|
||||||
would rather use worf than wofi.
|
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:
|
It supports various modes:
|
||||||
* Math
|
* Math
|
||||||
* DRun
|
* DRun
|
||||||
* File
|
* File
|
||||||
* Ssh
|
* Ssh
|
||||||
* Run
|
* Run
|
||||||
* Emoji
|
* Emoji
|
||||||
* // WebSearch --> tracked by https://github.com/alexmohr/worf/issues/55
|
|
||||||
* Auto
|
* Auto
|
||||||
|
|
||||||
Auto mode tries to detect the desired mode automatically, to achieve this some modes require a prefix in the search.
|
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"
|
wl-clipboard-rs = "0.9.2"
|
||||||
notify-rust="4.11.7"
|
notify-rust="4.11.7"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
|
urlencoding = "2.1.3"
|
||||||
|
|
|
@ -86,6 +86,9 @@ pub enum Mode {
|
||||||
|
|
||||||
/// Emoji browser
|
/// Emoji browser
|
||||||
Emoji,
|
Emoji,
|
||||||
|
|
||||||
|
/// Open search engine.
|
||||||
|
WebSearch,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
@ -136,6 +139,7 @@ impl FromStr for Mode {
|
||||||
"math" => Ok(Mode::Math),
|
"math" => Ok(Mode::Math),
|
||||||
"ssh" => Ok(Mode::Ssh),
|
"ssh" => Ok(Mode::Ssh),
|
||||||
"emoji" => Ok(Mode::Emoji),
|
"emoji" => Ok(Mode::Emoji),
|
||||||
|
"websearch" => Ok(Mode::WebSearch),
|
||||||
"auto" => Ok(Mode::Auto),
|
"auto" => Ok(Mode::Auto),
|
||||||
_ => Err(Error::InvalidArgument(
|
_ => Err(Error::InvalidArgument(
|
||||||
format!("{s} is not a valid argument, see help for details").to_owned(),
|
format!("{s} is not a valid argument, see help for details").to_owned(),
|
||||||
|
@ -412,6 +416,11 @@ pub struct Config {
|
||||||
/// See `KeyDetectionType` for details.
|
/// See `KeyDetectionType` for details.
|
||||||
#[clap(long = "key-detection-type")]
|
#[clap(long = "key-detection-type")]
|
||||||
key_detection_type: Option<KeyDetectionType>,
|
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 {
|
impl Config {
|
||||||
|
@ -499,6 +508,7 @@ impl Config {
|
||||||
Mode::Auto => "auto".to_owned(),
|
Mode::Auto => "auto".to_owned(),
|
||||||
Mode::Ssh => "ssh".to_owned(),
|
Mode::Ssh => "ssh".to_owned(),
|
||||||
Mode::Emoji => "emoji".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 {
|
pub fn dynamic_lines_limit(&self) -> bool {
|
||||||
self.dynamic_lines_limit.unwrap_or(true)
|
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 {
|
fn default_false() -> bool {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::modes::search::SearchProvider;
|
||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
config::Config,
|
config::Config,
|
||||||
|
@ -20,7 +21,7 @@ enum AutoRunType {
|
||||||
DRun,
|
DRun,
|
||||||
File,
|
File,
|
||||||
Ssh,
|
Ssh,
|
||||||
// WebSearch,
|
WebSearch,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -29,6 +30,7 @@ struct AutoItemProvider {
|
||||||
file: FileItemProvider<AutoRunType>,
|
file: FileItemProvider<AutoRunType>,
|
||||||
math: MathProvider<AutoRunType>,
|
math: MathProvider<AutoRunType>,
|
||||||
ssh: SshProvider<AutoRunType>,
|
ssh: SshProvider<AutoRunType>,
|
||||||
|
search: SearchProvider<AutoRunType>,
|
||||||
last_mode: Option<AutoRunType>,
|
last_mode: Option<AutoRunType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ impl AutoItemProvider {
|
||||||
file: FileItemProvider::new(AutoRunType::File, config.sort_order()),
|
file: FileItemProvider::new(AutoRunType::File, config.sort_order()),
|
||||||
math: MathProvider::new(AutoRunType::Math),
|
math: MathProvider::new(AutoRunType::Math),
|
||||||
ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()),
|
ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()),
|
||||||
|
search: SearchProvider::new(AutoRunType::WebSearch, config.search_query()),
|
||||||
last_mode: None,
|
last_mode: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +69,7 @@ impl AutoItemProvider {
|
||||||
|
|
||||||
fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
|
fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
|
||||||
// Regex for function names (word boundaries to match whole words)
|
// 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)
|
// Regex for strings that start with a number (including decimals)
|
||||||
let starts_with_number = r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)";
|
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))
|
(AutoRunType::File, self.file.get_elements(search_opt))
|
||||||
} else if search.starts_with("ssh") {
|
} else if search.starts_with("ssh") {
|
||||||
(AutoRunType::Ssh, self.ssh.get_elements(search_opt))
|
(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 {
|
} else {
|
||||||
return self.default_auto_elements(search_opt);
|
return self.default_auto_elements(search_opt);
|
||||||
};
|
};
|
||||||
|
@ -131,7 +141,7 @@ pub fn show(config: &Config) -> Result<(), Error> {
|
||||||
provider.clone(),
|
provider.clone(),
|
||||||
true,
|
true,
|
||||||
Some(
|
Some(
|
||||||
vec!["ssh", "emoji", "^\\$\\w+"]
|
vec!["ssh", "emoji", "^\\$\\w+", "^\\?\\s*"]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| Regex::new(s).unwrap())
|
.map(|s| Regex::new(s).unwrap())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -160,6 +170,12 @@ pub fn show(config: &Config) -> Result<(), Error> {
|
||||||
ssh::launch(&selection_result, config)?;
|
ssh::launch(&selection_result, config)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
AutoRunType::WebSearch => {
|
||||||
|
if let Some(action) = selection_result.action {
|
||||||
|
spawn_fork(&action, None)?;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if selection_result.label.starts_with("ssh") {
|
} else if selection_result.label.starts_with("ssh") {
|
||||||
selection_result.label = selection_result.label.chars().skip(4).collect();
|
selection_result.label = selection_result.label.chars().skip(4).collect();
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub mod emoji;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod run;
|
pub mod run;
|
||||||
|
pub mod search;
|
||||||
pub mod ssh;
|
pub mod ssh;
|
||||||
|
|
||||||
pub(crate) fn load_cache(cache_path: Option<PathBuf>) -> (Option<PathBuf>, HashMap<String, i64>) {
|
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::Ssh => modes::ssh::show(&config),
|
||||||
Mode::Emoji => modes::emoji::show(&config),
|
Mode::Emoji => modes::emoji::show(&config),
|
||||||
Mode::Auto => modes::auto::show(&config),
|
Mode::Auto => modes::auto::show(&config),
|
||||||
|
Mode::WebSearch => modes::search::show(&config),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
|
|
Loading…
Add table
Reference in a new issue