diff --git a/fish-rust/build.rs b/fish-rust/build.rs index ce9de9149..9a9f926ed 100644 --- a/fish-rust/build.rs +++ b/fish-rust/build.rs @@ -54,6 +54,7 @@ fn main() { "src/future_feature_flags.rs", "src/highlight.rs", "src/job_group.rs", + "src/kill.rs", "src/null_terminated_array.rs", "src/parse_constants.rs", "src/parse_tree.rs", diff --git a/fish-rust/src/env/environment_impl.rs b/fish-rust/src/env/environment_impl.rs index 3f69f72ff..00ee961b9 100644 --- a/fish-rust/src/env/environment_impl.rs +++ b/fish-rust/src/env/environment_impl.rs @@ -44,7 +44,7 @@ pub static UVAR_SCOPE_IS_GLOBAL: RelaxedAtomicBool = RelaxedAtomicBool::new(fals /// Helper to get the kill ring. fn get_kill_ring_entries() -> Vec { - ffi::kill_entries_ffi().from_ffi() + crate::kill::kill_entries() } /// Helper to get the history for a session ID. diff --git a/fish-rust/src/ffi.rs b/fish-rust/src/ffi.rs index 94e2eef41..5b497d4f2 100644 --- a/fish-rust/src/ffi.rs +++ b/fish-rust/src/ffi.rs @@ -33,7 +33,6 @@ include_cpp! { #include "history.h" #include "io.h" #include "input_common.h" - #include "kill.h" #include "parse_constants.h" #include "parser.h" #include "parse_util.h" @@ -133,7 +132,6 @@ include_cpp! { generate!("colorize_shell") generate!("reader_status_count") - generate!("kill_entries_ffi") generate!("get_history_variable_text_ffi") diff --git a/fish-rust/src/kill.rs b/fish-rust/src/kill.rs new file mode 100644 index 000000000..6ce73ac64 --- /dev/null +++ b/fish-rust/src/kill.rs @@ -0,0 +1,99 @@ +//! The killring. +//! +//! Works like the killring in emacs and readline. The killring is cut and paste with a memory of +//! previous cuts. + +use cxx::CxxWString; +use std::collections::VecDeque; +use std::pin::Pin; +use std::sync::Mutex; + +use crate::ffi::wcstring_list_ffi_t; +use crate::wchar::WString; +use crate::wchar_ffi::WCharFromFFI; + +#[cxx::bridge] +mod kill_ffi { + extern "C++" { + include!("wutil.h"); + type wcstring_list_ffi_t = super::wcstring_list_ffi_t; + } + + extern "Rust" { + #[cxx_name = "kill_add"] + fn kill_add_ffi(new_entry: &CxxWString); + #[cxx_name = "kill_replace"] + fn kill_replace_ffi(old_entry: &CxxWString, new_entry: &CxxWString); + #[cxx_name = "kill_yank_rotate"] + fn kill_yank_rotate_ffi(mut out_front: Pin<&mut CxxWString>); + #[cxx_name = "kill_yank"] + fn kill_yank_ffi(mut out_front: Pin<&mut CxxWString>); + #[cxx_name = "kill_entries"] + fn kill_entries_ffi(mut out: Pin<&mut wcstring_list_ffi_t>); + } +} + +static KILL_LIST: once_cell::sync::Lazy>> = + once_cell::sync::Lazy::new(|| Mutex::new(VecDeque::new())); + +fn kill_add_ffi(new_entry: &CxxWString) { + kill_add(new_entry.from_ffi()); +} + +/// Add a string to the top of the killring. +pub fn kill_add(new_entry: WString) { + if !new_entry.is_empty() { + KILL_LIST.lock().unwrap().push_front(new_entry); + } +} + +fn kill_replace_ffi(old_entry: &CxxWString, new_entry: &CxxWString) { + kill_replace(old_entry.from_ffi(), new_entry.from_ffi()) +} + +/// Replace the specified string in the killring. +pub fn kill_replace(old_entry: WString, new_entry: WString) { + let mut kill_list = KILL_LIST.lock().unwrap(); + if let Some(old_entry_idx) = kill_list.iter().position(|entry| entry == &old_entry) { + kill_list.remove(old_entry_idx); + } + if !new_entry.is_empty() { + kill_list.push_front(new_entry); + } +} + +fn kill_yank_rotate_ffi(mut out_front: Pin<&mut CxxWString>) { + out_front.as_mut().clear(); + out_front + .as_mut() + .push_chars(kill_yank_rotate().as_char_slice()); +} + +/// Rotate the killring. +pub fn kill_yank_rotate() -> WString { + let mut kill_list = KILL_LIST.lock().unwrap(); + kill_list.rotate_left(1); + kill_list.front().cloned().unwrap_or_default() +} + +fn kill_yank_ffi(mut out_front: Pin<&mut CxxWString>) { + out_front.as_mut().clear(); + out_front.as_mut().push_chars(kill_yank().as_char_slice()); +} + +/// Paste from the killring. +pub fn kill_yank() -> WString { + let kill_list = KILL_LIST.lock().unwrap(); + kill_list.front().cloned().unwrap_or_default() +} + +fn kill_entries_ffi(mut out_entries: Pin<&mut wcstring_list_ffi_t>) { + out_entries.as_mut().clear(); + for kill_entry in KILL_LIST.lock().unwrap().iter() { + out_entries.as_mut().push(kill_entry); + } +} + +pub fn kill_entries() -> Vec { + KILL_LIST.lock().unwrap().iter().cloned().collect() +} diff --git a/fish-rust/src/lib.rs b/fish-rust/src/lib.rs index 61115ec30..bad730bf8 100644 --- a/fish-rust/src/lib.rs +++ b/fish-rust/src/lib.rs @@ -42,6 +42,7 @@ mod global_safety; mod highlight; mod io; mod job_group; +mod kill; mod locale; mod nix; mod null_terminated_array; diff --git a/src/kill.cpp b/src/kill.cpp index c5b9b337d..e86a88286 100644 --- a/src/kill.cpp +++ b/src/kill.cpp @@ -1,61 +1,21 @@ -// The killring. -// -// Works like the killring in emacs and readline. The killring is cut and paste with a memory of -// previous cuts. #include "config.h" // IWYU pragma: keep #include "kill.h" -#include -#include -#include -#include - -#include "common.h" -#include "fallback.h" // IWYU pragma: keep - -/** Kill ring */ -static owning_lock> s_kill_list; - -void kill_add(wcstring str) { - if (!str.empty()) { - s_kill_list.acquire()->push_front(std::move(str)); - } -} - -void kill_replace(const wcstring &old, const wcstring &newv) { - auto kill_list = s_kill_list.acquire(); - // Remove old. - auto iter = std::find(kill_list->begin(), kill_list->end(), old); - if (iter != kill_list->end()) kill_list->erase(iter); - - // Add new. - if (!newv.empty()) { - kill_list->push_front(newv); - } -} - wcstring kill_yank_rotate() { - auto kill_list = s_kill_list.acquire(); - // Move the first element to the end. - if (kill_list->empty()) { - return {}; - } - kill_list->splice(kill_list->end(), *kill_list, kill_list->begin()); - return kill_list->front(); + wcstring front; + kill_yank_rotate(front); + return front; } wcstring kill_yank() { - auto kill_list = s_kill_list.acquire(); - if (kill_list->empty()) { - return {}; - } - return kill_list->front(); + wcstring front; + kill_yank(front); + return front; } std::vector kill_entries() { - auto kill_list = s_kill_list.acquire(); - return std::vector{kill_list->begin(), kill_list->end()}; + wcstring_list_ffi_t entries; + kill_entries(entries); + return std::move(entries.vals); } - -wcstring_list_ffi_t kill_entries_ffi() { return kill_entries(); } diff --git a/src/kill.h b/src/kill.h index 5802955a1..1b074c688 100644 --- a/src/kill.h +++ b/src/kill.h @@ -1,18 +1,8 @@ -// Prototypes for the killring. -// -// Works like the killring in emacs and readline. The killring is cut and paste with a memory of -// previous cuts. #ifndef FISH_KILL_H #define FISH_KILL_H #include "common.h" -#include "wutil.h" - -/// Replace the specified string in the killring. -void kill_replace(const wcstring &old, const wcstring &newv); - -/// Add a string to the top of the killring. -void kill_add(wcstring str); +#include "kill.rs.h" /// Rotate the killring. wcstring kill_yank_rotate(); @@ -23,7 +13,4 @@ wcstring kill_yank(); /// Get copy of kill ring as vector of strings std::vector kill_entries(); -/// Rust-friendly kill entries. -wcstring_list_ffi_t kill_entries_ffi(); - #endif