diff --git a/i18n/en/cosmic_edit.ftl b/i18n/en/cosmic_edit.ftl
index 25e057f..68c6bf6 100644
--- a/i18n/en/cosmic_edit.ftl
+++ b/i18n/en/cosmic_edit.ftl
@@ -41,6 +41,9 @@ enable-vim-bindings = Enable Vim bindings
find-placeholder = Find...
find-previous = Find previous
find-next = Find next
+replace-placeholder = Replace...
+replace = Replace
+replace-all = Replace all
# Menu
diff --git a/res/icons/replace-all-symbolic.svg b/res/icons/replace-all-symbolic.svg
new file mode 100644
index 0000000..dcaaada
--- /dev/null
+++ b/res/icons/replace-all-symbolic.svg
@@ -0,0 +1,7 @@
+
diff --git a/res/icons/replace-symbolic.svg b/res/icons/replace-symbolic.svg
new file mode 100644
index 0000000..30876bd
--- /dev/null
+++ b/res/icons/replace-symbolic.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/icon_cache.rs b/src/icon_cache.rs
index aeca7db..accdc2f 100644
--- a/src/icon_cache.rs
+++ b/src/icon_cache.rs
@@ -37,6 +37,8 @@ impl IconCache {
bundle!("go-up-symbolic", 16);
bundle!("list-add-symbolic", 16);
bundle!("object-select-symbolic", 16);
+ bundle!("replace-symbolic", 16);
+ bundle!("replace-all-symbolic", 16);
bundle!("window-close-symbolic", 16);
Self { cache }
diff --git a/src/main.rs b/src/main.rs
index 798e45e..a8d8bf7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -184,6 +184,8 @@ pub enum Message {
Find(Option),
FindNext,
FindPrevious,
+ FindReplace,
+ FindReplaceAll,
FindReplaceValueChanged(String),
FindSearchValueChanged(String),
GitProjectStatus(Vec<(String, PathBuf, Vec)>),
@@ -267,6 +269,7 @@ pub struct App {
context_page: ContextPage,
text_box_id: widget::Id,
find_opt: Option,
+ find_replace_id: widget::Id,
find_replace_value: String,
find_search_id: widget::Id,
find_search_value: String,
@@ -976,6 +979,7 @@ impl Application for App {
context_page: ContextPage::Settings,
text_box_id: widget::Id::unique(),
find_opt: None,
+ find_replace_id: widget::Id::unique(),
find_replace_value: String::new(),
find_search_id: widget::Id::unique(),
find_search_value: String::new(),
@@ -1242,6 +1246,22 @@ impl Application for App {
// Focus correct input
return self.update_focus();
}
+ Message::FindReplace => {
+ if !self.find_search_value.is_empty() {
+ if let Some(Tab::Editor(tab)) = self.active_tab() {
+ tab.replace(&self.find_search_value, &self.find_replace_value);
+ }
+ }
+
+ // Focus correct input
+ return self.update_focus();
+ }
+ Message::FindReplaceAll => {
+ log::warn!("FIND REPLACE ALL");
+
+ // Focus correct input
+ return self.update_focus();
+ }
Message::FindReplaceValueChanged(value) => {
self.find_replace_value = value;
}
@@ -1876,7 +1896,7 @@ impl Application for App {
}
if let Some(replace) = &self.find_opt {
- let text_input =
+ let find_input =
widget::text_input::text_input(fl!("find-placeholder"), &self.find_search_value)
.id(self.find_search_id.clone())
.on_input(Message::FindSearchValueChanged)
@@ -1890,7 +1910,7 @@ impl Application for App {
.into(),
);
let find_widget = widget::row::with_children(vec![
- text_input.into(),
+ find_input.into(),
widget::tooltip(
button(icon_cache_get("go-up-symbolic", 16))
.on_press(Message::FindPrevious)
@@ -1920,9 +1940,52 @@ impl Application for App {
.padding(space_xxs)
.spacing(space_xxs);
+ let mut column = widget::column::with_capacity(2).push(find_widget);
+ if *replace {
+ let replace_input = widget::text_input::text_input(
+ fl!("replace-placeholder"),
+ &self.find_replace_value,
+ )
+ .id(self.find_replace_id.clone())
+ .on_input(Message::FindReplaceValueChanged)
+ .on_submit(Message::FindReplace)
+ .width(Length::Fixed(320.0))
+ .trailing_icon(
+ button(icon_cache_get("edit-clear-symbolic", 16))
+ .on_press(Message::FindReplaceValueChanged(String::new()))
+ .style(style::Button::Icon)
+ .into(),
+ );
+ let replace_widget = widget::row::with_children(vec![
+ replace_input.into(),
+ widget::tooltip(
+ button(icon_cache_get("replace-symbolic", 16))
+ .on_press(Message::FindReplace)
+ .padding(space_xxs)
+ .style(style::Button::Icon),
+ fl!("replace"),
+ widget::tooltip::Position::Top,
+ )
+ .into(),
+ widget::tooltip(
+ button(icon_cache_get("replace-all-symbolic", 16))
+ .on_press(Message::FindReplaceAll)
+ .padding(space_xxs)
+ .style(style::Button::Icon),
+ fl!("replace-all"),
+ widget::tooltip::Position::Top,
+ )
+ .into(),
+ ])
+ .align_items(Alignment::Center)
+ .padding(space_xxs)
+ .spacing(space_xxs);
+
+ column = column.push(replace_widget);
+ }
+
tab_column = tab_column.push(
- widget::cosmic_container::container(find_widget)
- .layer(cosmic_theme::Layer::Primary),
+ widget::cosmic_container::container(column).layer(cosmic_theme::Layer::Primary),
);
}
diff --git a/src/tab.rs b/src/tab.rs
index e61dcfb..1f77d40 100644
--- a/src/tab.rs
+++ b/src/tab.rs
@@ -200,6 +200,42 @@ impl EditorTab {
}
}
+ pub fn replace(&self, value: &str, replace: &str) -> bool {
+ let mut editor = self.editor.lock().unwrap();
+ let mut cursor = editor.cursor();
+ let start_line = cursor.line;
+ while cursor.line < editor.with_buffer(|buffer| buffer.lines.len()) {
+ if let Some(index) = editor.with_buffer(|buffer| {
+ buffer.lines[cursor.line]
+ .text()
+ .match_indices(value)
+ .filter_map(|(i, _)| {
+ if cursor.line != start_line || i >= cursor.index {
+ Some(i)
+ } else {
+ None
+ }
+ })
+ .next()
+ }) {
+ cursor.index = index;
+ let mut end = cursor;
+ end.index = index + value.len();
+
+ editor.start_change();
+ editor.delete_range(cursor, end);
+ cursor = editor.insert_at(cursor, replace, None);
+ editor.set_cursor(cursor);
+ editor.finish_change();
+
+ return true;
+ }
+
+ cursor.line += 1;
+ }
+ false
+ }
+
// Code adapted from cosmic-text ViEditor search
pub fn search(&self, value: &str, forwards: bool) -> bool {
let mut editor = self.editor.lock().unwrap();