Skip to content

Commit

Permalink
Add case sensitive and use regex toggles for find, fixes #144
Browse files Browse the repository at this point in the history
  • Loading branch information
jackpot51 committed Mar 25, 2024
1 parent abfb5d8 commit 59933e6
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 33 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ grep = "0.3.1"
ignore = "0.4.21"
lexical-sort = "0.3.1"
log = "0.4.20"
notify = "6.1.1"
open = "5.0.2"
patch = "0.7.0"
notify = "6.1.1"
regex = "1.10"
serde = { version = "1", features = ["serde_derive"] }
tokio = { version = "1", features = ["process", "time"] }
# Extra syntax highlighting
Expand Down Expand Up @@ -58,4 +59,4 @@ wgpu = ["libcosmic/wgpu", "cosmic-files/wgpu"]

[profile.release-with-debug]
inherits = "release"
debug = true
debug = true
2 changes: 2 additions & 0 deletions i18n/en/cosmic_edit.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ find-next = Find next
replace-placeholder = Replace...
replace = Replace
replace-all = Replace all
case-sensitive = Case sensitive
use-regex = Use regex
# Menu

Expand Down
14 changes: 14 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ impl AppTheme {
pub struct Config {
pub app_theme: AppTheme,
pub auto_indent: bool,
pub find_case_sensitive: bool,
pub find_use_regex: bool,
pub font_name: String,
pub font_size: u16,
pub highlight_current_line: bool,
Expand All @@ -47,6 +49,8 @@ impl Default for Config {
Self {
app_theme: AppTheme::System,
auto_indent: true,
find_case_sensitive: false,
find_use_regex: false,
font_name: "Fira Mono".to_string(),
font_size: 14,
highlight_current_line: true,
Expand All @@ -61,6 +65,16 @@ impl Default for Config {
}

impl Config {
pub fn find_regex(&self, pattern: &str) -> Result<regex::Regex, regex::Error> {
let mut builder = if self.find_use_regex {
regex::RegexBuilder::new(pattern)
} else {
regex::RegexBuilder::new(&regex::escape(pattern))
};
builder.case_insensitive(!self.find_case_sensitive);
builder.build()
}

// Calculate metrics from font size
pub fn metrics(&self) -> Metrics {
let font_size = self.font_size.max(1) as f32;
Expand Down
100 changes: 92 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,14 @@ pub enum Message {
DefaultFontSize(usize),
DialogMessage(DialogMessage),
Find(Option<bool>),
FindCaseSensitive(bool),
FindNext,
FindPrevious,
FindReplace,
FindReplaceAll,
FindReplaceValueChanged(String),
FindSearchValueChanged(String),
FindUseRegex(bool),
GitProjectStatus(Vec<(String, PathBuf, Vec<GitStatus>)>),
Key(Modifiers, keyboard::Key),
LaunchUrl(String),
Expand Down Expand Up @@ -1556,10 +1558,27 @@ impl Application for App {
// Focus correct input
return self.update_focus();
}
Message::FindCaseSensitive(find_case_sensitive) => {
self.config.find_case_sensitive = find_case_sensitive;
return self.save_config();
}
Message::FindNext => {
if !self.find_search_value.is_empty() {
if let Some(Tab::Editor(tab)) = self.active_tab() {
tab.search(&self.find_search_value, true);
//TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) {
Ok(regex) => {
tab.search(&regex, true);
}
Err(err) => {
//TODO: put regex error in find box
log::warn!(
"failed to compile regex {:?}: {}",
self.find_search_value,
err
);
}
}
}
}

Expand All @@ -1569,7 +1588,20 @@ impl Application for App {
Message::FindPrevious => {
if !self.find_search_value.is_empty() {
if let Some(Tab::Editor(tab)) = self.active_tab() {
tab.search(&self.find_search_value, false);
//TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) {
Ok(regex) => {
tab.search(&regex, false);
}
Err(err) => {
//TODO: put regex error in find box
log::warn!(
"failed to compile regex {:?}: {}",
self.find_search_value,
err
);
}
}
}
}

Expand All @@ -1579,7 +1611,21 @@ impl Application for App {
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);
//TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) {
Ok(regex) => {
//TODO: support captures
tab.replace(&regex, &self.find_replace_value);
}
Err(err) => {
//TODO: put regex error in find box
log::warn!(
"failed to compile regex {:?}: {}",
self.find_search_value,
err
);
}
}
}
}

Expand All @@ -1589,11 +1635,25 @@ impl Application for App {
Message::FindReplaceAll => {
if !self.find_search_value.is_empty() {
if let Some(Tab::Editor(tab)) = self.active_tab() {
{
let mut editor = tab.editor.lock().unwrap();
editor.set_cursor(cosmic_text::Cursor::new(0, 0));
//TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) {
Ok(regex) => {
//TODO: support captures
{
let mut editor = tab.editor.lock().unwrap();
editor.set_cursor(cosmic_text::Cursor::new(0, 0));
}
while tab.replace(&regex, &self.find_replace_value) {}
}
Err(err) => {
//TODO: put regex error in find box
log::warn!(
"failed to compile regex {:?}: {}",
self.find_search_value,
err
);
}
}
while tab.replace(&self.find_search_value, &self.find_replace_value) {}
}
}

Expand All @@ -1606,6 +1666,10 @@ impl Application for App {
Message::FindSearchValueChanged(value) => {
self.find_search_value = value;
}
Message::FindUseRegex(find_use_regex) => {
self.config.find_use_regex = find_use_regex;
return self.save_config();
}
Message::GitProjectStatus(project_status) => {
self.git_project_status = Some(project_status);
}
Expand Down Expand Up @@ -2480,7 +2544,7 @@ impl Application for App {
.padding(space_xxs)
.spacing(space_xxs);

let mut column = widget::column::with_capacity(2).push(find_widget);
let mut column = widget::column::with_capacity(3).push(find_widget);
if *replace {
let replace_input = widget::text_input::text_input(
fl!("replace-placeholder"),
Expand Down Expand Up @@ -2524,6 +2588,26 @@ impl Application for App {
column = column.push(replace_widget);
}

column = column.push(
widget::row::with_children(vec![
widget::checkbox(
fl!("case-sensitive"),
self.config.find_case_sensitive,
Message::FindCaseSensitive,
)
.into(),
widget::checkbox(
fl!("use-regex"),
self.config.find_use_regex,
Message::FindUseRegex,
)
.into(),
])
.align_items(Alignment::Center)
.padding(space_xxs)
.spacing(space_xxs),
);

tab_column = tab_column
.push(widget::layer_container(column).layer(cosmic_theme::Layer::Primary));
}
Expand Down
44 changes: 21 additions & 23 deletions src/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use cosmic::{
use cosmic_files::mime_icon::{mime_for_path, mime_icon, FALLBACK_MIME_ICON};
use cosmic_text::{Attrs, Buffer, Edit, Shaping, SyntaxEditor, ViEditor, Wrap};
use notify::Watcher;
use regex::Regex;
use std::{
fs,
path::PathBuf,
Expand Down Expand Up @@ -208,18 +209,17 @@ impl EditorTab {
}
}

pub fn replace(&self, value: &str, replace: &str) -> bool {
pub fn replace(&self, regex: &Regex, 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)
if let Some((index, len)) = editor.with_buffer(|buffer| {
regex
.find_iter(buffer.lines[cursor.line].text())
.filter_map(|m| {
if cursor.line != start_line || m.start() >= cursor.index {
Some((m.start(), m.len()))
} else {
None
}
Expand All @@ -228,7 +228,7 @@ impl EditorTab {
}) {
cursor.index = index;
let mut end = cursor;
end.index = index + value.len();
end.index = index + len;

editor.start_change();
editor.delete_range(cursor, end);
Expand All @@ -245,19 +245,18 @@ impl EditorTab {
}

// Code adapted from cosmic-text ViEditor search
pub fn search(&self, value: &str, forwards: bool) -> bool {
pub fn search(&self, regex: &Regex, forwards: bool) -> bool {
let mut editor = self.editor.lock().unwrap();
let mut cursor = editor.cursor();
let start_line = cursor.line;
if forwards {
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)
regex
.find_iter(buffer.lines[cursor.line].text())
.filter_map(|m| {
if cursor.line != start_line || m.start() > cursor.index {
Some(m.start())
} else {
None
}
Expand All @@ -277,17 +276,16 @@ impl EditorTab {
cursor.line -= 1;

if let Some(index) = editor.with_buffer(|buffer| {
buffer.lines[cursor.line]
.text()
.rmatch_indices(value)
.filter_map(|(i, _)| {
if cursor.line != start_line || i < cursor.index {
Some(i)
regex
.find_iter(buffer.lines[cursor.line].text())
.filter_map(|m| {
if cursor.line != start_line || m.start() < cursor.index {
Some(m.start())
} else {
None
}
})
.next()
.last()
}) {
cursor.index = index;
editor.set_cursor(cursor);
Expand Down

0 comments on commit 59933e6

Please sign in to comment.