Refactors formatters

Moves annotation messages from formatter to annotations
Moves icons into dark/light folders
This commit is contained in:
Eric Amodio
2017-06-13 00:12:31 -04:00
parent 12debe583c
commit a2903ce4a9
10 changed files with 181 additions and 165 deletions

View File

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 815 B

View File

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 312 B

View File

Before

Width:  |  Height:  |  Size: 814 B

After

Width:  |  Height:  |  Size: 814 B

View File

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 313 B

View File

@@ -794,8 +794,8 @@
"title": "Toggle File Blame Annotations",
"category": "GitLens",
"icon": {
"dark": "images/git-icon-dark.svg",
"light": "images/git-icon-light.svg"
"dark": "images/dark/git-icon.svg",
"light": "images/light/git-icon.svg"
}
},
{

View File

@@ -113,7 +113,7 @@ export class AnnotationController extends Disposable {
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
: undefined,
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
? this.context.asAbsolutePath('images/blame-dark.svg')
? this.context.asAbsolutePath('images/dark/highlight-gutter.svg')
: undefined,
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
@@ -124,7 +124,7 @@ export class AnnotationController extends Disposable {
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
: undefined,
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
? this.context.asAbsolutePath('images/blame-light.svg')
? this.context.asAbsolutePath('images/light/highlight-gutter.svg')
: undefined,
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
@@ -147,7 +147,7 @@ export class AnnotationController extends Disposable {
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
: undefined,
gutterIconPath: cfgChangesHighlight.locations.includes(LineHighlightLocations.Gutter)
? this.context.asAbsolutePath('images/blame-dark.svg')
? this.context.asAbsolutePath('images/dark/highlight-gutter.svg')
: undefined,
overviewRulerColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
@@ -158,7 +158,7 @@ export class AnnotationController extends Disposable {
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
: undefined,
gutterIconPath: cfgChangesHighlight.locations.includes(LineHighlightLocations.Gutter)
? this.context.asAbsolutePath('images/blame-light.svg')
? this.context.asAbsolutePath('images/light/highlight-gutter.svg')
: undefined,
overviewRulerColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor

View File

@@ -1,6 +1,6 @@
import { DecorationInstanceRenderOptions, DecorationOptions, ThemableDecorationRenderOptions } from 'vscode';
import { IThemeConfig, themeDefaults } from '../configuration';
import { CommitFormatter, GitCommit, GitService, GitUri, ICommitFormatOptions } from '../gitService';
import { CommitFormatter, GitCommit, GitDiffLine, GitService, GitUri, ICommitFormatOptions } from '../gitService';
import * as moment from 'moment';
interface IHeatmapConfig {
@@ -20,6 +20,8 @@ interface IRenderOptions {
}
export const endOfLineIndex = 1000000;
const escapeMarkdownRegEx = /[`\>\#\*\_\-\+\.]/g;
// const sampleMarkdown = '## message `not code` *not important* _no underline_ \n> don\'t quote me \n- don\'t list me \n+ don\'t list me \n1. don\'t list me \nnot h1 \n=== \nnot h2 \n---\n***\n---\n___';
export class Annotations {
@@ -43,15 +45,50 @@ export class Annotations {
return '#793738';
}
static getHoverMessage(commit: GitCommit, dateFormat: string | null): string | string[] {
if (dateFormat === null) {
dateFormat = 'MMMM Do, YYYY h:MMa';
}
let message = '';
if (!commit.isUncommitted) {
message = commit.message
// Escape markdown
.replace(escapeMarkdownRegEx, '\\$&')
// Escape markdown header (since the above regex won't match it)
.replace(/^===/gm, '\u200b===')
// Keep under the same block-quote
.replace(/\n/g, ' \n');
message = `\n\n> ${message}`;
}
return `\`${commit.shortSha}\`   __${commit.author}__, ${moment(commit.date).fromNow()}   _(${moment(commit.date).format(dateFormat)})_${message}`;
}
static getHoverDiffMessage(commit: GitCommit, previous: GitDiffLine | undefined, current: GitDiffLine | undefined): string | undefined {
if (previous === undefined && current === undefined) return undefined;
const codeDiff = this._getCodeDiff(previous, current);
return commit.isUncommitted
? `\`Changes\`   \u2014   _uncommitted_\n${codeDiff}`
: `\`Changes\`   \u2014   \`${commit.previousShortSha}\` \u2194 \`${commit.shortSha}\`\n${codeDiff}`;
}
private static _getCodeDiff(previous: GitDiffLine | undefined, current: GitDiffLine | undefined): string {
return `\`\`\`
- ${previous === undefined ? '' : previous.line.trim()}
+ ${current === undefined ? '' : current.line.trim()}
\`\`\``;
}
static async changesHover(commit: GitCommit, line: number, uri: GitUri, git: GitService): Promise<DecorationOptions> {
let message: string | undefined = undefined;
if (commit.isUncommitted) {
const [previous, current] = await git.getDiffForLine(uri, line + uri.offset);
message = CommitFormatter.toHoverDiff(commit, previous, current);
message = this.getHoverDiffMessage(commit, previous, current);
}
else if (commit.previousSha !== undefined) {
const [previous, current] = await git.getDiffForLine(uri, line + uri.offset, commit.previousSha);
message = CommitFormatter.toHoverDiff(commit, previous, current);
message = this.getHoverDiffMessage(commit, previous, current);
}
return {
@@ -60,7 +97,7 @@ export class Annotations {
}
static detailsHover(commit: GitCommit, dateFormat: string | null): DecorationOptions {
const message = CommitFormatter.toHoverAnnotation(commit, dateFormat);
const message = this.getHoverMessage(commit, dateFormat);
return {
hoverMessage: message
} as DecorationOptions;
@@ -129,7 +166,7 @@ export class Annotations {
static hover(commit: GitCommit, renderOptions: IRenderOptions, heatmap: boolean, dateFormat: string | null): DecorationOptions {
return {
hoverMessage: CommitFormatter.toHoverAnnotation(commit, dateFormat),
hoverMessage: this.getHoverMessage(commit, dateFormat),
renderOptions: heatmap ? { before: { ...renderOptions.before } } : undefined
} as DecorationOptions;
}

View File

@@ -1,9 +1,9 @@
'use strict';
import { DecorationOptions, ExtensionContext, Position, Range, TextEditor, TextEditorDecorationType } from 'vscode';
import { endOfLineIndex } from './annotations';
import { Annotations, endOfLineIndex } from './annotations';
import { FileAnnotationType } from './annotationController';
import { AnnotationProviderBase } from './annotationProvider';
import { CommitFormatter, GitService, GitUri } from '../gitService';
import { GitService, GitUri } from '../gitService';
export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
@@ -43,14 +43,14 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
if (cfg.hover.details) {
decorators.push({
hoverMessage: CommitFormatter.toHoverAnnotation(commit, dateFormat),
hoverMessage: Annotations.getHoverMessage(commit, dateFormat),
range: range
} as DecorationOptions);
}
let message: string | undefined = undefined;
if (cfg.hover.changes) {
message = CommitFormatter.toHoverDiff(commit, chunk.previous[count], change);
message = Annotations.getHoverDiffMessage(commit, chunk.previous[count], change);
}
decorators.push({

View File

@@ -1,14 +1,10 @@
'use strict';
import { Strings } from '../../system';
import { GitCommit } from '../models/commit';
import { GitDiffLine } from '../models/diff';
import { Formatter, IFormatOptions } from './formatter';
import * as moment from 'moment';
const escapeMarkdownRegEx = /[`\>\#\*\_\-\+\.]/g;
// const sampleMarkdown = '## message `not code` *not important* _no underline_ \n> don\'t quote me \n- don\'t list me \n+ don\'t list me \n1. don\'t list me \nnot h1 \n=== \nnot h2 \n---\n***\n---\n___';
export interface ICommitFormatOptions {
dateFormat?: string | null;
export interface ICommitFormatOptions extends IFormatOptions {
tokenOptions?: {
ago?: Strings.ITokenOptions;
author?: Strings.ITokenOptions;
@@ -18,58 +14,34 @@ export interface ICommitFormatOptions {
};
}
export class CommitFormatter {
private _commit: GitCommit;
private _options: ICommitFormatOptions;
constructor(commit: GitCommit, options?: ICommitFormatOptions) {
this.reset(commit, options);
}
reset(commit: GitCommit, options?: ICommitFormatOptions) {
this._commit = commit;
if (options === undefined && this._options !== undefined) return;
options = options || {};
if (options.tokenOptions == null) {
options.tokenOptions = {};
}
if (options.dateFormat == null) {
options.dateFormat = 'MMMM Do, YYYY h:MMa';
}
this._options = options;
}
export class CommitFormatter extends Formatter<GitCommit, ICommitFormatOptions> {
get ago() {
const ago = moment(this._commit.date).fromNow();
const ago = moment(this._item.date).fromNow();
return this._padOrTruncate(ago, this._options.tokenOptions!.ago);
}
get author() {
const author = this._commit.author;
const author = this._item.author;
return this._padOrTruncate(author, this._options.tokenOptions!.author);
}
get authorAgo() {
const authorAgo = `${this._commit.author}, ${moment(this._commit.date).fromNow()}`;
const authorAgo = `${this._item.author}, ${moment(this._item.date).fromNow()}`;
return this._padOrTruncate(authorAgo, this._options.tokenOptions!.authorAgo);
}
get date() {
const date = moment(this._commit.date).format(this._options.dateFormat!);
const date = moment(this._item.date).format(this._options.dateFormat!);
return this._padOrTruncate(date, this._options.tokenOptions!.date);
}
get id() {
return this._commit.shortSha;
return this._item.shortSha;
}
get message() {
const message = this._commit.isUncommitted ? 'Uncommitted change' : this._commit.message;
const message = this._item.isUncommitted ? 'Uncommitted change' : this._item.message;
return this._padOrTruncate(message, this._options.tokenOptions!.message);
}
@@ -77,122 +49,10 @@ export class CommitFormatter {
return this.id;
}
private collapsableWhitespace: number = 0;
private _padOrTruncate(s: string, options: Strings.ITokenOptions | undefined) {
// NOTE: the collapsable whitespace logic relies on the javascript template evaluation to be left to right
if (options === undefined) {
options = {
truncateTo: undefined,
padDirection: 'left',
collapseWhitespace: false
};
}
let max = options.truncateTo;
if (max === undefined) {
if (this.collapsableWhitespace === 0) return s;
// If we have left over whitespace make sure it gets re-added
const diff = this.collapsableWhitespace - s.length;
this.collapsableWhitespace = 0;
if (diff <= 0) return s;
if (options.truncateTo === undefined) return s;
return Strings.padLeft(s, diff);
}
max += this.collapsableWhitespace;
this.collapsableWhitespace = 0;
const diff = max - s.length;
if (diff > 0) {
if (options.collapseWhitespace) {
this.collapsableWhitespace = diff;
}
if (options.padDirection === 'left') return Strings.padLeft(s, max);
if (options.collapseWhitespace) {
max -= diff;
}
return Strings.padRight(s, max);
}
if (diff < 0) return Strings.truncate(s, max);
return s;
}
private static _formatter: CommitFormatter | undefined = undefined;
static fromCommit(commit: GitCommit, options?: ICommitFormatOptions): CommitFormatter {
if (CommitFormatter._formatter === undefined) {
CommitFormatter._formatter = new CommitFormatter(commit, options);
}
else {
CommitFormatter._formatter.reset(commit, options);
}
return CommitFormatter._formatter;
}
static fromTemplate(template: string, commit: GitCommit, dateFormat: string | null): string;
static fromTemplate(template: string, commit: GitCommit, options?: ICommitFormatOptions): string;
static fromTemplate(template: string, commit: GitCommit, dateFormatOrOptions?: string | null | ICommitFormatOptions): string;
static fromTemplate(template: string, commit: GitCommit, dateFormatOrOptions?: string | null | ICommitFormatOptions): string {
let options: ICommitFormatOptions | undefined = undefined;
if (dateFormatOrOptions == null || typeof dateFormatOrOptions === 'string') {
const tokenOptions = Strings.getTokensFromTemplate(template)
.reduce((map, token) => {
map[token.key] = token.options;
return map;
}, {} as { [token: string]: ICommitFormatOptions });
options = {
dateFormat: dateFormatOrOptions,
tokenOptions: tokenOptions
};
}
else {
options = dateFormatOrOptions;
}
return Strings.interpolate(template, new CommitFormatter(commit, options));
}
static toHoverAnnotation(commit: GitCommit, dateFormat: string | null): string | string[] {
if (dateFormat === null) {
dateFormat = 'MMMM Do, YYYY h:MMa';
}
let message = '';
if (!commit.isUncommitted) {
message = commit.message
// Escape markdown
.replace(escapeMarkdownRegEx, '\\$&')
// Escape markdown header (since the above regex won't match it)
.replace(/^===/gm, '\u200b===')
// Keep under the same block-quote
.replace(/\n/g, ' \n');
message = `\n\n> ${message}`;
}
return `\`${commit.shortSha}\` &nbsp; __${commit.author}__, ${moment(commit.date).fromNow()} &nbsp; _(${moment(commit.date).format(dateFormat)})_${message}`;
}
static toHoverDiff(commit: GitCommit, previous: GitDiffLine | undefined, current: GitDiffLine | undefined): string | undefined {
if (previous === undefined && current === undefined) return undefined;
const codeDiff = this._getCodeDiff(previous, current);
return commit.isUncommitted
? `\`Changes\` &nbsp; \u2014 &nbsp; _uncommitted_\n${codeDiff}`
: `\`Changes\` &nbsp; \u2014 &nbsp; \`${commit.previousShortSha}\` \u2194 \`${commit.shortSha}\`\n${codeDiff}`;
}
private static _getCodeDiff(previous: GitDiffLine | undefined, current: GitDiffLine | undefined): string {
return `\`\`\`
- ${previous === undefined ? '' : previous.line.trim()}
+ ${current === undefined ? '' : current.line.trim()}
\`\`\``;
return super.fromTemplateCore(this, template, commit, dateFormatOrOptions);
}
}

View File

@@ -0,0 +1,119 @@
'use strict';
import { Strings } from '../../system';
export interface IFormatOptions {
dateFormat?: string | null;
tokenOptions?: { [id: string]: Strings.ITokenOptions | undefined };
}
type Constructor<T = {}> = new (...args: any[]) => T;
export abstract class Formatter<TItem = any, TOptions extends IFormatOptions = IFormatOptions> {
protected _item: TItem;
protected _options: TOptions;
constructor(item: TItem, options?: TOptions) {
this.reset(item, options);
}
reset(item: TItem, options?: TOptions) {
this._item = item;
if (options === undefined && this._options !== undefined) return;
if (options === undefined) {
options = {} as TOptions;
}
if (options.dateFormat == null) {
options.dateFormat = 'MMMM Do, YYYY h:MMa';
}
if (options.tokenOptions == null) {
options.tokenOptions = {};
}
this._options = options;
}
private collapsableWhitespace: number = 0;
protected _padOrTruncate(s: string, options: Strings.ITokenOptions | undefined) {
// NOTE: the collapsable whitespace logic relies on the javascript template evaluation to be left to right
if (options === undefined) {
options = {
truncateTo: undefined,
padDirection: 'left',
collapseWhitespace: false
};
}
let max = options.truncateTo;
if (max === undefined) {
if (this.collapsableWhitespace === 0) return s;
// If we have left over whitespace make sure it gets re-added
const diff = this.collapsableWhitespace - s.length;
this.collapsableWhitespace = 0;
if (diff <= 0) return s;
if (options.truncateTo === undefined) return s;
return Strings.padLeft(s, diff);
}
max += this.collapsableWhitespace;
this.collapsableWhitespace = 0;
const diff = max - s.length;
if (diff > 0) {
if (options.collapseWhitespace) {
this.collapsableWhitespace = diff;
}
if (options.padDirection === 'left') return Strings.padLeft(s, max);
if (options.collapseWhitespace) {
max -= diff;
}
return Strings.padRight(s, max);
}
if (diff < 0) return Strings.truncate(s, max);
return s;
}
private static _formatter: Formatter | undefined = undefined;
protected static fromTemplateCore<TFormatter extends Formatter<TItem, TOptions>, TItem, TOptions extends IFormatOptions>(formatter: TFormatter | Constructor<TFormatter>, template: string, item: TItem, dateFormatOrOptions?: string | null | TOptions): string {
if (formatter instanceof Formatter) return Strings.interpolate(template, formatter);
let options: TOptions | undefined = undefined;
if (dateFormatOrOptions == null || typeof dateFormatOrOptions === 'string') {
const tokenOptions = Strings.getTokensFromTemplate(template)
.reduce((map, token) => {
map[token.key] = token.options;
return map;
}, {} as { [token: string]: Strings.ITokenOptions | undefined });
options = {
dateFormat: dateFormatOrOptions,
tokenOptions: tokenOptions
} as TOptions;
}
else {
options = dateFormatOrOptions;
}
if (this._formatter === undefined) {
this._formatter = new formatter(item, options);
}
else {
this._formatter.reset(item, options);
}
return Strings.interpolate(template, this._formatter);
}
}