From 9c8b4d084db52bb599b750955c6636b9316a00d3 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 28 Sep 2019 09:55:31 +0200 Subject: [PATCH 1/5] Version 0.2.1 bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb1e821..261229e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "espanso" -version = "0.2.0" +version = "0.2.1" dependencies = [ "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 297d585..bae6602 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "espanso" -version = "0.2.0" +version = "0.2.1" authors = ["Federico Terzi "] license = "GPL-3.0" description = "Cross-platform Text Expander written in Rust" From a51e1401170cdfd4a7327133281a30bba2248f6a Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 28 Sep 2019 09:56:59 +0200 Subject: [PATCH 2/5] Fix #52 --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 58a8c83..2546658 100644 --- a/src/main.rs +++ b/src/main.rs @@ -244,6 +244,7 @@ fn main() { // Defaults help print clap_instance.print_long_help().expect("Unable to print help"); + println!(); } /// Daemon subcommand, start the event loop and spawn a background thread worker From 7ddd7f8235fb9be52e1db89f89e338978873f1e6 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 28 Sep 2019 10:04:45 +0200 Subject: [PATCH 3/5] Add icon to notification on linux. Fix #48 --- src/res/linux/icon.png | Bin 0 -> 11596 bytes src/ui/linux.rs | 24 ++++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/res/linux/icon.png diff --git a/src/res/linux/icon.png b/src/res/linux/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9bde0d24159582e4cc84a24a3c9cec2010040328 GIT binary patch literal 11596 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 KMNUMnLSTXr4gyC2 literal 0 HcmV?d00001 diff --git a/src/ui/linux.rs b/src/ui/linux.rs index de8ac81..85c2cf7 100644 --- a/src/ui/linux.rs +++ b/src/ui/linux.rs @@ -19,14 +19,20 @@ use std::process::Command; use super::MenuItem; -use log::error; +use log::{error, info}; +use std::path::PathBuf; -pub struct LinuxUIManager {} +const LINUX_ICON_CONTENT : &'static [u8] = include_bytes!("../res/linux/icon.png"); + +pub struct LinuxUIManager { + icon_path: PathBuf, +} impl super::UIManager for LinuxUIManager { fn notify(&self, message: &str) { let res = Command::new("notify-send") - .args(&["-t", "2000", "espanso", message]) + .args(&["-i", self.icon_path.to_str().unwrap_or_default(), + "-t", "2000", "espanso", message]) .output(); if let Err(e) = res { @@ -45,6 +51,16 @@ impl super::UIManager for LinuxUIManager { impl LinuxUIManager { pub fn new() -> LinuxUIManager { - LinuxUIManager{} + // Initialize the icon if not present + let data_dir = crate::context::get_data_dir(); + let icon_path = data_dir.join("icon.png"); + if !icon_path.exists() { + info!("Creating espanso icon in '{}'", icon_path.to_str().unwrap_or_default()); + std::fs::write(&icon_path, LINUX_ICON_CONTENT).expect("Unable to copy espanso icon"); + } + + LinuxUIManager{ + icon_path + } } } \ No newline at end of file From b0280d444377354d30f5c08dcc64f8fc193c45a3 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 28 Sep 2019 10:21:01 +0200 Subject: [PATCH 4/5] Add credits to ModifyPath. Fix #37 --- README.md | 113 ++++++------------------------------------------------ 1 file changed, 11 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index f90cc17..bc9da88 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ ![example](images/example.gif) +Visit the [espanso website](https://espanso.org). + #### What is a Text Expander? A *text expander* is a program that detects when you type @@ -30,111 +32,13 @@ ___ * **Custom scripts** support * **Shell commands** support * **App-specific** configurations +* Expandable with **packages** +* Built-in **package manager** for [espanso hub](https://hub.espanso.org/) * File based configuration -## Table of contents +## Get Started -- [Installation](#installation) - - [Windows](#install-windows) - - [Linux](#install-linux) - - [macOS](#install-macos) -- [Usage](#usage) -- [FAQ](#faq) -- [Donations](#donations) -- [License](#license) - -## Installation - -### Windows - -The installation on Windows is pretty straightforward, navigate to the -[Release](https://github.com/federico-terzi/espanso/releases) page and -download the latest installer ( usually named like -`espanso-win-0.1.0.exe` ). - -Because espanso is not digitally signed, you may experience a warning from -Windows Smartscreen. In this case, just click on "More info" (1) and then -on "Run anyway" (2), as shown in the picture: - -![Windows Smartscreen](images/windows-smartscreen.png) - -If you completed the installation procedure, you should have espanso running. -A good way to find out is by going on any text field and typing `:espanso`. -You should see "Hi there!" appear. - -### Linux - -TODO - -### MacOS - -The easiest way to install espanso on macOS using the [Homebrew](https://brew.sh/) -package manager, but you can also do it manually. - -#### Using Homebrew - -The first thing to do is to add the official espanso *tap* to Homebrew with -the following command: - -``` -brew tap federico-terzi/espanso -``` - -Then you can install espanso with: - -``` -brew install espanso -``` - -To make sure that espanso was correctly installed, you can open a terminal and type: - -``` -espanso --version -``` - -At this point, you have to [Enable Accessibility](#enabling-accessibility) to use espanso. - -#### Enabling Accessibility - -Because espanso uses the macOS [Accessibility API](https://developer.apple.com/library/archive/documentation/Accessibility/Conceptual/AccessibilityMacOSX/) -to work, you need to authorize it using the following procedure: - -Open a terminal and type the command: - -``` -espanso install -``` - -A dialog should show up, click on "Open System Preferences", as shown here: - -![Accessibility Prompt](images/accessibility-prompt.png) - -Then, in the "Privacy" panel click on the Lock icon (1) to enable edits and -then check "espanso" (2), as shown in the picture: - -![Accessibility Settings](images/accessibility-macos-enable.png) - -Now open the terminal again and type: - -``` -espanso install -``` - -If everything goes well, you should see the espanso icon appear in the status bar: - -![macOS status bar icon](images/espanso-icon-macos-statusbar.png) - -If you now type `:espanso` in any text field, you should see "Hi there!" appear! - -## Usage - -TODO - -## FAQ - -#### How does espanso work? - -TODO +Visit the [official documentation](https://espanso.org/docs/). ## Donations @@ -144,6 +48,11 @@ please consider making a small donation, it really helps :) [![Donate with PayPal](images/donate.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FHNLR5DRS267E&source=url) +## Remarks + +* Special thanks to the [ModifyPath](https://www.legroom.net/software/modpath) + script, used by espanso to improve the Windows installer. + ## License espanso was created by [Federico Terzi](http://federicoterzi.com) From a5347cfffe342535b1d3362a59d83c5f533f9dc3 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 28 Sep 2019 10:44:25 +0200 Subject: [PATCH 5/5] Improve code quality and add tests --- src/check.rs | 4 +- src/config/mod.rs | 14 +++--- src/context/macos.rs | 4 +- src/context/windows.rs | 4 +- src/event/manager.rs | 2 +- src/main.rs | 34 +++++++-------- src/matcher/scrolling.rs | 2 +- src/package/default.rs | 58 ++++++++++++++++++------- src/res/test/install_package_index.json | 30 +++++++++++++ src/ui/linux.rs | 2 +- 10 files changed, 105 insertions(+), 49 deletions(-) diff --git a/src/check.rs b/src/check.rs index 34b4f80..146b170 100644 --- a/src/check.rs +++ b/src/check.rs @@ -30,7 +30,7 @@ pub fn check_dependencies() -> bool { let status = Command::new("notify-send") .arg("-v") .output(); - if let Err(_) = status { + if status.is_err() { println!("Error: 'notify-send' command is needed for espanso to work correctly, please install it."); result = false; } @@ -39,7 +39,7 @@ pub fn check_dependencies() -> bool { let status = Command::new("xclip") .arg("-version") .output(); - if let Err(_) = status { + if status.is_err() { println!("Error: 'xclip' command is needed for espanso to work correctly, please install it."); result = false; } diff --git a/src/config/mod.rs b/src/config/mod.rs index 9ae30fd..ce1f192 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -34,7 +34,6 @@ use walkdir::WalkDir; pub(crate) mod runtime; -// TODO: add documentation link const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("../res/config.yml"); const DEFAULT_CONFIG_FILE_NAME : &str = "default.yml"; @@ -162,7 +161,7 @@ impl Configs { let mut contents = String::new(); let res = file.read_to_string(&mut contents); - if let Err(_) = res { + if res.is_err() { return Err(ConfigLoadError::UnableToReadFile) } @@ -187,7 +186,7 @@ impl Configs { }); let parent_matches : Vec = self.matches.iter().filter(|&m| { !trigger_set.contains(&m.trigger) - }).map(|m| m.clone()).collect(); + }).cloned().collect(); merged_matches.extend(parent_matches); self.matches = merged_matches; @@ -200,7 +199,7 @@ impl Configs { }); let default_matches : Vec = default.matches.iter().filter(|&m| { !trigger_set.contains(&m.trigger) - }).map(|m| m.clone()).collect(); + }).cloned().collect(); self.matches.extend(default_matches); } @@ -326,7 +325,7 @@ impl ConfigSet { // Create the espanso dir if id doesn't exist let res = create_dir_all(espanso_dir.as_path()); - if let Ok(_) = res { + if res.is_ok() { let default_file = espanso_dir.join(DEFAULT_CONFIG_FILE_NAME); // If config file does not exist, create one from template @@ -358,13 +357,12 @@ impl ConfigSet { return ConfigSet::load(espanso_dir.as_path()) } - return Err(ConfigLoadError::UnableToCreateDefaultConfig) + Err(ConfigLoadError::UnableToCreateDefaultConfig) } pub fn get_default_config_dir() -> PathBuf { let home_dir = dirs::home_dir().expect("Unable to get home directory"); - let espanso_dir = home_dir.join(".espanso"); - espanso_dir + home_dir.join(".espanso") } pub fn get_default_packages_dir() -> PathBuf { diff --git a/src/context/macos.rs b/src/context/macos.rs index 3e6c0ea..c419618 100644 --- a/src/context/macos.rs +++ b/src/context/macos.rs @@ -27,7 +27,7 @@ use std::fs; use log::{info, error}; use std::process::exit; -const STATUS_ICON_BINARY : &'static [u8] = include_bytes!("../res/mac/icon.png"); +const STATUS_ICON_BINARY : &[u8] = include_bytes!("../res/mac/icon.png"); pub struct MacContext { pub send_channel: Sender @@ -42,7 +42,7 @@ impl MacContext { if res == 0 { error!("Accessibility must be enabled to make espanso work on MacOS."); error!("Please allow espanso in the Security & Privacy panel, then restart espanso."); - error!("For more information: "); // TODO: add documentation link + error!("For more information: https://espanso.org/install/mac/"); exit(1); } } diff --git a/src/context/windows.rs b/src/context/windows.rs index d45715f..39d2a14 100644 --- a/src/context/windows.rs +++ b/src/context/windows.rs @@ -26,8 +26,8 @@ use std::{fs}; use widestring::U16CString; use log::{info}; -const BMP_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.bmp"); -const ICO_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.ico"); +const BMP_BINARY : &[u8] = include_bytes!("../res/win/espanso.bmp"); +const ICO_BINARY : &[u8] = include_bytes!("../res/win/espanso.ico"); pub struct WindowsContext { send_channel: Sender, diff --git a/src/event/manager.rs b/src/event/manager.rs index a119122..b6cc0b1 100644 --- a/src/event/manager.rs +++ b/src/event/manager.rs @@ -55,7 +55,7 @@ impl <'a> EventManager for DefaultEventManager<'a> { } } }, - Err(_) => panic!("Broken event channel"), + Err(e) => panic!("Broken event channel {}", e), } } } diff --git a/src/main.rs b/src/main.rs index 2546658..2cd9e96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,7 @@ mod clipboard; mod extension; mod sysdaemon; -const VERSION: &'static str = env!("CARGO_PKG_VERSION"); +const VERSION: &str = env!("CARGO_PKG_VERSION"); const LOG_FILE: &str = "espanso.log"; fn main() { @@ -168,52 +168,52 @@ fn main() { return; } - if let Some(_) = matches.subcommand_matches("dump") { + if matches.subcommand_matches("dump").is_some() { println!("{:#?}", config_set); return; } - if let Some(_) = matches.subcommand_matches("detect") { + if matches.subcommand_matches("detect").is_some() { detect_main(); return; } - if let Some(_) = matches.subcommand_matches("daemon") { + if matches.subcommand_matches("daemon").is_some() { daemon_main(config_set); return; } - if let Some(_) = matches.subcommand_matches("register") { + if matches.subcommand_matches("register").is_some() { register_main(config_set); return; } - if let Some(_) = matches.subcommand_matches("unregister") { + if matches.subcommand_matches("unregister").is_some() { unregister_main(config_set); return; } - if let Some(_) = matches.subcommand_matches("log") { + if matches.subcommand_matches("log").is_some() { log_main(); return; } - if let Some(_) = matches.subcommand_matches("start") { + if matches.subcommand_matches("start").is_some() { start_main(config_set); return; } - if let Some(_) = matches.subcommand_matches("status") { + if matches.subcommand_matches("status").is_some() { status_main(); return; } - if let Some(_) = matches.subcommand_matches("stop") { + if matches.subcommand_matches("stop").is_some() { stop_main(config_set); return; } - if let Some(_) = matches.subcommand_matches("restart") { + if matches.subcommand_matches("restart").is_some() { restart_main(config_set); return; } @@ -236,7 +236,7 @@ fn main() { list_package_main(config_set, matches); return; } - if let Some(_) = matches.subcommand_matches("refresh") { + if matches.subcommand_matches("refresh").is_some() { update_index_main(config_set); return; } @@ -525,22 +525,22 @@ fn detect_main() { /// Send the given command to the espanso daemon fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) { - let command = if let Some(_) = matches.subcommand_matches("exit") { + let command = if matches.subcommand_matches("exit").is_some() { Some(IPCCommand { id: String::from("exit"), payload: String::from(""), }) - }else if let Some(_) = matches.subcommand_matches("toggle") { + }else if matches.subcommand_matches("toggle").is_some() { Some(IPCCommand { id: String::from("toggle"), payload: String::from(""), }) - }else if let Some(_) = matches.subcommand_matches("enable") { + }else if matches.subcommand_matches("enable").is_some() { Some(IPCCommand { id: String::from("enable"), payload: String::from(""), }) - }else if let Some(_) = matches.subcommand_matches("disable") { + }else if matches.subcommand_matches("disable").is_some() { Some(IPCCommand { id: String::from("disable"), payload: String::from(""), @@ -748,7 +748,7 @@ fn acquire_lock() -> Option { let res = file.try_lock_exclusive(); - if let Ok(_) = res { + if res.is_ok() { return Some(file) } diff --git a/src/matcher/scrolling.rs b/src/matcher/scrolling.rs index a14df4a..7df7894 100644 --- a/src/matcher/scrolling.rs +++ b/src/matcher/scrolling.rs @@ -126,7 +126,7 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMa if m == config.toggle_key { let mut toggle_press_time = self.toggle_press_time.borrow_mut(); if let Ok(elapsed) = toggle_press_time.elapsed() { - if elapsed.as_millis() < config.toggle_interval as u128 { + if elapsed.as_millis() < u128::from(config.toggle_interval) { self.toggle(); let is_enabled = self.is_enabled.borrow(); diff --git a/src/package/default.rs b/src/package/default.rs index 96ce792..8950d59 100644 --- a/src/package/default.rs +++ b/src/package/default.rs @@ -117,16 +117,14 @@ impl DefaultPackageManager { }else{ started = true; } - }else{ - if started { - let caps = FIELD_REGEX.captures(&line); - if let Some(caps) = caps { - let property = caps.get(1); - let value = caps.get(2); - if property.is_some() && value.is_some() { - fields.insert(property.unwrap().as_str().to_owned(), - value.unwrap().as_str().to_owned()); - } + }else if started { + let caps = FIELD_REGEX.captures(&line); + if let Some(caps) = caps { + let property = caps.get(1); + let value = caps.get(2); + if property.is_some() && value.is_some() { + fields.insert(property.unwrap().as_str().to_owned(), + value.unwrap().as_str().to_owned()); } } } @@ -161,7 +159,7 @@ impl DefaultPackageManager { return local_index.last_update } - return 0; + 0 } fn list_local_packages_names(&self) -> Vec { @@ -260,14 +258,14 @@ impl super::PackageManager for DefaultPackageManager { let readme_path = temp_package_dir.join("README.md"); let package = Self::parse_package_from_readme(&readme_path); - if !package.is_some() { - return Ok(InstallResult::UnableToParsePackageInfo); // TODO: test + if package.is_none() { + return Ok(InstallResult::UnableToParsePackageInfo); } let package = package.unwrap(); let source_dir = temp_package_dir.join(package.version); if !source_dir.exists() { - return Ok(InstallResult::MissingPackageVersion); // TODO: test + return Ok(InstallResult::MissingPackageVersion); } let target_dir = &self.package_dir.join(name); @@ -317,7 +315,7 @@ mod tests { use std::path::Path; use crate::package::PackageManager; use std::fs::{create_dir, create_dir_all}; - use crate::package::InstallResult::{Installed, NotFoundInRepo}; + use crate::package::InstallResult::*; use std::io::Write; const OUTDATED_INDEX_CONTENT : &str = include_str!("../res/test/outdated_index.json"); @@ -496,6 +494,36 @@ mod tests { assert_eq!(temp.package_manager.install_package("not-existing").unwrap(), NotFoundInRepo); } + #[test] + fn test_install_package_missing_version() { + let mut temp = create_temp_package_manager(|_, data_dir| { + let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE); + std::fs::write(index_file, INSTALL_PACKAGE_INDEX); + }); + + assert_eq!(temp.package_manager.install_package("dummy-package2").unwrap(), MissingPackageVersion); + } + + #[test] + fn test_install_package_missing_readme_unable_to_parse_package_info() { + let mut temp = create_temp_package_manager(|_, data_dir| { + let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE); + std::fs::write(index_file, INSTALL_PACKAGE_INDEX); + }); + + assert_eq!(temp.package_manager.install_package("dummy-package3").unwrap(), UnableToParsePackageInfo); + } + + #[test] + fn test_install_package_bad_readme_unable_to_parse_package_info() { + let mut temp = create_temp_package_manager(|_, data_dir| { + let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE); + std::fs::write(index_file, INSTALL_PACKAGE_INDEX); + }); + + assert_eq!(temp.package_manager.install_package("dummy-package4").unwrap(), UnableToParsePackageInfo); + } + #[test] fn test_list_local_packages() { let mut temp = create_temp_package_manager(|_, data_dir| { diff --git a/src/res/test/install_package_index.json b/src/res/test/install_package_index.json index df67503..c9b1e62 100644 --- a/src/res/test/install_package_index.json +++ b/src/res/test/install_package_index.json @@ -12,6 +12,36 @@ }, + { + "name": "dummy-package2", + "title": "Dummy Package", + "version": "9.9.9", + "repo": "https://github.com/federico-terzi/espanso-hub-core", + "desc": "Dummy package", + "author": "Federico Terzi" + + }, + + { + "name": "dummy-package3", + "title": "Dummy Package", + "version": "0.1.0", + "repo": "https://github.com/federico-terzi/espanso-hub-core", + "desc": "Dummy package", + "author": "Federico Terzi" + + }, + + { + "name": "dummy-package4", + "title": "Dummy Package", + "version": "0.1.0", + "repo": "https://github.com/federico-terzi/espanso-hub-core", + "desc": "Dummy package", + "author": "Federico Terzi" + + }, + { "name": "italian-accents", diff --git a/src/ui/linux.rs b/src/ui/linux.rs index 85c2cf7..f45d65a 100644 --- a/src/ui/linux.rs +++ b/src/ui/linux.rs @@ -22,7 +22,7 @@ use super::MenuItem; use log::{error, info}; use std::path::PathBuf; -const LINUX_ICON_CONTENT : &'static [u8] = include_bytes!("../res/linux/icon.png"); +const LINUX_ICON_CONTENT : &[u8] = include_bytes!("../res/linux/icon.png"); pub struct LinuxUIManager { icon_path: PathBuf,