mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-02-17 02:51:47 -05:00
Adds support for git commands on scheme=git
Rewrites blame annotation controller and provider - fixes whitespace issues, reduces overhead, and provides better performance Rewrites status bar blame support - reduces overhead and provides better performance Adds showFileHistory command to status bar Renames showHistory to showFileHistory Fixes log to use iso 8601 for dates
This commit is contained in:
@@ -4,17 +4,22 @@
|
|||||||
### 0.9.0
|
### 0.9.0
|
||||||
|
|
||||||
- Adds support for git history (log)!
|
- Adds support for git history (log)!
|
||||||
- Adds new `gitlens.showHistory` command to open the history explorer
|
- Adds support for blame annotations and git commands on file revisions
|
||||||
- Adds new `gitlens.showHistory` option to the `gitlens.codeLens.recentChange.command` & `gitlens.codeLens.authors.command` settings
|
- Adds ability to show multiple blame annotation at the same time (one per vscode editor)
|
||||||
|
- Adds new `gitlens.showFileHistory` command to open the history explorer
|
||||||
|
- Adds new `gitlens.showFileHistory` option to the `gitlens.codeLens.recentChange.command`, `gitlens.codeLens.authors.command`, and `gitlens.statusBar.command` settings
|
||||||
- Adds per-language CodeLens location customization using the `gitlens.codeLens.languageLocations` setting
|
- Adds per-language CodeLens location customization using the `gitlens.codeLens.languageLocations` setting
|
||||||
- Adds new `gitlens.diffLineWithPrevious` command for line sensitive diffs
|
- Adds new `gitlens.diffLineWithPrevious` command for line sensitive diffs
|
||||||
- Adds new `gitlens.diffLineWithWorking` command for line sensitive diffs
|
- Adds new `gitlens.diffLineWithWorking` command for line sensitive diffs
|
||||||
- Adds `gitlens.diffWithPrevious` command to the explorer context menu
|
- Adds `gitlens.diffWithPrevious` command to the explorer context menu
|
||||||
- Adds output channel logging, controlled by the `gitlens.advanced.output.level` setting
|
- Adds output channel logging, controlled by the `gitlens.advanced.output.level` setting
|
||||||
|
- Complete rewrite of the blame annotation provider to reduce overhead and provide better performance
|
||||||
- Improves performance (significantly) when only showing CodeLens at the document level
|
- Improves performance (significantly) when only showing CodeLens at the document level
|
||||||
|
- Improves performance of status bar blame support
|
||||||
- Changes `gitlens.diffWithPrevious` command to always be file sensitive diffs
|
- Changes `gitlens.diffWithPrevious` command to always be file sensitive diffs
|
||||||
- Changes `gitlens.diffWithWorking` command to always be file sensitive diffs
|
- Changes `gitlens.diffWithWorking` command to always be file sensitive diffs
|
||||||
- Removes all debug logging, unless the `gitlens.advanced.debug` settings it on
|
- Removes all debug logging, unless the `gitlens.advanced.debug` settings it on
|
||||||
|
- Fixes many (most?) issues with whitespace toggling (required because of https://github.com/Microsoft/vscode/issues/11485)
|
||||||
- Fixes issue where blame annotations would not be cleared properly when switching between open files
|
- Fixes issue where blame annotations would not be cleared properly when switching between open files
|
||||||
|
|
||||||
### 0.5.5
|
### 0.5.5
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ Must be using Git and it must be in your path.
|
|||||||
|`gitlens.codeLens.locationCustomSymbols`|Specifies the set of document symbols to render active document CodeLens on. Must be a member of `SymbolKind`
|
|`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.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.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.showHistory` - opens the history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension
|
|`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. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension
|
||||||
|`gitlens.codeLens.authors.enabled`|Specifies whether the authors CodeLens is shown
|
|`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.showHistory` - opens the history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension
|
|`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. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension
|
||||||
|`gitlens.menus.fileDiff.enabled`|Specifies whether file-based diff commands will be added to the context menus
|
|`gitlens.menus.fileDiff.enabled`|Specifies whether file-based diff commands will be added to the context menus
|
||||||
|`gitlens.menus.lineDiff.enabled`|Specifies whether line-based diff commands will be added to the context menus
|
|`gitlens.menus.lineDiff.enabled`|Specifies whether line-based diff commands will be added to the context menus
|
||||||
|`gitlens.statusBar.enabled`|Specifies whether blame information is shown in the status bar
|
|`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.showHistory` - opens the history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
|`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. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
||||||
|
|
||||||
---
|
---
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|||||||
30
package.json
30
package.json
@@ -156,15 +156,15 @@
|
|||||||
},
|
},
|
||||||
"gitlens.codeLens.recentChange.command": {
|
"gitlens.codeLens.recentChange.command": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "gitlens.showHistory",
|
"default": "gitlens.showFileHistory",
|
||||||
"enum": [
|
"enum": [
|
||||||
"gitlens.toggleBlame",
|
"gitlens.toggleBlame",
|
||||||
"gitlens.showBlameHistory",
|
"gitlens.showBlameHistory",
|
||||||
"gitlens.showHistory",
|
"gitlens.showFileHistory",
|
||||||
"gitlens.diffWithPrevious",
|
"gitlens.diffWithPrevious",
|
||||||
"git.viewFileHistory"
|
"git.viewFileHistory"
|
||||||
],
|
],
|
||||||
"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.showHistory` - opens the history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
"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. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
||||||
},
|
},
|
||||||
"gitlens.codeLens.authors.enabled": {
|
"gitlens.codeLens.authors.enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -177,11 +177,11 @@
|
|||||||
"enum": [
|
"enum": [
|
||||||
"gitlens.toggleBlame",
|
"gitlens.toggleBlame",
|
||||||
"gitlens.showBlameHistory",
|
"gitlens.showBlameHistory",
|
||||||
"gitlens.showHistory",
|
"gitlens.showFileHistory",
|
||||||
"gitlens.diffWithPrevious",
|
"gitlens.diffWithPrevious",
|
||||||
"git.viewFileHistory"
|
"git.viewFileHistory"
|
||||||
],
|
],
|
||||||
"description": "Specifies the command executed when the authors CodeLens is clicked. `gitlens.toggleBlame` - toggles blame annotations. `gitlens.showBlameHistory` - opens the blame history explorer. `gitlens.showHistory` - opens the history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
"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. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
||||||
},
|
},
|
||||||
"gitlens.statusBar.enabled": {
|
"gitlens.statusBar.enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -194,12 +194,12 @@
|
|||||||
"enum": [
|
"enum": [
|
||||||
"gitlens.toggleBlame",
|
"gitlens.toggleBlame",
|
||||||
"gitlens.showBlameHistory",
|
"gitlens.showBlameHistory",
|
||||||
"gitlens.showHistory",
|
"gitlens.showFileHistory",
|
||||||
"gitlens.diffWithPrevious",
|
"gitlens.diffWithPrevious",
|
||||||
"gitlens.toggleCodeLens",
|
"gitlens.toggleCodeLens",
|
||||||
"git.viewFileHistory"
|
"git.viewFileHistory"
|
||||||
],
|
],
|
||||||
"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.showHistory` - opens the history explorer. `gitlens.diffWithPrevious` - compares the current checked-in file with the previous commit. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
"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. `git.viewFileHistory` - opens a file history picker, which requires the Git History (git log) extension"
|
||||||
},
|
},
|
||||||
"gitlens.menus.fileDiff.enabled": {
|
"gitlens.menus.fileDiff.enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
},
|
},
|
||||||
"gitlens.menus.lineDiff.enabled": {
|
"gitlens.menus.lineDiff.enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": false,
|
||||||
"description": "Specifies whether line-based diff commands will be added to the context menus"
|
"description": "Specifies whether line-based diff commands will be added to the context menus"
|
||||||
},
|
},
|
||||||
"gitlens.advanced.caching.enabled": {
|
"gitlens.advanced.caching.enabled": {
|
||||||
@@ -216,6 +216,11 @@
|
|||||||
"default": true,
|
"default": true,
|
||||||
"description": "Specifies whether git blame output will be cached"
|
"description": "Specifies whether git blame output will be cached"
|
||||||
},
|
},
|
||||||
|
"gitlens.advanced.caching.statusBar.maxLines": {
|
||||||
|
"type": "number",
|
||||||
|
"default": 0,
|
||||||
|
"description": "Specifies whether status bar git blame output will be cached for larger documents"
|
||||||
|
},
|
||||||
"gitlens.advanced.debug": {
|
"gitlens.advanced.debug": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
@@ -280,8 +285,8 @@
|
|||||||
"category": "GitLens"
|
"category": "GitLens"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.showHistory",
|
"command": "gitlens.showFileHistory",
|
||||||
"title": "Open Git History",
|
"title": "Open Git File History",
|
||||||
"category": "GitLens"
|
"category": "GitLens"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -289,6 +294,7 @@
|
|||||||
"explorer/context": [
|
"explorer/context": [
|
||||||
{
|
{
|
||||||
"command": "gitlens.diffWithPrevious",
|
"command": "gitlens.diffWithPrevious",
|
||||||
|
"alt": "gitlens.diffWithWorking",
|
||||||
"when": "config.gitlens.menus.fileDiff.enabled && config.git.enabled",
|
"when": "config.gitlens.menus.fileDiff.enabled && config.git.enabled",
|
||||||
"group": "2_gitlens-file"
|
"group": "2_gitlens-file"
|
||||||
}
|
}
|
||||||
@@ -313,11 +319,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.diffWithWorking",
|
"command": "gitlens.diffWithWorking",
|
||||||
|
"alt": "gitlens.diffLineWithWorking",
|
||||||
"when": "editorTextFocus && config.gitlens.menus.fileDiff.enabled && config.git.enabled",
|
"when": "editorTextFocus && config.gitlens.menus.fileDiff.enabled && config.git.enabled",
|
||||||
"group": "3_gitlens-file@1.0"
|
"group": "3_gitlens-file@1.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "gitlens.diffWithPrevious",
|
"command": "gitlens.diffWithPrevious",
|
||||||
|
"alt": "gitlens.diffLineWithPrevious",
|
||||||
"when": "editorTextFocus && config.gitlens.menus.fileDiff.enabled && config.git.enabled",
|
"when": "editorTextFocus && config.gitlens.menus.fileDiff.enabled && config.git.enabled",
|
||||||
"group": "3_gitlens-file@1.1"
|
"group": "3_gitlens-file@1.1"
|
||||||
},
|
},
|
||||||
@@ -358,7 +366,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "^3.1.2",
|
"mocha": "^3.1.2",
|
||||||
"tslint": "^3.15.1",
|
"tslint": "^3.15.1",
|
||||||
"typescript": "^2.0.8",
|
"typescript": "^2.0.9",
|
||||||
"vscode": "^1.0.3",
|
"vscode": "^1.0.3",
|
||||||
"@types/node": "^6.0.46",
|
"@types/node": "^6.0.46",
|
||||||
"@types/mocha": "^2.2.32",
|
"@types/mocha": "^2.2.32",
|
||||||
|
|||||||
@@ -1,64 +1,184 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Disposable, ExtensionContext, TextEditor, workspace } from 'vscode';
|
import { Functions, IDeferred } from './system';
|
||||||
|
import { commands, Disposable, ExtensionContext, TextDocument, TextEditor, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode';
|
||||||
import { BlameAnnotationProvider } from './blameAnnotationProvider';
|
import { BlameAnnotationProvider } from './blameAnnotationProvider';
|
||||||
|
import { TextDocumentComparer, TextEditorComparer } from './comparers';
|
||||||
|
import { BuiltInCommands } from './constants';
|
||||||
import GitProvider from './gitProvider';
|
import GitProvider from './gitProvider';
|
||||||
|
import { Logger } from './logger';
|
||||||
|
|
||||||
export default class BlameAnnotationController extends Disposable {
|
export default class BlameAnnotationController extends Disposable {
|
||||||
private _disposable: Disposable;
|
private _annotationProviders: Map<number, BlameAnnotationProvider> = new Map();
|
||||||
private _annotationProvider: BlameAnnotationProvider | undefined;
|
private _blameAnnotationsDisposable: Disposable;
|
||||||
|
private _pendingWhitespaceToggleDisposable: Disposable;
|
||||||
|
private _pendingClearAnnotations: Map<number, (() => void) & IDeferred> = new Map();
|
||||||
|
private _pendingWhitespaceToggles: Set<number> = new Set();
|
||||||
|
private _visibleColumns: Set<number>;
|
||||||
|
|
||||||
constructor(private context: ExtensionContext, private git: GitProvider) {
|
constructor(private context: ExtensionContext, private git: GitProvider) {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
|
|
||||||
const subscriptions: Disposable[] = [];
|
|
||||||
|
|
||||||
// subscriptions.push(window.onDidChangeActiveTextEditor(e => {
|
|
||||||
// if (!e || !this._controller || this._controller.editor === e) return;
|
|
||||||
// this.clear();
|
|
||||||
// }));
|
|
||||||
|
|
||||||
subscriptions.push(workspace.onDidCloseTextDocument(d => {
|
|
||||||
if (!this._annotationProvider || this._annotationProvider.uri.fsPath !== d.uri.fsPath) return;
|
|
||||||
this.clear();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._disposable = Disposable.from(...subscriptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.clear();
|
for (const fn of this._pendingClearAnnotations.values()) {
|
||||||
this._disposable && this._disposable.dispose();
|
fn.cancel();
|
||||||
|
}
|
||||||
|
this._annotationProviders.forEach(async (p, i) => await this.clear(i));
|
||||||
|
|
||||||
|
this._blameAnnotationsDisposable && this._blameAnnotationsDisposable.dispose();
|
||||||
|
this._pendingWhitespaceToggleDisposable && this._pendingWhitespaceToggleDisposable.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
async clear(column: number, toggleRenderWhitespace: boolean = true) {
|
||||||
this._annotationProvider && this._annotationProvider.dispose();
|
const provider = this._annotationProviders.get(column);
|
||||||
this._annotationProvider = undefined;
|
if (!provider) return;
|
||||||
|
|
||||||
|
this._annotationProviders.delete(column);
|
||||||
|
await provider.dispose(toggleRenderWhitespace);
|
||||||
|
|
||||||
|
if (this._annotationProviders.size === 0) {
|
||||||
|
Logger.log(`Remove listener registrations for blame annotations`);
|
||||||
|
this._blameAnnotationsDisposable && this._blameAnnotationsDisposable.dispose();
|
||||||
|
this._blameAnnotationsDisposable = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get annotated() {
|
async showBlameAnnotation(editor: TextEditor, shaOrLine?: string | number): Promise<boolean> {
|
||||||
return this._annotationProvider !== undefined;
|
if (!editor || !editor.document) return false;
|
||||||
}
|
|
||||||
|
|
||||||
showBlameAnnotation(editor: TextEditor, sha?: string): Promise<void> {
|
if (!this._blameAnnotationsDisposable && this._annotationProviders.size === 0) {
|
||||||
if (!editor || !editor.document || editor.document.isUntitled) {
|
Logger.log(`Add listener registrations for blame annotations`);
|
||||||
this.clear();
|
|
||||||
return Promise.resolve();
|
const subscriptions: Disposable[] = [];
|
||||||
|
|
||||||
|
subscriptions.push(window.onDidChangeVisibleTextEditors(Functions.debounce(this._onVisibleTextEditorsChanged, 100), this));
|
||||||
|
subscriptions.push(window.onDidChangeTextEditorViewColumn(this._onTextEditorViewColumnChanged, this));
|
||||||
|
subscriptions.push(workspace.onDidCloseTextDocument(this._onTextDocumentClosed, this));
|
||||||
|
|
||||||
|
this._blameAnnotationsDisposable = Disposable.from(...subscriptions);
|
||||||
|
|
||||||
|
this._visibleColumns = this._getVisibleColumns(window.visibleTextEditors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._annotationProvider) {
|
let provider = this._annotationProviders.get(editor.viewColumn);
|
||||||
this._annotationProvider = new BlameAnnotationProvider(this.context, this.git, editor);
|
if (provider) {
|
||||||
return this._annotationProvider.provideBlameAnnotation(sha);
|
if (TextEditorComparer.equals(provider.editor, editor)) {
|
||||||
|
await provider.setSelection(shaOrLine);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await this.clear(provider.editor.viewColumn, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
provider = new BlameAnnotationProvider(this.context, this.git, editor);
|
||||||
|
this._annotationProviders.set(editor.viewColumn, provider);
|
||||||
|
return provider.provideBlameAnnotation(shaOrLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleBlameAnnotation(editor: TextEditor, sha?: string): Promise<void> {
|
async toggleBlameAnnotation(editor: TextEditor, shaOrLine?: string | number): Promise<boolean> {
|
||||||
if (!editor || !editor.document || editor.document.isUntitled || this._annotationProvider) {
|
if (!editor || !editor.document) return false;
|
||||||
this.clear();
|
|
||||||
return Promise.resolve();
|
let provider = this._annotationProviders.get(editor.viewColumn);
|
||||||
|
if (!provider) return this.showBlameAnnotation(editor, shaOrLine);
|
||||||
|
|
||||||
|
await this.clear(provider.editor.viewColumn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getVisibleColumns(editors: TextEditor[]): Set<number> {
|
||||||
|
const set: Set<number> = new Set();
|
||||||
|
for (const e of editors) {
|
||||||
|
if (e.viewColumn === undefined) continue;
|
||||||
|
|
||||||
|
set.add(e.viewColumn);
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onActiveTextEditorChanged(e: TextEditor) {
|
||||||
|
if (e.viewColumn === undefined || this._pendingWhitespaceToggles.size === 0) return;
|
||||||
|
|
||||||
|
if (this._pendingWhitespaceToggles.has(e.viewColumn)) {
|
||||||
|
Logger.log('ActiveTextEditorChanged:', `Remove pending whitespace toggle for column ${e.viewColumn}`);
|
||||||
|
this._pendingWhitespaceToggles.delete(e.viewColumn);
|
||||||
|
|
||||||
|
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- toggle whitespace back on
|
||||||
|
Logger.log('ActiveTextEditorChanged:', `Toggle whitespace rendering on`);
|
||||||
|
commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.showBlameAnnotation(editor, sha);
|
if (this._pendingWhitespaceToggles.size === 0) {
|
||||||
|
Logger.log('ActiveTextEditorChanged:', `Remove listener registrations for pending whitespace toggles`);
|
||||||
|
this._pendingWhitespaceToggleDisposable.dispose();
|
||||||
|
this._pendingWhitespaceToggleDisposable = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onTextDocumentClosed(e: TextDocument) {
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (!TextDocumentComparer.equals(p.document, e)) continue;
|
||||||
|
|
||||||
|
Logger.log('TextDocumentClosed:', `Add pending clear of blame annotations for column ${key}`);
|
||||||
|
|
||||||
|
// Since we don't know if a whole column is going away -- we don't know if we should reset the whitespace
|
||||||
|
// So defer until onDidChangeVisibleTextEditors fires
|
||||||
|
const fn = Functions.debounce(() => {
|
||||||
|
this._pendingClearAnnotations.delete(key);
|
||||||
|
this.clear(key);
|
||||||
|
}, 250);
|
||||||
|
this._pendingClearAnnotations.set(key, fn);
|
||||||
|
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onTextEditorViewColumnChanged(e: TextEditorViewColumnChangeEvent) {
|
||||||
|
this._visibleColumns = this._getVisibleColumns(window.visibleTextEditors);
|
||||||
|
|
||||||
|
Logger.log('TextEditorViewColumnChanged:', `Clear blame annotations for column ${e.viewColumn}`);
|
||||||
|
await this.clear(e.viewColumn);
|
||||||
|
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (!TextEditorComparer.equals(p.editor, e.textEditor)) continue;
|
||||||
|
|
||||||
|
Logger.log('TextEditorViewColumnChanged:', `Clear blame annotations for column ${key}`);
|
||||||
|
await this.clear(key, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onVisibleTextEditorsChanged(e: TextEditor[]) {
|
||||||
|
if (e.every(_ => _.document.uri.scheme === 'inmemory')) return;
|
||||||
|
|
||||||
|
this._visibleColumns = this._getVisibleColumns(e);
|
||||||
|
|
||||||
|
for (const [key, fn] of this._pendingClearAnnotations) {
|
||||||
|
Logger.log('VisibleTextEditorsChanged:', `Remove pending blame annotations for column ${key}`);
|
||||||
|
fn.cancel();
|
||||||
|
this._pendingClearAnnotations.delete(key);
|
||||||
|
|
||||||
|
// Clear and reset the whitespace depending on if the column went away
|
||||||
|
Logger.log('VisibleTextEditorsChanged:', `Clear blame annotations for column ${key}`);
|
||||||
|
await this.clear(key, this._visibleColumns.has(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, p] of this._annotationProviders) {
|
||||||
|
if (e.some(_ => TextEditorComparer.equals(p.editor, _))) continue;
|
||||||
|
|
||||||
|
Logger.log('VisibleTextEditorsChanged:', `Clear blame annotations for column ${key}`);
|
||||||
|
const editor = window.activeTextEditor;
|
||||||
|
if (p.requiresRenderWhitespaceToggle && (editor && editor.viewColumn !== key)) {
|
||||||
|
this.clear(key, false);
|
||||||
|
|
||||||
|
if (!this._pendingWhitespaceToggleDisposable) {
|
||||||
|
Logger.log('VisibleTextEditorsChanged:', `Add listener registrations for pending whitespace toggles`);
|
||||||
|
this._pendingWhitespaceToggleDisposable = window.onDidChangeActiveTextEditor(this._onActiveTextEditorChanged, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.log('VisibleTextEditorsChanged:', `Add pending whitespace toggle for column ${key}`);
|
||||||
|
this._pendingWhitespaceToggles.add(key);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.clear(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Iterables } from './system';
|
import { Iterables } from './system';
|
||||||
import { commands, DecorationOptions, Disposable, ExtensionContext, OverviewRulerLane, Range, TextDocument, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, Uri, window, workspace } from 'vscode';
|
import { commands, DecorationOptions, Disposable, ExtensionContext, OverviewRulerLane, Range, TextDocument, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
||||||
import { BuiltInCommands } from './constants';
|
import { TextDocumentComparer } from './comparers';
|
||||||
import { BlameAnnotationStyle, IBlameConfig } from './configuration';
|
import { BlameAnnotationStyle, IBlameConfig } from './configuration';
|
||||||
import GitProvider, { GitCommit, IGitBlame } from './gitProvider';
|
import { BuiltInCommands } from './constants';
|
||||||
|
import GitProvider, { GitCommit, GitUri, IGitBlame } from './gitProvider';
|
||||||
|
import { Logger } from './logger';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
const blameDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
|
const blameDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({
|
||||||
@@ -15,13 +17,13 @@ const blameDecoration: TextEditorDecorationType = window.createTextEditorDecorat
|
|||||||
let highlightDecoration: TextEditorDecorationType;
|
let highlightDecoration: TextEditorDecorationType;
|
||||||
|
|
||||||
export class BlameAnnotationProvider extends Disposable {
|
export class BlameAnnotationProvider extends Disposable {
|
||||||
public uri: Uri;
|
public document: TextDocument;
|
||||||
|
public requiresRenderWhitespaceToggle: boolean = false;
|
||||||
|
|
||||||
private _blame: Promise<IGitBlame>;
|
private _blame: Promise<IGitBlame>;
|
||||||
private _config: IBlameConfig;
|
private _config: IBlameConfig;
|
||||||
private _disposable: Disposable;
|
private _disposable: Disposable;
|
||||||
private _document: TextDocument;
|
private _uri: GitUri;
|
||||||
private _renderWhitespaceSetting: string;
|
|
||||||
|
|
||||||
constructor(context: ExtensionContext, private git: GitProvider, public editor: TextEditor) {
|
constructor(context: ExtensionContext, private git: GitProvider, public editor: TextEditor) {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
@@ -44,49 +46,56 @@ export class BlameAnnotationProvider extends Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this._document = this.editor.document;
|
this.document = this.editor.document;
|
||||||
this.uri = this._document.uri;
|
this._uri = GitUri.fromUri(this.document.uri);
|
||||||
|
this._blame = this.git.getBlameForFile(this._uri.fsPath, this._uri.sha, this._uri.repoPath);
|
||||||
this._blame = this.git.getBlameForFile(this.uri.fsPath);
|
|
||||||
|
|
||||||
this._config = workspace.getConfiguration('gitlens').get<IBlameConfig>('blame');
|
this._config = workspace.getConfiguration('gitlens').get<IBlameConfig>('blame');
|
||||||
|
|
||||||
const subscriptions: Disposable[] = [];
|
const subscriptions: Disposable[] = [];
|
||||||
|
|
||||||
|
subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this));
|
||||||
subscriptions.push(window.onDidChangeTextEditorSelection(this._onActiveSelectionChanged, this));
|
subscriptions.push(window.onDidChangeTextEditorSelection(this._onActiveSelectionChanged, this));
|
||||||
|
|
||||||
this._disposable = Disposable.from(...subscriptions);
|
this._disposable = Disposable.from(...subscriptions);
|
||||||
|
|
||||||
|
this._onConfigurationChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
async dispose(toggleRenderWhitespace: boolean = true) {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
// HACK: This only works when switching to another editor - diffs handle whitespace toggle differently
|
|
||||||
if (this._renderWhitespaceSetting !== 'none') {
|
|
||||||
commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editor.setDecorations(blameDecoration, []);
|
this.editor.setDecorations(blameDecoration, []);
|
||||||
this.editor.setDecorations(highlightDecoration, []);
|
this.editor.setDecorations(highlightDecoration, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- toggle whitespace back on
|
||||||
|
if (toggleRenderWhitespace && this.requiresRenderWhitespaceToggle) {
|
||||||
|
Logger.log('BlameAnnotationProvider.dispose:', `Toggle whitespace rendering on`);
|
||||||
|
await commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace);
|
||||||
|
}
|
||||||
|
|
||||||
this._disposable && this._disposable.dispose();
|
this._disposable && this._disposable.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _onActiveSelectionChanged(e: TextEditorSelectionChangeEvent) {
|
private _onConfigurationChanged() {
|
||||||
const blame = await this.git.getBlameForLine(e.textEditor.document.fileName, e.selections[0].active.line);
|
const renderWhitespace = workspace.getConfiguration('editor').get<string>('renderWhitespace');
|
||||||
if (blame) {
|
this.requiresRenderWhitespaceToggle = !(renderWhitespace == null || renderWhitespace === 'none');
|
||||||
this._applyCommitHighlight(blame.commit.sha);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async provideBlameAnnotation(sha?: string) {
|
private async _onActiveSelectionChanged(e: TextEditorSelectionChangeEvent) {
|
||||||
|
if (!TextDocumentComparer.equals(this.document, e.textEditor && e.textEditor.document)) return;
|
||||||
|
|
||||||
|
return this.setSelection(e.selections[0].active.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
async provideBlameAnnotation(shaOrLine?: string | number): Promise<boolean> {
|
||||||
const blame = await this._blame;
|
const blame = await this._blame;
|
||||||
if (!blame || !blame.lines.length) return;
|
if (!blame || !blame.lines.length) return false;
|
||||||
|
|
||||||
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- toggle whitespace off
|
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- toggle whitespace off
|
||||||
this._renderWhitespaceSetting = workspace.getConfiguration('editor').get<string>('renderWhitespace');
|
if (this.requiresRenderWhitespaceToggle) {
|
||||||
if (this._renderWhitespaceSetting !== 'none') {
|
Logger.log('BlameAnnotationProvider.provideBlameAnnotation:', `Toggle whitespace rendering off`);
|
||||||
commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace);
|
await commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
let blameDecorationOptions: DecorationOptions[] | undefined;
|
let blameDecorationOptions: DecorationOptions[] | undefined;
|
||||||
@@ -103,23 +112,49 @@ export class BlameAnnotationProvider extends Disposable {
|
|||||||
this.editor.setDecorations(blameDecoration, blameDecorationOptions);
|
this.editor.setDecorations(blameDecoration, blameDecorationOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
sha = sha || Iterables.first(blame.commits.values()).sha;
|
this._setSelection(blame, shaOrLine);
|
||||||
|
return true;
|
||||||
return this._applyCommitHighlight(sha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _applyCommitHighlight(sha: string) {
|
async setSelection(shaOrLine?: string | number) {
|
||||||
const blame = await this._blame;
|
const blame = await this._blame;
|
||||||
if (!blame || !blame.lines.length) return;
|
if (!blame || !blame.lines.length) return;
|
||||||
|
|
||||||
|
return this._setSelection(blame, shaOrLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setSelection(blame: IGitBlame, shaOrLine?: string | number) {
|
||||||
|
const offset = this._uri.offset;
|
||||||
|
|
||||||
|
let sha: string;
|
||||||
|
if (typeof shaOrLine === 'string') {
|
||||||
|
sha = shaOrLine;
|
||||||
|
}
|
||||||
|
else if (typeof shaOrLine === 'number') {
|
||||||
|
const line = shaOrLine - offset;
|
||||||
|
if (line >= 0) {
|
||||||
|
sha = blame.lines[line].sha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sha = Iterables.first(blame.commits.values()).sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sha) {
|
||||||
|
this.editor.setDecorations(highlightDecoration, []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const highlightDecorationRanges = blame.lines
|
const highlightDecorationRanges = blame.lines
|
||||||
.filter(l => l.sha === sha)
|
.filter(l => l.sha === sha)
|
||||||
.map(l => this.editor.document.validateRange(new Range(l.line, 0, l.line, 1000000)));
|
.map(l => this.editor.document.validateRange(new Range(l.line + offset, 0, l.line + offset, 1000000)));
|
||||||
|
|
||||||
this.editor.setDecorations(highlightDecoration, highlightDecorationRanges);
|
this.editor.setDecorations(highlightDecoration, highlightDecorationRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getCompactGutterDecorations(blame: IGitBlame): DecorationOptions[] {
|
private _getCompactGutterDecorations(blame: IGitBlame): DecorationOptions[] {
|
||||||
|
const offset = this._uri.offset;
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let lastSha: string;
|
let lastSha: string;
|
||||||
return blame.lines.map(l => {
|
return blame.lines.map(l => {
|
||||||
@@ -143,7 +178,7 @@ export class BlameAnnotationProvider extends Disposable {
|
|||||||
count = -1;
|
count = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEmptyOrWhitespace = this._document.lineAt(l.line).isEmptyOrWhitespace;
|
const isEmptyOrWhitespace = this.document.lineAt(l.line).isEmptyOrWhitespace;
|
||||||
if (!isEmptyOrWhitespace) {
|
if (!isEmptyOrWhitespace) {
|
||||||
switch (++count) {
|
switch (++count) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -164,7 +199,7 @@ export class BlameAnnotationProvider extends Disposable {
|
|||||||
lastSha = l.sha;
|
lastSha = l.sha;
|
||||||
|
|
||||||
return <DecorationOptions>{
|
return <DecorationOptions>{
|
||||||
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)),
|
range: this.editor.document.validateRange(new Range(l.line + offset, 0, l.line + offset, 0)),
|
||||||
hoverMessage: hoverMessage,
|
hoverMessage: hoverMessage,
|
||||||
renderOptions: { before: { color: color, contentText: gutter, width: '11em' } }
|
renderOptions: { before: { color: color, contentText: gutter, width: '11em' } }
|
||||||
};
|
};
|
||||||
@@ -172,6 +207,8 @@ export class BlameAnnotationProvider extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getExpandedGutterDecorations(blame: IGitBlame): DecorationOptions[] {
|
private _getExpandedGutterDecorations(blame: IGitBlame): DecorationOptions[] {
|
||||||
|
const offset = this._uri.offset;
|
||||||
|
|
||||||
let width = 0;
|
let width = 0;
|
||||||
if (this._config.annotation.sha) {
|
if (this._config.annotation.sha) {
|
||||||
width += 5;
|
width += 5;
|
||||||
@@ -211,7 +248,7 @@ export class BlameAnnotationProvider extends Disposable {
|
|||||||
|
|
||||||
const gutter = this._getGutter(commit);
|
const gutter = this._getGutter(commit);
|
||||||
return <DecorationOptions>{
|
return <DecorationOptions>{
|
||||||
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)),
|
range: this.editor.document.validateRange(new Range(l.line + offset, 0, l.line + offset, 0)),
|
||||||
hoverMessage: hoverMessage,
|
hoverMessage: hoverMessage,
|
||||||
renderOptions: { before: { color: color, contentText: gutter, width: `${width}em` } }
|
renderOptions: { before: { color: color, contentText: gutter, width: `${width}em` } }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { Objects } from './system';
|
import { Objects } from './system';
|
||||||
import { Disposable, ExtensionContext, StatusBarAlignment, StatusBarItem, TextEditor, window, workspace } from 'vscode';
|
import { Disposable, ExtensionContext, StatusBarAlignment, StatusBarItem, TextDocument, TextEditor, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
||||||
import { IConfig, IStatusBarConfig, StatusBarCommand } from './configuration';
|
import { TextDocumentComparer } from './comparers';
|
||||||
|
import { IConfig, StatusBarCommand } from './configuration';
|
||||||
import { WorkspaceState } from './constants';
|
import { WorkspaceState } from './constants';
|
||||||
import GitProvider, { IGitBlameLine } from './gitProvider';
|
import GitProvider, { GitCommit, GitUri, IGitBlame } from './gitProvider';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
export default class BlameStatusBarController extends Disposable {
|
export default class BlameStatusBarController extends Disposable {
|
||||||
private _config: IStatusBarConfig;
|
private _blame: Promise<IGitBlame> | undefined;
|
||||||
|
private _config: IConfig;
|
||||||
private _disposable: Disposable;
|
private _disposable: Disposable;
|
||||||
private _statusBarItem: StatusBarItem|null;
|
private _document: TextDocument | undefined;
|
||||||
private _statusBarDisposable: Disposable|null;
|
private _statusBarItem: StatusBarItem | undefined;
|
||||||
|
private _statusBarDisposable: Disposable | undefined;
|
||||||
|
private _uri: GitUri;
|
||||||
|
private _useCaching: boolean;
|
||||||
|
|
||||||
constructor(private context: ExtensionContext, private git: GitProvider) {
|
constructor(private context: ExtensionContext, private git: GitProvider) {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
@@ -33,7 +38,7 @@ export default class BlameStatusBarController extends Disposable {
|
|||||||
private _onConfigure() {
|
private _onConfigure() {
|
||||||
const config = workspace.getConfiguration('').get<IConfig>('gitlens');
|
const config = workspace.getConfiguration('').get<IConfig>('gitlens');
|
||||||
|
|
||||||
if (!Objects.areEquivalent(config.statusBar, this._config)) {
|
if (!Objects.areEquivalent(config.statusBar, this._config && this._config.statusBar)) {
|
||||||
this._statusBarDisposable && this._statusBarDisposable.dispose();
|
this._statusBarDisposable && this._statusBarDisposable.dispose();
|
||||||
this._statusBarItem && this._statusBarItem.dispose();
|
this._statusBarItem && this._statusBarItem.dispose();
|
||||||
|
|
||||||
@@ -47,7 +52,7 @@ export default class BlameStatusBarController extends Disposable {
|
|||||||
break;
|
break;
|
||||||
case StatusBarCommand.GitViewHistory:
|
case StatusBarCommand.GitViewHistory:
|
||||||
if (!this.context.workspaceState.get(WorkspaceState.HasGitHistoryExtension, false)) {
|
if (!this.context.workspaceState.get(WorkspaceState.HasGitHistoryExtension, false)) {
|
||||||
config.statusBar.command = StatusBarCommand.BlameExplorer;
|
config.statusBar.command = StatusBarCommand.ShowBlameHistory;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -55,28 +60,70 @@ export default class BlameStatusBarController extends Disposable {
|
|||||||
|
|
||||||
const subscriptions: Disposable[] = [];
|
const subscriptions: Disposable[] = [];
|
||||||
|
|
||||||
subscriptions.push(window.onDidChangeActiveTextEditor(this._onActiveSelectionChanged, this));
|
subscriptions.push(window.onDidChangeActiveTextEditor(this._onActiveTextEditorChanged, this));
|
||||||
subscriptions.push(window.onDidChangeTextEditorSelection(e => this._onActiveSelectionChanged(e.textEditor)));
|
subscriptions.push(window.onDidChangeTextEditorSelection(this._onActiveSelectionChanged, this));
|
||||||
|
|
||||||
this._statusBarDisposable = Disposable.from(...subscriptions);
|
this._statusBarDisposable = Disposable.from(...subscriptions);
|
||||||
} else {
|
} else {
|
||||||
this._statusBarDisposable = null;
|
this._statusBarDisposable = undefined;
|
||||||
this._statusBarItem = null;
|
this._statusBarItem = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config.statusBar;
|
this._config = config;
|
||||||
|
|
||||||
|
this._onActiveTextEditorChanged(window.activeTextEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _onActiveSelectionChanged(editor: TextEditor): Promise<void> {
|
private async _onActiveTextEditorChanged(e: TextEditor): Promise<void> {
|
||||||
if (!editor || !editor.document || editor.document.isUntitled) {
|
if (!e || !e.document || e.document.isUntitled || e.viewColumn === undefined) {
|
||||||
this.clear();
|
this.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const blame = await this.git.getBlameForLine(editor.document.uri.fsPath, editor.selection.active.line);
|
this._document = e.document;
|
||||||
if (blame) {
|
this._uri = GitUri.fromUri(this._document.uri);
|
||||||
this.show(blame);
|
const maxLines = this._config.advanced.caching.statusBar.maxLines;
|
||||||
|
this._useCaching = this._config.advanced.caching.enabled && (maxLines <= 0 || this._document.lineCount <= maxLines);
|
||||||
|
if (this._useCaching) {
|
||||||
|
this._blame = this.git.getBlameForFile(this._uri.fsPath, this._uri.sha, this._uri.repoPath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._blame = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._showBlame(e.selection.active.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onActiveSelectionChanged(e: TextEditorSelectionChangeEvent): Promise<void> {
|
||||||
|
if (!TextDocumentComparer.equals(this._document, e.textEditor && e.textEditor.document)) return;
|
||||||
|
|
||||||
|
return this._showBlame(e.selections[0].active.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _showBlame(line: number) {
|
||||||
|
line = line - this._uri.offset;
|
||||||
|
|
||||||
|
let commit: GitCommit;
|
||||||
|
if (line >= 0) {
|
||||||
|
if (this._useCaching) {
|
||||||
|
const blame = await this._blame;
|
||||||
|
if (!blame || !blame.lines.length) {
|
||||||
|
this.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sha = blame.lines[line].sha;
|
||||||
|
commit = blame.commits.get(sha);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const blameLine = await this.git.getBlameForLine(this._uri.fsPath, line, this._uri.sha, this._uri.repoPath);
|
||||||
|
commit = blameLine && blameLine.commit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commit) {
|
||||||
|
this.show(commit);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.clear();
|
this.clear();
|
||||||
@@ -85,20 +132,23 @@ export default class BlameStatusBarController extends Disposable {
|
|||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this._statusBarItem && this._statusBarItem.hide();
|
this._statusBarItem && this._statusBarItem.hide();
|
||||||
|
this._document = undefined;
|
||||||
|
this._blame = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
show(blameLine: IGitBlameLine) {
|
show(commit: GitCommit) {
|
||||||
const commit = blameLine.commit;
|
|
||||||
this._statusBarItem.text = `$(git-commit) ${commit.author}, ${moment(commit.date).fromNow()}`;
|
this._statusBarItem.text = `$(git-commit) ${commit.author}, ${moment(commit.date).fromNow()}`;
|
||||||
//this._statusBarItem.tooltip = [`Last changed by ${commit.author}`, moment(commit.date).format('MMMM Do, YYYY h:MMa'), '', commit.message].join('\n');
|
|
||||||
|
|
||||||
switch (this._config.command) {
|
switch (this._config.statusBar.command) {
|
||||||
case StatusBarCommand.BlameAnnotate:
|
case StatusBarCommand.BlameAnnotate:
|
||||||
this._statusBarItem.tooltip = 'Toggle Blame Annotations';
|
this._statusBarItem.tooltip = 'Toggle Blame Annotations';
|
||||||
break;
|
break;
|
||||||
case StatusBarCommand.BlameExplorer:
|
case StatusBarCommand.ShowBlameHistory:
|
||||||
this._statusBarItem.tooltip = 'Open Blame History';
|
this._statusBarItem.tooltip = 'Open Blame History';
|
||||||
break;
|
break;
|
||||||
|
case StatusBarCommand.ShowFileHistory:
|
||||||
|
this._statusBarItem.tooltip = 'Open File History';
|
||||||
|
break;
|
||||||
case StatusBarCommand.DiffWithPrevious:
|
case StatusBarCommand.DiffWithPrevious:
|
||||||
this._statusBarItem.tooltip = 'Compare to Previous Commit';
|
this._statusBarItem.tooltip = 'Compare to Previous Commit';
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { Commands } from '../constants';
|
import { Commands } from '../constants';
|
||||||
import GitProvider, { GitCommit } from '../gitProvider';
|
import GitProvider, { GitCommit, GitUri } from '../gitProvider';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export default class DiffLineWithPreviousCommand extends EditorCommand {
|
export default class DiffLineWithPreviousCommand extends EditorCommand {
|
||||||
@@ -10,18 +10,23 @@ export default class DiffLineWithPreviousCommand extends EditorCommand {
|
|||||||
super(Commands.DiffLineWithPrevious);
|
super(Commands.DiffLineWithPrevious);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit): Promise<any>;
|
async execute(editor: TextEditor): Promise<any>;
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri): Promise<any>;
|
||||||
|
async execute(editor: TextEditor, edit?: TextEditorEdit, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
|
||||||
|
if (!(uri instanceof Uri)) {
|
||||||
|
if (!editor.document) return undefined;
|
||||||
|
uri = editor.document.uri;
|
||||||
|
}
|
||||||
|
|
||||||
line = line || editor.selection.active.line;
|
line = line || editor.selection.active.line;
|
||||||
|
|
||||||
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
||||||
if (!(uri instanceof Uri)) {
|
const gitUri = GitUri.fromUri(uri);
|
||||||
if (!editor.document) return undefined;
|
const blameline = line - gitUri.offset;
|
||||||
uri = editor.document.uri;
|
if (blameline < 0) return undefined;
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blame = await this.git.getBlameForLine(uri.fsPath, line);
|
const blame = await this.git.getBlameForLine(gitUri.fsPath, blameline, gitUri.sha, gitUri.repoPath);
|
||||||
if (!blame) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
if (!blame) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
||||||
|
|
||||||
// If the line is uncommitted, find the previous commit
|
// If the line is uncommitted, find the previous commit
|
||||||
@@ -33,7 +38,7 @@ export default class DiffLineWithPreviousCommand extends EditorCommand {
|
|||||||
|
|
||||||
const prevCommit = prevBlame.commit;
|
const prevCommit = prevBlame.commit;
|
||||||
commit = new GitCommit(commit.repoPath, commit.sha, commit.fileName, commit.author, commit.date, commit.message, commit.lines, commit.originalFileName, prevCommit.sha, prevCommit.fileName);
|
commit = new GitCommit(commit.repoPath, commit.sha, commit.fileName, commit.author, commit.date, commit.message, commit.lines, commit.originalFileName, prevCommit.sha, prevCommit.fileName);
|
||||||
line = blame.line.originalLine + 1;
|
line = blame.line.originalLine + 1 + gitUri.offset;
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.DiffWithPreviousLineCommand]', `getBlameForLine(${blame.line.originalLine}, ${commit.previousSha})`, ex);
|
Logger.error('[GitLens.DiffWithPreviousLineCommand]', `getBlameForLine(${blame.line.originalLine}, ${commit.previousSha})`, ex);
|
||||||
@@ -42,7 +47,7 @@ export default class DiffLineWithPreviousCommand extends EditorCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.DiffWithPreviousLineCommand]', `getBlameForLine(${line})`, ex);
|
Logger.error('[GitLens.DiffWithPreviousLineCommand]', `getBlameForLine(${blameline})`, ex);
|
||||||
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { Commands } from '../constants';
|
import { Commands } from '../constants';
|
||||||
import GitProvider, { GitCommit } from '../gitProvider';
|
import GitProvider, { GitCommit, GitUri } from '../gitProvider';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export default class DiffLineWithWorkingCommand extends EditorCommand {
|
export default class DiffLineWithWorkingCommand extends EditorCommand {
|
||||||
@@ -10,33 +10,38 @@ export default class DiffLineWithWorkingCommand extends EditorCommand {
|
|||||||
super(Commands.DiffLineWithWorking);
|
super(Commands.DiffLineWithWorking);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit): Promise<any>;
|
async execute(editor: TextEditor): Promise<any>;
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri): Promise<any>;
|
||||||
|
async execute(editor: TextEditor, edit?: TextEditorEdit, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
|
||||||
|
if (!(uri instanceof Uri)) {
|
||||||
|
if (!editor.document) return undefined;
|
||||||
|
uri = editor.document.uri;
|
||||||
|
}
|
||||||
|
|
||||||
line = line || editor.selection.active.line;
|
line = line || editor.selection.active.line;
|
||||||
|
|
||||||
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
||||||
if (!(uri instanceof Uri)) {
|
const gitUri = GitUri.fromUri(uri);
|
||||||
if (!editor.document) return undefined;
|
const blameline = line - gitUri.offset;
|
||||||
uri = editor.document.uri;
|
if (blameline < 0) return undefined;
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blame = await this.git.getBlameForLine(uri.fsPath, line);
|
const blame = await this.git.getBlameForLine(gitUri.fsPath, blameline, gitUri.sha, gitUri.repoPath);
|
||||||
if (!blame) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
if (!blame) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
||||||
|
|
||||||
commit = blame.commit;
|
commit = blame.commit;
|
||||||
// If the line is uncommitted, find the previous commit
|
// If the line is uncommitted, find the previous commit
|
||||||
if (commit.isUncommitted) {
|
if (commit.isUncommitted) {
|
||||||
commit = new GitCommit(commit.repoPath, commit.previousSha, commit.previousFileName, commit.author, commit.date, commit.message);
|
commit = new GitCommit(commit.repoPath, commit.previousSha, commit.previousFileName, commit.author, commit.date, commit.message);
|
||||||
line = blame.line.line + 1;
|
line = blame.line.line + 1 + gitUri.offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.DiffLineWithWorkingCommand]', `getBlameForLine(${line})`, ex);
|
Logger.error('[GitLens.DiffLineWithWorkingCommand]', `getBlameForLine(${blameline})`, ex);
|
||||||
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands.executeCommand(Commands.DiffWithWorking, commit.uri, commit, line);
|
return commands.executeCommand(Commands.DiffWithWorking, uri, commit, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Iterables } from '../system';
|
|||||||
import { commands, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
import { commands, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { BuiltInCommands, Commands } from '../constants';
|
import { BuiltInCommands, Commands } from '../constants';
|
||||||
import GitProvider, { GitCommit } from '../gitProvider';
|
import GitProvider, { GitCommit, GitUri } from '../gitProvider';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@@ -13,29 +13,33 @@ export default class DiffWithPreviousCommand extends EditorCommand {
|
|||||||
super(Commands.DiffWithPrevious);
|
super(Commands.DiffWithPrevious);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit): Promise<any>;
|
async execute(editor: TextEditor): Promise<any>;
|
||||||
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri): Promise<any>;
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri, commit: GitCommit, range?: Range): Promise<any>;
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri, commit: GitCommit, range?: Range): Promise<any>;
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri, commit: GitCommit, line?: number): Promise<any>;
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri, commit: GitCommit, line?: number): Promise<any>;
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, commit?: GitCommit, rangeOrLine?: Range | number): Promise<any> {
|
async execute(editor: TextEditor, edit?: TextEditorEdit, uri?: Uri, commit?: GitCommit, rangeOrLine?: Range | number): Promise<any> {
|
||||||
|
if (!(uri instanceof Uri)) {
|
||||||
|
if (!editor.document) return undefined;
|
||||||
|
uri = editor.document.uri;
|
||||||
|
}
|
||||||
|
|
||||||
let line = editor.selection.active.line;
|
let line = editor.selection.active.line;
|
||||||
if (typeof rangeOrLine === 'number') {
|
if (typeof rangeOrLine === 'number') {
|
||||||
line = rangeOrLine || line;
|
line = rangeOrLine || line;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!commit || rangeOrLine instanceof Range) {
|
if (!commit || rangeOrLine instanceof Range) {
|
||||||
if (!(uri instanceof Uri)) {
|
const gitUri = GitUri.fromUri(uri);
|
||||||
if (!editor.document) return undefined;
|
|
||||||
uri = editor.document.uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const log = await this.git.getLogForFile(uri.fsPath, <Range>rangeOrLine);
|
const log = await this.git.getLogForFile(gitUri.fsPath, <Range>rangeOrLine);
|
||||||
if (!log) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
if (!log) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
||||||
|
|
||||||
commit = commit ? Iterables.find(log.commits.values(), _ => _.sha === commit.sha) : Iterables.first(log.commits.values());
|
const sha = (commit && commit.sha) || gitUri.sha;
|
||||||
|
commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.DiffWithPreviousCommand]', `getLogForFile(${uri.fsPath})`, ex);
|
Logger.error('[GitLens.DiffWithPreviousCommand]', `getLogForFile(${gitUri.fsPath})`, ex);
|
||||||
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Iterables } from '../system';
|
|||||||
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
import { commands, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { BuiltInCommands, Commands } from '../constants';
|
import { BuiltInCommands, Commands } from '../constants';
|
||||||
import GitProvider, { GitCommit } from '../gitProvider';
|
import GitProvider, { GitCommit, GitUri } from '../gitProvider';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
@@ -12,31 +12,36 @@ export default class DiffWithWorkingCommand extends EditorCommand {
|
|||||||
super(Commands.DiffWithWorking);
|
super(Commands.DiffWithWorking);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit): Promise<any>;
|
async execute(editor: TextEditor): Promise<any>;
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri: Uri): Promise<any>;
|
||||||
|
async execute(editor: TextEditor, edit?: TextEditorEdit, uri?: Uri, commit?: GitCommit, line?: number): Promise<any> {
|
||||||
|
if (!(uri instanceof Uri)) {
|
||||||
|
if (!editor.document) return undefined;
|
||||||
|
uri = editor.document.uri;
|
||||||
|
}
|
||||||
|
|
||||||
line = line || editor.selection.active.line;
|
line = line || editor.selection.active.line;
|
||||||
|
|
||||||
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
||||||
if (!(uri instanceof Uri)) {
|
const gitUri = GitUri.fromUri(uri);
|
||||||
if (!editor.document) return undefined;
|
|
||||||
uri = editor.document.uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const log = await this.git.getLogForFile(uri.fsPath);
|
const log = await this.git.getLogForFile(gitUri.fsPath);
|
||||||
if (!log) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
if (!log) return window.showWarningMessage(`Unable to open diff. File is probably not under source control`);
|
||||||
|
|
||||||
commit = Iterables.first(log.commits.values());
|
commit = (gitUri.sha && log.commits.get(gitUri.sha)) || Iterables.first(log.commits.values());
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.DiffWithWorkingCommand]', `getLogForFile(${uri.fsPath})`, ex);
|
Logger.error('[GitLens.DiffWithWorkingCommand]', `getLogForFile(${gitUri.fsPath})`, ex);
|
||||||
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
return window.showErrorMessage(`Unable to open diff. See output channel for more details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gitUri = GitUri.fromUri(uri);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const compare = await this.git.getVersionedFile(commit.uri.fsPath, commit.repoPath, commit.sha);
|
const compare = await this.git.getVersionedFile(commit.uri.fsPath, commit.repoPath, commit.sha);
|
||||||
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), uri, `${path.basename(commit.uri.fsPath)} (${commit.sha}) ↔ ${path.basename(uri.fsPath)}`);
|
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), gitUri.fileUri(), `${path.basename(commit.uri.fsPath)} (${commit.sha}) ↔ ${path.basename(gitUri.fsPath)}`);
|
||||||
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
|
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
|
|||||||
@@ -3,30 +3,23 @@ import { TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
|||||||
import BlameAnnotationController from '../blameAnnotationController';
|
import BlameAnnotationController from '../blameAnnotationController';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { Commands } from '../constants';
|
import { Commands } from '../constants';
|
||||||
import GitProvider from '../gitProvider';
|
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export default class ShowBlameCommand extends EditorCommand {
|
export default class ShowBlameCommand extends EditorCommand {
|
||||||
constructor(private git: GitProvider, private annotationController: BlameAnnotationController) {
|
constructor(private annotationController: BlameAnnotationController) {
|
||||||
super(Commands.ShowBlame);
|
super(Commands.ShowBlame);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string) {
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string): Promise<any> {
|
||||||
if (sha) {
|
|
||||||
return this.annotationController.toggleBlameAnnotation(editor, sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(uri instanceof Uri)) {
|
|
||||||
if (!editor.document) return undefined;
|
|
||||||
uri = editor.document.uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blame = await this.git.getBlameForLine(uri.fsPath, editor.selection.active.line);
|
if (sha) {
|
||||||
return this.annotationController.showBlameAnnotation(editor, blame && blame.commit.sha);
|
return this.annotationController.showBlameAnnotation(editor, sha);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.annotationController.showBlameAnnotation(editor, editor.selection.active.line);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.ShowBlameCommand]', `getBlameForLine(${editor.selection.active.line})`, ex);
|
Logger.error('GitLens.ShowBlameCommand', ex);
|
||||||
return window.showErrorMessage(`Unable to show blame annotations. See output channel for more details`);
|
return window.showErrorMessage(`Unable to show blame annotations. See output channel for more details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { BuiltInCommands, Commands } from '../constants';
|
import { BuiltInCommands, Commands } from '../constants';
|
||||||
import GitProvider from '../gitProvider';
|
import GitProvider, { GitUri } from '../gitProvider';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export default class ShowBlameHistoryCommand extends EditorCommand {
|
export default class ShowBlameHistoryCommand extends EditorCommand {
|
||||||
@@ -20,8 +20,10 @@ export default class ShowBlameHistoryCommand extends EditorCommand {
|
|||||||
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
|
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gitUri = GitUri.fromUri(uri);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const locations = await this.git.getBlameLocations(uri.fsPath, range);
|
const locations = await this.git.getBlameLocations(gitUri.fsPath, range);
|
||||||
if (!locations) return window.showWarningMessage(`Unable to show blame history. File is probably not under source control`);
|
if (!locations) return window.showWarningMessage(`Unable to show blame history. File is probably not under source control`);
|
||||||
|
|
||||||
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations);
|
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations);
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { BuiltInCommands, Commands } from '../constants';
|
import { BuiltInCommands, Commands } from '../constants';
|
||||||
import GitProvider from '../gitProvider';
|
import GitProvider, { GitUri } from '../gitProvider';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export default class ShowHistoryCommand extends EditorCommand {
|
export default class ShowFileHistoryCommand extends EditorCommand {
|
||||||
constructor(private git: GitProvider) {
|
constructor(private git: GitProvider) {
|
||||||
super(Commands.ShowHistory);
|
super(Commands.ShowFileHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, position?: Position, sha?: string, line?: number) {
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, position?: Position, sha?: string, line?: number) {
|
||||||
@@ -19,14 +19,16 @@ export default class ShowHistoryCommand extends EditorCommand {
|
|||||||
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
|
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gitUri = GitUri.fromUri(uri);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const locations = await this.git.getLogLocations(uri.fsPath, sha, line);
|
const locations = await this.git.getLogLocations(gitUri.fsPath, sha, line);
|
||||||
if (!locations) return window.showWarningMessage(`Unable to show history. File is probably not under source control`);
|
if (!locations) return window.showWarningMessage(`Unable to show history. File is probably not under source control`);
|
||||||
|
|
||||||
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations);
|
return commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.ShowHistoryCommand]', 'getLogLocations', ex);
|
Logger.error('[GitLens.ShowFileHistoryCommand]', 'getLogLocations', ex);
|
||||||
return window.showErrorMessage(`Unable to show history. See output channel for more details`);
|
return window.showErrorMessage(`Unable to show history. See output channel for more details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,30 +3,23 @@ import { TextEditor, TextEditorEdit, Uri, window } from 'vscode';
|
|||||||
import BlameAnnotationController from '../blameAnnotationController';
|
import BlameAnnotationController from '../blameAnnotationController';
|
||||||
import { EditorCommand } from './commands';
|
import { EditorCommand } from './commands';
|
||||||
import { Commands } from '../constants';
|
import { Commands } from '../constants';
|
||||||
import GitProvider from '../gitProvider';
|
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
export default class ToggleBlameCommand extends EditorCommand {
|
export default class ToggleBlameCommand extends EditorCommand {
|
||||||
constructor(private git: GitProvider, private annotationController: BlameAnnotationController) {
|
constructor(private annotationController: BlameAnnotationController) {
|
||||||
super(Commands.ToggleBlame);
|
super(Commands.ToggleBlame);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string) {
|
async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string): Promise<any> {
|
||||||
if (sha) {
|
|
||||||
return this.annotationController.toggleBlameAnnotation(editor, sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(uri instanceof Uri)) {
|
|
||||||
if (!editor.document) return undefined;
|
|
||||||
uri = editor.document.uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blame = await this.git.getBlameForLine(uri.fsPath, editor.selection.active.line);
|
if (sha) {
|
||||||
return this.annotationController.toggleBlameAnnotation(editor, blame && blame.commit.sha);
|
return this.annotationController.toggleBlameAnnotation(editor, sha);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.annotationController.toggleBlameAnnotation(editor, editor.selection.active.line);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
Logger.error('[GitLens.ToggleBlameCommand]', `getBlameForLine(${editor.selection.active.line})`, ex);
|
Logger.error('GitLens.ToggleBlameCommand', ex);
|
||||||
return window.showErrorMessage(`Unable to show blame annotations. See output channel for more details`);
|
return window.showErrorMessage(`Unable to show blame annotations. See output channel for more details`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
src/comparers.ts
Normal file
42
src/comparers.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
'use strict';
|
||||||
|
import { TextDocument, TextEditor, Uri } from 'vscode';
|
||||||
|
|
||||||
|
abstract class Comparer<T> {
|
||||||
|
abstract equals(lhs: T, rhs: T): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UriComparer extends Comparer<Uri> {
|
||||||
|
equals(lhs: Uri, rhs: Uri) {
|
||||||
|
if (!lhs && !rhs) return true;
|
||||||
|
if ((lhs && !rhs) || (!lhs && rhs)) return false;
|
||||||
|
|
||||||
|
return lhs.scheme === rhs.scheme && lhs.fsPath === rhs.fsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextDocumentComparer extends Comparer<TextDocument> {
|
||||||
|
equals(lhs: TextDocument, rhs: TextDocument) {
|
||||||
|
if (!lhs && !rhs) return true;
|
||||||
|
if ((lhs && !rhs) || (!lhs && rhs)) return false;
|
||||||
|
|
||||||
|
return uriComparer.equals(lhs.uri, rhs.uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextEditorComparer extends Comparer<TextEditor> {
|
||||||
|
equals(lhs: TextEditor, rhs: TextEditor) {
|
||||||
|
if (!lhs && !rhs) return true;
|
||||||
|
if ((lhs && !rhs) || (!lhs && rhs)) return false;
|
||||||
|
|
||||||
|
return textDocumentComparer.equals(lhs.document, rhs.document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const textDocumentComparer = new TextDocumentComparer();
|
||||||
|
const textEditorComparer = new TextEditorComparer();
|
||||||
|
const uriComparer = new UriComparer();
|
||||||
|
export {
|
||||||
|
textDocumentComparer as TextDocumentComparer,
|
||||||
|
textEditorComparer as TextEditorComparer,
|
||||||
|
uriComparer as UriComparer
|
||||||
|
};
|
||||||
@@ -16,11 +16,11 @@ export interface IBlameConfig {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CodeLensCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.diffWithPrevious' | 'git.viewFileHistory';
|
export type CodeLensCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.diffWithPrevious' | 'git.viewFileHistory';
|
||||||
export const CodeLensCommand = {
|
export const CodeLensCommand = {
|
||||||
BlameAnnotate: Commands.ToggleBlame as CodeLensCommand,
|
BlameAnnotate: Commands.ToggleBlame as CodeLensCommand,
|
||||||
ShowBlameHistory: Commands.ShowBlameHistory as CodeLensCommand,
|
ShowBlameHistory: Commands.ShowBlameHistory as CodeLensCommand,
|
||||||
ShowHistory: Commands.ShowHistory as CodeLensCommand,
|
ShowFileHistory: Commands.ShowFileHistory as CodeLensCommand,
|
||||||
DiffWithPrevious: Commands.DiffWithPrevious as CodeLensCommand,
|
DiffWithPrevious: Commands.DiffWithPrevious as CodeLensCommand,
|
||||||
GitViewHistory: 'git.viewFileHistory' as CodeLensCommand
|
GitViewHistory: 'git.viewFileHistory' as CodeLensCommand
|
||||||
};
|
};
|
||||||
@@ -61,10 +61,11 @@ export interface ICodeLensesConfig {
|
|||||||
authors: ICodeLensConfig;
|
authors: ICodeLensConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StatusBarCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.toggleCodeLens' | 'gitlens.diffWithPrevious' | 'git.viewFileHistory';
|
export type StatusBarCommand = 'gitlens.toggleBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleCodeLens' | 'gitlens.diffWithPrevious' | 'git.viewFileHistory';
|
||||||
export const StatusBarCommand = {
|
export const StatusBarCommand = {
|
||||||
BlameAnnotate: Commands.ToggleBlame as StatusBarCommand,
|
BlameAnnotate: Commands.ToggleBlame as StatusBarCommand,
|
||||||
BlameExplorer: Commands.ShowBlameHistory as StatusBarCommand,
|
ShowBlameHistory: Commands.ShowBlameHistory as StatusBarCommand,
|
||||||
|
ShowFileHistory: Commands.ShowFileHistory as CodeLensCommand,
|
||||||
DiffWithPrevious: Commands.DiffWithPrevious as StatusBarCommand,
|
DiffWithPrevious: Commands.DiffWithPrevious as StatusBarCommand,
|
||||||
ToggleCodeLens: Commands.ToggleCodeLens as StatusBarCommand,
|
ToggleCodeLens: Commands.ToggleCodeLens as StatusBarCommand,
|
||||||
GitViewHistory: 'git.viewFileHistory' as StatusBarCommand
|
GitViewHistory: 'git.viewFileHistory' as StatusBarCommand
|
||||||
@@ -85,6 +86,9 @@ export const OutputLevel = {
|
|||||||
export interface IAdvancedConfig {
|
export interface IAdvancedConfig {
|
||||||
caching: {
|
caching: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
statusBar: {
|
||||||
|
maxLines: number;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
debug: boolean;
|
debug: boolean;
|
||||||
git: string;
|
git: string;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const BuiltInCommands = {
|
|||||||
ToggleRenderWhitespace: 'editor.action.toggleRenderWhitespace' as BuiltInCommands
|
ToggleRenderWhitespace: 'editor.action.toggleRenderWhitespace' as BuiltInCommands
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Commands = 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' | 'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showHistory' | 'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
|
export type Commands = 'gitlens.diffWithPrevious' | 'gitlens.diffLineWithPrevious' | 'gitlens.diffWithWorking' | 'gitlens.diffLineWithWorking' | 'gitlens.showBlame' | 'gitlens.showBlameHistory' | 'gitlens.showFileHistory' | 'gitlens.toggleBlame' | 'gitlens.toggleCodeLens';
|
||||||
export const Commands = {
|
export const Commands = {
|
||||||
DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands,
|
DiffWithPrevious: 'gitlens.diffWithPrevious' as Commands,
|
||||||
DiffLineWithPrevious: 'gitlens.diffLineWithPrevious' as Commands,
|
DiffLineWithPrevious: 'gitlens.diffLineWithPrevious' as Commands,
|
||||||
@@ -22,7 +22,7 @@ export const Commands = {
|
|||||||
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
|
DiffLineWithWorking: 'gitlens.diffLineWithWorking' as Commands,
|
||||||
ShowBlame: 'gitlens.showBlame' as Commands,
|
ShowBlame: 'gitlens.showBlame' as Commands,
|
||||||
ShowBlameHistory: 'gitlens.showBlameHistory' as Commands,
|
ShowBlameHistory: 'gitlens.showBlameHistory' as Commands,
|
||||||
ShowHistory: 'gitlens.showHistory' as Commands,
|
ShowFileHistory: 'gitlens.showFileHistory' as Commands,
|
||||||
ToggleBlame: 'gitlens.toggleBlame' as Commands,
|
ToggleBlame: 'gitlens.toggleBlame' as Commands,
|
||||||
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
|
ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import DiffWithWorkingCommand from './commands/diffWithWorking';
|
|||||||
import DiffLineWithWorkingCommand from './commands/diffLineWithWorking';
|
import DiffLineWithWorkingCommand from './commands/diffLineWithWorking';
|
||||||
import ShowBlameCommand from './commands/showBlame';
|
import ShowBlameCommand from './commands/showBlame';
|
||||||
import ShowBlameHistoryCommand from './commands/showBlameHistory';
|
import ShowBlameHistoryCommand from './commands/showBlameHistory';
|
||||||
import ShowHistoryCommand from './commands/showHistory';
|
import ShowFileHistoryCommand from './commands/showFileHistory';
|
||||||
import ToggleBlameCommand from './commands/toggleBlame';
|
import ToggleBlameCommand from './commands/toggleBlame';
|
||||||
import ToggleCodeLensCommand from './commands/toggleCodeLens';
|
import ToggleCodeLensCommand from './commands/toggleCodeLens';
|
||||||
|
|
||||||
@@ -64,10 +64,10 @@ export async function activate(context: ExtensionContext) {
|
|||||||
context.subscriptions.push(new DiffLineWithWorkingCommand(git));
|
context.subscriptions.push(new DiffLineWithWorkingCommand(git));
|
||||||
context.subscriptions.push(new DiffWithPreviousCommand(git));
|
context.subscriptions.push(new DiffWithPreviousCommand(git));
|
||||||
context.subscriptions.push(new DiffLineWithPreviousCommand(git));
|
context.subscriptions.push(new DiffLineWithPreviousCommand(git));
|
||||||
context.subscriptions.push(new ShowBlameCommand(git, annotationController));
|
context.subscriptions.push(new ShowBlameCommand(annotationController));
|
||||||
context.subscriptions.push(new ToggleBlameCommand(git, annotationController));
|
context.subscriptions.push(new ToggleBlameCommand(annotationController));
|
||||||
context.subscriptions.push(new ShowBlameHistoryCommand(git));
|
context.subscriptions.push(new ShowBlameHistoryCommand(git));
|
||||||
context.subscriptions.push(new ShowHistoryCommand(git));
|
context.subscriptions.push(new ShowFileHistoryCommand(git));
|
||||||
context.subscriptions.push(new ToggleCodeLensCommand(git));
|
context.subscriptions.push(new ToggleCodeLensCommand(git));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ export class GitBlameParserEnricher implements IGitEnricher<IGitBlame> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _parseEntries(data: string): IBlameEntry[] {
|
private _parseEntries(data: string): IBlameEntry[] {
|
||||||
if (!data) return null;
|
if (!data) return undefined;
|
||||||
|
|
||||||
const lines = data.split('\n');
|
const lines = data.split('\n');
|
||||||
if (!lines.length) return null;
|
if (!lines.length) return undefined;
|
||||||
|
|
||||||
const entries: IBlameEntry[] = [];
|
const entries: IBlameEntry[] = [];
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ export class GitBlameParserEnricher implements IGitEnricher<IGitBlame> {
|
|||||||
entry.fileName = lineParts.slice(1).join(' ');
|
entry.fileName = lineParts.slice(1).join(' ');
|
||||||
|
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
entry = null;
|
entry = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -120,7 +120,7 @@ export class GitBlameParserEnricher implements IGitEnricher<IGitBlame> {
|
|||||||
|
|
||||||
enrich(data: string, fileName: string): IGitBlame {
|
enrich(data: string, fileName: string): IGitBlame {
|
||||||
const entries = this._parseEntries(data);
|
const entries = this._parseEntries(data);
|
||||||
if (!entries) return null;
|
if (!entries) return undefined;
|
||||||
|
|
||||||
const authors: Map<string, IGitAuthor> = new Map();
|
const authors: Map<string, IGitAuthor> = new Map();
|
||||||
const commits: Map<string, GitCommit> = new Map();
|
const commits: Map<string, GitCommit> = new Map();
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ interface ILogEntry {
|
|||||||
|
|
||||||
export class GitLogParserEnricher implements IGitEnricher<IGitLog> {
|
export class GitLogParserEnricher implements IGitEnricher<IGitLog> {
|
||||||
private _parseEntries(data: string): ILogEntry[] {
|
private _parseEntries(data: string): ILogEntry[] {
|
||||||
if (!data) return null;
|
if (!data) return undefined;
|
||||||
|
|
||||||
const lines = data.split('\n');
|
const lines = data.split('\n');
|
||||||
if (!lines.length) return null;
|
if (!lines.length) return undefined;
|
||||||
|
|
||||||
const entries: ILogEntry[] = [];
|
const entries: ILogEntry[] = [];
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export class GitLogParserEnricher implements IGitEnricher<IGitLog> {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'author-date':
|
case 'author-date':
|
||||||
entry.authorDate = lineParts.slice(1).join(' ').trim();
|
entry.authorDate = `${lineParts[1]}T${lineParts[2]}${lineParts[3]}`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// case 'committer':
|
// case 'committer':
|
||||||
@@ -76,7 +76,7 @@ export class GitLogParserEnricher implements IGitEnricher<IGitLog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
entry = null;
|
entry = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -89,7 +89,7 @@ export class GitLogParserEnricher implements IGitEnricher<IGitLog> {
|
|||||||
|
|
||||||
enrich(data: string, fileName: string): IGitLog {
|
enrich(data: string, fileName: string): IGitLog {
|
||||||
const entries = this._parseEntries(data);
|
const entries = this._parseEntries(data);
|
||||||
if (!entries) return null;
|
if (!entries) return undefined;
|
||||||
|
|
||||||
const authors: Map<string, IGitAuthor> = new Map();
|
const authors: Map<string, IGitAuthor> = new Map();
|
||||||
const commits: Map<string, GitCommit> = new Map();
|
const commits: Map<string, GitCommit> = new Map();
|
||||||
|
|||||||
@@ -83,13 +83,13 @@ export default class Git {
|
|||||||
static log(fileName: string, repoPath?: string) {
|
static log(fileName: string, repoPath?: string) {
|
||||||
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
|
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
|
||||||
|
|
||||||
return gitCommand(root, 'log', `--follow`, `--name-only`, `--no-merges`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, file);
|
return gitCommand(root, 'log', `--follow`, `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static logRange(fileName: string, start: number, end: number, repoPath?: string) {
|
static logRange(fileName: string, start: number, end: number, repoPath?: string) {
|
||||||
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
|
const [file, root]: [string, string] = Git.splitPath(Git.normalizePath(fileName), repoPath);
|
||||||
|
|
||||||
return gitCommand(root, 'log', `--name-only`, `--no-merges`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, `-L ${start},${end}:${file}`);
|
return gitCommand(root, 'log', `--name-only`, `--no-merges`, `--date=iso8601-strict`, `--format=%H -%nauthor %an%nauthor-date %ai%ncommitter %cn%ncommitter-date %ci%nsummary %s%nfilename ?`, `-L ${start},${end}:${file}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getVersionedFile(fileName: string, repoPath: string, sha: string) {
|
static getVersionedFile(fileName: string, repoPath: string, sha: string) {
|
||||||
@@ -102,7 +102,7 @@ export default class Git {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Logger.log(`getVersionedFile(${fileName}, ${sha}); destination=${destination}`);
|
Logger.log(`getVersionedFile(${fileName}, ${repoPath}, ${sha}); destination=${destination}`);
|
||||||
fs.appendFile(destination, data, err => {
|
fs.appendFile(destination, data, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use strict';
|
||||||
import { spawnPromise } from 'spawn-rx';
|
import { spawnPromise } from 'spawn-rx';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default class GitBlameCodeLensProvider implements CodeLensProvider {
|
|||||||
resolveCodeLens(lens: CodeLens, token: CancellationToken): Thenable<CodeLens> {
|
resolveCodeLens(lens: CodeLens, token: CancellationToken): Thenable<CodeLens> {
|
||||||
if (lens instanceof GitDiffWithWorkingTreeCodeLens) return this._resolveDiffWithWorkingTreeCodeLens(lens, token);
|
if (lens instanceof GitDiffWithWorkingTreeCodeLens) return this._resolveDiffWithWorkingTreeCodeLens(lens, token);
|
||||||
if (lens instanceof GitDiffWithPreviousCodeLens) return this._resolveGitDiffWithPreviousCodeLens(lens, token);
|
if (lens instanceof GitDiffWithPreviousCodeLens) return this._resolveGitDiffWithPreviousCodeLens(lens, token);
|
||||||
return Promise.reject<CodeLens>(null);
|
return Promise.reject<CodeLens>(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
_resolveDiffWithWorkingTreeCodeLens(lens: GitDiffWithWorkingTreeCodeLens, token: CancellationToken): Thenable<CodeLens> {
|
_resolveDiffWithWorkingTreeCodeLens(lens: GitDiffWithWorkingTreeCodeLens, token: CancellationToken): Thenable<CodeLens> {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
let languageLocations = this._config.codeLens.languageLocations.find(_ => _.language.toLowerCase() === document.languageId);
|
let languageLocations = this._config.codeLens.languageLocations.find(_ => _.language.toLowerCase() === document.languageId);
|
||||||
if (languageLocations == null) {
|
if (languageLocations == null) {
|
||||||
languageLocations = <ICodeLensLanguageLocation>{
|
languageLocations = <ICodeLensLanguageLocation>{
|
||||||
language: null,
|
language: undefined,
|
||||||
location: this._config.codeLens.location,
|
location: this._config.codeLens.location,
|
||||||
customSymbols: this._config.codeLens.locationCustomSymbols
|
customSymbols: this._config.codeLens.locationCustomSymbols
|
||||||
};
|
};
|
||||||
@@ -171,7 +171,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
resolveCodeLens(lens: CodeLens, token: CancellationToken): Thenable<CodeLens> {
|
resolveCodeLens(lens: CodeLens, token: CancellationToken): Thenable<CodeLens> {
|
||||||
if (lens instanceof GitRecentChangeCodeLens) return this._resolveGitRecentChangeCodeLens(lens, token);
|
if (lens instanceof GitRecentChangeCodeLens) return this._resolveGitRecentChangeCodeLens(lens, token);
|
||||||
if (lens instanceof GitAuthorsCodeLens) return this._resolveGitAuthorsCodeLens(lens, token);
|
if (lens instanceof GitAuthorsCodeLens) return this._resolveGitAuthorsCodeLens(lens, token);
|
||||||
return Promise.reject<CodeLens>(null);
|
return Promise.reject<CodeLens>(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _resolveGitRecentChangeCodeLens(lens: GitRecentChangeCodeLens, token: CancellationToken): Promise<CodeLens> {
|
async _resolveGitRecentChangeCodeLens(lens: GitRecentChangeCodeLens, token: CancellationToken): Promise<CodeLens> {
|
||||||
@@ -186,7 +186,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
switch (this._config.codeLens.recentChange.command) {
|
switch (this._config.codeLens.recentChange.command) {
|
||||||
case CodeLensCommand.BlameAnnotate: return this._applyBlameAnnotateCommand<GitRecentChangeCodeLens>(title, lens, blame);
|
case CodeLensCommand.BlameAnnotate: return this._applyBlameAnnotateCommand<GitRecentChangeCodeLens>(title, lens, blame);
|
||||||
case CodeLensCommand.ShowBlameHistory: return this._applyShowBlameHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame);
|
case CodeLensCommand.ShowBlameHistory: return this._applyShowBlameHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame);
|
||||||
case CodeLensCommand.ShowHistory: return this._applyShowHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
|
case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
|
||||||
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
|
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitRecentChangeCodeLens>(title, lens, blame, recentCommit);
|
||||||
case CodeLensCommand.GitViewHistory: return this._applyGitHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame);
|
case CodeLensCommand.GitViewHistory: return this._applyGitHistoryCommand<GitRecentChangeCodeLens>(title, lens, blame);
|
||||||
default: return lens;
|
default: return lens;
|
||||||
@@ -202,7 +202,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
switch (this._config.codeLens.authors.command) {
|
switch (this._config.codeLens.authors.command) {
|
||||||
case CodeLensCommand.BlameAnnotate: return this._applyBlameAnnotateCommand<GitAuthorsCodeLens>(title, lens, blame);
|
case CodeLensCommand.BlameAnnotate: return this._applyBlameAnnotateCommand<GitAuthorsCodeLens>(title, lens, blame);
|
||||||
case CodeLensCommand.ShowBlameHistory: return this._applyShowBlameHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
|
case CodeLensCommand.ShowBlameHistory: return this._applyShowBlameHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
|
||||||
case CodeLensCommand.ShowHistory: return this._applyShowHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
|
case CodeLensCommand.ShowFileHistory: return this._applyShowFileHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
|
||||||
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitAuthorsCodeLens>(title, lens, blame);
|
case CodeLensCommand.DiffWithPrevious: return this._applyDiffWithPreviousCommand<GitAuthorsCodeLens>(title, lens, blame);
|
||||||
case CodeLensCommand.GitViewHistory: return this._applyGitHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
|
case CodeLensCommand.GitViewHistory: return this._applyGitHistoryCommand<GitAuthorsCodeLens>(title, lens, blame);
|
||||||
default: return lens;
|
default: return lens;
|
||||||
@@ -227,7 +227,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
return lens;
|
return lens;
|
||||||
}
|
}
|
||||||
|
|
||||||
_applyShowHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit) {
|
_applyShowFileHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines, commit?: GitCommit) {
|
||||||
let line = lens.range.start.line;
|
let line = lens.range.start.line;
|
||||||
const blameLine = commit.lines.find(_ => _.line === line);
|
const blameLine = commit.lines.find(_ => _.line === line);
|
||||||
if (blameLine) {
|
if (blameLine) {
|
||||||
@@ -237,7 +237,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
const position = lens.isFullRange ? new Position(1, 0) : lens.range.start;
|
const position = lens.isFullRange ? new Position(1, 0) : lens.range.start;
|
||||||
lens.command = {
|
lens.command = {
|
||||||
title: title,
|
title: title,
|
||||||
command: Commands.ShowHistory,
|
command: Commands.ShowFileHistory,
|
||||||
arguments: [Uri.file(lens.fileName), position, commit.sha, line]
|
arguments: [Uri.file(lens.fileName), position, commit.sha, line]
|
||||||
};
|
};
|
||||||
return lens;
|
return lens;
|
||||||
@@ -262,7 +262,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_applyGitHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines) {
|
_applyGitHistoryCommand<T extends GitRecentChangeCodeLens | GitAuthorsCodeLens>(title: string, lens: T, blame: IGitBlameLines) {
|
||||||
if (!this._hasGitHistoryExtension) return this._applyShowHistoryCommand(title, lens, blame);
|
if (!this._hasGitHistoryExtension) return this._applyShowFileHistoryCommand(title, lens, blame);
|
||||||
|
|
||||||
lens.command = {
|
lens.command = {
|
||||||
title: title,
|
title: title,
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { ExtensionContext, TextDocumentContentProvider, Uri } from 'vscode';
|
import { ExtensionContext, TextDocumentContentProvider, Uri, window } from 'vscode';
|
||||||
import { DocumentSchemes } from './constants';
|
import { DocumentSchemes } from './constants';
|
||||||
import GitProvider from './gitProvider';
|
import GitProvider from './gitProvider';
|
||||||
import { Logger } from './logger';
|
import { Logger } from './logger';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
export default class GitContentProvider implements TextDocumentContentProvider {
|
export default class GitContentProvider implements TextDocumentContentProvider {
|
||||||
static scheme = DocumentSchemes.Git;
|
static scheme = DocumentSchemes.Git;
|
||||||
|
|
||||||
constructor(context: ExtensionContext, private git: GitProvider) { }
|
constructor(context: ExtensionContext, private git: GitProvider) { }
|
||||||
|
|
||||||
provideTextDocumentContent(uri: Uri): string | Thenable<string> {
|
async provideTextDocumentContent(uri: Uri): Promise<string> {
|
||||||
const data = GitProvider.fromGitUri(uri);
|
const data = GitProvider.fromGitUri(uri);
|
||||||
return this.git.getVersionedFileText(data.originalFileName || data.fileName, data.repoPath, data.sha)
|
const fileName = data.originalFileName || data.fileName;
|
||||||
.then(text => data.decoration ? `${data.decoration}\n${text}` : text)
|
try {
|
||||||
.catch(ex => Logger.error('[GitLens.GitContentProvider]', 'getVersionedFileText', ex));
|
let text = await this.git.getVersionedFileText(fileName, data.repoPath, data.sha);
|
||||||
|
if (data.decoration) {
|
||||||
|
text = `${data.decoration}\n${text}`;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
Logger.error('[GitLens.GitContentProvider]', 'getVersionedFileText', ex);
|
||||||
|
await window.showErrorMessage(`Unable to show Git revision ${data.sha} of '${path.relative(data.repoPath, fileName)}'`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,16 +39,16 @@ enum RemoveCacheReason {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class GitProvider extends Disposable {
|
export default class GitProvider extends Disposable {
|
||||||
private _cache: Map<string, CacheEntry> | null;
|
private _cache: Map<string, CacheEntry> | undefined;
|
||||||
private _cacheDisposable: Disposable | null;
|
private _cacheDisposable: Disposable | undefined;
|
||||||
|
|
||||||
private _config: IConfig;
|
private _config: IConfig;
|
||||||
private _disposable: Disposable;
|
private _disposable: Disposable;
|
||||||
private _codeLensProviderDisposable: Disposable | null;
|
private _codeLensProviderDisposable: Disposable | undefined;
|
||||||
private _codeLensProviderSelector: DocumentFilter;
|
private _codeLensProviderSelector: DocumentFilter;
|
||||||
private _gitignore: Promise<ignore.Ignore>;
|
private _gitignore: Promise<ignore.Ignore>;
|
||||||
|
|
||||||
static EmptyPromise: Promise<IGitBlame | IGitLog> = Promise.resolve(null);
|
static EmptyPromise: Promise<IGitBlame | IGitLog> = Promise.resolve(undefined);
|
||||||
static BlameFormat = GitBlameFormat.incremental;
|
static BlameFormat = GitBlameFormat.incremental;
|
||||||
|
|
||||||
constructor(private context: ExtensionContext) {
|
constructor(private context: ExtensionContext) {
|
||||||
@@ -58,7 +58,7 @@ export default class GitProvider extends Disposable {
|
|||||||
|
|
||||||
this._onConfigure();
|
this._onConfigure();
|
||||||
|
|
||||||
this._gitignore = new Promise<ignore.Ignore | null>((resolve, reject) => {
|
this._gitignore = new Promise<ignore.Ignore | undefined>((resolve, reject) => {
|
||||||
const gitignorePath = path.join(repoPath, '.gitignore');
|
const gitignorePath = path.join(repoPath, '.gitignore');
|
||||||
fs.exists(gitignorePath, e => {
|
fs.exists(gitignorePath, e => {
|
||||||
if (e) {
|
if (e) {
|
||||||
@@ -67,11 +67,11 @@ export default class GitProvider extends Disposable {
|
|||||||
resolve(ignore().add(data));
|
resolve(ignore().add(data));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(undefined);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ export default class GitProvider extends Disposable {
|
|||||||
this._codeLensProviderSelector = GitCodeLensProvider.selector;
|
this._codeLensProviderSelector = GitCodeLensProvider.selector;
|
||||||
this._codeLensProviderDisposable = languages.registerCodeLensProvider(this._codeLensProviderSelector, new GitCodeLensProvider(this.context, this));
|
this._codeLensProviderDisposable = languages.registerCodeLensProvider(this._codeLensProviderSelector, new GitCodeLensProvider(this.context, this));
|
||||||
} else {
|
} else {
|
||||||
this._codeLensProviderDisposable = null;
|
this._codeLensProviderDisposable = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,9 +127,9 @@ export default class GitProvider extends Disposable {
|
|||||||
this._cacheDisposable = Disposable.from(...disposables);
|
this._cacheDisposable = Disposable.from(...disposables);
|
||||||
} else {
|
} else {
|
||||||
this._cacheDisposable && this._cacheDisposable.dispose();
|
this._cacheDisposable && this._cacheDisposable.dispose();
|
||||||
this._cacheDisposable = null;
|
this._cacheDisposable = undefined;
|
||||||
this._cache && this._cache.clear();
|
this._cache && this._cache.clear();
|
||||||
this._cache = null;
|
this._cache = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,13 +167,15 @@ export default class GitProvider extends Disposable {
|
|||||||
return Git.repoPath(cwd);
|
return Git.repoPath(cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlameForFile(fileName: string): Promise<IGitBlame | null> {
|
getBlameForFile(fileName: string, sha?: string, repoPath?: string): Promise<IGitBlame | undefined> {
|
||||||
Logger.log(`getBlameForFile('${fileName}')`);
|
Logger.log(`getBlameForFile('${fileName}', ${sha}, ${repoPath})`);
|
||||||
fileName = Git.normalizePath(fileName);
|
fileName = Git.normalizePath(fileName);
|
||||||
|
|
||||||
|
const useCaching = this.UseCaching && !sha;
|
||||||
|
|
||||||
let cacheKey: string | undefined;
|
let cacheKey: string | undefined;
|
||||||
let entry: CacheEntry | undefined;
|
let entry: CacheEntry | undefined;
|
||||||
if (this.UseCaching) {
|
if (useCaching) {
|
||||||
cacheKey = this._getCacheEntryKey(fileName);
|
cacheKey = this._getCacheEntryKey(fileName);
|
||||||
entry = this._cache.get(cacheKey);
|
entry = this._cache.get(cacheKey);
|
||||||
|
|
||||||
@@ -189,11 +191,11 @@ export default class GitProvider extends Disposable {
|
|||||||
return <Promise<IGitBlame>>GitProvider.EmptyPromise;
|
return <Promise<IGitBlame>>GitProvider.EmptyPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Git.blame(GitProvider.BlameFormat, fileName)
|
return Git.blame(GitProvider.BlameFormat, fileName, sha, repoPath)
|
||||||
.then(data => new GitBlameParserEnricher(GitProvider.BlameFormat).enrich(data, fileName))
|
.then(data => new GitBlameParserEnricher(GitProvider.BlameFormat).enrich(data, fileName))
|
||||||
.catch(ex => {
|
.catch(ex => {
|
||||||
// Trap and cache expected blame errors
|
// Trap and cache expected blame errors
|
||||||
if (this.UseCaching) {
|
if (useCaching) {
|
||||||
const msg = ex && ex.toString();
|
const msg = ex && ex.toString();
|
||||||
Logger.log(`Replace blame cache with empty promise for '${cacheKey}'`);
|
Logger.log(`Replace blame cache with empty promise for '${cacheKey}'`);
|
||||||
|
|
||||||
@@ -206,11 +208,11 @@ export default class GitProvider extends Disposable {
|
|||||||
this._cache.set(cacheKey, entry);
|
this._cache.set(cacheKey, entry);
|
||||||
return <Promise<IGitBlame>>GitProvider.EmptyPromise;
|
return <Promise<IGitBlame>>GitProvider.EmptyPromise;
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.UseCaching) {
|
if (useCaching) {
|
||||||
Logger.log(`Add blame cache for '${cacheKey}'`);
|
Logger.log(`Add blame cache for '${cacheKey}'`);
|
||||||
|
|
||||||
entry.blame = <ICachedBlame>{
|
entry.blame = <ICachedBlame>{
|
||||||
@@ -224,13 +226,13 @@ export default class GitProvider extends Disposable {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBlameForLine(fileName: string, line: number, sha?: string, repoPath?: string): Promise<IGitBlameLine | null> {
|
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.UseCaching && !sha) {
|
if (this.UseCaching && !sha) {
|
||||||
const blame = await this.getBlameForFile(fileName);
|
const blame = await this.getBlameForFile(fileName);
|
||||||
const blameLine = blame && blame.lines[line];
|
const blameLine = blame && blame.lines[line];
|
||||||
if (!blameLine) return null;
|
if (!blameLine) return undefined;
|
||||||
|
|
||||||
const commit = blame.commits.get(blameLine.sha);
|
const commit = blame.commits.get(blameLine.sha);
|
||||||
return <IGitBlameLine>{
|
return <IGitBlameLine>{
|
||||||
@@ -245,7 +247,7 @@ export default class GitProvider extends Disposable {
|
|||||||
try {
|
try {
|
||||||
const data = await Git.blameLines(GitProvider.BlameFormat, fileName, line + 1, line + 1, sha, repoPath);
|
const data = await Git.blameLines(GitProvider.BlameFormat, fileName, line + 1, line + 1, sha, repoPath);
|
||||||
const blame = new GitBlameParserEnricher(GitProvider.BlameFormat).enrich(data, fileName);
|
const blame = new GitBlameParserEnricher(GitProvider.BlameFormat).enrich(data, fileName);
|
||||||
if (!blame) return null;
|
if (!blame) return undefined;
|
||||||
|
|
||||||
const commit = Iterables.first(blame.commits.values());
|
const commit = Iterables.first(blame.commits.values());
|
||||||
if (repoPath) {
|
if (repoPath) {
|
||||||
@@ -258,15 +260,15 @@ export default class GitProvider extends Disposable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBlameForRange(fileName: string, range: Range): Promise<IGitBlameLines | null> {
|
async getBlameForRange(fileName: string, range: Range): Promise<IGitBlameLines | undefined> {
|
||||||
Logger.log(`getBlameForRange('${fileName}', ${range})`);
|
Logger.log(`getBlameForRange('${fileName}', ${range})`);
|
||||||
|
|
||||||
const blame = await this.getBlameForFile(fileName);
|
const blame = await this.getBlameForFile(fileName);
|
||||||
if (!blame) return null;
|
if (!blame) return undefined;
|
||||||
|
|
||||||
if (!blame.lines.length) return Object.assign({ allLines: blame.lines }, blame);
|
if (!blame.lines.length) return Object.assign({ allLines: blame.lines }, blame);
|
||||||
|
|
||||||
@@ -312,11 +314,11 @@ export default class GitProvider extends Disposable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBlameForShaRange(fileName: string, sha: string, range: Range): Promise<IGitBlameCommitLines | null> {
|
async getBlameForShaRange(fileName: string, sha: string, range: Range): Promise<IGitBlameCommitLines | undefined> {
|
||||||
Logger.log(`getBlameForShaRange('${fileName}', ${sha}, ${range})`);
|
Logger.log(`getBlameForShaRange('${fileName}', ${sha}, ${range})`);
|
||||||
|
|
||||||
const blame = await this.getBlameForFile(fileName);
|
const blame = await this.getBlameForFile(fileName);
|
||||||
if (!blame) return null;
|
if (!blame) return undefined;
|
||||||
|
|
||||||
const lines = blame.lines.slice(range.start.line, range.end.line + 1).filter(l => l.sha === sha);
|
const lines = blame.lines.slice(range.start.line, range.end.line + 1).filter(l => l.sha === sha);
|
||||||
let commit = blame.commits.get(sha);
|
let commit = blame.commits.get(sha);
|
||||||
@@ -329,11 +331,11 @@ export default class GitProvider extends Disposable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBlameLocations(fileName: string, range: Range): Promise<Location[] | null> {
|
async getBlameLocations(fileName: string, range: Range): Promise<Location[] | undefined> {
|
||||||
Logger.log(`getBlameForShaRange('${fileName}', ${range})`);
|
Logger.log(`getBlameForShaRange('${fileName}', ${range})`);
|
||||||
|
|
||||||
const blame = await this.getBlameForRange(fileName, range);
|
const blame = await this.getBlameForRange(fileName, range);
|
||||||
if (!blame) return null;
|
if (!blame) return undefined;
|
||||||
|
|
||||||
const commitCount = blame.commits.size;
|
const commitCount = blame.commits.size;
|
||||||
|
|
||||||
@@ -351,7 +353,7 @@ export default class GitProvider extends Disposable {
|
|||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLogForFile(fileName: string, range?: Range): Promise<IGitLog | null> {
|
getLogForFile(fileName: string, range?: Range): Promise<IGitLog | undefined> {
|
||||||
Logger.log(`getLogForFile('${fileName}', ${range})`);
|
Logger.log(`getLogForFile('${fileName}', ${range})`);
|
||||||
fileName = Git.normalizePath(fileName);
|
fileName = Git.normalizePath(fileName);
|
||||||
|
|
||||||
@@ -392,7 +394,7 @@ export default class GitProvider extends Disposable {
|
|||||||
this._cache.set(cacheKey, entry);
|
this._cache.set(cacheKey, entry);
|
||||||
return <Promise<IGitLog>>GitProvider.EmptyPromise;
|
return <Promise<IGitLog>>GitProvider.EmptyPromise;
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -410,11 +412,11 @@ export default class GitProvider extends Disposable {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLogLocations(fileName: string, sha?: string, line?: number): Promise<Location[] | null> {
|
async getLogLocations(fileName: string, sha?: string, line?: number): Promise<Location[] | undefined> {
|
||||||
Logger.log(`getLogLocations('${fileName}', ${sha}, ${line})`);
|
Logger.log(`getLogLocations('${fileName}', ${sha}, ${line})`);
|
||||||
|
|
||||||
const log = await this.getLogForFile(fileName);
|
const log = await this.getLogForFile(fileName);
|
||||||
if (!log) return null;
|
if (!log) return undefined;
|
||||||
|
|
||||||
const commitCount = log.commits.size;
|
const commitCount = log.commits.size;
|
||||||
|
|
||||||
@@ -454,7 +456,7 @@ export default class GitProvider extends Disposable {
|
|||||||
this._codeLensProviderDisposable.dispose();
|
this._codeLensProviderDisposable.dispose();
|
||||||
|
|
||||||
if (editor.document.fileName === (this._codeLensProviderSelector && this._codeLensProviderSelector.pattern)) {
|
if (editor.document.fileName === (this._codeLensProviderSelector && this._codeLensProviderSelector.pattern)) {
|
||||||
this._codeLensProviderDisposable = null;
|
this._codeLensProviderDisposable = undefined;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,7 +470,7 @@ export default class GitProvider extends Disposable {
|
|||||||
disposables.push(window.onDidChangeActiveTextEditor(e => {
|
disposables.push(window.onDidChangeActiveTextEditor(e => {
|
||||||
if (e.viewColumn && e.document !== editor.document) {
|
if (e.viewColumn && e.document !== editor.document) {
|
||||||
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
|
this._codeLensProviderDisposable && this._codeLensProviderDisposable.dispose();
|
||||||
this._codeLensProviderDisposable = null;
|
this._codeLensProviderDisposable = undefined;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -487,7 +489,7 @@ export default class GitProvider extends Disposable {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromGitUri(uri: Uri) {
|
static fromGitUri(uri: Uri): IGitUriData {
|
||||||
if (uri.scheme !== DocumentSchemes.Git) throw new Error(`fromGitUri(uri=${uri}) invalid scheme`);
|
if (uri.scheme !== DocumentSchemes.Git) throw new Error(`fromGitUri(uri=${uri}) invalid scheme`);
|
||||||
return GitProvider._fromGitUri<IGitUriData>(uri);
|
return GitProvider._fromGitUri<IGitUriData>(uri);
|
||||||
}
|
}
|
||||||
@@ -538,6 +540,42 @@ export default class GitProvider extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GitUri extends Uri {
|
||||||
|
offset: number;
|
||||||
|
repoPath?: string | undefined;
|
||||||
|
sha?: string | undefined;
|
||||||
|
|
||||||
|
constructor(uri?: Uri) {
|
||||||
|
super();
|
||||||
|
if (!uri) return;
|
||||||
|
|
||||||
|
const base = <any>this;
|
||||||
|
base._scheme = uri.scheme;
|
||||||
|
base._authority = uri.authority;
|
||||||
|
base._path = uri.path;
|
||||||
|
base._query = uri.query;
|
||||||
|
base._fragment = uri.fragment;
|
||||||
|
|
||||||
|
this.offset = 0;
|
||||||
|
if (uri.scheme === DocumentSchemes.Git || uri.scheme === DocumentSchemes.GitBlame) {
|
||||||
|
const data = GitProvider.fromGitUri(uri);
|
||||||
|
base._fsPath = data.originalFileName || data.fileName;
|
||||||
|
|
||||||
|
this.offset = (data.decoration && data.decoration.split('\n').length) || 0;
|
||||||
|
this.repoPath = data.repoPath;
|
||||||
|
this.sha = data.sha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileUri() {
|
||||||
|
return Uri.file(this.fsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromUri(uri: Uri) {
|
||||||
|
return new GitUri(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface IGitUriData {
|
export interface IGitUriData {
|
||||||
repoPath: string;
|
repoPath: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
'use strict';
|
||||||
// export * from './system/array';
|
// export * from './system/array';
|
||||||
// export * from './system/disposable';
|
// export * from './system/disposable';
|
||||||
// export * from './system/element';
|
// export * from './system/element';
|
||||||
|
|||||||
@@ -58,4 +58,11 @@ export namespace Iterables {
|
|||||||
export function next<T>(source: IterableIterator<T>): T {
|
export function next<T>(source: IterableIterator<T>): T {
|
||||||
return source.next().value;
|
return source.next().value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function some<T>(source: Iterable<T> | IterableIterator<T>, predicate: (item: T) => boolean): boolean {
|
||||||
|
for (const item of source) {
|
||||||
|
if (predicate(item)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user