mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-01-14 01:25:43 -05:00
Fixes issues with annotation character settings
Fixes #29 - Commit info tooltip duplicated for current line when blame is enabled Improves performance of navigating line when active line annotations & statusbar blame are enabled
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
Provides Git CodeLens information (most recent commit, # of authors), on-demand inline blame annotations, status bar blame information, file and blame history explorers, and commands to compare changes with the working tree or previous versions.
|
||||
|
||||
---
|
||||
## Features
|
||||
|
||||
- Provides (optional) **CodeLens** on code blocks:
|
||||
|
||||
@@ -99,6 +99,13 @@ export default class BlameAnnotationController extends Disposable {
|
||||
return provider.provideBlameAnnotation(shaOrLine);
|
||||
}
|
||||
|
||||
isAnnotating(editor: TextEditor): boolean {
|
||||
if (!editor || !editor.document) return false;
|
||||
if (editor.viewColumn === undefined && !this.git.hasGitUriForFile(editor)) return false;
|
||||
|
||||
return !!this._annotationProviders.get(editor.viewColumn || -1);
|
||||
}
|
||||
|
||||
async toggleBlameAnnotation(editor: TextEditor, shaOrLine?: string | number): Promise<boolean> {
|
||||
if (!editor || !editor.document) return false;
|
||||
if (editor.viewColumn === undefined && !this.git.hasGitUriForFile(editor)) return false;
|
||||
|
||||
@@ -9,16 +9,22 @@ export const defaultRelativeDateLength = 13;
|
||||
export const defaultAuthorLength = 16;
|
||||
export const defaultMessageLength = 32;
|
||||
|
||||
export let cssEllipse = '\\2026';
|
||||
export let cssIndent = '\\2759';
|
||||
export let cssSeparator = '\\2022';
|
||||
export let cssPadding = '\\00a0';
|
||||
export let cssEllipse = '\\002026';
|
||||
export let cssIndent = '\\002759';
|
||||
export let cssSeparator = '\\002022';
|
||||
export let cssPadding = '\\0000a0';
|
||||
|
||||
let cssEllipseLength: number = 1;
|
||||
|
||||
const cssUnicodeMatcher = /\\[0-9a-fA-F]{1,6}/;
|
||||
|
||||
export function configureCssCharacters(config: IBlameConfig) {
|
||||
cssEllipse = config.annotation.characters.ellipse || cssEllipse;
|
||||
cssIndent = config.annotation.characters.indent || cssIndent;
|
||||
cssPadding = config.annotation.characters.padding || cssPadding;
|
||||
cssSeparator = config.annotation.characters.separator || cssSeparator;
|
||||
|
||||
cssEllipseLength = cssUnicodeMatcher.test(cssEllipse) ? 1 : cssEllipse.length;
|
||||
}
|
||||
|
||||
export enum BlameAnnotationFormat {
|
||||
@@ -35,10 +41,10 @@ export default class BlameAnnotationFormatter {
|
||||
if (format === BlameAnnotationFormat.Unconstrained) {
|
||||
const authorAndDate = this.getAuthorAndDate(config, commit, 'MMMM Do, YYYY h:MMa');
|
||||
if (config.annotation.sha) {
|
||||
message = `${sha}${(authorAndDate ? ` ${cssSeparator} ${authorAndDate}` : '')}${(message ? ` ${cssSeparator} ${message}` : '')}`;
|
||||
message = `${sha}${(authorAndDate ? `${cssPadding}${cssSeparator}${cssPadding} ${authorAndDate}` : '')}${(message ? `${cssPadding}${cssSeparator}${cssPadding} ${message}` : '')}`;
|
||||
}
|
||||
else if (config.annotation.author || config.annotation.date) {
|
||||
message = `${authorAndDate}${(message ? ` ${cssSeparator} ${message}` : '')}`;
|
||||
message = `${authorAndDate}${(message ? `${cssPadding}${cssSeparator}${cssPadding} ${message}` : '')}`;
|
||||
}
|
||||
|
||||
return message;
|
||||
@@ -47,13 +53,13 @@ export default class BlameAnnotationFormatter {
|
||||
const author = this.getAuthor(config, commit, defaultAuthorLength);
|
||||
const date = this.getDate(config, commit, 'MM/DD/YYYY', true);
|
||||
if (config.annotation.sha) {
|
||||
message = `${sha}${(author ? ` ${cssSeparator} ${author}` : '')}${(date ? ` ${cssSeparator} ${date}` : '')}${(message ? ` ${cssSeparator} ${message}` : '')}`;
|
||||
message = `${sha}${(author ? `${cssPadding}${cssSeparator}${cssPadding} ${author}` : '')}${(date ? `${cssPadding}${cssSeparator}${cssPadding} ${date}` : '')}${(message ? `${cssPadding}${cssSeparator}${cssPadding} ${message}` : '')}`;
|
||||
}
|
||||
else if (config.annotation.author) {
|
||||
message = `${author}${(date ? ` ${cssSeparator} ${date}` : '')}${(message ? ` ${cssSeparator} ${message}` : '')}`;
|
||||
message = `${author}${(date ? `${cssPadding}${cssSeparator}${cssPadding} ${date}` : '')}${(message ? `${cssPadding}${cssSeparator}${cssPadding} ${message}` : '')}`;
|
||||
}
|
||||
else if (config.annotation.date) {
|
||||
message = `${date}${(message ? ` ${cssSeparator} ${message}` : '')}`;
|
||||
message = `${date}${(message ? `${cssPadding}${cssSeparator}${cssPadding} ${message}` : '')}`;
|
||||
}
|
||||
|
||||
return message;
|
||||
@@ -62,7 +68,7 @@ export default class BlameAnnotationFormatter {
|
||||
static getAnnotationHover(config: IBlameConfig, line: IGitCommitLine, commit: GitCommit): string | Array<string> {
|
||||
const message = commit.message.replace(/\n/g, '\n\n');
|
||||
if (commit.isUncommitted) {
|
||||
return `\`${'0'.repeat(8)}\` __Uncommitted changes__ \n\n > ${message}`;
|
||||
return `\`${'0'.repeat(8)}\` __Uncommitted changes__`;
|
||||
}
|
||||
|
||||
return `\`${commit.sha}\` __${commit.author}__, ${moment(commit.date).fromNow()} _(${moment(commit.date).format('MMMM Do, YYYY h:MMa')})_ \n\n > ${message}`;
|
||||
@@ -85,11 +91,11 @@ export default class BlameAnnotationFormatter {
|
||||
static getAuthor(config: IBlameConfig, commit: GitCommit, truncateTo: number = 0, force: boolean = false) {
|
||||
if (!force && !config.annotation.author) return '';
|
||||
|
||||
const author = commit.isUncommitted ? 'Uncommitted' : commit.author;
|
||||
const author = commit.isUncommitted ? 'Uncommited' : commit.author;
|
||||
if (!truncateTo) return author;
|
||||
|
||||
if (author.length > truncateTo) {
|
||||
return `${author.substring(0, truncateTo - cssEllipse.length)}${cssEllipse}`;
|
||||
return `${author.substring(0, truncateTo - cssEllipseLength)}${cssEllipse}`;
|
||||
}
|
||||
|
||||
if (force) return author; // Don't pad when just asking for the value
|
||||
@@ -106,7 +112,7 @@ export default class BlameAnnotationFormatter {
|
||||
|
||||
const truncateTo = config.annotation.date === 'relative' ? defaultRelativeDateLength : defaultAbsoluteDateLength;
|
||||
if (date.length > truncateTo) {
|
||||
return `${date.substring(0, truncateTo - cssEllipse.length)}${cssEllipse}`;
|
||||
return `${date.substring(0, truncateTo - cssEllipseLength)}${cssEllipse}`;
|
||||
}
|
||||
|
||||
if (force) return date; // Don't pad when just asking for the value
|
||||
@@ -116,9 +122,9 @@ export default class BlameAnnotationFormatter {
|
||||
static getMessage(config: IBlameConfig, commit: GitCommit, truncateTo: number = 0, force: boolean = false) {
|
||||
if (!force && !config.annotation.message) return '';
|
||||
|
||||
let message = commit.message;
|
||||
let message = commit.isUncommitted ? 'Uncommited change' : commit.message;
|
||||
if (truncateTo && message.length > truncateTo) {
|
||||
return `${message.substring(0, truncateTo - cssEllipse.length)}${cssEllipse}`;
|
||||
return `${message.substring(0, truncateTo - cssEllipseLength)}${cssEllipse}`;
|
||||
}
|
||||
|
||||
return message;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
import { Objects } from './system';
|
||||
import { Functions, Objects } from './system';
|
||||
import { DecorationOptions, DecorationInstanceRenderOptions, DecorationRenderOptions, Disposable, ExtensionContext, Range, StatusBarAlignment, StatusBarItem, TextDocumentChangeEvent, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode';
|
||||
import BlameAnnotationController from './blameAnnotationController';
|
||||
import BlameAnnotationFormatter, { BlameAnnotationFormat } from './blameAnnotationFormatter';
|
||||
import { TextDocumentComparer, TextEditorComparer } from './comparers';
|
||||
import { IBlameConfig, IConfig, StatusBarCommand } from './configuration';
|
||||
@@ -19,15 +20,19 @@ export default class BlameStatusBarController extends Disposable {
|
||||
private _activeEditorLineDisposable: Disposable | undefined;
|
||||
private _blame: Promise<IGitBlame> | undefined;
|
||||
private _config: IConfig;
|
||||
private _currentLine: number = -1;
|
||||
private _disposable: Disposable;
|
||||
private _editor: TextEditor | undefined;
|
||||
private _showBlameDebounced: (line: number, editor: TextEditor) => Promise<void>;
|
||||
private _statusBarItem: StatusBarItem | undefined;
|
||||
private _uri: GitUri;
|
||||
private _useCaching: boolean;
|
||||
|
||||
constructor(context: ExtensionContext, private git: GitProvider) {
|
||||
constructor(context: ExtensionContext, private git: GitProvider, private annotationController: BlameAnnotationController) {
|
||||
super(() => this.dispose());
|
||||
|
||||
this._showBlameDebounced = Functions.debounce(this._showBlame, 50);
|
||||
|
||||
this._onConfigure();
|
||||
|
||||
const subscriptions: Disposable[] = [];
|
||||
@@ -104,7 +109,9 @@ export default class BlameStatusBarController extends Disposable {
|
||||
this._onActiveTextEditorChanged(window.activeTextEditor);
|
||||
}
|
||||
|
||||
private async _onActiveTextEditorChanged(e: TextEditor): Promise<void> {
|
||||
private _onActiveTextEditorChanged(e: TextEditor) {
|
||||
this._currentLine = -1;
|
||||
|
||||
const previousEditor = this._editor;
|
||||
previousEditor && previousEditor.setDecorations(activeLineDecoration, []);
|
||||
|
||||
@@ -129,21 +136,26 @@ export default class BlameStatusBarController extends Disposable {
|
||||
this._blame = undefined;
|
||||
}
|
||||
|
||||
return await this._showBlame(e.selection.active.line, e);
|
||||
this._showBlame(e.selection.active.line, e);
|
||||
}
|
||||
|
||||
private async _onEditorSelectionChanged(e: TextEditorSelectionChangeEvent): Promise<void> {
|
||||
private _onEditorSelectionChanged(e: TextEditorSelectionChangeEvent): void {
|
||||
// Make sure this is for the editor we are tracking
|
||||
if (!TextEditorComparer.equals(e.textEditor, this._editor)) return;
|
||||
|
||||
return await this._showBlame(e.selections[0].active.line, e.textEditor);
|
||||
const line = e.selections[0].active.line;
|
||||
if (line === this._currentLine) return;
|
||||
this._currentLine = line;
|
||||
|
||||
this._showBlameDebounced(line, e.textEditor);
|
||||
}
|
||||
|
||||
private async _onDocumentChanged(e: TextDocumentChangeEvent): Promise<void> {
|
||||
private _onDocumentChanged(e: TextDocumentChangeEvent) {
|
||||
// Make sure this is for the editor we are tracking
|
||||
if (!this._editor || !TextDocumentComparer.equals(e.document, this._editor.document)) return;
|
||||
this._currentLine = -1;
|
||||
|
||||
return await this._showBlame(this._editor.selections[0].active.line, this._editor);
|
||||
this._showBlame(this._editor.selections[0].active.line, this._editor);
|
||||
}
|
||||
|
||||
private async _showBlame(line: number, editor: TextEditor) {
|
||||
@@ -248,11 +260,21 @@ export default class BlameStatusBarController extends Disposable {
|
||||
const log = await this.git.getLogForFile(this._uri.fsPath, commit.sha, this._uri.repoPath, undefined, 1);
|
||||
logCommit = log && log.commits.get(commit.sha);
|
||||
}
|
||||
const hoverMessage = BlameAnnotationFormatter.getAnnotationHover(config, blameLine, logCommit || commit);
|
||||
|
||||
let hoverMessage: string | string[];
|
||||
if (activeLine !== 'inline') {
|
||||
// If the messages match (or we couldn't find the log), then this is a possible duplicate annotation
|
||||
const possibleDuplicate = !logCommit || logCommit.message === commit.message;
|
||||
// If we don't have a possible dupe or we aren't showing annotations get the hover message
|
||||
if (!possibleDuplicate || !this.annotationController.isAnnotating(editor)) {
|
||||
hoverMessage = BlameAnnotationFormatter.getAnnotationHover(config, blameLine, logCommit || commit);
|
||||
}
|
||||
}
|
||||
|
||||
let decorationOptions: DecorationOptions;
|
||||
switch (activeLine) {
|
||||
case 'both':
|
||||
case 'inline':
|
||||
decorationOptions = {
|
||||
range: editor.document.validateRange(new Range(blameLine.line + offset, 0, blameLine.line + offset, 1000000)),
|
||||
hoverMessage: hoverMessage,
|
||||
@@ -265,18 +287,6 @@ export default class BlameStatusBarController extends Disposable {
|
||||
} as DecorationOptions;
|
||||
break;
|
||||
|
||||
case 'inline':
|
||||
decorationOptions = {
|
||||
range: editor.document.validateRange(new Range(blameLine.line + offset, 1000000, blameLine.line + offset, 1000000)),
|
||||
renderOptions: {
|
||||
after: {
|
||||
color: 'rgba(153, 153, 153, 0.3)',
|
||||
contentText: annotation
|
||||
}
|
||||
} as DecorationInstanceRenderOptions
|
||||
} as DecorationOptions;
|
||||
break;
|
||||
|
||||
case 'hover':
|
||||
decorationOptions = {
|
||||
range: editor.document.validateRange(new Range(blameLine.line + offset, 0, blameLine.line + offset, 1000000)),
|
||||
|
||||
@@ -62,7 +62,7 @@ export async function activate(context: ExtensionContext) {
|
||||
const annotationController = new BlameAnnotationController(context, git);
|
||||
context.subscriptions.push(annotationController);
|
||||
|
||||
const statusBarController = new BlameStatusBarController(context, git);
|
||||
const statusBarController = new BlameStatusBarController(context, git, annotationController);
|
||||
context.subscriptions.push(statusBarController);
|
||||
|
||||
context.subscriptions.push(new DiffWithWorkingCommand(git));
|
||||
|
||||
@@ -4,11 +4,11 @@ const _once = require('lodash.once');
|
||||
|
||||
export interface IDeferred {
|
||||
cancel(): void;
|
||||
flush(): void;
|
||||
flush(...args: any[]): void;
|
||||
}
|
||||
|
||||
export namespace Functions {
|
||||
export function debounce<T extends Function>(fn: T, wait?: number, options?: any): T & IDeferred {
|
||||
export function debounce<T extends Function>(fn: T, wait?: number, options?: { leading?: boolean, maxWait?: number, trailing?: boolean }): T & IDeferred {
|
||||
return _debounce(fn, wait, options);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user