Skip to content

Commit c395a58

Browse files
author
Eric Amodio
committed
Renamed to GitLens
Reworked Uri scheme to drastically reduce encoded data (big perf improvement)
1 parent 06b350b commit c395a58

File tree

14 files changed

+18808
-173
lines changed

14 files changed

+18808
-173
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Git CodeLens
1+
# GitLens
22

33
Provides Git blame (and history eventually) CodeLens for many supported Visual Studio Code languages (in theory -- the language must support symbol searching).
44

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
{
2-
"name": "git-codelens",
2+
"name": "gitlens",
33
"version": "0.0.1",
44
"author": "Eric Amodio",
55
"publisher": "eamodio",
66
"engines": {
77
"vscode": "^1.3.0"
88
},
99
"license": "SEE LICENSE IN LICENSE",
10-
"displayName": "Git CodeLens",
10+
"displayName": "GitLens",
1111
"description": "Provides Git blame information in CodeLens",
1212
"categories": [
1313
"Other"
1414
],
1515
"keywords": [
16-
"git", "gitblame", "blame"
16+
"git", "gitblame", "blame", "codelens"
1717
],
1818
"galleryBanner": {
1919
"color": "#0000FF",
@@ -23,7 +23,7 @@
2323
"main": "./out/src/extension",
2424
"contributes": {
2525
"commands": [{
26-
"command": "git.codelen.showBlameHistory",
26+
"command": "git.action.showBlameHistory",
2727
"title": "Show Blame History",
2828
"category": "Git"
2929
}]

src/codeLensProvider.ts

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
'use strict';
2-
import {CancellationToken, CodeLens, CodeLensProvider, commands, Location, Position, Range, SymbolInformation, SymbolKind, TextDocument, Uri} from 'vscode';
3-
import {Commands, VsCodeCommands} from './constants';
4-
import {IGitBlameLine, gitBlame} from './git';
5-
import {toGitBlameUri} from './gitBlameUri';
2+
import {CancellationToken, CodeLens, CodeLensProvider, commands, ExtensionContext, Location, Position, Range, SymbolInformation, SymbolKind, TextDocument, Uri} from 'vscode';
3+
import {Commands, VsCodeCommands, WorkspaceState} from './constants';
4+
import GitBlameProvider, {IGitBlame, IGitBlameCommit} from './gitBlameProvider';
65
import * as moment from 'moment';
76

87
export class GitBlameCodeLens extends CodeLens {
9-
constructor(private blame: Promise<IGitBlameLine[]>, public repoPath: string, public fileName: string, private blameRange: Range, range: Range) {
8+
private _locations: Location[] = [];
9+
10+
constructor(private blameProvider: GitBlameProvider, public fileName: string, private blameRange: Range, range: Range) {
1011
super(range);
1112
}
1213

13-
getBlameLines(): Promise<IGitBlameLine[]> {
14-
return this.blame.then(allLines => allLines.slice(this.blameRange.start.line, this.blameRange.end.line + 1));
14+
get locations() {
15+
return this._locations;
16+
}
17+
18+
getBlame(): Promise<IGitBlame> {
19+
return this.blameProvider.getBlameForRange(this.fileName, this.blameRange);
1520
}
1621

17-
static toUri(lens: GitBlameCodeLens, index: number, line: IGitBlameLine, lines: IGitBlameLine[], commits: string[]): Uri {
18-
return toGitBlameUri(Object.assign({ repoPath: lens.repoPath, index: index, range: lens.blameRange, lines: lines, commits: commits }, line));
22+
static toUri(lens: GitBlameCodeLens, repoPath: string, commit: IGitBlameCommit, index: number, commitCount: number): Uri {
23+
return GitBlameProvider.toBlameUri(repoPath, commit, lens.blameRange, index, commitCount);
1924
}
2025
}
2126

@@ -25,32 +30,35 @@ export class GitHistoryCodeLens extends CodeLens {
2530
}
2631

2732
// static toUri(lens: GitHistoryCodeLens, index: number): Uri {
28-
// return toGitBlameUri(Object.assign({ repoPath: lens.repoPath, index: index, range: lens.blameRange, lines: lines }, line));
33+
// return GitBlameProvider.toBlameUri(Object.assign({ repoPath: lens.repoPath, index: index, range: lens.blameRange, lines: lines }, line));
2934
// }
3035
}
3136

3237
export default class GitCodeLensProvider implements CodeLensProvider {
33-
constructor(public repoPath: string) { }
38+
public repoPath: string;
39+
40+
constructor(context: ExtensionContext, public blameProvider: GitBlameProvider) {
41+
this.repoPath = context.workspaceState.get(WorkspaceState.RepoPath) as string;
42+
}
3443

3544
provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable<CodeLens[]> {
36-
// TODO: Should I wait here?
37-
const blame = gitBlame(document.fileName);
45+
this.blameProvider.blameFile(document.fileName);
3846

3947
return (commands.executeCommand(VsCodeCommands.ExecuteDocumentSymbolProvider, document.uri) as Promise<SymbolInformation[]>).then(symbols => {
4048
let lenses: CodeLens[] = [];
41-
symbols.forEach(sym => this._provideCodeLens(document, sym, blame, lenses));
49+
symbols.forEach(sym => this._provideCodeLens(document, sym, lenses));
4250

4351
// Check if we have a lens for the whole document -- if not add one
4452
if (!lenses.find(l => l.range.start.line === 0 && l.range.end.line === 0)) {
4553
const docRange = document.validateRange(new Range(0, 1000000, 1000000, 1000000));
46-
lenses.push(new GitBlameCodeLens(blame, this.repoPath, document.fileName, docRange, new Range(0, 0, 0, docRange.start.character)));
54+
lenses.push(new GitBlameCodeLens(this.blameProvider, document.fileName, docRange, new Range(0, 0, 0, docRange.start.character)));
4755
lenses.push(new GitHistoryCodeLens(this.repoPath, document.fileName, docRange.with(new Position(docRange.start.line, docRange.start.character + 1))));
4856
}
4957
return lenses;
5058
});
5159
}
5260

53-
private _provideCodeLens(document: TextDocument, symbol: SymbolInformation, blame: Promise<IGitBlameLine[]>, lenses: CodeLens[]): void {
61+
private _provideCodeLens(document: TextDocument, symbol: SymbolInformation, lenses: CodeLens[]): void {
5462
switch (symbol.kind) {
5563
case SymbolKind.Package:
5664
case SymbolKind.Module:
@@ -68,7 +76,7 @@ export default class GitCodeLensProvider implements CodeLensProvider {
6876
}
6977

7078
const line = document.lineAt(symbol.location.range.start);
71-
lenses.push(new GitBlameCodeLens(blame, this.repoPath, document.fileName, symbol.location.range, line.range.with(new Position(line.range.start.line, line.firstNonWhitespaceCharacterIndex))));
79+
lenses.push(new GitBlameCodeLens(this.blameProvider, document.fileName, symbol.location.range, line.range.with(new Position(line.range.start.line, line.firstNonWhitespaceCharacterIndex))));
7280
lenses.push(new GitHistoryCodeLens(this.repoPath, document.fileName, line.range.with(new Position(line.range.start.line, line.firstNonWhitespaceCharacterIndex + 1))));
7381
}
7482

@@ -79,45 +87,34 @@ export default class GitCodeLensProvider implements CodeLensProvider {
7987

8088
_resolveGitBlameCodeLens(lens: GitBlameCodeLens, token: CancellationToken): Thenable<CodeLens> {
8189
return new Promise<CodeLens>((resolve, reject) => {
82-
lens.getBlameLines().then(lines => {
83-
if (!lines.length) {
90+
lens.getBlame().then(blame => {
91+
if (!blame.lines.length) {
8492
console.error('No blame lines found', lens);
8593
reject(null);
8694
return;
8795
}
8896

89-
let recentLine = lines[0];
97+
// TODO: Rework this to only get the locations in the ShowBlameHistory command, rather than here -- should save a lot of processing
98+
const commitCount = blame.commits.size;
9099

91-
let locations: Location[] = [];
92-
if (lines.length > 1) {
93-
let sorted = lines.sort((a, b) => b.date.getTime() - a.date.getTime());
94-
recentLine = sorted[0];
95-
96-
// console.log(lens.fileName, 'Blame lines:', sorted);
97-
98-
let map: Map<string, IGitBlameLine[]> = new Map();
99-
sorted.forEach(l => {
100-
let item = map.get(l.sha);
101-
if (item) {
102-
item.push(l);
103-
} else {
104-
map.set(l.sha, [l]);
100+
let recentCommit;
101+
Array.from(blame.commits.values())
102+
.sort((a, b) => b.date.getTime() - a.date.getTime())
103+
.forEach((c, i) => {
104+
if (i === 0) {
105+
recentCommit = c;
105106
}
106-
});
107107

108-
const commits = Array.from(map.keys());
109-
Array.from(map.values()).forEach((lines, i) => {
110-
const uri = GitBlameCodeLens.toUri(lens, i + 1, lines[0], lines, commits);
111-
lines.forEach(l => locations.push(new Location(uri, new Position(l.originalLine, 0))));
108+
const uri = GitBlameCodeLens.toUri(lens, this.repoPath, c, i + 1, commitCount);
109+
blame.lines
110+
.filter(l => l.sha === c.sha)
111+
.forEach(l => lens.locations.push(new Location(uri, new Position(l.originalLine, 0))));
112112
});
113-
} else {
114-
locations = [new Location(GitBlameCodeLens.toUri(lens, 1, recentLine, lines, [recentLine.sha]), lens.range.start)];
115-
}
116113

117114
lens.command = {
118-
title: `${recentLine.author}, ${moment(recentLine.date).fromNow()}`,
115+
title: `${recentCommit.author}, ${moment(recentCommit.date).fromNow()}`,
119116
command: Commands.ShowBlameHistory,
120-
arguments: [Uri.file(lens.fileName), lens.range.start, locations]
117+
arguments: [Uri.file(lens.fileName), lens.range.start, lens.locations]
121118
};
122119
resolve(lens);
123120
});

src/constants.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
export type WorkspaceState = 'repoPath';
2+
export const WorkspaceState = {
3+
RepoPath: 'repoPath' as WorkspaceState
4+
}
5+
6+
export const RepoPath: string = 'repoPath';
7+
18
export type Commands = 'git.action.showBlameHistory';
29
export const Commands = {
310
ShowBlameHistory: 'git.action.showBlameHistory' as Commands
@@ -8,8 +15,9 @@ export const DocumentSchemes = {
815
GitBlame: 'gitblame' as DocumentSchemes
916
}
1017

11-
export type VsCodeCommands = 'vscode.executeDocumentSymbolProvider' | 'editor.action.showReferences';
18+
export type VsCodeCommands = 'vscode.executeDocumentSymbolProvider' | 'vscode.executeCodeLensProvider' | 'editor.action.showReferences';
1219
export const VsCodeCommands = {
1320
ExecuteDocumentSymbolProvider: 'vscode.executeDocumentSymbolProvider' as VsCodeCommands,
21+
ExecuteCodeLensProvider: 'vscode.executeCodeLensProvider' as VsCodeCommands,
1422
ShowReferences: 'editor.action.showReferences' as VsCodeCommands
1523
}

src/contentProvider.ts

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
'use strict';
22
import {Disposable, EventEmitter, ExtensionContext, OverviewRulerLane, Range, TextEditor, TextEditorDecorationType, TextDocumentContentProvider, Uri, window, workspace} from 'vscode';
3-
import {DocumentSchemes} from './constants';
3+
import {DocumentSchemes, WorkspaceState} from './constants';
44
import {gitGetVersionText} from './git';
5-
import {fromGitBlameUri, IGitBlameUriData} from './gitBlameUri';
5+
import GitBlameProvider, {IGitBlameUriData} from './gitBlameProvider';
66
import * as moment from 'moment';
77

88
export default class GitBlameContentProvider implements TextDocumentContentProvider {
99
static scheme = DocumentSchemes.GitBlame;
1010

11+
public repoPath: string;
1112
private _blameDecoration: TextEditorDecorationType;
1213
private _onDidChange = new EventEmitter<Uri>();
13-
// private _subscriptions: Disposable;
14-
// private _dataMap: Map<string, IGitBlameUriData>;
14+
//private _subscriptions: Disposable;
15+
16+
constructor(context: ExtensionContext, public blameProvider: GitBlameProvider) {
17+
this.repoPath = context.workspaceState.get(WorkspaceState.RepoPath) as string;
1518

16-
constructor(context: ExtensionContext) {
1719
this._blameDecoration = window.createTextEditorDecorationType({
1820
dark: {
1921
backgroundColor: 'rgba(255, 255, 255, 0.15)',
@@ -30,25 +32,14 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi
3032
isWholeLine: true
3133
});
3234

33-
// this._dataMap = new Map();
34-
// this._subscriptions = Disposable.from(
35+
//this._subscriptions = Disposable.from(
3536
// window.onDidChangeActiveTextEditor(e => e ? console.log(e.document.uri) : console.log('active missing')),
36-
// workspace.onDidOpenTextDocument(d => {
37-
// let data = this._dataMap.get(d.uri.toString());
38-
// if (!data) return;
39-
40-
// // TODO: This only works on the first load -- not after since it is cached
41-
// this._tryAddBlameDecorations(d.uri, data);
42-
// }),
43-
// workspace.onDidCloseTextDocument(d => {
44-
// this._dataMap.delete(d.uri.toString());
45-
// })
46-
// );
37+
//);
4738
}
4839

4940
dispose() {
5041
this._onDidChange.dispose();
51-
// this._subscriptions && this._subscriptions.dispose();
42+
//this._subscriptions && this._subscriptions.dispose();
5243
}
5344

5445
get onDidChange() {
@@ -60,12 +51,11 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi
6051
}
6152

6253
provideTextDocumentContent(uri: Uri): string | Thenable<string> {
63-
const data = fromGitBlameUri(uri);
64-
// this._dataMap.set(uri.toString(), data);
54+
const data = GitBlameProvider.fromBlameUri(uri);
6555

6656
//const editor = this._findEditor(Uri.file(join(data.repoPath, data.file)));
6757

68-
return gitGetVersionText(data.repoPath, data.sha, data.file).then(text => {
58+
return gitGetVersionText(data.fileName, this.repoPath, data.sha).then(text => {
6959
this.update(uri);
7060

7161
// TODO: This only works on the first load -- not after since it is cached
@@ -77,7 +67,7 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi
7767
return text;
7868
});
7969

80-
// return gitGetVersionFile(data.repoPath, data.sha, data.file).then(dst => {
70+
// return gitGetVersionFile(data.file, this.repoPath, data.sha).then(dst => {
8171
// let uri = Uri.parse(`file:${dst}`)
8272
// return workspace.openTextDocument(uri).then(doc => {
8373
// this.update(uri);
@@ -102,12 +92,16 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi
10292
let editor = this._findEditor(uri);
10393
if (editor) {
10494
clearInterval(handle);
105-
editor.setDecorations(this._blameDecoration, data.lines.map(l => {
106-
return {
107-
range: editor.document.validateRange(new Range(l.originalLine, 0, l.originalLine, 1000000)),
108-
hoverMessage: `${moment(l.date).format('MMMM Do, YYYY hh:MMa')}\n${l.author}\n${l.sha}`
109-
};
110-
}));
95+
this.blameProvider.getBlameForShaRange(data.fileName, data.sha, data.range).then(blame => {
96+
if (blame.lines.length) {
97+
editor.setDecorations(this._blameDecoration, blame.lines.map(l => {
98+
return {
99+
range: editor.document.validateRange(new Range(l.originalLine, 0, l.originalLine, 1000000)),
100+
hoverMessage: `${moment(blame.commit.date).format('MMMM Do, YYYY hh:MMa')}\n${blame.commit.author}\n${l.sha}`
101+
};
102+
}));
103+
}
104+
});
111105
}
112106
}, 200);
113107
}

src/definitionProvider.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/extension.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,47 @@
11
'use strict';
2-
import {commands, DocumentSelector, ExtensionContext, languages, workspace} from 'vscode';
3-
import GitCodeLensProvider from './codeLensProvider';
2+
import {CodeLens, commands, DocumentSelector, ExtensionContext, languages, Uri, window, workspace} from 'vscode';
3+
import GitCodeLensProvider, {GitBlameCodeLens} from './codeLensProvider';
44
import GitContentProvider from './contentProvider';
55
import {gitRepoPath} from './git';
6-
import {Commands, VsCodeCommands} from './constants';
6+
import GitBlameProvider from './gitBlameProvider';
7+
import {Commands, VsCodeCommands, WorkspaceState} from './constants';
78

89
// this method is called when your extension is activated
910
export function activate(context: ExtensionContext) {
1011
// Workspace not using a folder. No access to git repo.
1112
if (!workspace.rootPath) {
12-
console.warn('Git CodeLens inactive: no rootPath');
13+
console.warn('GitLens inactive: no rootPath');
1314

1415
return;
1516
}
1617

17-
console.log(`Git CodeLens active: ${workspace.rootPath}`);
18-
18+
console.log(`GitLens active: ${workspace.rootPath}`);
1919
gitRepoPath(workspace.rootPath).then(repoPath => {
20-
context.subscriptions.push(workspace.registerTextDocumentContentProvider(GitContentProvider.scheme, new GitContentProvider(context)));
20+
context.workspaceState.update(WorkspaceState.RepoPath, repoPath);
21+
22+
const blameProvider = new GitBlameProvider();
23+
context.subscriptions.push(blameProvider);
24+
25+
context.subscriptions.push(workspace.registerTextDocumentContentProvider(GitContentProvider.scheme, new GitContentProvider(context, blameProvider)));
2126

2227
context.subscriptions.push(commands.registerCommand(Commands.ShowBlameHistory, (...args) => {
23-
return commands.executeCommand(VsCodeCommands.ShowReferences, ...args);
28+
if (args && args.length) {
29+
return commands.executeCommand(VsCodeCommands.ShowReferences, ...args);
30+
}
31+
32+
// const uri = window.activeTextEditor && window.activeTextEditor.document && window.activeTextEditor.document.uri;
33+
// if (uri) {
34+
// return (commands.executeCommand(VsCodeCommands.ExecuteCodeLensProvider, uri) as Promise<CodeLens[]>).then(lenses => {
35+
// const lens = <GitBlameCodeLens>lenses.find(l => l instanceof GitBlameCodeLens);
36+
// if (lens) {
37+
// return commands.executeCommand(Commands.ShowBlameHistory, Uri.file(lens.fileName), lens.range.start, lens.locations);
38+
// }
39+
// });
40+
// }
2441
}));
2542

2643
const selector: DocumentSelector = { scheme: 'file' };
27-
context.subscriptions.push(languages.registerCodeLensProvider(selector, new GitCodeLensProvider(repoPath)));
44+
context.subscriptions.push(languages.registerCodeLensProvider(selector, new GitCodeLensProvider(context, blameProvider)));
2845
}).catch(reason => console.warn(reason));
2946
}
3047

0 commit comments

Comments
 (0)