diff --git a/CHANGELOG.md b/CHANGELOG.md index d236016..f144d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] ### Added - Adds `Open Working File` command (`gitlens.openWorkingFile`) - opens the working file for the active file revision -- closes [#236](https://github.com/eamodio/vscode-gitlens/issues/236) +- Adds `Open Revision...` command (`gitlens.openFileRevision`) - opens the selected revision for the active file + +### Fixed +- Fixes issues with commit paging in certain quick pick menus ## [7.1.0-beta] - 2017-12-20 ### Changed diff --git a/README.md b/README.md index b4754bb..259be92 100644 --- a/README.md +++ b/README.md @@ -332,6 +332,8 @@ While GitLens is highly customizable and provides many [configuration settings]( - Adds a `Open Working File"` command (`gitlens.openWorkingFile`) to open the working file for the active file revision +- Adds a `Open Revision...` command (`gitlens.openFileRevision`) to open the selected revision for the active file + - Adds a `Open Changes (with difftool)` command (`gitlens.externalDiff`) to the source control group and source control resource context menus to open the changes of a file or set of files with the configured git difftool - Adds a `Open All Changes (with difftool)` command (`gitlens.externalDiffAll`) to open all working changes with the configured git difftool diff --git a/package.json b/package.json index d019909..c268242 100644 --- a/package.json +++ b/package.json @@ -1323,6 +1323,11 @@ "category": "GitLens" }, { + "command": "gitlens.openFileRevision", + "title": "Open Revision...", + "category": "GitLens" + }, + { "command": "gitlens.openRepoInRemote", "title": "Open Repository in Remote", "category": "GitLens" @@ -1738,6 +1743,10 @@ "when": "gitlens:activeIsTracked && gitlens:activeHasRemote" }, { + "command": "gitlens.openFileRevision", + "when": "gitlens:activeIsTracked" + }, + { "command": "gitlens.openRepoInRemote", "when": "gitlens:activeHasRemote" }, diff --git a/src/commands.ts b/src/commands.ts index 85e7456..c6c8931 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -79,7 +79,7 @@ export function configureCommands( context.subscriptions.push(new Commands.OpenBranchInRemoteCommand(git)); context.subscriptions.push(new Commands.OpenCommitInRemoteCommand(git)); context.subscriptions.push(new Commands.OpenFileInRemoteCommand(git)); - context.subscriptions.push(new Commands.OpenFileRevisionCommand(annotationController)); + context.subscriptions.push(new Commands.OpenFileRevisionCommand(annotationController, git)); context.subscriptions.push(new Commands.OpenInRemoteCommand()); context.subscriptions.push(new Commands.OpenRepoInRemoteCommand(git)); context.subscriptions.push(new Commands.OpenWorkingFileCommand(annotationController, git)); diff --git a/src/commands/diffWithRevision.ts b/src/commands/diffWithRevision.ts index ef99435..1c2ebf7 100644 --- a/src/commands/diffWithRevision.ts +++ b/src/commands/diffWithRevision.ts @@ -1,5 +1,5 @@ 'use strict'; -import { Strings } from '../system'; +import { Iterables, Strings } from '../system'; import { commands, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode'; import { ActiveEditorCommand, Commands, getCommandUri } from './common'; import { GlyphChars } from '../constants'; @@ -14,6 +14,7 @@ export interface DiffWithRevisionCommandArgs { line?: number; showOptions?: TextDocumentShowOptions; + nextPageCommand?: CommandQuickPickItem; } export class DiffWithRevisionCommand extends ActiveEditorCommand { @@ -42,14 +43,33 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand { if (progressCancellation.token.isCancellationRequested) return undefined; + let previousPageCommand: CommandQuickPickItem | undefined = undefined; + + if (log.truncated) { + const npc = new CommandQuickPickItem({ + label: `$(arrow-right) Show Next Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} newer commits` + }, Commands.DiffWithRevision, [uri, { ...args } as DiffWithRevisionCommandArgs]); + + const last = Iterables.last(log.commits.values()); + if (last != null) { + previousPageCommand = new CommandQuickPickItem({ + label: `$(arrow-left) Show Previous Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} older commits` + }, Commands.DiffWithRevision, [new GitUri(uri, last), { ...args, nextPageCommand: npc } as DiffWithRevisionCommandArgs]); + } + } + const label = `${gitUri.getFormattedPath()}${gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : ''}`; const pick = await FileHistoryQuickPick.show(this.git, log, gitUri, label, progressCancellation, { pickerOnly: true, + nextPageCommand: args.nextPageCommand, + previousPageCommand: previousPageCommand, showAllCommand: log !== undefined && log.truncated ? new CommandQuickPickItem({ label: `$(sync) Show All Commits`, description: `${Strings.pad(GlyphChars.Dash, 2, 3)} this may take a while` - }, Commands.ShowQuickFileHistory, [uri, { ...args, maxCount: 0 }]) + }, Commands.DiffWithRevision, [uri, { ...args, maxCount: 0 }]) : undefined }); if (pick === undefined) return undefined; diff --git a/src/commands/openFileRevision.ts b/src/commands/openFileRevision.ts index b0d416b..2a39e47 100644 --- a/src/commands/openFileRevision.ts +++ b/src/commands/openFileRevision.ts @@ -1,14 +1,22 @@ 'use strict'; +import { Iterables, Strings } from '../system'; import { Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode'; import { AnnotationController, FileAnnotationType } from '../annotations/annotationController'; -import { ActiveEditorCommand, Commands, openEditor } from './common'; +import { ActiveEditorCommand, Commands, getCommandUri, openEditor } from './common'; +import { GlyphChars } from '../constants'; +import { GitService, GitUri } from '../gitService'; import { Logger } from '../logger'; +import { Messages } from '../messages'; +import { CommandQuickPickItem, FileHistoryQuickPick } from '../quickPicks'; export interface OpenFileRevisionCommandArgs { uri?: Uri; + maxCount?: number; + line?: number; showOptions?: TextDocumentShowOptions; annotationType?: FileAnnotationType; + nextPageCommand?: CommandQuickPickItem; } export class OpenFileRevisionCommand extends ActiveEditorCommand { @@ -34,7 +42,8 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { } constructor( - private readonly annotationController: AnnotationController + private readonly annotationController: AnnotationController, + private readonly git: GitService ) { super(Commands.OpenFileRevision); } @@ -46,6 +55,55 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { } try { + if (args.uri === undefined) { + uri = getCommandUri(uri, editor); + if (uri === undefined) return undefined; + + const gitUri = await GitUri.fromUri(uri, this.git); + + const progressCancellation = FileHistoryQuickPick.showProgress(gitUri); + + const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, { maxCount: args.maxCount, ref: gitUri.sha }); + if (log === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to open history compare'); + + if (progressCancellation.token.isCancellationRequested) return undefined; + + let previousPageCommand: CommandQuickPickItem | undefined = undefined; + + if (log.truncated) { + const npc = new CommandQuickPickItem({ + label: `$(arrow-right) Show Next Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} newer commits` + }, Commands.OpenFileRevision, [uri, { ...args } as OpenFileRevisionCommandArgs]); + + const last = Iterables.last(log.commits.values()); + if (last != null) { + previousPageCommand = new CommandQuickPickItem({ + label: `$(arrow-left) Show Previous Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} older commits` + }, Commands.OpenFileRevision, [new GitUri(uri, last), { ...args, nextPageCommand: npc } as OpenFileRevisionCommandArgs]); + } + } + + const label = `${gitUri.getFormattedPath()}${gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : ''}`; + const pick = await FileHistoryQuickPick.show(this.git, log, gitUri, label, progressCancellation, { + pickerOnly: true, + nextPageCommand: args.nextPageCommand, + previousPageCommand: previousPageCommand, + showAllCommand: log !== undefined && log.truncated + ? new CommandQuickPickItem({ + label: `$(sync) Show All Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} this may take a while` + }, Commands.OpenFileRevision, [uri, { ...args, maxCount: 0 } as OpenFileRevisionCommandArgs]) + : undefined + }); + if (pick === undefined) return undefined; + + if (pick instanceof CommandQuickPickItem) return pick.execute(); + + args.uri = GitUri.toRevisionUri(pick.commit.sha, pick.commit.uri.fsPath, pick.commit.repoPath); + } + if (args.line !== undefined && args.line !== 0) { if (args.showOptions === undefined) { args.showOptions = {}; @@ -60,7 +118,7 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand { } catch (ex) { Logger.error(ex, 'OpenFileRevisionCommand'); - return window.showErrorMessage(`Unable to open in file revision. See output channel for more details`); + return window.showErrorMessage(`Unable to open file revision. See output channel for more details`); } } } \ No newline at end of file diff --git a/src/commands/showQuickFileHistory.ts b/src/commands/showQuickFileHistory.ts index 1c857ee..bdf93ad 100644 --- a/src/commands/showQuickFileHistory.ts +++ b/src/commands/showQuickFileHistory.ts @@ -1,5 +1,5 @@ 'use strict'; -import { Strings } from '../system'; +import { Iterables, Strings } from '../system'; import { commands, Range, TextEditor, Uri, window } from 'vscode'; import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common'; import { GlyphChars } from '../constants'; @@ -44,15 +44,33 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand { if (progressCancellation.token.isCancellationRequested) return undefined; + let previousPageCommand: CommandQuickPickItem | undefined = undefined; + + if (args.log.truncated) { + const npc = new CommandQuickPickItem({ + label: `$(arrow-right) Show Next Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${args.log.maxCount} newer commits` + }, Commands.ShowQuickFileHistory, [gitUri, { ...args, log: undefined } as ShowQuickFileHistoryCommandArgs]); + + const last = Iterables.last(args.log.commits.values()); + if (last != null) { + previousPageCommand = new CommandQuickPickItem({ + label: `$(arrow-left) Show Previous Commits`, + description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${args.log.maxCount} older commits` + }, Commands.ShowQuickFileHistory, [new GitUri(uri, last), { ...args, log: undefined, nextPageCommand: npc } as ShowQuickFileHistoryCommandArgs]); + } + } + const label = `${gitUri.getFormattedPath()}${gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : ''}`; const pick = await FileHistoryQuickPick.show(this.git, args.log, gitUri, label, progressCancellation, { goBackCommand: args.goBackCommand, nextPageCommand: args.nextPageCommand, + previousPageCommand: previousPageCommand, showAllCommand: args.log !== undefined && args.log.truncated ? new CommandQuickPickItem({ label: `$(sync) Show All Commits`, description: `${Strings.pad(GlyphChars.Dash, 2, 3)} this may take a while` - }, Commands.ShowQuickFileHistory, [uri, { ...args, maxCount: 0 }]) + }, Commands.ShowQuickFileHistory, [uri, { ...args, log: undefined, maxCount: 0 }]) : undefined, showInResultsExplorerCommand: args.log !== undefined ? new ShowCommitsInResultsQuickPickItem(args.log, { diff --git a/src/quickPicks/fileHistory.ts b/src/quickPicks/fileHistory.ts index c220109..e1d38a4 100644 --- a/src/quickPicks/fileHistory.ts +++ b/src/quickPicks/fileHistory.ts @@ -20,13 +20,11 @@ export class FileHistoryQuickPick { }); } - static async show(git: GitService, log: GitLog, uri: GitUri, placeHolder: string, progressCancellation: CancellationTokenSource, options: { goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem, pickerOnly?: boolean, showAllCommand?: CommandQuickPickItem, showInResultsExplorerCommand?: CommandQuickPickItem } = {}): Promise { + static async show(git: GitService, log: GitLog, uri: GitUri, placeHolder: string, progressCancellation: CancellationTokenSource, options: { goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem, previousPageCommand?: CommandQuickPickItem, pickerOnly?: boolean, showAllCommand?: CommandQuickPickItem, showInResultsExplorerCommand?: CommandQuickPickItem } = {}): Promise { options = { pickerOnly: false, ...options }; const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[]; - let previousPageCommand: CommandQuickPickItem | undefined = undefined; - let index = 0; if (options.showInResultsExplorerCommand !== undefined) { @@ -39,7 +37,7 @@ export class FileHistoryQuickPick { index++; items.splice(0, 0, options.showAllCommand); } - else { + else if (!options.pickerOnly) { const workingFileName = await git.findWorkingFileName(log.repoPath, path.relative(log.repoPath, uri.fsPath)); if (workingFileName) { index++; @@ -66,41 +64,14 @@ export class FileHistoryQuickPick { } } - if (options.nextPageCommand) { + if (options.nextPageCommand !== undefined) { index++; items.splice(0, 0, options.nextPageCommand); } - if (log.truncated) { - const npc = new CommandQuickPickItem({ - label: `$(arrow-right) Show Next Commits`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} newer commits` - }, Commands.ShowQuickFileHistory, [ - uri, - { - maxCount: log.maxCount, - goBackCommand: options.goBackCommand, - nextPageCommand: options.nextPageCommand - } as ShowQuickFileHistoryCommandArgs - ]); - - const last = Iterables.last(log.commits.values()); - if (last != null) { - previousPageCommand = new CommandQuickPickItem({ - label: `$(arrow-left) Show Previous Commits`, - description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${log.maxCount} older commits` - }, Commands.ShowQuickFileHistory, [ - new GitUri(uri, last), - { - maxCount: log.maxCount, - goBackCommand: options.goBackCommand, - nextPageCommand: npc - } as ShowQuickFileHistoryCommandArgs - ]); - - index++; - items.splice(0, 0, previousPageCommand); - } + if (options.previousPageCommand !== undefined) { + index++; + items.splice(0, 0, options.previousPageCommand); } } @@ -159,7 +130,7 @@ export class FileHistoryQuickPick { const scope = await Keyboard.instance.beginScope({ left: options.goBackCommand, - ',': previousPageCommand, + ',': options.previousPageCommand, '.': options.nextPageCommand });