Skip to content

Commit

Permalink
Speed up regex filtering by compiling it only once
Browse files Browse the repository at this point in the history
Also send a new value of a property in Toggled event.
  • Loading branch information
ArekPiekarz committed Jun 16, 2022
1 parent 09e559d commit 421b868
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 70 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

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

20 changes: 7 additions & 13 deletions src/commit_amend_checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ impl IEventHandler for CommitAmendCheckbox
fn handle(&mut self, source: Source, event: &Event)
{
match event {
Event::AmendedCommit => self.onAmendedCommit(),
Event::Committed => self.onCommitted(),
Event::Toggled => self.onToggled(),
Event::AmendedCommit => self.onAmendedCommit(),
Event::Committed => self.onCommitted(),
Event::Toggled(isSelected) => self.onToggled(*isSelected),
_ => handleUnknown(source, event)
}
}
Expand All @@ -45,12 +45,6 @@ impl CommitAmendCheckbox

// private

#[must_use]
pub fn isSelected(&self) -> bool
{
self.widget.is_active()
}

pub fn unselect(&self)
{
self.widget.set_active(false);
Expand All @@ -77,8 +71,8 @@ impl CommitAmendCheckbox
fn connectWidget(&self)
{
let eventSender = self.sender.clone();
self.widget.connect_toggled(move |_checkbox|
eventSender.send((Source::CommitAmendCheckbox, Event::Toggled)).unwrap());
self.widget.connect_toggled(move |checkbox|
eventSender.send((Source::CommitAmendCheckbox, Event::Toggled(checkbox.is_active()))).unwrap());
}

fn onAmendedCommit(&self)
Expand All @@ -93,9 +87,9 @@ impl CommitAmendCheckbox
}
}

fn onToggled(&self)
fn onToggled(&self, isSelected: bool)
{
if self.isSelected() {
if isSelected {
self.notifyOnSelected();
} else {
self.notifyOnUnselected();
Expand Down
4 changes: 2 additions & 2 deletions src/commit_log_author_filter_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ pub fn setupCommitLogAuthorFilterEntry(guiElementProvider: &GuiElementProvider,
pub(crate) fn setupCommitLogAuthorFilterRegexButton(guiElementProvider: &GuiElementProvider, sender: Sender)
{
let button = guiElementProvider.get::<gtk::ToggleButton>("Commit log author filter regex button");
button.connect_toggled(
move |_button| sender.send((Source::CommitLogAuthorFilterRegexButton, Event::Toggled)).unwrap());
button.connect_toggled(move |button|
sender.send((Source::CommitLogAuthorFilterRegexButton, Event::Toggled(button.is_active()))).unwrap());
}
6 changes: 3 additions & 3 deletions src/commit_log_filters_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl IEventHandler for CommitLogFiltersView
fn handle(&mut self, source: Source, event: &Event)
{
match event {
Event::Toggled => self.onToggled(),
Event::Toggled(isEnabled) => self.onToggled(*isEnabled),
_ => handleUnknown(source, event)
}
}
Expand All @@ -28,8 +28,8 @@ impl CommitLogFiltersView
Self{widget}
}

fn onToggled(&self)
fn onToggled(&self, isEnabled: bool)
{
self.widget.set_visible(!self.widget.is_visible());
self.widget.set_visible(isEnabled);
}
}
61 changes: 21 additions & 40 deletions src/commit_log_model_filter.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use crate::commit_log_column::{CommitLogColumn};
use crate::event::{Event, handleUnknown, IEventHandler, Sender, Source};
use crate::gui_element_provider::GuiElementProvider;
use crate::text_filter::TextFilter;

use gtk::traits::TreeModelExt;
use gtk::traits::TreeModelFilterExt;
use regex::RegexBuilder;
use std::cell::RefCell;
use std::ops::Not;
use std::rc::Rc;


pub struct CommitLogModelFilter
{
modelFilter: gtk::TreeModelFilter,
authorFilter: Rc<RefCell<AuthorFilter>>,
authorFilter: Rc<RefCell<TextFilter>>,
sender: Sender
}

Expand All @@ -24,7 +23,7 @@ impl IEventHandler for CommitLogModelFilter
match (source, event) {
(_, Event::RefilterRequested) => self.onRefilterRequested(),
(_, Event::TextEntered(filter)) => self.onCommitAuthorFilterChanged(filter),
(Source::CommitLogAuthorFilterRegexButton, Event::Toggled) => self.onRegexToggled(),
(Source::CommitLogAuthorFilterRegexButton, Event::Toggled(isEnabled)) => self.onRegexToggled(*isEnabled),
_ => handleUnknown(source, event)
}
}
Expand All @@ -36,31 +35,35 @@ impl CommitLogModelFilter
-> Self
{
let modelFilter = guiElementProvider.get::<gtk::TreeModelFilter>("Commit log store filter");
let authorFilter = Rc::new(RefCell::new(AuthorFilter::new()));
let authorFilter = Rc::new(RefCell::new(TextFilter::new()));
setupFilterFunction(&modelFilter, Rc::clone(&authorFilter));
Self{modelFilter, authorFilter, sender}
}


// private

fn onCommitAuthorFilterChanged(&self, filter: &str)
fn onCommitAuthorFilterChanged(&self, text: &str)
{
self.authorFilter.borrow_mut().text = filter.to_lowercase();
self.requestRefilter();
match self.authorFilter.borrow_mut().setText(text) {
Ok(()) => self.requestRefilter(),
Err(e) => eprintln!("Invalid author filter regex: {}", e)
}

}

fn onRefilterRequested(&self)
fn onRegexToggled(&self, isEnabled: bool)
{
self.modelFilter.refilter();
self.sender.send((Source::CommitLogModelFilter, Event::RefilterEnded)).unwrap();
match self.authorFilter.borrow_mut().enableRegex(isEnabled) {
Ok(()) => self.requestRefilter(),
Err(e) => eprintln!("Invalid author filter regex: {}", e)
}
}

fn onRegexToggled(&self)
fn onRefilterRequested(&self)
{
let mut authorFilter = self.authorFilter.borrow_mut();
authorFilter.useRegex = authorFilter.useRegex.not();
self.requestRefilter();
self.modelFilter.refilter();
self.sender.send((Source::CommitLogModelFilter, Event::RefilterEnded)).unwrap();
}

fn requestRefilter(&self)
Expand All @@ -69,38 +72,16 @@ impl CommitLogModelFilter
}
}

fn setupFilterFunction(modelFilter: &gtk::TreeModelFilter, authorFilter: Rc<RefCell<AuthorFilter>>)
fn setupFilterFunction(modelFilter: &gtk::TreeModelFilter, authorFilter: Rc<RefCell<TextFilter>>)
{
modelFilter.set_visible_func(move |model, iter| {
let authorFilter = &*authorFilter.borrow();
if authorFilter.text.is_empty() {
if authorFilter.isEmpty() {
return true;
}

let author = model.value(iter, CommitLogColumn::Author.into());
let author = author.get::<&str>().unwrap();
if authorFilter.useRegex {
match RegexBuilder::new(&authorFilter.text).case_insensitive(true).build() {
Ok(regex) => regex.is_match(author),
Err(_) => false
}

} else {
author.to_lowercase().contains(&authorFilter.text)
}
authorFilter.isMatch(author)
});
}

struct AuthorFilter
{
text: String,
useRegex: bool
}

impl AuthorFilter
{
fn new() -> Self
{
Self{text: String::new(), useRegex: false}
}
}
6 changes: 4 additions & 2 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ pub enum Event
// button
Clicked,

// checkbox
Toggled,
// checkbox, tool button
Toggled(IsEnabled),

// commit amend mode
CommitAmendEnabled,
Expand Down Expand Up @@ -68,6 +68,8 @@ pub enum Event
TextEntered(String)
}

type IsEnabled = bool;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Source
{
Expand Down
6 changes: 3 additions & 3 deletions src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,13 @@ fn setupDispatching(gui: GuiObjects, mut repository: Rc<RefCell<Repository>>, re
attach(receiver, move |(source, event)| { match (source, &event) {
(S::CommitAmendCheckbox, E::CommitAmendDisabled) => (&repository, &mut commitMessageView, &mut commitButton, &mut diffView).handle(source, &event),
(S::CommitAmendCheckbox, E::CommitAmendEnabled) => (&repository, &mut commitMessageView, &mut commitButton, &mut diffView).handle(source, &event),
(S::CommitAmendCheckbox, E::Toggled) => commitAmendCheckbox.handle(source, &event),
(S::CommitAmendCheckbox, E::Toggled(_)) => commitAmendCheckbox.handle(source, &event),
(S::CommitButton, E::AmendCommitRequested(_)) => repository.handle(source, &event),
(S::CommitButton, E::Clicked) => commitButton.handle(source, &event),
(S::CommitButton, E::CommitRequested(_)) => repository.handle(source, &event),
(S::CommitDiffViewWidget, E::ZoomRequested(_)) => commitDiffView.handle(source, &event),
(S::CommitLogAuthorFilterEntry, E::TextEntered(_)) => commitLogModelFilter.handle(source, &event),
(S::CommitLogAuthorFilterRegexButton, E::Toggled) => commitLogModelFilter.handle(source, &event),
(S::CommitLogAuthorFilterRegexButton, E::Toggled(_)) => commitLogModelFilter.handle(source, &event),
(S::CommitLogModelFilter, E::RefilterRequested) => (&mut commitLogView, &mut commitLogModelFilter).handle(source, &event),
(S::CommitLogModelFilter, E::RefilterEnded) => commitLogView.handle(source, &event),
(S::CommitLogView, E::CommitSelected(_)) => commitDiffView.handle(source, &event),
Expand All @@ -187,7 +187,7 @@ fn setupDispatching(gui: GuiObjects, mut repository: Rc<RefCell<Repository>>, re
(S::Repository, E::Refreshed) => (&unstagedChangesStore, &stagedChangesStore).handle(source, &event),
(S::Repository, E::UpdatedInStaged(_)) => stagedChangesStore.handle(source, &event),
(S::Repository, E::UpdatedInUnstaged(_)) => unstagedChangesStore.handle(source, &event),
(S::ShowCommitLogFiltersButton, E::Toggled) => commitLogFiltersView.handle(source, &event),
(S::ShowCommitLogFiltersButton, E::Toggled(_)) => commitLogFiltersView.handle(source, &event),
(S::StagedChangesStore, E::Refreshed) => stagedChangesView.handle(source, &event),
(S::StagedChangesView, E::FileChangeRefreshed(_)) => diffView.handle(source, &event),
(S::StagedChangesView, E::FileChangeSelected(_)) => (&mut diffView, &mut unstagedChangesView).handle(source, &event),
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ mod staged_changes;
mod staged_changes_store;
mod staged_changes_view;
mod line_diff;
mod text_filter;
mod text_view;
mod tool_bar_stack;
mod tree_selection;
Expand Down
3 changes: 2 additions & 1 deletion src/show_commit_log_filters_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ use gtk::traits::ToggleToolButtonExt;
pub(crate) fn setupShowCommitLogFiltersButton(guiElementProvider: &GuiElementProvider, sender: Sender)
{
let button = guiElementProvider.get::<gtk::ToggleToolButton>("Show commit log filters button");
button.connect_toggled(move |_button| sender.send((Source::ShowCommitLogFiltersButton, Event::Toggled)).unwrap());
button.connect_toggled(
move |button| sender.send((Source::ShowCommitLogFiltersButton, Event::Toggled(button.is_active()))).unwrap());
}
93 changes: 93 additions & 0 deletions src/text_filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use anyhow::{bail, Result};
use regex::{Regex, RegexBuilder};


pub(crate) struct TextFilter
{
regexState: RegexState
}

enum RegexState
{
Disabled{text: String},
Invalid{text: String},
Valid{regex: Regex}
}

impl TextFilter
{
pub(crate) fn new() -> Self
{
Self{regexState: RegexState::Disabled{text: String::new()}}
}

pub(crate) fn setText(&mut self, newText: &str) -> Result<()>
{
match &mut self.regexState {
RegexState::Disabled{text} => *text = newText.into(),
RegexState::Invalid{text} => {
match RegexBuilder::new(newText).case_insensitive(true).build() {
Ok(newRegex) => self.regexState = RegexState::Valid{regex: newRegex},
Err(e) => {
*text = newText.into();
bail!(e)
}
}
},
RegexState::Valid{regex} => {
match RegexBuilder::new(newText).case_insensitive(true).build() {
Ok(newRegex) => *regex = newRegex,
Err(e) => {
self.regexState = RegexState::Invalid{text: newText.into()};
bail!(e)
}
}
}
}
Ok(())
}

pub(crate) fn enableRegex(&mut self, shouldEnable: bool) -> Result<()>
{
if shouldEnable {
match &mut self.regexState {
RegexState::Disabled{text} => {
match RegexBuilder::new(&text).case_insensitive(true).build() {
Ok(regex) => self.regexState = RegexState::Valid{regex},
Err(e) => {
self.regexState = RegexState::Invalid{text: text.clone()};
bail!(e)
}
}
},
RegexState::Invalid{..} => (),
RegexState::Valid{..} => ()
}
} else {
match &mut self.regexState {
RegexState::Disabled{..} => (),
RegexState::Invalid{text} => self.regexState = RegexState::Disabled{text: text.clone()},
RegexState::Valid{regex} => self.regexState = RegexState::Disabled{text: regex.as_str().into()}
}
}
Ok(())
}

pub(crate) fn isEmpty(&self) -> bool
{
match &self.regexState {
RegexState::Disabled{text} => text.is_empty(),
RegexState::Invalid{..} => true,
RegexState::Valid{regex} => regex.as_str().is_empty()
}
}

pub(crate) fn isMatch(&self, input: &str) -> bool
{
match &self.regexState {
RegexState::Disabled{text} => input.to_lowercase().contains(text),
RegexState::Invalid{..} => false,
RegexState::Valid{regex} => regex.is_match(input)
}
}
}

0 comments on commit 421b868

Please sign in to comment.