-
-
Notifications
You must be signed in to change notification settings - Fork 55
/
j
executable file
·111 lines (90 loc) · 2.71 KB
/
j
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/usr/bin/env node
// Magic project opener: `cd` to a folder using fuzzy search. Looks at `~/\_/`, work projects, and several iCloud Drive folders.
// > Note: the first letter of the query should match, the rest is fuzzy-matched.
//
// - Navigate to a project directory:
//
// `j {{partial_name}}`
//
// # Optional environmental variables
//
// - `WORK_PROJECTS_DIR="~/unicorn"`: specify an additional folder to look in.
//
// ---
// NOTE: Should be used via Bash alias like this:
// function j { cd "$("$HOME/dotfiles/bin/j" $1)"; }
//
// Author: Artem Sapegin, sapegin.me
// License: MIT
// https://github.com/sapegin/dotfiles
import path from 'path';
import fs from 'fs';
import untildify from 'untildify';
import dotenv from 'dotenv';
const HOME = path.dirname(path.dirname(import.meta.dirname));
dotenv.config({ path: `${HOME}/.env` });
const PERSONAL_PROJECTS_DIR = `${HOME}/_`;
const WORK_PROJECTS_DIR = untildify(process.env.WORK_PROJECTS_DIR);
const EXTRA_PROJECTS = [HOME, ['dotfiles']];
const CLOUD_PROJECTS = [
{
label: 'Apple iCloud Docs',
dir: `${HOME}/Library/Mobile Documents/com~apple~CloudDocs`,
},
{
label: 'Cloud Writings',
dir: `${HOME}/Library/Mobile Documents/com~apple~CloudDocs/Writings`,
},
];
const getDirs = (d) =>
fs.readdirSync(d).filter((f) => fs.statSync(path.join(d, f)).isDirectory());
const tryRepo = (dir, query, dirs) => {
const repo = find(dirs ?? getDirs(dir), query);
if (repo) {
console.log(path.join(dir, repo));
process.exit(0);
}
};
const tryCloud = (projects, query) => {
// Fuzzy search, don't require the first letter to match
const nameRegexp = new RegExp(`${fuzzyfy(query)}`, 'i');
for (const { label, dir } of projects) {
if (nameRegexp.test(label)) {
console.log(dir);
process.exit(0);
}
}
};
function fuzzyfy(name) {
return name
.replace(/[^a-z0-9]/gi, '')
.split('')
.join('.*');
}
// TODO: Don't require fist letter to match but give such repos larger weight
function find(repos, name) {
// Fuzzy search but require the first letter to match
const nameRegexp = new RegExp(`^${fuzzyfy(name)}`, 'i');
repos = repos.filter((r) => nameRegexp.test(r));
// Order by repo name length, shortest names first
repos.sort((a, b) => (a.length > b.length ? 1 : -1));
// Return the most probable result
return repos?.[0];
}
const args = process.argv.splice(2);
if (args.length !== 1) {
console.log('Usage: j <name>');
process.exit(1);
}
const query = args[0];
// Cloud projects
tryCloud(CLOUD_PROJECTS, query);
// Personal projects
tryRepo(PERSONAL_PROJECTS_DIR, query);
// Work projects
if (WORK_PROJECTS_DIR) {
tryRepo(WORK_PROJECTS_DIR, query);
}
// Extra projects
tryRepo(EXTRA_PROJECTS[0], query, EXTRA_PROJECTS[1]);
console.log('Repository not found');