Skip to content

Commit

Permalink
fix: refactor coverage gutter formatter (#585)
Browse files Browse the repository at this point in the history
e234ae6 feat: add back light theme status decorations
0c930ef refactor: use common functions from decorations
e916d74 fix: use prepared svg icon in coverage formatter
  • Loading branch information
Tymek committed Jun 30, 2020
1 parent e234ae6 commit 861314e
Show file tree
Hide file tree
Showing 16 changed files with 329 additions and 202 deletions.
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 5 additions & 9 deletions src/Coverage/Formatters/GutterFormatter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { AbstractFormatter } from '../AbstractFormatter';
import * as vscode from 'vscode';
import { FileCoverage } from 'istanbul-lib-coverage';
import { isValidLocation } from '../helpers';
import prepareIcon from '../../../decorations/prepareIcon';
import coverageGutterIcon from '../../../../icons/coverage.svg';

export interface CoverageLines {
covered: vscode.Range[];
Expand All @@ -23,28 +25,22 @@ export class GutterFormatter extends AbstractFormatter {
backgroundColor: '',
overviewRulerColor: 'rgba(121, 31, 10, 0.75)',
overviewRulerLane: vscode.OverviewRulerLane.Left,
gutterIconPath: context.asAbsolutePath(
'./src/Coverage/Formatters/GutterFormatter/uncovered-gutter-icon.svg'
),
gutterIconPath: prepareIcon(context, 'uncovered', coverageGutterIcon, '#791F0A'),
});

this.partiallyCoveredLine = vscode.window.createTextEditorDecorationType({
backgroundColor: 'rgba(121, 86, 10, 0.75)',
overviewRulerColor: 'rgba(121, 86, 10, 0.75)',
overviewRulerLane: vscode.OverviewRulerLane.Left,
gutterIconPath: context.asAbsolutePath(
'./src/Coverage/Formatters/GutterFormatter/partially-covered-gutter-icon.svg'
),
gutterIconPath: prepareIcon(context, 'partially-covered', coverageGutterIcon, '#79560A'),
});

this.coveredLine = vscode.window.createTextEditorDecorationType({
isWholeLine: true,
backgroundColor: '',
overviewRulerColor: '',
overviewRulerLane: vscode.OverviewRulerLane.Left,
gutterIconPath: context.asAbsolutePath(
'./src/Coverage/Formatters/GutterFormatter/covered-gutter-icon.svg'
),
gutterIconPath: prepareIcon(context, 'covered', coverageGutterIcon, '#2D790A'),
});
}

Expand Down

This file was deleted.

This file was deleted.

43 changes: 33 additions & 10 deletions src/Decorations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
DecorationRangeBehavior,
ExtensionContext,
TextEditorDecorationType,
DecorationRenderOptions,
} from 'vscode';
import passingIcon from 'vscode-codicons/src/icons/check.svg';
import failingIcon from 'vscode-codicons/src/icons/chrome-close.svg';
Expand All @@ -25,10 +26,13 @@ export class Decorations {
constructor(context) {
this.context = context;

this.passing = this.createStateDecoration(['passing', passingIcon, '#35A15E'], 'green');
this.failing = this.createStateDecoration(['failing', failingIcon, '#D6443C'], 'red');
this.skip = this.createStateDecoration(['skip', skipIcon, '#fed37f'], 'yellow');
this.unknown = this.createStateDecoration(['unknown', unknownIcon, '#8C8C8C'], 'darkgrey');
this.passing = this.createStateDecoration([['passing', passingIcon, '#35A15E'], 'green']);
this.failing = this.createStateDecoration([['failing', failingIcon, '#D6443C'], 'red']);
this.skip = this.createStateDecoration([['skip', skipIcon, '#fed37f'], 'yellow']);
this.unknown = this.createStateDecoration(
[['unknown', unknownIcon, '#BBBBBB'], 'darkgrey'],
[['unknown-light', unknownIcon, '#555555']]
);
}

private resolvePath(...args: string[]): string {
Expand All @@ -55,16 +59,35 @@ export class Decorations {
}

private createStateDecoration(
icon: Parameters<Decorations['prepareIcon']>,
overviewRulerColor: string
dark: /* default */ [Parameters<Decorations['prepareIcon']>, string?],
light?: /* optional overrides */ [Parameters<Decorations['prepareIcon']>, string?]
): TextEditorDecorationType {
return window.createTextEditorDecorationType({
overviewRulerColor,
gutterIconPath: this.prepareIcon(...icon),
const [iconOptions, overviewRulerColor] = dark;
const icon = this.prepareIcon(...iconOptions);

const options: DecorationRenderOptions = {
gutterIconPath: icon,
gutterIconSize: 'contain',
overviewRulerLane: OverviewRulerLane.Left,
rangeBehavior: DecorationRangeBehavior.ClosedClosed,
});
dark: {
gutterIconPath: icon,
},
light: {
gutterIconPath: light !== undefined ? this.prepareIcon(...light[0]) : icon,
},
};

if (overviewRulerColor) {
options['overviewRulerColor'] = overviewRulerColor;
options['dark']['overviewRulerColor'] = overviewRulerColor;
}

if (light !== undefined && light[1] !== undefined) {
options['light']['overviewRulerColor'] = light[1];
}

return window.createTextEditorDecorationType(options);
}

public failingAssertionStyle(text: string): TextEditorDecorationType {
Expand Down
96 changes: 96 additions & 0 deletions src/decorations/StateDecorations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
window,
OverviewRulerLane,
DecorationRangeBehavior,
ExtensionContext,
TextEditorDecorationType,
DecorationRenderOptions,
} from 'vscode';
import passingIcon from 'vscode-codicons/src/icons/check.svg';
import failingIcon from 'vscode-codicons/src/icons/chrome-close.svg';
import skipIcon from 'vscode-codicons/src/icons/debug-step-over.svg';
import unknownIcon from 'vscode-codicons/src/icons/question.svg';
import prepareIcon from './prepareIcon';

export class StateDecorations {
public passing: TextEditorDecorationType;
public failing: TextEditorDecorationType;
public skip: TextEditorDecorationType;
public unknown: TextEditorDecorationType;

constructor(context: ExtensionContext) {
this.passing = this.createStateDecoration([
prepareIcon(context, 'passing', passingIcon, '#35A15E'),
'green',
]);
this.failing = this.createStateDecoration([
prepareIcon(context, 'failing', failingIcon, '#D6443C'),
'red',
]);
this.skip = this.createStateDecoration([
prepareIcon(context, 'skip', skipIcon, '#fed37f'),
'yellow',
]);
this.unknown = this.createStateDecoration(
[prepareIcon(context, 'unknown', unknownIcon, '#BBBBBB'), 'darkgrey'],
[prepareIcon(context, 'unknown-light', unknownIcon, '#555555')]
);
}

private createStateDecoration(
dark: /* default */ [string, string?],
light?: /* optional overrides */ [string, string?]
): TextEditorDecorationType {
const [icon, overviewRulerColor] = dark;

const options: DecorationRenderOptions = {
gutterIconPath: icon,
gutterIconSize: 'contain',
overviewRulerLane: OverviewRulerLane.Left,
rangeBehavior: DecorationRangeBehavior.ClosedClosed,
dark: {
gutterIconPath: icon,
},
light: {
gutterIconPath: light !== undefined ? light[0] : icon,
},
};

if (overviewRulerColor) {
options['overviewRulerColor'] = overviewRulerColor;
options['dark']['overviewRulerColor'] = overviewRulerColor;
}

if (light !== undefined && light[1] !== undefined) {
options['light']['overviewRulerColor'] = light[1];
}

return window.createTextEditorDecorationType(options);
}

public failingAssertionStyle(text: string): TextEditorDecorationType {
return window.createTextEditorDecorationType({
isWholeLine: true,
overviewRulerColor: 'red',
overviewRulerLane: OverviewRulerLane.Left,
light: {
before: {
color: '#FF564B',
},
after: {
color: '#FF564B',
contentText: ' // ' + text,
},
},
dark: {
before: {
color: '#AD322D',
},
after: {
color: '#AD322D',
contentText: ' // ' + text,
},
},
});
}
}
32 changes: 32 additions & 0 deletions src/decorations/prepareIcon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as path from 'path';
import * as fs from 'fs';
import { ExtensionContext } from 'vscode';
const ICONS_PATH = path.join('out', 'icons');

export default function prepareIcon(
context: ExtensionContext,
state: string,
source: string,
color?: string
): string {
const resolvePath = (...args: string[]): string => {
return context.asAbsolutePath(path.join(...args));
};

const resultIconPath = resolvePath(ICONS_PATH, `${state}.svg`);
let result = source.toString();

if (color !== undefined) {
result = result.replace('fill="currentColor"', `fill="${color}"`);
}

if (!fs.existsSync(resultIconPath) || fs.readFileSync(resultIconPath).toString() !== result) {
if (!fs.existsSync(resolvePath(ICONS_PATH))) {
fs.mkdirSync(resolvePath(ICONS_PATH));
}

fs.writeFileSync(resultIconPath, result);
}

return resultIconPath;
}
1 change: 0 additions & 1 deletion src/icons/failing.svg

This file was deleted.

1 change: 0 additions & 1 deletion src/icons/passing.svg

This file was deleted.

1 change: 0 additions & 1 deletion src/icons/skip.svg

This file was deleted.

1 change: 0 additions & 1 deletion src/icons/unknown.svg

This file was deleted.

3 changes: 3 additions & 0 deletions tests/Coverage/Formatters/GutterFormatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jest.mock('vscode', () => {
},
};
});
jest.mock('../../../src/decorations/prepareIcon', () => ({
default: (icon) => icon,
}));

import { GutterFormatter } from '../../../src/Coverage/Formatters/GutterFormatter';
import * as vscode from 'vscode';
Expand Down
Loading

0 comments on commit 861314e

Please sign in to comment.