command-t-standalone 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ // Copyright 2010 Wincent Colaiuta. All rights reserved.
2
+ //
3
+ // Redistribution and use in source and binary forms, with or without
4
+ // modification, are permitted provided that the following conditions are met:
5
+ //
6
+ // 1. Redistributions of source code must retain the above copyright notice,
7
+ // this list of conditions and the following disclaimer.
8
+ // 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ // this list of conditions and the following disclaimer in the documentation
10
+ // and/or other materials provided with the distribution.
11
+ //
12
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ // POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ #include <ruby.h>
25
+
26
+ extern VALUE CommandTMatch_initialize(int argc, VALUE *argv, VALUE self);
27
+ extern VALUE CommandTMatch_matches(VALUE self);
28
+ extern VALUE CommandTMatch_score(VALUE self);
29
+ extern VALUE CommandTMatch_to_s(VALUE self);
@@ -0,0 +1,445 @@
1
+ # Copyright 2010-2012 Wincent Colaiuta. All rights reserved.
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice,
7
+ # this list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ #
12
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ # POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ require 'ostruct'
25
+ require 'command-t/settings'
26
+
27
+ module CommandT
28
+ class MatchWindow
29
+ SELECTION_MARKER = '> '
30
+ MARKER_LENGTH = SELECTION_MARKER.length
31
+ UNSELECTED_MARKER = ' ' * MARKER_LENGTH
32
+ MH_START = '<commandt>'
33
+ MH_END = '</commandt>'
34
+ @@buffer = nil
35
+
36
+ def initialize options = {}
37
+ @prompt = options[:prompt]
38
+ @reverse_list = options[:match_window_reverse]
39
+ @min_height = options[:min_height]
40
+
41
+ # save existing window dimensions so we can restore them later
42
+ @windows = []
43
+ (0..(::VIM::Window.count - 1)).each do |i|
44
+ @windows << OpenStruct.new(:index => i,
45
+ :height => ::VIM::Window[i].height,
46
+ :width => ::VIM::Window[i].width)
47
+ end
48
+
49
+ # global settings (must manually save and restore)
50
+ @settings = Settings.new
51
+ ::VIM::set_option 'timeout' # ensure mappings timeout
52
+ ::VIM::set_option 'timeoutlen=0' # respond immediately to mappings
53
+ ::VIM::set_option 'nohlsearch' # don't highlight search strings
54
+ ::VIM::set_option 'noinsertmode' # don't make Insert mode the default
55
+ ::VIM::set_option 'noshowcmd' # don't show command info on last line
56
+ ::VIM::set_option 'report=9999' # don't show "X lines changed" reports
57
+ ::VIM::set_option 'sidescroll=0' # don't sidescroll in jumps
58
+ ::VIM::set_option 'sidescrolloff=0' # don't sidescroll automatically
59
+ ::VIM::set_option 'noequalalways' # don't auto-balance window sizes
60
+
61
+ # show match window
62
+ split_location = options[:match_window_at_top] ? 'topleft' : 'botright'
63
+ if @@buffer # still have buffer from last time
64
+ ::VIM::command "silent! #{split_location} #{@@buffer.number}sbuffer"
65
+ raise "Can't re-open GoToFile buffer" unless $curbuf.number == @@buffer.number
66
+ $curwin.height = 1
67
+ else # creating match window for first time and set it up
68
+ split_command = "silent! #{split_location} 1split GoToFile"
69
+ [
70
+ split_command,
71
+ 'setlocal bufhidden=unload', # unload buf when no longer displayed
72
+ 'setlocal buftype=nofile', # buffer is not related to any file
73
+ 'setlocal nomodifiable', # prevent manual edits
74
+ 'setlocal noswapfile', # don't create a swapfile
75
+ 'setlocal nowrap', # don't soft-wrap
76
+ 'setlocal nonumber', # don't show line numbers
77
+ 'setlocal nolist', # don't use List mode (visible tabs etc)
78
+ 'setlocal foldcolumn=0', # don't show a fold column at side
79
+ 'setlocal foldlevel=99', # don't fold anything
80
+ 'setlocal nocursorline', # don't highlight line cursor is on
81
+ 'setlocal nospell', # spell-checking off
82
+ 'setlocal nobuflisted', # don't show up in the buffer list
83
+ 'setlocal textwidth=0' # don't hard-wrap (break long lines)
84
+ ].each { |command| ::VIM::command command }
85
+
86
+ # don't show the color column
87
+ ::VIM::command 'setlocal colorcolumn=0' if VIM::exists?('+colorcolumn')
88
+
89
+ # don't show relative line numbers
90
+ ::VIM::command 'setlocal norelativenumber' if VIM::exists?('+relativenumber')
91
+
92
+ # sanity check: make sure the buffer really was created
93
+ raise "Can't find GoToFile buffer" unless $curbuf.name.match /GoToFile\z/
94
+ @@buffer = $curbuf
95
+ end
96
+
97
+ # syntax coloring
98
+ if VIM::has_syntax?
99
+ ::VIM::command "syntax match CommandTSelection \"^#{SELECTION_MARKER}.\\+$\""
100
+ ::VIM::command 'syntax match CommandTNoEntries "^-- NO MATCHES --$"'
101
+ ::VIM::command 'syntax match CommandTNoEntries "^-- NO SUCH FILE OR DIRECTORY --$"'
102
+ ::VIM::command 'setlocal synmaxcol=9999'
103
+
104
+ if VIM::has_conceal?
105
+ ::VIM::command 'setlocal conceallevel=2'
106
+ ::VIM::command 'setlocal concealcursor=nvic'
107
+ ::VIM::command 'syntax region CommandTCharMatched ' \
108
+ "matchgroup=CommandTCharMatched start=+#{MH_START}+ " \
109
+ "matchgroup=CommandTCharMatchedEnd end=+#{MH_END}+ concealends"
110
+ ::VIM::command 'highlight def CommandTCharMatched ' \
111
+ 'term=bold,underline cterm=bold,underline ' \
112
+ 'gui=bold,underline'
113
+ end
114
+
115
+ ::VIM::command 'highlight link CommandTSelection Visual'
116
+ ::VIM::command 'highlight link CommandTNoEntries Error'
117
+ ::VIM::evaluate 'clearmatches()'
118
+
119
+ # hide cursor
120
+ @cursor_highlight = get_cursor_highlight
121
+ hide_cursor
122
+ end
123
+
124
+ # perform cleanup using an autocmd to ensure we don't get caught out
125
+ # by some unexpected means of dismissing or leaving the Command-T window
126
+ # (eg. <C-W q>, <C-W k> etc)
127
+ ::VIM::command 'autocmd! * <buffer>'
128
+ ::VIM::command 'autocmd BufLeave <buffer> silent! ruby $command_t.leave'
129
+ ::VIM::command 'autocmd BufUnload <buffer> silent! ruby $command_t.unload'
130
+
131
+ @has_focus = false
132
+ @selection = nil
133
+ @abbrev = ''
134
+ @window = $curwin
135
+ end
136
+
137
+ def close
138
+ # Unlisted buffers like those provided by Netrw, NERDTree and Vim's help
139
+ # don't actually appear in the buffer list; if they are the only such
140
+ # buffers present when Command-T is invoked (for example, when invoked
141
+ # immediately after starting Vim with a directory argument, like `vim .`)
142
+ # then performing the normal clean-up will yield an "E90: Cannot unload
143
+ # last buffer" error. We can work around that by doing a :quit first.
144
+ if ::VIM::Buffer.count == 0
145
+ ::VIM::command 'silent quit'
146
+ end
147
+
148
+ # Workaround for upstream bug in Vim 7.3 on some platforms
149
+ #
150
+ # On some platforms, $curbuf.number always returns 0. One workaround is
151
+ # to build Vim with --disable-largefile, but as this is producing lots of
152
+ # support requests, implement the following fallback to the buffer name
153
+ # instead, at least until upstream gets fixed.
154
+ #
155
+ # For more details, see: https://wincent.com/issues/1617
156
+ if $curbuf.number == 0
157
+ # use bwipeout as bunload fails if passed the name of a hidden buffer
158
+ ::VIM::command 'silent! bwipeout! GoToFile'
159
+ @@buffer = nil
160
+ else
161
+ ::VIM::command "silent! bunload! #{@@buffer.number}"
162
+ end
163
+ end
164
+
165
+ def leave
166
+ close
167
+ unload
168
+ end
169
+
170
+ def unload
171
+ restore_window_dimensions
172
+ @settings.restore
173
+ @prompt.dispose
174
+ show_cursor
175
+ end
176
+
177
+ def add! char
178
+ @abbrev += char
179
+ end
180
+
181
+ def backspace!
182
+ @abbrev.chop!
183
+ end
184
+
185
+ def select_next
186
+ if @selection < @matches.length - 1
187
+ @selection += 1
188
+ print_match(@selection - 1) # redraw old selection (removes marker)
189
+ print_match(@selection) # redraw new selection (adds marker)
190
+ move_cursor_to_selected_line
191
+ else
192
+ # (possibly) loop or scroll
193
+ end
194
+ end
195
+
196
+ def select_prev
197
+ if @selection > 0
198
+ @selection -= 1
199
+ print_match(@selection + 1) # redraw old selection (removes marker)
200
+ print_match(@selection) # redraw new selection (adds marker)
201
+ move_cursor_to_selected_line
202
+ else
203
+ # (possibly) loop or scroll
204
+ end
205
+ end
206
+
207
+ def matches= matches
208
+ matches = matches.reverse if @reverse_list
209
+ if matches != @matches
210
+ @matches = matches
211
+ @selection = @reverse_list ? @matches.length - 1 : 0
212
+ print_matches
213
+ move_cursor_to_selected_line
214
+ end
215
+ end
216
+
217
+ def focus
218
+ unless @has_focus
219
+ @has_focus = true
220
+ if VIM::has_syntax?
221
+ ::VIM::command 'highlight link CommandTSelection Search'
222
+ end
223
+ end
224
+ end
225
+
226
+ def unfocus
227
+ if @has_focus
228
+ @has_focus = false
229
+ if VIM::has_syntax?
230
+ ::VIM::command 'highlight link CommandTSelection Visual'
231
+ end
232
+ end
233
+ end
234
+
235
+ def find char
236
+ # is this a new search or the continuation of a previous one?
237
+ now = Time.now
238
+ if @last_key_time.nil? or @last_key_time < (now - 0.5)
239
+ @find_string = char
240
+ else
241
+ @find_string += char
242
+ end
243
+ @last_key_time = now
244
+
245
+ # see if there's anything up ahead that matches
246
+ @matches.each_with_index do |match, idx|
247
+ if match[0, @find_string.length].casecmp(@find_string) == 0
248
+ old_selection = @selection
249
+ @selection = idx
250
+ print_match(old_selection) # redraw old selection (removes marker)
251
+ print_match(@selection) # redraw new selection (adds marker)
252
+ break
253
+ end
254
+ end
255
+ end
256
+
257
+ # Returns the currently selected item as a String.
258
+ def selection
259
+ @matches[@selection]
260
+ end
261
+
262
+ def print_no_such_file_or_directory
263
+ print_error 'NO SUCH FILE OR DIRECTORY'
264
+ end
265
+
266
+ private
267
+
268
+ def move_cursor_to_selected_line
269
+ # on some non-GUI terminals, the cursor doesn't hide properly
270
+ # so we move the cursor to prevent it from blinking away in the
271
+ # upper-left corner in a distracting fashion
272
+ @window.cursor = [@selection + 1, 0]
273
+ end
274
+
275
+ def print_error msg
276
+ return unless VIM::Window.select(@window)
277
+ unlock
278
+ clear
279
+ @window.height = @min_height > 0 ? @min_height : 1
280
+ @@buffer[1] = "-- #{msg} --"
281
+ lock
282
+ end
283
+
284
+ def restore_window_dimensions
285
+ # sort from tallest to shortest, tie-breaking on window width
286
+ @windows.sort! do |a, b|
287
+ order = b.height <=> a.height
288
+ if order.zero?
289
+ b.width <=> a.width
290
+ else
291
+ order
292
+ end
293
+ end
294
+
295
+ # starting with the tallest ensures that there are no constraints
296
+ # preventing windows on the side of vertical splits from regaining
297
+ # their original full size
298
+ @windows.each do |w|
299
+ # beware: window may be nil
300
+ if window = ::VIM::Window[w.index]
301
+ window.height = w.height
302
+ window.width = w.width
303
+ end
304
+ end
305
+ end
306
+
307
+ def match_text_for_idx idx
308
+ match = truncated_match @matches[idx].to_s
309
+ if idx == @selection
310
+ prefix = SELECTION_MARKER
311
+ suffix = padding_for_selected_match match
312
+ else
313
+ if VIM::has_syntax? && VIM::has_conceal?
314
+ match = match_with_syntax_highlight match
315
+ end
316
+ prefix = UNSELECTED_MARKER
317
+ suffix = ''
318
+ end
319
+ prefix + match + suffix
320
+ end
321
+
322
+ # Highlight matching characters within the matched string.
323
+ #
324
+ # Note that this is only approximate; it will highlight the first matching
325
+ # instances within the string, which may not actually be the instances that
326
+ # were used by the matching/scoring algorithm to determine the best score
327
+ # for the match.
328
+ #
329
+ def match_with_syntax_highlight match
330
+ highlight_chars = @prompt.abbrev.downcase.chars.to_a
331
+ match.chars.inject([]) do |output, char|
332
+ if char.downcase == highlight_chars.first
333
+ highlight_chars.shift
334
+ output.concat [MH_START, char, MH_END]
335
+ else
336
+ output << char
337
+ end
338
+ end.join
339
+ end
340
+
341
+ # Print just the specified match.
342
+ def print_match idx
343
+ return unless VIM::Window.select(@window)
344
+ unlock
345
+ @@buffer[idx + 1] = match_text_for_idx idx
346
+ lock
347
+ end
348
+
349
+ # Print all matches.
350
+ def print_matches
351
+ match_count = @matches.length
352
+ if match_count == 0
353
+ print_error 'NO MATCHES'
354
+ else
355
+ return unless VIM::Window.select(@window)
356
+ unlock
357
+ clear
358
+ actual_lines = 1
359
+ @window_width = @window.width # update cached value
360
+ max_lines = VIM::Screen.lines - 5
361
+ max_lines = 1 if max_lines < 0
362
+ actual_lines = match_count < @min_height ? @min_height : match_count
363
+ actual_lines = max_lines if actual_lines > max_lines
364
+ @window.height = actual_lines
365
+ (1..actual_lines).each do |line|
366
+ idx = line - 1
367
+ if @@buffer.count >= line
368
+ @@buffer[line] = match_text_for_idx idx
369
+ else
370
+ @@buffer.append line - 1, match_text_for_idx(idx)
371
+ end
372
+ end
373
+ lock
374
+ end
375
+ end
376
+
377
+ # Prepare padding for match text (trailing spaces) so that selection
378
+ # highlighting extends all the way to the right edge of the window.
379
+ def padding_for_selected_match str
380
+ len = str.length
381
+ if len >= @window_width - MARKER_LENGTH
382
+ ''
383
+ else
384
+ ' ' * (@window_width - MARKER_LENGTH - len)
385
+ end
386
+ end
387
+
388
+ # Convert "really/long/path" into "really...path" based on available
389
+ # window width.
390
+ def truncated_match str
391
+ len = str.length
392
+ available_width = @window_width - MARKER_LENGTH
393
+ return str if len <= available_width
394
+ left = (available_width / 2) - 1
395
+ right = (available_width / 2) - 2 + (available_width % 2)
396
+ str[0, left] + '...' + str[-right, right]
397
+ end
398
+
399
+ def clear
400
+ # range = % (whole buffer)
401
+ # action = d (delete)
402
+ # register = _ (black hole register, don't record deleted text)
403
+ ::VIM::command 'silent %d _'
404
+ end
405
+
406
+ def get_cursor_highlight
407
+ # there are 3 possible formats to check for, each needing to be
408
+ # transformed in a certain way in order to reapply the highlight:
409
+ # Cursor xxx guifg=bg guibg=fg -> :hi! Cursor guifg=bg guibg=fg
410
+ # Cursor xxx links to SomethingElse -> :hi! link Cursor SomethingElse
411
+ # Cursor xxx cleared -> :hi! clear Cursor
412
+ highlight = VIM::capture 'silent! 0verbose highlight Cursor'
413
+
414
+ if highlight =~ /^Cursor\s+xxx\s+links to (\w+)/
415
+ "link Cursor #{$~[1]}"
416
+ elsif highlight =~ /^Cursor\s+xxx\s+cleared/
417
+ 'clear Cursor'
418
+ elsif highlight =~ /Cursor\s+xxx\s+(.+)/
419
+ "Cursor #{$~[1]}"
420
+ else # likely cause E411 Cursor highlight group not found
421
+ nil
422
+ end
423
+ end
424
+
425
+ def hide_cursor
426
+ if @cursor_highlight
427
+ ::VIM::command 'highlight Cursor NONE'
428
+ end
429
+ end
430
+
431
+ def show_cursor
432
+ if @cursor_highlight
433
+ ::VIM::command "highlight #{@cursor_highlight}"
434
+ end
435
+ end
436
+
437
+ def lock
438
+ ::VIM::command 'setlocal nomodifiable'
439
+ end
440
+
441
+ def unlock
442
+ ::VIM::command 'setlocal modifiable'
443
+ end
444
+ end
445
+ end