move matching code in fde
This commit is contained in:
parent
347dce3e93
commit
0b44fe028e
3 changed files with 55 additions and 115 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"rust-analyzer.check.overrideCommand": ["just", "check-json"],
|
||||
"git-blame.gitWebUrl": "",
|
||||
"rust-analyzer.cargo.features": [
|
||||
"desktop"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ desktop = [
|
|||
"dep:freedesktop-desktop-entry",
|
||||
"dep:mime",
|
||||
"dep:shlex",
|
||||
"dep:textdistance",
|
||||
]
|
||||
# Enables keycode serialization
|
||||
serde-keycode = ["iced_core/serde"]
|
||||
|
|
@ -88,7 +87,6 @@ palette = "0.7.3"
|
|||
rfd = { version = "0.14.0", optional = true }
|
||||
serde = { version = "1.0.180", features = ["derive"] }
|
||||
slotmap = "1.0.6"
|
||||
textdistance = { version = "1.0.2", optional = true }
|
||||
thiserror = "1.0.44"
|
||||
tokio = { version = "1.24.2", optional = true }
|
||||
tracing = "0.1"
|
||||
|
|
@ -176,3 +174,6 @@ dirs = "5.0.1"
|
|||
|
||||
[patch."https://github.com/pop-os/libcosmic"]
|
||||
libcosmic = { path = "./" }
|
||||
|
||||
# [patch.crates-io]
|
||||
# freedesktop-desktop-entry = { path = "../freedesktop-desktop-entry" }
|
||||
162
src/desktop.rs
162
src/desktop.rs
|
|
@ -1,9 +1,8 @@
|
|||
pub use freedesktop_desktop_entry::DesktopEntry;
|
||||
use iced_widget::canvas::path::lyon_path::geom::euclid::approxord::min;
|
||||
pub use freedesktop_desktop_entry as fde;
|
||||
use freedesktop_desktop_entry::{DesktopEntry, MatchAppIdOptions};
|
||||
pub use mime::Mime;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cmp::max,
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
|
@ -64,6 +63,28 @@ pub struct DesktopEntryData {
|
|||
pub prefers_dgpu: bool,
|
||||
}
|
||||
|
||||
pub fn load_desktop_file<'a>(
|
||||
locale: impl Into<Option<&'a str>>,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Option<DesktopEntryData> {
|
||||
let path = path.as_ref();
|
||||
std::fs::read_to_string(path).ok().and_then(|input| {
|
||||
DesktopEntry::decode(path, &input)
|
||||
.ok()
|
||||
.map(|de| DesktopEntryData::from_desktop_entry(locale, PathBuf::from(path), &de))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_applications_filtered<'a, F: FnMut(&DesktopEntry) -> bool>(
|
||||
locale: impl Into<Option<&'a str>>,
|
||||
mut filter: F,
|
||||
) -> Vec<DesktopEntryData> {
|
||||
let locale = locale.into();
|
||||
fde::Iter::new(fde::default_paths())
|
||||
.filter_map(|path| load_desktop_file(locale, path))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn load_applications<'a>(
|
||||
locale: impl Into<Option<&'a str>>,
|
||||
include_no_display: bool,
|
||||
|
|
@ -82,33 +103,6 @@ pub fn app_id_or_fallback_matches(app_id: &str, entry: &DesktopEntryData) -> boo
|
|||
|| app_id.to_lowercase() == entry.name.to_lowercase()
|
||||
}
|
||||
|
||||
/// From 0 to 1.
|
||||
/// 1 is a perfect match.
|
||||
fn match_entry(id: &str, de: &DesktopEntry) -> f32 {
|
||||
let cmp = |id, de| {
|
||||
let lcsstr = textdistance::str::lcsstr(id, de);
|
||||
lcsstr as f32 / (max(id.len(), de.len())) as f32
|
||||
};
|
||||
|
||||
fn max_f32(a: f32, b: f32) -> f32 {
|
||||
if a > b {
|
||||
return a;
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
let id = id.to_lowercase();
|
||||
let de_id = de.appid.to_lowercase();
|
||||
let de_wm_class = de.startup_wm_class().unwrap_or_default().to_lowercase();
|
||||
let de_name = de.name(None).unwrap_or_default().to_lowercase();
|
||||
|
||||
return max_f32(
|
||||
cmp(&id, &de_id),
|
||||
max_f32(cmp(&id, &de_wm_class), cmp(&id, &de_name)),
|
||||
);
|
||||
}
|
||||
|
||||
/// The result will be in the same order of `app_ids`
|
||||
pub fn load_applications_for_app_ids<'a, I: AsRef<str>>(
|
||||
locale: impl Into<Option<&'a str>>,
|
||||
|
|
@ -128,107 +122,49 @@ pub fn load_applications_for_app_ids<'a, I: AsRef<str>>(
|
|||
|
||||
let all_desktop_entries = all_desktop_entries_string
|
||||
.iter()
|
||||
.filter_map(|(path, content)| DesktopEntry::decode(&path, &content).ok())
|
||||
.filter_map(|(path, content)| {
|
||||
DesktopEntry::decode(&path, &content).ok().and_then(|de| {
|
||||
if !include_no_display && de.no_display() {
|
||||
None
|
||||
} else {
|
||||
Some(de)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut applications = Vec::new();
|
||||
|
||||
let locale = locale.into();
|
||||
|
||||
for id in app_ids {
|
||||
let mut max_score = None;
|
||||
let mut second_max_score = 0.;
|
||||
|
||||
for de in &all_desktop_entries {
|
||||
if !include_no_display && de.no_display() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let score = match_entry(id.as_ref(), de);
|
||||
|
||||
match max_score {
|
||||
Some((prev_max_score, _)) => {
|
||||
if prev_max_score < score {
|
||||
second_max_score = prev_max_score;
|
||||
max_score = Some((score, de));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
max_score = Some((score, de));
|
||||
}
|
||||
}
|
||||
|
||||
if score > 0.99 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut has_matched = false;
|
||||
if let Some((max_score, de)) = max_score {
|
||||
let entropy = max_score - second_max_score;
|
||||
|
||||
// println!("{id}: {max_score}:{entropy}: {}", de.appid);
|
||||
|
||||
if max_score > 0.7 || entropy > 0.15 && max_score > 0.2 {
|
||||
match fde::try_match_app_id(
|
||||
id.as_ref(),
|
||||
&all_desktop_entries,
|
||||
MatchAppIdOptions::default(),
|
||||
) {
|
||||
Some(de) => {
|
||||
applications.push(DesktopEntryData::from_desktop_entry(
|
||||
locale,
|
||||
Some(de.path.to_path_buf()),
|
||||
de,
|
||||
));
|
||||
has_matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if fill_missing_ones && !has_matched {
|
||||
applications.push(DesktopEntryData {
|
||||
id: id.as_ref().to_string(),
|
||||
name: id.as_ref().to_string(),
|
||||
icon: IconSource::default(),
|
||||
..Default::default()
|
||||
});
|
||||
None => {
|
||||
if fill_missing_ones {
|
||||
applications.push(DesktopEntryData {
|
||||
id: id.as_ref().to_string(),
|
||||
name: id.as_ref().to_string(),
|
||||
icon: IconSource::default(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applications
|
||||
}
|
||||
|
||||
pub fn load_applications_filtered<'a, F: FnMut(&DesktopEntry) -> bool>(
|
||||
locale: impl Into<Option<&'a str>>,
|
||||
mut filter: F,
|
||||
) -> Vec<DesktopEntryData> {
|
||||
let locale = locale.into();
|
||||
|
||||
freedesktop_desktop_entry::Iter::new(freedesktop_desktop_entry::default_paths())
|
||||
.filter_map(|path| {
|
||||
std::fs::read_to_string(&path).ok().and_then(|input| {
|
||||
DesktopEntry::decode(&path, &input).ok().and_then(|de| {
|
||||
if !filter(&de) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(DesktopEntryData::from_desktop_entry(
|
||||
locale,
|
||||
path.clone(),
|
||||
&de,
|
||||
))
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn load_desktop_file<'a>(
|
||||
locale: impl Into<Option<&'a str>>,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Option<DesktopEntryData> {
|
||||
let path = path.as_ref();
|
||||
std::fs::read_to_string(path).ok().and_then(|input| {
|
||||
DesktopEntry::decode(path, &input)
|
||||
.ok()
|
||||
.map(|de| DesktopEntryData::from_desktop_entry(locale, PathBuf::from(path), &de))
|
||||
})
|
||||
}
|
||||
|
||||
impl DesktopEntryData {
|
||||
fn from_desktop_entry<'a>(
|
||||
locale: impl Into<Option<&'a str>>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue