use sort mode everywhere
This commit is contained in:
parent
99b893c468
commit
4c8a9771db
3 changed files with 78 additions and 36 deletions
|
@ -43,6 +43,13 @@ pub enum WrapMode {
|
||||||
Inherit,
|
Inherit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum SortOrder {
|
||||||
|
Default,
|
||||||
|
Alphabetical
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
/// searches `$PATH` for executables and allows them to be run by selecting them.
|
/// searches `$PATH` for executables and allows them to be run by selecting them.
|
||||||
|
@ -125,6 +132,22 @@ impl FromStr for WrapMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl FromStr for SortOrder {
|
||||||
|
type Err = ArgsError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"alphabetical" => Ok(SortOrder::Alphabetical),
|
||||||
|
"default" => Ok(SortOrder::Default),
|
||||||
|
_ => Err(ArgsError::InvalidParameter(
|
||||||
|
format!("{s} is not a valid argument, see help for details").to_owned(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Parser)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Parser)]
|
||||||
#[clap(about = "Worf is a wofi clone written in rust, it aims to be a drop-in replacement")]
|
#[clap(about = "Worf is a wofi clone written in rust, it aims to be a drop-in replacement")]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -238,8 +261,8 @@ pub struct Config {
|
||||||
#[clap(short = 'w', long = "columns")]
|
#[clap(short = 'w', long = "columns")]
|
||||||
columns: Option<u32>,
|
columns: Option<u32>,
|
||||||
|
|
||||||
#[clap(short = 'O', long = "sort-order")] // todo support this
|
#[clap(short = 'O', long = "sort-order")]
|
||||||
sort_order: Option<String>,
|
sort_order: Option<SortOrder>,
|
||||||
|
|
||||||
#[clap(short = 'Q', long = "search")]
|
#[clap(short = 'Q', long = "search")]
|
||||||
search: Option<String>,
|
search: Option<String>,
|
||||||
|
@ -498,6 +521,11 @@ impl Config {
|
||||||
pub fn no_actions(&self) -> bool {
|
pub fn no_actions(&self) -> bool {
|
||||||
self.no_actions.unwrap_or(false)
|
self.no_actions.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn sort_order(&self) -> SortOrder {
|
||||||
|
self.sort_order.clone().unwrap_or(SortOrder::Alphabetical)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_false() -> bool {
|
fn default_false() -> bool {
|
||||||
|
|
|
@ -25,7 +25,7 @@ use gtk4_layer_shell::{Edge, KeyboardMode, LayerShell};
|
||||||
use log;
|
use log;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::config::{Anchor, Config, MatchMethod, WrapMode};
|
use crate::config::{Anchor, Config, MatchMethod, SortOrder, WrapMode};
|
||||||
use crate::desktop::known_image_extension_regex_pattern;
|
use crate::desktop::known_image_extension_regex_pattern;
|
||||||
use crate::{Error, config, desktop};
|
use crate::{Error, config, desktop};
|
||||||
|
|
||||||
|
@ -1069,17 +1069,22 @@ fn percent_or_absolute(value: &str, base_value: i32) -> Option<i32> {
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
pub fn sort_menu_items_alphabetically_honor_initial_score<T: Clone>(items: &mut [MenuItem<T>]) {
|
pub fn apply_sort<T: Clone>(items: &mut [MenuItem<T>], order: &SortOrder) {
|
||||||
let special_score = items.len() as f64;
|
match order {
|
||||||
let mut regular_score = 0.0;
|
SortOrder::Default => {}
|
||||||
items.sort_by(|l, r| r.label.cmp(&l.label));
|
SortOrder::Alphabetical => {
|
||||||
|
let special_score = items.len() as f64;
|
||||||
|
let mut regular_score = 0.0;
|
||||||
|
items.sort_by(|l, r| r.label.cmp(&l.label));
|
||||||
|
|
||||||
for item in items.iter_mut() {
|
for item in items.iter_mut() {
|
||||||
if item.initial_sort_score == 0.0 {
|
if item.initial_sort_score == 0.0 {
|
||||||
item.initial_sort_score += regular_score;
|
item.initial_sort_score += regular_score;
|
||||||
regular_score += 1.0;
|
regular_score += 1.0;
|
||||||
} else {
|
} else {
|
||||||
item.initial_sort_score += special_score;
|
item.initial_sort_score += special_score;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::config::{Config, expand_path};
|
use crate::config::{Config, expand_path, SortOrder};
|
||||||
use crate::desktop::{
|
use crate::desktop::{
|
||||||
copy_to_clipboard, create_file_if_not_exists, find_desktop_files, get_locale_variants,
|
copy_to_clipboard, create_file_if_not_exists, find_desktop_files, get_locale_variants,
|
||||||
is_executable, load_cache_file, lookup_name_with_locale, save_cache_file, spawn_fork,
|
is_executable, load_cache_file, lookup_name_with_locale, save_cache_file, spawn_fork,
|
||||||
|
@ -30,10 +30,11 @@ struct DRunProvider<T: Clone> {
|
||||||
cache: HashMap<String, i64>,
|
cache: HashMap<String, i64>,
|
||||||
data: T,
|
data: T,
|
||||||
no_actions: bool,
|
no_actions: bool,
|
||||||
|
sort_order: SortOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Send + Sync> DRunProvider<T> {
|
impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
fn new(menu_item_data: T, no_actions: bool) -> Self {
|
fn new(menu_item_data: T, no_actions: bool, sort_order: SortOrder) -> Self {
|
||||||
let (cache_path, d_run_cache) = load_d_run_cache();
|
let (cache_path, d_run_cache) = load_d_run_cache();
|
||||||
DRunProvider {
|
DRunProvider {
|
||||||
items: None,
|
items: None,
|
||||||
|
@ -41,6 +42,7 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
cache: d_run_cache,
|
cache: d_run_cache,
|
||||||
data: menu_item_data,
|
data: menu_item_data,
|
||||||
no_actions,
|
no_actions,
|
||||||
|
sort_order,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +146,7 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
start.elapsed().as_millis()
|
start.elapsed().as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
gui::sort_menu_items_alphabetically_honor_initial_score(&mut entries);
|
gui::apply_sort(&mut entries, &self.sort_order);
|
||||||
entries
|
entries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,15 +169,17 @@ struct RunProvider {
|
||||||
items: Option<Vec<MenuItem<i32>>>,
|
items: Option<Vec<MenuItem<i32>>>,
|
||||||
cache_path: Option<PathBuf>,
|
cache_path: Option<PathBuf>,
|
||||||
cache: HashMap<String, i64>,
|
cache: HashMap<String, i64>,
|
||||||
|
sort_order: SortOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunProvider {
|
impl RunProvider {
|
||||||
fn new() -> Self {
|
fn new(sort_order: SortOrder) -> Self {
|
||||||
let (cache_path, d_run_cache) = load_run_cache();
|
let (cache_path, d_run_cache) = load_run_cache();
|
||||||
RunProvider {
|
RunProvider {
|
||||||
items: None,
|
items: None,
|
||||||
cache_path,
|
cache_path,
|
||||||
cache: d_run_cache,
|
cache: d_run_cache,
|
||||||
|
sort_order,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +230,7 @@ impl RunProvider {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
gui::sort_menu_items_alphabetically_honor_initial_score(&mut entries);
|
gui::apply_sort(&mut entries, &self.sort_order);
|
||||||
entries
|
entries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,13 +252,15 @@ impl<T: Clone + Send + Sync> ItemProvider<T> for DRunProvider<T> {
|
||||||
struct FileItemProvider<T: Clone> {
|
struct FileItemProvider<T: Clone> {
|
||||||
last_result: Option<Vec<MenuItem<T>>>,
|
last_result: Option<Vec<MenuItem<T>>>,
|
||||||
menu_item_data: T,
|
menu_item_data: T,
|
||||||
|
sort_order: SortOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> FileItemProvider<T> {
|
impl<T: Clone> FileItemProvider<T> {
|
||||||
fn new(menu_item_data: T) -> Self {
|
fn new(menu_item_data: T, sort_order: SortOrder) -> Self {
|
||||||
FileItemProvider {
|
FileItemProvider {
|
||||||
last_result: None,
|
last_result: None,
|
||||||
menu_item_data,
|
menu_item_data,
|
||||||
|
sort_order,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +415,7 @@ impl<T: Clone> ItemProvider<T> for FileItemProvider<T> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
gui::sort_menu_items_alphabetically_honor_initial_score(&mut items);
|
gui::apply_sort(&mut items, &self.sort_order);
|
||||||
|
|
||||||
self.last_result = Some(items.clone());
|
self.last_result = Some(items.clone());
|
||||||
(true, items)
|
(true, items)
|
||||||
|
@ -426,9 +432,9 @@ struct SshProvider<T: Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> SshProvider<T> {
|
impl<T: Clone> SshProvider<T> {
|
||||||
fn new(menu_item_data: T) -> Self {
|
fn new(menu_item_data: T, order: SortOrder) -> Self {
|
||||||
let re = Regex::new(r"(?m)^\s*Host\s+(.+)$").unwrap();
|
let re = Regex::new(r"(?m)^\s*Host\s+(.+)$").unwrap();
|
||||||
let items: Vec<_> = dirs::home_dir()
|
let mut items: Vec<_> = dirs::home_dir()
|
||||||
.map(|home| home.join(".ssh").join("config"))
|
.map(|home| home.join(".ssh").join("config"))
|
||||||
.filter(|path| path.exists())
|
.filter(|path| path.exists())
|
||||||
.map(|path| fs::read_to_string(&path).unwrap_or_default())
|
.map(|path| fs::read_to_string(&path).unwrap_or_default())
|
||||||
|
@ -456,6 +462,7 @@ impl<T: Clone> SshProvider<T> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
gui::apply_sort(&mut items, &order);
|
||||||
Self { elements: items }
|
Self { elements: items }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,7 +547,7 @@ struct EmojiProvider<T: Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> EmojiProvider<T> {
|
impl<T: Clone> EmojiProvider<T> {
|
||||||
fn new(data: T) -> Self {
|
fn new(data: T, sort_order: SortOrder) -> Self {
|
||||||
let emoji = emoji::search::search_annotation_all("");
|
let emoji = emoji::search::search_annotation_all("");
|
||||||
let mut menus = emoji
|
let mut menus = emoji
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -556,7 +563,7 @@ impl<T: Clone> EmojiProvider<T> {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
gui::sort_menu_items_alphabetically_honor_initial_score(&mut menus);
|
gui::apply_sort(&mut menus, &sort_order);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
elements: menus,
|
elements: menus,
|
||||||
|
@ -581,18 +588,20 @@ struct DMenuProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DMenuProvider {
|
impl DMenuProvider {
|
||||||
fn new() -> Result<DMenuProvider, Error> {
|
fn new(sort_order: SortOrder) -> Result<DMenuProvider, Error> {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
io::stdin()
|
io::stdin()
|
||||||
.read_to_string(&mut input)
|
.read_to_string(&mut input)
|
||||||
.map_err(|_| Error::StdInReadFail)?;
|
.map_err(|_| Error::StdInReadFail)?;
|
||||||
|
|
||||||
let items: Vec<MenuItem<String>> = input
|
let mut items: Vec<MenuItem<String>> = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(String::from)
|
.map(String::from)
|
||||||
.map(|s| MenuItem::new(s.clone(), None, None, vec![], None, 0.0, None))
|
.map(|s| MenuItem::new(s.clone(), None, None, vec![], None, 0.0, None))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
gui::apply_sort(&mut items, &sort_order);
|
||||||
|
|
||||||
Ok(Self { items })
|
Ok(Self { items })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -630,11 +639,11 @@ struct AutoItemProvider {
|
||||||
impl AutoItemProvider {
|
impl AutoItemProvider {
|
||||||
fn new(config: &Config) -> Self {
|
fn new(config: &Config) -> Self {
|
||||||
AutoItemProvider {
|
AutoItemProvider {
|
||||||
drun: DRunProvider::new(AutoRunType::DRun, config.no_actions()),
|
drun: DRunProvider::new(AutoRunType::DRun, config.no_actions(), config.sort_order()),
|
||||||
file: FileItemProvider::new(AutoRunType::File),
|
file: FileItemProvider::new(AutoRunType::File, config.sort_order()),
|
||||||
math: MathProvider::new(AutoRunType::Math),
|
math: MathProvider::new(AutoRunType::Math),
|
||||||
ssh: SshProvider::new(AutoRunType::Ssh),
|
ssh: SshProvider::new(AutoRunType::Ssh, config.sort_order()),
|
||||||
emoji: EmojiProvider::new(AutoRunType::Emoji),
|
emoji: EmojiProvider::new(AutoRunType::Emoji, config.sort_order()),
|
||||||
last_mode: None,
|
last_mode: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -698,7 +707,7 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
|
||||||
///
|
///
|
||||||
/// Will return `Err` if it was not able to spawn the process
|
/// Will return `Err` if it was not able to spawn the process
|
||||||
pub fn d_run(config: &Config) -> Result<(), Error> {
|
pub fn d_run(config: &Config) -> Result<(), Error> {
|
||||||
let provider = DRunProvider::new(0, config.no_actions());
|
let provider = DRunProvider::new(0, config.no_actions(), config.sort_order());
|
||||||
let cache_path = provider.cache_path.clone();
|
let cache_path = provider.cache_path.clone();
|
||||||
let mut cache = provider.cache.clone();
|
let mut cache = provider.cache.clone();
|
||||||
|
|
||||||
|
@ -719,7 +728,7 @@ pub fn d_run(config: &Config) -> Result<(), Error> {
|
||||||
///
|
///
|
||||||
/// Will return `Err` if it was not able to spawn the process
|
/// Will return `Err` if it was not able to spawn the process
|
||||||
pub fn run(config: &Config) -> Result<(), Error> {
|
pub fn run(config: &Config) -> Result<(), Error> {
|
||||||
let provider = RunProvider::new();
|
let provider = RunProvider::new(config.sort_order());
|
||||||
let cache_path = provider.cache_path.clone();
|
let cache_path = provider.cache_path.clone();
|
||||||
let mut cache = provider.cache.clone();
|
let mut cache = provider.cache.clone();
|
||||||
|
|
||||||
|
@ -812,7 +821,7 @@ pub fn auto(config: &Config) -> Result<(), Error> {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// In case an internal regex does not parse anymore, this should never happen
|
/// In case an internal regex does not parse anymore, this should never happen
|
||||||
pub fn file(config: &Config) -> Result<(), Error> {
|
pub fn file(config: &Config) -> Result<(), Error> {
|
||||||
let provider = FileItemProvider::new(0);
|
let provider = FileItemProvider::new(0, config.sort_order());
|
||||||
|
|
||||||
// todo ues a arc instead of cloning the config
|
// todo ues a arc instead of cloning the config
|
||||||
let selection_result = gui::show(
|
let selection_result = gui::show(
|
||||||
|
@ -856,7 +865,7 @@ fn ssh_launch<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Result<(),
|
||||||
/// * if it was not able to spawn the process
|
/// * if it was not able to spawn the process
|
||||||
/// * if it didn't find a terminal
|
/// * if it didn't find a terminal
|
||||||
pub fn ssh(config: &Config) -> Result<(), Error> {
|
pub fn ssh(config: &Config) -> Result<(), Error> {
|
||||||
let provider = SshProvider::new(0);
|
let provider = SshProvider::new(0,config.sort_order() );
|
||||||
let selection_result = gui::show(config.clone(), provider, true, None);
|
let selection_result = gui::show(config.clone(), provider, true, None);
|
||||||
if let Ok(mi) = selection_result {
|
if let Ok(mi) = selection_result {
|
||||||
ssh_launch(&mi, config)?;
|
ssh_launch(&mi, config)?;
|
||||||
|
@ -887,7 +896,7 @@ pub fn math(config: &Config) {
|
||||||
///
|
///
|
||||||
/// Forwards errors from the gui. See `gui::show` for details.
|
/// Forwards errors from the gui. See `gui::show` for details.
|
||||||
pub fn emoji(config: &Config) -> Result<(), Error> {
|
pub fn emoji(config: &Config) -> Result<(), Error> {
|
||||||
let provider = EmojiProvider::new(0);
|
let provider = EmojiProvider::new(0, config.sort_order());
|
||||||
let selection_result = gui::show(config.clone(), provider, true, None)?;
|
let selection_result = gui::show(config.clone(), provider, true, None)?;
|
||||||
match selection_result.action {
|
match selection_result.action {
|
||||||
None => Err(Error::MissingAction),
|
None => Err(Error::MissingAction),
|
||||||
|
@ -900,7 +909,7 @@ pub fn emoji(config: &Config) -> Result<(), Error> {
|
||||||
///
|
///
|
||||||
/// Forwards errors from the gui. See `gui::show` for details.
|
/// Forwards errors from the gui. See `gui::show` for details.
|
||||||
pub fn dmenu(config: &Config) -> Result<(), Error> {
|
pub fn dmenu(config: &Config) -> Result<(), Error> {
|
||||||
let provider = DMenuProvider::new()?;
|
let provider = DMenuProvider::new(config.sort_order())?;
|
||||||
|
|
||||||
let selection_result = gui::show(config.clone(), provider, true, None);
|
let selection_result = gui::show(config.clone(), provider, true, None);
|
||||||
match selection_result {
|
match selection_result {
|
||||||
|
|
Loading…
Add table
Reference in a new issue