]> git.r.bdr.sh - rbdr/dotfiles/blob - vim/tmp/command_t/ruby/command-t/controller.rb
Relative line numbers for vim
[rbdr/dotfiles] / vim / tmp / command_t / ruby / command-t / controller.rb
1 # Copyright 2010-2011 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 'command-t/finder/buffer_finder'
25 require 'command-t/finder/file_finder'
26 require 'command-t/match_window'
27 require 'command-t/prompt'
28 require 'command-t/vim/path_utilities'
29
30 module CommandT
31 class Controller
32 include VIM::PathUtilities
33
34 def initialize
35 @prompt = Prompt.new
36 @buffer_finder = CommandT::BufferFinder.new
37 set_up_file_finder
38 set_up_max_height
39 end
40
41 def show_buffer_finder
42 @path = VIM::pwd
43 @active_finder = @buffer_finder
44 show
45 end
46
47 def show_file_finder
48 # optional parameter will be desired starting directory, or ""
49 @path = File.expand_path(::VIM::evaluate('a:arg'), VIM::pwd)
50 @file_finder.path = @path
51 @active_finder = @file_finder
52 show
53 rescue Errno::ENOENT
54 # probably a problem with the optional parameter
55 @match_window.print_no_such_file_or_directory
56 end
57
58 def hide
59 @match_window.close
60 if VIM::Window.select @initial_window
61 if @initial_buffer.number == 0
62 # upstream bug: buffer number misreported as 0
63 # see: https://wincent.com/issues/1617
64 ::VIM::command "silent b #{@initial_buffer.name}"
65 else
66 ::VIM::command "silent b #{@initial_buffer.number}"
67 end
68 end
69 end
70
71 def flush
72 set_up_max_height
73 set_up_file_finder
74 end
75
76 def handle_key
77 key = ::VIM::evaluate('a:arg').to_i.chr
78 if @focus == @prompt
79 @prompt.add! key
80 list_matches
81 else
82 @match_window.find key
83 end
84 end
85
86 def backspace
87 if @focus == @prompt
88 @prompt.backspace!
89 list_matches
90 end
91 end
92
93 def delete
94 if @focus == @prompt
95 @prompt.delete!
96 list_matches
97 end
98 end
99
100 def accept_selection options = {}
101 selection = @match_window.selection
102 hide
103 open_selection(selection, options) unless selection.nil?
104 end
105
106 def toggle_focus
107 @focus.unfocus # old focus
108 @focus = @focus == @prompt ? @match_window : @prompt
109 @focus.focus # new focus
110 end
111
112 def cancel
113 hide
114 end
115
116 def select_next
117 @match_window.select_next
118 end
119
120 def select_prev
121 @match_window.select_prev
122 end
123
124 def clear
125 @prompt.clear!
126 list_matches
127 end
128
129 def cursor_left
130 @prompt.cursor_left if @focus == @prompt
131 end
132
133 def cursor_right
134 @prompt.cursor_right if @focus == @prompt
135 end
136
137 def cursor_end
138 @prompt.cursor_end if @focus == @prompt
139 end
140
141 def cursor_start
142 @prompt.cursor_start if @focus == @prompt
143 end
144
145 def leave
146 @match_window.leave
147 end
148
149 def unload
150 @match_window.unload
151 end
152
153 private
154
155 def show
156 @initial_window = $curwin
157 @initial_buffer = $curbuf
158 @match_window = MatchWindow.new \
159 :prompt => @prompt,
160 :match_window_at_top => get_bool('g:CommandTMatchWindowAtTop'),
161 :match_window_reverse => get_bool('g:CommandTMatchWindowReverse')
162 @focus = @prompt
163 @prompt.focus
164 register_for_key_presses
165 clear # clears prompt and lists matches
166 end
167
168 def set_up_max_height
169 @max_height = get_number('g:CommandTMaxHeight') || 0
170 end
171
172 def set_up_file_finder
173 @file_finder = CommandT::FileFinder.new nil,
174 :max_files => get_number('g:CommandTMaxFiles'),
175 :max_depth => get_number('g:CommandTMaxDepth'),
176 :always_show_dot_files => get_bool('g:CommandTAlwaysShowDotFiles'),
177 :never_show_dot_files => get_bool('g:CommandTNeverShowDotFiles'),
178 :scan_dot_directories => get_bool('g:CommandTScanDotDirectories')
179 end
180
181 def exists? name
182 ::VIM::evaluate("exists(\"#{name}\")").to_i != 0
183 end
184
185 def get_number name
186 exists?(name) ? ::VIM::evaluate("#{name}").to_i : nil
187 end
188
189 def get_bool name
190 exists?(name) ? ::VIM::evaluate("#{name}").to_i != 0 : nil
191 end
192
193 def get_string name
194 exists?(name) ? ::VIM::evaluate("#{name}").to_s : nil
195 end
196
197 # expect a string or a list of strings
198 def get_list_or_string name
199 return nil unless exists?(name)
200 list_or_string = ::VIM::evaluate("#{name}")
201 if list_or_string.kind_of?(Array)
202 list_or_string.map { |item| item.to_s }
203 else
204 list_or_string.to_s
205 end
206 end
207
208 # Backslash-escape space, \, |, %, #, "
209 def sanitize_path_string str
210 # for details on escaping command-line mode arguments see: :h :
211 # (that is, help on ":") in the Vim documentation.
212 str.gsub(/[ \\|%#"]/, '\\\\\0')
213 end
214
215 def default_open_command
216 if !get_bool('&hidden') && get_bool('&modified')
217 'sp'
218 else
219 'e'
220 end
221 end
222
223 def ensure_appropriate_window_selection
224 # normally we try to open the selection in the current window, but there
225 # is one exception:
226 #
227 # - we don't touch any "unlisted" buffer with buftype "nofile" (such as
228 # NERDTree or MiniBufExplorer); this is to avoid things like the "Not
229 # enough room" error which occurs when trying to open in a split in a
230 # shallow (potentially 1-line) buffer like MiniBufExplorer is current
231 #
232 # Other "unlisted" buffers, such as those with buftype "help" are treated
233 # normally.
234 initial = $curwin
235 while true do
236 break unless ::VIM::evaluate('&buflisted').to_i == 0 &&
237 ::VIM::evaluate('&buftype').to_s == 'nofile'
238 ::VIM::command 'wincmd w' # try next window
239 break if $curwin == initial # have already tried all
240 end
241 end
242
243 def open_selection selection, options = {}
244 command = options[:command] || default_open_command
245 selection = File.expand_path selection, @path
246 selection = relative_path_under_working_directory selection
247 selection = sanitize_path_string selection
248 ensure_appropriate_window_selection
249 ::VIM::command "silent #{command} #{selection}"
250 end
251
252 def map key, function, param = nil
253 ::VIM::command "noremap <silent> <buffer> #{key} " \
254 ":call CommandT#{function}(#{param})<CR>"
255 end
256
257 def xterm?
258 !!(::VIM::evaluate('&term') =~ /\Axterm/)
259 end
260
261 def vt100?
262 !!(::VIM::evaluate('&term') =~ /\Avt100/)
263 end
264
265 def register_for_key_presses
266 # "normal" keys (interpreted literally)
267 numbers = ('0'..'9').to_a.join
268 lowercase = ('a'..'z').to_a.join
269 uppercase = lowercase.upcase
270 punctuation = '<>`@#~!"$%&/()=+*-_.,;:?\\\'{}[] ' # and space
271 (numbers + lowercase + uppercase + punctuation).each_byte do |b|
272 map "<Char-#{b}>", 'HandleKey', b
273 end
274
275 # "special" keys (overridable by settings)
276 { 'Backspace' => '<BS>',
277 'Delete' => '<Del>',
278 'AcceptSelection' => '<CR>',
279 'AcceptSelectionSplit' => ['<C-CR>', '<C-s>'],
280 'AcceptSelectionTab' => '<C-t>',
281 'AcceptSelectionVSplit' => '<C-v>',
282 'ToggleFocus' => '<Tab>',
283 'Cancel' => ['<C-c>', '<Esc>'],
284 'SelectNext' => ['<C-n>', '<C-j>', '<Down>'],
285 'SelectPrev' => ['<C-p>', '<C-k>', '<Up>'],
286 'Clear' => '<C-u>',
287 'CursorLeft' => ['<Left>', '<C-h>'],
288 'CursorRight' => ['<Right>', '<C-l>'],
289 'CursorEnd' => '<C-e>',
290 'CursorStart' => '<C-a>' }.each do |key, value|
291 if override = get_list_or_string("g:CommandT#{key}Map")
292 [override].flatten.each do |mapping|
293 map mapping, key
294 end
295 else
296 [value].flatten.each do |mapping|
297 map mapping, key unless mapping == '<Esc>' && (xterm? || vt100?)
298 end
299 end
300 end
301 end
302
303 # Returns the desired maximum number of matches, based on available
304 # vertical space and the g:CommandTMaxHeight option.
305 def match_limit
306 limit = VIM::Screen.lines - 5
307 limit = 1 if limit < 0
308 limit = [limit, @max_height].min if @max_height > 0
309 limit
310 end
311
312 def list_matches
313 matches = @active_finder.sorted_matches_for @prompt.abbrev, :limit => match_limit
314 @match_window.matches = matches
315 end
316 end # class Controller
317 end # module commandT