Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 40 additions & 16 deletions extensions/github1s/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"enabledApiProposals": [
"fileSearchProvider",
"textSearchProvider",
"resolvers"
"scmHistoryProvider",
"contribSourceControlHistoryTitleMenu",
"contribSourceControlHistoryItemMenu"
],
"directories": {
"lib": "lib"
Expand Down Expand Up @@ -66,6 +68,7 @@
{
"id": "github1s.views.settings",
"name": "Settings",
"icon": "$(settings-gear)",
"type": "webview",
"when": "github1s:views:settings:visible == true"
}
Expand All @@ -74,16 +77,13 @@
{
"id": "github1s.views.fileHistory",
"name": "File History",
"icon": "$(history)",
"when": "github1s:views:fileHistory:visible == true"
},
{
"id": "github1s.views.commitList",
"name": "Commits",
"when": "github1s:views:commitList:visible == true"
},
{
"id": "github1s.views.codeReviewList",
"name": "Code Reviews",
"icon": "$(git-pull-request)",
"when": "github1s:views:codeReviewList:visible == true"
}
]
Expand Down Expand Up @@ -496,16 +496,6 @@
"command": "github1s.commands.searchCommit",
"when": "view == 'github1s.views.fileHistory'",
"group": "navigation@2"
},
{
"command": "github1s.commands.refreshCommitList",
"when": "view == 'github1s.views.commitList'",
"group": "navigation@1"
},
{
"command": "github1s.commands.searchCommit",
"when": "view == 'github1s.views.commitList'",
"group": "navigation@2"
}
],
"view/item/context": [
Expand Down Expand Up @@ -616,6 +606,40 @@
"when": "!isInDiffEditor && github1s:features:gutterBlame:enabled && github1s:features:gutterBlame:open",
"group": "navigation@6"
}
],
"scm/history/title": [
{
"command": "github1s.commands.searchCommit",
"when": "scmProvider == github1s",
"group": "navigation@3"
}
],
"scm/historyItem/context": [
{
"command": "github1s.commands.switchToCommit",
"when": "scmProvider == github1s",
"group": "inline@1"
},
{
"command": "github1s.commands.openCommitOnGitHub",
"when": "scmProvider == github1s && github1s:adapters:default:platformName == 'GitHub'",
"group": "inline@2"
},
{
"command": "github1s.commands.openCommitOnGitLab",
"when": "scmProvider == github1s && github1s:adapters:default:platformName == 'GitLab'",
"group": "inline@2"
},
{
"command": "github1s.commands.openCommitOnBitbucket",
"when": "scmProvider == github1s && github1s:adapters:default:platformName == 'Bitbucket'",
"group": "inline@2"
},
{
"command": "github1s.commands.openCommitOnOfficialPage",
"when": "scmProvider == github1s && github1s:adapters:default:platformName != 'GitHub' && github1s:adapters:default:platformName != 'GitLab' && github1s:adapters:default:platformName != 'Bitbucket' && github1s:adapters:default:platformName != 'npm'",
"group": "inline@2"
}
]
}
},
Expand Down
151 changes: 151 additions & 0 deletions extensions/github1s/src/changes/history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import * as vscode from 'vscode';
import queryString from 'query-string';
import { Commit, FileChangeStatus } from '@/adapters/types';
import { Repository } from '@/repository';
import router from '@/router';
import { getCommitChangedFiles } from './files';

export class GitHub1sHistoryProvider implements vscode.SourceControlHistoryProvider, vscode.Disposable {
// Current route ref, with its commit SHA filled in after the first page loads.
currentHistoryItemRef: vscode.SourceControlHistoryItemRef | undefined;
// This provider does not expose upstream or comparison-base refs.
readonly currentHistoryItemRemoteRef = undefined;
readonly currentHistoryItemBaseRef = undefined;

// Tell VS Code to reread the current refs above.
private readonly currentRefsChanged = new vscode.EventEmitter<void>();
readonly onDidChangeCurrentHistoryItemRefs = this.currentRefsChanged.event;
// Report ref changes so VS Code can refresh the graph, even when the ref ID is unchanged.
private readonly refsChanged = new vscode.EventEmitter<vscode.SourceControlHistoryItemRefsChangeEvent>();
readonly onDidChangeHistoryItemRefs = this.refsChanged.event;
private readonly removeRouterListener = router.addListener((current, previous) => {
if (current.repo !== previous.repo || current.ref !== previous.ref) {
this.refresh();
}
});

refresh() {
const { repo, ref } = router.getState();
const previousRef = this.currentHistoryItemRef;
this.currentHistoryItemRef = repo ? { id: ref, name: ref, icon: new vscode.ThemeIcon('target') } : undefined;
this.currentRefsChanged.fire();
// Repository navigation can keep the same branch name (for example, "main").
if (previousRef?.id === this.currentHistoryItemRef?.id) {
this.refsChanged.fire({
added: [],
removed: [],
modified: this.currentHistoryItemRef ? [this.currentHistoryItemRef] : [],
silent: false,
});
}
}

provideHistoryItemRefs(ids: string[] | undefined): vscode.SourceControlHistoryItemRef[] {
const ref = this.currentHistoryItemRef;
return ref && (!ids || ids.includes(ref.id)) ? [ref] : [];
}

async provideHistoryItems(options: vscode.SourceControlHistoryOptions, token: vscode.CancellationToken) {
const currentRef = this.currentHistoryItemRef;
if (!currentRef || token.isCancellationRequested) {
return [];
}

const repository = Repository.getCurrentInstance();
const { ref } = router.getState();
const skip = options.skip ?? 0;
const limit = typeof options.limit === 'number' ? options.limit : 50;
let commits = await repository.getCommitList(ref, '/', skip === 0);

while (commits.length < skip + limit && (await repository.hasMoreCommits(ref))) {
if (token.isCancellationRequested || currentRef !== this.currentHistoryItemRef) {
return [];
}
await repository.loadMoreCommits(ref);
commits = await repository.getCommitList(ref);
}

if (token.isCancellationRequested || currentRef !== this.currentHistoryItemRef) {
return [];
}

if (skip === 0) {
this.currentHistoryItemRef = { ...currentRef, revision: commits[0]?.sha };
this.currentRefsChanged.fire();
}
return commits.slice(skip, skip + limit).map((commit) => this.toHistoryItem(commit));
}

async resolveHistoryItem(id: string) {
const commit = await Repository.getCurrentInstance().getCommitItem(id);
return commit ? this.toHistoryItem(commit) : undefined;
}

async provideHistoryItemChanges(id: string, parentId: string | undefined) {
const commit = await Repository.getCurrentInstance().getCommitItem(id);
if (!commit) {
return [];
}
// The existing data source exposes individual commits, not arbitrary ranges.
if (parentId !== commit.parents[0]) {
throw new Error("Only changes against a commit's first parent are supported.");
}
const files = await getCommitChangedFiles(commit);
return files.map((file) => {
// A missing side makes VS Code open the existing file directly for additions/deletions.
const originalUri = !parentId || file.status === FileChangeStatus.Added ? undefined : file.baseFileUri;
const modifiedUri = file.status === FileChangeStatus.Removed ? undefined : file.headFileUri;
// Carry context for our diff editor commands (open either side, previous/next revision).
const query =
originalUri && modifiedUri
? queryString.stringify({
base: originalUri.with({ query: '' }).toString(),
head: modifiedUri.with({ query: '' }).toString(),
status: file.status,
})
: '';
return {
// Display resource for the file label and status badge, including deleted files.
uri: file.headFileUri.with({ query: queryString.stringify({ changeStatus: file.status }) }),
// Content resources for the diff's left (before) and right (after) sides.
originalUri: originalUri?.with({ query }),
modifiedUri: modifiedUri?.with({ query }),
};
});
}

resolveHistoryItemRefsCommonAncestor(): undefined {
return undefined;
}

resolveHistoryItemChatContext(): undefined {
return undefined;
}

resolveHistoryItemChangeRangeChatContext(): undefined {
return undefined;
}

private toHistoryItem(commit: Commit): vscode.SourceControlHistoryItem {
return {
id: commit.sha,
// Parent IDs define the graph edges.
parentIds: commit.parents,
subject: commit.message.split('\n')[0],
message: commit.message,
displayId: commit.sha.slice(0, 7),
author: commit.author,
authorEmail: commit.email,
authorIcon: commit.avatarUrl ? vscode.Uri.parse(commit.avatarUrl) : undefined,
timestamp: commit.createTime?.getTime(),
// Attach the current ref label to the commit it points to.
references: this.currentHistoryItemRef?.revision === commit.sha ? [this.currentHistoryItemRef] : undefined,
};
}

dispose() {
this.removeRouterListener();
this.currentRefsChanged.dispose();
this.refsChanged.dispose();
}
}
44 changes: 26 additions & 18 deletions extensions/github1s/src/changes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,34 @@

import * as vscode from 'vscode';
import * as adapterTypes from '@/adapters/types';
import { getExtensionContext } from '@/helpers/context';
import { GitHub1sQuickDiffProvider } from './quick-diff';
import { getChangedFileDiffCommand, getChangedFiles } from './files';
import { GitHub1sHistoryProvider } from './history';

export const updateSourceControlChanges = (() => {
const sourceControl = vscode.scm.createSourceControl('github1s', 'GitHub1s');
const changesGroup = sourceControl.createResourceGroup('changes', 'Changes');
sourceControl.quickDiffProvider = new GitHub1sQuickDiffProvider();
const sourceControl = vscode.scm.createSourceControl('github1s', 'GitHub1s');
const changesGroup = sourceControl.createResourceGroup('changes', 'Changes');
sourceControl.quickDiffProvider = new GitHub1sQuickDiffProvider();

return async () => {
const changedFiles = await getChangedFiles();
export const registerSourceControlHistory = () => {
const context = getExtensionContext();
const historyProvider = new GitHub1sHistoryProvider();
sourceControl.historyProvider = historyProvider;
context.subscriptions.push(sourceControl, historyProvider);
historyProvider.refresh();
};

changesGroup.resourceStates = changedFiles.map((changedFile) => {
return {
resourceUri: changedFile.headFileUri.with({ authority: '' }),
decorations: {
strikeThrough: changedFile.status === adapterTypes.FileChangeStatus.Removed,
tooltip: changedFile.status,
},
command: getChangedFileDiffCommand(changedFile),
};
});
};
})();
export const updateSourceControlChanges = async () => {
const changedFiles = await getChangedFiles();

changesGroup.resourceStates = changedFiles.map((changedFile) => {
return {
resourceUri: changedFile.headFileUri.with({ authority: '' }),
decorations: {
strikeThrough: changedFile.status === adapterTypes.FileChangeStatus.Removed,
tooltip: changedFile.status,
},
command: getChangedFileDiffCommand(changedFile),
};
});
};
53 changes: 27 additions & 26 deletions extensions/github1s/src/commands/commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@
*/

import * as vscode from 'vscode';
import queryString from 'query-string';
import router from '@/router';
import { getAdapter } from '@/adapters';
import { Repository } from '@/repository';
import { CommitTreeItem, getCommitTreeItemDescription } from '@/views/commit-list';
import { commitTreeDataProvider, fileHistoryTreeDataProvider } from '@/views';
import { fileHistoryTreeDataProvider } from '@/views';

type CommitCommandArgument = string | CommitTreeItem | vscode.SourceControl;

// Graph actions pass (sourceControl, historyItem); tree actions pass a CommitTreeItem.
const getCommitSha = (item?: CommitCommandArgument, historyItem?: vscode.SourceControlHistoryItem): string => {
if (historyItem) {
return historyItem.id;
}
return typeof item === 'string' ? item : item && 'commit' in item ? item.commit.sha : '';
};

export const checkCommitExists = async (repo: string, commitSha: string) => {
const dataSoruce = await getAdapter().resolveDataSource();
Expand All @@ -24,12 +35,11 @@ export const checkCommitExists = async (repo: string, commitSha: string) => {
}
};

const commandSwitchToCommit = async (commitItemOrSha?: string | CommitTreeItem) => {
let commitSha: string | undefined = commitItemOrSha
? typeof commitItemOrSha === 'string'
? commitItemOrSha
: commitItemOrSha.commit.sha
: '';
const commandSwitchToCommit = async (
commitItemOrSha?: CommitCommandArgument,
historyItem?: vscode.SourceControlHistoryItem,
) => {
let commitSha: string | undefined = getCommitSha(commitItemOrSha, historyItem);
const { repo } = router.getState();
const repository = Repository.getCurrentInstance();

Expand Down Expand Up @@ -89,17 +99,18 @@ const commandDiffCommitFile = async (commitItem: CommitTreeItem) => {
if (!activeDocumentUri) {
return;
}
const fileUri = router.buildUri({ ref: commitSha }, activeDocumentUri).with({ query: '' });
const fileUri = router.buildUri({ ref: commitSha }, activeDocumentUri).with({
query: queryString.stringify({ from: router.getState().ref }),
});
return vscode.commands.executeCommand('github1s.commands.openFilePreviousRevision', fileUri);
};

// this command is used in `source control commit list view`
const commandOpenCommitOnOfficialPage = async (commitItemOrSha?: string | CommitTreeItem) => {
const commitSha = commitItemOrSha
? typeof commitItemOrSha === 'string'
? commitItemOrSha
: commitItemOrSha.commit.sha
: '';
const commandOpenCommitOnOfficialPage = async (
commitItemOrSha?: CommitCommandArgument,
historyItem?: vscode.SourceControlHistoryItem,
) => {
const commitSha = getCommitSha(commitItemOrSha, historyItem);
if (commitSha) {
const { repo } = router.getState();
const routerParser = router.getParser();
Expand All @@ -109,16 +120,8 @@ const commandOpenCommitOnOfficialPage = async (commitItemOrSha?: string | Commit
}
};

const commandRefreshCommitList = (forceUpdate = true) => {
return commitTreeDataProvider.updateTree(forceUpdate);
};

const commandLoadMoreCommits = async () => {
return commitTreeDataProvider.loadMoreCommits();
};

const commandLoadMoreCommitChangedFiles = async (commitSha: string) => {
return commitTreeDataProvider.loadMoreChangedFiles(commitSha);
const commandRefreshCommitList = () => {
return vscode.commands.executeCommand('workbench.scm.action.graph.refresh');
};

const commandRefreshFileHistoryCommitList = (forceUpdate = true) => {
Expand All @@ -143,8 +146,6 @@ export const registerCommitCommands = (context: vscode.ExtensionContext) => {
vscode.commands.registerCommand('github1s.commands.openCommitOnGitLab', commandOpenCommitOnOfficialPage),
vscode.commands.registerCommand('github1s.commands.openCommitOnBitbucket', commandOpenCommitOnOfficialPage),
vscode.commands.registerCommand('github1s.commands.openCommitOnOfficialPage', commandOpenCommitOnOfficialPage),
vscode.commands.registerCommand('github1s.commands.loadMoreCommits', commandLoadMoreCommits),
vscode.commands.registerCommand('github1s.commands.loadMoreCommitChangedFiles', commandLoadMoreCommitChangedFiles),
vscode.commands.registerCommand('github1s.commands.loadMoreFileHistoryCommits', commandLoadMoreFileHistoryCommits),
vscode.commands.registerCommand(
'github1s.commands.loadMoreFileHistoryCommitChangedFiles',
Expand Down
Loading
Loading