You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
10 KiB
Ruby
358 lines
10 KiB
Ruby
# Copyright 2010-2012 Wincent Colaiuta. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
#
|
|
# 1. Redistributions of source code must retain the above copyright notice,
|
|
# this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
# this list of conditions and the following disclaimer in the documentation
|
|
# and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
require 'command-t/finder/buffer_finder'
|
|
require 'command-t/finder/jump_finder'
|
|
require 'command-t/finder/file_finder'
|
|
require 'command-t/finder/tag_finder'
|
|
require 'command-t/match_window'
|
|
require 'command-t/prompt'
|
|
require 'command-t/vim/path_utilities'
|
|
|
|
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_tag_finder
|
|
@path = VIM::pwd
|
|
@active_finder = tag_finder
|
|
show
|
|
end
|
|
|
|
def show_file_finder
|
|
# optional parameter will be desired starting directory, or ""
|
|
@path = File.expand_path(::VIM::evaluate('a:arg'), VIM::pwd)
|
|
@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.close
|
|
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
|
|
|
|
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
|
|
list_matches
|
|
else
|
|
@match_window.find key
|
|
end
|
|
end
|
|
|
|
def backspace
|
|
if @focus == @prompt
|
|
@prompt.backspace!
|
|
list_matches
|
|
end
|
|
end
|
|
|
|
def delete
|
|
if @focus == @prompt
|
|
@prompt.delete!
|
|
list_matches
|
|
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 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
|
|
|
|
private
|
|
|
|
def show
|
|
@initial_window = $curwin
|
|
@initial_buffer = $curbuf
|
|
@match_window = MatchWindow.new \
|
|
:prompt => @prompt,
|
|
:match_window_at_top => get_bool('g:CommandTMatchWindowAtTop'),
|
|
:match_window_reverse => get_bool('g:CommandTMatchWindowReverse'),
|
|
:min_height => min_height
|
|
@focus = @prompt
|
|
@prompt.focus
|
|
register_for_key_presses
|
|
clear # clears prompt and lists matches
|
|
end
|
|
|
|
def max_height
|
|
@max_height ||= get_number('g:CommandTMaxHeight') || 0
|
|
end
|
|
|
|
def min_height
|
|
@min_height ||= begin
|
|
min_height = get_number('g:CommandTMinHeight') || 0
|
|
min_height = max_height if max_height != 0 && min_height > max_height
|
|
min_height
|
|
end
|
|
end
|
|
|
|
def get_number name
|
|
VIM::exists?(name) ? ::VIM::evaluate("#{name}").to_i : nil
|
|
end
|
|
|
|
def get_bool name
|
|
VIM::exists?(name) ? ::VIM::evaluate("#{name}").to_i != 0 : nil
|
|
end
|
|
|
|
def get_string name
|
|
VIM::exists?(name) ? ::VIM::evaluate("#{name}").to_s : nil
|
|
end
|
|
|
|
# expect a string or a list of strings
|
|
def get_list_or_string name
|
|
return nil unless VIM::exists?(name)
|
|
list_or_string = ::VIM::evaluate("#{name}")
|
|
if list_or_string.kind_of?(Array)
|
|
list_or_string.map { |item| item.to_s }
|
|
else
|
|
list_or_string.to_s
|
|
end
|
|
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 !get_bool('&hidden') && get_bool('&modified')
|
|
'sp'
|
|
else
|
|
'e'
|
|
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
|
|
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>',
|
|
'CursorEnd' => '<C-e>',
|
|
'CursorLeft' => ['<Left>', '<C-h>'],
|
|
'CursorRight' => ['<Right>', '<C-l>'],
|
|
'CursorStart' => '<C-a>',
|
|
'Delete' => '<Del>',
|
|
'Refresh' => '<C-f>',
|
|
'SelectNext' => ['<C-n>', '<C-j>', '<Down>'],
|
|
'SelectPrev' => ['<C-p>', '<C-k>', '<Up>'],
|
|
'ToggleFocus' => '<Tab>',
|
|
}.each do |key, value|
|
|
if override = 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
|
|
|
|
# Returns the desired maximum number of matches, based on available
|
|
# vertical space and the g:CommandTMaxHeight option.
|
|
def match_limit
|
|
limit = VIM::Screen.lines - 5
|
|
limit = 1 if limit < 0
|
|
limit = [limit, max_height].min if max_height > 0
|
|
limit
|
|
end
|
|
|
|
def list_matches
|
|
matches = @active_finder.sorted_matches_for @prompt.abbrev, :limit => match_limit
|
|
@match_window.matches = matches
|
|
end
|
|
|
|
def buffer_finder
|
|
@buffer_finder ||= CommandT::BufferFinder.new
|
|
end
|
|
|
|
def file_finder
|
|
@file_finder ||= CommandT::FileFinder.new nil,
|
|
:max_depth => get_number('g:CommandTMaxDepth'),
|
|
:max_files => get_number('g:CommandTMaxFiles'),
|
|
:max_caches => get_number('g:CommandTMaxCachedDirectories'),
|
|
:always_show_dot_files => get_bool('g:CommandTAlwaysShowDotFiles'),
|
|
:never_show_dot_files => get_bool('g:CommandTNeverShowDotFiles'),
|
|
:scan_dot_directories => get_bool('g:CommandTScanDotDirectories')
|
|
end
|
|
|
|
def jump_finder
|
|
@jump_finder ||= CommandT::JumpFinder.new
|
|
end
|
|
|
|
def tag_finder
|
|
@tag_finder ||= CommandT::TagFinder.new \
|
|
:include_filenames => get_bool('g:CommandTTagIncludeFilenames')
|
|
end
|
|
end # class Controller
|
|
end # module commandT
|