'use strict'; import {CancellationToken, CodeLens, CodeLensProvider, commands, DocumentSelector, ExtensionContext, Location, Position, Range, SymbolInformation, SymbolKind, TextDocument, Uri} from 'vscode'; import {Commands, DocumentSchemes, VsCodeCommands, WorkspaceState} from './constants'; import GitProvider, {IGitBlame, IGitBlameCommit} from './gitProvider'; import {join} from 'path'; import * as moment from 'moment'; export class GitDiffWithWorkingTreeCodeLens extends CodeLens { constructor(private git: GitProvider, public fileName: string, public sha: string, range: Range) { super(range); } } export class GitDiffWithPreviousCodeLens extends CodeLens { constructor(private git: GitProvider, public fileName: string, public sha: string, public compareWithSha: string, range: Range) { super(range); } } export default class GitBlameCodeLensProvider implements CodeLensProvider { static selector: DocumentSelector = { scheme: DocumentSchemes.GitBlame }; constructor(context: ExtensionContext, private git: GitProvider) { } provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable { const data = this.git.fromBlameUri(document.uri); const fileName = data.fileName; return this.git.getBlameForFile(fileName).then(blame => { const commits = Array.from(blame.commits.values()).sort((a, b) => b.date.getTime() - a.date.getTime()); let index = commits.findIndex(c => c.sha === data.sha) + 1; let previousCommit: IGitBlameCommit; if (index < commits.length) { previousCommit = commits[index]; } const lenses: CodeLens[] = []; // Add codelens to each "group" of blame lines const lines = blame.lines.filter(l => l.sha === data.sha && l.originalLine >= data.range.start.line && l.originalLine <= data.range.end.line); let lastLine = lines[0].originalLine; lines.forEach(l => { if (l.originalLine !== lastLine + 1) { lenses.push(new GitDiffWithWorkingTreeCodeLens(this.git, fileName, data.sha, new Range(l.originalLine, 0, l.originalLine, 1))); if (previousCommit) { lenses.push(new GitDiffWithPreviousCodeLens(this.git, fileName, data.sha, previousCommit.sha, new Range(l.originalLine, 1, l.originalLine, 2))); } } lastLine = l.originalLine; }); // Check if we have a lens for the whole document -- if not add one if (!lenses.find(l => l.range.start.line === 0 && l.range.end.line === 0)) { lenses.push(new GitDiffWithWorkingTreeCodeLens(this.git, fileName, data.sha, new Range(0, 0, 0, 1))); if (previousCommit) { lenses.push(new GitDiffWithPreviousCodeLens(this.git, fileName, data.sha, previousCommit.sha, new Range(0, 1, 0, 2))); } } return lenses; }); } resolveCodeLens(lens: CodeLens, token: CancellationToken): Thenable { if (lens instanceof GitDiffWithWorkingTreeCodeLens) return this._resolveDiffWithWorkingTreeCodeLens(lens, token); if (lens instanceof GitDiffWithPreviousCodeLens) return this._resolveGitDiffWithPreviousCodeLens(lens, token); } _resolveDiffWithWorkingTreeCodeLens(lens: GitDiffWithWorkingTreeCodeLens, token: CancellationToken): Thenable { lens.command = { title: `Compare with Working Tree`, command: Commands.DiffWithWorking, arguments: [Uri.file(join(this.git.repoPath, lens.fileName)), lens.sha] }; return Promise.resolve(lens); } _resolveGitDiffWithPreviousCodeLens(lens: GitDiffWithPreviousCodeLens, token: CancellationToken): Thenable { lens.command = { title: `Compare with Previous (${lens.compareWithSha})`, command: Commands.DiffWithPrevious, arguments: [Uri.file(join(this.git.repoPath, lens.fileName)), lens.sha, lens.compareWithSha] }; return Promise.resolve(lens); } }