improve formatting w/ nightly toolchain

nightly rustformat allows some
advanced formatting for strings and
imports.
Add a pipeline steps that validates through this.
This commit is contained in:
Alexander Mohr 2025-07-26 17:38:22 +02:00
parent 997b87f607
commit 0fdee55fbe
26 changed files with 271 additions and 216 deletions

View file

@ -13,8 +13,47 @@ env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
jobs: jobs:
build: format_and_clippy_nightly_toolchain:
runs-on : ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: false
- name: Install ubuntu dependencies
run: |
sudo apt update
sudo apt install -y librust-gdk4-sys-dev \
libglib2.0-dev libgtk-layer-shell-dev libgtk-layer-shell0 gir1.2-gtklayershell-0.1 \
libgtk-4-dev gobject-introspection libgirepository1.0-dev gtk-doc-tools python3 valac \
git cmake gcc meson ninja-build
- name: Install gt4k layer shell
run: |
git clone https://github.com/wmww/gtk4-layer-shell
cd gtk4-layer-shell
meson setup -Dexamples=true -Ddocs=true -Dtests=true build
ninja -C build
sudo ninja -C build install
sudo ldconfig
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
- name: Install format
run: rustup component add rustfmt
- name: Check format with nightly rules
run: cargo +nightly fmt -- --check --config error_on_unformatted=true,error_on_line_overflow=true,format_strings=true,group_imports=StdExternalCrate,imports_granularity=Crate
- name: install clippy
run: rustup component add clippy --toolchain nightly
- name: run clippy nightly
run: cargo +nightly clippy --all-targets -- -D warnings
build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -38,11 +77,19 @@ jobs:
sudo ninja -C build install sudo ninja -C build install
sudo ldconfig sudo ldconfig
- name: Formatting - name: Install taplo toml toolkit
run: cargo install --locked taplo-cli
- name: Toml Formatting
run: taplo fmt --check --diff
- name: Rust Formatting
run: cargo fmt --all -- --check run: cargo fmt --all -- --check
- name: Clippy warnings - name: Clippy warnings
run: cargo clippy -- -D warnings run: cargo clippy -- -D warnings
- name: Build - name: Build
run: cargo build --verbose run: cargo build --verbose
- name: Run tests - name: Run tests
run: cargo test -- --show-output run: cargo test -- --show-output

View file

@ -1,9 +1,9 @@
[workspace] [workspace]
members = [ members = [
"worf", "worf",
"examples/worf-warden", "examples/worf-warden",
"examples/worf-hyprswitch", "examples/worf-hyprswitch",
"examples/worf-hyprspace", "examples/worf-hyprspace",
] ]
resolver = "3" resolver = "3"

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
worf = {path = "../../worf"} worf = { path = "../../worf" }
env_logger = "0.11.8" env_logger = "0.11.8"
hyprland = "0.4.0-beta.2" hyprland = "0.4.0-beta.2"
clap = "4.5.40" clap = "4.5.40"

View file

@ -385,10 +385,10 @@ fn workspace_from_selection<'a>(
action: Option<Action>, action: Option<Action>,
max_id: i32, max_id: i32,
) -> Result<(WorkspaceIdentifierWithSpecial<'a>, i32, bool), String> { ) -> Result<(WorkspaceIdentifierWithSpecial<'a>, i32, bool), String> {
if let Some(action) = action { if let Some(action) = action
if let Some(ws) = action.workspace { && let Some(ws) = action.workspace
return Ok((WorkspaceIdentifierWithSpecial::Id(ws.id), ws.id, false)); {
} return Ok((WorkspaceIdentifierWithSpecial::Id(ws.id), ws.id, false));
} }
find_first_free_workspace_id(max_id) find_first_free_workspace_id(max_id)
.map(|id| (WorkspaceIdentifierWithSpecial::Id(id), id, true)) .map(|id| (WorkspaceIdentifierWithSpecial::Id(id), id, true))

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
worf = {path = "../../worf"} worf = { path = "../../worf" }
env_logger = "0.11.8" env_logger = "0.11.8"
hyprland = "0.4.0-beta.2" hyprland = "0.4.0-beta.2"
sysinfo = "0.35.2" sysinfo = "0.35.2"

View file

@ -161,10 +161,10 @@ fn main() -> Result<(), String> {
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let update_cache = thread::spawn(move || { let update_cache = thread::spawn(move || {
windows.iter().for_each(|item| { windows.iter().for_each(|item| {
if let Some(window) = &item.data { if let Some(window) = &item.data
if let Some(icon) = &window.icon { && let Some(icon) = &window.icon
cache.insert(window.process.clone(), icon.clone()); {
} cache.insert(window.process.clone(), icon.clone());
} }
}); });
let updated_toml = toml::to_string(&cache); let updated_toml = toml::to_string(&cache);

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
worf = {path = "../../worf"} worf = { path = "../../worf" }
env_logger = "0.11.8" env_logger = "0.11.8"
log = "0.4.27" log = "0.4.27"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }

View file

@ -1,5 +1,3 @@
use clap::Parser;
use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashMap, collections::HashMap,
env, env,
@ -8,6 +6,9 @@ use std::{
thread::sleep, thread::sleep,
time::Duration, time::Duration,
}; };
use clap::Parser;
use serde::{Deserialize, Serialize};
use worf::{ use worf::{
config::{self, Config, CustomKeyHintLocation, Key}, config::{self, Config, CustomKeyHintLocation, Key},
desktop::{copy_to_clipboard, spawn_fork}, desktop::{copy_to_clipboard, spawn_fork},

View file

@ -1,6 +1,6 @@
image_size=28 image_size = 28
allow_images=true allow_images = true
term="kitty -e" term = "kitty -e"
insensitive=true insensitive = true
key_detection_type="Code" key_detection_type = "Code"
lines_size_factor=1.9 lines_size_factor = 1.9

View file

@ -1,7 +1,7 @@
image_size=28 image_size = 28
allow_images=true allow_images = true
term="kitty -e" term = "kitty -e"
insensitive=true insensitive = true
key_detection_type="Code" key_detection_type = "Code"
blurred_background=false blurred_background = false
lines_size_factor=1.9 lines_size_factor = 1.9

View file

@ -1,7 +1,7 @@
image_size=28 image_size = 28
allow_images=true allow_images = true
term="kitty -e" term = "kitty -e"
insensitive=true insensitive = true
key_detection_type="Code" key_detection_type = "Code"
blurred_background=false blurred_background = false
lines_size_factor=1.9 lines_size_factor = 1.9

View file

@ -1,5 +1,5 @@
image_size=28 image_size = 28
allow_images=true allow_images = true
term="kitty -e" term = "kitty -e"
insensitive=true insensitive = true
key_detection_type="Code" key_detection_type = "Code"

View file

@ -1,7 +1,7 @@
image_size=28 image_size = 28
allow_images=true allow_images = true
term="kitty -e" term = "kitty -e"
insensitive=true insensitive = true
key_detection_type="Code" key_detection_type = "Code"
blurred_background=false blurred_background = false
lines_size_factor=1.9 lines_size_factor = 1.9

View file

@ -1,7 +1,6 @@
image_size=28 image_size = 28
allow_images=true allow_images = true
term="kitty -e" term = "kitty -e"
insensitive=true insensitive = true
key_detection_type="Code" key_detection_type = "Code"
#blurred_background=false #blurred_background=false

View file

@ -1,10 +1,10 @@
image_size=0 image_size = 0
columns=999 columns = 999
allow_images=false allow_images = false
orientation="Horizontal" orientation = "Horizontal"
row_box_orientation="Horizontal" row_box_orientation = "Horizontal"
content_halign="Start" content_halign = "Start"
width="100%" width = "100%"
hide_scroll=true hide_scroll = true
location=["Top"] location = ["Top"]
lines=1 lines = 1

View file

@ -1,9 +1,9 @@
image_size=64 image_size = 64
columns=6 columns = 6
orientation="Vertical" orientation = "Vertical"
row_box_orientation="Vertical" row_box_orientation = "Vertical"
content_halign="Center" content_halign = "Center"
height="70%" height = "70%"
width="60%" width = "60%"
valign="Start" valign = "Start"
emoji_hide_label=true emoji_hide_label = true

View file

@ -1,12 +1,12 @@
image_size=64 image_size = 64
columns=4 columns = 4
orientation="Vertical" orientation = "Vertical"
row_box_orientation="Vertical" row_box_orientation = "Vertical"
content_halign="Center" content_halign = "Center"
height="105%" height = "105%"
width="100%" width = "100%"
valign="Start" valign = "Start"
blurred_background=true blurred_background = true
line_max_chars=32 line_max_chars = 32
key_detection_type="Code" key_detection_type = "Code"
term="kitty -e" term = "kitty -e"

View file

@ -1,9 +1,9 @@
image_size=28 image_size = 28
allow_images=false allow_images = false
term="kitty -e" term = "kitty -e"
insensitive=true insensitive = true
key_detection_type="Code" key_detection_type = "Code"
blurred_background=false blurred_background = false
location=["Top"] location = ["Top"]
lines=3 lines = 3
matching="None" matching = "None"

View file

@ -1,5 +1,5 @@
image_size=48 image_size = 48
columns=1 columns = 1
height="60%" height = "60%"
width="70%" width = "70%"
valign="Start" valign = "Start"

View file

@ -50,6 +50,6 @@ rayon = "1.10.0"
nix = { version = "0.30.0", features = ["process"] } nix = { version = "0.30.0", features = ["process"] }
emoji = "0.2.1" 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" urlencoding = "2.1.3"

View file

@ -1,8 +1,7 @@
use std::{env, fs, path::PathBuf, str::FromStr}; use std::{env, fs, path::PathBuf, str::FromStr};
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use crate::Error; use crate::Error;
@ -462,7 +461,9 @@ pub struct Config {
/// set where the window is displayed. /// set where the window is displayed.
/// can be used to anchor a window to an edge by /// can be used to anchor a window to an edge by
/// setting top,left for example /// setting top,left for example
#[clap(short = 'l', long = "location", value_delimiter = ',', value_parser = clap::builder::ValueParser::new(Anchor::from_str) #[clap(
short = 'l', long = "location",
value_delimiter = ',', value_parser = clap::builder::ValueParser::new(Anchor::from_str)
)] )]
location: Option<Vec<Anchor>>, location: Option<Vec<Anchor>>,
@ -1050,10 +1051,10 @@ pub fn expand_path(input: &str) -> PathBuf {
let mut path = input.to_string(); let mut path = input.to_string();
// Expand ~ to home directory // Expand ~ to home directory
if path.starts_with('~') { if path.starts_with('~')
if let Some(home_dir) = dirs::home_dir() { && let Some(home_dir) = dirs::home_dir()
path = path.replacen('~', home_dir.to_str().unwrap_or(""), 1); {
} path = path.replacen('~', home_dir.to_str().unwrap_or(""), 1);
} }
// Expand $VAR style environment variables // Expand $VAR style environment variables

View file

@ -1277,21 +1277,21 @@ where
drop(menu_rows); drop(menu_rows);
if let Some(item) = item { if let Some(item) = item
if let Err(e) = handle_selected_item(ui, meta, None, Some(item), None) { && let Err(e) = handle_selected_item(ui, meta, None, Some(item), None)
log::error!("failed to handle selected item {e}"); {
} log::error!("failed to handle selected item {e}");
} }
} else { } else {
drop(menu_rows); drop(menu_rows);
} }
if meta.config.read().unwrap().dynamic_lines() { if meta.config.read().unwrap().dynamic_lines()
if let Some(geometry) = get_monitor_geometry(ui.window.surface().as_ref()) { && let Some(geometry) = get_monitor_geometry(ui.window.surface().as_ref())
let height = {
calculate_dynamic_lines_window_height(&meta.config.read().unwrap(), ui, geometry); let height =
ui.window.set_height_request(height); calculate_dynamic_lines_window_height(&meta.config.read().unwrap(), ui, geometry);
} ui.window.set_height_request(height);
} }
} }
@ -1310,45 +1310,43 @@ fn handle_key_expand<T>(ui: &Rc<UiElements<T>>, meta: &Rc<MetaData<T>>) -> Propa
where where
T: Clone + Send + 'static, T: Clone + Send + 'static,
{ {
if let Some(fb) = ui.main_box.selected_children().first() { if let Some(fb) = ui.main_box.selected_children().first()
if let Some(child) = fb.child() { && let Some(child) = fb.child()
let expander = child.downcast::<Expander>().ok(); {
if let Some(expander) = expander { let expander = child.downcast::<Expander>().ok();
expander.set_expanded(true); if let Some(expander) = expander {
} else { expander.set_expanded(true);
let data = { } else {
let lock = ui.menu_rows.read().unwrap(); let data = {
let menu_item = lock.get(fb); let lock = ui.menu_rows.read().unwrap();
menu_item.map(|menu_item| { let menu_item = lock.get(fb);
( menu_item.map(|menu_item| {
meta.item_provider (
.lock() meta.item_provider
.unwrap() .lock()
.get_sub_elements(menu_item), .unwrap()
menu_item.clone(), .get_sub_elements(menu_item),
) menu_item.clone(),
}) )
}; })
};
if let Some((provider_data, menu_item)) = data { if let Some((provider_data, menu_item)) = data {
if let Some(items) = provider_data.items { if let Some(items) = provider_data.items {
build_ui_from_menu_items(ui, meta, items); build_ui_from_menu_items(ui, meta, items);
let query = match meta.expand_mode { let query = match meta.expand_mode {
ExpandMode::Verbatim => menu_item.label.clone(), ExpandMode::Verbatim => menu_item.label.clone(),
ExpandMode::WithSpace => format!("{} ", menu_item.label.clone()), ExpandMode::WithSpace => format!("{} ", menu_item.label.clone()),
}; };
set_search_text(ui, meta, &query); set_search_text(ui, meta, &query);
if let Ok(new_pos) = i32::try_from(query.len() + 1) { if let Ok(new_pos) = i32::try_from(query.len() + 1) {
ui.search.set_position(new_pos); ui.search.set_position(new_pos);
}
update_view(ui, meta, &query);
} else if let Err(e) =
handle_selected_item(ui, meta, None, Some(menu_item), None)
{
log::error!("{e}");
} }
update_view(ui, meta, &query);
} else if let Err(e) = handle_selected_item(ui, meta, None, Some(menu_item), None) {
log::error!("{e}");
} }
} }
} }
@ -1360,12 +1358,11 @@ fn handle_key_copy<T>(ui: &Rc<UiElements<T>>, meta: &Rc<MetaData<T>>) -> Propaga
where where
T: Clone + Send + 'static, T: Clone + Send + 'static,
{ {
if let Some(item) = get_selected_item(ui) { if let Some(item) = get_selected_item(ui)
if let Some(action) = item.action { && let Some(action) = item.action
if let Err(e) = desktop::copy_to_clipboard(action, None) { && let Err(e) = desktop::copy_to_clipboard(action, None)
log::error!("failed to copy to clipboard: {e}"); {
} log::error!("failed to copy to clipboard: {e}");
}
} }
if let Err(e) = meta.selected_sender.send(Err(Error::NoSelection)) { if let Err(e) = meta.selected_sender.send(Err(Error::NoSelection)) {
log::error!("failed to send message {e}"); log::error!("failed to send message {e}");
@ -1445,11 +1442,11 @@ fn window_show_resize<T: Clone + 'static>(config: &Config, ui: &Rc<UiElements<T>
return; return;
}; };
if !config.blurred_background_fullscreen() { if !config.blurred_background_fullscreen()
if let Some(background) = &ui.background { && let Some(background) = &ui.background
background.set_height_request(geometry.height()); {
background.set_width_request(geometry.width()); background.set_height_request(geometry.height());
} background.set_width_request(geometry.width());
} }
// Calculate target width from config, return early if not set // Calculate target width from config, return early if not set
@ -1541,7 +1538,8 @@ fn calculate_row_height<T: Clone + 'static>(
}; };
log::debug!( log::debug!(
"heights: scroll {scroll_height}, window {window_height}, keys {height_box}, height {height:?}, lines {lines:?}" "heights: scroll {scroll_height}, window {window_height}, keys {height_box}, height \
{height:?}, lines {lines:?}"
); );
height_box height_box
@ -1574,10 +1572,10 @@ where
if let Some(s) = ui.main_box.selected_children().into_iter().next() { if let Some(s) = ui.main_box.selected_children().into_iter().next() {
let list_items = ui.menu_rows.read().unwrap(); let list_items = ui.menu_rows.read().unwrap();
let item = list_items.get(&s); let item = list_items.get(&s);
if let Some(selected_item) = item { if let Some(selected_item) = item
if selected_item.visible { && selected_item.visible
return Some(selected_item.clone()); {
} return Some(selected_item.clone());
} }
} }
@ -1728,16 +1726,15 @@ fn create_menu_row<T: Clone + 'static + Send>(
label.set_max_width_chars(max_width_chars); label.set_max_width_chars(max_width_chars);
} }
if let Some(max_len) = meta.config.read().unwrap().line_max_chars() { if let Some(max_len) = meta.config.read().unwrap().line_max_chars()
if let Some(text) = label_text.as_ref() { && let Some(text) = label_text.as_ref()
if text.chars().count() > max_len { && text.chars().count() > max_len
let end = text {
.char_indices() let end = text
.nth(max_len) .char_indices()
.map_or(text.len(), |(idx, _)| idx); .nth(max_len)
label.set_text(&format!("{}...", &text[..end])); .map_or(text.len(), |(idx, _)| idx);
} label.set_text(&format!("{}...", &text[..end]));
}
} }
row_box.append(&label); row_box.append(&label);
@ -1772,16 +1769,16 @@ fn create_menu_row<T: Clone + 'static + Send>(
}; };
click.connect_pressed(move |_gesture, n_press, _x, _y| { click.connect_pressed(move |_gesture, n_press, _x, _y| {
if n_press == presses { if n_press == presses
if let Err(e) = handle_selected_item( && let Err(e) = handle_selected_item(
&click_ui, &click_ui,
&click_meta, &click_meta,
None, None,
Some(element_clone.clone()), Some(element_clone.clone()),
None, None,
) { )
log::error!("{e}"); {
} log::error!("{e}");
} }
}); });
row.add_controller(click); row.add_controller(click);
@ -1963,10 +1960,10 @@ fn find_visible_child<T: Clone>(
for i in range { for i in range {
let i_32 = i.try_into().unwrap_or(i32::MAX); let i_32 = i.try_into().unwrap_or(i32::MAX);
if let Some(child) = flow_box.child_at_index(i_32) { if let Some(child) = flow_box.child_at_index(i_32)
if child.is_visible() { && child.is_visible()
return Some(child); {
} return Some(child);
} }
} }

View file

@ -60,10 +60,10 @@ impl AutoItemProvider {
.is_some_and(|t| t != &AutoRunType::Auto) .is_some_and(|t| t != &AutoRunType::Auto)
{ {
let mut data = self.drun.get_elements(None); let mut data = self.drun.get_elements(None);
if let Some(items) = data.items.as_mut() { if let Some(items) = data.items.as_mut()
if let Some(mut ssh) = self.ssh.get_elements(None).items { && let Some(mut ssh) = self.ssh.get_elements(None).items
items.append(&mut ssh); {
} items.append(&mut ssh);
} }
self.last_mode = Some(AutoRunType::Auto); self.last_mode = Some(AutoRunType::Auto);
@ -77,7 +77,18 @@ 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)
static MATH_REGEX: LazyLock<Regex> = LazyLock::new(|| { static MATH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(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").unwrap() Regex::new(
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",
)
.unwrap()
}); });
// Regex for strings that start with a number (including decimals) // Regex for strings that start with a number (including decimals)

View file

@ -7,7 +7,6 @@ use std::{
use freedesktop_file_parser::EntryType; use freedesktop_file_parser::EntryType;
use rayon::prelude::*; use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
Error, Error,
@ -20,12 +19,6 @@ use crate::{
modes::load_cache, modes::load_cache,
}; };
#[derive(Debug, Deserialize, Serialize, Clone)]
struct DRunCache {
desktop_entry: String,
run_count: usize,
}
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct DRunProvider<T: Clone> { pub(crate) struct DRunProvider<T: Clone> {
items: Option<Vec<MenuItem<T>>>, items: Option<Vec<MenuItem<T>>>,
@ -78,8 +71,7 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
let entries: Vec<MenuItem<T>> = find_desktop_files() let entries: Vec<MenuItem<T>> = find_desktop_files()
.into_par_iter() .into_par_iter()
.filter(|file| { .filter(|file| {
!file.entry.no_display.unwrap_or(false) !file.entry.no_display.unwrap_or(false) && !file.entry.hidden.unwrap_or(false)
&& !file.entry.hidden.unwrap_or(false)
}) })
.filter_map(|file| { .filter_map(|file| {
let name = lookup_name_with_locale( let name = lookup_name_with_locale(
@ -89,7 +81,11 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
)?; )?;
let (action, working_dir, in_terminal) = match &file.entry.entry_type { let (action, working_dir, in_terminal) = match &file.entry.entry_type {
EntryType::Application(app) => (app.exec.clone(), app.path.clone(), app.terminal.unwrap_or(false)), EntryType::Application(app) => (
app.exec.clone(),
app.path.clone(),
app.terminal.unwrap_or(false),
),
_ => return None, _ => return None,
}; };
@ -104,7 +100,10 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
.unwrap_or(false); .unwrap_or(false);
if !cmd_exists { if !cmd_exists {
log::warn!("Skipping desktop entry for {name:?} because action {action:?} does not exist"); log::warn!(
"Skipping desktop entry for {name:?} because action {action:?} does not \
exist"
);
return None; return None;
} }
@ -142,8 +141,8 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
.or(icon.clone()) .or(icon.clone())
.unwrap_or("application-x-executable".to_string()); .unwrap_or("application-x-executable".to_string());
let action =
let action = self.get_action(in_terminal, action.exec.clone(), &action_name); self.get_action(in_terminal, action.exec.clone(), &action_name);
entry.sub_elements.push(MenuItem::new( entry.sub_elements.push(MenuItem::new(
action_name, action_name,

View file

@ -137,12 +137,11 @@ impl<T: Clone> ItemProvider<T> for FileItemProvider<T> {
if let Some(mut path_str) = if let Some(mut path_str) =
entry.path().to_str().map(std::string::ToString::to_string) entry.path().to_str().map(std::string::ToString::to_string)
{ {
if trimmed_search.starts_with('~') { if trimmed_search.starts_with('~')
if let Some(home_dir) = dirs::home_dir() { && let Some(home_dir) = dirs::home_dir()
if let Some(home_str) = home_dir.to_str() { && let Some(home_str) = home_dir.to_str()
path_str = path_str.replace(home_str, "~"); {
} path_str = path_str.replace(home_str, "~");
}
} }
if entry.path().is_dir() { if entry.path().is_dir() {

View file

@ -162,7 +162,8 @@ fn tokenize(expr: &str) -> Result<VecDeque<Token>, String> {
i += 1; i += 1;
} }
'0'..='9' | '.' => { '0'..='9' | '.' => {
// Only insert implicit multiplication if the last token is ')' and the last token in tokens is not already an operator (except ')') // Only insert implicit multiplication if the last token is ')'
// and the last token in tokens is not already an operator (except ')')
if let Some(Token::Op(')')) = last_token { if let Some(Token::Op(')')) = last_token {
if let Some(Token::Op(op)) = tokens.back() { if let Some(Token::Op(op)) = tokens.back() {
if *op == ')' { if *op == ')' {