mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-16 01:25:42 -05:00
Adds new GitLens custom view
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
import { commands, Disposable, SourceControlResourceGroup, SourceControlResourceState, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
|
||||
import { ExplorerNode } from '../views/explorerNodes';
|
||||
import { GitBranch, GitCommit } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { Telemetry } from '../telemetry';
|
||||
|
||||
@@ -125,6 +126,18 @@ export interface CommandViewContext extends CommandBaseContext {
|
||||
node: ExplorerNode;
|
||||
}
|
||||
|
||||
export function isCommandViewContextWithBranch(context: CommandContext): context is CommandViewContext & { node: (ExplorerNode & { branch: GitBranch }) } {
|
||||
return context.type === 'view' && (context.node as any).branch && (context.node as any).branch instanceof GitBranch;
|
||||
}
|
||||
|
||||
interface ICommandViewContextWithCommit<T extends GitCommit> extends CommandViewContext {
|
||||
node: (ExplorerNode & { commit: T });
|
||||
}
|
||||
|
||||
export function isCommandViewContextWithCommit<T extends GitCommit>(context: CommandContext): context is ICommandViewContextWithCommit<T> {
|
||||
return context.type === 'view' && (context.node as any).commit && (context.node as any).commit instanceof GitCommit;
|
||||
}
|
||||
|
||||
export type CommandContext = CommandScmGroupsContext | CommandScmStatesContext | CommandUnknownContext | CommandUriContext | CommandViewContext;
|
||||
|
||||
function isScmResourceGroup(group: any): group is SourceControlResourceGroup {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
import { Iterables } from '../system';
|
||||
import { TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
||||
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { copy } from 'copy-paste';
|
||||
@@ -17,6 +17,16 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
|
||||
super(Commands.CopyMessageToClipboard);
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
|
||||
if (isCommandViewContextWithCommit(context)) {
|
||||
args = { ...args };
|
||||
args.sha = context.node.commit.sha;
|
||||
return this.execute(context.editor, context.node.commit.uri, args);
|
||||
}
|
||||
|
||||
return this.execute(context.editor, context.uri, args);
|
||||
}
|
||||
|
||||
async execute(editor?: TextEditor, uri?: Uri, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
|
||||
uri = getCommandUri(uri, editor);
|
||||
|
||||
@@ -64,7 +74,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
|
||||
|
||||
// Get the full commit message -- since blame only returns the summary
|
||||
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, args.sha);
|
||||
if (!commit) return undefined;
|
||||
if (commit === undefined) return undefined;
|
||||
|
||||
args.message = commit.message;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
import { Iterables } from '../system';
|
||||
import { TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
||||
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { copy } from 'copy-paste';
|
||||
@@ -16,6 +16,16 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
|
||||
super(Commands.CopyShaToClipboard);
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
|
||||
if (isCommandViewContextWithCommit(context)) {
|
||||
args = { ...args };
|
||||
args.sha = context.node.commit.sha;
|
||||
return this.execute(context.editor, context.node.commit.uri, args);
|
||||
}
|
||||
|
||||
return this.execute(context.editor, context.uri, args);
|
||||
}
|
||||
|
||||
async execute(editor?: TextEditor, uri?: Uri, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
|
||||
uri = getCommandUri(uri, editor);
|
||||
|
||||
@@ -45,7 +55,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
|
||||
|
||||
try {
|
||||
const blame = await this.git.getBlameForLine(gitUri, blameline);
|
||||
if (!blame) return undefined;
|
||||
if (blame === undefined) return undefined;
|
||||
|
||||
args.sha = blame.commit.sha;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Iterables } from '../system';
|
||||
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
||||
import { BuiltInCommands, GlyphChars } from '../constants';
|
||||
import { BuiltInCommands, FakeSha, GlyphChars } from '../constants';
|
||||
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
|
||||
import { GitCommit, GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
@@ -14,6 +14,10 @@ export interface DiffWithPreviousCommandArgs {
|
||||
line?: number;
|
||||
range?: Range;
|
||||
showOptions?: TextDocumentShowOptions;
|
||||
|
||||
allowMissingPrevious?: boolean;
|
||||
leftTitlePrefix?: string;
|
||||
rightTitlePrefix?: string;
|
||||
}
|
||||
|
||||
export class DiffWithPreviousCommand extends ActiveEditorCommand {
|
||||
@@ -51,12 +55,12 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
|
||||
}
|
||||
}
|
||||
|
||||
if (args.commit.previousSha === undefined) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit);
|
||||
if (args.commit.previousSha === undefined && !args.allowMissingPrevious) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit);
|
||||
|
||||
try {
|
||||
const [rhs, lhs] = await Promise.all([
|
||||
this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha),
|
||||
this.git.getVersionedFile(args.commit.repoPath, args.commit.previousUri.fsPath, args.commit.previousSha)
|
||||
this.git.getVersionedFile(args.commit.repoPath, args.commit.previousUri.fsPath, args.commit.previousSha === undefined ? FakeSha : args.commit.previousSha)
|
||||
]);
|
||||
|
||||
if (args.line !== undefined && args.line !== 0) {
|
||||
@@ -69,7 +73,9 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
|
||||
await commands.executeCommand(BuiltInCommands.Diff,
|
||||
Uri.file(lhs),
|
||||
Uri.file(rhs),
|
||||
`${path.basename(args.commit.previousUri.fsPath)} (${args.commit.previousShortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha})`,
|
||||
args.commit.previousShortSha === undefined
|
||||
? `${path.basename(args.commit.uri.fsPath)} (${args.rightTitlePrefix || ''}${args.commit.shortSha})`
|
||||
: `${path.basename(args.commit.previousUri.fsPath)} (${args.leftTitlePrefix || ''}${args.commit.previousShortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(args.commit.uri.fsPath)} (${args.rightTitlePrefix || ''}${args.commit.shortSha})`,
|
||||
args.showOptions);
|
||||
}
|
||||
catch (ex) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
import { Arrays } from '../system';
|
||||
import { commands, TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
||||
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithBranch } from './common';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
@@ -18,6 +18,16 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
|
||||
super(Commands.OpenBranchInRemote);
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: OpenBranchInRemoteCommandArgs = {}): Promise<any> {
|
||||
if (isCommandViewContextWithBranch(context)) {
|
||||
args = { ...args };
|
||||
args.branch = context.node.branch.name;
|
||||
return this.execute(context.editor, context.uri, args);
|
||||
}
|
||||
|
||||
return this.execute(context.editor, context.uri, args);
|
||||
}
|
||||
|
||||
async execute(editor?: TextEditor, uri?: Uri, args: OpenBranchInRemoteCommandArgs = {}) {
|
||||
uri = getCommandUri(uri, editor);
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
'use strict';
|
||||
import { Arrays } from '../system';
|
||||
import { commands, TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri } from './common';
|
||||
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||
import { GitBlameCommit, GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { Messages } from '../messages';
|
||||
import { OpenInRemoteCommandArgs } from './openInRemote';
|
||||
import { CommitNode } from '../views/explorerNodes';
|
||||
|
||||
export interface OpenCommitInRemoteCommandArgs {
|
||||
sha?: string;
|
||||
@@ -19,7 +18,7 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: OpenCommitInRemoteCommandArgs = {}): Promise<any> {
|
||||
if (context.type === 'view' && context.node instanceof CommitNode) {
|
||||
if (isCommandViewContextWithCommit(context)) {
|
||||
args = { ...args };
|
||||
args.sha = context.node.commit.sha;
|
||||
return this.execute(context.editor, context.node.commit.uri, args);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
import { Arrays } from '../system';
|
||||
import { commands, Range, TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
|
||||
import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { OpenInRemoteCommandArgs } from './openInRemote';
|
||||
@@ -16,6 +16,16 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
|
||||
super(Commands.OpenFileInRemote);
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: OpenFileInRemoteCommandArgs = {}): Promise<any> {
|
||||
if (isCommandViewContextWithCommit(context)) {
|
||||
args = { ...args };
|
||||
args.range = false;
|
||||
return this.execute(context.editor, context.node.commit.uri, args);
|
||||
}
|
||||
|
||||
return this.execute(context.editor, context.uri, args);
|
||||
}
|
||||
|
||||
async execute(editor?: TextEditor, uri?: Uri, args: OpenFileInRemoteCommandArgs = { range: true }) {
|
||||
uri = getCommandUri(uri, editor);
|
||||
if (uri === undefined) return undefined;
|
||||
|
||||
@@ -28,6 +28,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
|
||||
|
||||
try {
|
||||
if (args.remotes.length === 1) {
|
||||
this.ensureRemoteBranchName(args);
|
||||
const command = new OpenRemoteCommandQuickPickItem(args.remotes[0], args.resource);
|
||||
return command.execute();
|
||||
}
|
||||
@@ -35,16 +36,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
|
||||
let placeHolder = '';
|
||||
switch (args.resource.type) {
|
||||
case 'branch':
|
||||
// Check to see if the remote is in the branch
|
||||
const index = args.resource.branch.indexOf('/');
|
||||
if (index >= 0) {
|
||||
const remoteName = args.resource.branch.substring(0, index);
|
||||
const remote = args.remotes.find(r => r.name === remoteName);
|
||||
if (remote !== undefined) {
|
||||
args.resource.branch = args.resource.branch.substring(index + 1);
|
||||
args.remotes = [remote];
|
||||
}
|
||||
}
|
||||
this.ensureRemoteBranchName(args);
|
||||
placeHolder = `open ${args.resource.branch} branch in${GlyphChars.Ellipsis}`;
|
||||
break;
|
||||
|
||||
@@ -54,6 +46,10 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
placeHolder = `open ${args.resource.fileName} in${GlyphChars.Ellipsis}`;
|
||||
break;
|
||||
|
||||
case 'revision':
|
||||
if (args.resource.commit !== undefined && args.resource.commit instanceof GitLogCommit) {
|
||||
if (args.resource.commit.status === 'D') {
|
||||
args.resource.sha = args.resource.commit.previousSha;
|
||||
@@ -71,10 +67,6 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
|
||||
placeHolder = `open ${args.resource.fileName}${shaSuffix} in${GlyphChars.Ellipsis}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'working-file':
|
||||
placeHolder = `open ${args.resource.fileName} in${GlyphChars.Ellipsis}`;
|
||||
break;
|
||||
}
|
||||
|
||||
if (args.remotes.length === 1) {
|
||||
@@ -93,4 +85,19 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
|
||||
return window.showErrorMessage(`Unable to open in remote provider. See output channel for more details`);
|
||||
}
|
||||
}
|
||||
|
||||
private ensureRemoteBranchName(args: OpenInRemoteCommandArgs) {
|
||||
if (args.remotes === undefined || args.resource === undefined || args.resource.type !== 'branch') return;
|
||||
|
||||
// Check to see if the remote is in the branch
|
||||
const index = args.resource.branch.indexOf('/');
|
||||
if (index >= 0) {
|
||||
const remoteName = args.resource.branch.substring(0, index);
|
||||
const remote = args.remotes.find(r => r.name === remoteName);
|
||||
if (remote !== undefined) {
|
||||
args.resource.branch = args.resource.branch.substring(index + 1);
|
||||
args.remotes = [remote];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import { Logger } from '../logger';
|
||||
import { Messages } from '../messages';
|
||||
import { CommandQuickPickItem, CommitsQuickPick } from '../quickPicks';
|
||||
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
|
||||
import { paste } from 'copy-paste';
|
||||
|
||||
const searchByRegex = /^([@:#])/;
|
||||
const searchByMap = new Map<string, GitRepoSearchBy>([
|
||||
@@ -49,12 +48,6 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
|
||||
args.search = `#${blameLine.commit.shortSha}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.search) {
|
||||
args.search = await new Promise<string>((resolve, reject) => {
|
||||
paste((err: Error, content: string) => resolve(err ? '' : content));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||
import { Commands, EditorCommand, getCommandUri } from './common';
|
||||
import { BuiltInCommands } from '../constants';
|
||||
import { GitExplorer } from '../views/gitExplorer';
|
||||
// import { GitExplorer } from '../views/gitExplorer';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { Messages } from '../messages';
|
||||
import { Logger } from '../logger';
|
||||
@@ -15,7 +15,7 @@ export interface ShowFileHistoryCommandArgs {
|
||||
|
||||
export class ShowFileHistoryCommand extends EditorCommand {
|
||||
|
||||
constructor(private git: GitService, private explorer?: GitExplorer) {
|
||||
constructor(private git: GitService) {
|
||||
super(Commands.ShowFileHistory);
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ export class ShowFileHistoryCommand extends EditorCommand {
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
try {
|
||||
if (this.explorer !== undefined) {
|
||||
this.explorer.addHistory(gitUri);
|
||||
return undefined;
|
||||
}
|
||||
// if (this.explorer !== undefined) {
|
||||
// this.explorer.addHistory(gitUri);
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
const locations = await this.git.getLogLocations(gitUri, args.sha, args.line);
|
||||
if (locations === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history');
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
import { Strings } from '../system';
|
||||
import { commands, TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri } from './common';
|
||||
import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { CommitNode } from '../views/explorerNodes';
|
||||
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
|
||||
@@ -27,7 +26,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
|
||||
|
||||
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
|
||||
if (context.type === 'view') {
|
||||
if (context.node instanceof CommitNode) {
|
||||
if (isCommandViewContextWithCommit(context)) {
|
||||
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
import { Strings } from '../system';
|
||||
import { TextEditor, Uri, window } from 'vscode';
|
||||
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
|
||||
import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
@@ -24,6 +24,18 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
|
||||
super(Commands.ShowQuickCommitFileDetails);
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, ...args: any[]): Promise<any> {
|
||||
if (context.type === 'view') {
|
||||
if (isCommandViewContextWithCommit(context)) {
|
||||
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
|
||||
}
|
||||
else {
|
||||
args = [{ sha: context.node.uri.sha }];
|
||||
}
|
||||
}
|
||||
return this.execute(context.editor, context.uri, ...args);
|
||||
}
|
||||
|
||||
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitFileDetailsCommandArgs = {}) {
|
||||
uri = getCommandUri(uri, editor);
|
||||
if (uri === undefined) return undefined;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
'use strict';
|
||||
import { Strings } from '../system';
|
||||
import { MessageItem, window } from 'vscode';
|
||||
import { GitService, GitStashCommit } from '../gitService';
|
||||
import { Command, CommandContext, Commands } from './common';
|
||||
import { Command, CommandContext, Commands, isCommandViewContextWithCommit } from './common';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { CommitQuickPickItem, StashListQuickPick } from '../quickPicks';
|
||||
import { GitService, GitStashCommit } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { CommandQuickPickItem } from '../quickPicks';
|
||||
import { StashCommitNode } from '../views/stashCommitNode';
|
||||
import { CommandQuickPickItem, CommitQuickPickItem, StashListQuickPick } from '../quickPicks';
|
||||
|
||||
export interface StashApplyCommandArgs {
|
||||
confirm?: boolean;
|
||||
@@ -24,12 +22,9 @@ export class StashApplyCommand extends Command {
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) {
|
||||
if (context.type === 'view' && context.node instanceof StashCommitNode) {
|
||||
if (isCommandViewContextWithCommit<GitStashCommit>(context)) {
|
||||
args = { ...args };
|
||||
|
||||
const stash = context.node.commit;
|
||||
args.stashItem = { stashName: stash.stashName, message: stash.message };
|
||||
|
||||
args.stashItem = { stashName: context.node.commit.stashName, message: context.node.commit.message };
|
||||
return this.execute(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
'use strict';
|
||||
import { MessageItem, window } from 'vscode';
|
||||
import { Command, CommandContext, Commands } from './common';
|
||||
import { Command, CommandContext, Commands, isCommandViewContextWithCommit } from './common';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { GitService } from '../gitService';
|
||||
import { GitService, GitStashCommit } from '../gitService';
|
||||
import { Logger } from '../logger';
|
||||
import { CommandQuickPickItem } from '../quickPicks';
|
||||
import { StashCommitNode } from '../views/stashCommitNode';
|
||||
|
||||
export interface StashDeleteCommandArgs {
|
||||
confirm?: boolean;
|
||||
@@ -21,12 +20,9 @@ export class StashDeleteCommand extends Command {
|
||||
}
|
||||
|
||||
protected async preExecute(context: CommandContext, args: StashDeleteCommandArgs = { confirm: true }) {
|
||||
if (context.type === 'view' && context.node instanceof StashCommitNode) {
|
||||
if (isCommandViewContextWithCommit<GitStashCommit>(context)) {
|
||||
args = { ...args };
|
||||
|
||||
const stash = context.node.commit;
|
||||
args.stashItem = { stashName: stash.stashName, message: stash.message };
|
||||
|
||||
args.stashItem = { stashName: context.node.commit.stashName, message: context.node.commit.message };
|
||||
return this.execute(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { FileAnnotationType } from './annotations/annotationController';
|
||||
import { Commands } from './commands';
|
||||
import { LineAnnotationType } from './currentLineController';
|
||||
import { GitExplorerView } from './views/gitExplorer';
|
||||
import { OutputLevel } from './logger';
|
||||
|
||||
export { ExtensionKey } from './constants';
|
||||
@@ -296,19 +297,10 @@ export interface IConfig {
|
||||
|
||||
defaultDateFormat: string | null;
|
||||
|
||||
fileHistoryExplorer: {
|
||||
commitFormat: string;
|
||||
// commitFileFormat: string;
|
||||
// dateFormat: string | null;
|
||||
};
|
||||
|
||||
gitExplorer: {
|
||||
view: GitExplorerView;
|
||||
commitFormat: string;
|
||||
commitFileFormat: string;
|
||||
// dateFormat: string | null;
|
||||
};
|
||||
|
||||
stashExplorer: {
|
||||
stashFormat: string;
|
||||
stashFileFormat: string;
|
||||
// dateFormat: string | null;
|
||||
|
||||
@@ -8,6 +8,8 @@ export const QualifiedExtensionId = `eamodio.${ExtensionId}`;
|
||||
|
||||
export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679';
|
||||
|
||||
export const FakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
|
||||
|
||||
export type BuiltInCommands = 'cursorMove' |
|
||||
'editor.action.showReferences' |
|
||||
'editor.action.toggleRenderWhitespace' |
|
||||
@@ -40,23 +42,25 @@ export const BuiltInCommands = {
|
||||
};
|
||||
|
||||
export type CommandContext =
|
||||
'gitlens:annotationStatus' |
|
||||
'gitlens:canToggleCodeLens' |
|
||||
'gitlens:enabled' |
|
||||
'gitlens:hasRemotes' |
|
||||
'gitlens:gitExplorer:view' |
|
||||
'gitlens:isBlameable' |
|
||||
'gitlens:isRepository' |
|
||||
'gitlens:isTracked' |
|
||||
'gitlens:key' |
|
||||
'gitlens:annotationStatus';
|
||||
'gitlens:key';
|
||||
export const CommandContext = {
|
||||
AnnotationStatus: 'gitlens:annotationStatus' as CommandContext,
|
||||
CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext,
|
||||
Enabled: 'gitlens:enabled' as CommandContext,
|
||||
GitExplorerView: 'gitlens:gitExplorer:view' as CommandContext,
|
||||
HasRemotes: 'gitlens:hasRemotes' as CommandContext,
|
||||
IsBlameable: 'gitlens:isBlameable' as CommandContext,
|
||||
IsRepository: 'gitlens:isRepository' as CommandContext,
|
||||
IsTracked: 'gitlens:isTracked' as CommandContext,
|
||||
Key: 'gitlens:key' as CommandContext,
|
||||
AnnotationStatus: 'gitlens:annotationStatus' as CommandContext
|
||||
Key: 'gitlens:key' as CommandContext
|
||||
};
|
||||
|
||||
export function setCommandContext(key: CommandContext | string, value: any) {
|
||||
@@ -77,6 +81,7 @@ export type GlyphChars = '\u21a9' |
|
||||
'\u2194' |
|
||||
'\u21e8' |
|
||||
'\u2191' |
|
||||
'\u2713' |
|
||||
'\u2014' |
|
||||
'\u2022' |
|
||||
'\u2026' |
|
||||
@@ -90,6 +95,7 @@ export const GlyphChars = {
|
||||
ArrowLeftRight: '\u2194' as GlyphChars,
|
||||
ArrowRightHollow: '\u21e8' as GlyphChars,
|
||||
ArrowUp: '\u2191' as GlyphChars,
|
||||
Check: '\u2713' as GlyphChars,
|
||||
Dash: '\u2014' as GlyphChars,
|
||||
Dot: '\u2022' as GlyphChars,
|
||||
Ellipsis: '\u2026' as GlyphChars,
|
||||
|
||||
@@ -476,11 +476,11 @@ export class CurrentLineController extends Disposable {
|
||||
break;
|
||||
case StatusBarCommand.DiffWithPrevious:
|
||||
this._statusBarItem.command = Commands.DiffLineWithPrevious;
|
||||
this._statusBarItem.tooltip = 'Compare Line Commit with Previous';
|
||||
this._statusBarItem.tooltip = 'Compare Line Revision with Previous';
|
||||
break;
|
||||
case StatusBarCommand.DiffWithWorking:
|
||||
this._statusBarItem.command = Commands.DiffLineWithWorking;
|
||||
this._statusBarItem.tooltip = 'Compare Line Commit with Working Tree';
|
||||
this._statusBarItem.tooltip = 'Compare Line Revision with Working';
|
||||
break;
|
||||
case StatusBarCommand.ToggleCodeLens:
|
||||
this._statusBarItem.tooltip = 'Toggle Git CodeLens';
|
||||
|
||||
@@ -20,9 +20,7 @@ import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensio
|
||||
import { CodeLensController } from './codeLensController';
|
||||
import { CurrentLineController, LineAnnotationType } from './currentLineController';
|
||||
import { GitContentProvider } from './gitContentProvider';
|
||||
// import { GitExplorer } from './views/gitExplorer';
|
||||
import { FileHistoryExplorer } from './views/fileHistoryExplorer';
|
||||
import { StashExplorer } from './views/stashExplorer';
|
||||
import { GitExplorer } from './views/gitExplorer';
|
||||
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
|
||||
import { GitContextTracker, GitService } from './gitService';
|
||||
import { Keyboard } from './keyboard';
|
||||
@@ -94,11 +92,7 @@ export async function activate(context: ExtensionContext) {
|
||||
|
||||
context.subscriptions.push(new Keyboard());
|
||||
|
||||
// const explorer = new GitExplorer(context, git);
|
||||
// context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', explorer));
|
||||
|
||||
context.subscriptions.push(window.registerTreeDataProvider('gitlens.fileHistoryExplorer', new FileHistoryExplorer(context, git)));
|
||||
context.subscriptions.push(window.registerTreeDataProvider('gitlens.stashExplorer', new StashExplorer(context, git)));
|
||||
context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', new GitExplorer(context, git)));
|
||||
|
||||
context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { }));
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ const GitWarnings = [
|
||||
/Not a git repository/,
|
||||
/is outside repository/,
|
||||
/no such path/,
|
||||
/does not have any commits/
|
||||
/does not have any commits/,
|
||||
/Path \'.*?\' does not exist in/,
|
||||
/Path \'.*?\' exists on disk, but not in/
|
||||
];
|
||||
|
||||
async function gitCommand(options: { cwd: string, encoding?: string }, ...args: any[]) {
|
||||
|
||||
@@ -3,12 +3,13 @@ import { commands, Range, Uri } from 'vscode';
|
||||
import { BuiltInCommands } from '../../constants';
|
||||
import { GitLogCommit } from '../../gitService';
|
||||
|
||||
export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'repo' | 'working-file';
|
||||
export type RemoteResource = { type: 'branch', branch: string } |
|
||||
export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'repo' | 'revision';
|
||||
export type RemoteResource =
|
||||
{ type: 'branch', branch: string } |
|
||||
{ type: 'commit', sha: string } |
|
||||
{ type: 'file', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string } |
|
||||
{ type: 'file', branch?: string, fileName: string, range?: Range } |
|
||||
{ type: 'repo' } |
|
||||
{ type: 'working-file', branch?: string, fileName: string, range?: Range };
|
||||
{ type: 'revision', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string };
|
||||
|
||||
export function getNameFromRemoteResource(resource: RemoteResource) {
|
||||
switch (resource.type) {
|
||||
@@ -16,7 +17,7 @@ export function getNameFromRemoteResource(resource: RemoteResource) {
|
||||
case 'commit': return 'Commit';
|
||||
case 'file': return 'File';
|
||||
case 'repo': return 'Repository';
|
||||
case 'working-file': return 'Working File';
|
||||
case 'revision': return 'Revision';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
@@ -43,16 +44,11 @@ export abstract class RemoteProvider {
|
||||
|
||||
open(resource: RemoteResource): Promise<{} | undefined> {
|
||||
switch (resource.type) {
|
||||
case 'branch':
|
||||
return this.openBranch(resource.branch);
|
||||
case 'commit':
|
||||
return this.openCommit(resource.sha);
|
||||
case 'file':
|
||||
return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range);
|
||||
case 'repo':
|
||||
return this.openRepo();
|
||||
case 'working-file':
|
||||
return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
|
||||
case 'branch': return this.openBranch(resource.branch);
|
||||
case 'commit': return this.openCommit(resource.sha);
|
||||
case 'file': return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
|
||||
case 'repo': return this.openRepo();
|
||||
case 'revision': return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
import { ExtensionContext, TextDocumentContentProvider, Uri, window } from 'vscode';
|
||||
import { CancellationToken, ExtensionContext, TextDocumentContentProvider, Uri, window } from 'vscode';
|
||||
import { DocumentSchemes } from './constants';
|
||||
import { GitService } from './gitService';
|
||||
import { Logger } from './logger';
|
||||
@@ -11,7 +11,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
|
||||
|
||||
constructor(context: ExtensionContext, private git: GitService) { }
|
||||
|
||||
async provideTextDocumentContent(uri: Uri): Promise<string> {
|
||||
async provideTextDocumentContent(uri: Uri, token: CancellationToken): Promise<string | undefined> {
|
||||
const data = GitService.fromGitContentUri(uri);
|
||||
const fileName = data.originalFileName || data.fileName;
|
||||
try {
|
||||
@@ -23,8 +23,8 @@ export class GitContentProvider implements TextDocumentContentProvider {
|
||||
}
|
||||
catch (ex) {
|
||||
Logger.error(ex, 'GitContentProvider', 'getVersionedFileText');
|
||||
await window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`);
|
||||
return '';
|
||||
window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,13 +32,12 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
|
||||
const lenses: CodeLens[] = [];
|
||||
|
||||
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, gitUri.sha, { firstIfMissing: true, previous: true });
|
||||
if (!commit) return lenses;
|
||||
|
||||
lenses.push(new GitDiffWithWorkingCodeLens(this.git, commit.uri.fsPath, commit, new Range(0, 0, 0, 1)));
|
||||
if (commit === undefined) return lenses;
|
||||
|
||||
if (commit.previousSha) {
|
||||
lenses.push(new GitDiffWithPreviousCodeLens(this.git, commit.previousUri.fsPath, commit, new Range(0, 1, 0, 2)));
|
||||
lenses.push(new GitDiffWithPreviousCodeLens(this.git, commit.previousUri.fsPath, commit, new Range(0, 0, 0, 1)));
|
||||
}
|
||||
lenses.push(new GitDiffWithWorkingCodeLens(this.git, commit.uri.fsPath, commit, new Range(0, 1, 0, 2)));
|
||||
|
||||
return lenses;
|
||||
}
|
||||
@@ -51,7 +50,7 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
|
||||
|
||||
_resolveDiffWithWorkingTreeCodeLens(lens: GitDiffWithWorkingCodeLens, token: CancellationToken): Thenable<CodeLens> {
|
||||
lens.command = {
|
||||
title: `Compare ${lens.commit.shortSha} with Working Tree`,
|
||||
title: `Compare Revision (${lens.commit.shortSha}) with Working`,
|
||||
command: Commands.DiffWithWorking,
|
||||
arguments: [
|
||||
Uri.file(lens.fileName),
|
||||
@@ -66,7 +65,7 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
|
||||
|
||||
_resolveGitDiffWithPreviousCodeLens(lens: GitDiffWithPreviousCodeLens, token: CancellationToken): Thenable<CodeLens> {
|
||||
lens.command = {
|
||||
title: `Compare ${lens.commit.shortSha} with Previous ${lens.commit.previousShortSha}`,
|
||||
title: `Compare Revision (${lens.commit.shortSha}) with Previous (${lens.commit.previousShortSha})`,
|
||||
command: Commands.DiffWithPrevious,
|
||||
arguments: [
|
||||
Uri.file(lens.fileName),
|
||||
|
||||
@@ -69,28 +69,31 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
|
||||
|
||||
export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
|
||||
|
||||
constructor(commit: GitLogCommit, item?: QuickPickItem) {
|
||||
const uris = commit.fileStatuses.map(s => (s.status === 'D')
|
||||
? GitService.toGitContentUri(commit.previousSha!, commit.previousShortSha!, s.fileName, commit.repoPath, s.originalFileName)
|
||||
: GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName));
|
||||
constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) {
|
||||
const repoPath = commit.repoPath;
|
||||
const uris = commit.fileStatuses
|
||||
.filter(s => s.status !== 'D')
|
||||
.map(s => GitUri.fromFileStatus(s, repoPath));
|
||||
|
||||
super(uris, item || {
|
||||
label: `$(file-symlink-file) Open Changed Files`,
|
||||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`
|
||||
// detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}`
|
||||
description: ''
|
||||
// detail: `Opens all of the changed file in the working tree`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenCommitWorkingTreeFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
|
||||
export class OpenCommitFileRevisionsCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
|
||||
|
||||
constructor(commit: GitLogCommit, item?: QuickPickItem) {
|
||||
const uris = commit.fileStatuses
|
||||
.filter(s => s.status !== 'D')
|
||||
.map(s => GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName));
|
||||
|
||||
constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) {
|
||||
const repoPath = commit.repoPath;
|
||||
const uris = commit.fileStatuses.filter(_ => _.status !== 'D').map(_ => GitUri.fromFileStatus(_, repoPath));
|
||||
super(uris, item || {
|
||||
label: `$(file-symlink-file) Open Changed Working Files`,
|
||||
description: ''
|
||||
// detail: `Opens all of the changed file in the working tree`
|
||||
label: `$(file-symlink-file) Open Changed Revisions`,
|
||||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`
|
||||
// detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}`
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -196,8 +199,8 @@ export class CommitDetailsQuickPick {
|
||||
} as ShowQuickCommitDetailsCommandArgs
|
||||
]));
|
||||
|
||||
items.push(new OpenCommitFilesCommandQuickPickItem(commit));
|
||||
items.push(new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit));
|
||||
items.push(new OpenCommitFilesCommandQuickPickItem(commit));
|
||||
items.push(new OpenCommitFileRevisionsCommandQuickPickItem(commit));
|
||||
|
||||
if (goBackCommand) {
|
||||
items.splice(0, 0, goBackCommand);
|
||||
|
||||
@@ -12,6 +12,17 @@ import * as path from 'path';
|
||||
|
||||
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
|
||||
|
||||
constructor(commit: GitLogCommit, item?: QuickPickItem) {
|
||||
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
|
||||
super(uri, item || {
|
||||
label: `$(file-symlink-file) Open File`,
|
||||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQuickPickItem {
|
||||
|
||||
constructor(commit: GitLogCommit, item?: QuickPickItem) {
|
||||
let description: string;
|
||||
let uri: Uri;
|
||||
@@ -24,23 +35,12 @@ export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPick
|
||||
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`;
|
||||
}
|
||||
super(uri, item || {
|
||||
label: `$(file-symlink-file) Open File`,
|
||||
label: `$(file-symlink-file) Open Revision`,
|
||||
description: description
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
|
||||
|
||||
constructor(commit: GitLogCommit, item?: QuickPickItem) {
|
||||
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
|
||||
super(uri, item || {
|
||||
label: `$(file-symlink-file) Open Working File`,
|
||||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class CommitFileDetailsQuickPick {
|
||||
|
||||
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: GitLog): Promise<CommandQuickPickItem | undefined> {
|
||||
@@ -74,7 +74,7 @@ export class CommitFileDetailsQuickPick {
|
||||
|
||||
if (commit.previousSha) {
|
||||
items.push(new CommandQuickPickItem({
|
||||
label: `$(git-compare) Compare File with Previous`,
|
||||
label: `$(git-compare) Compare File with Previous Revision`,
|
||||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousShortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}`
|
||||
}, Commands.DiffWithPrevious, [
|
||||
commit.uri,
|
||||
@@ -87,7 +87,7 @@ export class CommitFileDetailsQuickPick {
|
||||
|
||||
if (commit.workingFileName) {
|
||||
items.push(new CommandQuickPickItem({
|
||||
label: `$(git-compare) Compare File with Working Tree`,
|
||||
label: `$(git-compare) Compare File with Working Revision`,
|
||||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}`
|
||||
}, Commands.DiffWithWorking, [
|
||||
Uri.file(path.resolve(commit.repoPath, commit.workingFileName)),
|
||||
@@ -120,28 +120,28 @@ export class CommitFileDetailsQuickPick {
|
||||
]));
|
||||
}
|
||||
|
||||
items.push(new OpenCommitFileCommandQuickPickItem(commit));
|
||||
if (commit.workingFileName && commit.status !== 'D') {
|
||||
items.push(new OpenCommitWorkingTreeFileCommandQuickPickItem(commit));
|
||||
items.push(new OpenCommitFileCommandQuickPickItem(commit));
|
||||
}
|
||||
items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit));
|
||||
|
||||
const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider);
|
||||
if (remotes.length) {
|
||||
if (!stash) {
|
||||
items.push(new OpenRemotesCommandQuickPickItem(remotes, {
|
||||
type: 'file',
|
||||
fileName: commit.fileName,
|
||||
commit
|
||||
} as RemoteResource, currentCommand));
|
||||
}
|
||||
if (commit.workingFileName && commit.status !== 'D') {
|
||||
const branch = await git.getBranch(commit.repoPath || git.repoPath) as GitBranch;
|
||||
items.push(new OpenRemotesCommandQuickPickItem(remotes, {
|
||||
type: 'working-file',
|
||||
type: 'file',
|
||||
fileName: commit.workingFileName,
|
||||
branch: branch.name
|
||||
} as RemoteResource, currentCommand));
|
||||
}
|
||||
if (!stash) {
|
||||
items.push(new OpenRemotesCommandQuickPickItem(remotes, {
|
||||
type: 'revision',
|
||||
fileName: commit.fileName,
|
||||
commit
|
||||
} as RemoteResource, currentCommand));
|
||||
}
|
||||
}
|
||||
|
||||
if (commit.workingFileName) {
|
||||
|
||||
@@ -139,7 +139,7 @@ export class FileHistoryQuickPick {
|
||||
const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath!), _ => _.url, _ => !!_.provider);
|
||||
if (remotes.length) {
|
||||
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, {
|
||||
type: 'file',
|
||||
type: 'revision',
|
||||
branch: branch!.name,
|
||||
fileName: uri.getRelativePath(),
|
||||
sha: uri.sha
|
||||
|
||||
@@ -44,6 +44,14 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
description = `$(file-text) ${path.basename(resource.fileName)}`;
|
||||
break;
|
||||
|
||||
case 'repo':
|
||||
description = `$(repo) Repository`;
|
||||
break;
|
||||
|
||||
case 'revision':
|
||||
if (resource.commit !== undefined && resource.commit instanceof GitLogCommit) {
|
||||
if (resource.commit.status === 'D') {
|
||||
resource.sha = resource.commit.previousSha;
|
||||
@@ -59,14 +67,6 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
|
||||
description = `$(file-text) ${path.basename(resource.fileName)}${shortFileSha ? ` in ${GlyphChars.Space}$(git-commit) ${shortFileSha}` : ''}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'repo':
|
||||
description = `$(repo) Repository`;
|
||||
break;
|
||||
|
||||
case 'working-file':
|
||||
description = `$(file-text) ${path.basename(resource.fileName)}`;
|
||||
break;
|
||||
}
|
||||
|
||||
const remote = remotes[0];
|
||||
|
||||
@@ -4,26 +4,35 @@ import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { CommitNode } from './commitNode';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { GitBranch, GitService, GitUri } from '../gitService';
|
||||
import { GitBranch, GitRemote, GitService, GitUri } from '../gitService';
|
||||
|
||||
export class BranchHistoryNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'branch-history';
|
||||
readonly resourceType: ResourceType = 'gitlens:branch-history';
|
||||
|
||||
constructor(public readonly branch: GitBranch, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
constructor(public readonly branch: GitBranch, private readonly remote: GitRemote | undefined, uri: GitUri, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<CommitNode[]> {
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name);
|
||||
if (log === undefined) return [];
|
||||
|
||||
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
|
||||
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(`${this.branch.name}${this.branch.current ? ` ${GlyphChars.Dash} current` : ''}`, TreeItemCollapsibleState.Collapsed);
|
||||
async getTreeItem(): Promise<TreeItem> {
|
||||
const name = this.remote !== undefined
|
||||
? this.branch.name.substring(this.remote.name.length + 1)
|
||||
: this.branch.name;
|
||||
const item = new TreeItem(`${name}${this.branch!.current ? ` ${GlyphChars.Space} ${GlyphChars.Check}` : ''}`, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath('images/dark/icon-branch.svg'),
|
||||
light: this.context.asAbsolutePath('images/light/icon-branch.svg')
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,22 +7,29 @@ import { GitService, GitUri } from '../gitService';
|
||||
|
||||
export class BranchesNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'branches';
|
||||
readonly resourceType: ResourceType = 'gitlens:branches';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<BranchHistoryNode[]> {
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const branches = await this.git.getBranches(this.uri.repoPath!);
|
||||
if (branches === undefined) return [];
|
||||
|
||||
return [...Iterables.filterMap(branches.sort(_ => _.current ? 0 : 1), b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.context, this.git))];
|
||||
branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
|
||||
return [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchHistoryNode(b, undefined, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Collapsed);
|
||||
const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Expanded);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath('images/dark/icon-branch.svg'),
|
||||
light: this.context.asAbsolutePath('images/light/icon-branch.svg')
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import * as path from 'path';
|
||||
|
||||
export class CommitFileNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'commit-file';
|
||||
readonly resourceType: ResourceType = 'gitlens:commit-file';
|
||||
|
||||
constructor(public readonly status: IGitStatusFile, public commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
constructor(public readonly status: IGitStatusFile, public commit: GitCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class CommitFileNode extends ExplorerNode {
|
||||
}
|
||||
}
|
||||
|
||||
const item = new TreeItem(StatusFileFormatter.fromTemplate(this.template, this.status), TreeItemCollapsibleState.None);
|
||||
const item = new TreeItem(StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.commitFileFormat, this.status), TreeItemCollapsibleState.None);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
const icon = getGitStatusIcon(this.status.status);
|
||||
@@ -40,8 +40,18 @@ export class CommitFileNode extends ExplorerNode {
|
||||
}
|
||||
|
||||
getCommand(): Command | undefined {
|
||||
let allowMissingPrevious = false;
|
||||
let prefix = undefined;
|
||||
if (this.status.status === 'A') {
|
||||
allowMissingPrevious = true;
|
||||
prefix = 'added in ';
|
||||
}
|
||||
else if (this.status.status === 'D') {
|
||||
prefix = 'deleted in ';
|
||||
}
|
||||
|
||||
return {
|
||||
title: 'Compare File with Previous',
|
||||
title: 'Compare File with Previous Revision',
|
||||
command: Commands.DiffWithPrevious,
|
||||
arguments: [
|
||||
GitUri.fromFileStatus(this.status, this.commit.repoPath),
|
||||
@@ -51,7 +61,9 @@ export class CommitFileNode extends ExplorerNode {
|
||||
showOptions: {
|
||||
preserveFocus: true,
|
||||
preview: true
|
||||
}
|
||||
},
|
||||
allowMissingPrevious: allowMissingPrevious,
|
||||
rightTitlePrefix: prefix
|
||||
} as DiffWithPreviousCommandArgs
|
||||
]
|
||||
};
|
||||
|
||||
@@ -4,13 +4,13 @@ import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'v
|
||||
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
|
||||
import { CommitFileNode } from './commitFileNode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { CommitFormatter, GitCommit, GitService, GitUri } from '../gitService';
|
||||
import { CommitFormatter, GitLogCommit, GitService, GitUri } from '../gitService';
|
||||
|
||||
export class CommitNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'commit';
|
||||
readonly resourceType: ResourceType = 'gitlens:commit';
|
||||
|
||||
constructor(public readonly commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
constructor(public readonly commit: GitLogCommit, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(new GitUri(commit.uri, commit));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export class CommitNode extends ExplorerNode {
|
||||
const commit = Iterables.first(log.commits.values());
|
||||
if (commit === undefined) return [];
|
||||
|
||||
return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.git.config.gitExplorer.commitFileFormat, this.context, this.git))];
|
||||
return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.context, this.git))];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
@@ -31,7 +31,8 @@ export class CommitNode extends ExplorerNode {
|
||||
if (this.commit.type === 'file') {
|
||||
item.collapsibleState = TreeItemCollapsibleState.None;
|
||||
item.command = this.getCommand();
|
||||
item.contextValue = 'commit-file';
|
||||
const resourceType: ResourceType = 'gitlens:commit-file';
|
||||
item.contextValue = resourceType;
|
||||
}
|
||||
else {
|
||||
item.collapsibleState = TreeItemCollapsibleState.Collapsed;
|
||||
@@ -47,8 +48,19 @@ export class CommitNode extends ExplorerNode {
|
||||
}
|
||||
|
||||
getCommand(): Command | undefined {
|
||||
let allowMissingPrevious = false;
|
||||
let prefix = undefined;
|
||||
const status = this.commit.fileStatuses[0];
|
||||
if (status.status === 'A') {
|
||||
allowMissingPrevious = true;
|
||||
prefix = 'added in ';
|
||||
}
|
||||
else if (status.status === 'D') {
|
||||
prefix = 'deleted in ';
|
||||
}
|
||||
|
||||
return {
|
||||
title: 'Compare File with Previous',
|
||||
title: 'Compare File with Previous Revision',
|
||||
command: Commands.DiffWithPrevious,
|
||||
arguments: [
|
||||
new GitUri(this.uri, this.commit),
|
||||
@@ -58,7 +70,9 @@ export class CommitNode extends ExplorerNode {
|
||||
showOptions: {
|
||||
preserveFocus: true,
|
||||
preview: true
|
||||
}
|
||||
},
|
||||
allowMissingPrevious: allowMissingPrevious,
|
||||
rightTitlePrefix: prefix
|
||||
} as DiffWithPreviousCommandArgs
|
||||
]
|
||||
};
|
||||
|
||||
@@ -2,7 +2,22 @@
|
||||
import { Command, Event, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { GitUri } from '../gitService';
|
||||
|
||||
export declare type ResourceType = 'text' | 'status' | 'branches' | 'repository' | 'branch-history' | 'file-history' | 'stash-history' | 'commit' | 'stash-commit' | 'commit-file';
|
||||
export declare type ResourceType =
|
||||
'gitlens:branches' |
|
||||
'gitlens:branch-history' |
|
||||
'gitlens:commit' |
|
||||
'gitlens:commit-file' |
|
||||
'gitlens:file-history' |
|
||||
'gitlens:history' |
|
||||
'gitlens:message' |
|
||||
'gitlens:remote' |
|
||||
'gitlens:remotes' |
|
||||
'gitlens:repository' |
|
||||
'gitlens:stash' |
|
||||
'gitlens:stash-file' |
|
||||
'gitlens:stashes' |
|
||||
'gitlens:status' |
|
||||
'gitlens:status-upstream';
|
||||
|
||||
export abstract class ExplorerNode {
|
||||
|
||||
@@ -22,11 +37,11 @@ export abstract class ExplorerNode {
|
||||
refresh?(): void;
|
||||
}
|
||||
|
||||
export class TextExplorerNode extends ExplorerNode {
|
||||
export class MessageNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'text';
|
||||
readonly resourceType: ResourceType = 'gitlens:message';
|
||||
|
||||
constructor(private text: string) {
|
||||
constructor(private message: string) {
|
||||
super(new GitUri());
|
||||
}
|
||||
|
||||
@@ -35,7 +50,7 @@ export class TextExplorerNode extends ExplorerNode {
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
const item = new TreeItem(this.text, TreeItemCollapsibleState.None);
|
||||
const item = new TreeItem(this.message, TreeItemCollapsibleState.None);
|
||||
item.contextValue = this.resourceType;
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,12 @@ export * from './branchHistoryNode';
|
||||
export * from './commitFileNode';
|
||||
export * from './commitNode';
|
||||
export * from './fileHistoryNode';
|
||||
export * from './historyNode';
|
||||
export * from './remoteNode';
|
||||
export * from './remotesNode';
|
||||
export * from './repositoryNode';
|
||||
export * from './stashCommitNode';
|
||||
export * from './stashFileNode';
|
||||
export * from './stashNode';
|
||||
export * from './statusNode';
|
||||
export * from './stashesNode';
|
||||
export * from './statusNode';
|
||||
export * from './statusUpstreamNode';
|
||||
@@ -1,89 +0,0 @@
|
||||
'use strict';
|
||||
// import { Arrays } from '../system';
|
||||
import { commands, Event, EventEmitter, ExtensionContext, TextEditor, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
|
||||
import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
|
||||
import { UriComparer } from '../comparers';
|
||||
import { CommitNode, ExplorerNode, FileHistoryNode, TextExplorerNode } from './explorerNodes';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
|
||||
export * from './explorerNodes';
|
||||
|
||||
export class FileHistoryExplorer implements TreeDataProvider<ExplorerNode> {
|
||||
|
||||
private _node?: ExplorerNode;
|
||||
|
||||
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
|
||||
public get onDidChangeTreeData(): Event<ExplorerNode> {
|
||||
return this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
constructor(private context: ExtensionContext, private git: GitService) {
|
||||
commands.registerCommand('gitlens.fileHistoryExplorer.refresh', this.refresh, this);
|
||||
commands.registerCommand('gitlens.fileHistoryExplorer.openChanges', this.openChanges, this);
|
||||
commands.registerCommand('gitlens.fileHistoryExplorer.openFile', this.openFile, this);
|
||||
commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevision', this.openFileRevision, this);
|
||||
commands.registerCommand('gitlens.fileHistoryExplorer.openFileInRemote', this.openFileInRemote, this);
|
||||
commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevisionInRemote', this.openFileRevisionInRemote, this);
|
||||
|
||||
context.subscriptions.push(window.onDidChangeActiveTextEditor(this.onActiveEditorChanged, this));
|
||||
|
||||
this._node = this.getRootNode(window.activeTextEditor);
|
||||
}
|
||||
|
||||
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
|
||||
return node.getTreeItem();
|
||||
}
|
||||
|
||||
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
|
||||
if (this._node === undefined) return [new TextExplorerNode('No active file')];
|
||||
if (node === undefined) return this._node.getChildren();
|
||||
return node.getChildren();
|
||||
}
|
||||
|
||||
private getRootNode(editor: TextEditor | undefined): ExplorerNode | undefined {
|
||||
if (window.visibleTextEditors.length === 0) return undefined;
|
||||
if (editor === undefined) return this._node;
|
||||
|
||||
const uri = this.git.getGitUriForFile(editor.document.uri) || new GitUri(editor.document.uri, { repoPath: this.git.repoPath, fileName: editor.document.uri.fsPath });
|
||||
if (UriComparer.equals(uri, this._node && this._node.uri)) return this._node;
|
||||
|
||||
return new FileHistoryNode(uri, this.context, this.git);
|
||||
}
|
||||
|
||||
private onActiveEditorChanged(editor: TextEditor | undefined) {
|
||||
const node = this.getRootNode(editor);
|
||||
if (node === this._node) return;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh(node?: ExplorerNode) {
|
||||
this._node = node || this.getRootNode(window.activeTextEditor);
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
private openChanges(node: CommitNode) {
|
||||
const command = node.getCommand();
|
||||
if (command === undefined || command.arguments === undefined) return;
|
||||
|
||||
const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
|
||||
args.showOptions!.preview = false;
|
||||
return commands.executeCommand(command.command, uri, args);
|
||||
}
|
||||
|
||||
private openFile(node: CommitNode) {
|
||||
return openEditor(node.uri, { preserveFocus: true, preview: false });
|
||||
}
|
||||
|
||||
private openFileRevision(node: CommitNode) {
|
||||
return openEditor(GitService.toGitContentUri(node.uri), { preserveFocus: true, preview: false });
|
||||
}
|
||||
|
||||
private async openFileInRemote(node: CommitNode) {
|
||||
return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
|
||||
}
|
||||
|
||||
private async openFileRevisionInRemote(node: CommitNode) {
|
||||
return commands.executeCommand(Commands.OpenFileInRemote, new GitUri(node.commit.uri, node.commit), { range: false } as OpenFileInRemoteCommandArgs);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,12 @@
|
||||
import { Iterables } from '../system';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { CommitNode } from './commitNode';
|
||||
import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode';
|
||||
import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
|
||||
export class FileHistoryNode extends ExplorerNode {
|
||||
|
||||
static readonly rootType: ResourceType = 'file-history';
|
||||
readonly resourceType: ResourceType = 'file-history';
|
||||
readonly resourceType: ResourceType = 'gitlens:file-history';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
@@ -16,14 +15,20 @@ export class FileHistoryNode extends ExplorerNode {
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha);
|
||||
if (log === undefined) return [new TextExplorerNode('No file history')];
|
||||
if (log === undefined) return [new MessageNode('No file history')];
|
||||
|
||||
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.fileHistoryExplorer.commitFormat, this.context, this.git))];
|
||||
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(`History of ${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
|
||||
const item = new TreeItem(`${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath('images/dark/icon-history.svg'),
|
||||
light: this.context.asAbsolutePath('images/light/icon-history.svg')
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,162 @@
|
||||
'use strict';
|
||||
// import { Functions } from '../system';
|
||||
import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode';
|
||||
import { Functions } from '../system';
|
||||
import { commands, Event, EventEmitter, ExtensionContext, TextDocumentShowOptions, TextEditor, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
|
||||
import { Commands, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
|
||||
import { UriComparer } from '../comparers';
|
||||
import { ExplorerNode, FileHistoryNode, RepositoryNode, ResourceType, StashNode } from './explorerNodes';
|
||||
import { CommandContext, setCommandContext } from '../constants';
|
||||
import { CommitFileNode, CommitNode, ExplorerNode, HistoryNode, MessageNode, RepositoryNode, StashNode } from './explorerNodes';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
|
||||
export * from './explorerNodes';
|
||||
|
||||
export type GitExplorerView =
|
||||
'history' |
|
||||
'repository';
|
||||
export const GitExplorerView = {
|
||||
History: 'history' as GitExplorerView,
|
||||
Repository: 'repository' as GitExplorerView
|
||||
};
|
||||
|
||||
export interface OpenFileRevisionCommandArgs {
|
||||
uri?: Uri;
|
||||
showOptions?: TextDocumentShowOptions;
|
||||
}
|
||||
|
||||
export class GitExplorer implements TreeDataProvider<ExplorerNode> {
|
||||
|
||||
// private _refreshDebounced: () => void;
|
||||
private _root?: ExplorerNode;
|
||||
private _view: GitExplorerView = GitExplorerView.Repository;
|
||||
|
||||
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
|
||||
public get onDidChangeTreeData(): Event<ExplorerNode> {
|
||||
return this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
private _roots: ExplorerNode[] = [];
|
||||
constructor(private readonly context: ExtensionContext, private readonly git: GitService) {
|
||||
commands.registerCommand('gitlens.gitExplorer.switchToHistoryView', () => this.switchTo(GitExplorerView.History), this);
|
||||
commands.registerCommand('gitlens.gitExplorer.switchToRepositoryView', () => this.switchTo(GitExplorerView.Repository), this);
|
||||
commands.registerCommand('gitlens.gitExplorer.refresh', this.refresh, this);
|
||||
commands.registerCommand('gitlens.gitExplorer.openChanges', this.openChanges, this);
|
||||
commands.registerCommand('gitlens.gitExplorer.openChangesWithWorking', this.openChangesWithWorking, this);
|
||||
commands.registerCommand('gitlens.gitExplorer.openFile', this.openFile, this);
|
||||
commands.registerCommand('gitlens.gitExplorer.openFileRevision', this.openFileRevision, this);
|
||||
commands.registerCommand('gitlens.gitExplorer.openFileRevisionInRemote', this.openFileRevisionInRemote, this);
|
||||
commands.registerCommand('gitlens.gitExplorer.openChangedFiles', this.openChangedFiles, this);
|
||||
commands.registerCommand('gitlens.gitExplorer.openChangedFileRevisions', this.openChangedFileRevisions, this);
|
||||
|
||||
constructor(private context: ExtensionContext, private git: GitService) {
|
||||
commands.registerCommand('gitlens.gitExplorer.refresh', () => this.refresh());
|
||||
const fn = Functions.debounce(this.onActiveEditorChanged, 500);
|
||||
context.subscriptions.push(window.onDidChangeActiveTextEditor(fn, this));
|
||||
|
||||
// this._refreshDebounced = Functions.debounce(this.refresh.bind(this), 250);
|
||||
|
||||
// const editor = window.activeTextEditor;
|
||||
|
||||
// const uri = (editor !== undefined && editor.document !== undefined)
|
||||
// ? new GitUri(editor.document.uri, { repoPath: git.repoPath, fileName: editor.document.uri.fsPath })
|
||||
// : new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
|
||||
|
||||
const uri = new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
|
||||
this._roots.push(new RepositoryNode(uri, context, git));
|
||||
this._view = this.git.config.gitExplorer.view;
|
||||
setCommandContext(CommandContext.GitExplorerView, this._view);
|
||||
this._root = this.getRootNode();
|
||||
}
|
||||
|
||||
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
|
||||
// if (node.onDidChangeTreeData !== undefined) {
|
||||
// node.onDidChangeTreeData(() => setTimeout(this._refreshDebounced, 1));
|
||||
// }
|
||||
return node.getTreeItem();
|
||||
}
|
||||
|
||||
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
|
||||
if (this._roots.length === 0) return [];
|
||||
if (node === undefined) return this._roots;
|
||||
if (this._root === undefined) {
|
||||
if (this._view === GitExplorerView.History) return [new MessageNode('No active file; no history to show')];
|
||||
return [];
|
||||
}
|
||||
|
||||
if (node === undefined) return this._root.getChildren();
|
||||
return node.getChildren();
|
||||
}
|
||||
|
||||
addHistory(uri: GitUri) {
|
||||
this._add(uri, FileHistoryNode);
|
||||
}
|
||||
private getRootNode(editor?: TextEditor): ExplorerNode | undefined {
|
||||
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
|
||||
|
||||
addStash(uri: GitUri) {
|
||||
this._add(uri, StashNode);
|
||||
}
|
||||
|
||||
private _add<T extends ExplorerNode>(uri: GitUri, type: { new (uri: GitUri, context: ExtensionContext, git: GitService): T, rootType: ResourceType }) {
|
||||
if (!this._roots.some(_ => _.resourceType === type.rootType && UriComparer.equals(uri, _.uri))) {
|
||||
this._roots.push(new type(uri, this.context, this.git));
|
||||
switch (this._view) {
|
||||
case GitExplorerView.History: return this.getHistoryNode(editor || window.activeTextEditor);
|
||||
case GitExplorerView.Repository: return new RepositoryNode(uri, this.context, this.git);
|
||||
}
|
||||
this.refresh();
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._roots = [];
|
||||
this.refresh();
|
||||
private getHistoryNode(editor: TextEditor | undefined): ExplorerNode | undefined {
|
||||
if (window.visibleTextEditors.length === 0) return undefined;
|
||||
if (editor === undefined) return this._root;
|
||||
|
||||
const uri = this.git.getGitUriForFile(editor.document.uri) || new GitUri(editor.document.uri, { repoPath: this.git.repoPath, fileName: editor.document.uri.fsPath });
|
||||
if (UriComparer.equals(uri, this._root && this._root.uri)) return this._root;
|
||||
|
||||
return new HistoryNode(uri, this.context, this.git);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
private onActiveEditorChanged(editor: TextEditor | undefined) {
|
||||
if (this._view !== GitExplorerView.History) return;
|
||||
const root = this.getRootNode(editor);
|
||||
if (root === this._root) return;
|
||||
|
||||
this.refresh(root);
|
||||
}
|
||||
|
||||
refresh(root?: ExplorerNode) {
|
||||
this._root = root || this.getRootNode();
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
switchTo(view: GitExplorerView) {
|
||||
if (this._view === view) return;
|
||||
|
||||
this._view = view;
|
||||
setCommandContext(CommandContext.GitExplorerView, this._view);
|
||||
|
||||
this._root = undefined;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
private openChanges(node: CommitNode | StashNode) {
|
||||
const command = node.getCommand();
|
||||
if (command === undefined || command.arguments === undefined) return;
|
||||
|
||||
const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
|
||||
args.showOptions!.preview = false;
|
||||
return commands.executeCommand(command.command, uri, args);
|
||||
}
|
||||
|
||||
private openChangesWithWorking(node: CommitNode | StashNode) {
|
||||
const args: DiffWithWorkingCommandArgs = {
|
||||
commit: node.commit,
|
||||
showOptions: {
|
||||
preserveFocus: true,
|
||||
preview: false
|
||||
|
||||
}
|
||||
};
|
||||
return commands.executeCommand(Commands.DiffWithWorking, new GitUri(node.commit.uri, node.commit), args);
|
||||
}
|
||||
|
||||
private openFile(node: CommitNode | StashNode) {
|
||||
return openEditor(node.uri, { preserveFocus: true, preview: false });
|
||||
}
|
||||
|
||||
private openFileRevision(node: CommitNode | StashNode | CommitFileNode, options: OpenFileRevisionCommandArgs = { showOptions: { preserveFocus: true, preview: false } }) {
|
||||
return openEditor(options.uri || GitService.toGitContentUri(node.uri), options.showOptions || { preserveFocus: true, preview: false });
|
||||
}
|
||||
|
||||
private async openChangedFiles(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
|
||||
const repoPath = node.commit.repoPath;
|
||||
const uris = node.commit.fileStatuses.filter(s => s.status !== 'D').map(s => GitUri.fromFileStatus(s, repoPath));
|
||||
for (const uri of uris) {
|
||||
await openEditor(uri, options);
|
||||
}
|
||||
}
|
||||
|
||||
private async openChangedFileRevisions(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
|
||||
const uris = node.commit.fileStatuses
|
||||
.filter(s => s.status !== 'D')
|
||||
.map(s => GitService.toGitContentUri(node.commit.sha, node.commit.shortSha, s.fileName, node.commit.repoPath, s.originalFileName));
|
||||
for (const uri of uris) {
|
||||
await openEditor(uri, options);
|
||||
}
|
||||
}
|
||||
|
||||
private async openFileRevisionInRemote(node: CommitNode | StashNode) {
|
||||
return commands.executeCommand(Commands.OpenFileInRemote, new GitUri(node.commit.uri, node.commit), { range: false } as OpenFileInRemoteCommandArgs);
|
||||
}
|
||||
}
|
||||
30
src/views/historyNode.ts
Normal file
30
src/views/historyNode.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { FileHistoryNode } from './fileHistoryNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
|
||||
export class HistoryNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'gitlens:history';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
return [new FileHistoryNode(this.uri, this.context, this.git)];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(`${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath('images/dark/icon-history.svg'),
|
||||
light: this.context.asAbsolutePath('images/light/icon-history.svg')
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
35
src/views/remoteNode.ts
Normal file
35
src/views/remoteNode.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
import { Iterables } from '../system';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { BranchHistoryNode } from './branchHistoryNode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { GitRemote, GitService, GitUri } from '../gitService';
|
||||
|
||||
export class RemoteNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'gitlens:remote';
|
||||
|
||||
constructor(public readonly remote: GitRemote, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const branches = await this.git.getBranches(this.uri.repoPath!);
|
||||
if (branches === undefined) return [];
|
||||
|
||||
branches.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return [...Iterables.filterMap(branches, b => !b.remote || !b.name.startsWith(this.remote.name) ? undefined : new BranchHistoryNode(b, this.remote, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(this.remote.name, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
// item.iconPath = {
|
||||
// dark: this.context.asAbsolutePath('images/dark/icon-remote.svg'),
|
||||
// light: this.context.asAbsolutePath('images/light/icon-remote.svg')
|
||||
// };
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
35
src/views/remotesNode.ts
Normal file
35
src/views/remotesNode.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
import { Arrays, Iterables } from '../system';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { RemoteNode } from './remoteNode';
|
||||
|
||||
export class RemotesNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'gitlens:remotes';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const remotes = Arrays.uniqueBy(await this.git.getRemotes(this.uri.repoPath!), r => r.url, r => !!r.provider);
|
||||
if (remotes === undefined) return [];
|
||||
|
||||
remotes.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return [...Iterables.map(remotes, r => new RemoteNode(r, this.uri, this.context, this.git))];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(`Remotes`, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath('images/dark/icon-remote.svg'),
|
||||
light: this.context.asAbsolutePath('images/light/icon-remote.svg')
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,13 @@ import { BranchesNode } from './branchesNode';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
// import { StatusNode } from './statusNode';
|
||||
import { RemotesNode } from './remotesNode';
|
||||
import { StatusNode } from './statusNode';
|
||||
import { StashesNode } from './stashesNode';
|
||||
|
||||
export class RepositoryNode extends ExplorerNode {
|
||||
|
||||
static readonly rootType: ResourceType = 'repository';
|
||||
readonly resourceType: ResourceType = 'repository';
|
||||
readonly resourceType: ResourceType = 'gitlens:repository';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
@@ -17,8 +18,10 @@ export class RepositoryNode extends ExplorerNode {
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
return [
|
||||
// new StatusNode(this.uri, this.context, this.git),
|
||||
new BranchesNode(this.uri, this.context, this.git)
|
||||
new StatusNode(this.uri, this.context, this.git),
|
||||
new BranchesNode(this.uri, this.context, this.git),
|
||||
new RemotesNode(this.uri, this.context, this.git),
|
||||
new StashesNode(this.uri, this.context, this.git)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
'use strict';
|
||||
import { Event, EventEmitter, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { CommitFileNode } from './commitFileNode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
|
||||
|
||||
export class StashCommitNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'stash-commit';
|
||||
|
||||
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
|
||||
public get onDidChangeTreeData(): Event<ExplorerNode> {
|
||||
return this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(new GitUri(commit.uri, commit));
|
||||
}
|
||||
|
||||
async getChildren(): Promise<CommitFileNode[]> {
|
||||
return Promise.resolve((this.commit as GitStashCommit).fileStatuses.map(_ => new CommitFileNode(_, this.commit, this.git.config.stashExplorer.stashFileFormat, this.context, this.git)));
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const label = CommitFormatter.fromTemplate(this.git.config.stashExplorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
|
||||
|
||||
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = this.resourceType;
|
||||
// item.command = {
|
||||
// title: 'Show Stash Details',
|
||||
// command: Commands.ShowQuickCommitDetails,
|
||||
// arguments: [
|
||||
// new GitUri(commit.uri, commit),
|
||||
// {
|
||||
// commit: this.commit,
|
||||
// sha: this.commit.sha
|
||||
// } as ShowQuickCommitDetailsCommandArgs
|
||||
// ]
|
||||
// };
|
||||
return item;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
'use strict';
|
||||
// import { Functions } from '../system';
|
||||
import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode';
|
||||
import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
|
||||
import { ExplorerNode, StashCommitNode, StashNode } from './explorerNodes';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
|
||||
export * from './explorerNodes';
|
||||
|
||||
export class StashExplorer implements TreeDataProvider<ExplorerNode> {
|
||||
|
||||
private _node: ExplorerNode;
|
||||
|
||||
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
|
||||
public get onDidChangeTreeData(): Event<ExplorerNode> {
|
||||
return this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
constructor(private context: ExtensionContext, private git: GitService) {
|
||||
commands.registerCommand('gitlens.stashExplorer.refresh', this.refresh, this);
|
||||
commands.registerCommand('gitlens.stashExplorer.openChanges', this.openChanges, this);
|
||||
commands.registerCommand('gitlens.stashExplorer.openFile', this.openFile, this);
|
||||
commands.registerCommand('gitlens.stashExplorer.openStashedFile', this.openStashedFile, this);
|
||||
commands.registerCommand('gitlens.stashExplorer.openFileInRemote', this.openFileInRemote, this);
|
||||
|
||||
context.subscriptions.push(this.git.onDidChangeRepo(this.onRepoChanged, this));
|
||||
|
||||
this._node = this.getRootNode();
|
||||
}
|
||||
|
||||
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
|
||||
return node.getTreeItem();
|
||||
}
|
||||
|
||||
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
|
||||
if (node === undefined) return this._node.getChildren();
|
||||
return node.getChildren();
|
||||
}
|
||||
|
||||
private getRootNode(): ExplorerNode {
|
||||
const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
|
||||
return new StashNode(uri, this.context, this.git);
|
||||
}
|
||||
|
||||
private onRepoChanged(reasons: ('stash' | 'unknown')[]) {
|
||||
if (!reasons.includes('stash')) return;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
private openChanges(node: StashCommitNode) {
|
||||
const command = node.getCommand();
|
||||
if (command === undefined || command.arguments === undefined) return;
|
||||
|
||||
const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
|
||||
args.showOptions!.preview = false;
|
||||
return commands.executeCommand(command.command, uri, args);
|
||||
}
|
||||
|
||||
private openFile(node: StashCommitNode) {
|
||||
return openEditor(node.uri, { preserveFocus: true, preview: false });
|
||||
}
|
||||
|
||||
private openStashedFile(node: StashCommitNode) {
|
||||
return openEditor(GitService.toGitContentUri(node.uri), { preserveFocus: true, preview: false });
|
||||
}
|
||||
|
||||
private openFileInRemote(node: StashCommitNode) {
|
||||
return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
|
||||
}
|
||||
}
|
||||
14
src/views/stashFileNode.ts
Normal file
14
src/views/stashFileNode.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
import { ExtensionContext } from 'vscode';
|
||||
import { ResourceType } from './explorerNode';
|
||||
import { GitService, GitStashCommit, IGitStatusFile } from '../gitService';
|
||||
import { CommitFileNode } from './commitFileNode';
|
||||
|
||||
export class StashFileNode extends CommitFileNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'gitlens:stash-file';
|
||||
|
||||
constructor(readonly status: IGitStatusFile, readonly commit: GitStashCommit, readonly context: ExtensionContext, readonly git: GitService) {
|
||||
super(status, commit, context, git);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,35 @@
|
||||
'use strict';
|
||||
import { Iterables } from '../system';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { StashCommitNode } from './stashCommitNode';
|
||||
import { Event, EventEmitter, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
|
||||
import { StashFileNode } from './stashFileNode';
|
||||
|
||||
export class StashNode extends ExplorerNode {
|
||||
|
||||
static readonly rootType: ResourceType = 'stash-history';
|
||||
readonly resourceType: ResourceType = 'stash-history';
|
||||
readonly resourceType: ResourceType = 'gitlens:stash';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
|
||||
public get onDidChangeTreeData(): Event<ExplorerNode> {
|
||||
return this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(new GitUri(commit.uri, commit));
|
||||
}
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const stash = await this.git.getStashList(this.uri.repoPath!);
|
||||
if (stash === undefined) return [new TextExplorerNode('No stashed changes')];
|
||||
|
||||
return [...Iterables.map(stash.commits.values(), c => new StashCommitNode(c, this.context, this.git))];
|
||||
return Promise.resolve((this.commit as GitStashCommit).fileStatuses.map(s => new StashFileNode(s, this.commit, this.context, this.git)));
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(`Stashed Changes`, TreeItemCollapsibleState.Collapsed);
|
||||
const label = CommitFormatter.fromTemplate(this.git.config.gitExplorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
|
||||
|
||||
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = this.resourceType;
|
||||
return item;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
}
|
||||
34
src/views/stashesNode.ts
Normal file
34
src/views/stashesNode.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
import { Iterables } from '../system';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { StashNode } from './stashNode';
|
||||
|
||||
export class StashesNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'gitlens:stashes';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const stash = await this.git.getStashList(this.uri.repoPath!);
|
||||
if (stash === undefined) return [new MessageNode('No stashed changes')];
|
||||
|
||||
return [...Iterables.map(stash.commits.values(), c => new StashNode(c, this.context, this.git))];
|
||||
}
|
||||
|
||||
getTreeItem(): TreeItem {
|
||||
const item = new TreeItem(`Stashes`, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath('images/dark/icon-stash.svg'),
|
||||
light: this.context.asAbsolutePath('images/light/icon-stash.svg')
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,60 @@
|
||||
import { Strings } from '../system';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { GlyphChars } from '../constants';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { GitService, GitUri } from '../gitService';
|
||||
import { StatusUpstreamNode } from './statusUpstreamNode';
|
||||
|
||||
export class StatusNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'status';
|
||||
readonly resourceType: ResourceType = 'gitlens:status';
|
||||
|
||||
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(uri);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
return [];
|
||||
// const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||
// if (status === undefined) return [];
|
||||
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||
if (status === undefined) return [];
|
||||
|
||||
// return [...Iterables.map(status.files, b => new CommitFile(b, this.uri, this.context, this.git))];
|
||||
const children = [];
|
||||
|
||||
if (status.state.behind) {
|
||||
children.push(new StatusUpstreamNode(status, 'behind', this.git.config.gitExplorer.commitFormat, this.context, this.git));
|
||||
}
|
||||
|
||||
if (status.state.ahead) {
|
||||
children.push(new StatusUpstreamNode(status, 'ahead', this.git.config.gitExplorer.commitFormat, this.context, this.git));
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
async getTreeItem(): Promise<TreeItem> {
|
||||
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
|
||||
let suffix = '';
|
||||
if (status !== undefined) {
|
||||
suffix = ` ${GlyphChars.Dash} ${GlyphChars.ArrowUp} ${status.state.ahead} ${GlyphChars.ArrowDown} ${status.state.behind} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${status.branch} ${GlyphChars.ArrowLeftRight} ${status.upstream}`;
|
||||
if (status === undefined) return new TreeItem('No repo status');
|
||||
|
||||
let hasChildren = false;
|
||||
let label = '';
|
||||
if (status.upstream) {
|
||||
if (!status.state.ahead && !status.state.behind) {
|
||||
label = `${status.branch} is up-to-date with ${status.upstream}`;
|
||||
}
|
||||
else {
|
||||
label = `${status.branch} is not up-to-date with ${status.upstream}`;
|
||||
hasChildren = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
label = `${status.branch} is up-to-date`;
|
||||
}
|
||||
|
||||
const item = new TreeItem(`Status${suffix}`, TreeItemCollapsibleState.Collapsed);
|
||||
const item = new TreeItem(label, hasChildren ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath('images/dark/icon-repo.svg'),
|
||||
light: this.context.asAbsolutePath('images/light/icon-repo.svg')
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
53
src/views/statusUpstreamNode.ts
Normal file
53
src/views/statusUpstreamNode.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
import { Iterables } from '../system';
|
||||
import { ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
|
||||
import { ExplorerNode, ResourceType } from './explorerNode';
|
||||
import { GitService, GitStatus, GitUri } from '../gitService';
|
||||
import { CommitNode } from './commitNode';
|
||||
|
||||
export class StatusUpstreamNode extends ExplorerNode {
|
||||
|
||||
readonly resourceType: ResourceType = 'gitlens:status-upstream';
|
||||
|
||||
constructor(public readonly status: GitStatus, public readonly direction: 'ahead' | 'behind', private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
|
||||
super(new GitUri(Uri.file(status.repoPath), { repoPath: status.repoPath, fileName: status.repoPath }));
|
||||
}
|
||||
|
||||
async getChildren(): Promise<ExplorerNode[]> {
|
||||
const range = this.direction === 'ahead'
|
||||
? `${this.status.upstream}..${this.status.branch}`
|
||||
: `${this.status.branch}..${this.status.upstream}`;
|
||||
let log = await this.git.getLogForRepo(this.uri.repoPath!, range);
|
||||
if (log === undefined) return [];
|
||||
|
||||
if (this.direction !== 'ahead') return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];
|
||||
|
||||
// Since the last commit when we are looking 'ahead' can have no previous (because of the range given) -- look it up
|
||||
const commits = Array.from(log.commits.values());
|
||||
const commit = commits[commits.length - 1];
|
||||
if (commit.previousSha === undefined) {
|
||||
log = await this.git.getLogForRepo(this.uri.repoPath!, commit.sha, 2);
|
||||
if (log !== undefined) {
|
||||
commits[commits.length - 1] = Iterables.first(log.commits.values());
|
||||
}
|
||||
}
|
||||
|
||||
return [...Iterables.map(commits, c => new CommitNode(c, this.template, this.context, this.git))];
|
||||
}
|
||||
|
||||
async getTreeItem(): Promise<TreeItem> {
|
||||
const label = this.direction === 'ahead'
|
||||
? `${this.status.state.ahead} commit${this.status.state.ahead > 1 ? 's' : ''} ahead` // of ${this.status.upstream}`
|
||||
: `${this.status.state.behind} commit${this.status.state.behind > 1 ? 's' : ''} behind`; // ${this.status.upstream}`;
|
||||
|
||||
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = this.resourceType;
|
||||
|
||||
item.iconPath = {
|
||||
dark: this.context.asAbsolutePath(`images/dark/icon-${this.direction === 'ahead' ? 'upload' : 'download'}.svg`),
|
||||
light: this.context.asAbsolutePath(`images/light/icon-${this.direction === 'ahead' ? 'upload' : 'download'}.svg`)
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user