Refactors commands to use typed args objects

This commit is contained in:
Eric Amodio
2017-05-14 01:48:07 -04:00
parent ee29596d45
commit 1acc183621
43 changed files with 2366 additions and 1761 deletions

View File

@@ -1,31 +1,33 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorTracker } from '../activeEditorTracker';
import { ActiveEditorCommand, Commands } from './common';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { TextEditorComparer, UriComparer } from '../comparers';
import { GitService } from '../gitService';
import { Logger } from '../logger';
export interface CloseUnchangedFilesCommandArgs {
uris?: Uri[];
}
export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.CloseUnchangedFiles);
}
async execute(editor: TextEditor, uri?: Uri, uris?: Uri[]) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: CloseUnchangedFilesCommandArgs = {}) {
uri = getCommandUri(uri, editor);
try {
if (!uris) {
if (args.uris === undefined) {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return window.showWarningMessage(`Unable to close unchanged files`);
if (repoPath === undefined) return window.showWarningMessage(`Unable to close unchanged files`);
const status = await this.git.getStatusForRepo(repoPath);
if (!status) return window.showWarningMessage(`Unable to close unchanged files`);
if (status === undefined) return window.showWarningMessage(`Unable to close unchanged files`);
uris = status.files.map(_ => _.Uri);
args.uris = status.files.map(_ => _.Uri);
}
const editorTracker = new ActiveEditorTracker();
@@ -35,7 +37,7 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
do {
if (editor !== undefined) {
if ((editor.document !== undefined && editor.document.isDirty) ||
uris.some(_ => UriComparer.equals(_, editor!.document && editor!.document.uri))) {
args.uris.some(_ => UriComparer.equals(_, editor!.document && editor!.document.uri))) {
// If we didn't start with a valid editor, set one once we find it
if (active === undefined) {
active = editor;

View File

@@ -1,5 +1,5 @@
'use strict';
import { commands, Disposable, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { commands, Disposable, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { BuiltInCommands } from '../constants';
import { Logger } from '../logger';
import { Telemetry } from '../telemetry';
@@ -48,6 +48,12 @@ export const Commands = {
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
};
export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined {
if (uri instanceof Uri) return uri;
if (editor === undefined || editor.document === undefined) return undefined;
return editor.document.uri;
}
export type CommandContext = 'gitlens:canToggleCodeLens' | 'gitlens:enabled' | 'gitlens:hasRemotes' | 'gitlens:isBlameable' | 'gitlens:isRepository' | 'gitlens:isTracked' | 'gitlens:key';
export const CommandContext = {
CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext,
@@ -140,12 +146,16 @@ export abstract class ActiveEditorCachedCommand extends ActiveEditorCommand {
abstract execute(editor: TextEditor, ...args: any[]): any;
}
export async function openEditor(uri: Uri, pinned: boolean = false) {
export async function openEditor(uri: Uri, options?: TextDocumentShowOptions): Promise<TextEditor | undefined> {
try {
if (!pinned) return await commands.executeCommand(BuiltInCommands.Open, uri);
const defaults: TextDocumentShowOptions = {
preserveFocus: false,
preview: true,
viewColumn: (window.activeTextEditor && window.activeTextEditor.viewColumn) || 1
};
const document = await workspace.openTextDocument(uri);
return window.showTextDocument(document, (window.activeTextEditor && window.activeTextEditor.viewColumn) || 1, true);
return window.showTextDocument(document, { ...defaults, ...(options || {}) });
}
catch (ex) {
Logger.error(ex, 'openEditor');

View File

@@ -1,40 +1,43 @@
'use strict';
import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { copy } from 'copy-paste';
export interface CopyMessageToClipboardCommandArgs {
message?: string;
sha?: string;
}
export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.CopyMessageToClipboard);
}
async execute(editor: TextEditor, uri?: Uri, sha?: string, message?: string): Promise<any> {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: CopyMessageToClipboardCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
try {
// If we don't have an editor then get the message of the last commit to the branch
if (!uri) {
if (uri === undefined) {
if (!this.git.repoPath) return undefined;
const log = await this.git.getLogForRepo(this.git.repoPath, undefined, 1);
if (!log) return undefined;
message = Iterables.first(log.commits.values()).message;
copy(message);
args.message = Iterables.first(log.commits.values()).message;
copy(args.message);
return undefined;
}
const gitUri = await GitUri.fromUri(uri, this.git);
if (!message) {
if (!sha) {
if (editor && editor.document && editor.document.isDirty) return undefined;
if (args.message === undefined) {
if (args.sha === undefined) {
if (editor !== undefined && editor.document !== undefined && editor.document.isDirty) return undefined;
const line = (editor && editor.selection.active.line) || gitUri.offset;
const blameline = line - gitUri.offset;
@@ -46,7 +49,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
if (blame.commit.isUncommitted) return undefined;
sha = blame.commit.sha;
args.sha = blame.commit.sha;
if (!gitUri.repoPath) {
gitUri.repoPath = blame.commit.repoPath;
}
@@ -58,13 +61,13 @@ 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, sha);
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, args.sha);
if (!commit) return undefined;
message = commit.message;
args.message = commit.message;
}
copy(message);
copy(args.message);
return undefined;
}
catch (ex) {

View File

@@ -1,39 +1,41 @@
'use strict';
import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { copy } from 'copy-paste';
export interface CopyShaToClipboardCommandArgs {
sha?: string;
}
export class CopyShaToClipboardCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.CopyShaToClipboard);
}
async execute(editor: TextEditor, uri?: Uri, sha?: string): Promise<any> {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: CopyShaToClipboardCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
try {
// If we don't have an editor then get the sha of the last commit to the branch
if (!uri) {
if (uri === undefined) {
if (!this.git.repoPath) return undefined;
const log = await this.git.getLogForRepo(this.git.repoPath, undefined, 1);
if (!log) return undefined;
sha = Iterables.first(log.commits.values()).sha;
copy(sha);
args.sha = Iterables.first(log.commits.values()).sha;
copy(args.sha);
return undefined;
}
const gitUri = await GitUri.fromUri(uri, this.git);
if (!sha) {
if (editor && editor.document && editor.document.isDirty) return undefined;
if (args.sha === undefined) {
if (editor !== undefined && editor.document !== undefined && editor.document.isDirty) return undefined;
const line = (editor && editor.selection.active.line) || gitUri.offset;
const blameline = line - gitUri.offset;
@@ -43,7 +45,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (!blame) return undefined;
sha = blame.commit.sha;
args.sha = blame.commit.sha;
}
catch (ex) {
Logger.error(ex, 'CopyShaToClipboardCommand', `getBlameForLine(${blameline})`);
@@ -51,7 +53,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
}
}
copy(sha);
copy(args.sha);
return undefined;
}
catch (ex) {

View File

@@ -1,51 +1,53 @@
'use strict';
import { Iterables } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, BranchesQuickPick } from '../quickPicks';
export interface DiffDirectoryCommandCommandArgs {
shaOrBranch1?: string;
shaOrBranch2?: string;
}
export class DiffDirectoryCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.DiffDirectory);
}
async execute(editor: TextEditor, uri?: Uri, shaOrBranch1?: string, shaOrBranch2?: string): Promise<any> {
async execute(editor: TextEditor, uri?: Uri, args: DiffDirectoryCommandCommandArgs = {}): Promise<any> {
const diffTool = await this.git.getConfig('diff.tool');
if (!diffTool) {
const result = await window.showWarningMessage(`Unable to open directory compare because there is no Git diff tool configured`, 'View Git Docs');
if (!result) return undefined;
return commands.executeCommand(BuiltInCommands.Open, Uri.parse('https://git-scm.com/docs/git-config#git-config-difftool'));
}
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
uri = getCommandUri(uri, editor);
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return window.showWarningMessage(`Unable to open directory compare`);
if (!shaOrBranch1) {
if (!args.shaOrBranch1) {
const branches = await this.git.getBranches(repoPath);
const current = Iterables.find(branches, _ => _.current);
if (current == null) return window.showWarningMessage(`Unable to open directory compare`);
const pick = await BranchesQuickPick.show(branches, `Compare ${current.name} to \u2026`);
if (!pick) return undefined;
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
shaOrBranch1 = pick.branch.name;
if (!shaOrBranch1) return undefined;
args.shaOrBranch1 = pick.branch.name;
if (args.shaOrBranch1 === undefined) return undefined;
}
this.git.openDirectoryDiff(repoPath, shaOrBranch1, shaOrBranch2);
this.git.openDirectoryDiff(repoPath, args.shaOrBranch1, args.shaOrBranch2);
return undefined;
}
catch (ex) {

View File

@@ -1,51 +1,63 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { DiffWithPreviousCommandArgs } from './diffWithPrevious';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import * as path from 'path';
export interface DiffLineWithPreviousCommandArgs {
commit?: GitCommit;
line?: number;
showOptions?: TextDocumentShowOptions;
}
export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.DiffLineWithPrevious);
}
async execute(editor: TextEditor): Promise<any>;
async execute(editor: TextEditor, uri: Uri): Promise<any>;
async execute(editor: TextEditor, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: DiffLineWithPreviousCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
const gitUri = await GitUri.fromUri(uri, this.git);
line = line || (editor && editor.selection.active.line) || gitUri.offset;
args.line = args.line || (editor === undefined ? gitUri.offset : editor.selection.active.line);
if (!commit || GitService.isUncommitted(commit.sha)) {
if (editor && editor.document && editor.document.isDirty) return undefined;
if (args.commit === undefined || GitService.isUncommitted(args.commit.sha)) {
if (editor !== undefined && editor.document !== undefined && editor.document.isDirty) return undefined;
const blameline = line - gitUri.offset;
const blameline = args.line - gitUri.offset;
if (blameline < 0) return undefined;
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (!blame) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
if (blame === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
commit = blame.commit;
args.commit = blame.commit;
// If we don't have a sha or the current commit matches the blame, show the previous
if (!gitUri.sha || gitUri.sha === commit.sha) {
return commands.executeCommand(Commands.DiffWithPrevious, new GitUri(uri, commit), undefined, line);
if (gitUri.sha === undefined || gitUri.sha === args.commit.sha) {
return commands.executeCommand(Commands.DiffWithPrevious, new GitUri(uri, args.commit), {
line: args.line,
showOptions: args.showOptions
} as DiffWithPreviousCommandArgs);
}
// If the line is uncommitted, find the previous commit and treat it as a DiffWithWorking
if (commit.isUncommitted) {
uri = commit.uri;
commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha!, commit.previousFileName!, commit.author, commit.date, commit.message);
line = (blame.line.line + 1) + gitUri.offset;
return commands.executeCommand(Commands.DiffWithWorking, uri, commit, line);
if (args.commit.isUncommitted) {
uri = args.commit.uri;
args.commit = new GitCommit(args.commit.type, args.commit.repoPath, args.commit.previousSha!, args.commit.previousFileName!, args.commit.author, args.commit.date, args.commit.message);
args.line = (blame.line.line + 1) + gitUri.offset;
return commands.executeCommand(Commands.DiffWithWorking, uri, {
commit: args.commit,
line: args.line,
showOptions: args.showOptions
} as DiffWithWorkingCommandArgs);
}
}
catch (ex) {
@@ -57,11 +69,17 @@ export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
try {
const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, gitUri.sha!),
this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha)
this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha)
]);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) \u2194 ${path.basename(gitUri.fsPath)} (${gitUri.shortSha})`);
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs),
Uri.file(rhs),
`${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha}) \u2194 ${path.basename(gitUri.fsPath)} (${gitUri.shortSha})`,
args.showOptions);
// TODO: Figure out how to focus the left pane
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: args.line, at: 'center' });
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousLineCommand', 'getVersionedFile');

View File

@@ -1,41 +1,44 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
export interface DiffLineWithWorkingCommandArgs {
commit?: GitCommit;
line?: number;
showOptions?: TextDocumentShowOptions;
}
export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.DiffLineWithWorking);
}
async execute(editor: TextEditor): Promise<any>;
async execute(editor: TextEditor, uri: Uri): Promise<any>;
async execute(editor: TextEditor, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: DiffLineWithWorkingCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
const gitUri = await GitUri.fromUri(uri, this.git);
line = line || (editor && editor.selection.active.line) || gitUri.offset;
args.line = args.line || (editor === undefined ? gitUri.offset : editor.selection.active.line);
if (!commit || GitService.isUncommitted(commit.sha)) {
if (editor && editor.document && editor.document.isDirty) return undefined;
if (args.commit === undefined || GitService.isUncommitted(args.commit.sha)) {
if (editor !== undefined && editor.document !== undefined && editor.document.isDirty) return undefined;
const blameline = line - gitUri.offset;
const blameline = args.line - gitUri.offset;
if (blameline < 0) return undefined;
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (!blame) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
if (blame === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
commit = blame.commit;
args.commit = blame.commit;
// If the line is uncommitted, find the previous commit
if (commit.isUncommitted) {
commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha!, commit.previousFileName!, commit.author, commit.date, commit.message);
line = blame.line.line + 1 + gitUri.offset;
if (args.commit.isUncommitted) {
args.commit = new GitCommit(args.commit.type, args.commit.repoPath, args.commit.previousSha!, args.commit.previousFileName!, args.commit.author, args.commit.date, args.commit.message);
args.line = blame.line.line + 1 + gitUri.offset;
}
}
catch (ex) {
@@ -44,6 +47,6 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
}
}
return commands.executeCommand(Commands.DiffWithWorking, uri, commit, line);
return commands.executeCommand(Commands.DiffWithWorking, uri, args as DiffWithWorkingCommandArgs);
}
}

View File

@@ -1,44 +1,54 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, BranchesQuickPick } from '../quickPicks';
import * as path from 'path';
export interface DiffWithBranchCommandArgs {
line?: number;
showOptions?: TextDocumentShowOptions;
goBackCommand?: CommandQuickPickItem;
}
export class DiffWithBranchCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.DiffWithBranch);
}
async execute(editor: TextEditor, uri?: Uri, goBackCommand?: CommandQuickPickItem): Promise<any> {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: DiffWithBranchCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
const line = (editor && editor.selection.active.line) || 0;
args.line = args.line || (editor === undefined ? 0 : editor.selection.active.line);
const gitUri = await GitUri.fromUri(uri, this.git);
if (gitUri.repoPath === undefined) return undefined;
const branches = await this.git.getBranches(gitUri.repoPath);
const pick = await BranchesQuickPick.show(branches, `Compare ${path.basename(gitUri.fsPath)} to \u2026`, goBackCommand);
if (!pick) return undefined;
const pick = await BranchesQuickPick.show(branches, `Compare ${path.basename(gitUri.fsPath)} to \u2026`, args.goBackCommand);
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
const branch = pick.branch.name;
if (!branch) return undefined;
if (branch === undefined) return undefined;
try {
const compare = await this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, branch);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), gitUri.fileUri(), `${path.basename(gitUri.fsPath)} (${branch}) \u2194 ${path.basename(gitUri.fsPath)}`);
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(compare),
gitUri.fileUri(),
`${path.basename(gitUri.fsPath)} (${branch}) \u2194 ${path.basename(gitUri.fsPath)}`,
args.showOptions);
// TODO: Figure out how to focus the left pane
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: args.line, at: 'center' });
}
catch (ex) {
Logger.error(ex, 'DiffWithBranchCommand', 'getVersionedFile');

View File

@@ -1,51 +1,46 @@
'use strict';
import { Iterables } from '../system';
import { commands, Range, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import * as path from 'path';
export interface DiffWithNextCommandArgs {
commit?: GitLogCommit;
line?: number;
range?: Range;
showOptions?: TextDocumentShowOptions;
}
export class DiffWithNextCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.DiffWithNext);
}
async execute(editor: TextEditor): Promise<any>;
async execute(editor: TextEditor, uri: Uri): Promise<any>;
async execute(editor: TextEditor, uri: Uri, commit: GitLogCommit, range?: Range): Promise<any>;
async execute(editor: TextEditor, uri: Uri, commit: GitLogCommit, line?: number): Promise<any>;
async execute(editor: TextEditor, uri?: Uri, commit?: GitLogCommit, rangeOrLine?: Range | number): Promise<any> {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: DiffWithNextCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
let line = (editor && editor.selection.active.line) || 0;
if (typeof rangeOrLine === 'number') {
line = rangeOrLine || line;
rangeOrLine = undefined;
}
args.line = args.line || (editor === undefined ? 0 : editor.selection.active.line);
if (!commit || !(commit instanceof GitLogCommit) || rangeOrLine instanceof Range) {
if (args.commit === undefined || !(args.commit instanceof GitLogCommit) || args.range !== undefined) {
const gitUri = await GitUri.fromUri(uri, this.git);
try {
if (!gitUri.sha) {
// If the file is uncommitted, treat it as a DiffWithWorking
if (await this.git.isFileUncommitted(gitUri)) {
return commands.executeCommand(Commands.DiffWithWorking, uri);
}
// If the sha is missing or the file is uncommitted, treat it as a DiffWithWorking
if (gitUri.sha === undefined && await this.git.isFileUncommitted(gitUri)) {
return commands.executeCommand(Commands.DiffWithWorking, uri);
}
const sha = (commit && commit.sha) || gitUri.sha;
const sha = args.commit === undefined ? gitUri.sha : args.commit.sha;
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, rangeOrLine!);
if (!log) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, args.range!);
if (log === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
args.commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
}
catch (ex) {
Logger.error(ex, 'DiffWithNextCommand', `getLogForFile(${gitUri.repoPath}, ${gitUri.fsPath})`);
@@ -53,17 +48,22 @@ export class DiffWithNextCommand extends ActiveEditorCommand {
}
}
if (!commit.nextSha) {
return commands.executeCommand(Commands.DiffWithWorking, uri);
}
if (args.commit.nextSha === undefined) return commands.executeCommand(Commands.DiffWithWorking, uri);
try {
const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(commit.repoPath, commit.nextUri.fsPath, commit.nextSha),
this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha)
this.git.getVersionedFile(args.commit.repoPath, args.commit.nextUri.fsPath, args.commit.nextSha),
this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha)
]);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) \u2194 ${path.basename(commit.nextUri.fsPath)} (${commit.nextShortSha})`);
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs),
Uri.file(rhs),
`${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha}) \u2194 ${path.basename(args.commit.nextUri.fsPath)} (${args.commit.nextShortSha})`,
args.showOptions);
// TODO: Figure out how to focus the left pane
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: args.line, at: 'center' });
}
catch (ex) {
Logger.error(ex, 'DiffWithNextCommand', 'getVersionedFile');

View File

@@ -1,52 +1,47 @@
'use strict';
import { Iterables } from '../system';
import { commands, Range, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import * as moment from 'moment';
import * as path from 'path';
export interface DiffWithPreviousCommandArgs {
commit?: GitCommit;
line?: number;
range?: Range;
showOptions?: TextDocumentShowOptions;
}
export class DiffWithPreviousCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.DiffWithPrevious);
}
async execute(editor: TextEditor): Promise<any>;
async execute(editor: TextEditor, uri: Uri): Promise<any>;
async execute(editor: TextEditor, uri: Uri, commit: GitCommit, range?: Range): Promise<any>;
async execute(editor: TextEditor, uri: Uri, commit: GitCommit, line?: number): Promise<any>;
async execute(editor: TextEditor, uri?: Uri, commit?: GitCommit, rangeOrLine?: Range | number): Promise<any> {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: DiffWithPreviousCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
let line = (editor && editor.selection.active.line) || 0;
if (typeof rangeOrLine === 'number') {
line = rangeOrLine || line;
rangeOrLine = undefined;
}
args.line = args.line || (editor === undefined ? 0 : editor.selection.active.line);
if (!commit || rangeOrLine instanceof Range) {
if (args.commit === undefined || args.range !== undefined) {
const gitUri = await GitUri.fromUri(uri, this.git);
try {
if (!gitUri.sha) {
// If the file is uncommitted, treat it as a DiffWithWorking
if (await this.git.isFileUncommitted(gitUri)) {
return commands.executeCommand(Commands.DiffWithWorking, uri);
}
// If the sha is missing or the file is uncommitted, treat it as a DiffWithWorking
if (gitUri.sha === undefined && await this.git.isFileUncommitted(gitUri)) {
return commands.executeCommand(Commands.DiffWithWorking, uri);
}
const sha = (commit && commit.sha) || gitUri.sha;
const sha = args.commit === undefined ? gitUri.sha : args.commit.sha;
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, rangeOrLine!);
if (!log) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, args.range!);
if (log === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
args.commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousCommand', `getLogForFile(${gitUri.repoPath}, ${gitUri.fsPath})`);
@@ -54,18 +49,22 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
}
}
if (!commit.previousSha) {
return window.showInformationMessage(`Commit ${commit.shortSha} (${commit.author}, ${moment(commit.date).fromNow()}) has no previous commit`);
}
if (args.commit.previousSha === undefined) return window.showInformationMessage(`Commit ${args.commit.shortSha} (${args.commit.author}, ${moment(args.commit.date).fromNow()}) has no previous commit`);
try {
const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha),
this.git.getVersionedFile(commit.repoPath, commit.previousUri.fsPath, commit.previousSha)
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)
]);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.previousUri.fsPath)} (${commit.previousShortSha}) \u2194 ${path.basename(commit.uri.fsPath)} (${commit.shortSha})`);
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs),
Uri.file(rhs),
`${path.basename(args.commit.previousUri.fsPath)} (${args.commit.previousShortSha}) \u2194 ${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha})`,
args.showOptions);
// TODO: Figure out how to focus the left pane
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: args.line, at: 'center' });
}
catch (ex) {
Logger.error(ex, 'DiffWithPreviousCommand', 'getVersionedFile');

View File

@@ -1,34 +1,35 @@
'use strict';
// import { Iterables } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import * as path from 'path';
export interface DiffWithWorkingCommandArgs {
commit?: GitCommit;
line?: number;
showOptions?: TextDocumentShowOptions;
}
export class DiffWithWorkingCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.DiffWithWorking);
}
async execute(editor: TextEditor): Promise<any>;
async execute(editor: TextEditor, uri: Uri): Promise<any>;
async execute(editor: TextEditor, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: DiffWithWorkingCommandArgs = {}): Promise<any> {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
line = line || (editor && editor.selection.active.line) || 0;
args.line = args.line || (editor === undefined ? 0 : editor.selection.active.line);
if (!commit || GitService.isUncommitted(commit.sha)) {
if (args.commit === undefined || GitService.isUncommitted(args.commit.sha)) {
const gitUri = await GitUri.fromUri(uri, this.git);
try {
commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, gitUri.sha, { firstIfMissing: true });
if (!commit) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
args.commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, gitUri.sha, { firstIfMissing: true });
if (args.commit === undefined) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
}
catch (ex) {
Logger.error(ex, 'DiffWithWorkingCommand', `getLogCommit(${gitUri.repoPath}, ${gitUri.fsPath}, ${gitUri.sha})`);
@@ -42,9 +43,16 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
if (workingFileName === undefined) return undefined;
try {
const compare = await this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), Uri.file(path.resolve(gitUri.repoPath, workingFileName)), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) \u2194 ${path.basename(workingFileName)}`);
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
const compare = await this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha);
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(compare),
Uri.file(path.resolve(gitUri.repoPath, workingFileName)),
`${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha}) \u2194 ${path.basename(workingFileName)}`,
args.showOptions);
// TODO: Figure out how to focus the left pane
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: args.line, at: 'center' });
}
catch (ex) {
Logger.error(ex, 'DiffWithWorkingCommand', 'getVersionedFile');

View File

@@ -1,142 +1,137 @@
'use strict';
import { commands, Disposable, QuickPickItem } from 'vscode';
import { CommandContext, setCommandContext } from './common';
import { ExtensionKey } from '../constants';
import { CommandQuickPickItem, OpenFileCommandQuickPickItem } from '../quickPicks';
import { Logger } from '../logger';
const keyNoopCommand = Object.create(null) as QuickPickItem;
export { keyNoopCommand as KeyNoopCommand };
export declare type Keys = 'left' | 'right' | ',' | '.';
export const keys: Keys[] = [
'left',
'right',
',',
'.'
];
'use strict';
import { commands, Disposable } from 'vscode';
import { CommandContext, setCommandContext } from './common';
import { ExtensionKey } from '../constants';
import { QuickPickItem } from '../quickPicks';
import { Logger } from '../logger';
const keyNoopCommand = Object.create(null) as QuickPickItem;
export { keyNoopCommand as KeyNoopCommand };
export declare type Keys = 'left' | 'right' | ',' | '.';
export const keys: Keys[] = [
'left',
'right',
',',
'.'
];
export declare type KeyMapping = { [id: string]: (QuickPickItem | (() => Promise<QuickPickItem>) | undefined) };
let mappings: KeyMapping[] = [];
let _instance: Keyboard;
export class KeyboardScope extends Disposable {
constructor(private mapping: KeyMapping) {
super(() => this.dispose());
for (const key in mapping) {
mapping[key] = mapping[key] || keyNoopCommand;
}
}
async dispose() {
const index = mappings.indexOf(this.mapping);
Logger.log('KeyboardScope.dispose', mappings.length, index);
if (index === (mappings.length - 1)) {
mappings.pop();
await this.updateKeyCommandsContext(mappings[mappings.length - 1]);
}
else {
mappings.splice(index, 1);
}
}
async begin() {
mappings.push(this.mapping);
await this.updateKeyCommandsContext(this.mapping);
return this;
}
async clearKeyCommand(key: Keys) {
const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping || !mapping[key]) return;
Logger.log('KeyboardScope.clearKeyCommand', mappings.length, key);
mapping[key] = undefined;
await setCommandContext(`${CommandContext.Key}:${key}`, false);
}
async setKeyCommand(key: Keys, command: QuickPickItem | (() => Promise<QuickPickItem>)) {
const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping) return;
Logger.log('KeyboardScope.setKeyCommand', mappings.length, key, !!mapping[key]);
if (!mapping[key]) {
mapping[key] = command;
await setCommandContext(`${CommandContext.Key}:${key}`, true);
}
else {
mapping[key] = command;
}
}
private async updateKeyCommandsContext(mapping: KeyMapping) {
const promises = [];
for (const key of keys) {
promises.push(setCommandContext(`${CommandContext.Key}:${key}`, !!(mapping && mapping[key])));
}
await Promise.all(promises);
}
}
export class Keyboard extends Disposable {
static get instance(): Keyboard {
return _instance;
}
private _disposable: Disposable;
constructor() {
super(() => this.dispose());
const subscriptions: Disposable[] = [];
for (const key of keys) {
subscriptions.push(commands.registerCommand(`${ExtensionKey}.key.${key}`, () => this.execute(key), this));
}
this._disposable = Disposable.from(...subscriptions);
_instance = this;
}
dispose() {
this._disposable && this._disposable.dispose();
}
async beginScope(mapping?: KeyMapping): Promise<KeyboardScope> {
Logger.log('Keyboard.beginScope', mappings.length);
return await new KeyboardScope(mapping ? Object.assign(Object.create(null), mapping) : Object.create(null)).begin();
}
async execute(key: Keys): Promise<{} | undefined> {
if (!mappings.length) return undefined;
try {
const mapping = mappings[mappings.length - 1];
let command = mapping[key] as CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
if (typeof command === 'function') {
command = await command();
}
if (!command || !(command instanceof CommandQuickPickItem)) return undefined;
Logger.log('Keyboard.execute', key);
if (command instanceof OpenFileCommandQuickPickItem) {
// Have to open this pinned right now, because vscode doesn't have a way to open a unpinned, but unfocused editor
return await command.execute(true);
}
return await command.execute();
}
catch (ex) {
Logger.error(ex, 'Keyboard.execute');
return undefined;
}
}
let mappings: KeyMapping[] = [];
let _instance: Keyboard;
export class KeyboardScope extends Disposable {
constructor(private mapping: KeyMapping) {
super(() => this.dispose());
for (const key in mapping) {
mapping[key] = mapping[key] || keyNoopCommand;
}
}
async dispose() {
const index = mappings.indexOf(this.mapping);
Logger.log('KeyboardScope.dispose', mappings.length, index);
if (index === (mappings.length - 1)) {
mappings.pop();
await this.updateKeyCommandsContext(mappings[mappings.length - 1]);
}
else {
mappings.splice(index, 1);
}
}
async begin() {
mappings.push(this.mapping);
await this.updateKeyCommandsContext(this.mapping);
return this;
}
async clearKeyCommand(key: Keys) {
const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping || !mapping[key]) return;
Logger.log('KeyboardScope.clearKeyCommand', mappings.length, key);
mapping[key] = undefined;
await setCommandContext(`${CommandContext.Key}:${key}`, false);
}
async setKeyCommand(key: Keys, command: QuickPickItem | (() => Promise<QuickPickItem>)) {
const mapping = mappings[mappings.length - 1];
if (mapping !== this.mapping) return;
Logger.log('KeyboardScope.setKeyCommand', mappings.length, key, !!mapping[key]);
if (!mapping[key]) {
mapping[key] = command;
await setCommandContext(`${CommandContext.Key}:${key}`, true);
}
else {
mapping[key] = command;
}
}
private async updateKeyCommandsContext(mapping: KeyMapping) {
const promises = [];
for (const key of keys) {
promises.push(setCommandContext(`${CommandContext.Key}:${key}`, !!(mapping && mapping[key])));
}
await Promise.all(promises);
}
}
export class Keyboard extends Disposable {
static get instance(): Keyboard {
return _instance;
}
private _disposable: Disposable;
constructor() {
super(() => this.dispose());
const subscriptions: Disposable[] = [];
for (const key of keys) {
subscriptions.push(commands.registerCommand(`${ExtensionKey}.key.${key}`, () => this.execute(key), this));
}
this._disposable = Disposable.from(...subscriptions);
_instance = this;
}
dispose() {
this._disposable && this._disposable.dispose();
}
async beginScope(mapping?: KeyMapping): Promise<KeyboardScope> {
Logger.log('Keyboard.beginScope', mappings.length);
return await new KeyboardScope(mapping ? Object.assign(Object.create(null), mapping) : Object.create(null)).begin();
}
async execute(key: Keys): Promise<{} | undefined> {
if (!mappings.length) return undefined;
try {
const mapping = mappings[mappings.length - 1];
let command = mapping[key] as QuickPickItem | (() => Promise<QuickPickItem>);
if (typeof command === 'function') {
command = await command();
}
if (!command || typeof command.onDidPressKey !== 'function') return undefined;
Logger.log('Keyboard.execute', key);
return await command.onDidPressKey(key);
}
catch (ex) {
Logger.error(ex, 'Keyboard.execute');
return undefined;
}
}
}

View File

@@ -1,33 +1,35 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, openEditor } from './common';
import { TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri, openEditor } from './common';
import { GitService } from '../gitService';
import { Logger } from '../logger';
export interface OpenChangedFilesCommandArgs {
uris?: Uri[];
}
export class OpenChangedFilesCommand extends ActiveEditorCommand {
constructor(private git: GitService) {
super(Commands.OpenChangedFiles);
}
async execute(editor: TextEditor, uri?: Uri, uris?: Uri[]) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: OpenChangedFilesCommandArgs = {}) {
uri = getCommandUri(uri, editor);
try {
if (!uris) {
if (args.uris === undefined) {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return window.showWarningMessage(`Unable to open changed files`);
if (repoPath === undefined) return window.showWarningMessage(`Unable to open changed files`);
const status = await this.git.getStatusForRepo(repoPath);
if (!status) return window.showWarningMessage(`Unable to open changed files`);
if (status === undefined) return window.showWarningMessage(`Unable to open changed files`);
uris = status.files.filter(_ => _.status !== 'D').map(_ => _.Uri);
args.uris = status.files.filter(_ => _.status !== 'D').map(_ => _.Uri);
}
for (const uri of uris) {
await openEditor(uri, true);
for (const uri of args.uris) {
await openEditor(uri, { preserveFocus: true, preview: false } as TextDocumentShowOptions);
}
return undefined;

View File

@@ -1,9 +1,10 @@
'use strict';
import { Arrays } from '../system';
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote';
export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
@@ -12,24 +13,21 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
if ((editor && editor.document && editor.document.isDirty) || !uri) return undefined;
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
if (editor !== undefined && editor.document !== undefined && editor.document.isDirty) return undefined;
const gitUri = await GitUri.fromUri(uri, this.git);
if (!gitUri.repoPath) return undefined;
const line = (editor && editor.selection.active.line) || gitUri.offset;
const line = editor === undefined ? gitUri.offset : editor.selection.active.line;
try {
const blameline = line - gitUri.offset;
if (blameline < 0) return undefined;
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (!blame) return window.showWarningMessage(`Unable to open commit in remote provider. File is probably not under source control`);
if (blame === undefined) return window.showWarningMessage(`Unable to open commit in remote provider. File is probably not under source control`);
let commit = blame.commit;
// If the line is uncommitted, find the previous commit
@@ -38,7 +36,13 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
}
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);
return commands.executeCommand(Commands.OpenInRemote, uri, remotes, 'commit', [commit.sha]);
return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: {
type: 'commit',
sha: commit.sha
},
remotes
} as OpenInRemoteCommandArgs);
}
catch (ex) {
Logger.error(ex, 'OpenCommitInRemoteCommand');

View File

@@ -1,9 +1,10 @@
'use strict';
import { Arrays } from '../system';
import { commands, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote';
export class OpenFileInRemoteCommand extends ActiveEditorCommand {
@@ -12,12 +13,8 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
if (!uri) return undefined;
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
const gitUri = await GitUri.fromUri(uri, this.git);
if (!gitUri.repoPath) return undefined;
@@ -26,8 +23,18 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
try {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);
const range = editor && new Range(editor.selection.start.with({ line: editor.selection.start.line + 1 }), editor.selection.end.with({ line: editor.selection.end.line + 1 }));
return commands.executeCommand(Commands.OpenInRemote, uri, remotes, 'file', [gitUri.getRelativePath(), branch === undefined ? 'Current' : branch.name, gitUri.sha, range]);
const range = editor === undefined ? undefined : new Range(editor.selection.start.with({ line: editor.selection.start.line + 1 }), editor.selection.end.with({ line: editor.selection.end.line + 1 }));
return commands.executeCommand(Commands.OpenInRemote, uri, {
resource: {
type: 'file',
branch: branch === undefined ? 'Current' : branch.name,
fileName: gitUri.getRelativePath(),
range: range,
sha: gitUri.sha
},
remotes
} as OpenInRemoteCommandArgs);
}
catch (ex) {
Logger.error(ex, 'OpenFileInRemoteCommand');

View File

@@ -1,50 +1,73 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './common';
import { GitRemote, RemoteOpenType } from '../gitService';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
import { GitLogCommit, GitRemote, RemoteResource } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, OpenRemoteCommandQuickPickItem, RemotesQuickPick } from '../quickPicks';
export interface OpenInRemoteCommandArgs {
remotes?: GitRemote[];
resource?: RemoteResource;
goBackCommand?: CommandQuickPickItem;
}
export class OpenInRemoteCommand extends ActiveEditorCommand {
constructor() {
super(Commands.OpenInRemote);
}
async execute(editor: TextEditor, uri?: Uri, remotes?: GitRemote[], type?: RemoteOpenType, args: string[] = [], goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: OpenInRemoteCommandArgs = {}) {
uri = getCommandUri(uri, editor);
try {
if (remotes === undefined) return undefined;
if (type === undefined) throw new Error(`Invalid type ${type}`);
if (args.remotes === undefined || args.resource === undefined) return undefined;
if (remotes.length === 1) {
const command = new OpenRemoteCommandQuickPickItem(remotes[0], type, ...args);
if (args.remotes.length === 1) {
const command = new OpenRemoteCommandQuickPickItem(args.remotes[0], args.resource);
return command.execute();
}
let placeHolder: string = '';
switch (type) {
switch (args.resource.type) {
case 'branch':
placeHolder = `open ${args[0]} branch in\u2026`;
placeHolder = `open ${args.resource.branch} branch in\u2026`;
break;
case 'commit':
const shortSha = args[0].substring(0, 8);
const shortSha = args.resource.sha.substring(0, 8);
placeHolder = `open commit ${shortSha} in\u2026`;
break;
case 'file':
case 'working-file':
const shortFileSha = (args[2] && args[2].substring(0, 8)) || '';
const shaSuffix = shortFileSha ? ` \u00a0\u2022\u00a0 ${shortFileSha}` : '';
placeHolder = `open ${args[0]}${shaSuffix} in\u2026`;
case 'file':
if (args.resource.commit !== undefined && args.resource.commit instanceof GitLogCommit) {
if (args.resource.commit.status === 'D') {
args.resource.sha = args.resource.commit.previousSha;
placeHolder = `open ${args.resource.fileName} \u00a0\u2022\u00a0 ${args.resource.commit.previousShortSha} in\u2026`;
}
else {
args.resource.sha = args.resource.commit.sha;
placeHolder = `open ${args.resource.fileName} \u00a0\u2022\u00a0 ${args.resource.commit.shortSha} in\u2026`;
}
}
else {
const shortFileSha = args.resource.sha === undefined ? '' : args.resource.sha.substring(0, 8);
const shaSuffix = shortFileSha ? ` \u00a0\u2022\u00a0 ${shortFileSha}` : '';
placeHolder = `open ${args.resource.fileName}${shaSuffix} in\u2026`;
}
break;
case 'working-file':
placeHolder = `open ${args.resource.fileName} in\u2026`;
break;
}
const pick = await RemotesQuickPick.show(remotes, placeHolder, type, args, goBackCommand);
return pick && pick.execute();
const pick = await RemotesQuickPick.show(args.remotes, placeHolder, args.resource, args.goBackCommand);
if (pick === undefined) return undefined;
return pick.execute();
}
catch (ex) {

View File

@@ -4,19 +4,21 @@ import { BlameAnnotationController } from '../blameAnnotationController';
import { Commands, EditorCommand } from './common';
import { Logger } from '../logger';
export interface ShowBlameCommandArgs {
sha?: string;
}
export class ShowBlameCommand extends EditorCommand {
constructor(private annotationController: BlameAnnotationController) {
super(Commands.ShowBlame);
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string): Promise<any> {
try {
if (sha) {
return this.annotationController.showBlameAnnotation(editor, sha);
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, args: ShowBlameCommandArgs = {}): Promise<any> {
if (editor !== undefined && editor.document !== undefined && editor.document.isDirty) return undefined;
return this.annotationController.showBlameAnnotation(editor, editor.selection.active.line);
try {
return this.annotationController.showBlameAnnotation(editor, args.sha !== undefined ? args.sha : editor.selection.active.line);
}
catch (ex) {
Logger.error(ex, 'ShowBlameCommand');

View File

@@ -1,35 +1,40 @@
'use strict';
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { Commands, EditorCommand } from './common';
import { Commands, EditorCommand, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
export interface ShowBlameHistoryCommandArgs {
line?: number;
position?: Position;
range?: Range;
sha?: string;
}
export class ShowBlameHistoryCommand extends EditorCommand {
constructor(private git: GitService) {
super(Commands.ShowBlameHistory);
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, range?: Range, position?: Position, sha?: string, line?: number) {
if (!(uri instanceof Uri)) {
if (!editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, args: ShowBlameHistoryCommandArgs = {}) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
if (range == null || position == null) {
if (args.range == null || args.position == null) {
// If the command is executed manually -- treat it as a click on the root lens (i.e. show blame for the whole file)
range = editor.document.validateRange(new Range(0, 0, 1000000, 1000000));
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
args.range = editor.document.validateRange(new Range(0, 0, 1000000, 1000000));
args.position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
}
const gitUri = await GitUri.fromUri(uri, this.git);
try {
const locations = await this.git.getBlameLocations(gitUri, range, sha, line);
if (!locations) return window.showWarningMessage(`Unable to show blame history. File is probably not under source control`);
const locations = await this.git.getBlameLocations(gitUri, args.range, args.sha, args.line);
if (locations === undefined) return window.showWarningMessage(`Unable to show blame history. File is probably not under source control`);
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations);
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, args.position, locations);
}
catch (ex) {
Logger.error(ex, 'ShowBlameHistoryCommand', 'getBlameLocations');

View File

@@ -1,9 +1,10 @@
'use strict';
import { commands, InputBoxOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitRepoSearchBy, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitsQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
const searchByRegex = /^([@:#])/;
const searchByMap = new Map<string, GitRepoSearchBy>([
@@ -12,68 +13,73 @@ const searchByMap = new Map<string, GitRepoSearchBy>([
['#', GitRepoSearchBy.Sha]
]);
export interface ShowCommitSearchCommandArgs {
search?: string;
searchBy?: GitRepoSearchBy;
goBackCommand?: CommandQuickPickItem;
}
export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
constructor(private git: GitService) {
super(Commands.ShowCommitSearch);
}
async execute(editor: TextEditor, uri?: Uri, search?: string, searchBy?: GitRepoSearchBy, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: ShowCommitSearchCommandArgs = {}) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
const gitUri = await GitUri.fromUri(uri, this.git);
if (gitUri.repoPath === undefined) return undefined;
if (!search || searchBy == null) {
search = await window.showInputBox({
value: search,
if (!args.search || args.searchBy == null) {
args.search = await window.showInputBox({
value: args.search,
prompt: `Please enter a search string`,
placeHolder: `search by message, author (use @<name>), files (use :<pattern>), or commit id (use #<sha>)`
} as InputBoxOptions);
if (search === undefined) return goBackCommand && goBackCommand.execute();
if (args.search === undefined) return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
const match = searchByRegex.exec(search);
const match = searchByRegex.exec(args.search);
if (match && match[1]) {
searchBy = searchByMap.get(match[1]);
search = search.substring((search[1] === ' ') ? 2 : 1);
args.searchBy = searchByMap.get(match[1]);
args.search = args.search.substring((args.search[1] === ' ') ? 2 : 1);
}
else if (GitService.isSha(search)) {
searchBy = GitRepoSearchBy.Sha;
else if (GitService.isSha(args.search)) {
args.searchBy = GitRepoSearchBy.Sha;
}
else {
searchBy = GitRepoSearchBy.Message;
args.searchBy = GitRepoSearchBy.Message;
}
}
try {
if (searchBy === undefined) {
searchBy = GitRepoSearchBy.Message;
if (args.searchBy === undefined) {
args.searchBy = GitRepoSearchBy.Message;
}
const log = await this.git.getLogForRepoSearch(gitUri.repoPath, search, searchBy);
const log = await this.git.getLogForRepoSearch(gitUri.repoPath, args.search, args.searchBy);
if (log === undefined) return undefined;
let originalSearch: string | undefined = undefined;
let placeHolder: string | undefined = undefined;
switch (searchBy) {
switch (args.searchBy) {
case GitRepoSearchBy.Author:
originalSearch = `@${search}`;
placeHolder = `commits with author matching '${search}'`;
originalSearch = `@${args.search}`;
placeHolder = `commits with author matching '${args.search}'`;
break;
case GitRepoSearchBy.Files:
originalSearch = `:${search}`;
placeHolder = `commits with files matching '${search}'`;
originalSearch = `:${args.search}`;
placeHolder = `commits with files matching '${args.search}'`;
break;
case GitRepoSearchBy.Message:
originalSearch = search;
placeHolder = `commits with message matching '${search}'`;
originalSearch = args.search;
placeHolder = `commits with message matching '${args.search}'`;
break;
case GitRepoSearchBy.Sha:
originalSearch = `#${search}`;
placeHolder = `commits with id matching '${search}'`;
originalSearch = `#${args.search}`;
placeHolder = `commits with id matching '${args.search}'`;
break;
}
@@ -81,20 +87,32 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to commit search`
}, Commands.ShowCommitSearch, [gitUri, originalSearch, undefined, goBackCommand]);
}, Commands.ShowCommitSearch, [
gitUri,
{
search: originalSearch,
goBackCommand: args.goBackCommand
} as ShowCommitSearchCommandArgs
]);
const pick = await CommitsQuickPick.show(this.git, log, placeHolder!, currentCommand);
if (!pick) return undefined;
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
return commands.executeCommand(Commands.ShowQuickCommitDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to search for ${placeHolder}`
}, Commands.ShowCommitSearch, [gitUri, search, searchBy, goBackCommand]));
return commands.executeCommand(Commands.ShowQuickCommitDetails,
new GitUri(pick.commit.uri, pick.commit),
{
sha: pick.commit.sha,
commit: pick.commit,
goBackCommand: new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to search for ${placeHolder}`
}, Commands.ShowCommitSearch, [
gitUri,
args
])
} as ShowQuickCommitDetailsCommandArgs);
}
catch (ex) {
Logger.error(ex, 'ShowCommitSearchCommand');

View File

@@ -1,34 +1,38 @@
'use strict';
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { Commands, EditorCommand } from './common';
import { Commands, EditorCommand, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
export interface ShowFileHistoryCommandArgs {
line?: number;
position?: Position;
sha?: string;
}
export class ShowFileHistoryCommand extends EditorCommand {
constructor(private git: GitService) {
super(Commands.ShowFileHistory);
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, position?: Position, sha?: string, line?: number) {
if (!(uri instanceof Uri)) {
if (!editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, args: ShowFileHistoryCommandArgs = {}) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
if (position == null) {
if (args.position == null) {
// If the command is executed manually -- treat it as a click on the root lens (i.e. show blame for the whole file)
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
args.position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
}
const gitUri = await GitUri.fromUri(uri, this.git);
try {
const locations = await this.git.getLogLocations(gitUri, sha, line);
if (!locations) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
const locations = await this.git.getLogLocations(gitUri, args.sha, args.line);
if (locations === undefined) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations);
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, args.position, locations);
}
catch (ex) {
Logger.error(ex, 'ShowFileHistoryCommand', 'getLogLocations');

View File

@@ -1,9 +1,19 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { BranchesQuickPick, BranchHistoryQuickPick, CommandQuickPickItem } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
export interface ShowQuickBranchHistoryCommandArgs {
branch?: string;
log?: IGitLog;
maxCount?: number;
goBackCommand?: CommandQuickPickItem;
nextPageCommand?: CommandQuickPickItem;
}
export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
@@ -11,58 +21,63 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
super(Commands.ShowQuickBranchHistory);
}
async execute(editor: TextEditor, uri?: Uri, branch?: string, maxCount?: number, goBackCommand?: CommandQuickPickItem, log?: IGitLog, nextPageCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: ShowQuickBranchHistoryCommandArgs = {}) {
uri = getCommandUri(uri, editor);
const gitUri = uri && await GitUri.fromUri(uri, this.git);
if (maxCount == null) {
maxCount = this.git.config.advanced.maxQuickHistory;
if (args.maxCount == null) {
args.maxCount = this.git.config.advanced.maxQuickHistory;
}
let progressCancellation = branch === undefined ? undefined : BranchHistoryQuickPick.showProgress(branch);
let progressCancellation = args.branch === undefined ? undefined : BranchHistoryQuickPick.showProgress(args.branch);
try {
const repoPath = (gitUri && gitUri.repoPath) || this.git.repoPath;
if (repoPath === undefined) return window.showWarningMessage(`Unable to show branch history`);
if (branch === undefined) {
if (args.branch === undefined) {
const branches = await this.git.getBranches(repoPath);
const pick = await BranchesQuickPick.show(branches, `Show history for branch\u2026`);
if (!pick) return undefined;
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
branch = pick.branch.name;
if (branch === undefined) return undefined;
args.branch = pick.branch.name;
if (args.branch === undefined) return undefined;
progressCancellation = BranchHistoryQuickPick.showProgress(branch);
progressCancellation = BranchHistoryQuickPick.showProgress(args.branch);
}
if (!log) {
log = await this.git.getLogForRepo(repoPath, (gitUri && gitUri.sha) || branch, maxCount);
if (!log) return window.showWarningMessage(`Unable to show branch history`);
if (args.log === undefined) {
args.log = await this.git.getLogForRepo(repoPath, (gitUri && gitUri.sha) || args.branch, args.maxCount);
if (args.log === undefined) return window.showWarningMessage(`Unable to show branch history`);
}
if (progressCancellation !== undefined && progressCancellation.token.isCancellationRequested) return undefined;
const pick = await BranchHistoryQuickPick.show(this.git, log, gitUri, branch, progressCancellation!, goBackCommand, nextPageCommand);
if (!pick) return undefined;
const pick = await BranchHistoryQuickPick.show(this.git, args.log, gitUri, args.branch, progressCancellation!, args.goBackCommand, args.nextPageCommand);
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
return commands.executeCommand(Commands.ShowQuickCommitDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to \u00a0$(git-branch) ${branch} history`
}, Commands.ShowQuickBranchHistory, [uri, branch, maxCount, goBackCommand, log]),
log);
// Create a command to get back to here
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to \u00a0$(git-branch) ${args.branch} history`
}, Commands.ShowQuickBranchHistory, [
uri,
args
]);
return commands.executeCommand(Commands.ShowQuickCommitDetails,
new GitUri(pick.commit.uri, pick.commit),
{
sha: pick.commit.sha,
commit: pick.commit,
repoLog: args.log,
goBackCommand: currentCommand
} as ShowQuickCommitDetailsCommandArgs);
}
catch (ex) {
Logger.error(ex, 'ShowQuickBranchHistoryCommand');

View File

@@ -1,43 +1,50 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitCommit, GitLogCommit, GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails';
import * as path from 'path';
export interface ShowQuickCommitDetailsCommandArgs {
sha?: string;
commit?: GitCommit | GitLogCommit;
repoLog?: IGitLog;
goBackCommand?: CommandQuickPickItem;
}
export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
constructor(private git: GitService) {
super(Commands.ShowQuickCommitDetails);
}
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit | GitLogCommit, goBackCommand?: CommandQuickPickItem, repoLog?: IGitLog) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: ShowQuickCommitDetailsCommandArgs = {}) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
const gitUri = await GitUri.fromUri(uri, this.git);
let repoPath = gitUri.repoPath;
let workingFileName = path.relative(repoPath || '', gitUri.fsPath);
if (!sha) {
if (!editor) return undefined;
if (args.sha === undefined) {
if (editor === undefined) return undefined;
const blameline = editor.selection.active.line - gitUri.offset;
if (blameline < 0) return undefined;
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (!blame) return window.showWarningMessage(`Unable to show commit details. File is probably not under source control`);
if (blame === undefined) return window.showWarningMessage(`Unable to show commit details. File is probably not under source control`);
sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
args.sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
repoPath = blame.commit.repoPath;
workingFileName = blame.commit.fileName;
commit = blame.commit;
args.commit = blame.commit;
}
catch (ex) {
Logger.error(ex, 'ShowQuickCommitDetailsCommand', `getBlameForLine(${blameline})`);
@@ -46,51 +53,60 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
}
try {
if (!commit || (commit.type !== 'branch' && commit.type !== 'stash')) {
if (repoLog) {
commit = repoLog.commits.get(sha!);
if (args.commit === undefined || (args.commit.type !== 'branch' && args.commit.type !== 'stash')) {
if (args.repoLog !== undefined) {
args.commit = args.repoLog.commits.get(args.sha!);
// If we can't find the commit, kill the repoLog
if (commit === undefined) {
repoLog = undefined;
if (args.commit === undefined) {
args.repoLog = undefined;
}
}
if (repoLog === undefined) {
const log = await this.git.getLogForRepo(repoPath!, sha, 2);
if (args.repoLog === undefined) {
const log = await this.git.getLogForRepo(repoPath!, args.sha, 2);
if (log === undefined) return window.showWarningMessage(`Unable to show commit details`);
commit = log.commits.get(sha!);
args.commit = log.commits.get(args.sha!);
}
}
if (commit === undefined) return window.showWarningMessage(`Unable to show commit details`);
if (args.commit === undefined) return window.showWarningMessage(`Unable to show commit details`);
if (!commit.workingFileName) {
commit.workingFileName = workingFileName;
if (args.commit.workingFileName === undefined) {
args.commit.workingFileName = workingFileName;
}
if (!goBackCommand) {
if (args.goBackCommand === undefined) {
// Create a command to get back to the branch history
goBackCommand = new CommandQuickPickItem({
args.goBackCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to branch history`
}, Commands.ShowQuickCurrentBranchHistory, [new GitUri(commit.uri, commit)]);
}, Commands.ShowQuickCurrentBranchHistory, [
new GitUri(args.commit.uri, args.commit)
]);
}
// Create a command to get back to where we are right now
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(git-commit) ${commit.shortSha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand, repoLog]);
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(git-commit) ${args.commit.shortSha}`
}, Commands.ShowQuickCommitDetails, [
new GitUri(args.commit.uri, args.commit),
args
]);
const pick = await CommitDetailsQuickPick.show(this.git, commit as GitLogCommit, uri, goBackCommand, currentCommand, repoLog);
if (!pick) return undefined;
const pick = await CommitDetailsQuickPick.show(this.git, args.commit as GitLogCommit, uri, args.goBackCommand, currentCommand, args.repoLog);
if (pick === undefined) return undefined;
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) {
return pick.execute();
}
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) return pick.execute();
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, pick.gitUri, pick.sha, commit, currentCommand);
return commands.executeCommand(Commands.ShowQuickCommitFileDetails,
pick.gitUri,
{
commit: args.commit,
sha: pick.sha,
goBackCommand: currentCommand
} as ShowQuickCommitFileDetailsCommandArgs);
}
catch (ex) {
Logger.error(ex, 'ShowQuickCommitDetailsCommand');

View File

@@ -1,41 +1,48 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitCommit, GitLogCommit, GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitFileDetailsQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
import * as path from 'path';
export interface ShowQuickCommitFileDetailsCommandArgs {
sha?: string;
commit?: GitCommit | GitLogCommit;
fileLog?: IGitLog;
goBackCommand?: CommandQuickPickItem;
}
export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand {
constructor(private git: GitService) {
super(Commands.ShowQuickCommitFileDetails);
}
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit | GitLogCommit, goBackCommand?: CommandQuickPickItem, fileLog?: IGitLog) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: ShowQuickCommitFileDetailsCommandArgs = {}) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
let workingFileName = commit && commit.workingFileName;
let workingFileName = args.commit && args.commit.workingFileName;
const gitUri = await GitUri.fromUri(uri, this.git);
if (!sha) {
if (!editor) return undefined;
if (args.sha === undefined) {
if (editor === undefined) return undefined;
const blameline = editor.selection.active.line - gitUri.offset;
if (blameline < 0) return undefined;
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
if (!blame) return window.showWarningMessage(`Unable to show commit file details. File is probably not under source control`);
if (blame === undefined) return window.showWarningMessage(`Unable to show commit file details. File is probably not under source control`);
sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
args.sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
commit = blame.commit;
workingFileName = path.relative(commit.repoPath, gitUri.fsPath);
args.commit = blame.commit;
workingFileName = path.relative(args.commit.repoPath, gitUri.fsPath);
}
catch (ex) {
Logger.error(ex, 'ShowQuickCommitFileDetailsCommand', `getBlameForLine(${blameline})`);
@@ -44,50 +51,56 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
}
try {
if (!commit || (commit.type !== 'file' && commit.type !== 'stash')) {
if (fileLog) {
commit = fileLog.commits.get(sha!);
if (args.commit === undefined || (args.commit.type !== 'file' && args.commit.type !== 'stash')) {
if (args.fileLog !== undefined) {
args.commit = args.fileLog.commits.get(args.sha!);
// If we can't find the commit, kill the fileLog
if (commit === undefined) {
fileLog = undefined;
if (args.commit === undefined) {
args.fileLog = undefined;
}
}
if (fileLog === undefined) {
commit = await this.git.getLogCommit(commit ? commit.repoPath : gitUri.repoPath, gitUri.fsPath, sha, { previous: true });
if (commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
if (args.fileLog === undefined) {
args.commit = await this.git.getLogCommit(args.commit ? args.commit.repoPath : gitUri.repoPath, gitUri.fsPath, args.sha, { previous: true });
if (args.commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
}
}
if (commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
if (args.commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
commit.workingFileName = workingFileName;
commit.workingFileName = await this.git.findWorkingFileName(commit);
args.commit.workingFileName = workingFileName;
args.commit.workingFileName = await this.git.findWorkingFileName(args.commit);
const shortSha = sha!.substring(0, 8);
const shortSha = args.sha!.substring(0, 8);
if (!goBackCommand) {
if (args.goBackCommand === undefined) {
// Create a command to get back to the commit details
goBackCommand = new CommandQuickPickItem({
args.goBackCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(git-commit) ${shortSha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit]);
}, Commands.ShowQuickCommitDetails, [
new GitUri(args.commit.uri, args.commit),
{
commit: args.commit,
sha: args.sha
} as ShowQuickCommitDetailsCommandArgs
]);
}
const pick = await CommitFileDetailsQuickPick.show(this.git, commit as GitLogCommit, uri, goBackCommand,
// Create a command to get back to where we are right now
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(file-text) ${path.basename(commit.fileName)} in \u00a0$(git-commit) ${shortSha}`
}, Commands.ShowQuickCommitFileDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand]),
fileLog);
// Create a command to get back to where we are right now
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(file-text) ${path.basename(args.commit.fileName)} in \u00a0$(git-commit) ${shortSha}`
}, Commands.ShowQuickCommitFileDetails, [
new GitUri(args.commit.uri, args.commit),
args
]);
if (!pick) return undefined;
const pick = await CommitFileDetailsQuickPick.show(this.git, args.commit as GitLogCommit, uri, args.goBackCommand, currentCommand, args.fileLog);
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
return undefined;
}

View File

@@ -1,29 +1,37 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { ShowQuickBranchHistoryCommandArgs } from './showQuickBranchHistory';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks';
export interface ShowQuickCurrentBranchHistoryCommandArgs {
goBackCommand?: CommandQuickPickItem;
}
export class ShowQuickCurrentBranchHistoryCommand extends ActiveEditorCachedCommand {
constructor(private git: GitService) {
super(Commands.ShowQuickCurrentBranchHistory);
}
async execute(editor: TextEditor, uri?: Uri, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: ShowQuickCurrentBranchHistoryCommandArgs = {}) {
uri = getCommandUri(uri, editor);
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return window.showWarningMessage(`Unable to show branch history`);
const branch = await this.git.getBranch(repoPath);
if (!branch) return undefined;
if (branch === undefined) return undefined;
return commands.executeCommand(Commands.ShowQuickBranchHistory, uri, branch.name, undefined, goBackCommand);
return commands.executeCommand(Commands.ShowQuickBranchHistory,
uri,
{
branch: branch.name,
goBackCommand: args.goBackCommand
} as ShowQuickBranchHistoryCommandArgs);
}
catch (ex) {
Logger.error(ex, 'ShowQuickCurrentBranchHistoryCommand');

View File

@@ -1,52 +1,68 @@
'use strict';
import { commands, Range, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitService, GitUri, IGitLog } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, FileHistoryQuickPick } from '../quickPicks';
import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails';
import * as path from 'path';
export interface ShowQuickFileHistoryCommandArgs {
log?: IGitLog;
maxCount?: number;
range?: Range;
goBackCommand?: CommandQuickPickItem;
nextPageCommand?: CommandQuickPickItem;
}
export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
constructor(private git: GitService) {
super(Commands.ShowQuickFileHistory);
}
async execute(editor: TextEditor, uri?: Uri, range?: Range, maxCount?: number, goBackCommand?: CommandQuickPickItem, log?: IGitLog, nextPageCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
if (!uri) return commands.executeCommand(Commands.ShowQuickCurrentBranchHistory);
async execute(editor: TextEditor, uri?: Uri, args: ShowQuickFileHistoryCommandArgs = {}) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return commands.executeCommand(Commands.ShowQuickCurrentBranchHistory);
const gitUri = await GitUri.fromUri(uri, this.git);
if (maxCount == null) {
maxCount = this.git.config.advanced.maxQuickHistory;
if (args.maxCount == null) {
args.maxCount = this.git.config.advanced.maxQuickHistory;
}
const progressCancellation = FileHistoryQuickPick.showProgress(gitUri);
try {
if (!log) {
log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, gitUri.sha, maxCount, range);
if (!log) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
if (args.log === undefined) {
args.log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, gitUri.sha, args.maxCount, args.range);
if (args.log === undefined) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
}
if (progressCancellation.token.isCancellationRequested) return undefined;
const pick = await FileHistoryQuickPick.show(this.git, log, gitUri, progressCancellation, goBackCommand, nextPageCommand);
if (!pick) return undefined;
const pick = await FileHistoryQuickPick.show(this.git, args.log, gitUri, progressCancellation, args.goBackCommand, args.nextPageCommand);
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(pick.commit.fileName)}${gitUri.sha ? ` from \u00a0$(git-commit) ${gitUri.shortSha}` : ''}`
}, Commands.ShowQuickFileHistory, [uri, range, maxCount, goBackCommand, log]),
log);
// Create a command to get back to where we are right now
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(pick.commit.fileName)}${gitUri.sha ? ` from \u00a0$(git-commit) ${gitUri.shortSha}` : ''}`
}, Commands.ShowQuickFileHistory, [
uri,
args
]);
return commands.executeCommand(Commands.ShowQuickCommitFileDetails,
new GitUri(pick.commit.uri, pick.commit),
{
commit: pick.commit,
fileLog: args.log,
sha: pick.commit.sha,
goBackCommand: currentCommand
} as ShowQuickCommitFileDetailsCommandArgs);
}
catch (ex) {
Logger.error(ex, 'ShowQuickFileHistoryCommand');

View File

@@ -1,34 +1,34 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitService } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, RepoStatusQuickPick } from '../quickPicks';
export interface ShowQuickRepoStatusCommandArgs {
goBackCommand?: CommandQuickPickItem;
}
export class ShowQuickRepoStatusCommand extends ActiveEditorCachedCommand {
constructor(private git: GitService) {
super(Commands.ShowQuickRepoStatus);
}
async execute(editor: TextEditor, uri?: Uri, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: ShowQuickRepoStatusCommandArgs = {}) {
uri = getCommandUri(uri, editor);
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return window.showWarningMessage(`Unable to show repository status`);
const status = await this.git.getStatusForRepo(repoPath);
if (!status) return window.showWarningMessage(`Unable to show repository status`);
if (status === undefined) return window.showWarningMessage(`Unable to show repository status`);
const pick = await RepoStatusQuickPick.show(status, goBackCommand);
if (!pick) return undefined;
const pick = await RepoStatusQuickPick.show(status, args.goBackCommand);
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
return undefined;
}

View File

@@ -1,9 +1,14 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCachedCommand, Commands } from './common';
import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, StashListQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
export interface ShowQuickStashListCommandArgs {
goBackCommand?: CommandQuickPickItem;
}
export class ShowQuickStashListCommand extends ActiveEditorCachedCommand {
@@ -11,14 +16,12 @@ export class ShowQuickStashListCommand extends ActiveEditorCachedCommand {
super(Commands.ShowQuickStashList);
}
async execute(editor: TextEditor, uri?: Uri, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
async execute(editor: TextEditor, uri?: Uri, args: ShowQuickStashListCommandArgs = {}) {
uri = getCommandUri(uri, editor);
try {
const repoPath = await this.git.getRepoPathFromUri(uri);
if (repoPath === undefined) return window.showWarningMessage(`Unable to show stashed changes`);
if (!repoPath) return window.showWarningMessage(`Unable to show stashed changes`);
const stash = await this.git.getStashList(repoPath);
if (stash === undefined) return window.showWarningMessage(`Unable to show stashed changes`);
@@ -27,16 +30,25 @@ export class ShowQuickStashListCommand extends ActiveEditorCachedCommand {
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to stashed changes`
}, Commands.ShowQuickStashList, [uri, goBackCommand]);
}, Commands.ShowQuickStashList, [
uri,
{
goBackCommand: args.goBackCommand
} as ShowQuickStashListCommandArgs
]);
const pick = await StashListQuickPick.show(this.git, stash, 'list', goBackCommand, currentCommand);
if (!pick) return undefined;
const pick = await StashListQuickPick.show(this.git, stash, 'list', args.goBackCommand, currentCommand);
if (pick === undefined) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
if (pick instanceof CommandQuickPickItem) return pick.execute();
return commands.executeCommand(Commands.ShowQuickCommitDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit, currentCommand);
return commands.executeCommand(Commands.ShowQuickCommitDetails,
new GitUri(pick.commit.uri, pick.commit),
{
commit: pick.commit,
sha: pick.commit.sha,
goBackCommand: currentCommand
} as ShowQuickCommitDetailsCommandArgs);
}
catch (ex) {
Logger.error(ex, 'ShowQuickStashListCommand');

View File

@@ -6,42 +6,50 @@ import { CommitQuickPickItem, StashListQuickPick } from '../quickPicks';
import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks';
export interface StashApplyCommandArgs {
confirm?: boolean;
deleteAfter?: boolean;
stashItem?: { stashName: string, message: string };
goBackCommand?: CommandQuickPickItem;
}
export class StashApplyCommand extends Command {
constructor(private git: GitService) {
super(Commands.StashApply);
}
async execute(stashItem: { stashName: string, message: string }, confirm: boolean = true, deleteAfter: boolean = false, goBackCommand?: CommandQuickPickItem) {
async execute(args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) {
if (!this.git.config.insiders) return undefined;
if (!this.git.repoPath) return undefined;
if (!stashItem || !stashItem.stashName) {
if (args.stashItem === undefined || args.stashItem.stashName === undefined) {
const stash = await this.git.getStashList(this.git.repoPath);
if (!stash) return window.showInformationMessage(`There are no stashed changes`);
if (stash === undefined) return window.showInformationMessage(`There are no stashed changes`);
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to apply stashed changes`
}, Commands.StashApply, [stashItem, confirm, deleteAfter, goBackCommand]);
}, Commands.StashApply, [args]);
const pick = await StashListQuickPick.show(this.git, stash, 'apply', goBackCommand, currentCommand);
if (!pick || !(pick instanceof CommitQuickPickItem)) return goBackCommand && goBackCommand.execute();
const pick = await StashListQuickPick.show(this.git, stash, 'apply', args.goBackCommand, currentCommand);
if (pick === undefined || !(pick instanceof CommitQuickPickItem)) return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
goBackCommand = currentCommand;
stashItem = pick.commit as GitStashCommit;
args.goBackCommand = currentCommand;
args.stashItem = pick.commit as GitStashCommit;
}
try {
if (confirm) {
const message = stashItem.message.length > 80 ? `${stashItem.message.substring(0, 80)}\u2026` : stashItem.message;
if (args.confirm) {
const message = args.stashItem.message.length > 80 ? `${args.stashItem.message.substring(0, 80)}\u2026` : args.stashItem.message;
const result = await window.showWarningMessage(`Apply stashed changes '${message}' to your working tree?`, { title: 'Yes, delete after applying' } as MessageItem, { title: 'Yes' } as MessageItem, { title: 'No', isCloseAffordance: true } as MessageItem);
if (!result || result.title === 'No') return goBackCommand && goBackCommand.execute();
if (result === undefined || result.title === 'No') return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
deleteAfter = result.title !== 'Yes';
args.deleteAfter = result.title !== 'Yes';
}
return await this.git.stashApply(this.git.repoPath, stashItem.stashName, deleteAfter);
return await this.git.stashApply(this.git.repoPath, args.stashItem.stashName, args.deleteAfter);
}
catch (ex) {
Logger.error(ex, 'StashApplyCommand');

View File

@@ -5,25 +5,37 @@ import { Command, Commands } from './common';
import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks';
export interface StashDeleteCommandArgs {
confirm?: boolean;
stashItem?: { stashName: string, message: string };
goBackCommand?: CommandQuickPickItem;
}
export class StashDeleteCommand extends Command {
constructor(private git: GitService) {
super(Commands.StashDelete);
}
async execute(stashItem: { stashName: string, message: string }, confirm: boolean = true, goBackCommand?: CommandQuickPickItem) {
async execute(args: StashDeleteCommandArgs = { confirm: true }) {
if (!this.git.config.insiders) return undefined;
if (!this.git.repoPath) return undefined;
if (!stashItem || !stashItem.stashName) return undefined;
if (args.stashItem === undefined || args.stashItem.stashName === undefined) return undefined;
if (args.confirm === undefined) {
args.confirm = true;
}
try {
if (confirm) {
const message = stashItem.message.length > 80 ? `${stashItem.message.substring(0, 80)}\u2026` : stashItem.message;
if (args.confirm) {
const message = args.stashItem.message.length > 80 ? `${args.stashItem.message.substring(0, 80)}\u2026` : args.stashItem.message;
const result = await window.showWarningMessage(`Delete stashed changes '${message}'?`, { title: 'Yes' } as MessageItem, { title: 'No', isCloseAffordance: true } as MessageItem);
if (!result || result.title !== 'Yes') return goBackCommand && goBackCommand.execute();
if (result === undefined || result.title !== 'Yes') return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
}
return await this.git.stashDelete(this.git.repoPath, stashItem.stashName);
return await this.git.stashDelete(this.git.repoPath, args.stashItem.stashName);
}
catch (ex) {
Logger.error(ex, 'StashDeleteCommand');

View File

@@ -5,26 +5,37 @@ import { Command, Commands } from './common';
import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks';
export interface StashSaveCommandArgs {
message?: string;
unstagedOnly?: boolean;
goBackCommand?: CommandQuickPickItem;
}
export class StashSaveCommand extends Command {
constructor(private git: GitService) {
super(Commands.StashSave);
}
async execute(message?: string, unstagedOnly: boolean = false, goBackCommand?: CommandQuickPickItem) {
async execute(args: StashSaveCommandArgs = { unstagedOnly : false }) {
if (!this.git.config.insiders) return undefined;
if (!this.git.repoPath) return undefined;
if (args.unstagedOnly === undefined) {
args.unstagedOnly = false;
}
try {
if (message == null) {
message = await window.showInputBox({
if (args.message == null) {
args.message = await window.showInputBox({
prompt: `Please provide a stash message`,
placeHolder: `Stash message`
} as InputBoxOptions);
if (message === undefined) return goBackCommand && goBackCommand.execute();
if (args.message === undefined) return args.goBackCommand === undefined ? undefined : args.goBackCommand.execute();
}
return await this.git.stashSave(this.git.repoPath, message, unstagedOnly);
return await this.git.stashSave(this.git.repoPath, args.message, args.unstagedOnly);
}
catch (ex) {
Logger.error(ex, 'StashSaveCommand');

View File

@@ -4,21 +4,21 @@ import { BlameAnnotationController } from '../blameAnnotationController';
import { Commands, EditorCommand } from './common';
import { Logger } from '../logger';
export interface ToggleBlameCommandArgs {
sha?: string;
}
export class ToggleBlameCommand extends EditorCommand {
constructor(private annotationController: BlameAnnotationController) {
super(Commands.ToggleBlame);
}
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string): Promise<any> {
if (editor && editor.document && editor.document.isDirty) return undefined;
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, args: ToggleBlameCommandArgs = {}): Promise<any> {
if (editor !== undefined && editor.document !== undefined && editor.document.isDirty) return undefined;
try {
if (sha) {
return this.annotationController.toggleBlameAnnotation(editor, sha);
}
return this.annotationController.toggleBlameAnnotation(editor, editor.selection.active.line);
return this.annotationController.toggleBlameAnnotation(editor, args.sha !== undefined ? args.sha : editor.selection.active.line);
}
catch (ex) {
Logger.error(ex, 'ToggleBlameCommand');

View File

@@ -1,11 +1,16 @@
'use strict';
import { commands, Range, Uri } from 'vscode';
import { BuiltInCommands } from '../../constants';
import { GitLogCommit } from '../../gitService';
export type RemoteOpenType = 'branch' | 'commit' | 'file' | 'working-file';
export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'working-file';
export type RemoteResource = { type: 'branch', branch: string } |
{ type: 'commit', sha: string } |
{ type: 'file', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string } |
{ type: 'working-file', branch?: string, fileName: string, range?: Range };
export function getNameFromRemoteOpenType(type: RemoteOpenType) {
switch (type) {
export function getNameFromRemoteResource(resource: RemoteResource) {
switch (resource.type) {
case 'branch': return 'Branch';
case 'commit': return 'Commit';
case 'file': return 'File';
@@ -34,19 +39,16 @@ export abstract class RemoteProvider {
return commands.executeCommand(BuiltInCommands.Open, Uri.parse(url));
}
open(type: 'branch', branch: string): Promise<{}>;
open(type: 'commit', sha: string): Promise<{}>;
open(type: 'file', fileName: string, branch?: string, sha?: string, range?: Range): Promise<{}>;
open(type: RemoteOpenType, ...args: any[]): Promise<{}>;
open(type: RemoteOpenType, branchOrShaOrFileName: string, fileBranch?: string, fileSha?: string, fileRange?: Range): Promise<{}> {
switch (type) {
open(resource: RemoteResource): Promise<{}> {
switch (resource.type) {
case 'branch':
return this.openBranch(branchOrShaOrFileName);
return this.openBranch(resource.branch);
case 'commit':
return this.openCommit(branchOrShaOrFileName);
return this.openCommit(resource.sha);
case 'file':
return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range);
case 'working-file':
return this.openFile(branchOrShaOrFileName, fileBranch, fileSha, fileRange);
return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
}
}

View File

@@ -1,7 +1,7 @@
'use strict';
import { Functions, Iterables, Strings } from './system';
import { CancellationToken, CodeLens, CodeLensProvider, Command, commands, DocumentSelector, Event, EventEmitter, ExtensionContext, Position, Range, SymbolInformation, SymbolKind, TextDocument, Uri, workspace } from 'vscode';
import { Commands } from './commands';
import { Commands, DiffWithPreviousCommandArgs, ShowBlameHistoryCommandArgs, ShowFileHistoryCommandArgs, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from './commands';
import { BuiltInCommands, DocumentSchemes, ExtensionKey } from './constants';
import { CodeLensCommand, CodeLensLocation, IConfig, ICodeLensLanguageLocation } from './configuration';
import { GitCommit, GitService, GitUri, IGitBlame, IGitBlameLines } from './gitService';
@@ -321,7 +321,15 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = {
title: title,
command: Commands.ShowBlameHistory,
arguments: [Uri.file(lens.uri.fsPath), lens.blameRange, position, commit && commit.sha, line]
arguments: [
Uri.file(lens.uri.fsPath),
{
line,
position,
range: lens.blameRange,
sha: commit && commit.sha
} as ShowBlameHistoryCommandArgs
]
};
return lens;
}
@@ -339,7 +347,14 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = {
title: title,
command: Commands.ShowFileHistory,
arguments: [Uri.file(lens.uri.fsPath), position, commit && commit.sha, line]
arguments: [
Uri.file(lens.uri.fsPath),
{
line,
position,
sha: commit && commit.sha
} as ShowFileHistoryCommandArgs
]
};
return lens;
}
@@ -355,8 +370,10 @@ export class GitCodeLensProvider implements CodeLensProvider {
command: Commands.DiffWithPrevious,
arguments: [
Uri.file(lens.uri.fsPath),
commit,
lens.isFullRange ? undefined : lens.blameRange
{
commit: commit,
range: lens.isFullRange ? undefined : lens.blameRange
} as DiffWithPreviousCommandArgs
]
};
return lens;
@@ -366,7 +383,12 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickCommitDetails,
arguments: [Uri.file(lens.uri.fsPath), commit === undefined ? undefined : commit.sha, commit]
arguments: [
Uri.file(lens.uri.fsPath),
{
commit,
sha: commit === undefined ? undefined : commit.sha
} as ShowQuickCommitDetailsCommandArgs]
};
return lens;
}
@@ -375,7 +397,12 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickCommitFileDetails,
arguments: [Uri.file(lens.uri.fsPath), commit === undefined ? undefined : commit.sha, commit]
arguments: [
Uri.file(lens.uri.fsPath),
{
commit,
sha: commit === undefined ? undefined : commit.sha
} as ShowQuickCommitFileDetailsCommandArgs]
};
return lens;
}
@@ -384,7 +411,12 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickFileHistory,
arguments: [Uri.file(lens.uri.fsPath), lens.isFullRange ? undefined : lens.blameRange]
arguments: [
Uri.file(lens.uri.fsPath),
{
range: lens.isFullRange ? undefined : lens.blameRange
} as ShowQuickFileHistoryCommandArgs
]
};
return lens;
}

View File

@@ -1,7 +1,7 @@
'use strict';
// import { Iterables } from './system';
import { CancellationToken, CodeLens, CodeLensProvider, DocumentSelector, ExtensionContext, Range, TextDocument, Uri } from 'vscode';
import { Commands } from './commands';
import { Commands, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs } from './commands';
import { DocumentSchemes } from './constants';
import { GitCommit, GitService, GitUri } from './gitService';
@@ -55,8 +55,10 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
command: Commands.DiffWithWorking,
arguments: [
Uri.file(lens.fileName),
lens.commit,
lens.range.start.line
{
commit: lens.commit,
line: lens.range.start.line
} as DiffWithWorkingCommandArgs
]
};
return Promise.resolve(lens);
@@ -68,8 +70,10 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
command: Commands.DiffWithPrevious,
arguments: [
Uri.file(lens.fileName),
lens.commit,
lens.range.start.line
{
commit: lens.commit,
line: lens.range.start.line
} as DiffWithPreviousCommandArgs
]
};
return Promise.resolve(lens);

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
'use strict';
import { Arrays, Iterables } from '../system';
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand } from '../commands';
import { Commands, Keyboard, KeyNoopCommand, ShowCommitSearchCommandArgs, ShowQuickBranchHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './common';
import { GitService, GitUri, IGitLog } from '../gitService';
import { GitService, GitUri, IGitLog, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
export class BranchHistoryQuickPick {
@@ -23,17 +23,33 @@ export class BranchHistoryQuickPick {
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to \u00a0$(git-branch) ${branch} history`
}, Commands.ShowQuickBranchHistory, [uri, branch, log.maxCount, goBackCommand, log]);
}, Commands.ShowQuickBranchHistory, [
uri,
{
branch,
log,
maxCount: log.maxCount,
goBackCommand
} as ShowQuickBranchHistoryCommandArgs
]);
const remotes = Arrays.uniqueBy(await git.getRemotes((uri && uri.repoPath) || git.repoPath), _ => _.url, _ => !!_.provider);
if (remotes.length) {
items.splice(0, 0, new OpenRemotesCommandQuickPickItem(remotes, 'branch', branch, currentCommand));
items.splice(0, 0, new OpenRemotesCommandQuickPickItem(remotes, {
type: 'branch',
branch
} as RemoteResource, currentCommand));
}
items.splice(0, 0, new CommandQuickPickItem({
label: `$(search) Show Commit Search`,
description: `\u00a0 \u2014 \u00a0\u00a0 search for commits by message, author, files, or commit id`
}, Commands.ShowCommitSearch, [new GitUri(Uri.file(log.repoPath), { fileName: '', repoPath: log.repoPath }), undefined, undefined, currentCommand]));
}, Commands.ShowCommitSearch, [
new GitUri(Uri.file(log.repoPath), { fileName: '', repoPath: log.repoPath }),
{
goBackCommand: currentCommand
} as ShowCommitSearchCommandArgs
]));
let previousPageCommand: CommandQuickPickItem | undefined = undefined;
@@ -44,9 +60,11 @@ export class BranchHistoryQuickPick {
description: `\u00a0 \u2014 \u00a0\u00a0 this may take a while`
}, Commands.ShowQuickBranchHistory, [
new GitUri(Uri.file(log.repoPath), { fileName: '', repoPath: log.repoPath }),
branch,
0,
goBackCommand
{
branch,
maxCount: 0,
goBackCommand
} as ShowQuickBranchHistoryCommandArgs
]));
}
else {
@@ -55,9 +73,10 @@ export class BranchHistoryQuickPick {
description: `\u00a0 \u2014 \u00a0\u00a0 shows \u00a0$(git-branch) ${branch} history`
}, Commands.ShowQuickBranchHistory, [
new GitUri(Uri.file(log.repoPath), { fileName: '', repoPath: log.repoPath }),
branch,
undefined,
currentCommand
{
branch,
goBackCommand: currentCommand
} as ShowQuickBranchHistoryCommandArgs
]));
}
@@ -69,14 +88,29 @@ export class BranchHistoryQuickPick {
const npc = new CommandQuickPickItem({
label: `$(arrow-right) Show Next Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} newer commits`
}, Commands.ShowQuickBranchHistory, [uri, branch, log.maxCount, goBackCommand, undefined, nextPageCommand]);
}, Commands.ShowQuickBranchHistory, [
uri,
{
branch,
maxCount: log.maxCount,
nextPageCommand
} as ShowQuickBranchHistoryCommandArgs
]);
const last = Iterables.last(log.commits.values());
if (last != null) {
previousPageCommand = new CommandQuickPickItem({
label: `$(arrow-left) Show Previous Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} older commits`
}, Commands.ShowQuickBranchHistory, [new GitUri(uri ? uri : last.uri, last), branch, log.maxCount, goBackCommand, undefined, npc]);
}, Commands.ShowQuickBranchHistory, [
new GitUri(uri ? uri : last.uri, last),
{
branch,
maxCount: log.maxCount,
goBackCommand,
nextPageCommand: npc
} as ShowQuickBranchHistoryCommandArgs
]);
items.splice(0, 0, previousPageCommand);
}

View File

@@ -1,9 +1,9 @@
'use strict';
import { Arrays, Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand } from '../commands';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffDirectoryCommandCommandArgs, Keyboard, KeyNoopCommand, ShowQuickCommitDetailsCommandArgs, StashApplyCommandArgs, StashDeleteCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './common';
import { getGitStatusIcon, GitCommit, GitLogCommit, GitService, GitStashCommit, GitStatusFileStatus, GitUri, IGitLog, IGitStatusFile } from '../gitService';
import { getGitStatusIcon, GitCommit, GitLogCommit, GitService, GitStashCommit, GitStatusFileStatus, GitUri, IGitLog, IGitStatusFile, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment';
import * as path from 'path';
@@ -93,47 +93,93 @@ export class CommitDetailsQuickPick {
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(git-pull-request) Apply Stashed Changes`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.StashApply, [commit as GitStashCommit, true, false, currentCommand]));
}, Commands.StashApply, [
{
confirm: true,
deleteAfter: false,
stashItem: commit as GitStashCommit,
goBackCommand: currentCommand
} as StashApplyCommandArgs
]));
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(x) Delete Stashed Changes`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.StashDelete, [commit as GitStashCommit, true, currentCommand]));
}, Commands.StashDelete, [
{
confirm: true,
stashItem: commit as GitStashCommit,
goBackCommand: currentCommand
} as StashDeleteCommandArgs
]));
}
if (!stash) {
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit ID to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
}, Commands.CopyShaToClipboard, [
uri,
{
sha: commit.sha
} as CopyShaToClipboardCommandArgs
]));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
}, Commands.CopyMessageToClipboard, [
uri,
{
message: commit.message,
sha: commit.sha
} as CopyMessageToClipboardCommandArgs
]));
if (!stash) {
const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider);
if (remotes.length) {
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'commit', commit.sha, currentCommand));
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, {
type: 'commit',
sha: commit.sha
} as RemoteResource, currentCommand));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(git-compare) Directory Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousShortSha || `${commit.shortSha}^`} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.shortSha}`
}, Commands.DiffDirectory, [commit.uri, commit.previousSha || `${commit.sha}^`, commit.sha]));
}, Commands.DiffDirectory, [
commit.uri,
{
shaOrBranch1: commit.previousSha || `${commit.sha}^`,
shaOrBranch2: commit.sha
} as DiffDirectoryCommandCommandArgs
]));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(git-compare) Directory Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.shortSha} \u00a0 $(git-compare) \u00a0 $(file-directory) Working Tree`
}, Commands.DiffDirectory, [uri, commit.sha]));
}, Commands.DiffDirectory, [
uri,
{
shaOrBranch1: commit.sha
} as DiffDirectoryCommandCommandArgs
]));
items.splice(index++, 0, new CommandQuickPickItem({
label: `Changed Files`,
description: commit.getDiffStatus()
}, Commands.ShowQuickCommitDetails, [uri, commit.sha, commit, goBackCommand, repoLog]));
}, Commands.ShowQuickCommitDetails, [
uri,
{
commit,
repoLog,
sha: commit.sha,
goBackCommand
} as ShowQuickCommitDetailsCommandArgs
]));
items.push(new OpenCommitFilesCommandQuickPickItem(commit));
items.push(new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit));
@@ -146,9 +192,28 @@ export class CommitDetailsQuickPick {
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
if (!stash) {
// If we have the full history, we are good
if (repoLog && !repoLog.truncated && !repoLog.sha) {
previousCommand = commit.previousSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, repoLog]);
nextCommand = commit.nextSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, repoLog]);
if (repoLog !== undefined && !repoLog.truncated && repoLog.sha === undefined) {
previousCommand = commit.previousSha === undefined
? undefined
: new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [
commit.previousUri,
{
repoLog,
sha: commit.previousSha,
goBackCommand
} as ShowQuickCommitDetailsCommandArgs
]);
nextCommand = commit.nextSha === undefined
? undefined
: new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [
commit.nextUri,
{
repoLog,
sha: commit.nextSha,
goBackCommand
} as ShowQuickCommitDetailsCommandArgs
]);
}
else {
previousCommand = async () => {
@@ -156,7 +221,7 @@ export class CommitDetailsQuickPick {
let c = log && log.commits.get(commit.sha);
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
if (!c || !c.previousSha) {
if (c === undefined || c.previousSha === undefined) {
log = await git.getLogForRepo(commit.repoPath, commit.sha, git.config.advanced.maxQuickHistory);
c = log && log.commits.get(commit.sha);
@@ -165,8 +230,17 @@ export class CommitDetailsQuickPick {
c.nextSha = commit.nextSha;
}
}
if (!c || !c.previousSha) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
if (c === undefined || c.previousSha === undefined) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [
c.previousUri,
{
repoLog: log,
sha: c.previousSha,
goBackCommand
} as ShowQuickCommitDetailsCommandArgs
]);
};
nextCommand = async () => {
@@ -174,20 +248,29 @@ export class CommitDetailsQuickPick {
let c = log && log.commits.get(commit.sha);
// If we can't find the commit or the next commit isn't available (since it isn't trustworthy)
if (!c || !c.nextSha) {
if (c === undefined || c.nextSha === undefined) {
log = undefined;
c = undefined;
// Try to find the next commit
const nextLog = await git.getLogForRepo(commit.repoPath, commit.sha, 1, true);
const next = nextLog && Iterables.first(nextLog.commits.values());
if (next && next.sha !== commit.sha) {
if (next !== undefined && next.sha !== commit.sha) {
c = commit;
c.nextSha = next.sha;
}
}
if (!c || !c.nextSha) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
if (c === undefined || c.nextSha === undefined) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [
c.nextUri,
{
repoLog: log,
sha: c.nextSha,
goBackCommand
} as ShowQuickCommitDetailsCommandArgs
]);
};
}
}

View File

@@ -1,9 +1,9 @@
'use strict';
import { Arrays, Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand } from '../commands';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, Keyboard, KeyNoopCommand, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './common';
import { GitBranch, GitLogCommit, GitService, GitUri, IGitLog } from '../gitService';
import { GitBranch, GitLogCommit, GitService, GitUri, IGitLog, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment';
import * as path from 'path';
@@ -61,13 +61,25 @@ export class CommitFileDetailsQuickPick {
items.push(new CommandQuickPickItem({
label: `$(git-commit) Show Commit Details`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.shortSha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), commit.sha, commit, currentCommand]));
}, Commands.ShowQuickCommitDetails, [
new GitUri(commit.uri, commit),
{
commit,
sha: commit.sha,
goBackCommand: currentCommand
} as ShowQuickCommitDetailsCommandArgs
]));
if (commit.previousSha) {
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousShortSha} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.shortSha}`
}, Commands.DiffWithPrevious, [commit.uri, commit]));
}, Commands.DiffWithPrevious, [
commit.uri,
{
commit
} as DiffWithPreviousCommandArgs
]));
}
}
@@ -75,19 +87,35 @@ export class CommitFileDetailsQuickPick {
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.shortSha} \u00a0 $(git-compare) \u00a0 $(file-text) ${workingName}`
}, Commands.DiffWithWorking, [Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), commit]));
}, Commands.DiffWithWorking, [
Uri.file(path.resolve(commit.repoPath, commit.workingFileName)),
{
commit
} as DiffWithWorkingCommandArgs
]));
}
if (!stash) {
items.push(new CommandQuickPickItem({
label: `$(clippy) Copy Commit ID to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.shortSha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
}, Commands.CopyShaToClipboard, [
uri,
{
sha: commit.sha
} as CopyShaToClipboardCommandArgs
]));
items.push(new CommandQuickPickItem({
label: `$(clippy) Copy Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
}, Commands.CopyMessageToClipboard, [
uri,
{
message: commit.message,
sha: commit.sha
} as CopyMessageToClipboardCommandArgs
]));
}
items.push(new OpenCommitFileCommandQuickPickItem(commit));
@@ -98,11 +126,19 @@ export class CommitFileDetailsQuickPick {
const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider);
if (remotes.length) {
if (!stash) {
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'file', commit.fileName, undefined, commit, currentCommand));
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, 'working-file', commit.workingFileName, branch.name, undefined, currentCommand));
items.push(new OpenRemotesCommandQuickPickItem(remotes, {
type: 'working-file',
fileName: commit.workingFileName,
branch: branch.name
} as RemoteResource, currentCommand));
}
}
@@ -110,14 +146,25 @@ export class CommitFileDetailsQuickPick {
items.push(new CommandQuickPickItem({
label: `$(history) Show File History`,
description: `\u00a0 \u2014 \u00a0\u00a0 of ${path.basename(commit.fileName)}`
}, Commands.ShowQuickFileHistory, [Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), undefined, undefined, currentCommand, fileLog]));
}, Commands.ShowQuickFileHistory, [
Uri.file(path.resolve(commit.repoPath, commit.workingFileName)),
{
fileLog,
goBackCommand: currentCommand
} as ShowQuickFileHistoryCommandArgs
]));
}
if (!stash) {
items.push(new CommandQuickPickItem({
label: `$(history) Show ${commit.workingFileName ? 'Previous ' : ''}File History`,
description: `\u00a0 \u2014 \u00a0\u00a0 of ${path.basename(commit.fileName)} \u00a0\u2022\u00a0 from \u00a0$(git-commit) ${commit.shortSha}`
}, Commands.ShowQuickFileHistory, [new GitUri(commit.uri, commit), undefined, undefined, currentCommand]));
}, Commands.ShowQuickFileHistory, [
new GitUri(commit.uri, commit),
{
goBackCommand: currentCommand
} as ShowQuickFileHistoryCommandArgs
]));
}
if (goBackCommand) {
@@ -128,9 +175,28 @@ export class CommitFileDetailsQuickPick {
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
if (!stash) {
// If we have the full history, we are good
if (fileLog && !fileLog.truncated && !fileLog.sha) {
previousCommand = commit.previousSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, fileLog]);
nextCommand = commit.nextSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, fileLog]);
if (fileLog !== undefined && !fileLog.truncated && fileLog.sha === undefined) {
previousCommand = commit.previousSha === undefined
? undefined
: new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [
commit.previousUri,
{
fileLog,
sha: commit.previousSha,
goBackCommand
} as ShowQuickCommitFileDetailsCommandArgs
]);
nextCommand = commit.nextSha === undefined
? undefined
: new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [
commit.nextUri,
{
fileLog,
sha: commit.nextSha,
goBackCommand
} as ShowQuickCommitFileDetailsCommandArgs
]);
}
else {
previousCommand = async () => {
@@ -138,13 +204,13 @@ export class CommitFileDetailsQuickPick {
let c = log && log.commits.get(commit.sha);
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
if (!c || !c.previousSha) {
if (c === undefined || c.previousSha === undefined) {
log = await git.getLogForFile(commit.repoPath, uri.fsPath, commit.sha, git.config.advanced.maxQuickHistory);
if (log === undefined) return KeyNoopCommand;
c = log && log.commits.get(commit.sha);
// Since we exclude merge commits in file log, just grab the first returned commit
if (!c && commit.isMerge) {
if (c === undefined && commit.isMerge) {
c = Iterables.first(log.commits.values());
}
@@ -154,8 +220,17 @@ export class CommitFileDetailsQuickPick {
c.nextFileName = commit.nextFileName;
}
}
if (!c || !c.previousSha) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [c.previousUri, c.previousSha, undefined, goBackCommand, log]);
if (c === undefined || c.previousSha === undefined) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [
c.previousUri,
{
fileLog: log,
sha: c.previousSha,
goBackCommand
} as ShowQuickCommitFileDetailsCommandArgs
]);
};
nextCommand = async () => {
@@ -163,20 +238,29 @@ export class CommitFileDetailsQuickPick {
let c = log && log.commits.get(commit.sha);
// If we can't find the commit or the next commit isn't available (since it isn't trustworthy)
if (!c || !c.nextSha) {
if (c === undefined || c.nextSha === undefined) {
log = undefined;
c = undefined;
// Try to find the next commit
const next = await git.findNextCommit(commit.repoPath, uri.fsPath, commit.sha);
if (next && next.sha !== commit.sha) {
if (next !== undefined && next.sha !== commit.sha) {
c = commit;
c.nextSha = next.sha;
c.nextFileName = next.originalFileName || next.fileName;
}
}
if (!c || !c.nextSha) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [c.nextUri, c.nextSha, undefined, goBackCommand, log]);
if (c === undefined || c.nextSha === undefined) return KeyNoopCommand;
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [
c.nextUri,
{
fileLog: log,
sha: c.nextSha,
goBackCommand
} as ShowQuickCommitFileDetailsCommandArgs
]);
};
}
}

View File

@@ -1,6 +1,6 @@
'use strict';
import { CancellationTokenSource, commands, Disposable, QuickPickItem, QuickPickOptions, Uri, window, workspace } from 'vscode';
import { Commands, Keyboard, KeyboardScope, KeyMapping, openEditor } from '../commands';
import { CancellationTokenSource, commands, Disposable, QuickPickItem, QuickPickOptions, TextDocumentShowOptions, TextEditor, Uri, window, workspace } from 'vscode';
import { Commands, Keyboard, Keys, KeyboardScope, KeyMapping, openEditor } from '../commands';
import { IAdvancedConfig } from '../configuration';
import { ExtensionKey } from '../constants';
import { GitCommit, GitLogCommit, GitStashCommit } from '../gitService';
@@ -61,20 +61,45 @@ function _getInfiniteCancellablePromise(cancellation: CancellationTokenSource) {
});
}
export interface QuickPickItem extends QuickPickItem {
onDidSelect?(): void;
onDidPressKey?(key: Keys): Promise<{} | undefined>;
}
export class CommandQuickPickItem implements QuickPickItem {
label: string;
description: string;
detail?: string | undefined;
protected command: Commands | undefined;
protected args: any[] | undefined;
constructor(item: QuickPickItem, protected command: Commands | undefined, protected args?: any[]) {
constructor(item: QuickPickItem, args?: [Commands, any[]]);
constructor(item: QuickPickItem, command?: Commands, args?: any[]);
constructor(item: QuickPickItem, commandOrArgs?: Commands | [Commands, any[]], args?: any[]) {
if (commandOrArgs === undefined) {
this.command = undefined;
this.args = args;
}
else if (typeof commandOrArgs === 'string') {
this.command = commandOrArgs;
this.args = args;
}
else {
this.command = commandOrArgs[0];
this.args = commandOrArgs.slice(1);
}
Object.assign(this, item);
}
execute(): Thenable<{} | undefined> {
execute(): Promise<{} | undefined> {
if (this.command === undefined) return Promise.resolve(undefined);
return commands.executeCommand(this.command, ...(this.args || []));
return commands.executeCommand(this.command, ...(this.args || [])) as Promise<{} | undefined>;
}
onDidPressKey(key: Keys): Promise<{} | undefined> {
return this.execute();
}
}
@@ -91,12 +116,22 @@ export class OpenFileCommandQuickPickItem extends CommandQuickPickItem {
super(item, undefined, undefined);
}
async execute(pinned: boolean = false): Promise<{} | undefined> {
return this.open(pinned);
async execute(options?: TextDocumentShowOptions): Promise<TextEditor | undefined> {
return openEditor(this.uri, options);
}
async open(pinned: boolean = false): Promise<{} | undefined> {
return openEditor(this.uri, pinned);
onDidSelect(): Promise<{} | undefined> {
return this.execute({
preserveFocus: true,
preview: true
});
}
onDidPressKey(key: Keys): Promise<{} | undefined> {
return this.execute({
preserveFocus: true,
preview: false
});
}
}
@@ -106,12 +141,19 @@ export class OpenFilesCommandQuickPickItem extends CommandQuickPickItem {
super(item, undefined, undefined);
}
async execute(): Promise<{} | undefined> {
async execute(options: TextDocumentShowOptions = { preserveFocus: false, preview: false }): Promise<{} | undefined> {
for (const uri of this.uris) {
await openEditor(uri, true);
await openEditor(uri, options);
}
return undefined;
}
async onDidPressKey(key: Keys): Promise<{} | undefined> {
return this.execute({
preserveFocus: true,
preview: false
});
}
}
export class CommitQuickPickItem implements QuickPickItem {

View File

@@ -1,9 +1,9 @@
'use strict';
import { Arrays, Iterables } from '../system';
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand } from '../commands';
import { Commands, Keyboard, KeyNoopCommand, ShowQuickCurrentBranchHistoryCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, showQuickPickProgress } from './common';
import { GitService, GitUri, IGitLog } from '../gitService';
import { GitService, GitUri, IGitLog, RemoteResource } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as path from 'path';
@@ -30,7 +30,13 @@ export class FileHistoryQuickPick {
items.splice(0, 0, new CommandQuickPickItem({
label: `$(sync) Show All Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 this may take a while`
}, Commands.ShowQuickFileHistory, [Uri.file(uri.fsPath), undefined, 0, goBackCommand]));
}, Commands.ShowQuickFileHistory, [
Uri.file(uri.fsPath),
{
maxCount: 0,
goBackCommand
} as ShowQuickFileHistoryCommandArgs
]));
}
else {
const workingFileName = await git.findWorkingFileName(log.repoPath, path.relative(log.repoPath, uri.fsPath));
@@ -40,14 +46,22 @@ export class FileHistoryQuickPick {
label: `$(history) Show File History`,
description: `\u00a0 \u2014 \u00a0\u00a0 of ${path.basename(workingFileName)}`
}, Commands.ShowQuickFileHistory, [
Uri.file(path.resolve(log.repoPath, workingFileName)),
undefined,
undefined,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(uri.fsPath)}${uri.sha ? ` from \u00a0$(git-commit) ${uri.shortSha}` : ''}`
}, Commands.ShowQuickFileHistory, [uri, log.range, log.maxCount, goBackCommand, log])
]));
Uri.file(path.resolve(log.repoPath, workingFileName)),
{
goBackCommand: new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(uri.fsPath)}${uri.sha ? ` from \u00a0$(git-commit) ${uri.shortSha}` : ''}`
}, Commands.ShowQuickFileHistory, [
uri,
{
log: log,
maxCount: log.maxCount,
range: log.range,
goBackCommand
} as ShowQuickFileHistoryCommandArgs
])
} as ShowQuickFileHistoryCommandArgs
]));
}
}
@@ -60,14 +74,28 @@ export class FileHistoryQuickPick {
const npc = new CommandQuickPickItem({
label: `$(arrow-right) Show Next Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} newer commits`
}, Commands.ShowQuickFileHistory, [uri, undefined, log.maxCount, goBackCommand, undefined, nextPageCommand]);
}, Commands.ShowQuickFileHistory, [
uri,
{
maxCount: log.maxCount,
goBackCommand,
nextPageCommand
} as ShowQuickFileHistoryCommandArgs
]);
const last = Iterables.last(log.commits.values());
if (last != null) {
previousPageCommand = new CommandQuickPickItem({
label: `$(arrow-left) Show Previous Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} older commits`
}, Commands.ShowQuickFileHistory, [new GitUri(uri, last), undefined, log.maxCount, goBackCommand, undefined, npc]);
}, Commands.ShowQuickFileHistory, [
new GitUri(uri, last),
{
maxCount: log.maxCount,
goBackCommand,
nextPageCommand: npc
} as ShowQuickFileHistoryCommandArgs
]);
index++;
items.splice(0, 0, previousPageCommand);
@@ -80,23 +108,37 @@ export class FileHistoryQuickPick {
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(uri.fsPath)}${uri.sha ? ` from \u00a0$(git-commit) ${uri.shortSha}` : ''}`
}, Commands.ShowQuickFileHistory, [uri, log.range, log.maxCount, undefined, log]);
}, Commands.ShowQuickFileHistory, [
uri,
{
log,
maxCount: log.maxCount,
range: log.range
} as ShowQuickFileHistoryCommandArgs
]);
// Only show the full repo option if we are the root
if (!goBackCommand) {
if (goBackCommand === undefined) {
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(history) Show Branch History`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows \u00a0$(git-branch) ${branch!.name} history`
}, Commands.ShowQuickCurrentBranchHistory,
[
undefined,
currentCommand
{
goBackCommand: currentCommand
} as ShowQuickCurrentBranchHistoryCommandArgs
]));
}
const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath!), _ => _.url, _ => !!_.provider);
if (remotes.length) {
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'file', uri.getRelativePath(), branch!.name, uri.sha, currentCommand));
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, {
type: 'file',
branch: branch!.name,
fileName: uri.getRelativePath(),
sha: uri.sha
} as RemoteResource, currentCommand));
}
if (goBackCommand) {

View File

@@ -1,88 +1,66 @@
'use strict';
import { QuickPickOptions, window } from 'vscode';
import { Commands } from '../commands';
import { Commands, OpenInRemoteCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './common';
import { getNameFromRemoteOpenType, GitLogCommit, GitRemote, RemoteOpenType } from '../gitService';
import { getNameFromRemoteResource, GitLogCommit, GitRemote, RemoteResource } from '../gitService';
import * as path from 'path';
export class OpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
private remote: GitRemote;
private type: RemoteOpenType;
private resource: RemoteResource;
constructor(remote: GitRemote, type: RemoteOpenType, ...args: string[]) {
constructor(remote: GitRemote, resource: RemoteResource) {
super({
label: `$(link-external) Open ${getNameFromRemoteOpenType(type)} in ${remote.provider!.name}`,
label: `$(link-external) Open ${getNameFromRemoteResource(resource)} in ${remote.provider!.name}`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(repo) ${remote.provider!.path}`
}, undefined, undefined);
this.remote = remote;
this.type = type;
this.args = args;
this.resource = resource;
}
async execute(): Promise<{}> {
return this.remote.provider!.open(this.type, ...this.args!);
return this.remote.provider!.open(this.resource);
}
}
export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
constructor(remotes: GitRemote[], type: 'branch', branch: string, goBackCommand?: CommandQuickPickItem);
constructor(remotes: GitRemote[], type: 'commit', sha: string, goBackCommand?: CommandQuickPickItem);
constructor(remotes: GitRemote[], type: 'file', fileName: string, branch?: string, commit?: GitLogCommit, goBackCommand?: CommandQuickPickItem);
constructor(remotes: GitRemote[], type: 'file' | 'working-file', fileName: string, branch?: string, sha?: string, goBackCommand?: CommandQuickPickItem);
constructor(remotes: GitRemote[], type: RemoteOpenType, branchOrShaOrFileName: string, goBackCommandOrFileBranch?: CommandQuickPickItem | string, fileShaOrCommit?: string | GitLogCommit, goBackCommand?: CommandQuickPickItem) {
let fileBranch: string | undefined = undefined;
if (typeof goBackCommandOrFileBranch === 'string') {
fileBranch = goBackCommandOrFileBranch;
}
else if (!goBackCommand) {
goBackCommand = goBackCommandOrFileBranch;
}
constructor(remotes: GitRemote[], resource: RemoteResource, goBackCommand?: CommandQuickPickItem) {
const name = getNameFromRemoteResource(resource);
const name = getNameFromRemoteOpenType(type);
let fileSha: string | undefined = undefined;
let description: string | undefined = undefined;
let placeHolder: string | undefined = undefined;
switch (type) {
let description: string = '';
switch (resource.type) {
case 'branch':
description = `$(git-branch) ${branchOrShaOrFileName}`;
placeHolder = `open ${branchOrShaOrFileName} ${name.toLowerCase()} in\u2026`;
description = `$(git-branch) ${resource.branch}`;
break;
case 'commit':
const shortSha = branchOrShaOrFileName.substring(0, 8);
const shortSha = resource.sha.substring(0, 8);
description = `$(git-commit) ${shortSha}`;
placeHolder = `open ${name.toLowerCase()} ${shortSha} in\u2026`;
break;
case 'file':
case 'working-file':
const fileName = path.basename(branchOrShaOrFileName);
if (fileShaOrCommit instanceof GitLogCommit) {
if (fileShaOrCommit.status === 'D') {
fileSha = fileShaOrCommit.previousSha;
description = `$(file-text) ${fileName} in \u00a0$(git-commit) ${fileShaOrCommit.previousShortSha} (deleted in \u00a0$(git-commit) ${fileShaOrCommit.shortSha})`;
placeHolder = `open ${branchOrShaOrFileName} \u00a0\u2022\u00a0 ${fileShaOrCommit.previousShortSha} in\u2026`;
case 'file':
if (resource.commit !== undefined && resource.commit instanceof GitLogCommit) {
if (resource.commit.status === 'D') {
resource.sha = resource.commit.previousSha;
description = `$(file-text) ${path.basename(resource.fileName)} in \u00a0$(git-commit) ${resource.commit.previousShortSha} (deleted in \u00a0$(git-commit) ${resource.commit.shortSha})`;
}
else {
fileSha = fileShaOrCommit.sha;
description = `$(file-text) ${fileName} in \u00a0$(git-commit) ${fileShaOrCommit.shortSha}`;
placeHolder = `open ${branchOrShaOrFileName} \u00a0\u2022\u00a0 ${fileShaOrCommit.shortSha} in\u2026`;
resource.sha = resource.commit.sha;
description = `$(file-text) ${path.basename(resource.fileName)} in \u00a0$(git-commit) ${resource.commit.shortSha}`;
}
}
else {
fileSha = fileShaOrCommit;
const shortFileSha = (fileSha && fileSha.substring(0, 8)) || '';
const shaSuffix = shortFileSha ? ` \u00a0\u2022\u00a0 ${shortFileSha}` : '';
description = `$(file-text) ${fileName}${shortFileSha ? ` in \u00a0$(git-commit) ${shortFileSha}` : ''}`;
placeHolder = `open ${branchOrShaOrFileName}${shaSuffix} in\u2026`;
const shortFileSha = resource.sha === undefined ? '' : resource.sha.substring(0, 8);
description = `$(file-text) ${path.basename(resource.fileName)}${shortFileSha ? ` in \u00a0$(git-commit) ${shortFileSha}` : ''}`;
}
break;
case 'working-file':
description = `$(file-text) ${path.basename(resource.fileName)}`;
break;
}
const remote = remotes[0];
@@ -90,7 +68,14 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
super({
label: `$(link-external) Open ${name} in ${remote.provider!.name}`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(repo) ${remote.provider!.path} \u00a0\u2022\u00a0 ${description}`
}, Commands.OpenInRemote, [undefined, remotes, type, [branchOrShaOrFileName, fileBranch, fileSha], goBackCommand]);
}, Commands.OpenInRemote, [
undefined,
{
remotes,
resource,
goBackCommand
} as OpenInRemoteCommandArgs
]);
return;
}
@@ -102,15 +87,21 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
super({
label: `$(link-external) Open ${name} in ${provider}\u2026`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${description}`
}, Commands.OpenInRemote, [undefined, remotes, type, [branchOrShaOrFileName, fileBranch, fileSha], goBackCommand]);
}, Commands.OpenInRemote, [
undefined,
{
remotes,
resource,
goBackCommand
} as OpenInRemoteCommandArgs
]);
}
}
export class RemotesQuickPick {
static async show(remotes: GitRemote[], placeHolder: string, type: RemoteOpenType, args: string[], goBackCommand?: CommandQuickPickItem): Promise<OpenRemoteCommandQuickPickItem | CommandQuickPickItem | undefined> {
const items = remotes.map(_ => new OpenRemoteCommandQuickPickItem(_, type, ...args)) as (OpenRemoteCommandQuickPickItem | CommandQuickPickItem)[];
static async show(remotes: GitRemote[], placeHolder: string, resource: RemoteResource, goBackCommand?: CommandQuickPickItem): Promise<OpenRemoteCommandQuickPickItem | CommandQuickPickItem | undefined> {
const items = remotes.map(_ => new OpenRemoteCommandQuickPickItem(_, resource)) as (OpenRemoteCommandQuickPickItem | CommandQuickPickItem)[];
if (goBackCommand) {
items.splice(0, 0, goBackCommand);
@@ -118,12 +109,11 @@ export class RemotesQuickPick {
// const scope = await Keyboard.instance.beginScope({ left: goBackCommand });
const pick = await window.showQuickPick(items,
{
placeHolder: placeHolder,
ignoreFocusOut: getQuickPickIgnoreFocusOut()
} as QuickPickOptions);
if (!pick) return undefined;
const pick = await window.showQuickPick(items, {
placeHolder: placeHolder,
ignoreFocusOut: getQuickPickIgnoreFocusOut()
} as QuickPickOptions);
if (pick === undefined) return undefined;
// await scope.dispose();

View File

@@ -1,7 +1,7 @@
'use strict';
import { Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard } from '../commands';
import { Commands, Keyboard, OpenChangedFilesCommandArgs, ShowQuickBranchHistoryCommandArgs, ShowQuickRepoStatusCommandArgs, ShowQuickStashListCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem } from './common';
import { GitService, GitStatusFile, GitUri, IGitStatus } from '../gitService';
import * as path from 'path';
@@ -36,7 +36,12 @@ export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem {
label: `$(file-symlink-file) Open Changed Files`,
description: ''
//detail: `Opens all of the changed files in the repository`
}, Commands.OpenChangedFiles, [undefined, uris]);
}, Commands.OpenChangedFiles, [
undefined,
{
uris
} as OpenChangedFilesCommandArgs
]);
}
}
@@ -72,7 +77,12 @@ export class RepoStatusQuickPick {
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to \u00a0$(git-branch) ${status.branch} status`
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]);
}, Commands.ShowQuickRepoStatus, [
undefined,
{
goBackCommand
} as ShowQuickRepoStatusCommandArgs
]);
if (hasStaged) {
let index = 0;
@@ -81,7 +91,12 @@ export class RepoStatusQuickPick {
items.splice(unstagedIndex, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: unstagedStatus
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]));
}, Commands.ShowQuickRepoStatus, [
undefined,
{
goBackCommand
} as ShowQuickRepoStatusCommandArgs
]));
items.splice(unstagedIndex, 0, new OpenStatusFilesCommandQuickPickItem(files.filter(_ => _.status !== 'D' && _.staged), {
label: `\u00a0\u00a0\u00a0\u00a0 $(file-symlink-file) Open Staged Files`,
@@ -97,13 +112,23 @@ export class RepoStatusQuickPick {
items.splice(index++, 0, new CommandQuickPickItem({
label: `Staged Files`,
description: stagedStatus
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]));
}, Commands.ShowQuickRepoStatus, [
undefined,
{
goBackCommand
} as ShowQuickRepoStatusCommandArgs
]));
}
else if (files.some(_ => !_.staged)) {
items.splice(0, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: unstagedStatus
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]));
}, Commands.ShowQuickRepoStatus, [
undefined,
{
goBackCommand
} as ShowQuickRepoStatusCommandArgs
]));
}
if (files.length) {
@@ -117,13 +142,23 @@ export class RepoStatusQuickPick {
items.push(new CommandQuickPickItem({
label: `No changes in the working tree`,
description: ''
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]));
}, Commands.ShowQuickRepoStatus, [
undefined,
{
goBackCommand
} as ShowQuickRepoStatusCommandArgs
]));
}
items.splice(0, 0, new CommandQuickPickItem({
label: `$(repo-push) Show Stashed Changes`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows stashed changes in the repository`
}, Commands.ShowQuickStashList, [new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath }), currentCommand]));
}, Commands.ShowQuickStashList, [
new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath }),
{
goBackCommand: currentCommand
} as ShowQuickStashListCommandArgs
]));
if (status.upstream && status.state.ahead) {
items.splice(0, 0, new CommandQuickPickItem({
@@ -131,9 +166,12 @@ export class RepoStatusQuickPick {
description: `\u00a0 \u2014 \u00a0\u00a0 shows commits in \u00a0$(git-branch) ${status.branch} but not \u00a0$(git-branch) ${status.upstream}`
}, Commands.ShowQuickBranchHistory, [
new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath, sha: `${status.upstream}..${status.branch}` }),
status.branch, 0, currentCommand
])
);
{
branch: status.branch,
maxCount: 0,
goBackCommand: currentCommand
} as ShowQuickBranchHistoryCommandArgs
]));
}
if (status.upstream && status.state.behind) {
@@ -142,16 +180,24 @@ export class RepoStatusQuickPick {
description: `\u00a0 \u2014 \u00a0\u00a0 shows commits in \u00a0$(git-branch) ${status.upstream} but not \u00a0$(git-branch) ${status.branch}${status.sha ? ` (since \u00a0$(git-commit) ${status.sha.substring(0, 8)})` : ''}`
}, Commands.ShowQuickBranchHistory, [
new GitUri(Uri.file(status.repoPath), { fileName: '', repoPath: status.repoPath, sha: `${status.branch}..${status.upstream}` }),
status.upstream, 0, currentCommand
])
);
{
branch: status.upstream,
maxCount: 0,
goBackCommand: currentCommand
} as ShowQuickBranchHistoryCommandArgs
]));
}
if (status.upstream && !status.state.ahead && !status.state.behind) {
items.splice(0, 0, new CommandQuickPickItem({
label: `$(git-branch) ${status.branch} is up-to-date with \u00a0$(git-branch) ${status.upstream}`,
description: ''
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]));
}, Commands.ShowQuickRepoStatus, [
undefined,
{
goBackCommand
} as ShowQuickRepoStatusCommandArgs
]));
}

View File

@@ -1,7 +1,7 @@
'use strict';
import { Iterables } from '../system';
import { QuickPickOptions, window } from 'vscode';
import { Commands, Keyboard } from '../commands';
import { Commands, Keyboard, StashSaveCommandArgs } from '../commands';
import { GitService, IGitStash } from '../gitService';
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut } from '../quickPicks';
@@ -14,12 +14,22 @@ export class StashListQuickPick {
items.splice(0, 0, new CommandQuickPickItem({
label: `$(repo-push) Stash Unstaged Changes`,
description: `\u00a0 \u2014 \u00a0\u00a0 stashes only unstaged changes`
}, Commands.StashSave, [undefined, true, currentCommand]));
}, Commands.StashSave, [
{
unstagedOnly: true,
goBackCommand: currentCommand
} as StashSaveCommandArgs
]));
items.splice(0, 0, new CommandQuickPickItem({
label: `$(repo-push) Stash Changes`,
description: `\u00a0 \u2014 \u00a0\u00a0 stashes all changes`
}, Commands.StashSave, [undefined, undefined, currentCommand]));
}, Commands.StashSave, [
{
unstagedOnly: false,
goBackCommand: currentCommand
} as StashSaveCommandArgs
]));
}
if (goBackCommand) {