Refactored commands and blame annotationsmain
@ -0,0 +1,246 @@ | |||
'use strict' | |||
import {commands, DecorationOptions, Disposable, ExtensionContext, OverviewRulerLane, Range, TextDocument, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, Uri, window, workspace} from 'vscode'; | |||
import {BuiltInCommands} from './constants'; | |||
import {BlameAnnotationStyle, IBlameConfig} from './configuration'; | |||
import GitProvider, {IGitBlame, IGitCommit} from './gitProvider'; | |||
import * as moment from 'moment'; | |||
const blameDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({ | |||
before: { | |||
margin: '0 1.75em 0 0' | |||
} | |||
}); | |||
let highlightDecoration: TextEditorDecorationType; | |||
export class BlameAnnotationProvider extends Disposable { | |||
public uri: Uri; | |||
private _blame: Promise<IGitBlame>; | |||
private _config: IBlameConfig; | |||
private _disposable: Disposable; | |||
private _document: TextDocument; | |||
private _toggleWhitespace: boolean; | |||
constructor(private context: ExtensionContext, private git: GitProvider, public editor: TextEditor) { | |||
super(() => this.dispose()); | |||
if (!highlightDecoration) { | |||
highlightDecoration = window.createTextEditorDecorationType({ | |||
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._document = this.editor.document; | |||
this.uri = this._document.uri; | |||
this._blame = this.git.getBlameForFile(this.uri.fsPath); | |||
this._config = workspace.getConfiguration('gitlens').get<IBlameConfig>('blame'); | |||
const subscriptions: Disposable[] = []; | |||
subscriptions.push(window.onDidChangeTextEditorSelection(this._onActiveSelectionChanged, this)); | |||
this._disposable = Disposable.from(...subscriptions); | |||
} | |||
dispose() { | |||
if (this.editor) { | |||
// HACK: This only works when switching to another editor - diffs handle whitespace toggle differently | |||
if (this._toggleWhitespace) { | |||
commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace); | |||
} | |||
this.editor.setDecorations(blameDecoration, []); | |||
this.editor.setDecorations(highlightDecoration, []); | |||
} | |||
this._disposable && this._disposable.dispose(); | |||
} | |||
private _onActiveSelectionChanged(e: TextEditorSelectionChangeEvent) { | |||
this.git.getBlameForLine(e.textEditor.document.fileName, e.selections[0].active.line) | |||
.then(blame => blame && this._applyCommitHighlight(blame.commit.sha)); | |||
} | |||
provideBlameAnnotation(sha?: string) { | |||
return this._blame.then(blame => { | |||
if (!blame || !blame.lines.length) return; | |||
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- toggle whitespace off | |||
const whitespace = workspace.getConfiguration('editor').get<string>('renderWhitespace'); | |||
this._toggleWhitespace = whitespace !== 'false' && whitespace !== 'none'; | |||
if (this._toggleWhitespace) { | |||
commands.executeCommand(BuiltInCommands.ToggleRenderWhitespace); | |||
} | |||
let blameDecorationOptions: DecorationOptions[] | undefined; | |||
switch (this._config.annotation.style) { | |||
case BlameAnnotationStyle.Compact: | |||
blameDecorationOptions = this._getCompactGutterDecorations(blame); | |||
break; | |||
case BlameAnnotationStyle.Expanded: | |||
blameDecorationOptions = this._getExpandedGutterDecorations(blame); | |||
break; | |||
} | |||
if (blameDecorationOptions) { | |||
this.editor.setDecorations(blameDecoration, blameDecorationOptions); | |||
} | |||
sha = sha || blame.commits.values().next().value.sha; | |||
return this._applyCommitHighlight(sha); | |||
}); | |||
} | |||
private _applyCommitHighlight(sha: string) { | |||
return this._blame.then(blame => { | |||
if (!blame || !blame.lines.length) return; | |||
const highlightDecorationRanges = blame.lines | |||
.filter(l => l.sha === sha) | |||
.map(l => this.editor.document.validateRange(new Range(l.line, 0, l.line, 1000000))); | |||
this.editor.setDecorations(highlightDecoration, highlightDecorationRanges); | |||
}); | |||
} | |||
private _getCompactGutterDecorations(blame: IGitBlame): DecorationOptions[] { | |||
let count = 0; | |||
let lastSha; | |||
return blame.lines.map(l => { | |||
let color = l.previousSha ? '#999999' : '#6b6b6b'; | |||
let commit = blame.commits.get(l.sha); | |||
let hoverMessage: string | Array<string> = [`_${l.sha}_ - ${commit.message}`, `${commit.author}, ${moment(commit.date).format('MMMM Do, YYYY h:MM a')}`]; | |||
if (commit.isUncommitted) { | |||
color = 'rgba(0, 188, 242, 0.6)'; | |||
let previous = blame.commits.get(commit.previousSha); | |||
if (previous) { | |||
hoverMessage = ['Uncommitted changes', `_${previous.sha}_ - ${previous.message}`, `${previous.author}, ${moment(previous.date).format('MMMM Do, YYYY h:MM a')}`]; | |||
} else { | |||
hoverMessage = ['Uncommitted changes', `_${l.previousSha}_`]; | |||
} | |||
} | |||
let gutter = ''; | |||
if (lastSha !== l.sha) { | |||
count = -1; | |||
} | |||
const isEmptyOrWhitespace = this._document.lineAt(l.line).isEmptyOrWhitespace; | |||
if (!isEmptyOrWhitespace) { | |||
switch (++count) { | |||
case 0: | |||
gutter = commit.sha.substring(0, 8); | |||
break; | |||
case 1: | |||
gutter = `\\00a6\\00a0 ${this._getAuthor(commit, 17, true)}`; | |||
break; | |||
case 2: | |||
gutter = `\\00a6\\00a0 ${this._getDate(commit, true)}`; | |||
break; | |||
default: | |||
gutter = '\\00a6\\00a0'; | |||
break; | |||
} | |||
} | |||
lastSha = l.sha; | |||
return <DecorationOptions>{ | |||
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)), | |||
hoverMessage: hoverMessage, | |||
renderOptions: { before: { color: color, contentText: gutter, width: '11em' } } | |||
}; | |||
}); | |||
} | |||
private _getExpandedGutterDecorations(blame: IGitBlame): DecorationOptions[] { | |||
let width = 0; | |||
if (this._config.annotation.sha) { | |||
width += 5; | |||
} | |||
if (this._config.annotation.date) { | |||
if (width > 0) { | |||
width += 7; | |||
} else { | |||
width += 6; | |||
} | |||
} | |||
if (this._config.annotation.author) { | |||
if (width > 5 + 6) { | |||
width += 12; | |||
} else if (width > 0) { | |||
width += 11; | |||
} else { | |||
width += 10; | |||
} | |||
} | |||
return blame.lines.map(l => { | |||
let color = l.previousSha ? '#999999' : '#6b6b6b'; | |||
let commit = blame.commits.get(l.sha); | |||
let hoverMessage: string | Array<string> = [`_${l.sha}_ - ${commit.message}`, `${commit.author}, ${moment(commit.date).format('MMMM Do, YYYY h:MM a')}`]; | |||
if (commit.isUncommitted) { | |||
color = 'rgba(0, 188, 242, 0.6)'; | |||
let previous = blame.commits.get(commit.previousSha); | |||
if (previous) { | |||
hoverMessage = ['Uncommitted changes', `_${previous.sha}_ - ${previous.message}`, `${previous.author}, ${moment(previous.date).format('MMMM Do, YYYY h:MM a')}`]; | |||
} else { | |||
hoverMessage = ['Uncommitted changes', `_${l.previousSha}_`]; | |||
} | |||
} | |||
const gutter = this._getGutter(commit); | |||
return <DecorationOptions>{ | |||
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)), | |||
hoverMessage: hoverMessage, | |||
renderOptions: { before: { color: color, contentText: gutter, width: `${width}em` } } | |||
}; | |||
}); | |||
} | |||
private _getAuthor(commit: IGitCommit, max: number = 17, force: boolean = false) { | |||
if (!force && !this._config.annotation.author) return ''; | |||
let author = commit.isUncommitted ? 'Uncommitted': commit.author; | |||
if (author.length > max) { | |||
return `${author.substring(0, max - 1)}\\2026`; | |||
} | |||
return author; | |||
} | |||
private _getDate(commit: IGitCommit, force?: boolean) { | |||
if (!force && !this._config.annotation.date) return ''; | |||
return moment(commit.date).format('MM/DD/YYYY'); | |||
} | |||
private _getGutter(commit: IGitCommit) { | |||
const author = this._getAuthor(commit); | |||
const date = this._getDate(commit); | |||
if (this._config.annotation.sha) { | |||
return `${commit.sha.substring(0, 8)}${(date ? `\\00a0\\2022\\00a0 ${date}` : '')}${(author ? `\\00a0\\2022\\00a0 ${author}` : '')}`; | |||
} else if (this._config.annotation.date) { | |||
return `${date}${(author ? `\\00a0\\2022\\00a0 ${author}` : '')}`; | |||
} else { | |||
return author; | |||
} | |||
} | |||
} |
@ -1,189 +0,0 @@ | |||
'use strict' | |||
import {commands, DecorationOptions, Disposable, OverviewRulerLane, Position, Range, TextEditor, TextEditorEdit, TextEditorDecorationType, Uri, window} from 'vscode'; | |||
import {BuiltInCommands, Commands} from './constants'; | |||
import GitProvider from './gitProvider'; | |||
import BlameAnnotationController from './blameAnnotationController'; | |||
import * as moment from 'moment'; | |||
import * as path from 'path'; | |||
abstract class Command extends Disposable { | |||
private _subscriptions: Disposable; | |||
constructor(command: Commands) { | |||
super(() => this.dispose()); | |||
this._subscriptions = commands.registerCommand(command, this.execute, this); | |||
} | |||
dispose() { | |||
this._subscriptions && this._subscriptions.dispose(); | |||
} | |||
abstract execute(...args): any; | |||
} | |||
abstract class EditorCommand extends Disposable { | |||
private _subscriptions: Disposable; | |||
constructor(command: Commands) { | |||
super(() => this.dispose()); | |||
this._subscriptions = commands.registerTextEditorCommand(command, this.execute, this); | |||
} | |||
dispose() { | |||
this._subscriptions && this._subscriptions.dispose(); | |||
} | |||
abstract execute(editor: TextEditor, edit: TextEditorEdit, ...args): any; | |||
} | |||
export class DiffWithPreviousCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.DiffWithPrevious); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, repoPath?: string, sha?: string, shaUri?: Uri, compareWithSha?: string, compareWithUri?: Uri, line?: number) { | |||
line = line || editor.selection.active.line + 1; | |||
if (!sha || GitProvider.isUncommitted(sha)) { | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, line) | |||
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', `getBlameForLine(${line})`, ex)) | |||
.then(blame => { | |||
if (!blame) return; | |||
// If the line is uncommitted, find the previous commit | |||
const commit = blame.commit; | |||
if (commit.isUncommitted) { | |||
return this.git.getBlameForLine(commit.previousUri.fsPath, blame.line.originalLine + 1, commit.previousSha, commit.repoPath) | |||
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', `getBlameForLine(${blame.line.originalLine}, ${commit.previousSha})`, ex)) | |||
.then(prevBlame => { | |||
if (!prevBlame) return; | |||
const prevCommit = prevBlame.commit; | |||
return commands.executeCommand(Commands.DiffWithPrevious, commit.previousUri, commit.repoPath, commit.previousSha, commit.previousUri, prevCommit.sha, prevCommit.uri, blame.line.originalLine); | |||
}); | |||
} | |||
return commands.executeCommand(Commands.DiffWithPrevious, commit.uri, commit.repoPath, commit.sha, commit.uri, commit.previousSha, commit.previousUri, line); | |||
}); | |||
} | |||
if (!compareWithSha) { | |||
return window.showInformationMessage(`Commit ${sha} has no previous commit`); | |||
} | |||
return Promise.all([this.git.getVersionedFile(shaUri.fsPath, repoPath, sha), this.git.getVersionedFile(compareWithUri.fsPath, repoPath, compareWithSha)]) | |||
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', 'getVersionedFile', ex)) | |||
.then(values => commands.executeCommand(BuiltInCommands.Diff, Uri.file(values[1]), Uri.file(values[0]), `${path.basename(compareWithUri.fsPath)} (${compareWithSha}) ↔ ${path.basename(shaUri.fsPath)} (${sha})`) | |||
.then(() => commands.executeCommand(BuiltInCommands.RevealLine, {lineNumber: line, at: 'center'}))); | |||
} | |||
} | |||
export class DiffWithWorkingCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.DiffWithWorking); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, repoPath?: string, sha?: string, shaUri?: Uri, line?: number) { | |||
line = line || editor.selection.active.line + 1; | |||
if (!sha || GitProvider.isUncommitted(sha)) { | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, line) | |||
.catch(ex => console.error('[GitLens.DiffWithWorkingCommand]', `getBlameForLine(${line})`, ex)) | |||
.then(blame => { | |||
if (!blame) return; | |||
const commit = blame.commit; | |||
// If the line is uncommitted, find the previous commit | |||
if (commit.isUncommitted) { | |||
return commands.executeCommand(Commands.DiffWithWorking, commit.uri, commit.repoPath, commit.previousSha, commit.previousUri, blame.line.line + 1); | |||
} | |||
return commands.executeCommand(Commands.DiffWithWorking, commit.uri, commit.repoPath, commit.sha, commit.uri, line) | |||
}); | |||
}; | |||
return this.git.getVersionedFile(shaUri.fsPath, repoPath, sha) | |||
.catch(ex => console.error('[GitLens.DiffWithWorkingCommand]', 'getVersionedFile', ex)) | |||
.then(compare => commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), uri, `${path.basename(shaUri.fsPath)} (${sha}) ↔ ${path.basename(uri.fsPath)}`) | |||
.then(() => commands.executeCommand(BuiltInCommands.RevealLine, {lineNumber: line, at: 'center'}))); | |||
} | |||
} | |||
export class ShowBlameCommand extends EditorCommand { | |||
constructor(private git: GitProvider, private annotationController: BlameAnnotationController) { | |||
super(Commands.ShowBlame); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string) { | |||
if (sha) { | |||
return this.annotationController.toggleBlameAnnotation(editor, sha); | |||
} | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, editor.selection.active.line) | |||
.catch(ex => console.error('[GitLens.ShowBlameCommand]', `getBlameForLine(${editor.selection.active.line})`, ex)) | |||
.then(blame => this.annotationController.showBlameAnnotation(editor, blame && blame.commit.sha)); | |||
} | |||
} | |||
export class ShowBlameHistoryCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.ShowBlameHistory); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, range?: Range, position?: Position) { | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
// If the command is executed manually -- treat it as a click on the root lens (i.e. show blame for the whole file) | |||
range = editor.document.validateRange(new Range(0, 0, 1000000, 1000000)); | |||
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start; | |||
} | |||
return this.git.getBlameLocations(uri.fsPath, range) | |||
.catch(ex => console.error('[GitLens.ShowBlameHistoryCommand]', 'getBlameLocations', ex)) | |||
.then(locations => commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations)); | |||
} | |||
} | |||
export class ToggleBlameCommand extends EditorCommand { | |||
constructor(private git: GitProvider, private blameController: BlameAnnotationController) { | |||
super(Commands.ToggleBlame); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string) { | |||
if (sha) { | |||
return this.blameController.toggleBlameAnnotation(editor, sha); | |||
} | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, editor.selection.active.line) | |||
.catch(ex => console.error('[GitLens.ToggleBlameCommand]', `getBlameForLine(${editor.selection.active.line})`, ex)) | |||
.then(blame => this.blameController.toggleBlameAnnotation(editor, blame && blame.commit.sha)); | |||
} | |||
} | |||
export class ToggleCodeLensCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.ToggleCodeLens); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit) { | |||
return this.git.toggleCodeLens(editor); | |||
} | |||
} |
@ -0,0 +1,33 @@ | |||
'use strict' | |||
import {commands, Disposable, TextEditor, TextEditorEdit} from 'vscode'; | |||
import {Commands} from '../constants'; | |||
export abstract class Command extends Disposable { | |||
private _subscriptions: Disposable; | |||
constructor(command: Commands) { | |||
super(() => this.dispose()); | |||
this._subscriptions = commands.registerCommand(command, this.execute, this); | |||
} | |||
dispose() { | |||
this._subscriptions && this._subscriptions.dispose(); | |||
} | |||
abstract execute(...args): any; | |||
} | |||
export abstract class EditorCommand extends Disposable { | |||
private _subscriptions: Disposable; | |||
constructor(command: Commands) { | |||
super(() => this.dispose()); | |||
this._subscriptions = commands.registerTextEditorCommand(command, this.execute, this); | |||
} | |||
dispose() { | |||
this._subscriptions && this._subscriptions.dispose(); | |||
} | |||
abstract execute(editor: TextEditor, edit: TextEditorEdit, ...args): any; | |||
} |
@ -0,0 +1,51 @@ | |||
'use strict' | |||
import {commands, TextEditor, TextEditorEdit, Uri, window} from 'vscode'; | |||
import {EditorCommand} from './commands'; | |||
import {BuiltInCommands, Commands} from '../constants'; | |||
import GitProvider from '../gitProvider'; | |||
import * as path from 'path'; | |||
export default class DiffWithPreviousCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.DiffWithPrevious); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, repoPath?: string, sha?: string, shaUri?: Uri, compareWithSha?: string, compareWithUri?: Uri, line?: number) { | |||
line = line || editor.selection.active.line; | |||
if (!sha || GitProvider.isUncommitted(sha)) { | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, line) | |||
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', `getBlameForLine(${line})`, ex)) | |||
.then(blame => { | |||
if (!blame) return; | |||
// If the line is uncommitted, find the previous commit | |||
const commit = blame.commit; | |||
if (commit.isUncommitted) { | |||
return this.git.getBlameForLine(commit.previousUri.fsPath, blame.line.originalLine + 1, commit.previousSha, commit.repoPath) | |||
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', `getBlameForLine(${blame.line.originalLine}, ${commit.previousSha})`, ex)) | |||
.then(prevBlame => { | |||
if (!prevBlame) return; | |||
const prevCommit = prevBlame.commit; | |||
return commands.executeCommand(Commands.DiffWithPrevious, commit.previousUri, commit.repoPath, commit.previousSha, commit.previousUri, prevCommit.sha, prevCommit.uri, blame.line.originalLine); | |||
}); | |||
} | |||
return commands.executeCommand(Commands.DiffWithPrevious, commit.uri, commit.repoPath, commit.sha, commit.uri, commit.previousSha, commit.previousUri, line); | |||
}); | |||
} | |||
if (!compareWithSha) { | |||
return window.showInformationMessage(`Commit ${sha} has no previous commit`); | |||
} | |||
return Promise.all([this.git.getVersionedFile(shaUri.fsPath, repoPath, sha), this.git.getVersionedFile(compareWithUri.fsPath, repoPath, compareWithSha)]) | |||
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', 'getVersionedFile', ex)) | |||
.then(values => commands.executeCommand(BuiltInCommands.Diff, Uri.file(values[1]), Uri.file(values[0]), `${path.basename(compareWithUri.fsPath)} (${compareWithSha}) ↔ ${path.basename(shaUri.fsPath)} (${sha})`) | |||
.then(() => commands.executeCommand(BuiltInCommands.RevealLine, {lineNumber: line, at: 'center'}))); | |||
} | |||
} |
@ -0,0 +1,40 @@ | |||
'use strict' | |||
import {commands, TextEditor, TextEditorEdit, Uri, window} from 'vscode'; | |||
import {EditorCommand} from './commands'; | |||
import {BuiltInCommands, Commands} from '../constants'; | |||
import GitProvider from '../gitProvider'; | |||
import * as path from 'path'; | |||
export default class DiffWithWorkingCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.DiffWithWorking); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, repoPath?: string, sha?: string, shaUri?: Uri, line?: number) { | |||
line = line || editor.selection.active.line; | |||
if (!sha || GitProvider.isUncommitted(sha)) { | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, line) | |||
.catch(ex => console.error('[GitLens.DiffWithWorkingCommand]', `getBlameForLine(${line})`, ex)) | |||
.then(blame => { | |||
if (!blame) return; | |||
const commit = blame.commit; | |||
// If the line is uncommitted, find the previous commit | |||
if (commit.isUncommitted) { | |||
return commands.executeCommand(Commands.DiffWithWorking, commit.uri, commit.repoPath, commit.previousSha, commit.previousUri, blame.line.line + 1); | |||
} | |||
return commands.executeCommand(Commands.DiffWithWorking, commit.uri, commit.repoPath, commit.sha, commit.uri, line) | |||
}); | |||
}; | |||
return this.git.getVersionedFile(shaUri.fsPath, repoPath, sha) | |||
.catch(ex => console.error('[GitLens.DiffWithWorkingCommand]', 'getVersionedFile', ex)) | |||
.then(compare => commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), uri, `${path.basename(shaUri.fsPath)} (${sha}) ↔ ${path.basename(uri.fsPath)}`) | |||
.then(() => commands.executeCommand(BuiltInCommands.RevealLine, {lineNumber: line, at: 'center'}))); | |||
} | |||
} |
@ -0,0 +1,27 @@ | |||
'use strict' | |||
import {TextEditor, TextEditorEdit, Uri} from 'vscode'; | |||
import BlameAnnotationController from '../blameAnnotationController'; | |||
import {EditorCommand} from './commands'; | |||
import {Commands} from '../constants'; | |||
import GitProvider from '../gitProvider'; | |||
export default class ShowBlameCommand extends EditorCommand { | |||
constructor(private git: GitProvider, private annotationController: BlameAnnotationController) { | |||
super(Commands.ShowBlame); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string) { | |||
if (sha) { | |||
return this.annotationController.toggleBlameAnnotation(editor, sha); | |||
} | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, editor.selection.active.line) | |||
.catch(ex => console.error('[GitLens.ShowBlameCommand]', `getBlameForLine(${editor.selection.active.line})`, ex)) | |||
.then(blame => this.annotationController.showBlameAnnotation(editor, blame && blame.commit.sha)); | |||
} | |||
} |
@ -0,0 +1,26 @@ | |||
'use strict' | |||
import {commands, Position, Range, TextEditor, TextEditorEdit, Uri} from 'vscode'; | |||
import {EditorCommand} from './commands'; | |||
import {BuiltInCommands, Commands} from '../constants'; | |||
import GitProvider from '../gitProvider'; | |||
export default class ShowBlameHistoryCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.ShowBlameHistory); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, range?: Range, position?: Position) { | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
// If the command is executed manually -- treat it as a click on the root lens (i.e. show blame for the whole file) | |||
range = editor.document.validateRange(new Range(0, 0, 1000000, 1000000)); | |||
position = editor.document.validateRange(new Range(0, 0, 0, 1000000)).start; | |||
} | |||
return this.git.getBlameLocations(uri.fsPath, range) | |||
.catch(ex => console.error('[GitLens.ShowBlameHistoryCommand]', 'getBlameLocations', ex)) | |||
.then(locations => commands.executeCommand(BuiltInCommands.ShowReferences, uri, position, locations)); | |||
} | |||
} |
@ -0,0 +1,37 @@ | |||
'use strict' | |||
import {TextEditor, TextEditorEdit, Uri} from 'vscode'; | |||
import BlameAnnotationController from '../blameAnnotationController'; | |||
import {EditorCommand} from './commands'; | |||
import {Commands} from '../constants'; | |||
import GitProvider from '../gitProvider'; | |||
export default class ToggleBlameCommand extends EditorCommand { | |||
constructor(private git: GitProvider, private blameController: BlameAnnotationController) { | |||
super(Commands.ToggleBlame); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri, sha?: string) { | |||
if (sha) { | |||
return this.blameController.toggleBlameAnnotation(editor, sha); | |||
} | |||
if (!(uri instanceof Uri)) { | |||
if (!editor.document) return; | |||
uri = editor.document.uri; | |||
} | |||
return this.git.getBlameForLine(uri.fsPath, editor.selection.active.line) | |||
.catch(ex => console.error('[GitLens.ToggleBlameCommand]', `getBlameForLine(${editor.selection.active.line})`, ex)) | |||
.then(blame => this.blameController.toggleBlameAnnotation(editor, blame && blame.commit.sha)); | |||
} | |||
} | |||
export class ToggleCodeLensCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.ToggleCodeLens); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit) { | |||
return this.git.toggleCodeLens(editor); | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
'use strict' | |||
import {TextEditor, TextEditorEdit} from 'vscode'; | |||
import {EditorCommand} from './commands'; | |||
import {Commands} from '../constants'; | |||
import GitProvider from '../gitProvider'; | |||
export default class ToggleCodeLensCommand extends EditorCommand { | |||
constructor(private git: GitProvider) { | |||
super(Commands.ToggleCodeLens); | |||
} | |||
execute(editor: TextEditor, edit: TextEditorEdit) { | |||
return this.git.toggleCodeLens(editor); | |||
} | |||
} |