-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
140 lines (118 loc) · 3.53 KB
/
index.js
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
var _ = require('underscore');
_.str = require('underscore.string');
var fs = require('fs');
var path = require('path');
var findit = require('findit');
var fuzzy = require('fuzzy');
var util = require('./lib/util');
var claire = module.exports = {
defaultFilters: ['.git']
};
claire.getUnion = util.getUnion;
claire.expandPath = util.expandPath;
/*\
|*| Infers the search root experimentally.
\*/
function getRoot(term, opts) {
var parts = term.split(path.sep);
var root = parts.shift() || path.sep; // Windows support, I hope?
// Last section treated as term if not suffixed by path separator.
for(var i = 0; i < parts.length - 1; i++) {
var tryPath = path.join(root, parts[i]);
try {
var stat = fs.statSync(tryPath);
if(!stat.isDirectory()) {
break;
}
} catch (e) {
if(e.code === 'ENOENT') {
break;
}
throw e;
}
root = tryPath;
}
root = util.normalizePath(root);
return root;
}
claire.getRoot = getRoot;
/*\
|*| Calculates the relative search term based on the given root.
\*/
function getRelativeTerm(term, root) {
term = term.slice(root.length);
return _.str.trim(term, path.sep);
}
claire.getRelativeTerm = getRelativeTerm;
/*\
|*| Shortens directories of matches by rendering relative to the search root.
\*/
function shorten(term, match, opts) {
var matchOpts = {pre: opts.pre, post: opts.post};
var shared = util.getUnion(term, match.dir);
var cutoff = shared.lastIndexOf(path.sep) + 1;
match.dir = match.dir.slice(cutoff);
match.shared = shared.slice(0, cutoff);
var filepath = path.join(match.dir, match.file);
term = getRelativeTerm(term, match.shared);
var relativeMatch = fuzzy.match(term, filepath, matchOpts);
if(relativeMatch) {
match.rendered = relativeMatch.rendered;
}
}
/*\
|*| Finds and scores all matches for the given search term.
|*| opts:
|*| short:boolean = false // Render matches relative to search root instead of absolute.
|*| pre/post:any = undefined // Passed through to the fuzzy module, wraps matching characters when rendering matches.
\*/
function find(term, callback, opts) {
term = util.expandPath(term);
opts = opts || {};
var skipDirs = opts.filters || claire.defaultFilters;
var matches = [];
var root = getRoot(term, opts);
var finder = findit(root, {followSymlinks: true});
var matchOpts = {pre: opts.pre, post: opts.post};
var searchDepth = util.getPathDepth(getRelativeTerm(term, root));
finder.on('directory', function(dir, stat, stop) {
if(_.contains(skipDirs, path.basename(dir))) {
return stop();
}
var match = fuzzy.match(term, dir, matchOpts);
if(match) {
match.dir = dir;
match.file = '';
matches.push(match);
}
var relative = dir.slice(root.length);
var currentDepth = (relative) ? util.getPathDepth(relative) : 0;
if(currentDepth >= searchDepth) {
return stop();
}
});
finder.on('file', function(file, stat) {
var match = fuzzy.match(term, file, matchOpts);
if(!match) {
return;
}
match.dir = path.dirname(file);
match.file = path.basename(file);
matches.push(match);
});
finder.on('end', function(err) {
matches = _.sortBy(matches, function(match) {
return -match.score;
});
_.each(matches, function(match) {
match.dir = util.normalizePath(match.dir);
// Slice off shared path between search term and directory.
if(opts.short) {
shorten(term, match, opts);
}
});
callback(err, matches);
});
}
claire.find = find;
module.exports = claire;