Skip to content

Commit 6921f9d

Browse files
committed
Add colours for files in browser
1 parent 3e08ea2 commit 6921f9d

File tree

5 files changed

+190
-7
lines changed

5 files changed

+190
-7
lines changed

src/browserDecorations.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Git, IGitExtension } from './tokens';
2+
import * as fileStyle from './style/BrowserFile';
3+
import { DirListing, FileBrowser } from '@jupyterlab/filebrowser';
4+
import { Contents } from '@jupyterlab/services';
5+
import { DocumentRegistry } from '@jupyterlab/docregistry';
6+
import { ITranslator } from '@jupyterlab/translation';
7+
8+
const statusStyles: Map<Git.StatusCode, string> = new Map([
9+
// note: the classes cannot repeat,
10+
// otherwise the assignments will be overwritten
11+
['M', fileStyle.modified],
12+
['A', fileStyle.added],
13+
['D', fileStyle.deleted],
14+
['R', fileStyle.renamed],
15+
['C', fileStyle.copied],
16+
['U', fileStyle.updated],
17+
['?', fileStyle.untracked],
18+
['!', fileStyle.ignored]
19+
]);
20+
21+
class GitListingRenderer extends DirListing.Renderer {
22+
constructor(private gitExtension: IGitExtension) {
23+
super();
24+
}
25+
26+
updateItemNode(
27+
node: HTMLElement,
28+
model: Contents.IModel,
29+
fileType?: DocumentRegistry.IFileType,
30+
translator?: ITranslator
31+
) {
32+
super.updateItemNode(node, model, fileType, translator);
33+
const file = this.gitExtension.getFile(model.path);
34+
console.log(model.path, 'file status', file?.status);
35+
let status_code: Git.StatusCode = null;
36+
if (file) {
37+
status_code = file.status === 'staged' ? file.x : file.y;
38+
console.log(model.path, 'file x, y', file.x, file.y);
39+
console.log(model.path, 'file status code', status_code);
40+
}
41+
42+
for (const [otherStatus, className] of statusStyles.entries()) {
43+
if (status_code === otherStatus) {
44+
node.classList.add(className);
45+
} else {
46+
node.classList.remove(className);
47+
}
48+
}
49+
}
50+
}
51+
52+
export function substituteListingRenderer(
53+
extension: IGitExtension,
54+
fileBrowser: FileBrowser
55+
): void {
56+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
57+
// @ts-ignore
58+
const listing: DirListing = fileBrowser._listing;
59+
const renderer = new GitListingRenderer(extension);
60+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
61+
// @ts-ignore
62+
listing._renderer = renderer;
63+
}

src/components/FileItem.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@ import {
1313
import { Git } from '../tokens';
1414
import { FilePath } from './FilePath';
1515

16-
// Git status codes https://git-scm.com/docs/git-status
17-
export const STATUS_CODES = {
16+
export const STATUS_CODES: Record<Git.StatusCode, string> = {
1817
M: 'Modified',
1918
A: 'Added',
2019
D: 'Deleted',
2120
R: 'Renamed',
2221
C: 'Copied',
2322
U: 'Updated',
2423
'?': 'Untracked',
25-
'!': 'Ignored'
24+
'!': 'Ignored',
25+
' ': 'Unchanged',
26+
'': 'Unchanged'
2627
};
2728

2829
/**
@@ -124,7 +125,7 @@ export interface IFileItemProps {
124125
}
125126

126127
export class FileItem extends React.PureComponent<IFileItemProps> {
127-
protected _getFileChangedLabel(change: keyof typeof STATUS_CODES): string {
128+
protected _getFileChangedLabel(change: Git.StatusCode): string {
128129
return STATUS_CODES[change];
129130
}
130131

@@ -157,7 +158,7 @@ export class FileItem extends React.PureComponent<IFileItemProps> {
157158
render(): JSX.Element {
158159
const { file } = this.props;
159160
const status_code = file.status === 'staged' ? file.x : file.y;
160-
const status = this._getFileChangedLabel(status_code as any);
161+
const status = this._getFileChangedLabel(status_code);
161162

162163
return (
163164
<div

src/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { GitWidget } from './widgets/GitWidget';
2525
import { addStatusBarWidget } from './widgets/StatusWidget';
2626

2727
export { Git, IGitExtension } from './tokens';
28+
import { substituteListingRenderer } from './browserDecorations';
2829

2930
/**
3031
* The default running sessions extension.
@@ -181,6 +182,8 @@ async function activate(
181182
app.commands,
182183
app.contextMenu
183184
);
185+
186+
substituteListingRenderer(gitExtension, factory.defaultBrowser);
184187
}
185188

186189
return gitExtension;

src/style/BrowserFile.ts

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { style } from 'typestyle';
2+
3+
export const modified = style({
4+
$nest: {
5+
'&:not(.jp-mod-selected)': {
6+
$nest: {
7+
'.jp-DirListing-itemText': {
8+
color: 'var(--md-blue-700)'
9+
}
10+
}
11+
}
12+
}
13+
});
14+
15+
export const updated = style({
16+
$nest: {
17+
'&:not(.jp-mod-selected)': {
18+
$nest: {
19+
'.jp-DirListing-itemText': {
20+
color: 'var(--md-cyan-700)'
21+
}
22+
}
23+
}
24+
}
25+
});
26+
27+
export const renamed = style({
28+
$nest: {
29+
'&:not(.jp-mod-selected)': {
30+
$nest: {
31+
'.jp-DirListing-itemText': {
32+
color: 'var(--md-purple-700)'
33+
}
34+
}
35+
}
36+
}
37+
});
38+
39+
export const copied = style({
40+
$nest: {
41+
'&:not(.jp-mod-selected)': {
42+
$nest: {
43+
'.jp-DirListing-itemText': {
44+
color: 'var(--md-indigo-700)'
45+
}
46+
}
47+
}
48+
}
49+
});
50+
51+
export const untracked = style({
52+
$nest: {
53+
'&:not(.jp-mod-selected)': {
54+
$nest: {
55+
'.jp-DirListing-itemText': {
56+
color: 'var(--md-red-700)'
57+
}
58+
}
59+
}
60+
}
61+
});
62+
63+
export const added = style({
64+
$nest: {
65+
'&:not(.jp-mod-selected)': {
66+
$nest: {
67+
'.jp-DirListing-itemText': {
68+
color: 'var(--md-green-700)'
69+
}
70+
}
71+
}
72+
}
73+
});
74+
75+
export const ignored = style({
76+
$nest: {
77+
'&:not(.jp-mod-selected)': {
78+
$nest: {
79+
'.jp-DirListing-itemText': {
80+
color: 'var(--md-grey-700)'
81+
}
82+
}
83+
}
84+
}
85+
});
86+
87+
export const deleted = style({
88+
$nest: {
89+
'&:not(.jp-mod-selected)': {
90+
$nest: {
91+
'.jp-DirListing-itemText': {
92+
color: 'var(--md-grey-700)'
93+
}
94+
}
95+
},
96+
'.jp-DirListing-itemText': {
97+
textDecoration: 'line-through'
98+
}
99+
}
100+
});

src/tokens.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -604,8 +604,8 @@ export namespace Git {
604604
* has the status of each changed file
605605
*/
606606
export interface IStatusFileResult {
607-
x: string;
608-
y: string;
607+
x: StatusCode;
608+
y: StatusCode;
609609
to: string;
610610
from: string;
611611
is_binary: boolean | null;
@@ -750,6 +750,22 @@ export namespace Git {
750750
| 'partially-staged'
751751
| null;
752752

753+
/**
754+
* Git status codes https://git-scm.com/docs/git-status
755+
*/
756+
export type StatusCode =
757+
| 'M'
758+
| 'A'
759+
| 'D'
760+
| 'R'
761+
| 'C'
762+
| 'U'
763+
| '?'
764+
| '!'
765+
// TODO: is this ' ' or ''? The codabase is inconsistent!
766+
| ' '
767+
| '';
768+
753769
export interface ITagResult {
754770
code: number;
755771
message?: string;

0 commit comments

Comments
 (0)