Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,19 @@ git config split-diffs.min-line-width 40

This defaults to `80`, so screens below `160` characters will display unified diffs. Set it to `0` to always show split diffs.

### Custom theme directory

You can specify a custom directory for themes using the `theme-directory` setting. This supports path expansion for convenient configuration:

```
git config split-diffs.theme-directory ~/my/themes/split-diff
```

The following expansions are supported:
- `~` and `~/` expand to your home directory
- `$HOME`, `${HOME}`, `$USER`, and other environment variables are expanded
- Relative paths are resolved to absolute paths

## Themes

You can pick between several [themes](themes/):
Expand Down
101 changes: 101 additions & 0 deletions src/getGitConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,105 @@ split-diffs.theme-name=baz
THEME_NAME: 'baz',
});
});

describe('theme-directory path expansion', () => {
const originalEnv = process.env;

beforeEach(() => {
// Set up test environment variables
process.env = {
...originalEnv,
TEST_HOME: '/home/testuser',
TEST_USER: 'testuser',
TEST_CUSTOM: 'custom_value',
};
});

afterEach(() => {
// Restore original environment
process.env = originalEnv;
});

test('expands $TEST_HOME environment variable', () => {
const result = getGitConfig(`
split-diffs.theme-directory=$TEST_HOME/my/themes/split-diff
`);
expect(result.THEME_DIRECTORY).toBe('/home/testuser/my/themes/split-diff');
});

test('expands ${TEST_HOME} environment variable with braces', () => {
const result = getGitConfig(`
split-diffs.theme-directory=\${TEST_HOME}/my/themes/split-diff
`);
expect(result.THEME_DIRECTORY).toBe('/home/testuser/my/themes/split-diff');
});

test('expands $TEST_USER environment variable in path', () => {
const result = getGitConfig(`
split-diffs.theme-directory=/Users/$TEST_USER/my/themes/split-diff
`);
expect(result.THEME_DIRECTORY).toBe('/Users/testuser/my/themes/split-diff');
});

test('expands ${TEST_USER} environment variable with braces', () => {
const result = getGitConfig(`
split-diffs.theme-directory=/Users/\${TEST_USER}/my/themes/split-diff
`);
expect(result.THEME_DIRECTORY).toBe('/Users/testuser/my/themes/split-diff');
});

test('expands multiple environment variables', () => {
const result = getGitConfig(`
split-diffs.theme-directory=$TEST_HOME/\${TEST_USER}/themes/\${TEST_CUSTOM}
`);
expect(result.THEME_DIRECTORY).toBe('/home/testuser/testuser/themes/custom_value');
});

test('handles missing environment variables as empty string', () => {
const result = getGitConfig(`
split-diffs.theme-directory=$TEST_HOME/\${NONEXISTENT}/themes
`);
expect(result.THEME_DIRECTORY).toBe('/home/testuser/themes');
});

test('returns absolute path for relative input', () => {
const result = getGitConfig(`
split-diffs.theme-directory=./relative/path
`);
expect(result.THEME_DIRECTORY).toContain('/relative/path');
expect(result.THEME_DIRECTORY.startsWith('./')).toBe(false);
});

test('leaves absolute paths unchanged (except expansion)', () => {
const result = getGitConfig(`
split-diffs.theme-directory=/absolute/path
`);
expect(result.THEME_DIRECTORY).toBe('/absolute/path');
});

test('does not expand when theme-directory is not set', () => {
const result = getGitConfig(`
split-diffs.theme-name=arctic
`);
expect(result.THEME_DIRECTORY).toBe(DEFAULT_THEME_DIRECTORY);
});

test('expands ~ to actual home directory', () => {
const result = getGitConfig(`
split-diffs.theme-directory=~
`);
// Should expand to the actual home directory
expect(result.THEME_DIRECTORY).not.toBe('~');
expect(result.THEME_DIRECTORY).toBeTruthy();
});

test('expands ~/ to actual home directory with path', () => {
const result = getGitConfig(`
split-diffs.theme-directory=~/my/themes/split-diff
`);
// Should expand to the actual home directory + path
expect(result.THEME_DIRECTORY).not.toContain('~');
expect(result.THEME_DIRECTORY).toContain('/my/themes/split-diff');
});
});
});
26 changes: 24 additions & 2 deletions src/getGitConfig.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os from 'os';
import path from 'path';
import { fileURLToPath } from 'url';

Expand All @@ -23,6 +24,26 @@ const GIT_CONFIG_LINE_REGEX = new RegExp(
`${GIT_CONFIG_KEY_PREFIX}\\.([^=]+)=(.*)`
);

function expandPath(p: string): string {
let expanded = p;

// Expand ~ and ~/ to user home directory
if (expanded === '~' || expanded.startsWith('~/')) {
expanded = expanded.replace(/^~/, os.homedir());
}

// Expand $VAR and ${VAR} environment variables
expanded = expanded.replace(/\$\{([^}]+)\}/g, (_, varName) => {
return process.env[varName] || '';
});
expanded = expanded.replace(/\$([A-Z_][A-Z0-9_]*)/g, (_, varName) => {
return process.env[varName] || '';
});

// Return absolute path
return path.resolve(expanded);
}

function extractFromGitConfigString(configString: string) {
const rawConfig: Record<string, string> = {};
for (const line of configString.trim().split('\n')) {
Expand Down Expand Up @@ -53,8 +74,9 @@ export function getGitConfig(configString: string): GitConfig {
MIN_LINE_WIDTH: minLineWidth,
WRAP_LINES: rawConfig['wrap-lines'] !== 'false',
HIGHLIGHT_LINE_CHANGES: rawConfig['highlight-line-changes'] !== 'false',
THEME_DIRECTORY:
rawConfig['theme-directory'] ?? DEFAULT_THEME_DIRECTORY,
THEME_DIRECTORY: rawConfig['theme-directory']
? expandPath(rawConfig['theme-directory'])
: DEFAULT_THEME_DIRECTORY,
THEME_NAME: rawConfig['theme-name'] ?? DEFAULT_THEME_NAME,
SYNTAX_HIGHLIGHTING_THEME: rawConfig['syntax-highlighting-theme'],
};
Expand Down