Skip to content

Commit 4ea7029

Browse files
committed
progress towards checkboxes in simple staging
1 parent 4a87a37 commit 4ea7029

File tree

6 files changed

+361
-19
lines changed

6 files changed

+361
-19
lines changed

src/components/FileItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export class FileItem extends React.Component<IFileItemProps, {}> {
123123
}
124124
}
125125

126-
getFileLableIconClass() {
126+
getFileLabelIconClass() {
127127
if (this.showDiscardWarning()) {
128128
return classes(fileIconStyle, parseFileExtension(this.props.file.to));
129129
} else {
@@ -248,7 +248,7 @@ export class FileItem extends React.Component<IFileItemProps, {}> {
248248
this.props.moveFile(this.props.file.to);
249249
}}
250250
/>
251-
<span className={this.getFileLableIconClass()} />
251+
<span className={this.getFileLabelIconClass()} />
252252
<span
253253
className={this.getFileLabelClass()}
254254
onContextMenu={e => {

src/components/FileItemSimple.tsx

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { Dialog, showDialog } from '@jupyterlab/apputils';
2+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
3+
import * as React from 'react';
4+
import { classes } from 'typestyle';
5+
import { GitExtension } from '../model';
6+
import {
7+
fileButtonStyle,
8+
fileGitButtonStyle,
9+
fileIconStyle,
10+
fileLabelStyle,
11+
fileStyle
12+
} from '../style/FileItemStyle';
13+
import {
14+
changeStageButtonStyle,
15+
diffFileButtonStyle,
16+
discardFileButtonStyle
17+
} from '../style/GitStageStyle';
18+
import { Git } from '../tokens';
19+
import { openListedFile, parseFileExtension } from '../utils';
20+
import { isDiffSupported } from './diff/Diff';
21+
import { openDiffView } from './diff/DiffWidget';
22+
import { ISpecialRef } from './diff/model';
23+
24+
export interface IFileItemSimpleProps {
25+
file: Git.IStatusFileResult;
26+
stage: string;
27+
model: GitExtension;
28+
discardFile: (file: string) => Promise<void>;
29+
renderMime: IRenderMimeRegistry;
30+
}
31+
32+
export class FileItemSimple extends React.Component<IFileItemSimpleProps, {}> {
33+
constructor(props: IFileItemSimpleProps) {
34+
super(props);
35+
}
36+
37+
getDiffFileIconClass() {
38+
return classes(
39+
fileButtonStyle,
40+
changeStageButtonStyle,
41+
fileGitButtonStyle,
42+
diffFileButtonStyle
43+
);
44+
}
45+
46+
getDiscardFileIconClass() {
47+
return classes(
48+
fileButtonStyle,
49+
changeStageButtonStyle,
50+
fileGitButtonStyle,
51+
discardFileButtonStyle
52+
);
53+
}
54+
55+
/**
56+
* Callback method discarding unstanged changes for selected file.
57+
* It shows modal asking for confirmation and when confirmed make
58+
* server side call to git checkout to discard changes in selected file.
59+
*/
60+
async discardSelectedFileChanges() {
61+
const result = await showDialog({
62+
title: 'Discard changes',
63+
body: `Are you sure you want to permanently discard changes to ${
64+
this.props.file.from
65+
}? This action cannot be undone.`,
66+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Discard' })]
67+
});
68+
if (result.button.accept) {
69+
this.props.discardFile(this.props.file.to);
70+
}
71+
}
72+
73+
render() {
74+
return (
75+
<li className={fileStyle}>
76+
<span
77+
className={classes(
78+
fileIconStyle,
79+
parseFileExtension(this.props.file.to)
80+
)}
81+
/>
82+
<span
83+
className={fileLabelStyle}
84+
onDoubleClick={() =>
85+
openListedFile(
86+
this.props.file.x,
87+
this.props.file.y,
88+
this.props.file.to,
89+
this.props.model
90+
)
91+
}
92+
title={this.props.file.to}
93+
>
94+
{this.props.file.to}
95+
</span>
96+
{this.props.stage === 'Changed' && (
97+
<React.Fragment>
98+
<button
99+
className={`jp-Git-button ${this.getDiscardFileIconClass()}`}
100+
title={'Discard this change'}
101+
onClick={() => {
102+
this.discardSelectedFileChanges();
103+
}}
104+
/>
105+
{isDiffSupported(this.props.file.to) &&
106+
this.diffButton({ specialRef: 'WORKING' })}
107+
</React.Fragment>
108+
)}
109+
{this.props.stage === 'Staged' &&
110+
isDiffSupported(this.props.file.to) &&
111+
this.diffButton({ specialRef: 'INDEX' })}
112+
</li>
113+
);
114+
}
115+
116+
/**
117+
* Creates a button element which is used to request diff from within the
118+
* Git panel.
119+
*
120+
* @param currentRef the ref to diff against the git 'HEAD' ref
121+
*/
122+
private diffButton(currentRef: ISpecialRef): JSX.Element {
123+
return (
124+
<button
125+
className={`jp-Git-button ${this.getDiffFileIconClass()}`}
126+
title={'Diff this file'}
127+
onClick={async () => {
128+
await openDiffView(
129+
this.props.file.to,
130+
this.props.model,
131+
{
132+
previousRef: { gitRef: 'HEAD' },
133+
currentRef: { specialRef: currentRef.specialRef }
134+
},
135+
this.props.renderMime
136+
);
137+
}}
138+
/>
139+
);
140+
}
141+
}

src/components/FileList.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { GitAuthorForm } from '../widgets/AuthorBox';
1717
import { CommitBox } from './CommitBox';
1818
import { openDiffView } from './diff/DiffWidget';
1919
import { GitStage, IGitStageProps } from './GitStage';
20+
import { GitStageSimple } from './GitStageSimple';
2021

2122
export namespace CommandIDs {
2223
export const gitFileOpen = 'gf:Open';
@@ -321,6 +322,17 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
321322
discardAllUnstagedFiles = async () => {
322323
try {
323324
await this.props.model.checkout();
325+
} catch (reason) {
326+
showErrorMessage('Discard all unstaged changes failed.', reason, [
327+
Dialog.warnButton({ label: 'DISMISS' })
328+
]);
329+
}
330+
};
331+
332+
/** Discard changes in all unstaged and staged files */
333+
discardAllChanges = async () => {
334+
try {
335+
await this.props.model.resetToCommit();
324336
} catch (reason) {
325337
showErrorMessage('Discard all changes failed.', reason, [
326338
Dialog.warnButton({ label: 'DISMISS' })
@@ -333,9 +345,10 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
333345
await this.props.model.add(file);
334346
};
335347

336-
/** Discard changes in a specific unstaged file */
337-
discardUnstagedFile = async (file: string) => {
348+
/** Discard changes in a specific unstaged or staged file */
349+
discardChanges = async (file: string) => {
338350
try {
351+
await this.props.model.reset(file);
339352
await this.props.model.checkout({ filename: file });
340353
} catch (reason) {
341354
showErrorMessage(`Discard changes for ${file} failed.`, reason, [
@@ -435,7 +448,7 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
435448
displayFiles={this.displayUnstaged}
436449
moveAllFiles={this.addAllUnstagedFiles}
437450
discardAllFiles={this.discardAllUnstagedFiles}
438-
discardFile={this.discardUnstagedFile}
451+
discardFile={this.discardChanges}
439452
moveFile={this.addUnstagedFile}
440453
moveFileIconClass={moveFileUpButtonStyle}
441454
moveFileIconSelectedClass={moveFileUpButtonSelectedStyle}
@@ -485,15 +498,13 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
485498
/>
486499
{this.props.settings.composite['simpleStaging'] ? (
487500
<div>
488-
<Changed
501+
<GitStageSimple
502+
heading={'Changed'}
489503
files={allFilesExcludingUnmodified()}
490-
discardAllFiles={null}
491-
discardFile={null}
492-
moveFile={null}
493-
moveFileIconClass={null}
494-
moveFileIconSelectedClass={null}
495-
moveAllFilesTitle={null}
496-
moveFileTitle={null}
504+
model={this.props.model}
505+
discardAllFiles={this.discardAllChanges}
506+
discardFile={this.discardChanges}
507+
renderMime={this.props.renderMime}
497508
/>
498509
</div>
499510
) : (

src/components/GitPanel.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ISettingRegistry } from '@jupyterlab/coreutils';
22
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
33
import * as React from 'react';
4-
import { GitExtension } from '../model';
4+
import { GitExtension, BranchMarker } from '../model';
55
import {
66
findRepoButtonStyle,
77
panelContainerStyle,
@@ -28,6 +28,8 @@ export interface IGitSessionNodeState {
2828
untrackedFiles: Git.IStatusFileResult[];
2929
hasChangedFiles: boolean;
3030

31+
marker: BranchMarker;
32+
3133
isHistoryVisible: boolean;
3234
}
3335

@@ -55,6 +57,7 @@ export class GitPanel extends React.Component<
5557
stagedFiles: [],
5658
unstagedFiles: [],
5759
untrackedFiles: [],
60+
marker: null,
5861
isHistoryVisible: false
5962
};
6063

@@ -99,7 +102,11 @@ export class GitPanel extends React.Component<
99102
this.setState({
100103
branches: branchData.branches,
101104
currentBranch: currentBranch,
102-
upstreamBranch: upstreamBranch
105+
upstreamBranch: upstreamBranch,
106+
marker: this.props.model.getMarker(
107+
this.props.model.pathRepository,
108+
currentBranch
109+
)
103110
});
104111
}
105112
};
@@ -151,6 +158,12 @@ export class GitPanel extends React.Component<
151158
}
152159
}
153160

161+
this.state.marker.addMarked(
162+
...stagedFiles.map(x => x.to),
163+
...unstagedFiles.map(x => x.to)
164+
);
165+
this.state.marker.addUnmarked(...untrackedFiles.map(x => x.to));
166+
154167
this.setState({
155168
stagedFiles: stagedFiles,
156169
unstagedFiles: unstagedFiles,

src/components/GitStageSimple.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { Dialog, showDialog } from '@jupyterlab/apputils';
2+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
3+
import * as React from 'react';
4+
import { classes } from 'typestyle';
5+
import { GitExtension } from '../model';
6+
import {
7+
changeStageButtonStyle,
8+
discardFileButtonStyle,
9+
sectionAreaStyle,
10+
sectionFileContainerStyle,
11+
sectionHeaderLabelStyle
12+
} from '../style/GitStageStyle';
13+
import { Git } from '../tokens';
14+
import { FileItemSimple } from './FileItemSimple';
15+
16+
export interface IGitStageSimpleProps {
17+
heading: string;
18+
files: Git.IStatusFileResult[];
19+
model: GitExtension;
20+
discardAllFiles: () => Promise<void>;
21+
discardFile: (file: string) => Promise<void>;
22+
renderMime: IRenderMimeRegistry;
23+
}
24+
25+
export class GitStageSimple extends React.Component<IGitStageSimpleProps> {
26+
constructor(props: IGitStageSimpleProps) {
27+
super(props);
28+
this.state = {
29+
showDiscardWarning: false
30+
};
31+
}
32+
33+
checkContents() {
34+
if (this.props.files.length > 0) {
35+
return false;
36+
} else {
37+
return true;
38+
}
39+
}
40+
41+
/**
42+
* Callback method discarding all unstanged changes.
43+
* It shows modal asking for confirmation and when confirmed make
44+
* server side call to git checkout to discard all unstanged changes.
45+
*/
46+
async discardAllChanges() {
47+
const result = await showDialog({
48+
title: 'Discard all changes',
49+
body: `Are you sure you want to permanently discard changes to all files? This action cannot be undone.`,
50+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Discard' })]
51+
});
52+
if (result.button.accept) {
53+
this.props.discardAllFiles();
54+
}
55+
}
56+
57+
render() {
58+
return (
59+
<div>
60+
<div className={sectionAreaStyle}>
61+
<span className={sectionHeaderLabelStyle}>
62+
{this.props.heading}({this.props.files.length})
63+
</span>
64+
{this.props.heading === 'Changed' && (
65+
<button
66+
disabled={this.checkContents()}
67+
className={classes(
68+
changeStageButtonStyle,
69+
discardFileButtonStyle
70+
)}
71+
title={'Discard All Changes'}
72+
onClick={() => this.discardAllChanges()}
73+
/>
74+
)}
75+
</div>
76+
<ul className={sectionFileContainerStyle}>
77+
{this.props.files.map(
78+
(file: Git.IStatusFileResult, fileIndex: number) => {
79+
return (
80+
<FileItemSimple
81+
key={fileIndex}
82+
file={file}
83+
stage={this.props.heading}
84+
model={this.props.model}
85+
discardFile={this.props.discardFile}
86+
renderMime={this.props.renderMime}
87+
/>
88+
);
89+
}
90+
)}
91+
</ul>
92+
</div>
93+
);
94+
}
95+
}

0 commit comments

Comments
 (0)