diff --git a/lib/controllers/file-patch-controller.js b/lib/controllers/file-patch-controller.js index 1ba40a897d..a0953eba60 100644 --- a/lib/controllers/file-patch-controller.js +++ b/lib/controllers/file-patch-controller.js @@ -1,3 +1,5 @@ +import path from 'path'; + import React from 'react'; import PropTypes from 'prop-types'; @@ -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, @@ -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); @@ -55,6 +65,13 @@ export default class FilePatchController extends React.Component { File has no contents ); + } else if (!this.shouldDisplayLargeDiff(this.props.filePatch)) { + return ( +
+

This is a large diff. For performance reasons, it is not rendered by default.

+ +
+ ); } else { // NOTE: Outer div is required for etch to render elements correctly const filePath = this.props.filePatch.getPath(); @@ -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); } @@ -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}); diff --git a/styles/pane-view.less b/styles/pane-view.less index d5160d6b09..e18fdf118d 100644 --- a/styles/pane-view.less +++ b/styles/pane-view.less @@ -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; + } } diff --git a/test/controllers/file-patch-controller.test.js b/test/controllers/file-patch-controller.test.js index 3641e36546..bc5e1e4ef4 100644 --- a/test/controllers/file-patch-controller.test.js +++ b/test/controllers/file-patch-controller.test.js @@ -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 = (