Skip to content

Commit fe74aff

Browse files
committed
Refactor config
Mainly an attempt to clear up what is configurable via git and what gets loaded separately.
1 parent 3bea4c7 commit fe74aff

8 files changed

Lines changed: 203 additions & 101 deletions

File tree

src/config.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/context.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
11
import * as shikiji from 'shikiji';
2-
import { Config } from './config';
2+
import { Config } from './getConfig';
33
import { FormattedString, T } from './formattedString';
4+
import { ChalkInstance } from 'chalk';
45

56
/**
67
* Internal context object used to pass around config and config-derived
78
* constants.
89
*/
910
export type Context = Config & {
11+
CHALK: ChalkInstance;
1012
SPLIT_DIFFS: boolean;
13+
SCREEN_WIDTH: number;
1114
LINE_WIDTH: number;
1215
BLANK_LINE: string;
1316
HORIZONTAL_SEPARATOR: FormattedString;
1417
HIGHLIGHTER?: shikiji.Highlighter;
1518
};
1619

17-
export async function getContextForConfig(config: Config): Promise<Context> {
20+
export async function getContextForConfig(
21+
config: Config,
22+
chalk: ChalkInstance,
23+
screenWidth: number
24+
): Promise<Context> {
25+
const SCREEN_WIDTH = screenWidth;
26+
1827
// Only split diffs if there's enough room
19-
const SPLIT_DIFFS = config.SCREEN_WIDTH >= config.MIN_LINE_WIDTH * 2;
28+
const SPLIT_DIFFS = SCREEN_WIDTH >= config.MIN_LINE_WIDTH * 2;
2029

2130
let LINE_WIDTH: number;
2231
if (SPLIT_DIFFS) {
23-
LINE_WIDTH = Math.floor(config.SCREEN_WIDTH / 2);
32+
LINE_WIDTH = Math.floor(SCREEN_WIDTH / 2);
2433
} else {
25-
LINE_WIDTH = config.SCREEN_WIDTH;
34+
LINE_WIDTH = SCREEN_WIDTH;
2635
}
2736

2837
const BLANK_LINE = ''.padStart(LINE_WIDTH);
2938
const HORIZONTAL_SEPARATOR = T()
30-
.fillWidth(config.SCREEN_WIDTH, '─')
31-
.addSpan(0, config.SCREEN_WIDTH, config.BORDER_COLOR);
39+
.fillWidth(SCREEN_WIDTH, '─')
40+
.addSpan(0, SCREEN_WIDTH, config.BORDER_COLOR);
3241

3342
let HIGHLIGHTER = undefined;
3443
if (config.SYNTAX_HIGHLIGHTING_THEME) {
@@ -39,6 +48,8 @@ export async function getContextForConfig(config: Config): Promise<Context> {
3948
}
4049
return {
4150
...config,
51+
CHALK: chalk,
52+
SCREEN_WIDTH,
4253
SPLIT_DIFFS,
4354
LINE_WIDTH,
4455
BLANK_LINE,

src/getConfig.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { GitConfig } from './getGitConfig';
2+
import { Theme, loadTheme } from './themes';
3+
import * as shikiji from 'shikiji';
4+
5+
export type Config = Theme & {
6+
MIN_LINE_WIDTH: number;
7+
WRAP_LINES: boolean;
8+
HIGHLIGHT_LINE_CHANGES: boolean;
9+
};
10+
11+
export const DEFAULT_THEME_NAME = 'dark';
12+
13+
export const CONFIG_DEFAULTS: Omit<Config, keyof Theme> = {
14+
MIN_LINE_WIDTH: 80,
15+
WRAP_LINES: true,
16+
HIGHLIGHT_LINE_CHANGES: true,
17+
};
18+
19+
export function getConfig(gitConfig: GitConfig): Config {
20+
const theme = loadTheme(gitConfig.THEME_NAME ?? DEFAULT_THEME_NAME);
21+
22+
return {
23+
...CONFIG_DEFAULTS,
24+
...theme,
25+
...gitConfig,
26+
SYNTAX_HIGHLIGHTING_THEME: (theme.SYNTAX_HIGHLIGHTING_THEME ??
27+
gitConfig.SYNTAX_HIGHLIGHTING_THEME) as shikiji.BundledTheme,
28+
};
29+
}

src/getGitConfig.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { DEFAULT_THEME_NAME } from './getConfig';
2+
import {
3+
DEFAULT_MIN_LINE_WIDTH,
4+
GitConfig,
5+
getGitConfig,
6+
} from './getGitConfig';
7+
8+
const DEFAULT_CONFIG: GitConfig = {
9+
WRAP_LINES: true,
10+
HIGHLIGHT_LINE_CHANGES: true,
11+
MIN_LINE_WIDTH: DEFAULT_MIN_LINE_WIDTH,
12+
THEME_NAME: DEFAULT_THEME_NAME,
13+
};
14+
15+
describe('getGitConfig', () => {
16+
test('empty', () => {
17+
expect(getGitConfig('')).toEqual(DEFAULT_CONFIG);
18+
});
19+
20+
test('full', () => {
21+
expect(
22+
getGitConfig(`
23+
split-diffs.wrap-lines=false
24+
split-diffs.highlight-line-changes=false
25+
split-diffs.min-line-width=40
26+
split-diffs.theme-name=arctic
27+
split-diffs.syntax-highlighting-theme=dark-plus
28+
`)
29+
).toEqual({
30+
WRAP_LINES: false,
31+
HIGHLIGHT_LINE_CHANGES: false,
32+
MIN_LINE_WIDTH: 40,
33+
THEME_NAME: 'arctic',
34+
SYNTAX_HIGHLIGHTING_THEME: 'dark-plus',
35+
});
36+
});
37+
38+
test('partial', () => {
39+
expect(
40+
getGitConfig(`
41+
split-diffs.wrap-lines=true
42+
split-diffs.syntax-highlighting-theme=nord
43+
`)
44+
).toEqual({
45+
...DEFAULT_CONFIG,
46+
WRAP_LINES: true,
47+
SYNTAX_HIGHLIGHTING_THEME: 'nord',
48+
});
49+
});
50+
51+
test('invalid values', () => {
52+
expect(
53+
getGitConfig(`
54+
split-diffs.wrap-lines=1
55+
split-diffs.highlight-line-changes=1
56+
split-diffs.min-line-width=bar
57+
split-diffs.syntax-highlighting-theme=foo
58+
split-diffs.theme-name=baz
59+
`)
60+
).toEqual({
61+
...DEFAULT_CONFIG,
62+
SYNTAX_HIGHLIGHTING_THEME: 'foo',
63+
THEME_NAME: 'baz',
64+
});
65+
});
66+
});

src/getGitConfig.ts

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
import { ChalkInstance } from 'chalk';
2-
import { exec } from 'child_process';
3-
import * as util from 'util';
4-
import { Config } from './config';
5-
import { loadTheme } from './themes';
6-
import * as shikiji from 'shikiji';
7-
const execAsync = util.promisify(exec);
1+
export type GitConfig = {
2+
MIN_LINE_WIDTH: number;
3+
WRAP_LINES: boolean;
4+
HIGHLIGHT_LINE_CHANGES: boolean;
5+
THEME_NAME: string;
6+
SYNTAX_HIGHLIGHTING_THEME?: string;
7+
};
8+
9+
export const DEFAULT_MIN_LINE_WIDTH = 80;
10+
export const DEFAULT_THEME_NAME = 'dark';
811

912
const GIT_CONFIG_KEY_PREFIX = 'split-diffs';
1013
const GIT_CONFIG_LINE_REGEX = new RegExp(
1114
`${GIT_CONFIG_KEY_PREFIX}\.([^=]+)=(.*)`
1215
);
1316

14-
async function getRawGitConfig() {
15-
const { stdout } = await execAsync('git config -l');
16-
17+
function extractFromGitConfigString(configString: string) {
1718
const rawConfig: Record<string, string> = {};
18-
for (const line of stdout.trim().split('\n')) {
19+
for (const line of configString.trim().split('\n')) {
1920
const match = line.match(GIT_CONFIG_LINE_REGEX);
2021
if (!match) {
2122
continue;
@@ -26,30 +27,10 @@ async function getRawGitConfig() {
2627
return rawConfig;
2728
}
2829

29-
// TODO: Make this less manual
30-
export async function getGitConfig(
31-
screenWidth: number,
32-
chalk: ChalkInstance
33-
): Promise<Config> {
34-
const rawConfig = await getRawGitConfig();
35-
36-
// Defaults to "dark"
37-
const themeName = rawConfig['theme-name'] ?? 'dark';
38-
const theme = loadTheme(themeName);
39-
40-
// Defaults to the theme's setting
41-
const syntaxHighlightingTheme = (rawConfig['syntax-highlighting-theme'] ??
42-
theme.SYNTAX_HIGHLIGHTING_THEME) as shikiji.BundledTheme;
43-
44-
// Defaults to true
45-
const wrapLines = rawConfig['wrap-lines'] === 'false' ? false : true;
46-
47-
// Defaults to true
48-
const highlightLineChanges =
49-
rawConfig['highlight-line-changes'] === 'false' ? false : true;
30+
export function getGitConfig(configString: string): GitConfig {
31+
const rawConfig = extractFromGitConfigString(configString);
5032

51-
// Defaults to 80
52-
let minLineWidth = 80;
33+
let minLineWidth = DEFAULT_MIN_LINE_WIDTH;
5334
try {
5435
const parsedMinLineWidth = parseInt(rawConfig['min-line-width'], 10);
5536
if (!isNaN(parsedMinLineWidth)) {
@@ -58,12 +39,10 @@ export async function getGitConfig(
5839
} catch {}
5940

6041
return {
61-
...theme,
62-
CHALK: chalk,
63-
SCREEN_WIDTH: screenWidth,
6442
MIN_LINE_WIDTH: minLineWidth,
65-
WRAP_LINES: wrapLines,
66-
HIGHLIGHT_LINE_CHANGES: highlightLineChanges,
67-
SYNTAX_HIGHLIGHTING_THEME: syntaxHighlightingTheme,
43+
WRAP_LINES: rawConfig['wrap-lines'] !== 'false',
44+
HIGHLIGHT_LINE_CHANGES: rawConfig['highlight-line-changes'] !== 'false',
45+
THEME_NAME: rawConfig['theme-name'] ?? DEFAULT_THEME_NAME,
46+
SYNTAX_HIGHLIGHTING_THEME: rawConfig['syntax-highlighting-theme'],
6847
};
6948
}

src/index.test.ts

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,95 @@
11
import { Readable, Writable } from 'stream';
2-
import { Config } from './config';
2+
import { Config } from './getConfig';
33
import { getContextForConfig } from './context';
4-
import { ThemeColorName } from './themes';
4+
import { Theme, ThemeColorName } from './themes';
55
import { transformContentsStreaming } from './transformContentsStreaming';
6+
import { ChalkInstance } from 'chalk';
67

78
const TEST_THEME = Object.fromEntries(
89
Object.keys(ThemeColorName).map((name) => [name, {}])
9-
);
10+
) as Theme;
1011

1112
const replaceColoredText = () => (text: string) => text.replace(/./g, '░');
1213

14+
// Provide a fake chalk implementation to make it easier to read snapshots
15+
// @ts-expect-error
16+
const TEST_CHALK = {
17+
rgb: replaceColoredText,
18+
bgRgb: replaceColoredText,
19+
} as ChalkInstance;
20+
1321
const TEST_CONFIG: Config = {
14-
// Provide a fake chalk implementation to make it easier to read snapshots
15-
CHALK: {
16-
// @ts-expect-error
17-
rgb: replaceColoredText,
18-
// @ts-expect-error
19-
bgRgb: replaceColoredText,
20-
},
21-
SCREEN_WIDTH: 120,
2222
MIN_LINE_WIDTH: 60,
2323
WRAP_LINES: false,
2424
HIGHLIGHT_LINE_CHANGES: false,
2525
...TEST_THEME,
2626
};
2727

28-
const CONFIG_OVERRIDES: Record<string, Partial<Config>> = {
28+
type TestOverrides = { screenWidth: number; config: Partial<Config> };
29+
30+
const CONFIG_OVERRIDES: Record<string, TestOverrides> = {
2931
splitWithoutWrapping: {
30-
SCREEN_WIDTH: 80,
31-
MIN_LINE_WIDTH: 40,
32-
WRAP_LINES: false,
32+
screenWidth: 80,
33+
config: {
34+
MIN_LINE_WIDTH: 40,
35+
WRAP_LINES: false,
36+
},
3337
},
3438
splitWithWrapping: {
35-
SCREEN_WIDTH: 80,
36-
MIN_LINE_WIDTH: 40,
37-
WRAP_LINES: true,
39+
screenWidth: 80,
40+
config: {
41+
MIN_LINE_WIDTH: 40,
42+
WRAP_LINES: true,
43+
},
3844
},
3945
unifiedWithWrapping: {
40-
SCREEN_WIDTH: 80,
41-
MIN_LINE_WIDTH: 80,
42-
WRAP_LINES: true,
46+
screenWidth: 80,
47+
config: {
48+
MIN_LINE_WIDTH: 80,
49+
WRAP_LINES: true,
50+
},
4351
},
4452
// This is in split mode
4553
inlineChangesHighlighted: {
46-
HIGHLIGHT_LINE_CHANGES: true,
47-
DELETED_WORD_COLOR: { color: { r: 255, g: 0, b: 0, a: 255 } },
48-
INSERTED_WORD_COLOR: { color: { r: 0, g: 255, b: 0, a: 255 } },
54+
screenWidth: 120,
55+
config: {
56+
HIGHLIGHT_LINE_CHANGES: true,
57+
DELETED_WORD_COLOR: { color: { r: 255, g: 0, b: 0, a: 255 } },
58+
INSERTED_WORD_COLOR: { color: { r: 0, g: 255, b: 0, a: 255 } },
59+
},
4960
},
5061
unifiedWithInlineChangesHighlighted: {
51-
SCREEN_WIDTH: 80,
52-
MIN_LINE_WIDTH: 80,
53-
HIGHLIGHT_LINE_CHANGES: true,
54-
DELETED_WORD_COLOR: { color: { r: 255, g: 0, b: 0, a: 255 } },
55-
INSERTED_WORD_COLOR: { color: { r: 0, g: 255, b: 0, a: 255 } },
62+
screenWidth: 80,
63+
config: {
64+
MIN_LINE_WIDTH: 80,
65+
HIGHLIGHT_LINE_CHANGES: true,
66+
DELETED_WORD_COLOR: { color: { r: 255, g: 0, b: 0, a: 255 } },
67+
INSERTED_WORD_COLOR: { color: { r: 0, g: 255, b: 0, a: 255 } },
68+
},
5669
},
5770
syntaxHighlighted: {
58-
SCREEN_WIDTH: 80,
59-
MIN_LINE_WIDTH: 40,
60-
WRAP_LINES: false,
61-
SYNTAX_HIGHLIGHTING_THEME: 'dark-plus',
71+
screenWidth: 80,
72+
config: {
73+
MIN_LINE_WIDTH: 40,
74+
WRAP_LINES: false,
75+
SYNTAX_HIGHLIGHTING_THEME: 'dark-plus',
76+
},
6277
},
6378
};
6479

65-
for (const [configName, configOverride] of Object.entries(CONFIG_OVERRIDES)) {
80+
for (const [configName, { screenWidth, config }] of Object.entries(
81+
CONFIG_OVERRIDES
82+
)) {
6683
async function transform(input: string): Promise<string> {
6784
const testConfig: Config = {
6885
...TEST_CONFIG,
69-
...configOverride,
86+
...config,
7087
};
71-
const context = await getContextForConfig(testConfig);
88+
const context = await getContextForConfig(
89+
testConfig,
90+
TEST_CHALK,
91+
screenWidth
92+
);
7293

7394
let string = '';
7495
await transformContentsStreaming(

0 commit comments

Comments
 (0)