-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfuzzyfinder.vim
246 lines (207 loc) · 7.29 KB
/
fuzzyfinder.vim
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
" Homebrewed fuzzy finder in Qt
" =============================
"
" A fuzzy finder that runs outside the Vim process, thus not succeptible to
" slow syntax highlighting. This could be a curses-based application, or a
" graphical one (in GTK or Qt or something else). A graphical application
" suits better for use with GVim. Qt was choosen because:
"
" 1. I could do it from Python, and I feel confortable programming in this
" language, which is a lot better than VimL.
" 2. I could use QtWebKit, and hence HTML, CSS and JavaScript to build the
" UI, in which, again, I feel a lot more confortable than in VimL.
" 3. I like Qt, mainly because of KDE, and wanted to explore it.
"
" Despite the fact that it's done in Qt, it also works in console Vim, provided
" it runs inside an emulated terminal (a graphical server is needed).
"
" Additionally, to avoid re-importing Qt modules everytime a file or a buffer
" needs to be opened, a simple process is used, which only sends to the
" daemonized process which has the Qt stuff the data it needs (the list of
" files/buffers to select from, and some options). This is specially useful in
" low memory condition, when the kernel otherwise would have to make room in
" RAM for lots of python bindings by swapping stuff to disk everytime the menu
" were launched. A daemon constantly used (the portion which has the heavy Qt
" stuff) may take advantage of kernel heuristics and be kept in memory most of
" the time, avoiding hitting disk/swap to present the menu. The process which
" communicates with the daemon is lighter (it just does UNIX socket stuff), and
" should not cause anoying delays when launched.
let g:fuzzy = {}
function! s:select(dirname, cmd, accept_input, ...)
let initial_info = a:0 ? a:1 : ''
" resolve() mangles fugitive:// paths.
if match(a:dirname, '^fugitive:\/\/') == 0
let dirname = a:dirname
else
let dirname = resolve(a:dirname)
endif
if empty(initial_info)
let info = s:project_root_info(dirname)
else
let info = initial_info
endif
if g:utils.looks_like_gitroot(info.toplevel)
let title = ':'.a:cmd.' file from Git repo at ' . info.toplevel
else
let title = ':'.a:cmd.' from ' . info.toplevel
endif
let choice = g:utils.menucmd({
\ 'limit': 20,
\ 'input': info.initial,
\ 'history_key': 'file:' . info.toplevel,
\ 'word_delimiters': '/',
\ 'completion_sep': '/',
\ 'accept_input': a:accept_input,
\ 'title': title
\ }).pipe_from(info.filescmd).output()
if empty(choice)
return
endif
let resolved = resolve(info.toplevel . '/' . choice)
if isdirectory(resolved)
return s:select(resolved, a:cmd, a:accept_input, initial_info)
endif
return resolved
endfunction
function! g:fuzzy.open(command, dirname)
let accept_input = a:command !=# 'read'
let choice = s:select(a:dirname, a:command, accept_input)
if empty(choice)
return
endif
if accept_input
let choice = g:utils.makepath(choice)
endif
execute a:command . ' ' . choice
endfunction
function! g:fuzzy.open_from_branch()
let filename = expand('%:p')
let root = g:utils.gitroot(filename)
if empty(root)
return
endif
let branch = g:gitcommand.select_branch(root)
if empty(branch)
return
endif
let entriescmd = g:utils.fish('git ls-tree -r --name-only ' . branch, {
\ 'cwd': root,
\ 'cmd': 1
\ })
let fname = g:utils.menucmd({
\ 'limit': 20,
\ 'input': g:utils.relativepath(root, filename),
\ 'word_delimiters': '/',
\ 'completion_sep': '/',
\ 'title': 'Select file from Git branch ' . branch
\ }).pipe_from(entriescmd).output()
if empty(fname)
return
endif
execute 'Gedit ' . branch . ':' . fname
endfunction
function! g:fuzzy.reopen(cmd)
let entriescmd = 'grep -v " a- " ' .
\ ' | grep "."' .
\ ' | sed "s/^.*\"\([^\"]*\)\".*\$/\\1/"'
let fname = g:utils.menucmd({
\ 'limit': 100,
\ 'input': bufname("%"),
\ 'word_delimiters': '/',
\ 'completion_sep': '/',
\ 'accept_input': 1,
\ 'title': ':'.a:cmd.' buffer'
\ }).pipe_from(entriescmd).input(execute('silent ls')).output()
if empty(fname)
return
endif
execute a:cmd . ' ' . g:utils.makepath(fname)
endfunction
function! g:fuzzy.openold(cmd)
let entriescmd = 'grep "."' .
\ ' | grep -v "term://"' .
\ ' | grep -v ".git/"' .
\ ' | grep -v "/tmp/nvim"' .
\ ' | cut -d":" -f2- | sed "s/^\s\+//"'
let fname = g:utils.menucmd({
\ 'limit': 100,
\ 'word_delimiters': '/',
\ 'completion_sep': '/',
\ 'title': ':'.a:cmd.' old file'
\ }).pipe_from(entriescmd).input(execute('silent oldfiles')).output()
if empty(fname)
return
endif
execute a:cmd . " " . fname
endfunction
function! s:project_root_info(dirname)
let dirname = substitute(a:dirname, '\.git\/\/', '.git/', '')
let toplevel = g:utils.project_root(dirname)
let filescmd = g:utils.project_files_cmd(dirname)
if match(dirname, '^fugitive:\/\/') == 0
let initial = split(dirname, '\/\.git\/\?[a-fA-F0-9]\{40\}\/')[-1]
else
let initial = g:utils.relativepath(toplevel, dirname)
end
if !empty(initial)
let initial = initial . '/'
endif
return {
\ 'initial': initial,
\ 'toplevel': toplevel,
\ 'filescmd': filescmd
\ }
endfunction
function! s:gather_dirs(root, maxdepth, ...)
let root = substitute(resolve(a:root), '/$', '', '')
if a:0 != 0
let options = a:1
if !has_key(options, 'self')
let options.self = 1
endif
if !has_key(options, 'parent')
let options.parent = 1
endif
else
let options = { 'self': 1, 'parent': 1 }
endif
let firstentries = []
if options.parent
let firstentries = add(firstentries, '../')
endif
if options.self
let firstentries = add(firstentries, './')
endif
let entriescmd = 'cd ' . shellescape(root) . '; and find -L . '
if a:maxdepth >= 0
let entriescmd = entriescmd . '-maxdepth ' . a:maxdepth . ' '
endif
let finalcmd = entriescmd .
\ '-type d \! -empty \( ' .
\ '\( ' .
\ '-path "./.wine" ' .
\ '-o -path "./wine-diii" ' .
\ '-o -path "./.cache" ' .
\ '-o -path "./**/.git" ' .
\ '-o -path "./**/.hg" ' .
\ '-o -path "./**/__pycache__" ' .
\ '-o -path "./**/*.egg-info" ' .
\ '-o -path "./**/node_modules" ' .
\ '-o -path "./**/bower_components" ' .
\ '-o -path "./**/parts/omelette" ' .
\ '\) ' .
\ '-prune -o -print \) ' .
\ '2>/dev/null | ' .
\ 'sed "/^\.\$/d" |' .
\ 'sort -u | ' .
\ 'sed "s/^\.\///" | sed "s/\$/\//"'
if !empty(firstentries)
call map(firstentries, '"(echo \"" . v:val . "\" | psub)"')
let finalcmd = 'cat ' . join(firstentries, ' ') . ' (' . finalcmd . ' | psub)'
endif
return finalcmd
endfunction
function! s:getparent(dirname)
py import os.path, vim
return '/' . pyeval("os.path.join(*vim.eval('a:dirname').split(os.path.sep)[:-1])")
endfunction