Browse Source

Refactors commit quick pick commands

Splits showQuickCommitDetails into showQuickCommitDetails and showQuickCommitFileDetails
Adds closeUnchangedFiles command
Adds openChangedFiles command
Adds diffDirectory command
Adds contextual description to the `go back` commands
Fixes #44 by adding a warning message about Git version requirements
Fixes intermittent errors when adding active line annotations
Fixes intermittent errors when opening multiple files via quick picks
Updates dependencies
Preps v2.11.0
main
Eric Amodio 7 years ago
parent
commit
eaea44872c
30 changed files with 764 additions and 353 deletions
  1. +24
    -1
      CHANGELOG.md
  2. +3
    -3
      README.md
  3. +50
    -11
      package.json
  4. +59
    -0
      src/activeEditorTracker.ts
  5. +10
    -1
      src/blameActiveLineController.ts
  6. +5
    -1
      src/commands.ts
  7. +70
    -0
      src/commands/closeUnchangedFiles.ts
  8. +19
    -2
      src/commands/commands.ts
  9. +35
    -0
      src/commands/diffDirectory.ts
  10. +41
    -0
      src/commands/openChangedFiles.ts
  11. +20
    -49
      src/commands/showQuickCommitDetails.ts
  12. +89
    -0
      src/commands/showQuickCommitFileDetails.ts
  13. +12
    -17
      src/commands/showQuickFileHistory.ts
  14. +12
    -30
      src/commands/showQuickRepoHistory.ts
  15. +2
    -14
      src/commands/showQuickRepoStatus.ts
  16. +9
    -1
      src/comparers.ts
  17. +4
    -2
      src/configuration.ts
  18. +15
    -3
      src/extension.ts
  19. +3
    -3
      src/git/enrichers/logParserEnricher.ts
  20. +47
    -34
      src/git/git.ts
  21. +4
    -1
      src/git/gitEnrichment.ts
  22. +13
    -2
      src/gitCodeLensProvider.ts
  23. +24
    -10
      src/gitProvider.ts
  24. +2
    -1
      src/quickPicks.ts
  25. +27
    -119
      src/quickPicks/commitDetails.ts
  26. +111
    -0
      src/quickPicks/commitFileDetails.ts
  27. +13
    -9
      src/quickPicks/fileHistory.ts
  28. +3
    -16
      src/quickPicks/quickPicks.ts
  29. +1
    -3
      src/quickPicks/repoHistory.ts
  30. +37
    -20
      src/quickPicks/repoStatus.ts

+ 24
- 1
CHANGELOG.md View File

@ -1,5 +1,28 @@
## Release Notes
### 2.11.0
- Adds `gitlens.showQuickCommitFileDetails` command to show a quick pick list of details for a file commit
- Adds `gitlens.showQuickCommitFileDetails` command to CodeLens
- Adds `gitlens.showQuickCommitFileDetails` command to the status bar
- Adds `gitlens.closeUnchangedFiles` command to close any editors that don't have uncommitted changes
- Adds `gitlens.openChangedFiles` command to open all files that have uncommitted changes
- Adds `gitlens.diffDirectory` command to open the configured git difftool to compare directory versions
- Adds `Directory Compare with Previous Commit` command on the `gitlens.showQuickCommitDetails` quick pick
- Adds `Directory Compare with Working Tree` command on the `gitlens.showQuickCommitDetails` quick pick
- Adds a `Changed Files` grouping on the `gitlens.showQuickCommitDetails` quick pick
- Adds a `Close Unchanged Files` command on the `gitlens.showQuickRepoStatus` quick pick
- Adds a contextual description to the `go back` command in quick pick lists
- Changes layout of the `gitlens.showQuickRepoStatus` quick pick for better clarity
- Changes behavior of `gitlens.showQuickCommitDetails` to show commit a quick pick list of details for a commit
- Changes default of `gitlens.codeLens.recentChange.command` to be `gitlens.showQuickCommitFileDetails` (though there is no visible behavior change)
- Renames `Open Files` to `Open Changed Files` on the `gitlens.showQuickCommitDetails` quick pick
- Renames `Open Working Files` to `Open Changed Working Files` on the `gitlens.showQuickCommitDetails` quick pick
- Renames `Show Changed Files` to `Show Commit Details` on the `gitlens.showQuickCommitFileDetails` quick pick
- Renames `Open Files` to `Open Changed Files` on the `gitlens.showQuickRepoStatus` quick pick
- Fixes [#44](https://github.com/eamodio/vscode-gitlens/issues/43) by adding a warning message about Git version requirements
- Fixes intermittent errors when adding active line annotations
- Fixes intermittent errors when opening multiple files via quick picks
### 2.10.1
- Fixes [#43](https://github.com/eamodio/vscode-gitlens/issues/43) - File-level CodeLens isn't using the blame of the whole file as it should
- Fixes issue with single quotes (') in annotations
@ -108,7 +131,7 @@
- Adds `gitlens.showQuickRepoHistory` and `gitlens.showQuickCommitDetails` commands to the status bar
- Changes the default command of `gitlens.codeLens.recentChange.command` to `gitlens.showQuickCommitDetails`
- Changes the default command of `gitlens.statusBar.command` to `gitlens.showQuickCommitDetails`
- Changes Changes behavior of `gitlens.showQuickCommitDetails` to show commit commands rather than file set (use `Show Changed Files` command to get to the file set)
- Changes behavior of `gitlens.showQuickCommitDetails` to show commit commands rather than file set (use `Show Changed Files` command to get to the file set)
- Changes `gitlens.diffWithPrevious` command to behave as `gitlens.diffWithWorking` if the file has uncommitted changes
- Renames `gitlens.diffWithPrevious` command from `Diff Commit with Previous` to `Compare with Previous Commit`
- Renames `gitlens.diffLineWithPrevious` command from `Diff Commit (line) with Previous` to `Compare Line with Previous Commit`

+ 3
- 3
README.md View File

@ -46,12 +46,12 @@ Provides Git CodeLens information (most recent commit, # of authors), on-demand
|`gitlens.codeLens.locationCustomSymbols`|Specifies the set of document symbols to render active document CodeLens on. Must be a member of `SymbolKind`
|`gitlens.codeLens.languageLocations`|Specifies where CodeLens will be rendered in the active document for the specified languages
|`gitlens.codeLens.recentChange.enabled`|Specifies whether the recent change CodeLens is shown
|`gitlens.codeLens.recentChange.command`|"Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.codeLens.recentChange.command`|"Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.codeLens.authors.enabled`|Specifies whether the authors CodeLens is shown
|`gitlens.codeLens.authors.command`|Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.codeLens.authors.command`|Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.menus.diff.enabled`|Specifies whether diff commands will be added to the context menus
|`gitlens.statusBar.enabled`|Specifies whether blame information is shown in the status bar
|`gitlens.statusBar.command`|"Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.statusBar.command`|"Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick
|`gitlens.statusBar.date`|Specifies whether and how the commit date will be shown in the blame status bar. `off` - no date. `relative` - relative date (e.g. 1 day ago). `absolute` - date format specified by `gitlens.statusBar.dateFormat`
|`gitlens.statusBar.dateFormat`|Specifies the date format of how absolute dates will be shown in the blame status bar. See https://momentjs.com/docs/#/displaying/format/ for valid formats

+ 50
- 11
package.json View File

@ -1,6 +1,6 @@
{
"name": "gitlens",
"version": "2.10.1",
"version": "2.11.0",
"author": {
"name": "Eric Amodio",
"email": "eamodio@gmail.com"
@ -214,17 +214,18 @@
},
"gitlens.codeLens.recentChange.command": {
"type": "string",
"default": "gitlens.showQuickCommitDetails",
"default": "gitlens.showQuickCommitFileDetails",
"enum": [
"gitlens.toggleBlame",
"gitlens.showBlameHistory",
"gitlens.showFileHistory",
"gitlens.diffWithPrevious",
"gitlens.showQuickCommitDetails",
"gitlens.showQuickCommitFileDetails",
"gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory"
],
"description": "Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
"description": "Specifies the command executed when the recent change CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
},
"gitlens.codeLens.authors.enabled": {
"type": "boolean",
@ -240,10 +241,11 @@
"gitlens.showFileHistory",
"gitlens.diffWithPrevious",
"gitlens.showQuickCommitDetails",
"gitlens.showQuickCommitFileDetails",
"gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory"
],
"description": "Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
"description": "Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
},
"gitlens.menus.diff.enabled": {
"type": "boolean",
@ -265,10 +267,11 @@
"gitlens.diffWithPrevious",
"gitlens.toggleCodeLens",
"gitlens.showQuickCommitDetails",
"gitlens.showQuickCommitFileDetails",
"gitlens.showQuickFileHistory",
"gitlens.showQuickRepoHistory"
],
"description": "Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
"description": "Specifies the command executed when the blame status bar item is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showFileHistory` - opens the file history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `gitlens.showQuickCommitDetails` - shows a commit details quick pick. `gitlens.showQuickCommitFileDetails` - shows a commit file details quick pick. `gitlens.showQuickFileHistory` - shows a file history quick pick. `gitlens.showQuickFileHistory` - shows a repository history quick pick"
},
"gitlens.statusBar.date": {
"type": "string",
@ -349,6 +352,11 @@
"category": "GitLens:KeyPress"
},
{
"command": "gitlens.diffDirectory",
"title": "Directory Compare",
"category": "GitLens"
},
{
"command": "gitlens.diffWithPrevious",
"title": "Compare with Previous Commit",
"category": "GitLens"
@ -403,6 +411,11 @@
"category": "GitLens"
},
{
"command": "gitlens.showQuickCommitFileDetails",
"title": "Show Line Commit Details",
"category": "GitLens"
},
{
"command": "gitlens.showQuickFileHistory",
"title": "Show File History",
"category": "GitLens"
@ -426,6 +439,16 @@
"command": "gitlens.copyMessageToClipboard",
"title": "Copy Commit Message to Clipboard",
"category": "GitLens"
},
{
"command": "gitlens.closeUnchangedFiles",
"title": "Close Unchanged Files",
"category": "GitLens"
},
{
"command": "gitlens.openChangedFiles",
"title": "Open Changed Files",
"category": "GitLens"
}
],
"menus": {
@ -439,6 +462,10 @@
"when": "false"
},
{
"command": "gitlens.diffDirectory",
"when": "gitlens:enabled"
},
{
"command": "gitlens.diffWithPrevious",
"when": "gitlens:enabled"
},
@ -479,6 +506,10 @@
"when": "gitlens:enabled && gitlens:isBlameable"
},
{
"command": "gitlens.showQuickCommitFileDetails",
"when": "gitlens:enabled && gitlens:isBlameable"
},
{
"command": "gitlens.showQuickFileHistory",
"when": "gitlens:enabled"
},
@ -497,6 +528,14 @@
{
"command": "gitlens.copyMessageToClipboard",
"when": "gitlens:enabled && gitlens:isBlameable"
},
{
"command": "gitlens.closeUnchangedFiles",
"when": "gitlens:enabled"
},
{
"command": "gitlens.openChangedFiles",
"when": "gitlens:enabled"
}
],
"explorer/context": [
@ -572,7 +611,7 @@
"group": "1_gitlens@2"
},
{
"command": "gitlens.showQuickCommitDetails",
"command": "gitlens.showQuickCommitFileDetails",
"when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable",
"group": "1_gitlens@3"
},
@ -651,7 +690,7 @@
"when": "gitlens:enabled"
},
{
"command": "gitlens.showQuickCommitDetails",
"command": "gitlens.showQuickCommitFileDetails",
"key": "alt+c",
"mac": "alt+c",
"when": "editorTextFocus && gitlens:enabled"
@ -701,17 +740,17 @@
"lodash.isequal": "^4.5.0",
"lodash.once": "^4.1.1",
"moment": "^2.17.1",
"spawn-rx": "^2.0.8",
"spawn-rx": "^2.0.10",
"tmp": "^0.0.31"
},
"devDependencies": {
"@types/copy-paste": "^1.1.30",
"@types/mocha": "^2.2.39",
"@types/node": "^7.0.5",
"@types/node": "^7.0.7",
"@types/tmp": "^0.0.32",
"mocha": "^3.2.0",
"tslint": "^4.4.2",
"tslint": "^4.5.1",
"typescript": "^2.2.1",
"vscode": "^1.0.3"
"vscode": "^1.0.5"
}
}

+ 59
- 0
src/activeEditorTracker.ts View File

@ -0,0 +1,59 @@
'use strict';
import { commands, Disposable, TextEditor, window } from 'vscode';
export class ActiveEditorTracker extends Disposable {
private _disposable: Disposable;
private _resolver: (value?: TextEditor | PromiseLike<TextEditor>) => void;
constructor() {
super(() => this.dispose());
this._disposable = window.onDidChangeActiveTextEditor(e => this._resolver && this._resolver(e));
}
dispose() {
this._disposable && this._disposable.dispose();
}
async awaitClose(timeout: number = 500): Promise<TextEditor> {
this.close();
return this.wait(timeout);
}
async awaitNext(timeout: number = 500): Promise<TextEditor> {
this.next();
return this.wait(timeout);
}
async close(): Promise<{}> {
return commands.executeCommand('workbench.action.closeActiveEditor');
}
async next(): Promise<{}> {
return commands.executeCommand('workbench.action.nextEditor');
}
async wait(timeout: number = 500): Promise<TextEditor> {
const editor = await new Promise<TextEditor>((resolve, reject) => {
let timer: any;
this._resolver = (editor: TextEditor) => {
if (timer) {
clearTimeout(timer as any);
timer = 0;
resolve(editor);
}
};
timer = setTimeout(() => {
resolve(window.activeTextEditor);
timer = 0;
}, timeout) as any;
});
this._resolver = undefined;
return editor;
}
}

+ 10
- 1
src/blameActiveLineController.ts View File

@ -224,6 +224,9 @@ export class BlameActiveLineController extends Disposable {
}
async show(commit: GitCommit, blameLine: IGitCommitLine, editor: TextEditor) {
// I have no idea why I need this protection -- but it happens
if (!editor.document) return;
if (this._config.statusBar.enabled) {
switch (this._config.statusBar.date) {
case 'off':
@ -260,9 +263,12 @@ export class BlameActiveLineController extends Disposable {
case StatusBarCommand.ToggleCodeLens:
this._statusBarItem.tooltip = 'Toggle Git CodeLens';
break;
case StatusBarCommand.ShowQuickFileHistory:
case StatusBarCommand.ShowQuickCommitDetails:
this._statusBarItem.tooltip = 'Show Commit Details';
break;
case StatusBarCommand.ShowQuickCommitFileDetails:
this._statusBarItem.tooltip = 'Show Line Commit Details';
break;
case StatusBarCommand.ShowQuickFileHistory:
this._statusBarItem.tooltip = 'Show File History';
break;
@ -296,6 +302,9 @@ export class BlameActiveLineController extends Disposable {
logCommit = log && log.commits.get(commit.sha);
}
// I have no idea why I need this protection -- but it happens
if (!editor.document) return;
let hoverMessage: string | string[];
if (activeLine !== 'inline') {
// If the messages match (or we couldn't find the log), then this is a possible duplicate annotation

+ 5
- 1
src/commands.ts View File

@ -1,17 +1,21 @@
'use strict';
export { Keyboard } from './commands/keyboard';
export { ActiveEditorCommand, Command, Commands, EditorCommand } from './commands/commands';
export { ActiveEditorCommand, Command, Commands, EditorCommand, openEditor } from './commands/commands';
export { CloseUnchangedFilesCommand } from './commands/closeUnchangedFiles';
export { CopyMessageToClipboardCommand } from './commands/copyMessageToClipboard';
export { CopyShaToClipboardCommand } from './commands/copyShaToClipboard';
export { DiffDirectoryCommand } from './commands/diffDirectory';
export { DiffLineWithPreviousCommand } from './commands/diffLineWithPrevious';
export { DiffLineWithWorkingCommand } from './commands/diffLineWithWorking';
export { DiffWithPreviousCommand } from './commands/diffWithPrevious';
export { DiffWithWorkingCommand } from './commands/diffWithWorking';
export { OpenChangedFilesCommand } from './commands/openChangedFiles';
export { ShowBlameCommand } from './commands/showBlame';
export { ShowBlameHistoryCommand } from './commands/showBlameHistory';
export { ShowFileHistoryCommand } from './commands/showFileHistory';
export { ShowQuickCommitDetailsCommand } from './commands/showQuickCommitDetails';
export { ShowQuickCommitFileDetailsCommand } from './commands/showQuickCommitFileDetails';
export { ShowQuickFileHistoryCommand } from './commands/showQuickFileHistory';
export { ShowQuickRepoHistoryCommand } from './commands/showQuickRepoHistory';
export { ShowQuickRepoStatusCommand } from './commands/showQuickRepoStatus';

+ 70
- 0
src/commands/closeUnchangedFiles.ts View File

@ -0,0 +1,70 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorTracker } from '../activeEditorTracker';
import { ActiveEditorCommand, Commands } from './commands';
import { TextEditorComparer, UriComparer } from '../comparers';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger';
import * as path from 'path';
export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
constructor(private git: GitProvider, public repoPath: string) {
super(Commands.CloseUnchangedFiles);
}
async execute(editor: TextEditor, uri?: Uri, uris?: Uri[]) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
try {
if (!uris) {
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to close unchanged files`);
const statuses = await this.git.getStatusesForRepo(repoPath);
if (!statuses) return window.showWarningMessage(`Unable to close unchanged files`);
uris = statuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
}
const editorTracker = new ActiveEditorTracker();
let active = window.activeTextEditor;
let editor = active;
do {
if (editor) {
if ((editor.document && editor.document.isDirty) ||
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) {
active = editor;
}
editor = await editorTracker.awaitNext(500);
}
else {
if (active === editor) {
active = undefined;
}
editor = await editorTracker.awaitClose(500);
}
}
else {
if (active === editor) {
active = undefined;
}
editor = await editorTracker.awaitClose(500);
}
} while ((!active && !editor) || !TextEditorComparer.equals(active, editor, { useId: true, usePosition: true }));
editorTracker.dispose();
return undefined;
}
catch (ex) {
Logger.error('[GitLens.CloseUnchangedFilesCommand]', ex);
return window.showErrorMessage(`Unable to close unchanged files. See output channel for more details`);
}
}
}

+ 19
- 2
src/commands/commands.ts View File

@ -1,18 +1,23 @@
'use strict';
import { commands, Disposable, TextEditor, TextEditorEdit, window } from 'vscode';
import { commands, Disposable, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { BuiltInCommands } from '../constants';
export type Commands = 'gitlens.copyMessageToClipboard' | 'gitlens.copyShaToClipboard' | 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' | 'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' | 'gitlens.showQuickRepoStatus' | 'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
export type Commands = 'gitlens.closeUnchangedFiles' | 'gitlens.copyMessageToClipboard' | 'gitlens.copyShaToClipboard' | 'gitlens.diffDirectory' | 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' | 'gitlens.openChangedFiles' | 'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory' | 'gitlens.showQuickRepoStatus' | 'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
export const Commands = {
CloseUnchangedFiles: 'gitlens.closeUnchangedFiles' as Commands,
CopyMessageToClipboard: 'gitlens.copyMessageToClipboard' as Commands,
CopyShaToClipboard: 'gitlens.copyShaToClipboard' as Commands,
DiffDirectory: 'gitlens.diffDirectory' as Commands,
DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands,
DiffLineWithPrevious: 'gitlens.diffLineWithPrevious' as Commands,
DiffWithWorking: 'gitlens.diffWithWorking' as Commands,
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
OpenChangedFiles: 'gitlens.openChangedFiles' as Commands,
ShowBlame: 'gitlens.showBlame' as Commands,
ShowBlameHistory: 'gitlens.showBlameHistory' as Commands,
ShowFileHistory: 'gitlens.showFileHistory' as Commands,
ShowQuickCommitDetails: 'gitlens.showQuickCommitDetails' as Commands,
ShowQuickCommitFileDetails: 'gitlens.showQuickCommitFileDetails' as Commands,
ShowQuickFileHistory: 'gitlens.showQuickFileHistory' as Commands,
ShowQuickRepoHistory: 'gitlens.showQuickRepoHistory' as Commands,
ShowQuickRepoStatus: 'gitlens.showQuickRepoStatus' as Commands,
@ -68,4 +73,16 @@ export abstract class ActiveEditorCommand extends Disposable {
}
abstract execute(editor: TextEditor, ...args: any[]): any;
}
export async function openEditor(uri: Uri, pinned: boolean = false) {
try {
if (!pinned) return await commands.executeCommand(BuiltInCommands.Open, uri);
const document = await workspace.openTextDocument(uri);
return window.showTextDocument(document, (window.activeTextEditor && window.activeTextEditor.viewColumn) || 1, true);
}
catch (ex) {
return undefined;
}
}

+ 35
- 0
src/commands/diffDirectory.ts View File

@ -0,0 +1,35 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger';
export class DiffDirectoryCommand extends ActiveEditorCommand {
constructor(private git: GitProvider, public repoPath: string) {
super(Commands.DiffDirectory);
}
async execute(editor: TextEditor, uri?: Uri, shaOrBranch1?: string, shaOrBranch2?: string): Promise<any> {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
try {
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to open directory diff`);
if (!shaOrBranch1) {
//window.showQuickPick()
return undefined;
}
this.git.openDirectoryDiff(repoPath, shaOrBranch1, shaOrBranch2);
return undefined;
}
catch (ex) {
Logger.error('GitLens.DiffDirectoryCommand', ex);
return window.showErrorMessage(`Unable to open directory diff. See output channel for more details`);
}
}
}

+ 41
- 0
src/commands/openChangedFiles.ts View File

@ -0,0 +1,41 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, openEditor } from './commands';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger';
import * as path from 'path';
export class OpenChangedFilesCommand extends ActiveEditorCommand {
constructor(private git: GitProvider, public repoPath: string) {
super(Commands.OpenChangedFiles);
}
async execute(editor: TextEditor, uri?: Uri, uris?: Uri[]) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
try {
if (!uris) {
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to open changed files`);
const statuses = await this.git.getStatusesForRepo(repoPath);
if (!statuses) return window.showWarningMessage(`Unable to open changed files`);
uris = statuses.filter(_ => _.status !== 'D').map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
}
for (const uri of uris) {
await openEditor(uri, true);
}
return undefined;
}
catch (ex) {
Logger.error('[GitLens.OpenChangedFilesCommand]', ex);
return window.showErrorMessage(`Unable to open changed files. See output channel for more details`);
}
}
}

+ 20
- 49
src/commands/showQuickCommitDetails.ts View File

@ -4,7 +4,7 @@ import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitFileDetailsQuickPick, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand {
@ -12,7 +12,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand {
super(Commands.ShowQuickCommitDetails);
}
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit, goBackCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = { showFileHistory: true }) {
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit | GitLogCommit, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
@ -35,72 +35,43 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand {
sha = blame.commit.isUncommitted ? blame.commit.previousSha : blame.commit.sha;
repoPath = blame.commit.repoPath;
return commands.executeCommand(Commands.ShowQuickFileHistory, uri, undefined, blame.commit);
commit = blame.commit;
}
catch (ex) {
Logger.error('[GitLens.ShowQuickCommitDetails]', `getBlameForLine(${blameline})`, ex);
Logger.error('[GitLens.ShowQuickCommitDetailsCommand]', `getBlameForLine(${blameline})`, ex);
return window.showErrorMessage(`Unable to show commit details. See output channel for more details`);
}
}
try {
let pick: CommitWithFileStatusQuickPickItem | CommandQuickPickItem;
let alreadyPickedCommit = !!commit;
let workingFileName: string;
if (!alreadyPickedCommit) {
let log = await this.git.getLogForRepo(repoPath, sha, 0);
if (!commit || !(commit instanceof GitLogCommit) || commit.type !== 'repo') {
let log = await this.git.getLogForRepo(repoPath, sha, 2);
if (!log) return window.showWarningMessage(`Unable to show commit details`);
commit = Iterables.first(log.commits.values());
pick = await CommitDetailsQuickPick.show(commit as GitLogCommit, uri, goBackCommand);
if (!pick) return undefined;
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) {
return pick.execute();
}
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
const workingCommit = await this.git.findMostRecentCommitForFile(pick.uri.fsPath, pick.sha);
// TODO: Leave this at undefined until findMostRecentCommitForFile actually works
workingFileName = !workingCommit ? pick.fileName : undefined;
log = await this.git.getLogForFile(pick.gitUri.fsPath, pick.sha, undefined, undefined, 2);
if (!log) return window.showWarningMessage(`Unable to show commit details`);
commit = Iterables.find(log.commits.values(), c => c.sha === commit.sha);
uri = pick.gitUri || uri;
}
else {
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
const workingCommit = await this.git.findMostRecentCommitForFile(commit.uri.fsPath, commit.sha);
// TODO: Leave this at undefined until findMostRecentCommitForFile actually works
workingFileName = !workingCommit ? commit.fileName : undefined;
}
pick = await CommitFileDetailsQuickPick.show(this.git, commit, workingFileName, uri,
// Create a command to get back to where we are right now
new CommandQuickPickItem({
if (!goBackCommand) {
// Create a command to get back to the commit details
goBackCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: null
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand, options]),
// If we have already picked a commit, just jump back to the previous (since we skipped a quickpick menu)
// Otherwise setup a normal back command
alreadyPickedCommit
? goBackCommand
: new CommandQuickPickItem({
label: `go back \u21A9`,
description: null
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, undefined, goBackCommand, options]),
{ showFileHistory: options.showFileHistory });
description: `\u00a0 \u2014 \u00a0\u00a0 to repository history`
}, Commands.ShowQuickRepoHistory, [new GitUri(commit.uri, commit)]);
}
const pick = await CommitDetailsQuickPick.show(commit as GitLogCommit, uri, goBackCommand);
if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) {
if (!(pick instanceof CommitWithFileStatusQuickPickItem)) {
return pick.execute();
}
return undefined;
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, pick.gitUri, pick.sha, undefined,
// 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$(git-commit) ${pick.sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand]));
}
catch (ex) {
Logger.error('[GitLens.ShowQuickCommitDetailsCommand]', ex);

+ 89
- 0
src/commands/showQuickCommitFileDetails.ts View File

@ -0,0 +1,89 @@
'use strict';
import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitFileDetailsQuickPick } from '../quickPicks';
import * as path from 'path';
export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCommand {
constructor(private git: GitProvider) {
super(Commands.ShowQuickCommitFileDetails);
}
async execute(editor: TextEditor, uri?: Uri, sha?: string, commit?: GitCommit | GitLogCommit, goBackCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = { showFileHistory: true }) {
if (!(uri instanceof Uri)) {
if (!editor || !editor.document) return undefined;
uri = editor.document.uri;
}
const gitUri = await GitUri.fromUri(uri, this.git);
let repoPath = gitUri.repoPath;
if (!sha) {
if (!editor) return undefined;
const blameline = editor.selection.active.line - gitUri.offset;
if (blameline < 0) return undefined;
try {
const blame = await this.git.getBlameForLine(gitUri.fsPath, blameline, gitUri.sha, gitUri.repoPath);
if (!blame) 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;
repoPath = blame.commit.repoPath;
commit = blame.commit;
}
catch (ex) {
Logger.error('[GitLens.ShowQuickCommitFileDetailsCommand]', `getBlameForLine(${blameline})`, ex);
return window.showErrorMessage(`Unable to show commit file details. See output channel for more details`);
}
}
try {
if (!commit || ((commit instanceof GitLogCommit) && commit.type !== 'file')) {
let log = await this.git.getLogForFile(uri.fsPath, sha, undefined, undefined, 2);
if (!log) return window.showWarningMessage(`Unable to show commit file details`);
commit = Iterables.find(log.commits.values(), c => c.sha === sha);
}
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
const workingCommit = await this.git.findMostRecentCommitForFile(commit.uri.fsPath, commit.sha);
// TODO: Leave this at undefined until findMostRecentCommitForFile actually works
const workingFileName = !workingCommit ? commit.fileName : undefined;
if (!goBackCommand) {
// Create a command to get back to the commit details
goBackCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to details of \u00a0$(git-commit) ${sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), sha, commit]);
}
const pick = await CommitFileDetailsQuickPick.show(this.git, commit, workingFileName, 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) ${sha}`
}, Commands.ShowQuickCommitFileDetails, [new GitUri(commit.uri, commit), sha, commit, goBackCommand, options]),
{ showFileHistory: options.showFileHistory });
if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
return undefined;
}
catch (ex) {
Logger.error('[GitLens.ShowQuickCommitFileDetailsCommand]', ex);
return window.showErrorMessage(`Unable to show commit file details. See output channel for more details`);
}
}
}

+ 12
- 17
src/commands/showQuickFileHistory.ts View File

@ -1,9 +1,10 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitProvider, GitUri } from '../gitProvider';
import { GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger';
import { CommandQuickPickItem, FileHistoryQuickPick } from '../quickPicks';
import * as path from 'path';
export class ShowQuickFileHistoryCommand extends ActiveEditorCommand {
@ -11,7 +12,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCommand {
super(Commands.ShowQuickFileHistory);
}
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, commit?: GitCommit, goBackCommand?: CommandQuickPickItem) {
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
@ -27,27 +28,21 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCommand {
}
try {
if (!commit) {
const log = await this.git.getLogForFile(gitUri.fsPath, gitUri.sha, gitUri.repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
const log = await this.git.getLogForFile(gitUri.fsPath, gitUri.sha, gitUri.repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show file history. File is probably not under source control`);
let pick = await FileHistoryQuickPick.show(log, uri, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
let pick = await FileHistoryQuickPick.show(log, uri, gitUri.sha, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
commit = pick.commit;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
return commands.executeCommand(Commands.ShowQuickCommitDetails,
new GitUri(commit.uri, commit),
commit.sha, commit,
return commands.executeCommand(Commands.ShowQuickCommitFileDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: null
}, Commands.ShowQuickFileHistory, [uri, maxCount, undefined, goBackCommand]),
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(pick.commit.fileName)}`
}, Commands.ShowQuickFileHistory, [uri, maxCount, goBackCommand]),
{ showFileHistory: false });
}
catch (ex) {

+ 12
- 30
src/commands/showQuickRepoHistory.ts View File

@ -1,7 +1,7 @@
'use strict';
import { commands, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitCommit, GitProvider, GitUri } from '../gitProvider';
import { GitProvider, GitUri } from '../gitProvider';
import { Logger } from '../logger';
import { CommandQuickPickItem, RepoHistoryQuickPick } from '../quickPicks';
@ -11,7 +11,7 @@ export class ShowQuickRepoHistoryCommand extends ActiveEditorCommand {
super(Commands.ShowQuickRepoHistory);
}
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, commit?: GitCommit, goBackCommand?: CommandQuickPickItem) {
async execute(editor: TextEditor, uri?: Uri, maxCount?: number, goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri;
}
@ -21,42 +21,24 @@ export class ShowQuickRepoHistoryCommand extends ActiveEditorCommand {
}
try {
let repoPath: string;
if (uri instanceof Uri) {
const gitUri = await GitUri.fromUri(uri, this.git);
repoPath = gitUri.repoPath;
if (!repoPath) {
repoPath = await this.git.getRepoPathFromFile(gitUri.fsPath);
}
}
if (!repoPath) {
repoPath = this.repoPath;
}
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to show repository history`);
if (!commit) {
const log = await this.git.getLogForRepo(repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show repository history`);
const pick = await RepoHistoryQuickPick.show(log, uri, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
const log = await this.git.getLogForRepo(repoPath, undefined, maxCount);
if (!log) return window.showWarningMessage(`Unable to show repository history`);
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
const pick = await RepoHistoryQuickPick.show(log, uri, maxCount, this.git.config.advanced.maxQuickHistory, goBackCommand);
if (!pick) return undefined;
commit = pick.commit;
if (pick instanceof CommandQuickPickItem) {
return pick.execute();
}
return commands.executeCommand(Commands.ShowQuickCommitDetails,
new GitUri(commit.uri, commit),
commit.sha, undefined,
return commands.executeCommand(Commands.ShowQuickCommitDetails, new GitUri(pick.commit.uri, pick.commit), pick.commit.sha, pick.commit,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: null
}, Commands.ShowQuickRepoHistory, [uri, maxCount, undefined, goBackCommand]));
description: `\u00a0 \u2014 \u00a0\u00a0 to repository history`
}, Commands.ShowQuickRepoHistory, [uri, maxCount, goBackCommand]));
}
catch (ex) {
Logger.error('[GitLens.ShowQuickRepoHistoryCommand]', ex);

+ 2
- 14
src/commands/showQuickRepoStatus.ts View File

@ -1,7 +1,7 @@
'use strict';
import { TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands } from './commands';
import { GitProvider, GitUri } from '../gitProvider';
import { GitProvider } from '../gitProvider';
import { Logger } from '../logger';
import { CommandQuickPickItem, RepoStatusQuickPick } from '../quickPicks';
@ -17,19 +17,7 @@ export class ShowQuickRepoStatusCommand extends ActiveEditorCommand {
}
try {
let repoPath: string;
if (uri instanceof Uri) {
const gitUri = await GitUri.fromUri(uri, this.git);
repoPath = gitUri.repoPath;
if (!repoPath) {
repoPath = await this.git.getRepoPathFromFile(gitUri.fsPath);
}
}
if (!repoPath) {
repoPath = this.repoPath;
}
const repoPath = await this.git.getRepoPathFromUri(uri, this.repoPath);
if (!repoPath) return window.showWarningMessage(`Unable to show repository status`);
const statuses = await this.git.getStatusesForRepo(repoPath);

+ 9
- 1
src/comparers.ts View File

@ -27,10 +27,18 @@ class TextDocumentComparer extends Comparer {
class TextEditorComparer extends Comparer<TextEditor> {
equals(lhs: TextEditor, rhs: TextEditor) {
equals(lhs: TextEditor, rhs: TextEditor, options: { useId: boolean, usePosition: boolean } = { useId: false, usePosition: false }) {
if (!lhs && !rhs) return true;
if ((lhs && !rhs) || (!lhs && rhs)) return false;
if (options.usePosition && (lhs.viewColumn !== rhs.viewColumn)) return false;
if (options.useId && (!lhs.document || !rhs.document)) {
if ((lhs as any)._id !== (rhs as any)._id) return false;
return true;
}
return textDocumentComparer.equals(lhs.document, rhs.document);
}
}

+ 4
- 2
src/configuration.ts View File

@ -28,13 +28,14 @@ export interface IBlameConfig {
};
}
export type CodeLensCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export type CodeLensCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export const CodeLensCommand = {
BlameAnnotate: Commands.ToggleBlame as CodeLensCommand,
ShowBlameHistory: Commands.ShowBlameHistory as CodeLensCommand,
ShowFileHistory: Commands.ShowFileHistory as CodeLensCommand,
DiffWithPrevious: Commands.DiffWithPrevious as CodeLensCommand,
ShowQuickCommitDetails: Commands.ShowQuickCommitDetails as CodeLensCommand,
ShowQuickCommitFileDetails: Commands.ShowQuickCommitFileDetails as CodeLensCommand,
ShowQuickFileHistory: Commands.ShowQuickFileHistory as CodeLensCommand,
ShowQuickRepoHistory: Commands.ShowQuickRepoHistory as CodeLensCommand
};
@ -75,7 +76,7 @@ export interface ICodeLensesConfig {
authors: ICodeLensConfig;
}
export type StatusBarCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleCodeLens' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export type StatusBarCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleCodeLens' | 'gitlens.diffWithPrevious' | 'gitlens.showQuickCommitDetails' | 'gitlens.showQuickCommitFileDetails' | 'gitlens.showQuickFileHistory' | 'gitlens.showQuickRepoHistory';
export const StatusBarCommand = {
BlameAnnotate: Commands.ToggleBlame as StatusBarCommand,
ShowBlameHistory: Commands.ShowBlameHistory as StatusBarCommand,
@ -83,6 +84,7 @@ export const StatusBarCommand = {
DiffWithPrevious: Commands.DiffWithPrevious as StatusBarCommand,
ToggleCodeLens: Commands.ToggleCodeLens as StatusBarCommand,
ShowQuickCommitDetails: Commands.ShowQuickCommitDetails as StatusBarCommand,
ShowQuickCommitFileDetails: Commands.ShowQuickCommitFileDetails as StatusBarCommand,
ShowQuickFileHistory: Commands.ShowQuickFileHistory as StatusBarCommand,
ShowQuickRepoHistory: Commands.ShowQuickRepoHistory as StatusBarCommand
};

+ 15
- 3
src/extension.ts View File

@ -4,11 +4,12 @@ import { BlameabilityTracker } from './blameabilityTracker';
import { BlameActiveLineController } from './blameActiveLineController';
import { BlameAnnotationController } from './blameAnnotationController';
import { configureCssCharacters } from './blameAnnotationFormatter';
import { CloseUnchangedFilesCommand, OpenChangedFilesCommand } from './commands';
import { CopyMessageToClipboardCommand, CopyShaToClipboardCommand } from './commands';
import { DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
import { DiffDirectoryCommand, DiffLineWithPreviousCommand, DiffLineWithWorkingCommand, DiffWithPreviousCommand, DiffWithWorkingCommand} from './commands';
import { ShowBlameCommand, ToggleBlameCommand } from './commands';
import { ShowBlameHistoryCommand, ShowFileHistoryCommand } from './commands';
import { ShowQuickCommitDetailsCommand, ShowQuickFileHistoryCommand, ShowQuickRepoHistoryCommand, ShowQuickRepoStatusCommand} from './commands';
import { ShowQuickCommitDetailsCommand, ShowQuickCommitFileDetailsCommand, ShowQuickFileHistoryCommand, ShowQuickRepoHistoryCommand, ShowQuickRepoStatusCommand} from './commands';
import { ToggleCodeLensCommand } from './commands';
import { Keyboard } from './commands';
import { IAdvancedConfig, IBlameConfig } from './configuration';
@ -44,12 +45,19 @@ export async function activate(context: ExtensionContext) {
catch (ex) {
Logger.error(ex);
if (ex.message.includes('Unable to find git')) {
await window.showErrorMessage(`GitLens: Unable to find Git. Please make sure Git is installed. Also ensure that Git is either in the PATH, or that 'gitlens.advanced.git' is pointed to its installed location.`);
await window.showErrorMessage(`GitLens was unable to find Git. Please make sure Git is installed. Also ensure that Git is either in the PATH, or that 'gitlens.advanced.git' is pointed to its installed location.`);
}
commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:enabled', false);
return;
}
const version = Git.gitInfo().version;
const [major, minor] = version.split('.');
// If git is less than v2.2.0
if (parseInt(major, 10) < 2 || parseInt(minor, 10) < 2) {
await window.showErrorMessage(`GitLens requires a newer version of Git (>= 2.2.0) than is currently installed (${version}). Please install a more recent version of Git.`);
}
let gitEnabled = workspace.getConfiguration('git').get<boolean>('enabled');
commands.executeCommand(BuiltInCommands.SetContext, 'gitlens:enabled', gitEnabled);
context.subscriptions.push(workspace.onDidChangeConfiguration(() => {
@ -79,8 +87,11 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new Keyboard(context));
context.subscriptions.push(new CloseUnchangedFilesCommand(git, repoPath));
context.subscriptions.push(new OpenChangedFilesCommand(git, repoPath));
context.subscriptions.push(new CopyMessageToClipboardCommand(git, repoPath));
context.subscriptions.push(new CopyShaToClipboardCommand(git, repoPath));
context.subscriptions.push(new DiffDirectoryCommand(git, repoPath));
context.subscriptions.push(new DiffWithWorkingCommand(git));
context.subscriptions.push(new DiffLineWithWorkingCommand(git));
context.subscriptions.push(new DiffWithPreviousCommand(git));
@ -90,6 +101,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new ShowBlameHistoryCommand(git));
context.subscriptions.push(new ShowFileHistoryCommand(git));
context.subscriptions.push(new ShowQuickCommitDetailsCommand(git));
context.subscriptions.push(new ShowQuickCommitFileDetailsCommand(git));
context.subscriptions.push(new ShowQuickFileHistoryCommand(git));
context.subscriptions.push(new ShowQuickRepoHistoryCommand(git, repoPath));
context.subscriptions.push(new ShowQuickRepoStatusCommand(git, repoPath));

+ 3
- 3
src/git/enrichers/logParserEnricher.ts View File

@ -1,5 +1,5 @@
'use strict';
import Git, { GitFileStatus, GitLogCommit, IGitAuthor, IGitEnricher, IGitLog } from './../git';
import Git, { GitFileStatus, GitLogCommit, GitLogType, IGitAuthor, IGitEnricher, IGitLog } from './../git';
import * as moment from 'moment';
import * as path from 'path';
@ -149,7 +149,7 @@ export class GitLogParserEnricher implements IGitEnricher {
return entries;
}
enrich(data: string, type: 'file' | 'repo', fileNameOrRepoPath: string, isRepoPath: boolean = false): IGitLog {
enrich(data: string, type: GitLogType, fileNameOrRepoPath: string, isRepoPath: boolean = false): IGitLog {
const entries = this._parseEntries(data, isRepoPath);
if (!entries) return undefined;
@ -189,7 +189,7 @@ export class GitLogParserEnricher implements IGitEnricher {
authors.set(entry.author, author);
}
commit = new GitLogCommit(repoPath, entry.sha, relativeFileName, entry.author, moment(entry.authorDate).toDate(), entry.summary, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
commit = new GitLogCommit(type, repoPath, entry.sha, relativeFileName, entry.author, moment(entry.authorDate).toDate(), entry.summary, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
if (relativeFileName !== entry.fileName) {
commit.originalFileName = entry.fileName;

+ 47
- 34
src/git/git.ts View File

@ -91,8 +91,17 @@ export default class Git {
return gitCommand(root, ...params, `--`, file);
}
static diffDir(repoPath: string, sha1: string, sha2?: string) {
const params = [`difftool`, `--dir-diff`, sha1];
if (sha2) {
params.push(sha2);
}
return gitCommand(repoPath, ...params);
}
static diffStatus(repoPath: string, sha1?: string, sha2?: string) {
const params = [`diff`, `--name- status`, `-M`];
const params = [`diff`, `--name-status`, `-M`];
if (sha1) {
params.push(sha1);
}
@ -103,6 +112,43 @@ export default class Git {
return gitCommand(repoPath, ...params);
}
static async getVersionedFile(fileName: string, repoPath: string, sha: string) {
const data = await Git.getVersionedFileText(fileName, repoPath, sha);
const ext = path.extname(fileName);
return new Promise<string>((resolve, reject) => {
tmp.file({ prefix: `${path.basename(fileName, ext)}-${sha}__`, postfix: ext },
(err, destination, fd, cleanupCallback) => {
if (err) {
reject(err);
return;
}
Logger.log(`getVersionedFile(${fileName}, ${repoPath}, ${sha}); destination=${destination}`);
fs.appendFile(destination, data, err => {
if (err) {
reject(err);
return;
}
resolve(destination);
});
});
});
}
static getVersionedFileText(fileName: string, repoPath: string, sha: string) {
const [file, root] = Git.splitPath(Git.normalizePath(fileName), repoPath);
sha = sha.replace('^', '');
if (Git.isUncommitted(sha)) return Promise.reject(new Error(`sha=${sha} is uncommitted`));
return gitCommand(root, 'show', `${sha}:./${file}`);
}
static gitInfo(): IGit {
return git;
}
static log(fileName: string, sha?: string, repoPath?: string, maxCount?: number, reverse: boolean = false) {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
@ -150,39 +196,6 @@ export default class Git {
return gitCommand(repoPath, ...params);
}
static async getVersionedFile(fileName: string, repoPath: string, sha: string) {
const data = await Git.getVersionedFileText(fileName, repoPath, sha);
const ext = path.extname(fileName);
return new Promise<string>((resolve, reject) => {
tmp.file({ prefix: `${path.basename(fileName, ext)}-${sha}__`, postfix: ext },
(err, destination, fd, cleanupCallback) => {
if (err) {
reject(err);
return;
}
Logger.log(`getVersionedFile(${fileName}, ${repoPath}, ${sha}); destination=${destination}`);
fs.appendFile(destination, data, err => {
if (err) {
reject(err);
return;
}
resolve(destination);
});
});
});
}
static getVersionedFileText(fileName: string, repoPath: string, sha: string) {
const [file, root] = Git.splitPath(Git.normalizePath(fileName), repoPath);
sha = sha.replace('^', '');
if (Git.isUncommitted(sha)) return Promise.reject(new Error(`sha=${sha} is uncommitted`));
return gitCommand(root, 'show', `${sha}:./${file}`);
}
static statusFile(fileName: string, repoPath: string): Promise<string> {
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);

+ 4
- 1
src/git/gitEnrichment.ts View File

@ -103,12 +103,15 @@ export class GitCommit implements IGitCommit {
}
}
export type GitLogType = 'file' | 'repo';
export class GitLogCommit extends GitCommit {
fileStatuses: { status: GitFileStatus, fileName: string }[];
status: GitFileStatus;
constructor(
public type: GitLogType,
repoPath: string,
sha: string,
fileName: string,
@ -145,7 +148,7 @@ export interface IGitCommitLine {
export interface IGitLog {
repoPath: string;
authors: Map<string, IGitAuthor>;
commits: Map<string, GitCommit>;
commits: Map<string, GitLogCommit>;
}
export declare type GitFileStatus = '?' | 'A' | 'C' | 'D' | 'M' | 'R' | 'U';

+ 13
- 2
src/gitCodeLensProvider.ts View File

@ -271,6 +271,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickCommitDetails: return this._applyShowQuickCommitDetailsCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickCommitFileDetails: return this._applyShowQuickCommitFileDetailsCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickFileHistory: return this._applyShowQuickFileHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
case CodeLensCommand.ShowQuickRepoHistory: return this._applyShowQuickRepoHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
default: return lens;
@ -291,6 +292,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickCommitDetails: return this._applyShowQuickCommitDetailsCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickCommitFileDetails: return this._applyShowQuickCommitFileDetailsCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickFileHistory: return this._applyShowQuickFileHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
case CodeLensCommand.ShowQuickRepoHistory: return this._applyShowQuickRepoHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
default: return lens;
@ -363,8 +365,17 @@ export default class GitCodeLensProvider implements CodeLensProvider {
_applyShowQuickCommitDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickFileHistory,
arguments: [Uri.file(lens.uri.fsPath), undefined, commit]
command: CodeLensCommand.ShowQuickCommitDetails,
arguments: [Uri.file(lens.uri.fsPath), commit.sha, commit]
};
return lens;
}
_applyShowQuickCommitFileDetailsCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit): T {
lens.command = {
title: title,
command: CodeLensCommand.ShowQuickCommitFileDetails,
arguments: [Uri.file(lens.uri.fsPath), commit.sha, commit]
};
return lens;
}

+ 24
- 10
src/gitProvider.ts View File

@ -293,13 +293,22 @@ export class GitProvider extends Disposable {
return Git.repoPath(cwd);
}
async getRepoPathFromUri(uri?: Uri, fallbackRepoPath?: string): Promise<string | undefined> {
if (!(uri instanceof Uri)) return fallbackRepoPath;
const gitUri = await GitUri.fromUri(uri, this);
if (gitUri.repoPath) return gitUri.repoPath;
return (await this.getRepoPathFromFile(gitUri.fsPath)) || fallbackRepoPath;
}
async getRepoPathFromFile(fileName: string): Promise<string | undefined> {
const log = await this.getLogForFile(fileName, undefined, undefined, undefined, 1);
return log && log.repoPath;
}
getBlameForFile(fileName: string, sha?: string, repoPath?: string): Promise<IGitBlame | undefined> {
Logger.log(`getBlameForFile('${fileName}', ${sha}, ${repoPath})`);
Logger.log(`getBlameForFile('${fileName}', ${sha}, '${repoPath}')`);
fileName = Git.normalizePath(fileName);
const useCaching = this.UseGitCaching && !sha;
@ -362,7 +371,7 @@ export class GitProvider extends Disposable {
}
async getBlameForLine(fileName: string, line: number, sha?: string, repoPath?: string): Promise<IGitBlameLine | undefined> {
Logger.log(`getBlameForLine('${fileName}', ${line}, ${sha}, ${repoPath})`);
Logger.log(`getBlameForLine('${fileName}', ${line}, ${sha}, '${repoPath}')`);
if (this.UseGitCaching && !sha) {
const blame = await this.getBlameForFile(fileName);
@ -400,7 +409,7 @@ export class GitProvider extends Disposable {
}
async getBlameForRange(fileName: string, range: Range, sha?: string, repoPath?: string): Promise<IGitBlameLines | undefined> {
Logger.log(`getBlameForRange('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, ${repoPath})`);
Logger.log(`getBlameForRange('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, '${repoPath}')`);
const blame = await this.getBlameForFile(fileName, sha, repoPath);
if (!blame) return undefined;
@ -408,7 +417,7 @@ export class GitProvider extends Disposable {
}
getBlameForRangeSync(blame: IGitBlame, fileName: string, range: Range, sha?: string, repoPath?: string): IGitBlameLines | undefined {
Logger.log(`getBlameForRangeSync('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, ${repoPath})`);
Logger.log(`getBlameForRangeSync('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, '${repoPath}')`);
if (!blame.lines.length) return Object.assign({ allLines: blame.lines }, blame);
@ -455,7 +464,7 @@ export class GitProvider extends Disposable {
}
async getBlameLocations(fileName: string, range: Range, sha?: string, repoPath?: string, selectedSha?: string, line?: number): Promise<Location[] | undefined> {
Logger.log(`getBlameLocations('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, ${repoPath})`);
Logger.log(`getBlameLocations('${fileName}', [${range.start.line}, ${range.end.line}], ${sha}, '${repoPath}')`);
const blame = await this.getBlameForRange(fileName, range, sha, repoPath);
if (!blame) return undefined;
@ -494,7 +503,7 @@ export class GitProvider extends Disposable {
}
getLogForFile(fileName: string, sha?: string, repoPath?: string, range?: Range, maxCount?: number, reverse: boolean = false): Promise<IGitLog | undefined> {
Logger.log(`getLogForFile('${fileName}', ${sha}, ${repoPath}, ${range && `[${range.start.line}, ${range.end.line}]`}, ${maxCount})`);
Logger.log(`getLogForFile('${fileName}', ${sha}, '${repoPath}', ${range && `[${range.start.line}, ${range.end.line}]`}, ${maxCount})`);
fileName = Git.normalizePath(fileName);
const useCaching = this.UseGitCaching && !sha && !range && !maxCount;
@ -555,7 +564,7 @@ export class GitProvider extends Disposable {
}
async getLogLocations(fileName: string, sha?: string, repoPath?: string, selectedSha?: string, line?: number): Promise<Location[] | undefined> {
Logger.log(`getLogLocations('${fileName}', ${sha}, ${repoPath}, ${selectedSha}, ${line})`);
Logger.log(`getLogLocations('${fileName}', ${sha}, '${repoPath}', ${selectedSha}, ${line})`);
const log = await this.getLogForFile(fileName, sha, repoPath);
if (!log) return undefined;
@ -590,13 +599,13 @@ export class GitProvider extends Disposable {
}
async isFileUncommitted(fileName: string, repoPath: string): Promise<boolean> {
Logger.log(`isFileUncommitted('${fileName}', ${repoPath})`);
Logger.log(`isFileUncommitted('${fileName}', '${repoPath}')`);
const status = await this.getStatusForFile(fileName, repoPath);
return !!status;
}
async getVersionedFile(fileName: string, repoPath: string, sha: string) {
Logger.log(`getVersionedFile('${fileName}', ${repoPath}, ${sha})`);
Logger.log(`getVersionedFile('${fileName}', '${repoPath}', ${sha})`);
const file = await Git.getVersionedFile(fileName, repoPath, sha);
if (this.UseUriCaching) {
@ -608,7 +617,7 @@ export class GitProvider extends Disposable {
}
getVersionedFileText(fileName: string, repoPath: string, sha: string) {
Logger.log(`getVersionedFileText('${fileName}', ${repoPath}, ${sha})`);
Logger.log(`getVersionedFileText('${fileName}', '${repoPath}', ${sha})`);
return Git.getVersionedFileText(fileName, repoPath, sha);
}
@ -618,6 +627,11 @@ export class GitProvider extends Disposable {
this.hasGitUriForFile(editor));
}
openDirectoryDiff(repoPath: string, sha1: string, sha2?: string) {
Logger.log(`openDirectoryDiff('${repoPath}', ${sha1}, ${sha2})`);
return Git.diffDir(repoPath, sha1, sha2);
}
toggleCodeLens(editor: TextEditor) {
if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
(!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled)) return;

+ 2
- 1
src/quickPicks.ts View File

@ -1,7 +1,8 @@
'use strict';
export { CommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks/quickPicks';
export { CommitQuickPickItem, CommitWithFileStatusQuickPickItem } from './quickPicks/gitQuickPicks';
export { OpenCommitFileCommandQuickPickItem, OpenCommitWorkingTreeFileCommandQuickPickItem, OpenCommitFilesCommandQuickPickItem, OpenCommitWorkingTreeFilesCommandQuickPickItem, CommitDetailsQuickPick, CommitFileDetailsQuickPick } from './quickPicks/commitDetails';
export { OpenCommitFilesCommandQuickPickItem, OpenCommitWorkingTreeFilesCommandQuickPickItem, CommitDetailsQuickPick } from './quickPicks/commitDetails';
export { OpenCommitFileCommandQuickPickItem, OpenCommitWorkingTreeFileCommandQuickPickItem, CommitFileDetailsQuickPick } from './quickPicks/commitFileDetails';
export { FileHistoryQuickPick } from './quickPicks/fileHistory';
export { RepoHistoryQuickPick } from './quickPicks/repoHistory';
export { OpenStatusFileCommandQuickPickItem, OpenStatusFilesCommandQuickPickItem, RepoStatusQuickPick } from './quickPicks/repoStatus';

+ 27
- 119
src/quickPicks/commitDetails.ts View File

@ -1,45 +1,20 @@
'use strict';
import { Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard } from '../commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { GitLogCommit, GitProvider } from '../gitProvider';
import { CommitWithFileStatusQuickPickItem } from './gitQuickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFilesCommandQuickPickItem } from './quickPicks';
import * as moment from 'moment';
import * as path from 'path';
export { CommandQuickPickItem, CommitWithFileStatusQuickPickItem };
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = GitProvider.toGitContentUri(commit);
super(uri, item || {
label: `$(file-symlink-file) Open File`,
description: `\u00a0 \u2014 \u00a0\u00a0 as of \u00a0 $(git-commit) \u00a0 ${commit.sha} \u00a0\u2022\u00a0 ${commit.getFormattedPath()}`
});
}
}
export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
super(uri, item || {
label: `$(file-symlink-file) Open Working File`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.getFormattedPath()}`
});
}
}
export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) {
const repoPath = commit.repoPath;
const uris = commit.fileStatuses.map(_ => GitProvider.toGitContentUri(commit.sha, _.fileName, repoPath, commit.originalFileName));
super(uris, item || {
label: `$(file-symlink-file) Open Files`,
description: `\u00a0 \u2014 \u00a0\u00a0 as of \u00a0 $(git-commit) \u00a0 ${commit.sha}`
label: `$(file-symlink-file) Open Changed Files`,
description: `\u00a0 \u2014 \u00a0\u00a0 in \u00a0$(git-commit) ${commit.sha}`
//detail: `Opens all of the changed files in $(git-commit) ${commit.sha}`
});
}
@ -51,7 +26,7 @@ export class OpenCommitWorkingTreeFilesCommandQuickPickItem extends OpenFilesCom
const repoPath = commit.repoPath;
const uris = commit.fileStatuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
super(uris, item || {
label: `$(file-symlink-file) Open Working Files`,
label: `$(file-symlink-file) Open Changed Working Files`,
description: undefined
//detail: `Opens all of the changed file in the working tree`
});
@ -63,103 +38,35 @@ export class CommitDetailsQuickPick {
static async show(commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem): Promise<CommitWithFileStatusQuickPickItem | CommandQuickPickItem | undefined> {
const items: (CommitWithFileStatusQuickPickItem | CommandQuickPickItem)[] = commit.fileStatuses.map(fs => new CommitWithFileStatusQuickPickItem(commit, fs.fileName, fs.status));
items.splice(0, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Sha to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
items.splice(1, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
items.splice(2, 0, new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit));
items.splice(3, 0, new OpenCommitFilesCommandQuickPickItem(commit));
if (goBackCommand) {
items.splice(0, 0, goBackCommand);
}
await Keyboard.instance.enterScope(['left', goBackCommand]);
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
matchOnDetail: true,
placeHolder: `${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item);
}
} as QuickPickOptions);
await Keyboard.instance.exitScope();
return pick;
}
}
export class CommitFileDetailsQuickPick {
static async show(git: GitProvider, commit: GitCommit, workingFileName: string, uri: Uri, currentCommand?: CommandQuickPickItem, goBackCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = {}): Promise<CommandQuickPickItem | undefined> {
const items: CommandQuickPickItem[] = [];
let index = 0;
const workingName = (workingFileName && path.basename(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 log = await git.getLogForFile(commit.uri.fsPath, undefined, undefined, undefined, 2);
if (!log) return undefined;
commit = Iterables.first(log.commits.values());
}
if (commit.previousSha) {
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousSha} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.sha}`
}, Commands.DiffWithPrevious, [commit.uri, commit]));
}
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha} \u00a0 $(git-compare) \u00a0 $(file-text) ${workingName}`
}, Commands.DiffWithWorking, [uri, commit]));
items.push(new CommandQuickPickItem({
label: `$(diff) Show Changed Files`,
description: undefined, //`\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`,
detail: `Shows all of the changed files in commit $(git-commit) ${commit.sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), commit.sha, undefined, currentCommand]));
items.push(new CommandQuickPickItem({
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Sha to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.sha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
items.push(new CommandQuickPickItem({
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(clippy) Copy Commit Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.message}`
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
items.push(new OpenCommitWorkingTreeFileCommandQuickPickItem(commit));
items.push(new OpenCommitFileCommandQuickPickItem(commit));
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(git-compare) Directory Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousSha || `${commit.sha}^`} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.sha}`
}, Commands.DiffDirectory, [commit.uri, commit.previousSha || `${commit.sha}^`, commit.sha]));
if (options.showFileHistory) {
if (workingFileName) {
items.push(new CommandQuickPickItem({
label: `$(history) Show File History`,
description: undefined, //`\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)}`,
detail: `Shows the commit history of the file, starting at the most recent commit`
}, Commands.ShowQuickFileHistory, [commit.uri, undefined, undefined, currentCommand]));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `$(git-compare) Directory Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha} \u00a0 $(git-compare) \u00a0 $(file-directory) Working Tree`
}, Commands.DiffDirectory, [uri, commit.sha]));
items.push(new CommandQuickPickItem({
label: `$(history) Show Previous File History`,
description: undefined, //`\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)}`,
detail: `Shows the previous commit history of the file, starting at $(git-commit) ${commit.sha}`
}, Commands.ShowQuickFileHistory, [new GitUri(commit.uri, commit), undefined, undefined, currentCommand]));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `Changed Files`,
description: null
}, Commands.ShowQuickCommitDetails, [uri, commit.sha, commit, goBackCommand]));
items.push(new OpenCommitFilesCommandQuickPickItem(commit));
items.push(new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit));
if (goBackCommand) {
items.splice(0, 0, goBackCommand);
@ -169,7 +76,8 @@ export class CommitFileDetailsQuickPick {
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
placeHolder: `${commit.getFormattedPath()} \u2022 ${isUncommitted ? 'Uncommitted \u21E8 ' : '' }${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
matchOnDetail: true,
placeHolder: `${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item);

+ 111
- 0
src/quickPicks/commitFileDetails.ts View File

@ -0,0 +1,111 @@
'use strict';
import { Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard } from '../commands';
import { GitCommit, GitLogCommit, GitProvider, GitUri } from '../gitProvider';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem } from './quickPicks';
import * as moment from 'moment';
import * as path from 'path';
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = GitProvider.toGitContentUri(commit);
super(uri, item || {
label: `$(file-symlink-file) Open File`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)} in \u00a0$(git-commit) ${commit.sha}`
});
}
}
export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitCommit, item?: QuickPickItem) {
const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
super(uri, item || {
label: `$(file-symlink-file) Open Working File`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)}`
});
}
}
export class CommitFileDetailsQuickPick {
static async show(git: GitProvider, commit: GitCommit | GitLogCommit, workingFileName: string, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, options: { showFileHistory?: boolean } = {}): Promise<CommandQuickPickItem | undefined> {
const items: CommandQuickPickItem[] = [];
const workingName = (workingFileName && path.basename(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 log = await git.getLogForFile(commit.uri.fsPath, undefined, undefined, undefined, 2);
if (!log) return undefined;
commit = Iterables.first(log.commits.values());
}
if (!options.showFileHistory) {
items.push(new CommandQuickPickItem({
label: `$(git-commit) Show Commit Details`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha}`
}, Commands.ShowQuickCommitDetails, [new GitUri(commit.uri, commit), commit.sha, commit, currentCommand]));
}
if (commit.previousSha) {
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Previous Commit`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.previousSha} \u00a0 $(git-compare) \u00a0 $(git-commit) ${commit.sha}`
}, Commands.DiffWithPrevious, [commit.uri, commit]));
}
items.push(new CommandQuickPickItem({
label: `$(git-compare) Compare with Working Tree`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(git-commit) ${commit.sha} \u00a0 $(git-compare) \u00a0 $(file-text) ${workingName}`
}, Commands.DiffWithWorking, [uri, commit]));
items.push(new CommandQuickPickItem({
label: `$(clippy) Copy Commit Sha to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.sha}`
}, Commands.CopyShaToClipboard, [uri, commit.sha]));
items.push(new CommandQuickPickItem({
label: `$(clippy) Copy Commit Message to Clipboard`,
description: `\u00a0 \u2014 \u00a0\u00a0 ${commit.message}`
}, Commands.CopyMessageToClipboard, [uri, commit.sha, commit.message]));
items.push(new OpenCommitFileCommandQuickPickItem(commit));
items.push(new OpenCommitWorkingTreeFileCommandQuickPickItem(commit));
if (workingFileName && options.showFileHistory) {
items.push(new CommandQuickPickItem({
label: `$(history) Show File History`,
description: `\u00a0 \u2014 \u00a0\u00a0 of ${path.basename(commit.fileName)}`
}, Commands.ShowQuickFileHistory, [commit.uri, undefined, currentCommand]));
}
items.push(new CommandQuickPickItem({
label: `$(history) Show ${workingFileName && options.showFileHistory ? 'Previous ' : ''}File History`,
description: `\u00a0 \u2014 \u00a0\u00a0 of ${path.basename(commit.fileName)} \u00a0\u2022\u00a0 starting from \u00a0$(git-commit) ${commit.sha}`
}, Commands.ShowQuickFileHistory, [new GitUri(commit.uri, commit), undefined, currentCommand]));
if (goBackCommand) {
items.splice(0, 0, goBackCommand);
}
await Keyboard.instance.enterScope(['left', goBackCommand]);
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
placeHolder: `${commit.getFormattedPath()} \u2022 ${isUncommitted ? 'Uncommitted \u21E8 ' : '' }${commit.sha} \u2022 ${commit.author}, ${moment(commit.date).fromNow()} \u2022 ${commit.message}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item);
}
} as QuickPickOptions);
await Keyboard.instance.exitScope();
return pick;
}
}

+ 13
- 9
src/quickPicks/fileHistory.ts View File

@ -5,12 +5,11 @@ import { Commands, Keyboard } from '../commands';
import { IGitLog } from '../gitProvider';
import { CommitQuickPickItem } from './gitQuickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks';
export { CommandQuickPickItem, CommitQuickPickItem };
import * as path from 'path';
export class FileHistoryQuickPick {
static async show(log: IGitLog, uri: Uri, maxCount: number, defaultMaxCount: number, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
static async show(log: IGitLog, uri: Uri, sha: string, maxCount: number, defaultMaxCount: number, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[];
if (maxCount !== 0 && items.length >= defaultMaxCount) {
@ -18,7 +17,7 @@ export class FileHistoryQuickPick {
label: `$(sync) Show All Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 Currently only showing the first ${defaultMaxCount} commits`,
detail: `This may take a while`
}, Commands.ShowQuickFileHistory, [uri, 0, undefined, goBackCommand]));
}, Commands.ShowQuickFileHistory, [uri, 0, goBackCommand]));
}
// Only show the full repo option if we are the root
@ -27,10 +26,15 @@ export class FileHistoryQuickPick {
label: `$(repo) Show Repository History`,
description: null,
detail: 'Shows the commit history of the repository'
}, Commands.ShowQuickRepoHistory, [undefined, undefined, undefined, new CommandQuickPickItem({
label: `go back \u21A9`,
description: null
}, Commands.ShowQuickFileHistory, [uri, maxCount])]));
}, Commands.ShowQuickRepoHistory,
[
undefined,
undefined,
new CommandQuickPickItem({
label: `go back \u21A9`,
description: `\u00a0 \u2014 \u00a0\u00a0 to history of \u00a0$(file-text) ${path.basename(uri.fsPath)}`
}, Commands.ShowQuickFileHistory, [uri, maxCount])
]));
}
if (goBackCommand) {
@ -44,7 +48,7 @@ export class FileHistoryQuickPick {
const pick = await window.showQuickPick(items, {
matchOnDescription: true,
matchOnDetail: true,
placeHolder: commit.getFormattedPath(),
placeHolder: `${commit.getFormattedPath()}${sha ? ` \u00a0\u2022\u00a0 ${sha}` : ''}`,
ignoreFocusOut: getQuickPickIgnoreFocusOut(),
onDidSelectItem: (item: QuickPickItem) => {
Keyboard.instance.setKeyCommand('right', item);

+ 3
- 16
src/quickPicks/quickPicks.ts View File

@ -1,25 +1,12 @@
'use strict';
import { commands, QuickPickItem, TextEditor, Uri, window, workspace } from 'vscode';
import { Commands } from '../commands';
import { commands, QuickPickItem, TextEditor, Uri, workspace } from 'vscode';
import { Commands, openEditor } from '../commands';
import { IAdvancedConfig } from '../configuration';
import { BuiltInCommands } from '../constants';
export function getQuickPickIgnoreFocusOut() {
return !workspace.getConfiguration('gitlens').get<IAdvancedConfig>('advanced').quickPick.closeOnFocusOut;
}
export async function openEditor(uri: Uri, pinned: boolean = false) {
try {
if (!pinned) return await commands.executeCommand(BuiltInCommands.Open, uri);
const document = await workspace.openTextDocument(uri);
return window.showTextDocument(document, (window.activeTextEditor && window.activeTextEditor.viewColumn) || 1, true);
}
catch (ex) {
return undefined;
}
}
export class CommandQuickPickItem implements QuickPickItem {
label: string;
@ -58,7 +45,7 @@ export class OpenFilesCommandQuickPickItem extends CommandQuickPickItem {
async execute(): Promise<{}> {
for (const uri of this.uris) {
openEditor(uri, true);
await openEditor(uri, true);
}
return undefined;
}

+ 1
- 3
src/quickPicks/repoHistory.ts View File

@ -6,8 +6,6 @@ import { IGitLog } from '../gitProvider';
import { CommitQuickPickItem } from './gitQuickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from './quickPicks';
export { CommandQuickPickItem, CommitQuickPickItem };
export class RepoHistoryQuickPick {
static async show(log: IGitLog, uri: Uri, maxCount: number, defaultMaxCount: number, goBackCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
@ -18,7 +16,7 @@ export class RepoHistoryQuickPick {
label: `$(sync) Show All Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 Currently only showing the first ${defaultMaxCount} commits`,
detail: `This may take a while`
}, Commands.ShowQuickRepoHistory, [uri, 0, undefined, goBackCommand]));
}, Commands.ShowQuickRepoHistory, [uri, 0, goBackCommand]));
}
if (goBackCommand) {

+ 37
- 20
src/quickPicks/repoStatus.ts View File

@ -1,13 +1,11 @@
'use strict';
import { Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Keyboard } from '../commands';
import { Commands, Keyboard } from '../commands';
import { getGitStatusIcon, GitFileStatusItem } from '../gitProvider';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem } from './quickPicks';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem } from './quickPicks';
import * as path from 'path';
export { CommandQuickPickItem };
export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(status: GitFileStatusItem, item?: QuickPickItem) {
@ -26,17 +24,17 @@ export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPick
}
}
export class OpenStatusFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem {
constructor(statuses: GitFileStatusItem[], item?: QuickPickItem) {
const repoPath = statuses.length && statuses[0].repoPath;
const uris = statuses.map(_ => Uri.file(path.resolve(repoPath, _.fileName)));
super(uris, item || {
label: `$(file-symlink-file) Open Files`,
description: undefined,
detail: `Opens all of the changed files in the repository`
});
super(item || {
label: `$(file-symlink-file) Open Changed Files`,
description: undefined
//detail: `Opens all of the changed files in the repository`
}, Commands.OpenChangedFiles, [undefined, uris]);
}
}
@ -49,23 +47,42 @@ export class RepoStatusQuickPick {
const items = Array.from(Iterables.map(statuses, s => new OpenStatusFileCommandQuickPickItem(s))) as (OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem)[];
if (statuses.some(_ => _.staged)) {
const index = statuses.findIndex(_ => !_.staged);
if (index > -1) {
items.splice(index, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && !_.staged), {
label: `$(file-symlink-file) Open Unstaged Files`,
description: undefined,
detail: `Opens all of the unstaged files in the repository`
let index = 0;
const unstagedIndex = statuses.findIndex(_ => !_.staged);
if (unstagedIndex > -1) {
items.splice(unstagedIndex, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: undefined
}, Commands.ShowQuickRepoStatus, [goBackCommand]));
items.splice(index++, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && _.staged), {
label: `$(file-symlink-file) Open Staged Files`,
description: undefined
}));
items.splice(0, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && _.staged), {
label: `$(file-symlink-file) Open Staged Files`,
description: undefined,
detail: `Opens all of the staged files in the repository`
items.splice(index++, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D' && !_.staged), {
label: `$(file-symlink-file) Open Unstaged Files`,
description: undefined
}));
}
items.splice(index++, 0, new CommandQuickPickItem({
label: `Staged Files`,
description: undefined
}, Commands.ShowQuickRepoStatus, [goBackCommand]));
}
else if (statuses.some(_ => !_.staged)) {
items.splice(0, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: undefined
}, Commands.ShowQuickRepoStatus, [goBackCommand]));
}
if (statuses.length) {
items.splice(0, 0, new CommandQuickPickItem({
label: '$(x) Close Unchanged Files',
description: null
}, Commands.CloseUnchangedFiles));
items.splice(0, 0, new OpenStatusFilesCommandQuickPickItem(statuses.filter(_ => _.status !== 'D')));
}

Loading…
Cancel
Save