diff --git a/.github/scripts/ubuntu/build_deb.sh b/.github/scripts/ubuntu/build_deb.sh new file mode 100755 index 0000000..d2352ad --- /dev/null +++ b/.github/scripts/ubuntu/build_deb.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e + +echo "Installing cargo-deb" +cargo install cargo-deb --version 1.34.0 + +cd espanso + +echo "Building X11 deb package" +cargo deb -p espanso + +echo "Building Wayland deb package" +cargo deb -p espanso --variant wayland -- --features wayland + +cd .. +cp espanso/target/debian/espanso_*.deb espanso-debian-x11-amd64.deb +sha256sum espanso-debian-x11-amd64.deb > espanso-debian-x11-amd64-sha256.txt +cp espanso/target/debian/espanso-wayland*.deb espanso-debian-wayland-amd64.deb +sha256sum espanso-debian-wayland-amd64.deb > espanso-debian-wayland-amd64-sha256.txt +ls -la + +echo "Copying to mounted volume" +cp espanso-debian-* /shared diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 974040b..195608c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,6 +117,34 @@ jobs: if: ${{ github.ref == 'refs/heads/master' }} run: | gh release upload ${{ needs.extract-version.outputs.espanso_version }} Espanso-X11.AppImage Espanso-X11.AppImage.sha256.txt + + linux-deb: + needs: ["extract-version", "create-release"] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Print target version + run: | + echo Using version ${{ needs.extract-version.outputs.espanso_version }} + - name: Build docker image + run: | + sudo docker build -t espanso-ubuntu . -f .github/scripts/ubuntu/Dockerfile + - name: Build Deb packages + run: | + sudo docker run --rm -v "$(pwd):/shared" espanso-ubuntu espanso/.github/scripts/ubuntu/build_deb.sh + - uses: actions/upload-artifact@v2 + name: "Upload artifacts" + with: + name: Ubuntu-Debian Artifacts + path: | + espanso-debian-x11-amd64.deb + espanso-debian-wayland-amd64.deb + - name: Upload artifacts to Github Releases (if master) + if: ${{ github.ref == 'refs/heads/master' }} + run: | + gh release upload ${{ needs.extract-version.outputs.espanso_version }} espanso-debian-x11-amd64.deb espanso-debian-wayland-amd64.deb espanso-debian-x11-amd64-sha256.txt espanso-debian-wayland-amd64-sha256.txt + macos-intel: needs: ["extract-version", "create-release"] @@ -203,4 +231,26 @@ jobs: - name: Upload artifacts to Github Releases (if master) if: ${{ github.ref == 'refs/heads/master' }} run: | - gh release upload ${{ needs.extract-version.outputs.espanso_version }} Espanso-Mac-M1.zip Espanso-Mac-M1.zip.sha256.txt \ No newline at end of file + gh release upload ${{ needs.extract-version.outputs.espanso_version }} Espanso-Mac-M1.zip Espanso-Mac-M1.zip.sha256.txt + + macos-publish-homebrew: + needs: ["extract-version", "create-release", "macos-m1", "macos-intel"] + runs-on: macos-11 + + steps: + - uses: actions/checkout@v2 + - name: Print target version + run: | + echo Using version ${{ needs.extract-version.outputs.espanso_version }} + + - name: "Setup SSH deploy key" + uses: webfactory/ssh-agent@fc49353b67b2b7c1e0e6a600572d01a69f2672dd + with: + ssh-private-key: ${{ secrets.HOMEBREW_CASK_SSH_PRIVATE_KEY }} + + - name: Create and Publish Homebrew Cask + if: ${{ github.ref == 'refs/heads/master' }} + run: | + VERSION="${{ needs.extract-version.outputs.espanso_version }}" ./scripts/publish_homebrew_version.sh + + echo "Cask formula has been published here: " \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 193d7ec..a9e3bdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,7 +572,7 @@ dependencies = [ [[package]] name = "espanso" -version = "2.1.2-alpha" +version = "2.1.3-alpha" dependencies = [ "anyhow", "caps", diff --git a/espanso-config/src/config/resolve.rs b/espanso-config/src/config/resolve.rs index 548c433..518c1ff 100644 --- a/espanso-config/src/config/resolve.rs +++ b/espanso-config/src/config/resolve.rs @@ -271,7 +271,7 @@ impl Config for ResolvedConfig { match self.parsed.search_trigger.as_deref() { Some("OFF") | Some("off") => None, Some(x) => Some(x.to_string()), - None => Some("jkj".to_string()), + None => None, } } diff --git a/espanso-config/src/matches/group/loader/yaml/mod.rs b/espanso-config/src/matches/group/loader/yaml/mod.rs index 1559fda..ebe1dea 100644 --- a/espanso-config/src/matches/group/loader/yaml/mod.rs +++ b/espanso-config/src/matches/group/loader/yaml/mod.rs @@ -300,6 +300,7 @@ pub fn try_convert_into_match( effect, label: yaml_match.label, id: next_id(), + search_terms: yaml_match.search_terms.unwrap_or_default(), }, warnings, )) diff --git a/espanso-config/src/matches/group/loader/yaml/parse.rs b/espanso-config/src/matches/group/loader/yaml/parse.rs index 50c1463..bcdfe18 100644 --- a/espanso-config/src/matches/group/loader/yaml/parse.rs +++ b/espanso-config/src/matches/group/loader/yaml/parse.rs @@ -114,6 +114,9 @@ pub struct YAMLMatch { #[serde(default)] pub html: Option, + + #[serde(default)] + pub search_terms: Option>, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] diff --git a/espanso-config/src/matches/mod.rs b/espanso-config/src/matches/mod.rs index 33fd8e4..474b40b 100644 --- a/espanso-config/src/matches/mod.rs +++ b/espanso-config/src/matches/mod.rs @@ -35,6 +35,7 @@ pub struct Match { // Metadata pub label: Option, + pub search_terms: Vec, } impl Default for Match { @@ -44,6 +45,7 @@ impl Default for Match { effect: MatchEffect::None, label: None, id: 0, + search_terms: vec![], } } } @@ -66,6 +68,15 @@ impl Match { pub fn cause_description(&self) -> Option<&str> { self.cause.description() } + + pub fn search_terms(&self) -> Vec<&str> { + self + .search_terms + .iter() + .map(|term| term.as_str()) + .chain(self.cause.search_terms()) + .collect() + } } // Causes @@ -100,6 +111,14 @@ impl MatchCause { // TODO: insert rendering for hotkey/shortcut // TODO: insert rendering for regex? I'm worried it might be too long } + + pub fn search_terms(&self) -> Vec<&str> { + if let MatchCause::Trigger(trigger_cause) = &self { + trigger_cause.triggers.iter().map(|s| s.as_str()).collect() + } else { + vec![] + } + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/espanso-engine/src/process/middleware/context_menu.rs b/espanso-engine/src/process/middleware/context_menu.rs index 9ee53fd..55a756f 100644 --- a/espanso-engine/src/process/middleware/context_menu.rs +++ b/espanso-engine/src/process/middleware/context_menu.rs @@ -99,7 +99,7 @@ impl Middleware for ContextMenuMiddleware { 0, MenuItem::Simple(SimpleMenuItem { id: CONTEXT_ITEM_SECURE_INPUT_EXPLAIN, - label: "Why is espanso not working?".to_string(), + label: "Why is Espanso not working?".to_string(), }), ); items.insert( diff --git a/espanso-inject/src/mac/mod.rs b/espanso-inject/src/mac/mod.rs index 2bb12d7..fc8a2ee 100644 --- a/espanso-inject/src/mac/mod.rs +++ b/espanso-inject/src/mac/mod.rs @@ -32,7 +32,7 @@ use crate::{keys, InjectionOptions, Injector}; #[allow(improper_ctypes)] #[link(name = "espansoinject", kind = "static")] extern "C" { - pub fn inject_string(string: *const c_char); + pub fn inject_string(string: *const c_char, delay: i32); pub fn inject_separate_vkeys(vkey_array: *const i32, vkey_count: i32, delay: i32); pub fn inject_vkeys_combination(vkey_array: *const i32, vkey_count: i32, delay: i32); } @@ -60,10 +60,10 @@ impl MacInjector { } impl Injector for MacInjector { - fn send_string(&self, string: &str, _: InjectionOptions) -> Result<()> { + fn send_string(&self, string: &str, options: InjectionOptions) -> Result<()> { let c_string = CString::new(string)?; unsafe { - inject_string(c_string.as_ptr()); + inject_string(c_string.as_ptr(), options.delay); } Ok(()) } diff --git a/espanso-inject/src/mac/native.h b/espanso-inject/src/mac/native.h index 6d52913..abab728 100644 --- a/espanso-inject/src/mac/native.h +++ b/espanso-inject/src/mac/native.h @@ -23,7 +23,7 @@ #include // Inject a complete string using the KEYEVENTF_UNICODE flag -extern "C" void inject_string(char * string); +extern "C" void inject_string(char * string, int32_t delay); // Send a sequence of vkey presses and releases extern "C" void inject_separate_vkeys(int32_t *vkey_array, int32_t vkey_count, int32_t delay); diff --git a/espanso-inject/src/mac/native.mm b/espanso-inject/src/mac/native.mm index cecbb56..8828c28 100644 --- a/espanso-inject/src/mac/native.mm +++ b/espanso-inject/src/mac/native.mm @@ -27,8 +27,10 @@ // so that we can later skip them in the detect module. CGPoint ESPANSO_POINT_MARKER = CGPointMake(-27469, 0); -void inject_string(char *string) +void inject_string(char *string, int32_t delay) { + long udelay = delay * 1000; + char * stringCopy = strdup(string); dispatch_async(dispatch_get_main_queue(), ^(void) { // Convert the c string to a UniChar array as required by the CGEventKeyboardSetUnicodeString method @@ -49,7 +51,7 @@ void inject_string(char *string) CGEventPost(kCGHIDEventTap, e2); CFRelease(e2); - usleep(2000); + usleep(udelay); } // Because of a bug ( or undocumented limit ) of the CGEventKeyboardSetUnicodeString method @@ -69,7 +71,7 @@ void inject_string(char *string) CGEventPost(kCGHIDEventTap, e); CFRelease(e); - usleep(2000); + usleep(udelay); // Some applications require an explicit release of the space key // For more information: https://github.com/federico-terzi/espanso/issues/159 @@ -78,7 +80,7 @@ void inject_string(char *string) CGEventPost(kCGHIDEventTap, e2); CFRelease(e2); - usleep(2000); + usleep(udelay); i += chunk_size; } diff --git a/espanso-modulo/src/search/algorithm.rs b/espanso-modulo/src/search/algorithm.rs index 80c829a..9faf0be 100644 --- a/espanso-modulo/src/search/algorithm.rs +++ b/espanso-modulo/src/search/algorithm.rs @@ -44,7 +44,9 @@ fn exact_match(query: &str, items: &[SearchItem]) -> Vec { .iter() .enumerate() .filter(|(_, item)| { - item.label.contains(query) || item.trigger.as_deref().map_or(false, |t| t.contains(query)) + item.label.contains(query) + || item.trigger.as_deref().map_or(false, |t| t.contains(query)) + || item.search_terms.iter().any(|term| term.contains(query)) }) .map(|(i, _)| i) .collect() @@ -61,6 +63,10 @@ fn case_insensitive_exact_match(query: &str, items: &[SearchItem]) -> Vec .trigger .as_deref() .map_or(false, |t| t.to_lowercase().contains(query)) + || item + .search_terms + .iter() + .any(|term| term.to_lowercase().contains(&lowercase_query)) }) .map(|(i, _)| i) .collect() @@ -79,6 +85,10 @@ fn case_insensitive_keyword(query: &str, items: &[SearchItem]) -> Vec { .trigger .as_deref() .map_or(false, |t| t.to_lowercase().contains(keyword)) + && !item + .search_terms + .iter() + .any(|term| term.to_lowercase().contains(keyword)) { return false; } diff --git a/espanso-modulo/src/search/config.rs b/espanso-modulo/src/search/config.rs index 69fc542..38635fc 100644 --- a/espanso-modulo/src/search/config.rs +++ b/espanso-modulo/src/search/config.rs @@ -58,6 +58,7 @@ pub struct SearchItem { pub id: String, pub label: String, pub trigger: Option, + pub search_terms: Vec, #[serde(default)] pub is_builtin: bool, diff --git a/espanso-modulo/src/search/generator.rs b/espanso-modulo/src/search/generator.rs index 98a9c88..497d69c 100644 --- a/espanso-modulo/src/search/generator.rs +++ b/espanso-modulo/src/search/generator.rs @@ -28,6 +28,7 @@ pub fn generate(config: SearchConfig) -> types::Search { id: item.id, label: item.label, trigger: item.trigger, + search_terms: item.search_terms, is_builtin: item.is_builtin, }) .collect(); diff --git a/espanso-modulo/src/sys/search/mod.rs b/espanso-modulo/src/sys/search/mod.rs index de44836..6ed1efe 100644 --- a/espanso-modulo/src/sys/search/mod.rs +++ b/espanso-modulo/src/sys/search/mod.rs @@ -26,6 +26,7 @@ pub mod types { pub id: String, pub label: String, pub trigger: Option, + pub search_terms: Vec, pub is_builtin: bool, } diff --git a/espanso/Cargo.toml b/espanso/Cargo.toml index 65def50..5a5a745 100644 --- a/espanso/Cargo.toml +++ b/espanso/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "espanso" -version = "2.1.2-alpha" +version = "2.1.3-alpha" authors = ["Federico Terzi "] license = "GPL-3.0" description = "Cross-platform Text Expander written in Rust" @@ -72,4 +72,16 @@ espanso-mac-utils = { path = "../espanso-mac-utils" } [target.'cfg(target_os="linux")'.dependencies] caps = "0.5.2" const_format = "0.2.14" -regex = "1.4.3" \ No newline at end of file +regex = "1.4.3" + +[package.metadata.deb] +maintainer = "Federico Terzi " +depends = "$auto, systemd, libxtst6, xclip, libnotify-bin, libxkbcommon0, libwxgtk3.0-gtk3-0v5" +section = "utility" +license-file = ["../LICENSE", "1"] + +[package.metadata.deb.variants.wayland] +depends = "$auto, systemd, libnotify-bin, libxkbcommon0, libwxgtk3.0-gtk3-0v5" +# TODO: once this issue [1] is fixed, we should create a variant for +# wayland to automatically run the setcap script. +# [1]: https://github.com/mmstick/cargo-deb/issues/151 diff --git a/espanso/src/cli/service/mod.rs b/espanso/src/cli/service/mod.rs index 6cf18fa..b9dc7fc 100644 --- a/espanso/src/cli/service/mod.rs +++ b/espanso/src/cli/service/mod.rs @@ -103,6 +103,8 @@ fn service_main(args: CliModuleArgs) -> i32 { stop_main(&paths); std::thread::sleep(std::time::Duration::from_millis(300)); return start_main(&paths, &paths_overrides, sub_args); + } else { + eprintln!("Invalid usage, please run `espanso service --help` for more information."); } SERVICE_SUCCESS diff --git a/espanso/src/cli/worker/engine/dispatch/executor/secure_input.rs b/espanso/src/cli/worker/engine/dispatch/executor/secure_input.rs index 40ae47c..4a1f911 100644 --- a/espanso/src/cli/worker/engine/dispatch/executor/secure_input.rs +++ b/espanso/src/cli/worker/engine/dispatch/executor/secure_input.rs @@ -33,9 +33,7 @@ impl SecureInputManagerAdapter { impl SecureInputManager for SecureInputManagerAdapter { fn display_secure_input_troubleshoot(&self) -> anyhow::Result<()> { - // TODO: replace with actual URL - // TODO: in the future, this might be a self-contained WebView window - opener::open_browser("https://espanso.org/docs")?; + opener::open_browser("https://espanso.org/docs/next/troubleshooting/secure-input/")?; Ok(()) } diff --git a/espanso/src/cli/worker/engine/process/middleware/match_select.rs b/espanso/src/cli/worker/engine/process/middleware/match_select.rs index 0f0b74e..a4850c7 100644 --- a/espanso/src/cli/worker/engine/process/middleware/match_select.rs +++ b/espanso/src/cli/worker/engine/process/middleware/match_select.rs @@ -32,6 +32,7 @@ pub struct MatchSummary<'a> { pub id: i32, pub label: &'a str, pub tag: Option<&'a str>, + pub additional_search_terms: Vec<&'a str>, pub is_builtin: bool, } @@ -65,6 +66,11 @@ impl<'a> MatchSelector for MatchSelectorAdapter<'a> { id: m.id.to_string(), label: clipped_label, tag: m.tag.map(String::from), + additional_search_terms: m + .additional_search_terms + .into_iter() + .map(String::from) + .collect(), is_builtin: m.is_builtin, } }) diff --git a/espanso/src/cli/worker/engine/process/middleware/render/extension/choice.rs b/espanso/src/cli/worker/engine/process/middleware/render/extension/choice.rs index d2f245d..8fe4ddf 100644 --- a/espanso/src/cli/worker/engine/process/middleware/render/extension/choice.rs +++ b/espanso/src/cli/worker/engine/process/middleware/render/extension/choice.rs @@ -49,6 +49,7 @@ fn convert_items(choices: &[espanso_render::extension::choice::Choice]) -> Vec super::engine::process::middleware::match_select::MatchProvider<'a> id: m.id, label: m.description(), tag: m.cause_description(), + additional_search_terms: m.search_terms(), is_builtin: false, }, MatchVariant::Builtin(m) => MatchSummary { id: m.id, label: m.label, tag: m.triggers.first().map(String::as_ref), + additional_search_terms: vec![], is_builtin: true, }, }) diff --git a/espanso/src/gui/mod.rs b/espanso/src/gui/mod.rs index 86ae5db..2c1e998 100644 --- a/espanso/src/gui/mod.rs +++ b/espanso/src/gui/mod.rs @@ -32,6 +32,7 @@ pub struct SearchItem { pub id: String, pub label: String, pub tag: Option, + pub additional_search_terms: Vec, pub is_builtin: bool, } diff --git a/espanso/src/gui/modulo/search.rs b/espanso/src/gui/modulo/search.rs index 163ef94..a55d525 100644 --- a/espanso/src/gui/modulo/search.rs +++ b/espanso/src/gui/modulo/search.rs @@ -73,6 +73,7 @@ struct ModuloSearchItemConfig<'a> { id: &'a str, label: &'a str, trigger: Option<&'a str>, + search_terms: Vec<&'a str>, is_builtin: bool, } @@ -84,6 +85,15 @@ fn convert_items(items: &[SearchItem]) -> Vec { id: &item.id, label: &item.label, trigger: item.tag.as_deref(), + search_terms: if item.additional_search_terms.is_empty() { + vec![] + } else { + item + .additional_search_terms + .iter() + .map(|term| term.as_str()) + .collect() + }, is_builtin: item.is_builtin, }) .collect() diff --git a/espanso/src/main.rs b/espanso/src/main.rs index 97d42d9..d89dfd5 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -360,7 +360,7 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#)) .subcommand(restart_subcommand.clone()) .subcommand(stop_subcommand.clone()) .subcommand(status_subcommand.clone()) - .about("Register and manage 'espanso' as a system service."), + .about("A collection of commands to manage the Espanso service (for example, enabling auto-start on system boot)."), ) .subcommand(start_subcommand) .subcommand(restart_subcommand) diff --git a/espanso/src/patch/patches/linux/thunderbird_x11.rs b/espanso/src/patch/patches/linux/thunderbird_x11.rs index eeb366a..8474bcb 100644 --- a/espanso/src/patch/patches/linux/thunderbird_x11.rs +++ b/espanso/src/patch/patches/linux/thunderbird_x11.rs @@ -19,8 +19,6 @@ use std::sync::Arc; -use espanso_config::config::Backend; - use crate::patch::patches::{PatchedConfig, Patches}; use crate::patch::PatchDefinition; @@ -34,8 +32,6 @@ pub fn patch() -> PatchDefinition { base, name, Patches { - paste_shortcut: Some(Some("CTRL+SHIFT+V".to_string())), - backend: Some(Backend::Clipboard), key_delay: Some(Some(15)), inject_delay: Some(Some(15)), ..Default::default() diff --git a/scripts/publish_homebrew_version.sh b/scripts/publish_homebrew_version.sh new file mode 100755 index 0000000..7c378ae --- /dev/null +++ b/scripts/publish_homebrew_version.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e + +if [[ -z "$VERSION" ]]; then + echo "Missing target VERSION environment variable, please specify it" + exit 1 +fi + +# Removing the v suffix, if present +VERSION=${VERSION#"v"} + +rm -Rf target/homebrew +mkdir -p target/homebrew/artifacts + +echo "Targeting version $VERSION" +echo "Downloading macOS artifacts" + +gh release download v$VERSION --pattern "Espanso-Mac*" --dir target/homebrew/artifacts + +echo "Reading artifacts hashes" +INTEL_SHA=$(cat target/homebrew/artifacts/Espanso-Mac-Intel.zip.sha256.txt | awk -F ' ' '{print $1}') +M1_SHA=$(cat target/homebrew/artifacts/Espanso-Mac-M1.zip.sha256.txt | awk -F ' ' '{print $1}') + +echo "Cloning tap repository" + +pushd target/homebrew +git clone git@github.com:espanso/homebrew-espanso.git + +pushd homebrew-espanso +echo "Rendering formula template" + +cat ../../../scripts/resources/macos/formula_template.rb | sed "s/{{{VERSION}}}/$VERSION/g" | \ + sed "s/{{{INTEL_SHA}}}/$INTEL_SHA/g" | sed "s/{{{M1_SHA}}}/$M1_SHA/g" > ./Casks/espanso.rb + +echo "Committing version update" +git add Casks/espanso.rb +git commit -m "Version bump: $VERSION" + +echo "Pushing changes" +git push + +echo "Done!" \ No newline at end of file diff --git a/scripts/resources/macos/formula_template.rb b/scripts/resources/macos/formula_template.rb new file mode 100644 index 0000000..7a3e299 --- /dev/null +++ b/scripts/resources/macos/formula_template.rb @@ -0,0 +1,19 @@ +cask "espanso" do + version "{{{VERSION}}}" + + if Hardware::CPU.intel? + url "https://github.com/federico-terzi/espanso/releases/download/v#{version}/Espanso-Mac-Intel.zip" + sha256 "{{{INTEL_SHA}}}" + else + url "https://github.com/federico-terzi/espanso/releases/download/v#{version}/Espanso-Mac-M1.zip" + sha256 "{{{M1_SHA}}}" + end + + name "Espanso" + desc "A Privacy-first, Cross-platform Text Expander" + homepage "https://espanso.org/" + + app "Espanso.app" + + zap trash: "~/Library/Caches/espanso" +end \ No newline at end of file diff --git a/snapcraft.yaml b/snapcraft.yaml index c69662a..82b7b3e 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,5 +1,5 @@ name: espanso -version: 2.1.2-alpha +version: 2.1.3-alpha summary: A Cross-platform Text Expander written in Rust description: | espanso is a Cross-platform, Text Expander written in Rust.