dotvim/.vim/bundle/command-t/ruby/command-t/controller.rb
2015-07-02 10:45:40 +02:00

431 lines
12 KiB
Ruby

# Copyright 2010-2014 Greg Hurrell. All rights reserved.
# Licensed under the terms of the BSD 2-clause license.
require 'command-t/finder/buffer_finder'
require 'command-t/finder/jump_finder'
require 'command-t/finder/file_finder'
require 'command-t/finder/mru_buffer_finder'
require 'command-t/finder/tag_finder'
require 'command-t/match_window'
require 'command-t/prompt'
require 'command-t/vim/path_utilities'
require 'command-t/util'
module CommandT
class Controller
include VIM::PathUtilities
def initialize
@prompt = Prompt.new
end
def show_buffer_finder
@path = VIM::pwd
@active_finder = buffer_finder
show
end
def show_jump_finder
@path = VIM::pwd
@active_finder = jump_finder
show
end
def show_mru_finder
@path = VIM::pwd
@active_finder = mru_finder
show
end
def show_tag_finder
@path = VIM::pwd
@active_finder = tag_finder
show
end
def show_file_finder
# optional parameter will be desired starting directory, or ""
arg = ::VIM::evaluate('a:arg')
if arg && arg.size > 0
@path = File.expand_path(arg, VIM::pwd)
else
traverse = VIM::get_string('g:CommandTTraverseSCM') || 'file'
case traverse
when 'file'
@path = nearest_ancestor(VIM::current_file_dir, scm_markers)
when 'dir'
@path = nearest_ancestor(VIM::pwd, scm_markers)
end
@path = VIM::pwd unless @path
end
@active_finder = file_finder
file_finder.path = @path
show
rescue Errno::ENOENT
# probably a problem with the optional parameter
@match_window.print_no_such_file_or_directory
end
def hide
@match_window.leave
if VIM::Window.select @initial_window
if @initial_buffer.number == 0
# upstream bug: buffer number misreported as 0
# see: https://wincent.com/issues/1617
::VIM::command "silent b #{@initial_buffer.name}"
else
::VIM::command "silent b #{@initial_buffer.number}"
end
end
end
# Take current matches and stick them in the quickfix window.
def quickfix
hide
matches = @matches.map do |match|
"{ 'filename': '#{VIM::escape_for_single_quotes match}' }"
end.join(', ')
::VIM::command 'call setqflist([' + matches + '])'
::VIM::command 'cope'
end
def refresh
return unless @active_finder && @active_finder.respond_to?(:flush)
@active_finder.flush
list_matches!
end
def flush
@max_height = nil
@min_height = nil
@file_finder = nil
@tag_finder = nil
end
def handle_key
key = ::VIM::evaluate('a:arg').to_i.chr
if @focus == @prompt
@prompt.add! key
@needs_update = true
else
@match_window.find key
end
end
def backspace
if @focus == @prompt
@prompt.backspace!
@needs_update = true
end
end
def delete
if @focus == @prompt
@prompt.delete!
@needs_update = true
end
end
def accept_selection(options = {})
selection = @match_window.selection
hide
open_selection(selection, options) unless selection.nil?
end
def toggle_focus
@focus.unfocus # old focus
@focus = @focus == @prompt ? @match_window : @prompt
@focus.focus # new focus
end
def cancel
hide
end
def select_next
@match_window.select_next
end
def select_prev
@match_window.select_prev
end
def clear
@prompt.clear!
list_matches!
end
def clear_prev_word
@prompt.clear_prev_word!
list_matches!
end
def cursor_left
@prompt.cursor_left if @focus == @prompt
end
def cursor_right
@prompt.cursor_right if @focus == @prompt
end
def cursor_end
@prompt.cursor_end if @focus == @prompt
end
def cursor_start
@prompt.cursor_start if @focus == @prompt
end
def leave
@match_window.leave
end
def unload
@match_window.unload
end
def list_matches(options = {})
return unless @needs_update || options[:force]
@matches = @active_finder.sorted_matches_for(
@prompt.abbrev,
:case_sensitive => case_sensitive?,
:limit => match_limit,
:threads => CommandT::Util.processor_count
)
@match_window.matches = @matches
@needs_update = false
end
def tab_command
VIM::get_string('g:CommandTAcceptSelectionTabCommand') || 'tabe'
end
def split_command
VIM::get_string('g:CommandTAcceptSelectionSplitCommand') || 'sp'
end
def vsplit_command
VIM::get_string('g:CommandTAcceptSelectionVSplitCommand') || 'vs'
end
private
def scm_markers
markers = VIM::get_string('g:CommandTSCMDirectories')
markers = markers && markers.split(/\s*,\s*/)
markers = %w[.git .hg .svn .bzr _darcs] unless markers && markers.length
markers
end
def list_matches!
list_matches(:force => true)
end
def show
@initial_window = $curwin
@initial_buffer = $curbuf
@match_window = MatchWindow.new \
:highlight_color => VIM::get_string('g:CommandTHighlightColor'),
:match_window_at_top => VIM::get_bool('g:CommandTMatchWindowAtTop'),
:match_window_reverse => VIM::get_bool('g:CommandTMatchWindowReverse'),
:min_height => min_height,
:debounce_interval => VIM::get_number('g:CommandTInputDebounce') || 50,
:prompt => @prompt
@focus = @prompt
@prompt.focus
register_for_key_presses
set_up_autocmds
clear # clears prompt and lists matches
end
def max_height
@max_height ||= VIM::get_number('g:CommandTMaxHeight') || 0
end
def min_height
@min_height ||= begin
min_height = VIM::get_number('g:CommandTMinHeight') || 0
min_height = max_height if max_height != 0 && min_height > max_height
min_height
end
end
def case_sensitive?
if @prompt.abbrev.match(/[A-Z]/)
if VIM::exists?('g:CommandTSmartCase')
smart_case = VIM::get_bool('g:CommandTSmartCase')
else
smart_case = VIM::get_bool('&smartcase')
end
if smart_case
return true
end
end
if VIM::exists?('g:CommandTIgnoreCase')
return !VIM::get_bool('g:CommandTIgnoreCase')
end
false
end
# Backslash-escape space, \, |, %, #, "
def sanitize_path_string(str)
# for details on escaping command-line mode arguments see: :h :
# (that is, help on ":") in the Vim documentation.
str.gsub(/[ \\|%#"]/, '\\\\\0')
end
def default_open_command
if !VIM::get_bool('&modified') ||
VIM::get_bool('&hidden') ||
VIM::get_bool('&autowriteall') && !VIM::get_bool('&readonly')
VIM::get_string('g:CommandTAcceptSelectionCommand') || 'e'
else
'sp'
end
end
def ensure_appropriate_window_selection
# normally we try to open the selection in the current window, but there
# is one exception:
#
# - we don't touch any "unlisted" buffer with buftype "nofile" (such as
# NERDTree or MiniBufExplorer); this is to avoid things like the "Not
# enough room" error which occurs when trying to open in a split in a
# shallow (potentially 1-line) buffer like MiniBufExplorer is current
#
# Other "unlisted" buffers, such as those with buftype "help" are treated
# normally.
initial = $curwin
while true do
break unless ::VIM::evaluate('&buflisted').to_i == 0 &&
::VIM::evaluate('&buftype').to_s == 'nofile'
::VIM::command 'wincmd w' # try next window
break if $curwin == initial # have already tried all
end
end
def open_selection(selection, options = {})
command = options[:command] || default_open_command
selection = File.expand_path selection, @path
selection = relative_path_under_working_directory selection
selection = sanitize_path_string selection
selection = File.join('.', selection) if selection =~ /^\+/
ensure_appropriate_window_selection
@active_finder.open_selection command, selection, options
end
def map(key, function, param = nil)
::VIM::command "noremap <silent> <buffer> #{key} " \
":call CommandT#{function}(#{param})<CR>"
end
def term
@term ||= ::VIM::evaluate('&term')
end
def register_for_key_presses
# "normal" keys (interpreted literally)
numbers = ('0'..'9').to_a.join
lowercase = ('a'..'z').to_a.join
uppercase = lowercase.upcase
punctuation = '<>`@#~!"$%&/()=+*-_.,;:?\\\'{}[] ' # and space
(numbers + lowercase + uppercase + punctuation).each_byte do |b|
map "<Char-#{b}>", 'HandleKey', b
end
# "special" keys (overridable by settings)
{
'AcceptSelection' => '<CR>',
'AcceptSelectionSplit' => ['<C-CR>', '<C-s>'],
'AcceptSelectionTab' => '<C-t>',
'AcceptSelectionVSplit' => '<C-v>',
'Backspace' => '<BS>',
'Cancel' => ['<C-c>', '<Esc>'],
'Clear' => '<C-u>',
'ClearPrevWord' => '<C-w>',
'CursorEnd' => '<C-e>',
'CursorLeft' => ['<Left>', '<C-h>'],
'CursorRight' => ['<Right>', '<C-l>'],
'CursorStart' => '<C-a>',
'Delete' => '<Del>',
'Quickfix' => '<C-q>',
'Refresh' => '<C-f>',
'SelectNext' => ['<C-n>', '<C-j>', '<Down>'],
'SelectPrev' => ['<C-p>', '<C-k>', '<Up>'],
'ToggleFocus' => '<Tab>',
}.each do |key, value|
if override = VIM::get_list_or_string("g:CommandT#{key}Map")
Array(override).each do |mapping|
map mapping, key
end
else
Array(value).each do |mapping|
unless mapping == '<Esc>' && term =~ /\A(screen|xterm|vt100)/
map mapping, key
end
end
end
end
end
def set_up_autocmds
::VIM::command 'augroup Command-T'
::VIM::command 'au!'
::VIM::command 'autocmd CursorHold <buffer> :call CommandTListMatches()'
::VIM::command 'augroup END'
end
# Returns the desired maximum number of matches, based on available vertical
# space and the g:CommandTMaxHeight option.
#
# Note the "available" space is actually a theoretical upper bound; it takes
# into account screen dimensions but not things like existing splits which
# may reduce the amount of space in practice.
def match_limit
limit = [1, VIM::Screen.lines - 5].max
limit = [limit, max_height].min if max_height > 0
limit
end
def buffer_finder
@buffer_finder ||= CommandT::BufferFinder.new
end
def mru_finder
@mru_finder ||= CommandT::MRUBufferFinder.new
end
def file_finder
@file_finder ||= CommandT::FileFinder.new nil,
:max_depth => VIM::get_number('g:CommandTMaxDepth'),
:max_files => VIM::get_number('g:CommandTMaxFiles'),
:max_caches => VIM::get_number('g:CommandTMaxCachedDirectories'),
:always_show_dot_files => VIM::get_bool('g:CommandTAlwaysShowDotFiles'),
:never_show_dot_files => VIM::get_bool('g:CommandTNeverShowDotFiles'),
:scan_dot_directories => VIM::get_bool('g:CommandTScanDotDirectories'),
:wild_ignore => VIM::get_string('g:CommandTWildIgnore'),
:scanner => VIM::get_string('g:CommandTFileScanner')
end
def jump_finder
@jump_finder ||= CommandT::JumpFinder.new
end
def tag_finder
@tag_finder ||= CommandT::TagFinder.new \
:include_filenames => VIM::get_bool('g:CommandTTagIncludeFilenames')
end
end # class Controller
end # module CommandT