Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions lib/controllers/file-patch-controller.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import path from 'path';

import React from 'react';
import PropTypes from 'prop-types';

Expand All @@ -10,6 +12,7 @@ import FilePatchView from '../views/file-patch-view';

export default class FilePatchController extends React.Component {
static propTypes = {
largeDiffLineThreshold: PropTypes.number,
activeWorkingDirectory: PropTypes.string,
repository: PropTypes.object.isRequired,
commandRegistry: PropTypes.object.isRequired,
Expand All @@ -27,9 +30,16 @@ export default class FilePatchController extends React.Component {
}

static defaultProps = {
largeDiffLineThreshold: 1000,
switchboard: new Switchboard(),
}

static confirmedLargeFilePatches = new Set()

static resetConfirmedLargeFilePatches() {
this.confirmedLargeFilePatches = new Set();
}

constructor(props, context) {
super(props, context);

Expand All @@ -55,6 +65,13 @@ export default class FilePatchController extends React.Component {
<span className="icon icon-info">File has no contents</span>
</div>
);
} else if (!this.shouldDisplayLargeDiff(this.props.filePatch)) {
return (
<div className="github-PaneView pane-item large-file-patch">
<p>This is a large diff. For performance reasons, it is not rendered by default.</p>
<button className="btn btn-primary" onClick={this.handleShowDiffClick}>Show Diff</button>
</div>
);
} else {
// NOTE: Outer div is required for etch to render elements correctly
const filePath = this.props.filePatch.getPath();
Expand Down Expand Up @@ -83,6 +100,16 @@ export default class FilePatchController extends React.Component {
}
}

shouldDisplayLargeDiff(filePatch) {
const fullPath = path.join(this.props.repository.getWorkingDirectoryPath(), this.props.filePatch.getPath());
if (FilePatchController.confirmedLargeFilePatches.has(fullPath)) {
return true;
}

const lineCount = filePatch.getHunks().reduce((acc, hunk) => hunk.getLines().length, 0);
return lineCount < this.props.largeDiffLineThreshold;
}

onDidChangeTitle(callback) {
return this.emitter.on('did-change-title', callback);
}
Expand All @@ -91,6 +118,15 @@ export default class FilePatchController extends React.Component {
return this.emitter.on('did-destroy', callback);
}

@autobind
handleShowDiffClick() {
if (this.props.repository) {
const fullPath = path.join(this.props.repository.getWorkingDirectoryPath(), this.props.filePatch.getPath());
FilePatchController.confirmedLargeFilePatches.add(fullPath);
this.forceUpdate();
}
}

async stageHunk(hunk) {
this.props.switchboard.didBeginStageOperation({stage: true, hunk: true});

Expand Down
6 changes: 5 additions & 1 deletion styles/pane-view.less
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
display: flex;
height: 100%;

&.is-blank {
&.is-blank, &.large-file-patch {
align-items: center;
justify-content: center;
text-align: center;
font-size: 1.2em;
padding: @component-padding;
}

&.large-file-patch {
flex-direction: column;
}
}
73 changes: 73 additions & 0 deletions test/controllers/file-patch-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ describe('FilePatchController', function() {

const filePatch = new FilePatch('a.txt', 'a.txt', 'modified', [new Hunk(1, 1, 1, 3, '', [])]);
const repository = Repository.absent();
sinon.stub(repository, 'getWorkingDirectoryPath').returns('/absent');
const resolutionProgress = new ResolutionProgress();

FilePatchController.resetConfirmedLargeFilePatches();

component = (
<FilePatchController
commandRegistry={commandRegistry}
Expand Down Expand Up @@ -79,6 +82,76 @@ describe('FilePatchController', function() {
assert.isTrue(changeHandler.calledWith(actualTitle));
});

describe('when the FilePatch has many lines', function() {
it('renders a confirmation widget', function() {
const hunk1 = new Hunk(0, 0, 1, 1, '', [
new HunkLine('line-1', 'added', 1, 1),
new HunkLine('line-2', 'added', 2, 2),
new HunkLine('line-3', 'added', 3, 3),
new HunkLine('line-4', 'added', 4, 4),
new HunkLine('line-5', 'added', 5, 5),
new HunkLine('line-6', 'added', 6, 6),
]);
const filePatch = new FilePatch('a.txt', 'a.txt', 'modified', [hunk1]);

const wrapper = mount(React.cloneElement(component, {
filePatch, largeDiffLineThreshold: 5,
}));

assert.isFalse(wrapper.find('FilePatchView').exists());
assert.match(wrapper.text(), /large diff/);
});

it('renders the full diff when the confirmation is clicked', function() {
const hunk = new Hunk(0, 0, 1, 1, '', [
new HunkLine('line-1', 'added', 1, 1),
new HunkLine('line-2', 'added', 2, 2),
new HunkLine('line-3', 'added', 3, 3),
new HunkLine('line-4', 'added', 4, 4),
new HunkLine('line-5', 'added', 5, 5),
new HunkLine('line-6', 'added', 6, 6),
]);
const filePatch = new FilePatch('a.txt', 'a.txt', 'modified', [hunk]);

const wrapper = mount(React.cloneElement(component, {
filePatch, largeDiffLineThreshold: 5,
}));

assert.isFalse(wrapper.find('FilePatchView').exists());

wrapper.find('button').simulate('click');
assert.isTrue(wrapper.find('FilePatchView').exists());
});

it('renders the full diff if the file has been confirmed before', async function() {
const hunk = new Hunk(0, 0, 1, 1, '', [
new HunkLine('line-1', 'added', 1, 1),
new HunkLine('line-2', 'added', 2, 2),
new HunkLine('line-3', 'added', 3, 3),
new HunkLine('line-4', 'added', 4, 4),
new HunkLine('line-5', 'added', 5, 5),
new HunkLine('line-6', 'added', 6, 6),
]);
const filePatch1 = new FilePatch('a.txt', 'a.txt', 'modified', [hunk]);
const filePatch2 = new FilePatch('b.txt', 'b.txt', 'modified', [hunk]);

const wrapper = mount(React.cloneElement(component, {
filePatch: filePatch1, largeDiffLineThreshold: 5,
}));

assert.isFalse(wrapper.find('FilePatchView').exists());

wrapper.find('button').simulate('click');
assert.isTrue(wrapper.find('FilePatchView').exists());

wrapper.setProps({filePatch: filePatch2});
await assert.async.isFalse(wrapper.find('FilePatchView').exists());

wrapper.setProps({filePatch: filePatch1});
assert.isTrue(wrapper.find('FilePatchView').exists());
});
});

it('renders FilePatchView only if FilePatch has hunks', function() {
const emptyFilePatch = new FilePatch('a.txt', 'a.txt', 'modified', []);

Expand Down