mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-19 09:45:36 -05:00
Adds blame and active line annotation support to git diff split view
Adds command (compare, copy sha/message, etc) support to git diff split view Fixes #41 - Toggle Blame annotations on compare files page
This commit is contained in:
@@ -107,15 +107,24 @@ export class BlameActiveLineController extends Disposable {
|
||||
this._onActiveTextEditorChanged(window.activeTextEditor);
|
||||
}
|
||||
|
||||
private _onActiveTextEditorChanged(editor: TextEditor) {
|
||||
private isEditorBlameable(editor: TextEditor): boolean {
|
||||
if (!editor || !editor.document) return false;
|
||||
|
||||
const scheme = editor.document.uri.scheme;
|
||||
if (scheme !== DocumentSchemes.File && scheme !== DocumentSchemes.Git && scheme !== DocumentSchemes.GitLensGit) return false;
|
||||
|
||||
if (editor.document.isUntitled && scheme !== DocumentSchemes.Git && scheme !== DocumentSchemes.GitLensGit) return false;
|
||||
|
||||
return this.git.isEditorBlameable(editor);
|
||||
}
|
||||
|
||||
private async _onActiveTextEditorChanged(editor: TextEditor) {
|
||||
this._currentLine = -1;
|
||||
|
||||
const previousEditor = this._editor;
|
||||
previousEditor && previousEditor.setDecorations(activeLineDecoration, []);
|
||||
|
||||
if (!editor || !editor.document || (editor.document.isUntitled && editor.document.uri.scheme !== DocumentSchemes.Git) ||
|
||||
(editor.document.uri.scheme !== DocumentSchemes.File && editor.document.uri.scheme !== DocumentSchemes.Git) ||
|
||||
(editor.viewColumn === undefined && !this.git.hasGitUriForFile(editor))) {
|
||||
if (!this.isEditorBlameable(editor)) {
|
||||
this.clear(editor);
|
||||
|
||||
this._editor = undefined;
|
||||
@@ -125,7 +134,7 @@ export class BlameActiveLineController extends Disposable {
|
||||
|
||||
this._blameable = editor && editor.document && !editor.document.isDirty;
|
||||
this._editor = editor;
|
||||
this._uri = GitUri.fromUri(editor.document.uri, this.git);
|
||||
this._uri = await GitUri.fromUri(editor.document.uri, this.git);
|
||||
const maxLines = this._config.advanced.caching.statusBar.maxLines;
|
||||
this._useCaching = this._config.advanced.caching.enabled && (maxLines <= 0 || editor.document.lineCount <= maxLines);
|
||||
if (this._useCaching) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { BlameabilityChangeEvent, BlameabilityTracker } from './blameabilityTrac
|
||||
import { BlameAnnotationProvider } from './blameAnnotationProvider';
|
||||
import { TextDocumentComparer, TextEditorComparer } from './comparers';
|
||||
import { IBlameConfig } from './configuration';
|
||||
import { GitProvider } from './gitProvider';
|
||||
import { GitProvider, GitUri } from './gitProvider';
|
||||
import { Logger } from './logger';
|
||||
import { WhitespaceController } from './whitespaceController';
|
||||
|
||||
@@ -153,8 +153,7 @@ export class BlameAnnotationController extends Disposable {
|
||||
}
|
||||
|
||||
async showBlameAnnotation(editor: TextEditor, shaOrLine?: string | number): Promise<boolean> {
|
||||
if (!editor || !editor.document) return false;
|
||||
if (editor.viewColumn === undefined && !this.git.hasGitUriForFile(editor)) return false;
|
||||
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
||||
|
||||
const currentProvider = this._annotationProviders.get(editor.viewColumn || -1);
|
||||
if (currentProvider && TextEditorComparer.equals(currentProvider.editor, editor)) {
|
||||
@@ -162,7 +161,8 @@ export class BlameAnnotationController extends Disposable {
|
||||
return true;
|
||||
}
|
||||
|
||||
const provider = new BlameAnnotationProvider(this.context, this.git, this._whitespaceController, editor);
|
||||
const gitUri = await GitUri.fromUri(editor.document.uri, this.git);
|
||||
const provider = new BlameAnnotationProvider(this.context, this.git, this._whitespaceController, editor, gitUri);
|
||||
if (!await provider.supportsBlame()) return false;
|
||||
|
||||
if (currentProvider) {
|
||||
@@ -191,15 +191,13 @@ export class BlameAnnotationController extends Disposable {
|
||||
}
|
||||
|
||||
isAnnotating(editor: TextEditor): boolean {
|
||||
if (!editor || !editor.document) return false;
|
||||
if (editor.viewColumn === undefined && !this.git.hasGitUriForFile(editor)) return false;
|
||||
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
||||
|
||||
return !!this._annotationProviders.get(editor.viewColumn || -1);
|
||||
}
|
||||
|
||||
async toggleBlameAnnotation(editor: TextEditor, shaOrLine?: string | number): Promise<boolean> {
|
||||
if (!editor || !editor.document) return false;
|
||||
if (editor.viewColumn === undefined && !this.git.hasGitUriForFile(editor)) return false;
|
||||
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
|
||||
|
||||
let provider = this._annotationProviders.get(editor.viewColumn || -1);
|
||||
if (!provider) return this.showBlameAnnotation(editor, shaOrLine);
|
||||
|
||||
@@ -15,15 +15,13 @@ export class BlameAnnotationProvider extends Disposable {
|
||||
private _blame: Promise<IGitBlame>;
|
||||
private _config: IBlameConfig;
|
||||
private _disposable: Disposable;
|
||||
private _uri: GitUri;
|
||||
|
||||
constructor(context: ExtensionContext, private git: GitProvider, private whitespaceController: WhitespaceController | undefined, public editor: TextEditor) {
|
||||
constructor(context: ExtensionContext, private git: GitProvider, private whitespaceController: WhitespaceController | undefined, public editor: TextEditor, private uri: GitUri) {
|
||||
super(() => this.dispose());
|
||||
|
||||
this.document = this.editor.document;
|
||||
this._uri = GitUri.fromUri(this.document.uri, this.git);
|
||||
|
||||
this._blame = this.git.getBlameForFile(this._uri.fsPath, this._uri.sha, this._uri.repoPath);
|
||||
this._blame = this.git.getBlameForFile(this.uri.fsPath, this.uri.sha, this.uri.repoPath);
|
||||
|
||||
this._config = workspace.getConfiguration('gitlens').get<IBlameConfig>('blame');
|
||||
|
||||
@@ -104,7 +102,7 @@ export class BlameAnnotationProvider extends Disposable {
|
||||
private _setSelection(blame: IGitBlame, shaOrLine?: string | number) {
|
||||
if (!BlameDecorations.highlight) return;
|
||||
|
||||
const offset = this._uri.offset;
|
||||
const offset = this.uri.offset;
|
||||
|
||||
let sha: string;
|
||||
if (typeof shaOrLine === 'string') {
|
||||
@@ -134,7 +132,7 @@ export class BlameAnnotationProvider extends Disposable {
|
||||
}
|
||||
|
||||
private _getCompactGutterDecorations(blame: IGitBlame): DecorationOptions[] {
|
||||
const offset = this._uri.offset;
|
||||
const offset = this.uri.offset;
|
||||
|
||||
let count = 0;
|
||||
let lastSha: string;
|
||||
@@ -195,7 +193,7 @@ export class BlameAnnotationProvider extends Disposable {
|
||||
}
|
||||
|
||||
private _getExpandedGutterDecorations(blame: IGitBlame, trailing: boolean = false): DecorationOptions[] {
|
||||
const offset = this._uri.offset;
|
||||
const offset = this.uri.offset;
|
||||
|
||||
let width = 0;
|
||||
if (!trailing) {
|
||||
|
||||
@@ -28,7 +28,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
if (!message) {
|
||||
if (!sha) {
|
||||
|
||||
@@ -28,7 +28,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
if (!sha) {
|
||||
if (editor && editor.document && editor.document.isDirty) return undefined;
|
||||
|
||||
@@ -20,7 +20,7 @@ export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
|
||||
uri = editor.document.uri;
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
line = line || (editor && editor.selection.active.line) || gitUri.offset;
|
||||
|
||||
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
||||
|
||||
@@ -18,7 +18,7 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
|
||||
uri = editor.document.uri;
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
line = line || (editor && editor.selection.active.line) || gitUri.offset;
|
||||
|
||||
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
||||
|
||||
@@ -31,7 +31,7 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
|
||||
}
|
||||
|
||||
if (!commit || rangeOrLine instanceof Range) {
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
try {
|
||||
if (!gitUri.sha) {
|
||||
|
||||
@@ -24,7 +24,7 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
|
||||
line = line || (editor && editor.selection.active.line) || 0;
|
||||
|
||||
if (!commit || GitProvider.isUncommitted(commit.sha)) {
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
try {
|
||||
const log = await this.git.getLogForFile(gitUri.fsPath, gitUri.sha, gitUri.repoPath, undefined, gitUri.sha ? undefined : 1);
|
||||
@@ -38,7 +38,7 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
|
||||
}
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
try {
|
||||
const compare = await this.git.getVersionedFile(commit.uri.fsPath, commit.repoPath, commit.sha);
|
||||
|
||||
@@ -23,7 +23,7 @@ export class ShowBlameHistoryCommand extends EditorCommand {
|
||||
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
try {
|
||||
const locations = await this.git.getBlameLocations(gitUri.fsPath, range, gitUri.sha, gitUri.repoPath, sha, line);
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ShowFileHistoryCommand extends EditorCommand {
|
||||
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start;
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
try {
|
||||
const locations = await this.git.getLogLocations(gitUri.fsPath, gitUri.sha, gitUri.repoPath, sha, line);
|
||||
|
||||
@@ -18,7 +18,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCommand {
|
||||
uri = editor.document.uri;
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
let repoPath = gitUri.repoPath;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCommand {
|
||||
return commands.executeCommand(Commands.ShowQuickRepoHistory);
|
||||
}
|
||||
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
|
||||
if (maxCount == null) {
|
||||
maxCount = this.git.config.advanced.maxQuickHistory;
|
||||
|
||||
@@ -23,7 +23,7 @@ export class ShowQuickRepoHistoryCommand extends ActiveEditorCommand {
|
||||
try {
|
||||
let repoPath: string;
|
||||
if (uri instanceof Uri) {
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
repoPath = gitUri.repoPath;
|
||||
|
||||
if (!repoPath) {
|
||||
|
||||
@@ -19,7 +19,7 @@ export class ShowQuickRepoStatusCommand extends ActiveEditorCommand {
|
||||
try {
|
||||
let repoPath: string;
|
||||
if (uri instanceof Uri) {
|
||||
const gitUri = GitUri.fromUri(uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(uri, this.git);
|
||||
repoPath = gitUri.repoPath;
|
||||
|
||||
if (!repoPath) {
|
||||
|
||||
@@ -16,10 +16,11 @@ export const BuiltInCommands = {
|
||||
ToggleRenderWhitespace: 'editor.action.toggleRenderWhitespace' as BuiltInCommands
|
||||
};
|
||||
|
||||
export type DocumentSchemes = 'file' | 'gitlens-git';
|
||||
export type DocumentSchemes = 'file' | 'git' | 'gitlens-git';
|
||||
export const DocumentSchemes = {
|
||||
File: 'file' as DocumentSchemes,
|
||||
Git: 'gitlens-git' as DocumentSchemes
|
||||
Git: 'git' as DocumentSchemes,
|
||||
GitLensGit: 'gitlens-git' as DocumentSchemes
|
||||
};
|
||||
|
||||
export type WorkspaceState = 'repoPath';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
'use strict';
|
||||
import { Iterables } from '../system';
|
||||
import { Uri } from 'vscode';
|
||||
import { DocumentSchemes } from '../constants';
|
||||
import { Git, GitProvider } from '../gitProvider';
|
||||
@@ -22,7 +23,7 @@ export class GitUri extends Uri {
|
||||
base._fragment = uri.fragment;
|
||||
|
||||
this.offset = 0;
|
||||
if (uri.scheme === DocumentSchemes.Git) {
|
||||
if (uri.scheme === DocumentSchemes.GitLensGit) {
|
||||
const data = GitProvider.fromGitContentUri(uri);
|
||||
base._fsPath = data.originalFileName || data.fileName;
|
||||
|
||||
@@ -52,12 +53,17 @@ export class GitUri extends Uri {
|
||||
return Uri.file(this.sha ? this.path : this.fsPath);
|
||||
}
|
||||
|
||||
static fromUri(uri: Uri, git?: GitProvider) {
|
||||
static async fromUri(uri: Uri, git: GitProvider) {
|
||||
if (uri instanceof GitUri) return uri;
|
||||
|
||||
if (git) {
|
||||
const gitUri = git.getGitUriForFile(uri.fsPath);
|
||||
if (gitUri) return gitUri;
|
||||
const gitUri = git.getGitUriForFile(uri.fsPath);
|
||||
if (gitUri) return gitUri;
|
||||
|
||||
// If this is a git uri, assume it is showing the most recent commit
|
||||
if (uri.scheme === 'git' && uri.query === '~') {
|
||||
const log = await git.getLogForFile(uri.fsPath, undefined, undefined, undefined, 1);
|
||||
const commit = log && Iterables.first(log.commits.values());
|
||||
if (commit) return new GitUri(uri, commit);
|
||||
}
|
||||
|
||||
return new GitUri(uri);
|
||||
|
||||
@@ -69,7 +69,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
|
||||
|
||||
if (languageLocations.location === CodeLensLocation.None) return lenses;
|
||||
|
||||
const gitUri = GitUri.fromUri(document.uri, this.git);
|
||||
const gitUri = await GitUri.fromUri(document.uri, this.git);
|
||||
|
||||
const blamePromise = this.git.getBlameForFile(gitUri.fsPath, gitUri.sha, gitUri.repoPath);
|
||||
let blame: IGitBlame;
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as path from 'path';
|
||||
|
||||
export class GitContentProvider implements TextDocumentContentProvider {
|
||||
|
||||
static scheme = DocumentSchemes.Git;
|
||||
static scheme = DocumentSchemes.GitLensGit;
|
||||
|
||||
constructor(context: ExtensionContext, private git: GitProvider) { }
|
||||
|
||||
|
||||
@@ -612,6 +612,12 @@ export class GitProvider extends Disposable {
|
||||
return Git.getVersionedFileText(fileName, repoPath, sha);
|
||||
}
|
||||
|
||||
isEditorBlameable(editor: TextEditor): boolean {
|
||||
return (editor.viewColumn !== undefined ||
|
||||
editor.document.uri.scheme === DocumentSchemes.Git ||
|
||||
this.hasGitUriForFile(editor));
|
||||
}
|
||||
|
||||
toggleCodeLens(editor: TextEditor) {
|
||||
if (this.config.codeLens.visibility !== CodeLensVisibility.OnDemand ||
|
||||
(!this.config.codeLens.recentChange.enabled && !this.config.codeLens.authors.enabled)) return;
|
||||
@@ -632,7 +638,7 @@ export class GitProvider extends Disposable {
|
||||
}
|
||||
|
||||
static fromGitContentUri(uri: Uri): IGitUriData {
|
||||
if (uri.scheme !== DocumentSchemes.Git) throw new Error(`fromGitUri(uri=${uri}) invalid scheme`);
|
||||
if (uri.scheme !== DocumentSchemes.GitLensGit) throw new Error(`fromGitUri(uri=${uri}) invalid scheme`);
|
||||
return GitProvider._fromGitContentUri<IGitUriData>(uri);
|
||||
}
|
||||
|
||||
@@ -658,11 +664,11 @@ export class GitProvider extends Disposable {
|
||||
}
|
||||
|
||||
const extension = path.extname(fileName);
|
||||
return Uri.parse(`${DocumentSchemes.Git}:${path.basename(fileName, extension)}:${data.sha}${extension}?${JSON.stringify(data)}`);
|
||||
return Uri.parse(`${DocumentSchemes.GitLensGit}:${path.basename(fileName, extension)}:${data.sha}${extension}?${JSON.stringify(data)}`);
|
||||
}
|
||||
|
||||
static toReferenceGitContentUri(commit: GitCommit, index: number, commitCount: number, originalFileName?: string, decoration?: string): Uri {
|
||||
return GitProvider._toReferenceGitContentUri(commit, DocumentSchemes.Git, commitCount, GitProvider._toGitUriData(commit, index, originalFileName, decoration));
|
||||
return GitProvider._toReferenceGitContentUri(commit, DocumentSchemes.GitLensGit, commitCount, GitProvider._toGitUriData(commit, index, originalFileName, decoration));
|
||||
}
|
||||
|
||||
private static _toReferenceGitContentUri(commit: GitCommit, scheme: DocumentSchemes, commitCount: number, data: IGitUriData) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export class GitDiffWithPreviousCodeLens extends CodeLens {
|
||||
|
||||
export class GitRevisionCodeLensProvider implements CodeLensProvider {
|
||||
|
||||
static selector: DocumentSelector = { scheme: DocumentSchemes.Git };
|
||||
static selector: DocumentSelector = { scheme: DocumentSchemes.GitLensGit };
|
||||
|
||||
constructor(context: ExtensionContext, private git: GitProvider) { }
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
|
||||
});
|
||||
|
||||
this.fileName = fileName;
|
||||
this.gitUri = GitUri.fromUri(Uri.file(path.resolve(commit.repoPath, fileName)));
|
||||
this.gitUri = new GitUri(Uri.file(path.resolve(commit.repoPath, fileName)));
|
||||
this.sha = commit.sha;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user