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

Fix logic for automatically opening diff views based on selection change #1311

Merged
merged 11 commits into from Mar 5, 2018

Conversation

Projects
None yet
2 participants
@kuychaco
Member

kuychaco commented Feb 15, 2018

Fixes #1287

Problem: We’re seeing surprised diff views popping up unexpectedly, such as in the following scenarios

Scenario 1: see steps in #1287
Scenario 2: have two repos open in project. have pending diff in pane 2 corresponding to repo 2. in pane 1 focus a file from repo 1, then focus a file from repo 2. note that new diff view opens.
Scenario 3: only have one repo. have pending diff in pane 2. focus pane 1. focus staging view (be sure not to click on a file). use arrow key to change selection (up or down). note that new diff view pops up in pane 1. The only time we want to open a brand new pending diff view in a pane is if the user explicitly expresses intent, such as by clicking the file (see 1 below).

These point to bugs in our logic for automatically opening diff views when the selection changes. We want to do this in two cases:

  1. when navigating up/down and there is a pending diff view open in the active pane, update the diff view to reflect the new selection. This enables users to quickly glance at the change they've made
  2. when there is a pending diff view open and the file associated with it no longer has changes (such as when changes are staged or deleted), then display the next diff view in its place according to what is selected in the staging view. This was a highly requested feature so that one could quickly stage using the keyboard by hitting enter multiple times. Or hit the “Stage File” button in a pending diff view in rapid sequence.

So let’s get clear on the circumstances under which we do want a pending diff item to be opened.

In order to get this behavior right, we need to get more specific around when/what/how exactly we want to automatically show the next file.

If there are stale pending pane items corresponding to a file that no
longer exists in the changed files list, update them to reflect the new
selection.

When? The following criteria must be satisfied

  • A staging action has taken place, the repo updates, the staged file no longer appears in the staged changes list, and a new file is selected
  • There is at least one pending diff view open corresponding to the file that has just been staged. Multiple pending diff views can be open across multiple panes, must check all
    • since we can’t easily know which file has been staged, we need to deduce this somehow
      • diff view no longer has staging view file associated with it
      • diff view belongs to active repo (check to discern between staging action and repo change)

What? What parts of the workspace should be affected?

  • Any pane that has a pending diff view that meets the criteria above should be updated such that its pending item corresponds to the new selection in the staging view
    • This behavior is consistent with the fact that staging lines/hunks in a diff view affects all diff views for that file open in other panes

How? Implementation details for updating pending items according to what is outlined above

  • Check to see when there is a change in the selected file in the staging view
  • Identify the panes that have pending diff views that no longer have staging view files associated with them and belong to the active repo
  • For each pane, call workspace.open with the newly selected file and pass the pane as an option, as well as pending: true. This will cause the old diff view to be replaced

TODO:

  • maintain selection across repo changes.
    repro steps for bug: open diff view for repo 1 that is not the first one listed. make repo 2 active. click diff view for repo 1 and note that it changes to be first file listed.
Fix logic for automatically opening diff views based on selection change
If there are stale pending pane items corresponding to a file that no
longer exists in the changed files list, update them to reflect the new
selection.

If the selection in the changed file list has changed due to keyboard
nav and there is a pending diff view in the active pane, update the diff
view to reflect the new selection.
} else {
const panesWithStaleItemsToUpdate = this.getPanesWithStalePendingFilePatchItem();
if (panesWithStaleItemsToUpdate.length > 0) {
// Staging event caused selection to change, so we need to update all old diff views to reflect new selection

This comment has been minimized.

@kuychaco

kuychaco Feb 15, 2018

Member

Not just when staging. also when deleting files and stashing

}
}
}
async showFilePatchItem(filePath, stagingStatus, {activate} = {activate: false}) {
getPanesWithStalePendingFilePatchItem() {
// "stale" meaning there is no longer a changed file associated with it due to contents being fully staged/unstaged

This comment has been minimized.

@kuychaco

kuychaco Feb 15, 2018

Member

again, not just when staging/unstaging

await view.selectNext();
assert.isFalse(showFilePatchItem.called);
getPendingFilePatchItem.returns(true);
getPanesWithStalePendingFilePatchItem.returns(['pending-file-patch-item']);

This comment has been minimized.

@kuychaco

kuychaco Feb 15, 2018

Member

have multiple array items to improve this and test the code that runs showFilePatchItem for multiple panes

const pendingItem = pane.getPendingItem();
if (!pendingItem) { return false; }
const isDiffViewItem = pendingItem.getRealItem && pendingItem.getRealItem() instanceof FilePatchController;
const isInActiveRepo = pendingItem.getWorkingDirectory() === this.props.workingDirectoryPath;

This comment has been minimized.

@kuychaco

kuychaco Feb 16, 2018

Member

add comment Diff views from repositories other than the active one do not count as stale. Ex: ...

@kuychaco kuychaco requested a review from smashwilson Feb 19, 2018

@smashwilson

I've found one thing that looks like a typo which should probably be changed. Other than that, I'd say the rest of this PR is ready to :shipit:

this.quietlySelectItem(this.activeFilePatch.getFilePath(), this.activeFilePatch.getStagingStatus());
} else {
const isRepoSame = oldProps.workingDirectoryPath === this.props.workingDirectoryPath;
const selectionsPresent = previouslySelectedItems.size > 0 && currentlySelectedItems > 0;

This comment has been minimized.

@smashwilson

smashwilson Feb 24, 2018

Member

🚨 It looks like currentlySelectedItems is an Array. Did you mean currentlySelectedItems.length > 0?

Also: the test suite is even though this is here. Does that mean we're missing a test case, or does it mean that the currentlySelectedItems isn't actually needed?

const activate = pendingItem === this.props.workspace.getActivePaneItem();
await this.showFilePatchItem(selectedItem.filePath, this.selection.getActiveListKey(), {activate});
if (openNew) {
// User explicitly asked to view diff, such as via click

This comment has been minimized.

@smashwilson

smashwilson Feb 24, 2018

Member

👍

I like explicitly separating the logic between "the user explicitly triggered this" and "we're inferring that this should happen."

const activePane = this.props.workspace.getCenter().getActivePane();
const activePendingItem = activePane.getPendingItem();
const activePaneHasPendingFilePatchItem = activePendingItem && activePendingItem.getRealItem &&
activePendingItem.getRealItem() instanceof FilePatchController;

This comment has been minimized.

@smashwilson

smashwilson Feb 24, 2018

Member

instanceof checks always make me feel vaguely uncomfortable. I think they raise my "OO code smell" instincts 😄

(No useful alternatives to suggest here, though; we don't have much choice when we're dealing with arbitrary PaneItems.)

const encodedFilePath = encodeURIComponent(filePath);
const encodedWorkdir = encodeURIComponent(this.props.workingDirectoryPath);
const filePatchItem = await this.props.workspace.open(
`atom-github://file-patch/${encodedFilePath}?workdir=${encodedWorkdir}&stagingStatus=${stagingStatus}`,
{pending: true, activatePane: activate, activateItem: activate},
{pending: true, activatePane: activate, activateItem: activate, pane},

This comment has been minimized.

@smashwilson

smashwilson Feb 24, 2018

Member

Ah, it'll be good to control the Pane here.

... I still want to do that change in :atom: core to be able to specify "categories" of pending pane, so that there's only one pending pane of each category at once and we can keep ours independent from tree-view's.

await view.selectPrevious();
assert.isTrue(showFilePatchItem.calledTwice);

This comment has been minimized.

@smashwilson

smashwilson Feb 24, 2018

Member

Since this is .calledTwice, shouldn't there be two assert.isTrue(showFilePatchItem.calledWith(...)) calls below it to assert that they're both correct? Or are the other calls too annoying to assert specifically? 🤔

Same for the next stanza there 👇

await view.selectPrevious();
await assert.async.isTrue(showFilePatchItem.calledWith(filePatches[0].filePath));
assert.isTrue(showFilePatchItem.calledThrice);

This comment has been minimized.

@smashwilson

smashwilson Feb 24, 2018

Member

Hehe. Times you get to use the word "thrice" in source code++

@smashwilson

This comment has been minimized.

Member

smashwilson commented Mar 5, 2018

Nice

@smashwilson smashwilson merged commit 2fa2492 into master Mar 5, 2018

3 checks passed

ci/circleci Your tests passed on CircleCI!
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

@smashwilson smashwilson deleted the ku-no-more-suprise-diffs-please branch Mar 5, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment