Skip to content

Commit bd87296

Browse files
authored
Merge pull request jupyterlab#622 from ianhi/changes-tab
Add setting to allow double clicking file to open diff
2 parents 6aad8a1 + ed87f77 commit bd87296

File tree

9 files changed

+183
-36
lines changed

9 files changed

+183
-36
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,6 @@ node_modules/
119119
*.code-workspace
120120
.history
121121
.vscode
122+
123+
# vim stuff
124+
*.swp

schema/plugin.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
"description": "Disable all branch operations (new, switch) when there are changed/staged files",
1818
"default": false
1919
},
20+
"doubleClickDiff": {
21+
"type": "boolean",
22+
"title": "Show diff on double click",
23+
"description": "If true, doubling clicking a file in the list of changed files will open a diff",
24+
"default": false
25+
},
2026
"historyCount": {
2127
"type": "integer",
2228
"title": "History count",

src/components/FileItem.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
selectedFileStyle
1212
} from '../style/FileItemStyle';
1313
import { Git } from '../tokens';
14-
import { openListedFile } from '../utils';
1514
import { FilePath } from './FilePath';
1615

1716
// Git status codes https://git-scm.com/docs/git-status
@@ -32,6 +31,7 @@ export interface IFileItemProps {
3231
file: Git.IStatusFile;
3332
markBox?: boolean;
3433
model: GitExtension;
34+
onDoubleClick: () => void;
3535
selected?: boolean;
3636
selectFile?: (file: Git.IStatusFile | null) => void;
3737
}
@@ -94,9 +94,7 @@ export class FileItem extends React.Component<IFileItemProps> {
9494
this.props.contextMenu(event);
9595
})
9696
}
97-
onDoubleClick={() => {
98-
openListedFile(this.props.file, this.props.model);
99-
}}
97+
onDoubleClick={this.props.onDoubleClick}
10098
title={`${this.props.file.to}${status}`}
10199
>
102100
{this.props.markBox && (

src/components/FileList.tsx

Lines changed: 145 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
330330
}
331331

332332
private _renderStaged(files: Git.IStatusFile[]) {
333+
const doubleClickDiff = this.props.settings.get('doubleClickDiff')
334+
.composite as boolean;
333335
return (
334336
<GitStage
335337
actions={
@@ -346,12 +348,22 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
346348
nFiles={files.length}
347349
>
348350
{files.map((file: Git.IStatusFile) => {
351+
const openFile = () => {
352+
openListedFile(file, this.props.model);
353+
};
354+
const diffButton = this._createDiffButton(file, 'INDEX');
349355
return (
350356
<FileItem
351357
key={file.to}
352358
actions={
353359
<React.Fragment>
354-
{this._createDiffButton(file, 'INDEX')}
360+
<ActionButton
361+
className={hiddenButtonStyle}
362+
iconName={'open-file'}
363+
title={'Open this file'}
364+
onClick={openFile}
365+
/>
366+
{diffButton}
355367
<ActionButton
356368
className={hiddenButtonStyle}
357369
iconName={'git-remove'}
@@ -367,6 +379,13 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
367379
model={this.props.model}
368380
selected={this._isSelectedFile(file)}
369381
selectFile={this.updateSelectedFile}
382+
onDoubleClick={
383+
doubleClickDiff
384+
? diffButton
385+
? () => this._openDiffView(file, 'INDEX')
386+
: () => undefined
387+
: openFile
388+
}
370389
/>
371390
);
372391
})}
@@ -375,6 +394,8 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
375394
}
376395

377396
private _renderChanged(files: Git.IStatusFile[]) {
397+
const doubleClickDiff = this.props.settings.get('doubleClickDiff')
398+
.composite as boolean;
378399
const disabled = files.length === 0;
379400
return (
380401
<GitStage
@@ -401,11 +422,22 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
401422
nFiles={files.length}
402423
>
403424
{files.map((file: Git.IStatusFile) => {
425+
const openFile = () => {
426+
openListedFile(file, this.props.model);
427+
};
428+
const diffButton = this._createDiffButton(file, 'WORKING');
404429
return (
405430
<FileItem
406431
key={file.to}
407432
actions={
408433
<React.Fragment>
434+
<ActionButton
435+
className={hiddenButtonStyle}
436+
iconName={'open-file'}
437+
title={'Open this file'}
438+
onClick={openFile}
439+
/>
440+
{diffButton}
409441
<ActionButton
410442
className={hiddenButtonStyle}
411443
iconName={'git-discard'}
@@ -414,7 +446,6 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
414446
this.discardChanges(file.to);
415447
}}
416448
/>
417-
{this._createDiffButton(file, 'WORKING')}
418449
<ActionButton
419450
className={hiddenButtonStyle}
420451
iconName={'git-add'}
@@ -430,6 +461,13 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
430461
model={this.props.model}
431462
selected={this._isSelectedFile(file)}
432463
selectFile={this.updateSelectedFile}
464+
onDoubleClick={
465+
doubleClickDiff
466+
? diffButton
467+
? () => this._openDiffView(file, 'WORKING')
468+
: () => undefined
469+
: openFile
470+
}
433471
/>
434472
);
435473
})}
@@ -438,6 +476,8 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
438476
}
439477

440478
private _renderUntracked(files: Git.IStatusFile[]) {
479+
const doubleClickDiff = this.props.settings.get('doubleClickDiff')
480+
.composite as boolean;
441481
return (
442482
<GitStage
443483
actions={
@@ -458,18 +498,33 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
458498
<FileItem
459499
key={file.to}
460500
actions={
461-
<ActionButton
462-
className={hiddenButtonStyle}
463-
iconName={'git-add'}
464-
title={'Track this file'}
465-
onClick={() => {
466-
this.addFile(file.to);
467-
}}
468-
/>
501+
<React.Fragment>
502+
<ActionButton
503+
className={hiddenButtonStyle}
504+
iconName={'open-file'}
505+
title={'Open this file'}
506+
onClick={async () => {
507+
openListedFile(file, this.props.model);
508+
}}
509+
/>
510+
<ActionButton
511+
className={hiddenButtonStyle}
512+
iconName={'git-add'}
513+
title={'Track this file'}
514+
onClick={() => {
515+
this.addFile(file.to);
516+
}}
517+
/>
518+
</React.Fragment>
469519
}
470520
file={file}
471521
contextMenu={this.contextMenuUntracked}
472522
model={this.props.model}
523+
onDoubleClick={() => {
524+
if (!doubleClickDiff) {
525+
openListedFile(file, this.props.model);
526+
}
527+
}}
473528
selected={this._isSelectedFile(file)}
474529
selectFile={this.updateSelectedFile}
475530
/>
@@ -480,6 +535,8 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
480535
}
481536

482537
private _renderSimpleStage(files: Git.IStatusFile[]) {
538+
const doubleClickDiff = this.props.settings.get('doubleClickDiff')
539+
.composite as boolean;
483540
return (
484541
<GitStage
485542
actions={
@@ -495,10 +552,35 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
495552
nFiles={files.length}
496553
>
497554
{files.map((file: Git.IStatusFile) => {
498-
let actions = null;
555+
const openFile = () => {
556+
openListedFile(file, this.props.model);
557+
};
558+
559+
// Default value for actions and double click
560+
let actions: JSX.Element = (
561+
<ActionButton
562+
className={hiddenButtonStyle}
563+
iconName={'open-file'}
564+
title={'Open this file'}
565+
onClick={openFile}
566+
/>
567+
);
568+
let onDoubleClick = doubleClickDiff
569+
? (): void => undefined
570+
: openFile;
571+
572+
let diffButton: JSX.Element;
499573
if (file.status === 'unstaged') {
574+
diffButton = this._createDiffButton(file, 'WORKING');
500575
actions = (
501576
<React.Fragment>
577+
<ActionButton
578+
className={hiddenButtonStyle}
579+
iconName={'open-file'}
580+
title={'Open this file'}
581+
onClick={openFile}
582+
/>
583+
{diffButton}
502584
<ActionButton
503585
className={hiddenButtonStyle}
504586
iconName={'git-discard'}
@@ -507,11 +589,31 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
507589
this.discardChanges(file.to);
508590
}}
509591
/>
510-
{this._createDiffButton(file, 'WORKING')}
511592
</React.Fragment>
512593
);
594+
onDoubleClick = doubleClickDiff
595+
? diffButton
596+
? () => this._openDiffView(file, 'WORKING')
597+
: () => undefined
598+
: openFile;
513599
} else if (file.status === 'staged') {
514-
actions = this._createDiffButton(file, 'INDEX');
600+
diffButton = this._createDiffButton(file, 'INDEX');
601+
actions = (
602+
<React.Fragment>
603+
<ActionButton
604+
className={hiddenButtonStyle}
605+
iconName={'open-file'}
606+
title={'Open this file'}
607+
onClick={openFile}
608+
/>
609+
{diffButton}
610+
</React.Fragment>
611+
);
612+
onDoubleClick = doubleClickDiff
613+
? diffButton
614+
? () => this._openDiffView(file, 'INDEX')
615+
: () => undefined
616+
: openFile;
515617
}
516618

517619
return (
@@ -521,6 +623,7 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
521623
file={file}
522624
markBox={true}
523625
model={this.props.model}
626+
onDoubleClick={onDoubleClick}
524627
/>
525628
);
526629
})}
@@ -529,7 +632,8 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
529632
}
530633

531634
/**
532-
* Creates a button element which is used to request diff of a file.
635+
* Creates a button element which, depending on the settings, is used
636+
* to either request a diff of the file, or open the file
533637
*
534638
* @param path File path of interest
535639
* @param currentRef the ref to diff against the git 'HEAD' ref
@@ -544,29 +648,38 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
544648
className={hiddenButtonStyle}
545649
iconName={'git-diff'}
546650
title={'Diff this file'}
547-
onClick={async () => {
548-
try {
549-
await openDiffView(
550-
file.to,
551-
this.props.model,
552-
{
553-
previousRef: { gitRef: 'HEAD' },
554-
currentRef: { specialRef: currentRef }
555-
},
556-
this.props.renderMime,
557-
!file.is_binary
558-
);
559-
} catch (reason) {
560-
console.error(
561-
`Fail to open diff view for ${file.to}.\n${reason}`
562-
);
563-
}
564-
}}
651+
onClick={() => this._openDiffView(file, currentRef)}
565652
/>
566653
)
567654
);
568655
}
569656

657+
/**
658+
* Returns a callback which opens a diff of the file
659+
*
660+
* @param file File to open diff for
661+
* @param currentRef the ref to diff against the git 'HEAD' ref
662+
*/
663+
private async _openDiffView(
664+
file: Git.IStatusFile,
665+
currentRef: ISpecialRef['specialRef']
666+
): Promise<void> {
667+
try {
668+
await openDiffView(
669+
file.to,
670+
this.props.model,
671+
{
672+
previousRef: { gitRef: 'HEAD' },
673+
currentRef: { specialRef: currentRef }
674+
},
675+
this.props.renderMime,
676+
!file.is_binary
677+
);
678+
} catch (reason) {
679+
console.error(`Failed to open diff view for ${file.to}.\n${reason}`);
680+
}
681+
}
682+
570683
private _contextMenuStaged: Menu;
571684
private _contextMenuUnstaged: Menu;
572685
private _contextMenuUntracked: Menu;

src/gitMenuCommands.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export namespace CommandIDs {
2121
export const gitInit = 'git:init';
2222
export const gitOpenUrl = 'git:open-url';
2323
export const gitToggleSimpleStaging = 'git:toggle-simple-staging';
24+
export const gitToggleDoubleClickDiff = 'git:toggle-double-click-diff';
2425
export const gitAddRemote = 'git:add-remote';
2526
export const gitClone = 'git:clone';
2627
}
@@ -115,6 +116,15 @@ export function addCommands(
115116
}
116117
});
117118

119+
/** add toggle for double click opens diffs */
120+
commands.addCommand(CommandIDs.gitToggleDoubleClickDiff, {
121+
label: 'Double click opens diff',
122+
isToggled: () => !!settings.composite['doubleClickDiff'],
123+
execute: args => {
124+
settings.set('doubleClickDiff', !settings.composite['doubleClickDiff']);
125+
}
126+
});
127+
118128
/** Command to add a remote Git repository */
119129
commands.addCommand(CommandIDs.gitAddRemote, {
120130
label: 'Add remote repository',

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,7 @@ function createGitMenu(
166166

167167
menu.addItem({ command: CommandIDs.gitToggleSimpleStaging });
168168

169+
menu.addItem({ command: CommandIDs.gitToggleDoubleClickDiff });
170+
169171
return menu;
170172
}

0 commit comments

Comments
 (0)