Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor paths(with tests) and bulk operations #87

Conversation

gurgalex
Copy link
Collaborator

@gurgalex gurgalex commented May 30, 2019

Closes #48
cc: @Davejkane

why the refactor of paths?

Making the indexes and images fields private forces the user of access methods which at least make the caller consider the possibility of no images being present.

With private fields, removing an image must be done by calling a method which does the proper manipulations for the user.

Some methods now can limit how much mutable access (if at all) is given to the images vec. For instance, sorting the vector can do everything except that which alters its length.
Some areas only need read access, so no need to give the ability to replace the whole vec outright.
A dedicated reload_imags method is provided for this, which always makes sure the current image index is not larger than the last element's index.

private fields seemed to aid in writing tests as well.

MultiNormal mode

Typing numbers on either the numpad or keyboard while in Normal mode will now switch to MultiNormal mode. This allows for performing many repetitions of Normal actions, but in bulk, like jumping 10 images ahead.

Concerns with MultiNormal implementation

  • Lots of code duplication between handling of actions of MultiNormal and Normal mode. Any ideas for somewhat merging the two modes?

  • Should actions that can only be performed once (like toggling fullscreen) be allowed as actions in MultiNormal mode. Example 22f would only toggle the state of fullscreen once.

  • Infobar display of times to repeat. Should the numbers be on the right like vim?

Small changes

  • Made zooming in and out undo each other (*1.1 and /1.1)
  • Removed duplication of process_action. It is present only once for each mode, instead of needing duplication on every keypress/text match.

@gurgalex gurgalex requested a review from Davejkane May 30, 2019 07:37
@gurgalex gurgalex force-pushed the refactor_paths_and_test_bulk_operations branch from 2a02a5c to 46c4416 Compare May 30, 2019 10:37
@NickHackman
Copy link
Contributor

NickHackman commented May 30, 2019

@gurgalex Great work! Code review inc...

But first, initial thoughts:
Ideally we follow Vim and put the numerical input on the right side of the infobar, since knowing your current index or image would impact your action. Also this is a really big PR, in the future breaking it up would be better in my personal opinion.

Copy link
Contributor

@NickHackman NickHackman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's all I have for right now, mostly just my two cents. I'm probably going to look at it again at a later point, maybe tomorrow I don't know.

As per usual @gurgalex really good code I just think there are some things that maybe would make your life easier. ❤️

Some(self.paths.images[self.paths.index].to_owned())
} else {
None
let target_path = match self.paths.current_image_path() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this instance, sorry if this was unclear, we want the image path at the current index because we want to find the current image if it occurs in the new glob.

In this match it returns, but it really should just be None, because we have no image to find.
This causes newglob to fail on a start that is empty, your comment is right we don't have any images, but that's a 👌

This is why later on in Development there's this

        if let Some(target_path) = target { // <---- here, because we don't have any images
            // find location of current image, if it exists in self.paths.images
            match self
                .paths
                .images
                .iter()
                .position(|path| path == &target_path)
            {
                Some(new_index) => self.paths.index = new_index,
                None => {
                    self.paths.index = 0;
                }
            }
        }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Believe this is fixed with the latest changes.

pub fn skip_forward(&mut self) -> Result<(), String> {
let skip_size = compute_skip_size(&self.paths.images);
self.increment(skip_size)
pub fn skip_forward(&mut self, times: usize) -> Result<(), String> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So Okay, I could be wrong here, but hear me out.

So in every function you have it taking a usize called times. This is a suitable solution, but they all have the same function definition... pub fn name(&mut self, times: usize) -> Result<(), String> There's gotta be a way to simplify this down, right? Using generics and using functions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeating of function signatures seems fine, as each does something different.

I'm mostly concerned with the near duplication that run_multi_normal_mode does of run_normal_mode
https://github.com/Davejkane/riv/pull/87/files#diff-52fc4303c2b25fd9f3edcce1fbd21455R497

} => match (a, n) {
(Action::Backspace, _) => {
// Divide repeat by int 10 to remove last digit
self.ui_state.repeat /= 10;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Davejkane I'm not sure on how you want this to perform, but in Vim if you backspace at any point it just clears the entire buffer rather than removing one of the numbers.

@@ -366,6 +371,7 @@ fn normal_help_text() -> Vec<&'static str> {
"+------------+----------------------------+-----------------------------------------------------+",
"| Key 1 | Key 2 | Action |",
"+------------+----------------------------+-----------------------------------------------------+",
"| 0-9 | | Perform the below actions in bulk |",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a huge nitpick, but maybe include that it takes one of the below operations?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True
In the Key 2 slot.
Text: Key of the action to perform


/// Perform an Action `times` times
#[derive(Clone, Debug)]
pub struct ProcessAction<'a> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're just associating two variables, why not use a tuple? Is there a benefit of a struct versus a tuple?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with a struct for the labeled fields (action, and times).
Also being able to implement conversions (such as from ProcessAction -> MultiAction)

"0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" => {
let previous_count = state.repeat;
// Safe to unwrap as only digits were matched
let next_digit = text.parse::<usize>().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why parse and add it on here? Instead of just storing it in a String or maybe Cow borrowed string and then when the user hits an action then parse it? This would also make it so you don't have to divide by 10 in mod.rs to truncate a digit

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing immediately was one way to reduce code duplication when the text is not 0-9.
The amount of times would not have to be parsed on every action.
I think I could move the parsing out of the ui event -> MultiNormalAction translation if Normal mode read from the same field repeat.

src/ui.rs Outdated
let first_digit = text.parse::<usize>().unwrap();
// Save the first digit before switching
state.repeat = first_digit;
Action::SwitchMultiNormalMode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated above, maybe have the Mode store the number of times to repeat? (As a String or Cow maybe?) I don't know enough about Cow yet...

Copy link
Owner

@Davejkane Davejkane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gurgalex, first off thanks for the great work. I knew this would probably be our biggest feature to date, but it's also the feature that's really going to make riv unique, so thanks for taking it on.

I haven't had time just yet to do some proper code review, but I have had the chance to do some QA.

When I use 23j for instance, after rendering the 23, it then renders the old index very briefly before rendering the new index. It's jarring. But this could potentially be taken care of by rendering the number at the right side of the bar along with the rest of the bar.

I want 56G to go the 56th image exactly. but right now it goes to the end. That's a feature request.

I want the dot operator to work with bulk operations. If I press 10k then I want every . press to also be 10k. Currently . will just be k.

@gurgalex
Copy link
Collaborator Author

@Davejkane
I currently have the . (dot/period) presses only perform the last non-dot pressed action.
2jand then . would perform the same operation 2j
2. would perform 2j
3. would perform 3j

Did you want the dot presses to multiply as well?
Example
2j
last action: 2j
. would perform 2j
2. would perform 4j
2. again would perform 8j
. would now perform 8j

@gurgalex
Copy link
Collaborator Author

gurgalex commented May 31, 2019

@Davejkane
I believe all current concerns are addressed.

@Davejkane
Copy link
Owner

I just checked with vim and it appears that the way you're doing it, without multiplication is correct. I'll try to get around to code review today.

Copy link
Owner

@Davejkane Davejkane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks great to me. Really good job. Please squash and merge and tidy up the commit message.

@gurgalex gurgalex merged commit 5053228 into Davejkane:development Jun 1, 2019
@gurgalex gurgalex deleted the refactor_paths_and_test_bulk_operations branch June 8, 2019 03:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Take Numeric arguments for Actions
3 participants