@ -1,145 +1,148 @@ | |||
'use strict'; | |||
import { Strings } from '../system'; | |||
import { commands, TextEditor, Uri, window } from 'vscode'; | |||
import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common'; | |||
import { GlyphChars } from '../constants'; | |||
import { GitCommit, GitLog, GitLogCommit, GitUri } from '../gitService'; | |||
import { Logger } from '../logger'; | |||
import { CommandQuickPickItem, CommitQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks/quickPicks'; | |||
import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails'; | |||
import { Messages } from '../messages'; | |||
import * as path from 'path'; | |||
import { Container } from '../container'; | |||
export interface ShowQuickCommitDetailsCommandArgs { | |||
sha?: string; | |||
commit?: GitCommit | GitLogCommit; | |||
repoLog?: GitLog; | |||
goBackCommand?: CommandQuickPickItem; | |||
} | |||
export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand { | |||
static getMarkdownCommandArgs(sha: string): string; | |||
static getMarkdownCommandArgs(args: ShowQuickCommitDetailsCommandArgs): string; | |||
static getMarkdownCommandArgs(argsOrSha: ShowQuickCommitDetailsCommandArgs | string): string { | |||
const args = typeof argsOrSha === 'string' | |||
? { sha: argsOrSha } | |||
: argsOrSha; | |||
return super.getMarkdownCommandArgsCore<ShowQuickCommitDetailsCommandArgs>(Commands.ShowQuickCommitDetails, args); | |||
} | |||
constructor() { | |||
super(Commands.ShowQuickCommitDetails); | |||
} | |||
protected async preExecute(context: CommandContext, args: ShowQuickCommitDetailsCommandArgs = {}): Promise<any> { | |||
if (context.type === 'view') { | |||
args = { ...args }; | |||
args.sha = context.node.uri.sha; | |||
if (isCommandViewContextWithCommit(context)) { | |||
args.commit = context.node.commit; | |||
} | |||
} | |||
return this.execute(context.editor, context.uri, args); | |||
} | |||
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitDetailsCommandArgs = {}) { | |||
uri = getCommandUri(uri, editor); | |||
if (uri === undefined) return undefined; | |||
const gitUri = await GitUri.fromUri(uri); | |||
let repoPath = gitUri.repoPath; | |||
let workingFileName = path.relative(repoPath || '', gitUri.fsPath); | |||
args = { ...args }; | |||
if (args.sha === undefined) { | |||
if (editor === undefined) return undefined; | |||
const blameline = editor.selection.active.line; | |||
if (blameline < 0) return undefined; | |||
try { | |||
const blame = await Container.git.getBlameForLine(gitUri, blameline); | |||
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show commit details'); | |||
// Because the previous sha of an uncommitted file isn't trust worthy we just have to kick out | |||
if (blame.commit.isUncommitted) return Messages.showLineUncommittedWarningMessage('Unable to show commit details'); | |||
args.sha = blame.commit.sha; | |||
repoPath = blame.commit.repoPath; | |||
workingFileName = blame.commit.fileName; | |||
args.commit = blame.commit; | |||
} | |||
catch (ex) { | |||
Logger.error(ex, 'ShowQuickCommitDetailsCommand', `getBlameForLine(${blameline})`); | |||
return window.showErrorMessage(`Unable to show commit details. See output channel for more details`); | |||
} | |||
} | |||
try { | |||
if (args.commit === undefined || args.commit.isFile) { | |||
if (args.repoLog !== undefined) { | |||
args.commit = args.repoLog.commits.get(args.sha!); | |||
// If we can't find the commit, kill the repoLog | |||
if (args.commit === undefined) { | |||
args.repoLog = undefined; | |||
} | |||
} | |||
if (args.repoLog === undefined) { | |||
const log = await Container.git.getLog(repoPath!, { maxCount: 2, ref: args.sha }); | |||
if (log === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit details`); | |||
args.commit = log.commits.get(args.sha!); | |||
} | |||
} | |||
if (args.commit === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit details`); | |||
if (args.commit.workingFileName === undefined) { | |||
args.commit.workingFileName = workingFileName; | |||
} | |||
if (args.goBackCommand === undefined) { | |||
// Create a command to get back to the branch history | |||
args.goBackCommand = new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to branch history` | |||
}, Commands.ShowQuickCurrentBranchHistory, [ | |||
args.commit.toGitUri() | |||
]); | |||
} | |||
// Create a command to get back to where we are right now | |||
const currentCommand = new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to details of ${GlyphChars.Space}$(git-commit) ${args.commit.shortSha}` | |||
}, Commands.ShowQuickCommitDetails, [ | |||
args.commit.toGitUri(), | |||
args | |||
]); | |||
const pick = await CommitQuickPick.show(args.commit as GitLogCommit, uri, args.goBackCommand, currentCommand, args.repoLog); | |||
if (pick === undefined) return undefined; | |||
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) return pick.execute(); | |||
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, | |||
pick.commit.toGitUri(), | |||
{ | |||
commit: pick.commit, | |||
sha: pick.sha, | |||
goBackCommand: currentCommand | |||
} as ShowQuickCommitFileDetailsCommandArgs); | |||
} | |||
catch (ex) { | |||
Logger.error(ex, 'ShowQuickCommitDetailsCommand'); | |||
return window.showErrorMessage(`Unable to show commit details. See output channel for more details`); | |||
} | |||
} | |||
'use strict'; | |||
import { Strings } from '../system'; | |||
import { commands, TextEditor, Uri, window } from 'vscode'; | |||
import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common'; | |||
import { GlyphChars } from '../constants'; | |||
import { GitCommit, GitLog, GitLogCommit, GitUri } from '../gitService'; | |||
import { Logger } from '../logger'; | |||
import { CommandQuickPickItem, CommitQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks/quickPicks'; | |||
import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails'; | |||
import { Messages } from '../messages'; | |||
import * as path from 'path'; | |||
import { Container } from '../container'; | |||
export interface ShowQuickCommitDetailsCommandArgs { | |||
sha?: string; | |||
commit?: GitCommit | GitLogCommit; | |||
repoLog?: GitLog; | |||
goBackCommand?: CommandQuickPickItem; | |||
} | |||
export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand { | |||
static getMarkdownCommandArgs(sha: string): string; | |||
static getMarkdownCommandArgs(args: ShowQuickCommitDetailsCommandArgs): string; | |||
static getMarkdownCommandArgs(argsOrSha: ShowQuickCommitDetailsCommandArgs | string): string { | |||
const args = typeof argsOrSha === 'string' | |||
? { sha: argsOrSha } | |||
: argsOrSha; | |||
return super.getMarkdownCommandArgsCore<ShowQuickCommitDetailsCommandArgs>(Commands.ShowQuickCommitDetails, args); | |||
} | |||
constructor() { | |||
super(Commands.ShowQuickCommitDetails); | |||
} | |||
protected async preExecute(context: CommandContext, args: ShowQuickCommitDetailsCommandArgs = {}): Promise<any> { | |||
if (context.type === 'view') { | |||
args = { ...args }; | |||
args.sha = context.node.uri.sha; | |||
if (isCommandViewContextWithCommit(context)) { | |||
args.commit = context.node.commit; | |||
} | |||
} | |||
return this.execute(context.editor, context.uri, args); | |||
} | |||
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitDetailsCommandArgs = {}) { | |||
uri = getCommandUri(uri, editor); | |||
if (uri === undefined) return undefined; | |||
const gitUri = await GitUri.fromUri(uri); | |||
let repoPath = gitUri.repoPath; | |||
let workingFileName = path.relative(repoPath || '', gitUri.fsPath); | |||
args = { ...args }; | |||
if (args.sha === undefined) { | |||
if (editor === undefined) return undefined; | |||
const blameline = editor.selection.active.line; | |||
if (blameline < 0) return undefined; | |||
try { | |||
const blame = await Container.git.getBlameForLine(gitUri, blameline); | |||
if (blame === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show commit details'); | |||
// Because the previous sha of an uncommitted file isn't trust worthy we just have to kick out | |||
if (blame.commit.isUncommitted) return Messages.showLineUncommittedWarningMessage('Unable to show commit details'); | |||
args.sha = blame.commit.sha; | |||
repoPath = blame.commit.repoPath; | |||
workingFileName = blame.commit.fileName; | |||
args.commit = blame.commit; | |||
} | |||
catch (ex) { | |||
Logger.error(ex, 'ShowQuickCommitDetailsCommand', `getBlameForLine(${blameline})`); | |||
return window.showErrorMessage(`Unable to show commit details. See output channel for more details`); | |||
} | |||
} | |||
try { | |||
if (args.commit === undefined || args.commit.isFile) { | |||
if (args.repoLog !== undefined) { | |||
args.commit = args.repoLog.commits.get(args.sha!); | |||
// If we can't find the commit, kill the repoLog | |||
if (args.commit === undefined) { | |||
args.repoLog = undefined; | |||
} | |||
} | |||
if (args.repoLog === undefined) { | |||
const log = await Container.git.getLog(repoPath!, { maxCount: 2, ref: args.sha }); | |||
if (log === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit details`); | |||
args.commit = log.commits.get(args.sha!); | |||
} | |||
} | |||
if (args.commit === undefined) return Messages.showCommitNotFoundWarningMessage(`Unable to show commit details`); | |||
if (args.commit.workingFileName === undefined) { | |||
args.commit.workingFileName = workingFileName; | |||
} | |||
if (args.goBackCommand === undefined) { | |||
const branch = await Container.git.getBranch(args.commit.repoPath); | |||
if (branch !== undefined) { | |||
// Create a command to get back to the branch history | |||
args.goBackCommand = new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to ${branch.name} history` | |||
}, Commands.ShowQuickCurrentBranchHistory, [ | |||
args.commit.toGitUri() | |||
]); | |||
} | |||
} | |||
// Create a command to get back to where we are right now | |||
const currentCommand = new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to details of ${GlyphChars.Space}$(git-commit) ${args.commit.shortSha}` | |||
}, Commands.ShowQuickCommitDetails, [ | |||
args.commit.toGitUri(), | |||
args | |||
]); | |||
const pick = await CommitQuickPick.show(args.commit as GitLogCommit, uri, args.goBackCommand, currentCommand, args.repoLog); | |||
if (pick === undefined) return undefined; | |||
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) return pick.execute(); | |||
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, | |||
pick.commit.toGitUri(), | |||
{ | |||
commit: pick.commit, | |||
sha: pick.sha, | |||
goBackCommand: currentCommand | |||
} as ShowQuickCommitFileDetailsCommandArgs); | |||
} | |||
catch (ex) { | |||
Logger.error(ex, 'ShowQuickCommitDetailsCommand'); | |||
return window.showErrorMessage(`Unable to show commit details. See output channel for more details`); | |||
} | |||
} | |||
} |
@ -1,324 +1,326 @@ | |||
'use strict'; | |||
import { Iterables, Strings } from '../system'; | |||
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; | |||
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands'; | |||
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './commonQuickPicks'; | |||
import { GlyphChars } from '../constants'; | |||
import { Container } from '../container'; | |||
import { GitLog, GitLogCommit, GitUri, RemoteResource } from '../gitService'; | |||
import { KeyCommand, KeyNoopCommand } from '../keyboard'; | |||
import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; | |||
import * as path from 'path'; | |||
export class ApplyCommitFileChangesCommandQuickPickItem extends CommandQuickPickItem { | |||
constructor( | |||
private readonly commit: GitLogCommit, | |||
item?: QuickPickItem | |||
) { | |||
super(item || { | |||
label: `$(git-pull-request) Apply Changes`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(file-text) ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}` | |||
}, undefined, undefined); | |||
} | |||
async execute(): Promise<{} | undefined> { | |||
const uri = this.commit.toGitUri(); | |||
await Container.git.checkoutFile(uri); | |||
return openEditor(uri, { preserveFocus: true, preview: false }); | |||
} | |||
} | |||
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { | |||
constructor( | |||
commit: GitLogCommit, | |||
item?: QuickPickItem | |||
) { | |||
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName)); | |||
super(uri, item || { | |||
label: `$(file-symlink-file) Open File`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}` | |||
}); | |||
} | |||
} | |||
export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQuickPickItem { | |||
constructor( | |||
commit: GitLogCommit, | |||
item?: QuickPickItem | |||
) { | |||
let description: string; | |||
let uri: Uri; | |||
if (commit.status === 'D') { | |||
uri = GitUri.toRevisionUri(commit.previousFileSha, commit.previousUri.fsPath, commit.repoPath); | |||
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.previousShortSha} (deleted in ${GlyphChars.Space}$(git-commit) ${commit.shortSha})`; | |||
} | |||
else { | |||
uri = GitUri.toRevisionUri(commit.sha, commit.uri.fsPath, commit.repoPath); | |||
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`; | |||
} | |||
super(uri, item || { | |||
label: `$(file-symlink-file) Open Revision`, | |||
description: description | |||
}); | |||
} | |||
} | |||
export class CommitFileQuickPick { | |||
static async show(commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: GitLog): Promise<CommandQuickPickItem | undefined> { | |||
const items: CommandQuickPickItem[] = []; | |||
const stash = commit.isStash; | |||
const workingName = (commit.workingFileName && path.basename(commit.workingFileName)) || path.basename(commit.fileName); | |||
const isUncommitted = commit.isUncommitted; | |||
if (isUncommitted) { | |||
// Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file | |||
const c = await Container.git.getRecentLogCommitForFile(undefined, commit.uri.fsPath); | |||
if (c === undefined) return undefined; | |||
commit = c; | |||
} | |||
await commit.resolvePreviousFileSha(); | |||
if (stash) { | |||
items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); | |||
} | |||
if (commit.previousFileShortSha) { | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(git-compare) Open Changes`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousFileShortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}` | |||
}, Commands.DiffWithPrevious, [ | |||
commit.uri, | |||
{ | |||
commit | |||
} as DiffWithPreviousCommandArgs | |||
]) | |||
); | |||
} | |||
if (commit.workingFileName) { | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(git-compare) Open Changes with Working Tree`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}` | |||
}, Commands.DiffWithWorking, [ | |||
Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), | |||
{ | |||
commit | |||
} as DiffWithWorkingCommandArgs | |||
]) | |||
); | |||
} | |||
if (commit.workingFileName && commit.status !== 'D') { | |||
items.push(new OpenCommitFileCommandQuickPickItem(commit)); | |||
} | |||
items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit)); | |||
const remotes = await Container.git.getRemotes(commit.repoPath); | |||
if (remotes.length) { | |||
if (commit.workingFileName && commit.status !== 'D') { | |||
const branch = await Container.git.getBranch(commit.repoPath); | |||
items.push(new OpenRemotesCommandQuickPickItem(remotes, { | |||
type: 'file', | |||
fileName: commit.workingFileName, | |||
branch: branch!.name | |||
} as RemoteResource, currentCommand)); | |||
} | |||
if (!stash) { | |||
items.push(new OpenRemotesCommandQuickPickItem(remotes, { | |||
type: 'revision', | |||
fileName: commit.fileName, | |||
commit | |||
} as RemoteResource, currentCommand)); | |||
} | |||
} | |||
if (!stash) { | |||
items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(clippy) Copy Commit ID to Clipboard`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.shortSha}` | |||
}, Commands.CopyShaToClipboard, [ | |||
uri, | |||
{ | |||
sha: commit.sha | |||
} as CopyShaToClipboardCommandArgs | |||
]) | |||
); | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(clippy) Copy Commit Message to Clipboard`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage(`${GlyphChars.Space}$(ellipsis)`)}` | |||
}, Commands.CopyMessageToClipboard, [ | |||
uri, | |||
{ | |||
message: commit.message, | |||
sha: commit.sha | |||
} as CopyMessageToClipboardCommandArgs | |||
])); | |||
} | |||
if (commit.workingFileName) { | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(history) Show File History`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(commit.fileName)}` | |||
}, 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: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(commit.fileName)} ${Strings.pad(GlyphChars.Dot, 1, 1)} from ${GlyphChars.Space}$(git-commit) ${commit.shortSha}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
commit.toGitUri(), | |||
{ | |||
goBackCommand: currentCommand | |||
} as ShowQuickFileHistoryCommandArgs | |||
])); | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(git-commit) Show Commit Details`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha}` | |||
}, Commands.ShowQuickCommitDetails, [ | |||
commit.toGitUri(), | |||
{ | |||
commit, | |||
sha: commit.sha, | |||
goBackCommand: currentCommand | |||
} as ShowQuickCommitDetailsCommandArgs | |||
]) | |||
); | |||
} | |||
if (goBackCommand) { | |||
items.splice(0, 0, goBackCommand); | |||
} | |||
let previousCommand: KeyCommand | (() => Promise<KeyCommand>) | undefined = undefined; | |||
let nextCommand: KeyCommand | (() => Promise<KeyCommand>) | undefined = undefined; | |||
if (!stash) { | |||
// If we have the full history, we are good | |||
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 () => { | |||
let log = fileLog; | |||
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 === undefined || c.previousSha === undefined) { | |||
log = await Container.git.getLogForFile(commit.repoPath, uri.fsPath, { maxCount: Container.config.advanced.maxListItems, ref: commit.sha, renames: true }); | |||
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 === undefined && commit.isMerge) { | |||
c = Iterables.first(log.commits.values()); | |||
} | |||
if (c) { | |||
// Copy over next info, since it is trustworthy at this point | |||
c.nextSha = commit.nextSha; | |||
c.nextFileName = commit.nextFileName; | |||
} | |||
} | |||
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 () => { | |||
let log = fileLog; | |||
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 === undefined || c.nextSha === undefined) { | |||
log = undefined; | |||
c = undefined; | |||
// Try to find the next commit | |||
const next = await Container.git.findNextCommit(commit.repoPath, uri.fsPath, commit.sha); | |||
if (next !== undefined && next.sha !== commit.sha) { | |||
c = commit; | |||
c.nextSha = next.sha; | |||
c.nextFileName = next.originalFileName || next.fileName; | |||
} | |||
} | |||
if (c === undefined || c.nextSha === undefined) return KeyNoopCommand; | |||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ | |||
c.nextUri, | |||
{ | |||
fileLog: log, | |||
sha: c.nextSha, | |||
goBackCommand | |||
} as ShowQuickCommitFileDetailsCommandArgs | |||
]); | |||
}; | |||
} | |||
} | |||
const scope = await Container.keyboard.beginScope({ | |||
left: goBackCommand, | |||
',': previousCommand, | |||
'.': nextCommand | |||
}); | |||
const pick = await window.showQuickPick(items, { | |||
matchOnDescription: true, | |||
placeHolder: `${commit.getFormattedPath()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${isUncommitted ? `Uncommitted ${GlyphChars.ArrowRightHollow} ` : ''}${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author}, ${commit.formattedDate} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage(`${GlyphChars.Space}$(ellipsis)`)}`, | |||
ignoreFocusOut: getQuickPickIgnoreFocusOut(), | |||
onDidSelectItem: (item: QuickPickItem) => { | |||
scope.setKeyCommand('right', item as KeyCommand); | |||
} | |||
} as QuickPickOptions); | |||
await scope.dispose(); | |||
return pick; | |||
} | |||
'use strict'; | |||
import { Iterables, Strings } from '../system'; | |||
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; | |||
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, ShowQuickCommitDetailsCommandArgs, ShowQuickCommitFileDetailsCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands'; | |||
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './commonQuickPicks'; | |||
import { GlyphChars } from '../constants'; | |||
import { Container } from '../container'; | |||
import { GitLog, GitLogCommit, GitUri, RemoteResource } from '../gitService'; | |||
import { KeyCommand, KeyNoopCommand } from '../keyboard'; | |||
import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; | |||
import * as path from 'path'; | |||
export class ApplyCommitFileChangesCommandQuickPickItem extends CommandQuickPickItem { | |||
constructor( | |||
private readonly commit: GitLogCommit, | |||
item?: QuickPickItem | |||
) { | |||
super(item || { | |||
label: `$(git-pull-request) Apply Changes`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(file-text) ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}` | |||
}, undefined, undefined); | |||
} | |||
async execute(): Promise<{} | undefined> { | |||
const uri = this.commit.toGitUri(); | |||
await Container.git.checkoutFile(uri); | |||
return openEditor(uri, { preserveFocus: true, preview: false }); | |||
} | |||
} | |||
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem { | |||
constructor( | |||
commit: GitLogCommit, | |||
item?: QuickPickItem | |||
) { | |||
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName)); | |||
super(uri, item || { | |||
label: `$(file-symlink-file) Open File`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}` | |||
}); | |||
} | |||
} | |||
export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQuickPickItem { | |||
constructor( | |||
commit: GitLogCommit, | |||
item?: QuickPickItem | |||
) { | |||
let description: string; | |||
let uri: Uri; | |||
if (commit.status === 'D') { | |||
uri = GitUri.toRevisionUri(commit.previousFileSha, commit.previousUri.fsPath, commit.repoPath); | |||
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.previousShortSha} (deleted in ${GlyphChars.Space}$(git-commit) ${commit.shortSha})`; | |||
} | |||
else { | |||
uri = GitUri.toRevisionUri(commit.sha, commit.uri.fsPath, commit.repoPath); | |||
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`; | |||
} | |||
super(uri, item || { | |||
label: `$(file-symlink-file) Open Revision`, | |||
description: description | |||
}); | |||
} | |||
} | |||
export class CommitFileQuickPick { | |||
static async show(commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: GitLog): Promise<CommandQuickPickItem | undefined> { | |||
const items: CommandQuickPickItem[] = []; | |||
const stash = commit.isStash; | |||
const workingName = (commit.workingFileName && path.basename(commit.workingFileName)) || path.basename(commit.fileName); | |||
const isUncommitted = commit.isUncommitted; | |||
if (isUncommitted) { | |||
// Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file | |||
const c = await Container.git.getRecentLogCommitForFile(undefined, commit.uri.fsPath); | |||
if (c === undefined) return undefined; | |||
commit = c; | |||
} | |||
await commit.resolvePreviousFileSha(); | |||
if (stash) { | |||
items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); | |||
} | |||
if (commit.previousFileShortSha) { | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(git-compare) Open Changes`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousFileShortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}` | |||
}, Commands.DiffWithPrevious, [ | |||
commit.uri, | |||
{ | |||
commit | |||
} as DiffWithPreviousCommandArgs | |||
]) | |||
); | |||
} | |||
if (commit.workingFileName) { | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(git-compare) Open Changes with Working Tree`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}` | |||
}, Commands.DiffWithWorking, [ | |||
Uri.file(path.resolve(commit.repoPath, commit.workingFileName)), | |||
{ | |||
commit | |||
} as DiffWithWorkingCommandArgs | |||
]) | |||
); | |||
} | |||
if (commit.workingFileName && commit.status !== 'D') { | |||
items.push(new OpenCommitFileCommandQuickPickItem(commit)); | |||
} | |||
items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit)); | |||
const remotes = await Container.git.getRemotes(commit.repoPath); | |||
if (remotes.length) { | |||
if (commit.workingFileName && commit.status !== 'D') { | |||
const branch = await Container.git.getBranch(commit.repoPath); | |||
if (branch !== undefined) { | |||
items.push(new OpenRemotesCommandQuickPickItem(remotes, { | |||
type: 'file', | |||
fileName: commit.workingFileName, | |||
branch: branch.name | |||
} as RemoteResource, currentCommand)); | |||
} | |||
} | |||
if (!stash) { | |||
items.push(new OpenRemotesCommandQuickPickItem(remotes, { | |||
type: 'revision', | |||
fileName: commit.fileName, | |||
commit | |||
} as RemoteResource, currentCommand)); | |||
} | |||
} | |||
if (!stash) { | |||
items.push(new ApplyCommitFileChangesCommandQuickPickItem(commit)); | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(clippy) Copy Commit ID to Clipboard`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.shortSha}` | |||
}, Commands.CopyShaToClipboard, [ | |||
uri, | |||
{ | |||
sha: commit.sha | |||
} as CopyShaToClipboardCommandArgs | |||
]) | |||
); | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(clippy) Copy Commit Message to Clipboard`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${commit.getShortMessage(`${GlyphChars.Space}$(ellipsis)`)}` | |||
}, Commands.CopyMessageToClipboard, [ | |||
uri, | |||
{ | |||
message: commit.message, | |||
sha: commit.sha | |||
} as CopyMessageToClipboardCommandArgs | |||
])); | |||
} | |||
if (commit.workingFileName) { | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(history) Show File History`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(commit.fileName)}` | |||
}, 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: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(commit.fileName)} ${Strings.pad(GlyphChars.Dot, 1, 1)} from ${GlyphChars.Space}$(git-commit) ${commit.shortSha}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
commit.toGitUri(), | |||
{ | |||
goBackCommand: currentCommand | |||
} as ShowQuickFileHistoryCommandArgs | |||
])); | |||
items.push(new CommandQuickPickItem({ | |||
label: `$(git-commit) Show Commit Details`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha}` | |||
}, Commands.ShowQuickCommitDetails, [ | |||
commit.toGitUri(), | |||
{ | |||
commit, | |||
sha: commit.sha, | |||
goBackCommand: currentCommand | |||
} as ShowQuickCommitDetailsCommandArgs | |||
]) | |||
); | |||
} | |||
if (goBackCommand) { | |||
items.splice(0, 0, goBackCommand); | |||
} | |||
let previousCommand: KeyCommand | (() => Promise<KeyCommand>) | undefined = undefined; | |||
let nextCommand: KeyCommand | (() => Promise<KeyCommand>) | undefined = undefined; | |||
if (!stash) { | |||
// If we have the full history, we are good | |||
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 () => { | |||
let log = fileLog; | |||
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 === undefined || c.previousSha === undefined) { | |||
log = await Container.git.getLogForFile(commit.repoPath, uri.fsPath, { maxCount: Container.config.advanced.maxListItems, ref: commit.sha, renames: true }); | |||
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 === undefined && commit.isMerge) { | |||
c = Iterables.first(log.commits.values()); | |||
} | |||
if (c) { | |||
// Copy over next info, since it is trustworthy at this point | |||
c.nextSha = commit.nextSha; | |||
c.nextFileName = commit.nextFileName; | |||
} | |||
} | |||
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 () => { | |||
let log = fileLog; | |||
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 === undefined || c.nextSha === undefined) { | |||
log = undefined; | |||
c = undefined; | |||
// Try to find the next commit | |||
const next = await Container.git.findNextCommit(commit.repoPath, uri.fsPath, commit.sha); | |||
if (next !== undefined && next.sha !== commit.sha) { | |||
c = commit; | |||
c.nextSha = next.sha; | |||
c.nextFileName = next.originalFileName || next.fileName; | |||
} | |||
} | |||
if (c === undefined || c.nextSha === undefined) return KeyNoopCommand; | |||
return new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [ | |||
c.nextUri, | |||
{ | |||
fileLog: log, | |||
sha: c.nextSha, | |||
goBackCommand | |||
} as ShowQuickCommitFileDetailsCommandArgs | |||
]); | |||
}; | |||
} | |||
} | |||
const scope = await Container.keyboard.beginScope({ | |||
left: goBackCommand, | |||
',': previousCommand, | |||
'.': nextCommand | |||
}); | |||
const pick = await window.showQuickPick(items, { | |||
matchOnDescription: true, | |||
placeHolder: `${commit.getFormattedPath()} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${isUncommitted ? `Uncommitted ${GlyphChars.ArrowRightHollow} ` : ''}${commit.shortSha} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.author}, ${commit.formattedDate} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${commit.getShortMessage(`${GlyphChars.Space}$(ellipsis)`)}`, | |||
ignoreFocusOut: getQuickPickIgnoreFocusOut(), | |||
onDidSelectItem: (item: QuickPickItem) => { | |||
scope.setKeyCommand('right', item as KeyCommand); | |||
} | |||
} as QuickPickOptions); | |||
await scope.dispose(); | |||
return pick; | |||
} | |||
} |
@ -1,159 +1,161 @@ | |||
'use strict'; | |||
import { Iterables, Strings } from '../system'; | |||
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode'; | |||
import { Commands, ShowQuickCurrentBranchHistoryCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands'; | |||
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, ShowBranchesAndTagsQuickPickItem, showQuickPickProgress } from './commonQuickPicks'; | |||
import { GlyphChars } from '../constants'; | |||
import { Container } from '../container'; | |||
import { GitLog, GitUri, RemoteResource } from '../gitService'; | |||
import { KeyNoopCommand } from '../keyboard'; | |||
import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; | |||
import * as path from 'path'; | |||
export class FileHistoryQuickPick { | |||
static showProgress(placeHolder: string) { | |||
return showQuickPickProgress(placeHolder, | |||
{ | |||
left: KeyNoopCommand, | |||
',': KeyNoopCommand, | |||
'.': KeyNoopCommand | |||
}); | |||
} | |||
static async show(log: GitLog, uri: GitUri, placeHolder: string, options: { currentCommand?: CommandQuickPickItem, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem, previousPageCommand?: CommandQuickPickItem, pickerOnly?: boolean, progressCancellation?: CancellationTokenSource, showAllCommand?: CommandQuickPickItem, showInResultsExplorerCommand?: CommandQuickPickItem } = {}): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> { | |||
options = { pickerOnly: false, ...options }; | |||
const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[]; | |||
let index = 0; | |||
if (options.pickerOnly) { | |||
index++; | |||
items.splice(0, 0, new ShowBranchesAndTagsQuickPickItem(log.repoPath, placeHolder, options.currentCommand)); | |||
} | |||
if (options.showInResultsExplorerCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.showInResultsExplorerCommand); | |||
} | |||
if (log.truncated || log.sha) { | |||
if (options.showAllCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.showAllCommand); | |||
} | |||
else if (!options.pickerOnly) { | |||
const [workingFileName] = await Container.git.findWorkingFileName(path.relative(log.repoPath, uri.fsPath), log.repoPath); | |||
if (workingFileName) { | |||
index++; | |||
items.splice(0, 0, new CommandQuickPickItem({ | |||
label: `$(history) Show File History`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(workingFileName)}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
Uri.file(path.resolve(log.repoPath, workingFileName)), | |||
{ | |||
goBackCommand: new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${GlyphChars.Space}$(file-text) ${path.basename(uri.fsPath)}${uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : ''}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
uri, | |||
{ | |||
log: log, | |||
maxCount: log.maxCount, | |||
range: log.range, | |||
goBackCommand: options.goBackCommand | |||
} as ShowQuickFileHistoryCommandArgs | |||
]) | |||
} as ShowQuickFileHistoryCommandArgs | |||
])); | |||
} | |||
} | |||
if (options.nextPageCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.nextPageCommand); | |||
} | |||
if (options.previousPageCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.previousPageCommand); | |||
} | |||
} | |||
if (!options.pickerOnly) { | |||
const branch = await Container.git.getBranch(uri.repoPath!); | |||
const currentCommand = new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${GlyphChars.Space}$(file-text) ${path.basename(uri.fsPath)}${uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : ''}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
uri, | |||
{ | |||
log, | |||
maxCount: log.maxCount, | |||
range: log.range | |||
} as ShowQuickFileHistoryCommandArgs | |||
]); | |||
// Only show the full repo option if we are the root | |||
if (options.goBackCommand === undefined) { | |||
items.splice(index++, 0, new CommandQuickPickItem({ | |||
label: `$(history) Show Branch History`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${GlyphChars.Space}$(git-branch) ${branch!.name} history` | |||
}, Commands.ShowQuickCurrentBranchHistory, | |||
[ | |||
undefined, | |||
{ | |||
goBackCommand: currentCommand | |||
} as ShowQuickCurrentBranchHistoryCommandArgs | |||
])); | |||
} | |||
const remotes = await Container.git.getRemotes(uri.repoPath!); | |||
if (remotes.length) { | |||
const resource = uri.sha !== undefined | |||
? { | |||
type: 'revision', | |||
branch: branch!.name, | |||
fileName: uri.getRelativePath(), | |||
sha: uri.sha | |||
} as RemoteResource | |||
: { | |||
type: 'file', | |||
branch: branch!.name, | |||
fileName: uri.getRelativePath() | |||
} as RemoteResource; | |||
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, resource, currentCommand)); | |||
} | |||
if (options.goBackCommand) { | |||
items.splice(0, 0, options.goBackCommand); | |||
} | |||
} | |||
if (options.progressCancellation !== undefined && options.progressCancellation.token.isCancellationRequested) return undefined; | |||
const scope = await Container.keyboard.beginScope({ | |||
left: options.goBackCommand, | |||
',': options.previousPageCommand, | |||
'.': options.nextPageCommand | |||
}); | |||
options.progressCancellation && options.progressCancellation.cancel(); | |||
const pick = await window.showQuickPick(items, { | |||
matchOnDescription: true, | |||
matchOnDetail: true, | |||
placeHolder: placeHolder, | |||
ignoreFocusOut: getQuickPickIgnoreFocusOut() | |||
// onDidSelectItem: (item: QuickPickItem) => { | |||
// scope.setKeyCommand('right', item); | |||
// } | |||
} as QuickPickOptions); | |||
await scope.dispose(); | |||
return pick; | |||
} | |||
'use strict'; | |||
import { Iterables, Strings } from '../system'; | |||
import { CancellationTokenSource, QuickPickOptions, Uri, window } from 'vscode'; | |||
import { Commands, ShowQuickCurrentBranchHistoryCommandArgs, ShowQuickFileHistoryCommandArgs } from '../commands'; | |||
import { CommandQuickPickItem, CommitQuickPickItem, getQuickPickIgnoreFocusOut, ShowBranchesAndTagsQuickPickItem, showQuickPickProgress } from './commonQuickPicks'; | |||
import { GlyphChars } from '../constants'; | |||
import { Container } from '../container'; | |||
import { GitLog, GitUri, RemoteResource } from '../gitService'; | |||
import { KeyNoopCommand } from '../keyboard'; | |||
import { OpenRemotesCommandQuickPickItem } from './remotesQuickPick'; | |||
import * as path from 'path'; | |||
export class FileHistoryQuickPick { | |||
static showProgress(placeHolder: string) { | |||
return showQuickPickProgress(placeHolder, | |||
{ | |||
left: KeyNoopCommand, | |||
',': KeyNoopCommand, | |||
'.': KeyNoopCommand | |||
}); | |||
} | |||
static async show(log: GitLog, uri: GitUri, placeHolder: string, options: { currentCommand?: CommandQuickPickItem, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem, previousPageCommand?: CommandQuickPickItem, pickerOnly?: boolean, progressCancellation?: CancellationTokenSource, showAllCommand?: CommandQuickPickItem, showInResultsExplorerCommand?: CommandQuickPickItem } = {}): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> { | |||
options = { pickerOnly: false, ...options }; | |||
const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[]; | |||
let index = 0; | |||
if (options.pickerOnly) { | |||
index++; | |||
items.splice(0, 0, new ShowBranchesAndTagsQuickPickItem(log.repoPath, placeHolder, options.currentCommand)); | |||
} | |||
if (options.showInResultsExplorerCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.showInResultsExplorerCommand); | |||
} | |||
if (log.truncated || log.sha) { | |||
if (options.showAllCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.showAllCommand); | |||
} | |||
else if (!options.pickerOnly) { | |||
const [workingFileName] = await Container.git.findWorkingFileName(path.relative(log.repoPath, uri.fsPath), log.repoPath); | |||
if (workingFileName) { | |||
index++; | |||
items.splice(0, 0, new CommandQuickPickItem({ | |||
label: `$(history) Show File History`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} of ${path.basename(workingFileName)}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
Uri.file(path.resolve(log.repoPath, workingFileName)), | |||
{ | |||
goBackCommand: new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${GlyphChars.Space}$(file-text) ${path.basename(uri.fsPath)}${uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : ''}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
uri, | |||
{ | |||
log: log, | |||
maxCount: log.maxCount, | |||
range: log.range, | |||
goBackCommand: options.goBackCommand | |||
} as ShowQuickFileHistoryCommandArgs | |||
]) | |||
} as ShowQuickFileHistoryCommandArgs | |||
])); | |||
} | |||
} | |||
if (options.nextPageCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.nextPageCommand); | |||
} | |||
if (options.previousPageCommand !== undefined) { | |||
index++; | |||
items.splice(0, 0, options.previousPageCommand); | |||
} | |||
} | |||
if (!options.pickerOnly) { | |||
const branch = await Container.git.getBranch(uri.repoPath!); | |||
if (branch !== undefined) { | |||
const currentCommand = new CommandQuickPickItem({ | |||
label: `go back ${GlyphChars.ArrowBack}`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${GlyphChars.Space}$(file-text) ${path.basename(uri.fsPath)}${uri.sha ? ` from ${GlyphChars.Space}$(git-commit) ${uri.shortSha}` : ''}` | |||
}, Commands.ShowQuickFileHistory, [ | |||
uri, | |||
{ | |||
log, | |||
maxCount: log.maxCount, | |||
range: log.range | |||
} as ShowQuickFileHistoryCommandArgs | |||
]); | |||
// Only show the full repo option if we are the root | |||
if (options.goBackCommand === undefined) { | |||
items.splice(index++, 0, new CommandQuickPickItem({ | |||
label: `$(history) Show Branch History`, | |||
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${GlyphChars.Space}$(git-branch) ${branch.name} history` | |||
}, Commands.ShowQuickCurrentBranchHistory, | |||
[ | |||
undefined, | |||
{ | |||
goBackCommand: currentCommand | |||
} as ShowQuickCurrentBranchHistoryCommandArgs | |||
])); | |||
} | |||
const remotes = await Container.git.getRemotes(uri.repoPath!); | |||
if (remotes.length) { | |||
const resource = uri.sha !== undefined | |||
? { | |||
type: 'revision', | |||
branch: branch.name, | |||
fileName: uri.getRelativePath(), | |||
sha: uri.sha | |||
} as RemoteResource | |||
: { | |||
type: 'file', | |||
branch: branch.name, | |||
fileName: uri.getRelativePath() | |||
} as RemoteResource; | |||
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, resource, currentCommand)); | |||
} | |||
} | |||
if (options.goBackCommand) { | |||
items.splice(0, 0, options.goBackCommand); | |||
} | |||
} | |||
if (options.progressCancellation !== undefined && options.progressCancellation.token.isCancellationRequested) return undefined; | |||
const scope = await Container.keyboard.beginScope({ | |||
left: options.goBackCommand, | |||
',': options.previousPageCommand, | |||
'.': options.nextPageCommand | |||
}); | |||
options.progressCancellation && options.progressCancellation.cancel(); | |||
const pick = await window.showQuickPick(items, { | |||
matchOnDescription: true, | |||
matchOnDetail: true, | |||
placeHolder: placeHolder, | |||
ignoreFocusOut: getQuickPickIgnoreFocusOut() | |||
// onDidSelectItem: (item: QuickPickItem) => { | |||
// scope.setKeyCommand('right', item); | |||
// } | |||
} as QuickPickOptions); | |||
await scope.dispose(); | |||
return pick; | |||
} | |||
} |