From 85224f9976e58d416c52a81d1f77fc34a641a36f Mon Sep 17 00:00:00 2001 From: Jakub Panek Date: Sat, 28 Jan 2023 20:53:46 +0100 Subject: [PATCH] fix: set language environment in proper way (#2070) --- Cargo.lock | 3 ++ lapce-proxy/Cargo.toml | 5 +++ lapce-proxy/src/terminal.rs | 65 ++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3af502a5..ab8832da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2694,6 +2694,7 @@ dependencies = [ "alacritty_terminal", "anyhow", "clap", + "cocoa", "crossbeam-channel", "directories", "dyn-clone", @@ -2710,11 +2711,13 @@ dependencies = [ "lapce-core", "lapce-rpc", "lapce-xi-rope", + "libc", "locale_config", "log 0.4.17", "lsp-types", "mio 0.6.23", "notify", + "objc", "once_cell", "parking_lot 0.11.2", "psp-types", diff --git a/lapce-proxy/Cargo.toml b/lapce-proxy/Cargo.toml index 90be7213..3ce213e6 100644 --- a/lapce-proxy/Cargo.toml +++ b/lapce-proxy/Cargo.toml @@ -68,3 +68,8 @@ wasi-common = "1.0.0" [dependencies.wasi-experimental-http-wasmtime] git = "https://github.com/lapce/wasi-experimental-http" # path = "../../wasi-experimental-http/crates/wasi-experimental-http-wasmtime" + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.24" +objc = "0.2" +libc = "0.2" \ No newline at end of file diff --git a/lapce-proxy/src/terminal.rs b/lapce-proxy/src/terminal.rs index 51d41e8c..d97ff527 100644 --- a/lapce-proxy/src/terminal.rs +++ b/lapce-proxy/src/terminal.rs @@ -328,12 +328,69 @@ fn set_current(&mut self, new: Option) { } } +/// Code taken (and slightly modified) from wezterm's env-bootstrap +/// Source: https://github.com/wez/wezterm/blob/691ec187ba29fcae45ddf4e4f88fd02c49988c86/env-bootstrap/src/lib.rs#L86 +/// License: https://github.com/wez/wezterm/blob/691ec187ba29fcae45ddf4e4f88fd02c49988c86/LICENSE.md +/// SPDX: MIT #[cfg(target_os = "macos")] fn set_locale_environment() { - let locale = locale_config::Locale::global_default() - .to_string() - .replace('-', "_"); - std::env::set_var("LC_ALL", locale + ".UTF-8"); + use cocoa::base::id; + use cocoa::foundation::NSString; + use objc::runtime::Object; + use objc::*; + + fn lang_is_set() -> bool { + match std::env::var_os("LANG") { + None => false, + Some(lang) => !lang.is_empty(), + } + } + + if !lang_is_set() { + unsafe fn nsstring_to_str<'a>(ns: *mut Object) -> &'a str { + let data = NSString::UTF8String(ns as id) as *const u8; + let len = NSString::len(ns as id); + let bytes = std::slice::from_raw_parts(data, len); + std::str::from_utf8_unchecked(bytes) + } + + unsafe { + let locale: *mut Object = + msg_send![class!(NSLocale), autoupdatingCurrentLocale]; + let lang_code_obj: *mut Object = msg_send![locale, languageCode]; + let country_code_obj: *mut Object = msg_send![locale, countryCode]; + + { + let lang_code = nsstring_to_str(lang_code_obj); + let country_code = nsstring_to_str(country_code_obj); + + let candidate = format!("{lang_code}_{country_code}.UTF-8"); + let candidate_cstr = + std::ffi::CString::new(<&[u8]>::clone(&candidate.as_bytes())) + .expect("make cstr from str"); + + // If this looks like a working locale then export it to + // the environment so that our child processes inherit it. + let old = libc::setlocale(libc::LC_CTYPE, std::ptr::null()); + if !libc::setlocale(libc::LC_CTYPE, candidate_cstr.as_ptr()) + .is_null() + { + std::env::set_var("LANG", &candidate); + } else { + log::warn!( + "setlocale({}) failed, fall back to en_US.UTF-8", + candidate + ); + std::env::set_var("LANG", "en_US.UTF-8"); + } + libc::setlocale(libc::LC_CTYPE, old); + } + + let _: () = msg_send![lang_code_obj, release]; + let _: () = msg_send![country_code_obj, release]; + let _: () = msg_send![locale, release]; + } + } } #[inline]