@ -1,21 +1,26 @@
'use strict' ;
import { CancellationToken , CodeLens , CodeLensProvider , commands , Location , Position , Range , SymbolInformation , SymbolKind , TextDocument , Uri } from 'vscode' ;
import { Commands , VsCodeCommands } from './constants' ;
import { IGitBlameLine , gitBlame } from './git' ;
import { toGitBlameUri } from './gitBlameUri' ;
import { CancellationToken , CodeLens , CodeLensProvider , commands , ExtensionContext , Location , Position , Range , SymbolInformation , SymbolKind , TextDocument , Uri } from 'vscode' ;
import { Commands , VsCodeCommands , WorkspaceState } from './constants' ;
import GitBlameProvider , { IGitBlame , IGitBlameCommit } from './gitBlameProvider' ;
import * as moment from 'moment' ;
export class GitBlameCodeLens extends CodeLens {
constructor ( private blame : Promise < IGitBlameLine [ ] > , public repoPath : string , public fileName : string , private blameRange : Range , range : Range ) {
private _locations : Location [ ] = [ ] ;
constructor ( private blameProvider : GitBlameProvider , public fileName : string , private blameRange : Range , range : Range ) {
super ( range ) ;
}
getBlameLines ( ) : Promise < IGitBlameLine [ ] > {
return this . blame . then ( allLines = > allLines . slice ( this . blameRange . start . line , this . blameRange . end . line + 1 ) ) ;
get locations() {
return this . _locations ;
}
getBlame ( ) : Promise < IGitBlame > {
return this . blameProvider . getBlameForRange ( this . fileName , this . blameRange ) ;
}
static toUri ( lens : GitBlameCodeLens , index : number , line : IGitBlameLine , lines : IGitBlameLine [ ] , commits : string [ ] ) : Uri {
return toGitBlameUri ( Object . assign ( { repoPath : lens.repoPath , index : index , range : lens.blameRange , lines : lines , commits : commits } , line ) ) ;
static toUri ( lens : GitBlameCodeLens , repoPath : string , commit : IGitBlameCommit , index : number , commitCount : number ) : Uri {
return GitBlameProvider . toBlameUri ( repoPath , commit , lens . blameRange , index , commitCount ) ;
}
}
@ -25,32 +30,35 @@ export class GitHistoryCodeLens extends CodeLens {
}
// static toUri(lens: GitHistoryCodeLens, index: number): Uri {
// return toGit BlameUri(Object.assign({ repoPath: lens.repoPath, index: index, range: lens.blameRange, lines: lines }, line));
// return GitBlameProvider.to BlameUri(Object.assign({ repoPath: lens.repoPath, index: index, range: lens.blameRange, lines: lines }, line));
// }
}
export default class GitCodeLensProvider implements CodeLensProvider {
constructor ( public repoPath : string ) { }
public repoPath : string ;
constructor ( context : ExtensionContext , public blameProvider : GitBlameProvider ) {
this . repoPath = context . workspaceState . get ( WorkspaceState . RepoPath ) as string ;
}
provideCodeLenses ( document : TextDocument , token : CancellationToken ) : CodeLens [ ] | Thenable < CodeLens [ ] > {
// TODO: Should I wait here?
const blame = gitBlame ( document . fileName ) ;
this . blameProvider . blameFile ( document . fileName ) ;
return ( commands . executeCommand ( VsCodeCommands . ExecuteDocumentSymbolProvider , document . uri ) as Promise < SymbolInformation [ ] > ) . then ( symbols = > {
let lenses : CodeLens [ ] = [ ] ;
symbols . forEach ( sym = > this . _provideCodeLens ( document , sym , blame , lenses) ) ;
symbols . forEach ( sym = > this . _provideCodeLens ( document , sym , lenses ) ) ;
// 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 ) ) {
const docRange = document . validateRange ( new Range ( 0 , 1000000 , 1000000 , 1000000 ) ) ;
lenses . push ( new GitBlameCodeLens ( blame , this . repoPath , document . fileName , docRange , new Range ( 0 , 0 , 0 , docRange . start . character ) ) ) ;
lenses . push ( new GitBlameCodeLens ( this . blameProvider , document . fileName , docRange , new Range ( 0 , 0 , 0 , docRange . start . character ) ) ) ;
lenses . push ( new GitHistoryCodeLens ( this . repoPath , document . fileName , docRange . with ( new Position ( docRange . start . line , docRange . start . character + 1 ) ) ) ) ;
}
return lenses ;
} ) ;
}
private _provideCodeLens ( document : TextDocument , symbol : SymbolInformation , blame : Promise < IGitBlameLine [ ] > , lenses : CodeLens [ ] ) : void {
private _provideCodeLens ( document : TextDocument , symbol : SymbolInformation , lenses : CodeLens [ ] ) : void {
switch ( symbol . kind ) {
case SymbolKind . Package :
case SymbolKind . Module :
@ -68,7 +76,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
}
const line = document . lineAt ( symbol . location . range . start ) ;
lenses . push ( new GitBlameCodeLens ( blame , this . repoPath , document . fileName , symbol . location . range , line . range . with ( new Position ( line . range . start . line , line . firstNonWhitespaceCharacterIndex ) ) ) ) ;
lenses . push ( new GitBlameCodeLens ( this . blameProvider , document . fileName , symbol . location . range , line . range . with ( new Position ( line . range . start . line , line . firstNonWhitespaceCharacterIndex ) ) ) ) ;
lenses . push ( new GitHistoryCodeLens ( this . repoPath , document . fileName , line . range . with ( new Position ( line . range . start . line , line . firstNonWhitespaceCharacterIndex + 1 ) ) ) ) ;
}
@ -79,45 +87,34 @@ export default class GitCodeLensProvider implements CodeLensProvider {
_resolveGitBlameCodeLens ( lens : GitBlameCodeLens , token : CancellationToken ) : Thenable < CodeLens > {
return new Promise < CodeLens > ( ( resolve , reject ) = > {
lens . getBlameLines ( ) . then ( lines = > {
if ( ! lines . length ) {
lens . getBlame ( ) . then ( blame = > {
if ( ! blame . lines. length ) {
console . error ( 'No blame lines found' , lens ) ;
reject ( null ) ;
return ;
}
let recentLine = lines [ 0 ] ;
// TODO: Rework this to only get the locations in the ShowBlameHistory command, rather than here -- should save a lot of processing
const commitCount = blame . commits . size ;
let locations : Location [ ] = [ ] ;
if ( lines . length > 1 ) {
let sorted = lines . sort ( ( a , b ) = > b . date . getTime ( ) - a . date . getTime ( ) ) ;
recentLine = sorted [ 0 ] ;
// console.log(lens.fileName, 'Blame lines:', sorted);
let map : Map < string , IGitBlameLine [ ] > = new Map ( ) ;
sorted . forEach ( l = > {
let item = map . get ( l . sha ) ;
if ( item ) {
item . push ( l ) ;
} else {
map . set ( l . sha , [ l ] ) ;
let recentCommit ;
Array . from ( blame . commits . values ( ) )
. sort ( ( a , b ) = > b . date . getTime ( ) - a . date . getTime ( ) )
. forEach ( ( c , i ) = > {
if ( i === 0 ) {
recentCommit = c ;
}
} ) ;
const commits = Array . from ( map . keys ( ) ) ;
Array . from ( map . values ( ) ) . forEach ( ( lines , i ) = > {
const uri = GitBlameCodeLens . toUri ( lens , i + 1 , lines [ 0 ] , lines , commits ) ;
lines . forEach ( l = > locations . push ( new Location ( uri , new Position ( l . originalLine , 0 ) ) ) ) ;
const uri = GitBlameCodeLens . toUri ( lens , this . repoPath , c , i + 1 , commitCount ) ;
blame . lines
. filter ( l = > l . sha === c . sha )
. forEach ( l = > lens . locations . push ( new Location ( uri , new Position ( l . originalLine , 0 ) ) ) ) ;
} ) ;
} else {
locations = [ new Location ( GitBlameCodeLens . toUri ( lens , 1 , recentLine , lines , [ recentLine . sha ] ) , lens . range . start ) ] ;
}
lens . command = {
title : ` ${ recentLine . author } , ${ moment ( recentLine . date ) . fromNow ( ) } ` ,
title : ` ${ recentCommit . author } , ${ moment ( recentCommit . date ) . fromNow ( ) } ` ,
command : Commands.ShowBlameHistory ,
arguments : [ Uri . file ( lens . fileName ) , lens . range . start , locations ]
arguments : [ Uri . file ( lens . fileName ) , lens . range . start , lens . l ocations ]
} ;
resolve ( lens ) ;
} ) ;