From 1576c08fa8f07788a6c6b79672edcd800d6b49eb Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Fri, 26 Aug 2016 18:27:38 -0400 Subject: [PATCH] Reworked git access Cleaned up the blame hightlights (wip) --- blame.png | Bin 24733 -> 0 bytes images/blame-dark.png | Bin 0 -> 24729 bytes images/blame-light.png | Bin 0 -> 24723 bytes package.json | 5 ++- src/codeLensProvider.ts | 112 ++++++++++++++++++++++++------------------------ src/contentProvider.ts | 48 ++++++++------------- src/git.ts | 75 ++++++-------------------------- src/gitBlameUri.ts | 27 ++++++++++++ typings/spawn-rx.d.ts | 13 ++++++ 9 files changed, 131 insertions(+), 149 deletions(-) delete mode 100644 blame.png create mode 100644 images/blame-dark.png create mode 100644 images/blame-light.png create mode 100644 src/gitBlameUri.ts create mode 100644 typings/spawn-rx.d.ts diff --git a/blame.png b/blame.png deleted file mode 100644 index 22f8bcd53767b413a3498fd74adf6085a1e3383b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24733 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 bqakqKp^L@w1@vw3N5IX=(=pd!*1~@S0J^rm literal 0 HcmV?d00001 diff --git a/images/blame-light.png b/images/blame-light.png new file mode 100644 index 0000000000000000000000000000000000000000..02b1947b8e7200666652a02b5c9c8057bdb99ecf GIT binary patch literal 24723 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 literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 21936f2..6b75076 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,11 @@ "postinstall": "node ./node_modules/vscode/bin/install && tsc" }, "dependencies": { - "tmp": "^0.0.28" + "tmp": "^0.0.28", + "spawn-rx": "^2.0.1" }, "devDependencies": { "typescript": "^1.8.10", - "vscode": "^0.11.15" + "vscode": "^0.11.17" } } \ No newline at end of file diff --git a/src/codeLensProvider.ts b/src/codeLensProvider.ts index 96738ec..ae489c5 100644 --- a/src/codeLensProvider.ts +++ b/src/codeLensProvider.ts @@ -2,7 +2,7 @@ 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 './contentProvider'; +import {toGitBlameUri} from './gitBlameUri'; import * as moment from 'moment'; export class GitBlameCodeLens extends CodeLens { @@ -34,7 +34,7 @@ export default class GitCodeLensProvider implements CodeLensProvider { provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable { // TODO: Should I wait here? - let blame = gitBlame(document.fileName); + const blame = gitBlame(document.fileName); return (commands.executeCommand(VsCodeCommands.ExecuteDocumentSymbolProvider, document.uri) as Promise).then(symbols => { let lenses: CodeLens[] = []; @@ -44,6 +44,7 @@ export default class GitCodeLensProvider implements CodeLensProvider { 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 GitHistoryCodeLens(this.repoPath, document.fileName, docRange.with(new Position(docRange.start.line, docRange.start.character + 1)))); } return lenses; }); @@ -66,67 +67,66 @@ export default class GitCodeLensProvider implements CodeLensProvider { return; } - var line = document.lineAt(symbol.location.range.start); + 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 GitHistoryCodeLens(this.repoPath, document.fileName, line.range.with(new Position(line.range.start.line, line.firstNonWhitespaceCharacterIndex + 1)))); } resolveCodeLens(lens: CodeLens, token: CancellationToken): Thenable { - if (lens instanceof GitBlameCodeLens) { - return lens.getBlameLines().then(lines => { - if (!lines.length) { - console.error('No blame lines found', lens); - throw new Error('No blame lines found'); - } - - let recentLine = lines[0]; - - 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 = new Map(); - sorted.forEach(l => { - let item = map.get(l.sha); - if (item) { - item.push(l); - } else { - map.set(l.sha, [l]); - } - }); - - Array.from(map.values()).forEach((lines, i) => { - const uri = GitBlameCodeLens.toUri(lens, i + 1, lines[0], lines); - lines.forEach(l => { - locations.push(new Location(uri, new Position(l.originalLine, 0))); - }); - }); - - //locations = Array.from(map.values()).map((l, i) => new Location(GitBlameCodeLens.toUri(lens, i, l[0], l), new Position(l[0].originalLine, 0)));//lens.range.start)) - } else { - locations = [new Location(GitBlameCodeLens.toUri(lens, 1, recentLine, lines), lens.range.start)]; - } - - lens.command = { - title: `${recentLine.author}, ${moment(recentLine.date).fromNow()}`, - command: Commands.ShowBlameHistory, - arguments: [Uri.file(lens.fileName), lens.range.start, locations] - }; - return lens; - }).catch(ex => Promise.reject(ex)); // TODO: Figure out a better way to stop the codelens from appearing - } + if (lens instanceof GitBlameCodeLens) return this._resolveGitBlameCodeLens(lens, token); + if (lens instanceof GitHistoryCodeLens) return this._resolveGitHistoryCodeLens(lens, token); + } + + _resolveGitBlameCodeLens(lens: GitBlameCodeLens, token: CancellationToken): Thenable { + return lens.getBlameLines().then(lines => { + if (!lines.length) { + console.error('No blame lines found', lens); + throw new Error('No blame lines found'); + } + + let recentLine = lines[0]; + + 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 = new Map(); + sorted.forEach(l => { + let item = map.get(l.sha); + if (item) { + item.push(l); + } else { + map.set(l.sha, [l]); + } + }); + + Array.from(map.values()).forEach((lines, i) => { + const uri = GitBlameCodeLens.toUri(lens, i + 1, lines[0], lines); + lines.forEach(l => locations.push(new Location(uri, new Position(l.originalLine, 0)))); + }); + } else { + locations = [new Location(GitBlameCodeLens.toUri(lens, 1, recentLine, lines), lens.range.start)]; + } - // TODO: Play with this more -- get this to open the correct diff to the right place - if (lens instanceof GitHistoryCodeLens) { lens.command = { - title: `View Diff`, - command: 'git.viewFileHistory', // viewLineHistory - arguments: [Uri.file(lens.fileName)] + title: `${recentLine.author}, ${moment(recentLine.date).fromNow()}`, + command: Commands.ShowBlameHistory, + arguments: [Uri.file(lens.fileName), lens.range.start, locations] }; - return Promise.resolve(lens); - } + return lens; + }).catch(ex => Promise.reject(ex)); // TODO: Figure out a better way to stop the codelens from appearing + } + + _resolveGitHistoryCodeLens(lens: GitHistoryCodeLens, token: CancellationToken): Thenable { + // TODO: Play with this more -- get this to open the correct diff to the right place + lens.command = { + title: `View History`, + command: 'git.viewFileHistory', // viewLineHistory + arguments: [Uri.file(lens.fileName)] + }; + return Promise.resolve(lens); } } diff --git a/src/contentProvider.ts b/src/contentProvider.ts index 0847eb1..b5ef73b 100644 --- a/src/contentProvider.ts +++ b/src/contentProvider.ts @@ -1,8 +1,8 @@ 'use strict'; import {Disposable, EventEmitter, ExtensionContext, OverviewRulerLane, Range, TextEditor, TextEditorDecorationType, TextDocumentContentProvider, Uri, window, workspace} from 'vscode'; import {DocumentSchemes} from './constants'; -import {gitGetVersionFile, gitGetVersionText, IGitBlameLine} from './git'; -import {basename, dirname, extname, join} from 'path'; +import {gitGetVersionText} from './git'; +import {fromGitBlameUri, IGitBlameUriData} from './gitBlameUri'; import * as moment from 'moment'; export default class GitBlameContentProvider implements TextDocumentContentProvider { @@ -10,21 +10,29 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi private _blameDecoration: TextEditorDecorationType; private _onDidChange = new EventEmitter(); - private _subscriptions: Disposable; + // private _subscriptions: Disposable; // private _dataMap: Map; constructor(context: ExtensionContext) { - // TODO: Light & Dark this._blameDecoration = window.createTextEditorDecorationType({ - backgroundColor: 'rgba(254, 220, 95, 0.15)', - gutterIconPath: context.asAbsolutePath('blame.png'), - overviewRulerColor: 'rgba(254, 220, 95, 0.60)', + dark: { + backgroundColor: 'rgba(255, 255, 255, 0.15)', + gutterIconPath: context.asAbsolutePath('images/blame-dark.png'), + overviewRulerColor: 'rgba(255, 255, 255, 0.75)', + }, + light: { + backgroundColor: 'rgba(0, 0, 0, 0.15)', + gutterIconPath: context.asAbsolutePath('images/blame-light.png'), + overviewRulerColor: 'rgba(0, 0, 0, 0.75)', + }, + gutterIconSize: 'contain', overviewRulerLane: OverviewRulerLane.Right, isWholeLine: true }); // this._dataMap = new Map(); // this._subscriptions = Disposable.from( + // window.onDidChangeActiveTextEditor(e => e ? console.log(e.document.uri) : console.log('active missing')), // workspace.onDidOpenTextDocument(d => { // let data = this._dataMap.get(d.uri.toString()); // if (!data) return; @@ -40,7 +48,7 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi dispose() { this._onDidChange.dispose(); - this._subscriptions && this._subscriptions.dispose(); + // this._subscriptions && this._subscriptions.dispose(); } get onDidChange() { @@ -57,7 +65,6 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi //const editor = this._findEditor(Uri.file(join(data.repoPath, data.file))); - //console.log('provideTextDocumentContent', uri, data); return gitGetVersionText(data.repoPath, data.sha, data.file).then(text => { this.update(uri); @@ -81,6 +88,7 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi private _findEditor(uri: Uri): TextEditor { let uriString = uri.toString(); + // TODO: This is a big hack :) const matcher = (e: any) => (e._documentData && e._documentData._uri && e._documentData._uri.toString()) === uriString; if (matcher(window.activeTextEditor)) { return window.activeTextEditor; @@ -89,6 +97,7 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi } private _tryAddBlameDecorations(uri: Uri, data: IGitBlameUriData) { + // Needs to be on a timer for some reason because we won't find the editor otherwise -- is there an event? let handle = setInterval(() => { let editor = this._findEditor(uri); if (editor) { @@ -96,7 +105,7 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi editor.setDecorations(this._blameDecoration, data.lines.map(l => { return { range: editor.document.validateRange(new Range(l.originalLine, 0, l.originalLine, 1000000)), - hoverMessage: `${moment(l.date).fromNow()}\n${l.author}\n${l.sha}` + hoverMessage: `${moment(l.date).format('MMMM Do, YYYY hh:MMa')}\n${l.author}\n${l.sha}` }; })); } @@ -106,23 +115,4 @@ export default class GitBlameContentProvider implements TextDocumentContentProvi // private _addBlameDecorations(editor: TextEditor, data: IGitBlameUriData) { // editor.setDecorations(this._blameDecoration, data.lines.map(l => editor.document.validateRange(new Range(l.line, 0, l.line, 1000000)))); // } -} - -export interface IGitBlameUriData extends IGitBlameLine { - repoPath: string, - range: Range, - index: number, - lines: IGitBlameLine[] -} - -export function toGitBlameUri(data: IGitBlameUriData) { - let ext = extname(data.file); - let path = `${dirname(data.file)}/${data.sha}: ${basename(data.file, ext)}${ext}`; - return Uri.parse(`${DocumentSchemes.GitBlame}:${data.index}. ${moment(data.date).format('YYYY-MM-DD hh:MMa')} ${path}?${JSON.stringify(data)}`); -} - -export function fromGitBlameUri(uri: Uri): IGitBlameUriData { - let data = JSON.parse(uri.query); - data.range = new Range(data.range[0].line, data.range[0].character, data.range[1].line, data.range[1].character); - return data; } \ No newline at end of file diff --git a/src/git.ts b/src/git.ts index 18f0d3f..ace42be 100644 --- a/src/git.ts +++ b/src/git.ts @@ -1,8 +1,8 @@ 'use strict'; -import {spawn} from 'child_process'; import {basename, dirname, extname} from 'path'; import * as fs from 'fs'; import * as tmp from 'tmp'; +import {spawnPromise} from 'spawn-rx'; export declare interface IGitBlameLine { sha: string; @@ -14,28 +14,14 @@ export declare interface IGitBlameLine { code: string; } -export function gitRepoPath(cwd): Promise { - let data: Array = []; - const capture = input => data.push(input.toString().replace(/\r?\n|\r/g, '')); - const output = () => data[0]; - - return gitCommand(cwd, capture, output, 'rev-parse', '--show-toplevel'); - - // return new Promise((resolve, reject) => { - // gitCommand(cwd, capture, output, 'rev-parse', '--show-toplevel') - // .then(result => resolve(result[0])) - // .catch(reason => reject(reason)); - // }); +export function gitRepoPath(cwd) { + return gitCommand(cwd, 'rev-parse', '--show-toplevel').then(data => data.replace(/\r?\n|\r/g, '')); } -//const blameMatcher = /^(.*)\t\((.*)\t(.*)\t(.*?)\)(.*)$/gm; -//const blameMatcher = /^([0-9a-fA-F]{8})\s([\S]*)\s([0-9\S]+)\s\((.*?)\s([0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{2}:[0-9]{2}:[0-9]{2}\s[-|+][0-9]{4})\s([0-9]+)\)(.*)$/gm; const blameMatcher = /^([0-9a-fA-F]{8})\s([\S]*)\s+([0-9\S]+)\s\((.*)\s([0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{2}:[0-9]{2}:[0-9]{2}\s[-|+][0-9]{4})\s+([0-9]+)\)(.*)$/gm; -export function gitBlame(fileName: string): Promise { - let data: string = ''; - const capture = input => data += input.toString(); - const output = () => { +export function gitBlame(fileName: string) { + return gitCommand(dirname(fileName), 'blame', '-fnw', '--', fileName).then(data => { let lines: Array = []; let m: Array; while ((m = blameMatcher.exec(data)) != null) { @@ -50,18 +36,12 @@ export function gitBlame(fileName: string): Promise { }); } return lines; - }; - - return gitCommand(dirname(fileName), capture, output, 'blame', '-fnw', '--', fileName); + }); } -export function gitGetVersionFile(repoPath: string, sha: string, source: string): Promise { - let data: Array = []; - const capture = input => data.push(input); - const output = () => data; - +export function gitGetVersionFile(repoPath: string, sha: string, source: string): Promise { return new Promise((resolve, reject) => { - (gitCommand(repoPath, capture, output, 'show', `${sha}:${source.replace(/\\/g, '/')}`) as Promise>).then(o => { + gitCommand(repoPath, 'show', `${sha}:${source.replace(/\\/g, '/')}`).then(data => { let ext = extname(source); tmp.file({ prefix: `${basename(source, ext)}-${sha}_`, postfix: ext }, (err, destination, fd, cleanupCallback) => { if (err) { @@ -72,7 +52,7 @@ export function gitGetVersionFile(repoPath: string, sha: string, source: string) console.log("File: ", destination); console.log("Filedescriptor: ", fd); - fs.appendFile(destination, o.join(), err => { + fs.appendFile(destination, data, err => { if (err) { reject(err); return; @@ -84,39 +64,10 @@ export function gitGetVersionFile(repoPath: string, sha: string, source: string) }); } -export function gitGetVersionText(repoPath: string, sha: string, source: string): Promise { - let data: Array = []; - const capture = input => data.push(input.toString()); - const output = () => data; - - return new Promise((resolve, reject) => (gitCommand(repoPath, capture, output, 'show', `${sha}:${source.replace(/\\/g, '/')}`) as Promise>).then(o => resolve(o.join()))); +export function gitGetVersionText(repoPath: string, sha: string, source: string) { + return gitCommand(repoPath, 'show', `${sha}:${source.replace(/\\/g, '/')}`); } -function gitCommand(cwd: string, capture: (input: Buffer) => void, output: () => any, ...args): Promise { - return new Promise((resolve, reject) => { - let spawn = require('child_process').spawn; - let process = spawn('git', args, { cwd: cwd }); - - process.stdout.on('data', data => { - capture(data); - }); - - let errors: Array = []; - process.stderr.on('data', err => { - errors.push(err.toString()); - }); - - process.on('close', (exitCode, exitSignal) => { - if (exitCode && errors.length) { - reject(errors.toString()); - return; - } - - try { - resolve(output()); - } catch (ex) { - reject(ex); - } - }); - }); +function gitCommand(cwd: string, ...args) { + return spawnPromise('git', args, { cwd: cwd }); } \ No newline at end of file diff --git a/src/gitBlameUri.ts b/src/gitBlameUri.ts new file mode 100644 index 0000000..4504acf --- /dev/null +++ b/src/gitBlameUri.ts @@ -0,0 +1,27 @@ +import {Range, Uri} from 'vscode'; +import {DocumentSchemes} from './constants'; +import {IGitBlameLine} from './git'; +import {basename, dirname, extname} from 'path'; +import * as moment from 'moment'; + +export interface IGitBlameUriData extends IGitBlameLine { + repoPath: string, + range: Range, + index: number, + lines: IGitBlameLine[] +} + +export function toGitBlameUri(data: IGitBlameUriData) { + const pad = n => ("000" + n).slice(-3); + + let ext = extname(data.file); + let path = `${dirname(data.file)}/${data.sha}: ${basename(data.file, ext)}${ext}`; + // TODO: Need to specify an index here, since I can't control the sort order -- just alphabetic or by file location + return Uri.parse(`${DocumentSchemes.GitBlame}:${pad(data.index)}. ${data.author}, ${moment(data.date).format('MMM D, YYYY hh:MMa')} - ${path}?${JSON.stringify(data)}`); +} + +export function fromGitBlameUri(uri: Uri): IGitBlameUriData { + let data = JSON.parse(uri.query); + data.range = new Range(data.range[0].line, data.range[0].character, data.range[1].line, data.range[1].character); + return data; +} \ No newline at end of file diff --git a/typings/spawn-rx.d.ts b/typings/spawn-rx.d.ts new file mode 100644 index 0000000..a0ee414 --- /dev/null +++ b/typings/spawn-rx.d.ts @@ -0,0 +1,13 @@ +/// +declare module "spawn-rx" { + import { Observable } from 'rxjs/Observable'; + + namespace spawnrx { + function findActualExecutable(exe: string, args: Array): { cmd: string, args: Array }; + function spawnDetached(exe: string, params: Array, opts: Object): Observable; + function spawn(exe: string, params: Array, opts: Object): Observable; + function spawnDetachedPromise(exe: string, params: Array, opts: Object): Promise; + function spawnPromise(exe: string, params: Array, opts: Object): Promise; + } + export = spawnrx; +} \ No newline at end of file