Add case sensitive and use regex toggles for find, fixes #144
This commit is contained in:
parent
abfb5d845f
commit
59933e6a03
6 changed files with 133 additions and 33 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1107,6 +1107,7 @@ dependencies = [
|
|||
"notify",
|
||||
"open",
|
||||
"patch",
|
||||
"regex",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"smol_str",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -58,4 +59,4 @@ wgpu = ["libcosmic/wgpu", "cosmic-files/wgpu"]
|
|||
|
||||
[profile.release-with-debug]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
debug = true
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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(®ex::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;
|
||||
|
|
|
|||
100
src/main.rs
100
src/main.rs
|
|
@ -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),
|
||||
|
|
@ -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(®ex, true);
|
||||
}
|
||||
Err(err) => {
|
||||
//TODO: put regex error in find box
|
||||
log::warn!(
|
||||
"failed to compile regex {:?}: {}",
|
||||
self.find_search_value,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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(®ex, false);
|
||||
}
|
||||
Err(err) => {
|
||||
//TODO: put regex error in find box
|
||||
log::warn!(
|
||||
"failed to compile regex {:?}: {}",
|
||||
self.find_search_value,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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(®ex, &self.find_replace_value);
|
||||
}
|
||||
Err(err) => {
|
||||
//TODO: put regex error in find box
|
||||
log::warn!(
|
||||
"failed to compile regex {:?}: {}",
|
||||
self.find_search_value,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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(®ex, &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) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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"),
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
44
src/tab.rs
44
src/tab.rs
|
|
@ -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,
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue