commit
e37aa98616
2
.github/scripts/ubuntu/Dockerfile
vendored
2
.github/scripts/ubuntu/Dockerfile
vendored
|
@ -9,7 +9,7 @@ RUN apt-get update \
|
|||
ENV RUSTUP_HOME=/usr/local/rustup \
|
||||
CARGO_HOME=/usr/local/cargo \
|
||||
PATH=/usr/local/cargo/bin:$PATH \
|
||||
RUST_VERSION=1.55.0
|
||||
RUST_VERSION=1.57.0
|
||||
|
||||
RUN set -eux; \
|
||||
dpkgArch="$(dpkg --print-architecture)"; \
|
||||
|
|
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -187,9 +187,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.66"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -212,6 +212,7 @@ dependencies = [
|
|||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"pure-rust-locales",
|
||||
"time",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
@ -399,6 +400,16 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cstr_core"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "644828c273c063ab0d39486ba42a5d1f3a499d35529c759e763a9c6cb8a0fb08"
|
||||
dependencies = [
|
||||
"cty",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.20"
|
||||
|
@ -409,6 +420,12 @@ dependencies = [
|
|||
"syn 1.0.67",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cty"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
||||
|
||||
[[package]]
|
||||
name = "dbus"
|
||||
version = "0.9.1"
|
||||
|
@ -572,7 +589,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "espanso"
|
||||
version = "2.1.3-alpha"
|
||||
version = "2.1.4-beta"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"caps",
|
||||
|
@ -851,6 +868,7 @@ dependencies = [
|
|||
"log",
|
||||
"rand 0.8.3",
|
||||
"regex",
|
||||
"sys-locale",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -2040,6 +2058,12 @@ dependencies = [
|
|||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pure-rust-locales"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b45c49fc4f91f35bae654f85ebb3a44d60ac64f11b3166ffa609def390c732d8"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
|
@ -2577,6 +2601,19 @@ dependencies = [
|
|||
"unicode-xid 0.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sys-locale"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91f89ebb59fa30d4f65fafc2d68e94f6975256fd87e812dd99cb6e020c8563df"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cstr_core",
|
||||
"libc",
|
||||
"web-sys",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
version = "0.3.7"
|
||||
|
|
|
@ -28,4 +28,4 @@ widestring = "0.4.3"
|
|||
wait-timeout = { version = "0.2.0", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
cc = "1.0.73"
|
|
@ -30,8 +30,6 @@ int32_t clipboard_get_text(char * buffer, int32_t buffer_size) {
|
|||
const char * text = [string UTF8String];
|
||||
strncpy(buffer, text, buffer_size);
|
||||
|
||||
[string release];
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,3 +21,5 @@ pub(crate) const DEFAULT_CLIPBOARD_THRESHOLD: usize = 100;
|
|||
pub(crate) const DEFAULT_PRE_PASTE_DELAY: usize = 100;
|
||||
pub(crate) const DEFAULT_SHORTCUT_EVENT_DELAY: usize = 10;
|
||||
pub(crate) const DEFAULT_RESTORE_CLIPBOARD_DELAY: usize = 300;
|
||||
pub(crate) const DEFAULT_POST_FORM_DELAY: usize = 200;
|
||||
pub(crate) const DEFAULT_POST_SEARCH_DELAY: usize = 200;
|
||||
|
|
|
@ -150,6 +150,18 @@ pub trait Config: Send + Sync {
|
|||
// If false, avoid showing the SecureInput notification on macOS
|
||||
fn secure_input_notification(&self) -> bool;
|
||||
|
||||
// The number of milliseconds to wait after a form has been closed.
|
||||
// This is useful to let the target application regain focus
|
||||
// after a form has been closed, otherwise the injection might
|
||||
// not be targeted to the right application.
|
||||
fn post_form_delay(&self) -> usize;
|
||||
|
||||
// The number of milliseconds to wait after the search bar has been closed.
|
||||
// This is useful to let the target application regain focus
|
||||
// after the search bar has been closed, otherwise the injection might
|
||||
// not be targeted to the right application.
|
||||
fn post_search_delay(&self) -> usize;
|
||||
|
||||
// If true, use the `xclip` command to implement the clipboard instead of
|
||||
// the built-in native module on X11.
|
||||
fn x11_use_xclip_backend(&self) -> bool;
|
||||
|
@ -188,6 +200,8 @@ pub trait Config: Send + Sync {
|
|||
toggle_key: {:?}
|
||||
auto_restart: {:?}
|
||||
restore_clipboard_delay: {:?}
|
||||
post_form_delay: {:?}
|
||||
post_search_delay: {:?}
|
||||
backspace_limit: {}
|
||||
search_trigger: {:?}
|
||||
search_shortcut: {:?}
|
||||
|
@ -220,6 +234,8 @@ pub trait Config: Send + Sync {
|
|||
self.toggle_key(),
|
||||
self.auto_restart(),
|
||||
self.restore_clipboard_delay(),
|
||||
self.post_form_delay(),
|
||||
self.post_search_delay(),
|
||||
self.backspace_limit(),
|
||||
self.search_trigger(),
|
||||
self.search_shortcut(),
|
||||
|
|
|
@ -44,6 +44,8 @@ pub(crate) struct ParsedConfig {
|
|||
pub show_notifications: Option<bool>,
|
||||
pub show_icon: Option<bool>,
|
||||
pub secure_input_notification: Option<bool>,
|
||||
pub post_form_delay: Option<usize>,
|
||||
pub post_search_delay: Option<usize>,
|
||||
pub win32_exclude_orphan_events: Option<bool>,
|
||||
pub win32_keyboard_layout_cache_interval: Option<i64>,
|
||||
pub x11_use_xclip_backend: Option<bool>,
|
||||
|
|
|
@ -103,6 +103,12 @@ pub(crate) struct YAMLConfig {
|
|||
#[serde(default)]
|
||||
pub show_icon: Option<bool>,
|
||||
|
||||
#[serde(default)]
|
||||
pub post_form_delay: Option<usize>,
|
||||
|
||||
#[serde(default)]
|
||||
pub post_search_delay: Option<usize>,
|
||||
|
||||
#[serde(default)]
|
||||
pub secure_input_notification: Option<bool>,
|
||||
|
||||
|
@ -201,6 +207,8 @@ impl TryFrom<YAMLConfig> for ParsedConfig {
|
|||
pre_paste_delay: yaml_config.pre_paste_delay,
|
||||
restore_clipboard_delay: yaml_config.restore_clipboard_delay,
|
||||
paste_shortcut_event_delay: yaml_config.paste_shortcut_event_delay,
|
||||
post_form_delay: yaml_config.post_form_delay,
|
||||
post_search_delay: yaml_config.post_search_delay,
|
||||
|
||||
win32_exclude_orphan_events: yaml_config.win32_exclude_orphan_events,
|
||||
win32_keyboard_layout_cache_interval: yaml_config.win32_keyboard_layout_cache_interval,
|
||||
|
@ -260,6 +268,8 @@ mod tests {
|
|||
show_icon: false
|
||||
show_notifications: false
|
||||
secure_input_notification: false
|
||||
post_form_delay: 300
|
||||
post_search_delay: 400
|
||||
win32_exclude_orphan_events: false
|
||||
win32_keyboard_layout_cache_interval: 300
|
||||
x11_use_xclip_backend: true
|
||||
|
@ -314,6 +324,8 @@ mod tests {
|
|||
show_icon: Some(false),
|
||||
show_notifications: Some(false),
|
||||
secure_input_notification: Some(false),
|
||||
post_form_delay: Some(300),
|
||||
post_search_delay: Some(400),
|
||||
win32_exclude_orphan_events: Some(false),
|
||||
win32_keyboard_layout_cache_interval: Some(300),
|
||||
x11_use_xclip_backend: Some(true),
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
use super::{
|
||||
default::{
|
||||
DEFAULT_CLIPBOARD_THRESHOLD, DEFAULT_PRE_PASTE_DELAY, DEFAULT_RESTORE_CLIPBOARD_DELAY,
|
||||
DEFAULT_SHORTCUT_EVENT_DELAY,
|
||||
DEFAULT_CLIPBOARD_THRESHOLD, DEFAULT_POST_FORM_DELAY, DEFAULT_POST_SEARCH_DELAY,
|
||||
DEFAULT_PRE_PASTE_DELAY, DEFAULT_RESTORE_CLIPBOARD_DELAY, DEFAULT_SHORTCUT_EVENT_DELAY,
|
||||
},
|
||||
parse::ParsedConfig,
|
||||
path::calculate_paths,
|
||||
|
@ -299,6 +299,20 @@ impl Config for ResolvedConfig {
|
|||
self.parsed.secure_input_notification.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn post_form_delay(&self) -> usize {
|
||||
self
|
||||
.parsed
|
||||
.post_form_delay
|
||||
.unwrap_or(DEFAULT_POST_FORM_DELAY)
|
||||
}
|
||||
|
||||
fn post_search_delay(&self) -> usize {
|
||||
self
|
||||
.parsed
|
||||
.post_search_delay
|
||||
.unwrap_or(DEFAULT_POST_SEARCH_DELAY)
|
||||
}
|
||||
|
||||
fn win32_exclude_orphan_events(&self) -> bool {
|
||||
self.parsed.win32_exclude_orphan_events.unwrap_or(true)
|
||||
}
|
||||
|
@ -398,6 +412,8 @@ impl ResolvedConfig {
|
|||
show_icon,
|
||||
show_notifications,
|
||||
secure_input_notification,
|
||||
post_form_delay,
|
||||
post_search_delay,
|
||||
win32_exclude_orphan_events,
|
||||
win32_keyboard_layout_cache_interval,
|
||||
x11_use_xclip_backend,
|
||||
|
|
|
@ -387,6 +387,14 @@ impl Config for LegacyInteropConfig {
|
|||
self.config.enable_active
|
||||
}
|
||||
|
||||
fn post_form_delay(&self) -> usize {
|
||||
crate::config::default::DEFAULT_POST_FORM_DELAY
|
||||
}
|
||||
|
||||
fn post_search_delay(&self) -> usize {
|
||||
crate::config::default::DEFAULT_POST_SEARCH_DELAY
|
||||
}
|
||||
|
||||
fn win32_exclude_orphan_events(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ scopeguard = "1.1.0"
|
|||
sctk = { package = "smithay-client-toolkit", version = "0.14.0", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
cc = "1.0.73"
|
||||
|
||||
[dev-dependencies]
|
||||
enum-as-inner = "0.3.3"
|
|
@ -37,7 +37,6 @@ pub fn get_active_layout() -> Option<String> {
|
|||
if gnome::is_gnome() {
|
||||
gnome::get_active_layout()
|
||||
} else {
|
||||
log::warn!("unable to determine the currently active layout, you might need to explicitly specify the layout in the config for espanso to work correctly.");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ impl<'a> Middleware for ImageResolverMiddleware<'a> {
|
|||
if let EventType::ImageRequested(m_event) = &event.etype {
|
||||
// On Windows, we have to replace the forward / with the backslash \ in the path
|
||||
let path = if cfg!(target_os = "windows") {
|
||||
m_event.image_path.replace("/", "\\")
|
||||
m_event.image_path.replace('/', "\\")
|
||||
} else {
|
||||
m_event.image_path.to_owned()
|
||||
};
|
||||
|
|
|
@ -20,4 +20,4 @@ lazy_static = "1.4.0"
|
|||
widestring = "0.4.3"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
cc = "1.0.73"
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
int32_t info_get_title(char *buffer, int32_t buffer_size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
CFArrayRef windows = CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements | kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
int32_t result = 0;
|
||||
|
||||
|
@ -46,7 +45,6 @@ int32_t info_get_title(char *buffer, int32_t buffer_size)
|
|||
|
||||
CFRelease(windows);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -54,7 +52,6 @@ int32_t info_get_title(char *buffer, int32_t buffer_size)
|
|||
// Partially taken from: https://stackoverflow.com/questions/480866/get-the-title-of-the-current-active-window-document-in-mac-os-x/23451568#23451568
|
||||
int32_t info_get_title_fallback(char *buffer, int32_t buffer_size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
// Get the process ID of the frontmost application.
|
||||
NSRunningApplication* app = [[NSWorkspace sharedWorkspace] frontmostApplication];
|
||||
pid_t pid = [app processIdentifier];
|
||||
|
@ -93,31 +90,26 @@ int32_t info_get_title_fallback(char *buffer, int32_t buffer_size)
|
|||
} else {
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t info_get_exec(char *buffer, int32_t buffer_size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSRunningApplication *frontApp = [[NSWorkspace sharedWorkspace] frontmostApplication];
|
||||
NSString *bundlePath = [frontApp bundleURL].path;
|
||||
const char * path = [bundlePath UTF8String];
|
||||
|
||||
snprintf(buffer, buffer_size, "%s", path);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t info_get_class(char *buffer, int32_t buffer_size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSRunningApplication *frontApp = [[NSWorkspace sharedWorkspace] frontmostApplication];
|
||||
NSString *bundleId = frontApp.bundleIdentifier;
|
||||
const char * bundle = [bundleId UTF8String];
|
||||
|
||||
snprintf(buffer, buffer_size, "%s", bundle);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -27,7 +27,7 @@ scopeguard = "1.1.0"
|
|||
itertools = "0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
cc = "1.0.73"
|
||||
|
||||
[dev-dependencies]
|
||||
enum-as-inner = "0.3.3"
|
|
@ -14,4 +14,4 @@ lazy_static = "1.4.0"
|
|||
regex = "1.4.3"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
cc = "1.0.73"
|
|
@ -15,7 +15,7 @@ lazy_static = "1.4.0"
|
|||
regex = "1.4.3"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
cc = "1.0.73"
|
||||
regex = "1.4.3"
|
||||
zip = "0.5.12"
|
||||
winres = "0.1.11"
|
||||
|
|
|
@ -49,7 +49,7 @@ fn build_native() {
|
|||
.expect("unable to extract wxWidgets source dir");
|
||||
|
||||
// Compile wxWidgets
|
||||
let tool = cc::windows_registry::find_tool("msvc", "msbuild")
|
||||
let tool = cc::windows_registry::find_tool("msvc", "devenv")
|
||||
.expect("unable to locate MSVC compiler, did you install Visual Studio?");
|
||||
let mut vcvars_path = None;
|
||||
let mut current_root = tool.path();
|
||||
|
|
|
@ -41,7 +41,7 @@ const long DEFAULT_STYLE = wxSTAY_ON_TOP | wxFRAME_TOOL_WINDOW | wxRESIZE_BORDER
|
|||
#endif
|
||||
#ifdef __LINUX__
|
||||
const int SEARCH_BAR_FONT_SIZE = 20;
|
||||
const long DEFAULT_STYLE = wxSTAY_ON_TOP | wxFRAME_TOOL_WINDOW | wxBORDER_NONE;
|
||||
const long DEFAULT_STYLE = wxSTAY_ON_TOP;
|
||||
#endif
|
||||
|
||||
const int HELP_TEXT_FONT_SIZE = 10;
|
||||
|
|
|
@ -10,6 +10,7 @@ anyhow = "1.0.38"
|
|||
thiserror = "1.0.23"
|
||||
regex = "1.4.3"
|
||||
lazy_static = "1.4.0"
|
||||
chrono = "0.4.19"
|
||||
chrono = {version = "0.4.19", features=["unstable-locales"]}
|
||||
enum-as-inner = "0.3.3"
|
||||
rand = "0.8.3"
|
||||
sys-locale = "0.1.0"
|
|
@ -17,22 +17,30 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use chrono::{DateTime, Duration, Local};
|
||||
use chrono::{DateTime, Duration, Local, Locale};
|
||||
|
||||
use crate::{Extension, ExtensionOutput, ExtensionResult, Number, Params, Value};
|
||||
|
||||
pub struct DateExtension {
|
||||
pub trait LocaleProvider {
|
||||
fn get_system_locale(&self) -> String;
|
||||
}
|
||||
|
||||
pub struct DateExtension<'a> {
|
||||
fixed_date: Option<DateTime<Local>>,
|
||||
locale_provider: &'a dyn LocaleProvider,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl DateExtension {
|
||||
pub fn new() -> Self {
|
||||
Self { fixed_date: None }
|
||||
impl<'a> DateExtension<'a> {
|
||||
pub fn new(locale_provider: &'a dyn LocaleProvider) -> Self {
|
||||
Self {
|
||||
fixed_date: None,
|
||||
locale_provider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extension for DateExtension {
|
||||
impl<'a> Extension for DateExtension<'a> {
|
||||
fn name(&self) -> &str {
|
||||
"date"
|
||||
}
|
||||
|
@ -53,9 +61,14 @@ impl Extension for DateExtension {
|
|||
}
|
||||
|
||||
let format = params.get("format");
|
||||
let locale = params
|
||||
.get("locale")
|
||||
.and_then(|val| val.as_string())
|
||||
.map(String::from)
|
||||
.unwrap_or_else(|| self.locale_provider.get_system_locale());
|
||||
|
||||
let date = if let Some(Value::String(format)) = format {
|
||||
now.format(format).to_string()
|
||||
DateExtension::format_date_with_locale_string(now, format, &locale)
|
||||
} else {
|
||||
now.to_rfc2822()
|
||||
};
|
||||
|
@ -64,7 +77,7 @@ impl Extension for DateExtension {
|
|||
}
|
||||
}
|
||||
|
||||
impl DateExtension {
|
||||
impl<'a> DateExtension<'a> {
|
||||
fn get_date(&self) -> DateTime<Local> {
|
||||
if let Some(fixed_date) = self.fixed_date {
|
||||
fixed_date
|
||||
|
@ -72,6 +85,340 @@ impl DateExtension {
|
|||
Local::now()
|
||||
}
|
||||
}
|
||||
|
||||
fn format_date_with_locale(date: DateTime<Local>, format: &str, locale: Locale) -> String {
|
||||
date.format_localized(format, locale).to_string()
|
||||
}
|
||||
|
||||
fn format_date_with_locale_string(
|
||||
date: DateTime<Local>,
|
||||
format: &str,
|
||||
locale_str: &str,
|
||||
) -> String {
|
||||
let locale = convert_locale_string_to_locale(locale_str).unwrap_or(Locale::en_US);
|
||||
Self::format_date_with_locale(date, format, locale)
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_locale_string_to_locale(locale_str: &str) -> Option<Locale> {
|
||||
match locale_str {
|
||||
"aa-DJ" => Some(Locale::aa_DJ),
|
||||
"aa-ER" => Some(Locale::aa_ER),
|
||||
"aa-ET" => Some(Locale::aa_ET),
|
||||
"af-ZA" => Some(Locale::af_ZA),
|
||||
"agr-PE" => Some(Locale::agr_PE),
|
||||
"ak-GH" => Some(Locale::ak_GH),
|
||||
"am-ET" => Some(Locale::am_ET),
|
||||
"an-ES" => Some(Locale::an_ES),
|
||||
"anp-IN" => Some(Locale::anp_IN),
|
||||
"ar-AE" => Some(Locale::ar_AE),
|
||||
"ar-BH" => Some(Locale::ar_BH),
|
||||
"ar-DZ" => Some(Locale::ar_DZ),
|
||||
"ar-EG" => Some(Locale::ar_EG),
|
||||
"ar-IN" => Some(Locale::ar_IN),
|
||||
"ar-IQ" => Some(Locale::ar_IQ),
|
||||
"ar-JO" => Some(Locale::ar_JO),
|
||||
"ar-KW" => Some(Locale::ar_KW),
|
||||
"ar-LB" => Some(Locale::ar_LB),
|
||||
"ar-LY" => Some(Locale::ar_LY),
|
||||
"ar-MA" => Some(Locale::ar_MA),
|
||||
"ar-OM" => Some(Locale::ar_OM),
|
||||
"ar-QA" => Some(Locale::ar_QA),
|
||||
"ar-SA" => Some(Locale::ar_SA),
|
||||
"ar-SD" => Some(Locale::ar_SD),
|
||||
"ar-SS" => Some(Locale::ar_SS),
|
||||
"ar-SY" => Some(Locale::ar_SY),
|
||||
"ar-TN" => Some(Locale::ar_TN),
|
||||
"ar-YE" => Some(Locale::ar_YE),
|
||||
"as-IN" => Some(Locale::as_IN),
|
||||
"ast-ES" => Some(Locale::ast_ES),
|
||||
"ayc-PE" => Some(Locale::ayc_PE),
|
||||
"az-AZ" => Some(Locale::az_AZ),
|
||||
"az-IR" => Some(Locale::az_IR),
|
||||
"be-BY" => Some(Locale::be_BY),
|
||||
"bem-ZM" => Some(Locale::bem_ZM),
|
||||
"ber-DZ" => Some(Locale::ber_DZ),
|
||||
"ber-MA" => Some(Locale::ber_MA),
|
||||
"bg-BG" => Some(Locale::bg_BG),
|
||||
"bhb-IN" => Some(Locale::bhb_IN),
|
||||
"bho-IN" => Some(Locale::bho_IN),
|
||||
"bho-NP" => Some(Locale::bho_NP),
|
||||
"bi-VU" => Some(Locale::bi_VU),
|
||||
"bn-BD" => Some(Locale::bn_BD),
|
||||
"bn-IN" => Some(Locale::bn_IN),
|
||||
"bo-CN" => Some(Locale::bo_CN),
|
||||
"bo-IN" => Some(Locale::bo_IN),
|
||||
"br-FR" => Some(Locale::br_FR),
|
||||
"brx-IN" => Some(Locale::brx_IN),
|
||||
"bs-BA" => Some(Locale::bs_BA),
|
||||
"byn-ER" => Some(Locale::byn_ER),
|
||||
"ca-AD" => Some(Locale::ca_AD),
|
||||
"ca-ES" => Some(Locale::ca_ES),
|
||||
"ca-FR" => Some(Locale::ca_FR),
|
||||
"ca-IT" => Some(Locale::ca_IT),
|
||||
"ce-RU" => Some(Locale::ce_RU),
|
||||
"chr-US" => Some(Locale::chr_US),
|
||||
"cmn-TW" => Some(Locale::cmn_TW),
|
||||
"crh-UA" => Some(Locale::crh_UA),
|
||||
"cs-CZ" => Some(Locale::cs_CZ),
|
||||
"csb-PL" => Some(Locale::csb_PL),
|
||||
"cv-RU" => Some(Locale::cv_RU),
|
||||
"cy-GB" => Some(Locale::cy_GB),
|
||||
"da-DK" => Some(Locale::da_DK),
|
||||
"de-AT" => Some(Locale::de_AT),
|
||||
"de-BE" => Some(Locale::de_BE),
|
||||
"de-CH" => Some(Locale::de_CH),
|
||||
"de-DE" => Some(Locale::de_DE),
|
||||
"de-IT" => Some(Locale::de_IT),
|
||||
"de-LI" => Some(Locale::de_LI),
|
||||
"de-LU" => Some(Locale::de_LU),
|
||||
"doi-IN" => Some(Locale::doi_IN),
|
||||
"dsb-DE" => Some(Locale::dsb_DE),
|
||||
"dv-MV" => Some(Locale::dv_MV),
|
||||
"dz-BT" => Some(Locale::dz_BT),
|
||||
"el-CY" => Some(Locale::el_CY),
|
||||
"el-GR" => Some(Locale::el_GR),
|
||||
"en-AG" => Some(Locale::en_AG),
|
||||
"en-AU" => Some(Locale::en_AU),
|
||||
"en-BW" => Some(Locale::en_BW),
|
||||
"en-CA" => Some(Locale::en_CA),
|
||||
"en-DK" => Some(Locale::en_DK),
|
||||
"en-GB" => Some(Locale::en_GB),
|
||||
"en-HK" => Some(Locale::en_HK),
|
||||
"en-IE" => Some(Locale::en_IE),
|
||||
"en-IL" => Some(Locale::en_IL),
|
||||
"en-IN" => Some(Locale::en_IN),
|
||||
"en-NG" => Some(Locale::en_NG),
|
||||
"en-NZ" => Some(Locale::en_NZ),
|
||||
"en-PH" => Some(Locale::en_PH),
|
||||
"en-SC" => Some(Locale::en_SC),
|
||||
"en-SG" => Some(Locale::en_SG),
|
||||
"en-US" => Some(Locale::en_US),
|
||||
"en-ZA" => Some(Locale::en_ZA),
|
||||
"en-ZM" => Some(Locale::en_ZM),
|
||||
"en-ZW" => Some(Locale::en_ZW),
|
||||
"eo" => Some(Locale::eo),
|
||||
"es-AR" => Some(Locale::es_AR),
|
||||
"es-BO" => Some(Locale::es_BO),
|
||||
"es-CL" => Some(Locale::es_CL),
|
||||
"es-CO" => Some(Locale::es_CO),
|
||||
"es-CR" => Some(Locale::es_CR),
|
||||
"es-CU" => Some(Locale::es_CU),
|
||||
"es-DO" => Some(Locale::es_DO),
|
||||
"es-EC" => Some(Locale::es_EC),
|
||||
"es-ES" => Some(Locale::es_ES),
|
||||
"es-GT" => Some(Locale::es_GT),
|
||||
"es-HN" => Some(Locale::es_HN),
|
||||
"es-MX" => Some(Locale::es_MX),
|
||||
"es-NI" => Some(Locale::es_NI),
|
||||
"es-PA" => Some(Locale::es_PA),
|
||||
"es-PE" => Some(Locale::es_PE),
|
||||
"es-PR" => Some(Locale::es_PR),
|
||||
"es-PY" => Some(Locale::es_PY),
|
||||
"es-SV" => Some(Locale::es_SV),
|
||||
"es-US" => Some(Locale::es_US),
|
||||
"es-UY" => Some(Locale::es_UY),
|
||||
"es-VE" => Some(Locale::es_VE),
|
||||
"et-EE" => Some(Locale::et_EE),
|
||||
"eu-ES" => Some(Locale::eu_ES),
|
||||
"fa-IR" => Some(Locale::fa_IR),
|
||||
"ff-SN" => Some(Locale::ff_SN),
|
||||
"fi-FI" => Some(Locale::fi_FI),
|
||||
"fil-PH" => Some(Locale::fil_PH),
|
||||
"fo-FO" => Some(Locale::fo_FO),
|
||||
"fr-BE" => Some(Locale::fr_BE),
|
||||
"fr-CA" => Some(Locale::fr_CA),
|
||||
"fr-CH" => Some(Locale::fr_CH),
|
||||
"fr-FR" => Some(Locale::fr_FR),
|
||||
"fr-LU" => Some(Locale::fr_LU),
|
||||
"fur-IT" => Some(Locale::fur_IT),
|
||||
"fy-DE" => Some(Locale::fy_DE),
|
||||
"fy-NL" => Some(Locale::fy_NL),
|
||||
"ga-IE" => Some(Locale::ga_IE),
|
||||
"gd-GB" => Some(Locale::gd_GB),
|
||||
"gez-ER" => Some(Locale::gez_ER),
|
||||
"gez-ET" => Some(Locale::gez_ET),
|
||||
"gl-ES" => Some(Locale::gl_ES),
|
||||
"gu-IN" => Some(Locale::gu_IN),
|
||||
"gv-GB" => Some(Locale::gv_GB),
|
||||
"ha-NG" => Some(Locale::ha_NG),
|
||||
"hak-TW" => Some(Locale::hak_TW),
|
||||
"he-IL" => Some(Locale::he_IL),
|
||||
"hi-IN" => Some(Locale::hi_IN),
|
||||
"hif-FJ" => Some(Locale::hif_FJ),
|
||||
"hne-IN" => Some(Locale::hne_IN),
|
||||
"hr-HR" => Some(Locale::hr_HR),
|
||||
"hsb-DE" => Some(Locale::hsb_DE),
|
||||
"ht-HT" => Some(Locale::ht_HT),
|
||||
"hu-HU" => Some(Locale::hu_HU),
|
||||
"hy-AM" => Some(Locale::hy_AM),
|
||||
"ia-FR" => Some(Locale::ia_FR),
|
||||
"id-ID" => Some(Locale::id_ID),
|
||||
"ig-NG" => Some(Locale::ig_NG),
|
||||
"ik-CA" => Some(Locale::ik_CA),
|
||||
"is-IS" => Some(Locale::is_IS),
|
||||
"it-CH" => Some(Locale::it_CH),
|
||||
"it-IT" => Some(Locale::it_IT),
|
||||
"iu-CA" => Some(Locale::iu_CA),
|
||||
"ja-JP" => Some(Locale::ja_JP),
|
||||
"ka-GE" => Some(Locale::ka_GE),
|
||||
"kab-DZ" => Some(Locale::kab_DZ),
|
||||
"kk-KZ" => Some(Locale::kk_KZ),
|
||||
"kl-GL" => Some(Locale::kl_GL),
|
||||
"km-KH" => Some(Locale::km_KH),
|
||||
"kn-IN" => Some(Locale::kn_IN),
|
||||
"ko-KR" => Some(Locale::ko_KR),
|
||||
"kok-IN" => Some(Locale::kok_IN),
|
||||
"ks-IN" => Some(Locale::ks_IN),
|
||||
"ku-TR" => Some(Locale::ku_TR),
|
||||
"kw-GB" => Some(Locale::kw_GB),
|
||||
"ky-KG" => Some(Locale::ky_KG),
|
||||
"lb-LU" => Some(Locale::lb_LU),
|
||||
"lg-UG" => Some(Locale::lg_UG),
|
||||
"li-BE" => Some(Locale::li_BE),
|
||||
"li-NL" => Some(Locale::li_NL),
|
||||
"lij-IT" => Some(Locale::lij_IT),
|
||||
"ln-CD" => Some(Locale::ln_CD),
|
||||
"lo-LA" => Some(Locale::lo_LA),
|
||||
"lt-LT" => Some(Locale::lt_LT),
|
||||
"lv-LV" => Some(Locale::lv_LV),
|
||||
"lzh-TW" => Some(Locale::lzh_TW),
|
||||
"mag-IN" => Some(Locale::mag_IN),
|
||||
"mai-IN" => Some(Locale::mai_IN),
|
||||
"mai-NP" => Some(Locale::mai_NP),
|
||||
"mfe-MU" => Some(Locale::mfe_MU),
|
||||
"mg-MG" => Some(Locale::mg_MG),
|
||||
"mhr-RU" => Some(Locale::mhr_RU),
|
||||
"mi-NZ" => Some(Locale::mi_NZ),
|
||||
"miq-NI" => Some(Locale::miq_NI),
|
||||
"mjw-IN" => Some(Locale::mjw_IN),
|
||||
"mk-MK" => Some(Locale::mk_MK),
|
||||
"ml-IN" => Some(Locale::ml_IN),
|
||||
"mn-MN" => Some(Locale::mn_MN),
|
||||
"mni-IN" => Some(Locale::mni_IN),
|
||||
"mnw-MM" => Some(Locale::mnw_MM),
|
||||
"mr-IN" => Some(Locale::mr_IN),
|
||||
"ms-MY" => Some(Locale::ms_MY),
|
||||
"mt-MT" => Some(Locale::mt_MT),
|
||||
"my-MM" => Some(Locale::my_MM),
|
||||
"nan-TW" => Some(Locale::nan_TW),
|
||||
"nb-NO" => Some(Locale::nb_NO),
|
||||
"nds-DE" => Some(Locale::nds_DE),
|
||||
"nds-NL" => Some(Locale::nds_NL),
|
||||
"ne-NP" => Some(Locale::ne_NP),
|
||||
"nhn-MX" => Some(Locale::nhn_MX),
|
||||
"niu-NU" => Some(Locale::niu_NU),
|
||||
"niu-NZ" => Some(Locale::niu_NZ),
|
||||
"nl-AW" => Some(Locale::nl_AW),
|
||||
"nl-BE" => Some(Locale::nl_BE),
|
||||
"nl-NL" => Some(Locale::nl_NL),
|
||||
"nn-NO" => Some(Locale::nn_NO),
|
||||
"nr-ZA" => Some(Locale::nr_ZA),
|
||||
"nso-ZA" => Some(Locale::nso_ZA),
|
||||
"oc-FR" => Some(Locale::oc_FR),
|
||||
"om-ET" => Some(Locale::om_ET),
|
||||
"om-KE" => Some(Locale::om_KE),
|
||||
"or-IN" => Some(Locale::or_IN),
|
||||
"os-RU" => Some(Locale::os_RU),
|
||||
"pa-IN" => Some(Locale::pa_IN),
|
||||
"pa-PK" => Some(Locale::pa_PK),
|
||||
"pap-AW" => Some(Locale::pap_AW),
|
||||
"pap-CW" => Some(Locale::pap_CW),
|
||||
"pl-PL" => Some(Locale::pl_PL),
|
||||
"ps-AF" => Some(Locale::ps_AF),
|
||||
"pt-BR" => Some(Locale::pt_BR),
|
||||
"pt-PT" => Some(Locale::pt_PT),
|
||||
"quz-PE" => Some(Locale::quz_PE),
|
||||
"raj-IN" => Some(Locale::raj_IN),
|
||||
"ro-RO" => Some(Locale::ro_RO),
|
||||
"ru-RU" => Some(Locale::ru_RU),
|
||||
"ru-UA" => Some(Locale::ru_UA),
|
||||
"rw-RW" => Some(Locale::rw_RW),
|
||||
"sa-IN" => Some(Locale::sa_IN),
|
||||
"sah-RU" => Some(Locale::sah_RU),
|
||||
"sat-IN" => Some(Locale::sat_IN),
|
||||
"sc-IT" => Some(Locale::sc_IT),
|
||||
"sd-IN" => Some(Locale::sd_IN),
|
||||
"se-NO" => Some(Locale::se_NO),
|
||||
"sgs-LT" => Some(Locale::sgs_LT),
|
||||
"shn-MM" => Some(Locale::shn_MM),
|
||||
"shs-CA" => Some(Locale::shs_CA),
|
||||
"si-LK" => Some(Locale::si_LK),
|
||||
"sid-ET" => Some(Locale::sid_ET),
|
||||
"sk-SK" => Some(Locale::sk_SK),
|
||||
"sl-SI" => Some(Locale::sl_SI),
|
||||
"sm-WS" => Some(Locale::sm_WS),
|
||||
"so-DJ" => Some(Locale::so_DJ),
|
||||
"so-ET" => Some(Locale::so_ET),
|
||||
"so-KE" => Some(Locale::so_KE),
|
||||
"so-SO" => Some(Locale::so_SO),
|
||||
"sq-AL" => Some(Locale::sq_AL),
|
||||
"sq-MK" => Some(Locale::sq_MK),
|
||||
"sr-ME" => Some(Locale::sr_ME),
|
||||
"sr-RS" => Some(Locale::sr_RS),
|
||||
"ss-ZA" => Some(Locale::ss_ZA),
|
||||
"st-ZA" => Some(Locale::st_ZA),
|
||||
"sv-FI" => Some(Locale::sv_FI),
|
||||
"sv-SE" => Some(Locale::sv_SE),
|
||||
"sw-KE" => Some(Locale::sw_KE),
|
||||
"sw-TZ" => Some(Locale::sw_TZ),
|
||||
"szl-PL" => Some(Locale::szl_PL),
|
||||
"ta-IN" => Some(Locale::ta_IN),
|
||||
"ta-LK" => Some(Locale::ta_LK),
|
||||
"tcy-IN" => Some(Locale::tcy_IN),
|
||||
"te-IN" => Some(Locale::te_IN),
|
||||
"tg-TJ" => Some(Locale::tg_TJ),
|
||||
"th-TH" => Some(Locale::th_TH),
|
||||
"the-NP" => Some(Locale::the_NP),
|
||||
"ti-ER" => Some(Locale::ti_ER),
|
||||
"ti-ET" => Some(Locale::ti_ET),
|
||||
"tig-ER" => Some(Locale::tig_ER),
|
||||
"tk-TM" => Some(Locale::tk_TM),
|
||||
"tl-PH" => Some(Locale::tl_PH),
|
||||
"tn-ZA" => Some(Locale::tn_ZA),
|
||||
"to-TO" => Some(Locale::to_TO),
|
||||
"tpi-PG" => Some(Locale::tpi_PG),
|
||||
"tr-CY" => Some(Locale::tr_CY),
|
||||
"tr-TR" => Some(Locale::tr_TR),
|
||||
"ts-ZA" => Some(Locale::ts_ZA),
|
||||
"tt-RU" => Some(Locale::tt_RU),
|
||||
"ug-CN" => Some(Locale::ug_CN),
|
||||
"uk-UA" => Some(Locale::uk_UA),
|
||||
"unm-US" => Some(Locale::unm_US),
|
||||
"ur-IN" => Some(Locale::ur_IN),
|
||||
"ur-PK" => Some(Locale::ur_PK),
|
||||
"uz-UZ" => Some(Locale::uz_UZ),
|
||||
"ve-ZA" => Some(Locale::ve_ZA),
|
||||
"vi-VN" => Some(Locale::vi_VN),
|
||||
"wa-BE" => Some(Locale::wa_BE),
|
||||
"wae-CH" => Some(Locale::wae_CH),
|
||||
"wal-ET" => Some(Locale::wal_ET),
|
||||
"wo-SN" => Some(Locale::wo_SN),
|
||||
"xh-ZA" => Some(Locale::xh_ZA),
|
||||
"yi-US" => Some(Locale::yi_US),
|
||||
"yo-NG" => Some(Locale::yo_NG),
|
||||
"yue-HK" => Some(Locale::yue_HK),
|
||||
"yuw-PG" => Some(Locale::yuw_PG),
|
||||
"zh-CN" => Some(Locale::zh_CN),
|
||||
"zh-HK" => Some(Locale::zh_HK),
|
||||
"zh-SG" => Some(Locale::zh_SG),
|
||||
"zh-TW" => Some(Locale::zh_TW),
|
||||
"zu-ZA" => Some(Locale::zu_ZA),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultLocaleProvider {}
|
||||
impl LocaleProvider for DefaultLocaleProvider {
|
||||
fn get_system_locale(&self) -> String {
|
||||
sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"))
|
||||
}
|
||||
}
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl DefaultLocaleProvider {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -79,9 +426,30 @@ mod tests {
|
|||
use super::*;
|
||||
use chrono::offset::TimeZone;
|
||||
|
||||
struct MockLocaleProvider {
|
||||
locale: String,
|
||||
}
|
||||
impl LocaleProvider for MockLocaleProvider {
|
||||
fn get_system_locale(&self) -> String {
|
||||
self.locale.clone()
|
||||
}
|
||||
}
|
||||
impl MockLocaleProvider {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
locale: "en-US".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_locale(locale: String) -> Self {
|
||||
Self { locale }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_formatted_correctly() {
|
||||
let mut extension = DateExtension::new();
|
||||
let locale_provider = MockLocaleProvider::new();
|
||||
let mut extension = DateExtension::new(&locale_provider);
|
||||
extension.fixed_date = Some(Local.ymd(2014, 7, 8).and_hms(9, 10, 11));
|
||||
|
||||
let param = vec![("format".to_string(), Value::String("%H:%M:%S".to_string()))]
|
||||
|
@ -98,7 +466,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn offset_works_correctly() {
|
||||
let mut extension = DateExtension::new();
|
||||
let locale_provider = MockLocaleProvider::new();
|
||||
let mut extension = DateExtension::new(&locale_provider);
|
||||
extension.fixed_date = Some(Local.ymd(2014, 7, 8).and_hms(9, 10, 11));
|
||||
|
||||
let param = vec![
|
||||
|
@ -115,4 +484,61 @@ mod tests {
|
|||
ExtensionOutput::Single("10:10:11".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_locale_works_correctly() {
|
||||
let locale_provider = MockLocaleProvider::new_with_locale("it-IT".to_string());
|
||||
let mut extension = DateExtension::new(&locale_provider);
|
||||
extension.fixed_date = Some(Local.ymd(2014, 7, 8).and_hms(9, 10, 11));
|
||||
|
||||
let param = vec![("format".to_string(), Value::String("%A".to_string()))]
|
||||
.into_iter()
|
||||
.collect::<Params>();
|
||||
assert_eq!(
|
||||
extension
|
||||
.calculate(&Default::default(), &Default::default(), ¶m)
|
||||
.into_success()
|
||||
.unwrap(),
|
||||
ExtensionOutput::Single("martedì".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_locale_should_default_to_en_us() {
|
||||
let locale_provider = MockLocaleProvider::new_with_locale("invalid".to_string());
|
||||
let mut extension = DateExtension::new(&locale_provider);
|
||||
extension.fixed_date = Some(Local.ymd(2014, 7, 8).and_hms(9, 10, 11));
|
||||
|
||||
let param = vec![("format".to_string(), Value::String("%A".to_string()))]
|
||||
.into_iter()
|
||||
.collect::<Params>();
|
||||
assert_eq!(
|
||||
extension
|
||||
.calculate(&Default::default(), &Default::default(), ¶m)
|
||||
.into_success()
|
||||
.unwrap(),
|
||||
ExtensionOutput::Single("Tuesday".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn override_locale() {
|
||||
let locale_provider = MockLocaleProvider::new();
|
||||
let mut extension = DateExtension::new(&locale_provider);
|
||||
extension.fixed_date = Some(Local.ymd(2014, 7, 8).and_hms(9, 10, 11));
|
||||
|
||||
let param = vec![
|
||||
("format".to_string(), Value::String("%A".to_string())),
|
||||
("locale".to_string(), Value::String("it-IT".to_string())),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<Params>();
|
||||
assert_eq!(
|
||||
extension
|
||||
.calculate(&Default::default(), &Default::default(), ¶m)
|
||||
.into_success()
|
||||
.unwrap(),
|
||||
ExtensionOutput::Single("martedì".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
99
espanso-render/src/extension/exec_util.rs
Normal file
99
espanso-render/src/extension/exec_util.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2022 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub enum MacShell {
|
||||
Bash,
|
||||
Sh,
|
||||
Zsh,
|
||||
}
|
||||
|
||||
// Determine the PATH env variable value available inside a regular terminal session
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn determine_path_env_variable_override(explicit_shell: Option<MacShell>) -> Option<String> {
|
||||
let shell: MacShell = explicit_shell.or_else(determine_default_macos_shell)?;
|
||||
|
||||
match shell {
|
||||
MacShell::Bash => {
|
||||
launch_command_and_get_output("bash", &["--login", "-c", "source ~/.bashrc; echo $PATH"])
|
||||
}
|
||||
MacShell::Sh => launch_command_and_get_output("sh", &["--login", "-c", "echo $PATH"]),
|
||||
MacShell::Zsh => {
|
||||
launch_command_and_get_output("zsh", &["--login", "-c", "source ~/.zshrc; echo $PATH"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn determine_path_env_variable_override(_: Option<MacShell>) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn determine_default_macos_shell() -> Option<MacShell> {
|
||||
use regex::Regex;
|
||||
use std::process::Command;
|
||||
|
||||
let output = Command::new("sh")
|
||||
.args(&["--login", "-c", "dscl . -read ~/ UserShell"])
|
||||
.output()
|
||||
.ok()?;
|
||||
|
||||
lazy_static! {
|
||||
static ref EXTRACT_SHELL_REGEX: Regex =
|
||||
Regex::new(r"UserShell:\s(.*)$").expect("unable to generate regex to extract default shell");
|
||||
}
|
||||
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
let captures = EXTRACT_SHELL_REGEX.captures(output_str.trim())?;
|
||||
|
||||
let shell = captures.get(1)?.as_str().trim();
|
||||
|
||||
if shell.ends_with("/bash") {
|
||||
Some(MacShell::Bash)
|
||||
} else if shell.ends_with("/zsh") {
|
||||
Some(MacShell::Zsh)
|
||||
} else if shell.ends_with("/sh") {
|
||||
Some(MacShell::Sh)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn determine_default_macos_shell() -> Option<MacShell> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn launch_command_and_get_output(command: &str, args: &[&str]) -> Option<String> {
|
||||
use std::process::Command;
|
||||
|
||||
let output = Command::new(command).args(args).output().ok()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
Some(output_str.to_string())
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
* Copyright (C) 2019-2022 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -21,6 +21,7 @@ pub mod choice;
|
|||
pub mod clipboard;
|
||||
pub mod date;
|
||||
pub mod echo;
|
||||
mod exec_util;
|
||||
pub mod form;
|
||||
pub mod random;
|
||||
pub mod script;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
* Copyright (C) 2019-2022 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,7 +24,7 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{Extension, ExtensionOutput, ExtensionResult, Params, Value};
|
||||
use log::{error, info};
|
||||
use log::{debug, error, info};
|
||||
use thiserror::Error;
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
|
@ -35,10 +35,16 @@ pub enum Shell {
|
|||
WSL2,
|
||||
Bash,
|
||||
Sh,
|
||||
Zsh,
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
fn execute_cmd(&self, cmd: &str, vars: &HashMap<String, String>) -> std::io::Result<Output> {
|
||||
fn execute_cmd(
|
||||
&self,
|
||||
cmd: &str,
|
||||
vars: &HashMap<String, String>,
|
||||
override_path_on_macos: bool,
|
||||
) -> std::io::Result<Output> {
|
||||
let mut is_wsl = false;
|
||||
|
||||
let mut command = match self {
|
||||
|
@ -74,6 +80,11 @@ impl Shell {
|
|||
command.args(&["-c", cmd]);
|
||||
command
|
||||
}
|
||||
Shell::Zsh => {
|
||||
let mut command = Command::new("zsh");
|
||||
command.args(&["-c", cmd]);
|
||||
command
|
||||
}
|
||||
};
|
||||
|
||||
// Set the OS-specific flags
|
||||
|
@ -84,6 +95,27 @@ impl Shell {
|
|||
command.env(key, value);
|
||||
}
|
||||
|
||||
// If Espanso is executed as an app bundle on macOS, it doesn't inherit the PATH
|
||||
// environment variables that are available inside a terminal, and this can be confusing for users.
|
||||
// For example, one might use "jq" inside the terminal but then it throws an error with "command not found"
|
||||
// if launched through the Espanso shell extension.
|
||||
// For this reason, Espanso tries to obtain the same PATH value by spawning a login shell and extracting
|
||||
// the PATH after the processing.
|
||||
if cfg!(target_os = "macos") && override_path_on_macos {
|
||||
let supported_mac_shell = match self {
|
||||
Shell::Bash => Some(super::exec_util::MacShell::Bash),
|
||||
Shell::Sh => Some(super::exec_util::MacShell::Sh),
|
||||
Shell::Zsh => Some(super::exec_util::MacShell::Zsh),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(path_env_override) =
|
||||
super::exec_util::determine_path_env_variable_override(supported_mac_shell)
|
||||
{
|
||||
debug!("overriding PATH env variable with: {}", path_env_override);
|
||||
command.env("PATH", path_env_override);
|
||||
}
|
||||
}
|
||||
|
||||
// In WSL environment, we have to specify which ENV variables
|
||||
// should be passed to linux.
|
||||
// For more information: https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/
|
||||
|
@ -110,6 +142,7 @@ impl Shell {
|
|||
"wsl2" => Some(Shell::WSL2),
|
||||
"bash" => Some(Shell::Bash),
|
||||
"sh" => Some(Shell::Sh),
|
||||
"zsh" => Some(Shell::Zsh),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +153,17 @@ impl Default for Shell {
|
|||
if cfg!(target_os = "windows") {
|
||||
Shell::Powershell
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Shell::Sh
|
||||
lazy_static! {
|
||||
static ref DEFAULT_MACOS_SHELL: Option<super::exec_util::MacShell> =
|
||||
super::exec_util::determine_default_macos_shell();
|
||||
}
|
||||
|
||||
match *DEFAULT_MACOS_SHELL {
|
||||
Some(super::exec_util::MacShell::Bash) => Shell::Bash,
|
||||
Some(super::exec_util::MacShell::Sh) => Shell::Sh,
|
||||
Some(super::exec_util::MacShell::Zsh) => Shell::Zsh,
|
||||
None => Shell::Sh,
|
||||
}
|
||||
} else if cfg!(target_os = "linux") {
|
||||
Shell::Bash
|
||||
} else {
|
||||
|
@ -172,7 +215,13 @@ impl Extension for ShellExtension {
|
|||
self.config_path.to_string_lossy().to_string(),
|
||||
);
|
||||
|
||||
match shell.execute_cmd(cmd, &env_variables) {
|
||||
let macos_override_path = params
|
||||
.get("macos_override_path")
|
||||
.and_then(|v| v.as_bool())
|
||||
.copied()
|
||||
.unwrap_or(true);
|
||||
|
||||
match shell.execute_cmd(cmd, &env_variables, macos_override_path) {
|
||||
Ok(output) => {
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
let error_str = String::from_utf8_lossy(&output.stderr);
|
||||
|
|
|
@ -31,4 +31,4 @@ notify-rust = "4.2.2"
|
|||
crossbeam = "0.8.0"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
cc = "1.0.73"
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "espanso"
|
||||
version = "2.1.3-alpha"
|
||||
version = "2.1.4-beta"
|
||||
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
description = "Cross-platform Text Expander written in Rust"
|
||||
|
@ -81,7 +81,7 @@ section = "utility"
|
|||
license-file = ["../LICENSE", "1"]
|
||||
|
||||
[package.metadata.deb.variants.wayland]
|
||||
depends = "$auto, systemd, libnotify-bin, libxkbcommon0, libwxgtk3.0-gtk3-0v5"
|
||||
depends = "$auto, systemd, libnotify-bin, libxkbcommon0, libwxgtk3.0-gtk3-0v5, wl-clipboard"
|
||||
# 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
|
||||
|
|
|
@ -197,3 +197,15 @@ impl<'a> espanso_engine::process::EnabledStatusProvider for ConfigManager<'a> {
|
|||
self.active().enable()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::gui::modulo::form::ModuloFormUIOptionProvider for ConfigManager<'a> {
|
||||
fn get_post_form_delay(&self) -> usize {
|
||||
self.active().post_form_delay()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::gui::modulo::search::ModuloSearchUIOptionProvider for ConfigManager<'a> {
|
||||
fn get_post_search_delay(&self) -> usize {
|
||||
self.active().post_search_delay()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,8 +107,10 @@ pub fn initialize_and_spawn(
|
|||
let default_config = &*config_manager.default();
|
||||
|
||||
let modulo_manager = crate::gui::modulo::manager::ModuloManager::new();
|
||||
let modulo_form_ui = crate::gui::modulo::form::ModuloFormUI::new(&modulo_manager);
|
||||
let modulo_search_ui = crate::gui::modulo::search::ModuloSearchUI::new(&modulo_manager);
|
||||
let modulo_form_ui =
|
||||
crate::gui::modulo::form::ModuloFormUI::new(&modulo_manager, &config_manager);
|
||||
let modulo_search_ui =
|
||||
crate::gui::modulo::search::ModuloSearchUI::new(&modulo_manager, &config_manager);
|
||||
let modulo_text_ui = crate::gui::modulo::textview::ModuloTextUI::new(&modulo_manager);
|
||||
|
||||
let context: Box<dyn Context> = Box::new(super::context::DefaultContext::new(
|
||||
|
@ -186,7 +188,8 @@ pub fn initialize_and_spawn(
|
|||
let clipboard_adapter = ClipboardAdapter::new(&*clipboard, &config_manager);
|
||||
let clipboard_extension =
|
||||
espanso_render::extension::clipboard::ClipboardExtension::new(&clipboard_adapter);
|
||||
let date_extension = espanso_render::extension::date::DateExtension::new();
|
||||
let locale_provider = espanso_render::extension::date::DefaultLocaleProvider::new();
|
||||
let date_extension = espanso_render::extension::date::DateExtension::new(&locale_provider);
|
||||
let echo_extension = espanso_render::extension::echo::EchoExtension::new();
|
||||
// For backwards compatiblity purposes, the echo extension can also be called with "dummy" type
|
||||
let dummy_extension = espanso_render::extension::echo::EchoExtension::new_with_alias("dummy");
|
||||
|
|
|
@ -20,18 +20,30 @@
|
|||
use serde::Serialize;
|
||||
use serde_json::{json, Map, Value};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::gui::{FormField, FormUI};
|
||||
|
||||
use super::manager::ModuloManager;
|
||||
|
||||
pub trait ModuloFormUIOptionProvider {
|
||||
fn get_post_form_delay(&self) -> usize;
|
||||
}
|
||||
|
||||
pub struct ModuloFormUI<'a> {
|
||||
manager: &'a ModuloManager,
|
||||
option_provider: &'a dyn ModuloFormUIOptionProvider,
|
||||
}
|
||||
|
||||
impl<'a> ModuloFormUI<'a> {
|
||||
pub fn new(manager: &'a ModuloManager) -> Self {
|
||||
Self { manager }
|
||||
pub fn new(
|
||||
manager: &'a ModuloManager,
|
||||
option_provider: &'a dyn ModuloFormUIOptionProvider,
|
||||
) -> Self {
|
||||
Self {
|
||||
manager,
|
||||
option_provider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +64,7 @@ impl<'a> FormUI for ModuloFormUI<'a> {
|
|||
.manager
|
||||
.invoke(&["form", "-j", "-i", "-"], &json_config)?;
|
||||
let json: Result<HashMap<String, String>, _> = serde_json::from_str(&output);
|
||||
match json {
|
||||
let result = match json {
|
||||
Ok(json) => {
|
||||
if json.is_empty() {
|
||||
Ok(None)
|
||||
|
@ -61,7 +73,16 @@ impl<'a> FormUI for ModuloFormUI<'a> {
|
|||
}
|
||||
}
|
||||
Err(error) => Err(error.into()),
|
||||
};
|
||||
|
||||
let post_form_delay = self.option_provider.get_post_form_delay();
|
||||
if post_form_delay > 0 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(
|
||||
post_form_delay.try_into().unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,13 @@ impl ModuloManager {
|
|||
error!("modulo reported an error: {}", error);
|
||||
}
|
||||
|
||||
if !child_output.status.success() {
|
||||
error!(
|
||||
"modulo exited with non-zero status code: {:?}",
|
||||
child_output.status.code()
|
||||
)
|
||||
}
|
||||
|
||||
if !output.trim().is_empty() {
|
||||
Ok(output.to_string())
|
||||
} else {
|
||||
|
|
|
@ -19,19 +19,30 @@
|
|||
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, convert::TryInto};
|
||||
|
||||
use crate::gui::{SearchItem, SearchUI};
|
||||
|
||||
use super::manager::ModuloManager;
|
||||
|
||||
pub trait ModuloSearchUIOptionProvider {
|
||||
fn get_post_search_delay(&self) -> usize;
|
||||
}
|
||||
|
||||
pub struct ModuloSearchUI<'a> {
|
||||
manager: &'a ModuloManager,
|
||||
option_provider: &'a dyn ModuloSearchUIOptionProvider,
|
||||
}
|
||||
|
||||
impl<'a> ModuloSearchUI<'a> {
|
||||
pub fn new(manager: &'a ModuloManager) -> Self {
|
||||
Self { manager }
|
||||
pub fn new(
|
||||
manager: &'a ModuloManager,
|
||||
option_provider: &'a dyn ModuloSearchUIOptionProvider,
|
||||
) -> Self {
|
||||
Self {
|
||||
manager,
|
||||
option_provider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +59,7 @@ impl<'a> SearchUI for ModuloSearchUI<'a> {
|
|||
.manager
|
||||
.invoke(&["search", "-j", "-i", "-"], &json_config)?;
|
||||
let json: Result<HashMap<String, Value>, _> = serde_json::from_str(&output);
|
||||
match json {
|
||||
let result = match json {
|
||||
Ok(json) => {
|
||||
if let Some(Value::String(selected_id)) = json.get("selected") {
|
||||
Ok(Some(selected_id.clone()))
|
||||
|
@ -57,7 +68,16 @@ impl<'a> SearchUI for ModuloSearchUI<'a> {
|
|||
}
|
||||
}
|
||||
Err(error) => Err(error.into()),
|
||||
};
|
||||
|
||||
let post_search_delay = self.option_provider.get_post_search_delay();
|
||||
if post_search_delay > 0 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(
|
||||
post_search_delay.try_into().unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ generate_patchable_config!(
|
|||
backspace_limit -> usize,
|
||||
apply_patch -> bool,
|
||||
undo_backspace -> bool,
|
||||
post_form_delay -> usize,
|
||||
post_search_delay -> usize,
|
||||
win32_exclude_orphan_events -> bool,
|
||||
win32_keyboard_layout_cache_interval -> i64,
|
||||
x11_use_xclip_backend -> bool,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! cc = "1.0.66"
|
||||
//! cc = "1.0.73"
|
||||
//! glob = "0.3.0"
|
||||
//! envmnt = "*"
|
||||
//! ```
|
||||
|
@ -26,7 +26,7 @@ fn main() {
|
|||
|
||||
// First, we try to find the directory containing the various versions:
|
||||
// C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Redist\MSVC\
|
||||
let tool = cc::windows_registry::find_tool("msvc", "msbuild")
|
||||
let tool = cc::windows_registry::find_tool("msvc", "devenv")
|
||||
.expect("unable to locate MSVC compiler, did you install Visual Studio?");
|
||||
let mut versions_dir = None;
|
||||
let mut current_root = tool.path();
|
||||
|
@ -56,8 +56,15 @@ fn main() {
|
|||
.expect("unable to extract path of vcruntime140_1.dll file");
|
||||
|
||||
// Copy the DLLs in the target directory
|
||||
let parent_dir = target_file.parent().expect("unable to obtain directory containing DLLs");
|
||||
for entry in glob::glob(&format!(r"{}\*.dll", parent_dir.to_string_lossy().to_string())).expect("unable to glob over DLLs") {
|
||||
let parent_dir = target_file
|
||||
.parent()
|
||||
.expect("unable to obtain directory containing DLLs");
|
||||
for entry in glob::glob(&format!(
|
||||
r"{}\*.dll",
|
||||
parent_dir.to_string_lossy().to_string()
|
||||
))
|
||||
.expect("unable to glob over DLLs")
|
||||
{
|
||||
let entry = entry.expect("unable to unwrap DLL entry");
|
||||
let filename = entry.file_name().expect("unable to obtain filename");
|
||||
std::fs::copy(&entry, target_dir.join(filename)).expect("unable to copy DLL");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: espanso
|
||||
version: 2.1.3-alpha
|
||||
version: 2.1.4-beta
|
||||
summary: A Cross-platform Text Expander written in Rust
|
||||
description: |
|
||||
espanso is a Cross-platform, Text Expander written in Rust.
|
||||
|
|
Loading…
Reference in New Issue
Block a user