Skip to content

Commit cff6100

Browse files
authored
feat: support for github pull request (conwnet#228)
1 parent 5da739d commit cff6100

13 files changed

Lines changed: 437 additions & 45 deletions

File tree

extensions/github1s/package.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,44 @@
6666
"title": "Edit files in Gitpod",
6767
"category": "GitHub1s"
6868
}
69+
],
70+
"colors": [
71+
{
72+
"id": "gitDecoration.addedResourceForeground",
73+
"description": "%colors.added%",
74+
"defaults": {
75+
"light": "#587c0c",
76+
"dark": "#81b88b",
77+
"highContrast": "#1b5225"
78+
}
79+
},
80+
{
81+
"id": "gitDecoration.deletedResourceForeground",
82+
"description": "%colors.deleted%",
83+
"defaults": {
84+
"light": "#ad0707",
85+
"dark": "#c74e39",
86+
"highContrast": "#c74e39"
87+
}
88+
},
89+
{
90+
"id": "gitDecoration.modifiedResourceForeground",
91+
"description": "%colors.modified%",
92+
"defaults": {
93+
"light": "#895503",
94+
"dark": "#E2C08D",
95+
"highContrast": "#E2C08D"
96+
}
97+
},
98+
{
99+
"id": "gitDecoration.submoduleResourceForeground",
100+
"description": "%colors.submodule%",
101+
"defaults": {
102+
"light": "#1258a7",
103+
"dark": "#8db9e2",
104+
"highContrast": "#8db9e2"
105+
}
106+
}
69107
]
70108
},
71109
"scripts": {

extensions/github1s/src/extension.ts

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@ import {
1414
commandGetCurrentAuthority,
1515
commandOpenGitpod,
1616
} from '@/commands';
17-
import {
18-
GitHub1sFileSystemProvider,
19-
GitHub1sFileSearchProvider,
20-
GitHub1sTextSearchProvider,
21-
GitHub1sSubmoduleDecorationProvider,
22-
} from '@/providers';
17+
import { registerVSCodeProviders } from '@/providers';
18+
import { GitHub1sFileSystemProvider } from '@/providers/fileSystemProvider';
2319
import { showSponsors } from '@/sponsors';
2420
import { showGitpod } from '@/gitpod';
2521
import router from '@/router';
@@ -34,30 +30,8 @@ export async function activate(context: vscode.ExtensionContext) {
3430
await router.initialize();
3531
// register the necessary event listeners
3632
registerEventListeners();
37-
38-
// providers
39-
const fsProvider = new GitHub1sFileSystemProvider();
40-
context.subscriptions.push(
41-
vscode.workspace.registerFileSystemProvider(
42-
GitHub1sFileSystemProvider.scheme,
43-
fsProvider,
44-
{
45-
isCaseSensitive: true,
46-
isReadonly: true,
47-
}
48-
),
49-
vscode.workspace.registerFileSearchProvider(
50-
GitHub1sFileSearchProvider.scheme,
51-
new GitHub1sFileSearchProvider(fsProvider)
52-
),
53-
vscode.workspace.registerTextSearchProvider(
54-
GitHub1sTextSearchProvider.scheme,
55-
new GitHub1sTextSearchProvider()
56-
),
57-
vscode.window.registerFileDecorationProvider(
58-
new GitHub1sSubmoduleDecorationProvider(fsProvider)
59-
)
60-
);
33+
// register VS Code providers
34+
registerVSCodeProviders();
6135

6236
// views
6337
context.subscriptions.push(

extensions/github1s/src/helpers/util.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ export const dirname = (path: string): string => {
3838
return trimmedPath.substr(0, trimmedPath.lastIndexOf('/')) || '';
3939
};
4040

41+
export const basename = (path: string): string => {
42+
const trimmedPath = trimEnd(path, '/');
43+
return trimmedPath.substr(trimmedPath.lastIndexOf('/') + 1) || '';
44+
};
45+
4146
export const uniqueId = ((id) => () => id++)(1);
4247

4348
export const prop = (obj: object, path: (string | number)[] = []): any => {

extensions/github1s/src/interfaces/github-api-rest.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,23 @@ export const getGithubAllFiles = (
129129
).replace(/^\//, ':')}?recursive=1`
130130
).catch(handleRequestError);
131131
};
132+
133+
export const getGitHubPullDetail = (
134+
owner: string,
135+
repo: string,
136+
pullNumber: number
137+
) => {
138+
return fetch(
139+
`https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}`
140+
);
141+
};
142+
143+
export const getGithubPullFiles = (
144+
owner: string,
145+
repo: string,
146+
pullNumber: number
147+
) => {
148+
return fetch(
149+
`https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}/files?per_page=100`
150+
);
151+
};

extensions/github1s/src/listeners/router/source-control.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import { RouterState } from '@/router/types';
7+
import { updateSourceControlChanges } from '@/source-control/changes';
78
import { updateCheckoutRefOnStatusBar } from '@/source-control/status-bar';
89

910
export const sourceControlRouterListener = (
@@ -13,4 +14,8 @@ export const sourceControlRouterListener = (
1314
if (currentState.ref !== previousState.ref) {
1415
updateCheckoutRefOnStatusBar();
1516
}
17+
18+
if (currentState.pullNumber !== previousState.pullNumber) {
19+
updateSourceControlChanges();
20+
}
1621
};

extensions/github1s/src/listeners/vscode.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,45 @@
55

66
import * as vscode from 'vscode';
77
import router from '@/router';
8+
import { PageType } from '@/router/types';
9+
import { GitHub1sFileSearchProvider } from '@/providers/fileSearchProvider';
10+
11+
// current editor is recovered by vscode, but should be closed now
12+
const shouldClosedThisEditor = async (editor) => {
13+
const { pullNumber } = await router.getState();
14+
const resourceUriQuery = editor?.document.uri.query;
15+
const resourcePullNumber = resourceUriQuery?.match(/\bpull=(\d+)/)?.[1];
16+
17+
return resourcePullNumber && +resourcePullNumber !== pullNumber;
18+
};
819

920
export const registerVSCodeEventListeners = () => {
1021
// replace current url when user change active editor
1122
vscode.window.onDidChangeActiveTextEditor(async (editor) => {
12-
const filePath = editor?.document.uri.path || '';
13-
const { owner, repo, ref } = await router.getState();
23+
const { owner, repo, ref, pageType, pullNumber } = await router.getState();
24+
const activeFileUri = editor?.document.uri;
25+
26+
if (activeFileUri?.scheme !== GitHub1sFileSearchProvider.scheme) {
27+
return;
28+
}
29+
30+
if (await shouldClosedThisEditor(editor)) {
31+
vscode.commands.executeCommand(
32+
'workbench.action.closeActiveEditor',
33+
activeFileUri
34+
);
35+
return;
36+
}
1437

15-
// if no file opened and the branch is HEAD current, only retain owner and repo in url
16-
const browserPath =
17-
!filePath && ref.toUpperCase() === 'HEAD'
18-
? `/${owner}/${repo}`
19-
: `/${owner}/${repo}/${filePath ? 'blob' : 'tree'}/${ref}${filePath}`;
20-
router.history.replace(browserPath);
38+
// only `tree/blob` page will replace url with the active editor change
39+
if ([PageType.TREE, PageType.BLOB].includes(pageType)) {
40+
const filePath = activeFileUri?.path || '';
41+
// if no file opened and the branch is HEAD current, only retain owner and repo in url
42+
const browserPath =
43+
!filePath && ref.toUpperCase() === 'HEAD'
44+
? `/${owner}/${repo}`
45+
: `/${owner}/${repo}/${filePath ? 'blob' : 'tree'}/${ref}${filePath}`;
46+
router.history.replace(browserPath);
47+
}
2148
});
2249
};
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* @file GitHub1s Submodule FileDecorationProvider,
3+
* @author netcon
4+
* Decorate the directory which is a submodule in the file tree
5+
*/
6+
7+
import {
8+
CancellationToken,
9+
Disposable,
10+
Event,
11+
FileDecoration,
12+
FileDecorationProvider,
13+
ProviderResult,
14+
Uri,
15+
ThemeColor,
16+
} from 'vscode';
17+
import router from '@/router';
18+
import repository, { FileChangeType } from '@/repository';
19+
import { PageType } from '@/router/types';
20+
21+
const changedFileDecorationDataMap: { [key: string]: FileDecoration } = {
22+
[FileChangeType.ADDED]: {
23+
tooltip: 'Added',
24+
badge: 'A',
25+
color: new ThemeColor('gitDecoration.addedResourceForeground'),
26+
},
27+
[FileChangeType.REMOVED]: {
28+
tooltip: 'Deleted',
29+
badge: 'D',
30+
color: new ThemeColor('gitDecoration.deletedResourceForeground'),
31+
},
32+
[FileChangeType.MODIFIED]: {
33+
tooltip: 'Modified',
34+
badge: 'M',
35+
color: new ThemeColor('gitDecoration.modifiedResourceForeground'),
36+
},
37+
[FileChangeType.RENAMED]: {
38+
tooltip: 'Renamed',
39+
badge: 'R',
40+
color: new ThemeColor('gitDecoration.modifiedResourceForeground'),
41+
},
42+
};
43+
44+
export class GitHub1sChangedFileDecorationProvider
45+
implements FileDecorationProvider, Disposable {
46+
private readonly disposable: Disposable;
47+
48+
onDidChangeFileDecorations?: Event<Uri | Uri[]>;
49+
50+
dispose() {
51+
this.disposable?.dispose();
52+
}
53+
54+
provideFileDecoration(
55+
uri: Uri,
56+
_token: CancellationToken
57+
): ProviderResult<FileDecoration> {
58+
const currentFilePath = uri.path.slice(1);
59+
return router.getState().then(async (routerState) => {
60+
if (![PageType.PULL].includes(routerState.pageType)) {
61+
return null;
62+
}
63+
64+
const changedFiles = await repository.getPullFiles(
65+
routerState.pullNumber
66+
);
67+
const changedFile = changedFiles?.find(
68+
(changedFile) => changedFile.filename === currentFilePath
69+
);
70+
if (changedFile) {
71+
return changedFileDecorationDataMap[changedFile.status];
72+
}
73+
const includeChangedFile = changedFiles?.find((changedFile) =>
74+
changedFile.filename.startsWith(`${currentFilePath}/`)
75+
);
76+
if (includeChangedFile) {
77+
return changedFileDecorationDataMap[FileChangeType.MODIFIED];
78+
}
79+
return null;
80+
});
81+
}
82+
}
Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,42 @@
11
/**
2-
* @file export providers
2+
* @file register VS Code providers
33
* @author fezhang
44
*/
55

6-
export { GitHub1sFileSystemProvider } from './fileSystemProvider';
7-
export { GitHub1sFileSearchProvider } from './fileSearchProvider';
8-
export { GitHub1sTextSearchProvider } from './textSearchProvider';
9-
export { GitHub1sSubmoduleDecorationProvider } from './submoduleDecorationProvider';
6+
import * as vscode from 'vscode';
7+
import { getExtensionContext } from '@/helpers/context';
8+
import { GitHub1sFileSystemProvider } from './fileSystemProvider';
9+
import { GitHub1sFileSearchProvider } from './fileSearchProvider';
10+
import { GitHub1sTextSearchProvider } from './textSearchProvider';
11+
import { GitHub1sSubmoduleDecorationProvider } from './submoduleDecorationProvider';
12+
import { GitHub1sChangedFileDecorationProvider } from './changedFileDecorationProvider';
13+
14+
export const registerVSCodeProviders = () => {
15+
const context = getExtensionContext();
16+
const fsProvider = new GitHub1sFileSystemProvider();
17+
18+
context.subscriptions.push(
19+
vscode.workspace.registerFileSystemProvider(
20+
GitHub1sFileSystemProvider.scheme,
21+
fsProvider,
22+
{
23+
isCaseSensitive: true,
24+
isReadonly: true,
25+
}
26+
),
27+
vscode.workspace.registerFileSearchProvider(
28+
GitHub1sFileSearchProvider.scheme,
29+
new GitHub1sFileSearchProvider(fsProvider)
30+
),
31+
vscode.workspace.registerTextSearchProvider(
32+
GitHub1sTextSearchProvider.scheme,
33+
new GitHub1sTextSearchProvider()
34+
),
35+
vscode.window.registerFileDecorationProvider(
36+
new GitHub1sSubmoduleDecorationProvider(fsProvider)
37+
),
38+
vscode.window.registerFileDecorationProvider(
39+
new GitHub1sChangedFileDecorationProvider()
40+
)
41+
);
42+
};

0 commit comments

Comments
 (0)