Remove command-t for pick
This commit is contained in:
parent
3e4286bf43
commit
71202e83ee
83 changed files with 8 additions and 478048 deletions
1
vim/vim.symlink/bundle/command-t
Submodule
1
vim/vim.symlink/bundle/command-t
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 3d640a8baa6d1934d2b5d5d0df059595413cdfaf
|
|
@ -1 +0,0 @@
|
|||
data/benchmark.yml export-ignore
|
5
vim/vim.symlink/bundle/command-t/.gitignore
vendored
5
vim/vim.symlink/bundle/command-t/.gitignore
vendored
|
@ -1,5 +0,0 @@
|
|||
.release-notes.txt
|
||||
.ruby-version
|
||||
command-t.recipe
|
||||
/.bundle
|
||||
vendor/bundle
|
9
vim/vim.symlink/bundle/command-t/.gitmodules
vendored
9
vim/vim.symlink/bundle/command-t/.gitmodules
vendored
|
@ -1,9 +0,0 @@
|
|||
[submodule "vendor/vimball"]
|
||||
path = vendor/vimball
|
||||
url = https://github.com/tomtom/vimball.rb.git
|
||||
[submodule "vendor/vimscriptuploader"]
|
||||
path = vendor/vimscriptuploader
|
||||
url = https://github.com/tomtom/vimscriptuploader.rb.git
|
||||
[submodule "vendor/vroom"]
|
||||
path = vendor/vroom
|
||||
url = https://github.com/google/vroom.git
|
|
@ -1,8 +0,0 @@
|
|||
Greg Hurrell <greg@hurrell.net> Greg Hurrell <greg@hurrell.net>
|
||||
Greg Hurrell <greg@hurrell.net> Wincent Colaiuta <win@wincent.com>
|
||||
Kevin Webster <webster.kevin@gmail.com> rabidpraxis <webster.kevin@gmail.com>
|
||||
Nicolas Alpi <nicolas.alpi@gmail.com> Spyou <nicolas.alpi@gmail.com>
|
||||
Noon Silk <noonsilk@gmail.com> Noon Silk <noonsilk@gmail.com>
|
||||
Noon Silk <noonsilk@gmail.com> Noon Silk <superhappyfun@gmail.com>
|
||||
Sung Pae <sung@metablu.com> guns <sung@metablu.com>
|
||||
Sung Pae <sung@metablu.com> guns <self@sungpae.com>
|
|
@ -1 +0,0 @@
|
|||
--colour
|
|
@ -1,2 +0,0 @@
|
|||
--- {}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'nokogiri'
|
||||
gem 'mechanize'
|
||||
gem 'rake'
|
||||
gem 'rr'
|
||||
gem 'rspec'
|
|
@ -1,48 +0,0 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
diff-lcs (1.2.5)
|
||||
domain_name (0.5.15)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
http-cookie (1.0.2)
|
||||
domain_name (~> 0.5)
|
||||
mechanize (2.7.3)
|
||||
domain_name (~> 0.5, >= 0.5.1)
|
||||
http-cookie (~> 1.0)
|
||||
mime-types (~> 2.0)
|
||||
net-http-digest_auth (~> 1.1, >= 1.1.1)
|
||||
net-http-persistent (~> 2.5, >= 2.5.2)
|
||||
nokogiri (~> 1.4)
|
||||
ntlm-http (~> 0.1, >= 0.1.1)
|
||||
webrobots (>= 0.0.9, < 0.2)
|
||||
mime-types (2.1)
|
||||
mini_portile (0.5.2)
|
||||
net-http-digest_auth (1.4)
|
||||
net-http-persistent (2.9.4)
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
ntlm-http (0.1.1)
|
||||
rake (10.1.1)
|
||||
rr (1.1.2)
|
||||
rspec (2.14.1)
|
||||
rspec-core (~> 2.14.0)
|
||||
rspec-expectations (~> 2.14.0)
|
||||
rspec-mocks (~> 2.14.0)
|
||||
rspec-core (2.14.7)
|
||||
rspec-expectations (2.14.5)
|
||||
diff-lcs (>= 1.1.3, < 2.0)
|
||||
rspec-mocks (2.14.5)
|
||||
unf (0.1.3)
|
||||
unf_ext
|
||||
unf_ext (0.0.6)
|
||||
webrobots (0.1.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
mechanize
|
||||
nokogiri
|
||||
rake
|
||||
rr
|
||||
rspec
|
|
@ -1,22 +0,0 @@
|
|||
Copyright 2010-2014 Greg Hurrell. 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.
|
|
@ -1,21 +0,0 @@
|
|||
rubyfiles := $(shell find ruby -name '*.rb')
|
||||
cfiles := $(shell find ruby -name '*.c')
|
||||
cheaders := $(shell find ruby -name '*.h')
|
||||
depends := $(shell find ruby -name depend)
|
||||
txtfiles := $(shell find doc -name '*.txt')
|
||||
vimfiles := $(shell find autoload plugin -name '*.vim')
|
||||
|
||||
vimball: command-t.vba
|
||||
|
||||
command-t.recipe: $(rubyfiles) $(cfiles) $(cheaders) $(depends) $(txtfiles) $(vimfiles)
|
||||
echo "$^" | perl -pe 's/ /\n/g' > $@
|
||||
command-t.vba: command-t.recipe
|
||||
vendor/vimball/vimball.rb -d . -b . vba $^
|
||||
|
||||
.PHONY: spec
|
||||
spec:
|
||||
rspec spec
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f command-t.vba
|
|
@ -1 +0,0 @@
|
|||
doc/command-t.txt
|
|
@ -1,179 +0,0 @@
|
|||
require 'yaml'
|
||||
|
||||
def bail_on_failure
|
||||
exitstatus = $?.exitstatus
|
||||
if exitstatus != 0
|
||||
err "last command failed with exit status #{exitstatus}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
def version
|
||||
`git describe`.chomp
|
||||
end
|
||||
|
||||
def rubygems_version
|
||||
# RubyGems will barf if we try to pass an intermediate version number
|
||||
# like "1.1b2-10-g61a374a", so no choice but to abbreviate it
|
||||
`git describe --abbrev=0`.chomp
|
||||
end
|
||||
|
||||
def yellow
|
||||
"\033[33m"
|
||||
end
|
||||
|
||||
def red
|
||||
"\033[31m"
|
||||
end
|
||||
|
||||
def clear
|
||||
"\033[0m"
|
||||
end
|
||||
|
||||
def warn(str)
|
||||
puts "#{yellow}warning: #{str}#{clear}"
|
||||
end
|
||||
|
||||
def err(str)
|
||||
puts "#{red}error: #{str}#{clear}"
|
||||
end
|
||||
|
||||
def prepare_release_notes
|
||||
# extract base release notes from README.txt HISTORY section
|
||||
File.open('.release-notes.txt', 'w') do |out|
|
||||
lines = File.readlines('README.txt').each { |line| line.chomp! }
|
||||
while line = lines.shift do
|
||||
next unless line =~ /^HISTORY +\*command-t-history\*$/
|
||||
break unless lines.shift == '' &&
|
||||
(line = lines.shift) && line =~ /^\d\.\d/ &&
|
||||
lines.shift == ''
|
||||
while line = lines.shift && line != ''
|
||||
out.puts line
|
||||
end
|
||||
break
|
||||
end
|
||||
out.puts ''
|
||||
out.puts '# Please edit the release notes to taste.'
|
||||
out.puts '# Blank lines and lines beginning with a hash will be removed.'
|
||||
out.puts '# To abort, exit your editor with a non-zero exit status (:cquit in Vim).'
|
||||
end
|
||||
|
||||
unless system "$EDITOR .release-notes.txt"
|
||||
err "editor exited with non-zero exit status; aborting"
|
||||
exit 1
|
||||
end
|
||||
|
||||
filtered = read_release_notes
|
||||
File.open('.release-notes.txt', 'w') do |out|
|
||||
out.print filtered
|
||||
end
|
||||
end
|
||||
|
||||
def read_release_notes
|
||||
File.readlines('.release-notes.txt').reject do |line|
|
||||
line =~ /^(#.*|\s*)$/ # filter comment lines and blank lines
|
||||
end.join
|
||||
end
|
||||
|
||||
task :default => :spec
|
||||
|
||||
desc 'Print help on preparing a release'
|
||||
task :help do
|
||||
puts <<-END
|
||||
|
||||
The general release sequence is:
|
||||
|
||||
rake prerelease
|
||||
rake gem
|
||||
rake push
|
||||
rake upload:all
|
||||
|
||||
Note: the upload task depends on the Mechanize gem; and may require a
|
||||
prior `gem install mechanize`
|
||||
|
||||
END
|
||||
end
|
||||
|
||||
desc 'Run specs'
|
||||
task :spec do
|
||||
system 'bundle exec rspec spec'
|
||||
bail_on_failure
|
||||
end
|
||||
|
||||
desc 'Create vimball archive'
|
||||
task :vimball => :check_tag do
|
||||
system 'make'
|
||||
bail_on_failure
|
||||
FileUtils.cp 'command-t.vba', "command-t-#{version}.vba"
|
||||
end
|
||||
|
||||
desc 'Clean compiled products'
|
||||
task :clean do
|
||||
Dir.chdir 'ruby/command-t' do
|
||||
system 'make clean' if File.exists?('Makefile')
|
||||
system 'rm -f Makefile'
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Clobber all generated files'
|
||||
task :clobber => :clean do
|
||||
system 'make clean'
|
||||
end
|
||||
|
||||
desc 'Compile extension'
|
||||
task :make do
|
||||
Dir.chdir 'ruby/command-t' do
|
||||
ruby 'extconf.rb'
|
||||
system 'make clean'
|
||||
bail_on_failure
|
||||
system 'make'
|
||||
bail_on_failure
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Check that the current HEAD is tagged'
|
||||
task :check_tag do
|
||||
unless system 'git describe --exact-match HEAD 2> /dev/null'
|
||||
warn 'current HEAD is not tagged'
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Run checks prior to release'
|
||||
task :prerelease => ['make', 'spec', :vimball, :check_tag]
|
||||
|
||||
namespace :upload do
|
||||
desc 'Upload current vimball to Amazon S3'
|
||||
task :s3 => :vimball do
|
||||
sh 'aws --curl-options=--insecure put ' +
|
||||
"s3.wincent.com/command-t/releases/command-t-#{version}.vba " +
|
||||
"command-t-#{version}.vba"
|
||||
sh 'aws --curl-options=--insecure put ' +
|
||||
"s3.wincent.com/command-t/releases/command-t-#{version}.vba?acl " +
|
||||
'--public'
|
||||
end
|
||||
|
||||
desc 'Upload current vimball to www.vim.org'
|
||||
task :vim => :vimball do
|
||||
prepare_release_notes
|
||||
sh "vendor/vimscriptuploader/vimscriptuploader.rb \
|
||||
--id 3025 \
|
||||
--file command-t-#{version}.vba \
|
||||
--message-file .release-notes.txt \
|
||||
--version #{version} \
|
||||
--config ~/.vim_org.yml \
|
||||
.vim_org.yml"
|
||||
end
|
||||
|
||||
desc 'Upload current vimball everywhere'
|
||||
task :all => [ :s3, :vim ]
|
||||
end
|
||||
|
||||
desc 'Create the ruby gem package'
|
||||
task :gem => :check_tag do
|
||||
sh "gem build command-t.gemspec"
|
||||
end
|
||||
|
||||
desc 'Push gem to Gemcutter ("gem push")'
|
||||
task :push => :gem do
|
||||
sh "gem push command-t-#{rubygems_version}.gem"
|
||||
end
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2014 Vít Ondruch <v.ondruch@gmail.com> -->
|
||||
<component type="addon">
|
||||
<id>vim-command-t</id>
|
||||
<extends>gvim.desktop</extends>
|
||||
<name>command-t</name>
|
||||
<summary>Provides an extremely fast, intuitive mechanism for opening files with a minimal number of keystrokes</summary>
|
||||
<url type="homepage">https://wincent.com/products/command-t</url>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>BSD</project_license>
|
||||
<updatecontact>v.ondruch@gmail.com</updatecontact>
|
||||
</component>
|
|
@ -1,180 +0,0 @@
|
|||
" Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
" Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
if exists("g:command_t_autoloaded") || &cp
|
||||
finish
|
||||
endif
|
||||
let g:command_t_autoloaded = 1
|
||||
|
||||
function s:CommandTRubyWarning()
|
||||
echohl WarningMsg
|
||||
echo "command-t.vim requires Vim to be compiled with Ruby support"
|
||||
echo "For more information type: :help command-t"
|
||||
echohl none
|
||||
endfunction
|
||||
|
||||
function commandt#CommandTShowBufferFinder()
|
||||
if has('ruby')
|
||||
ruby $command_t.show_buffer_finder
|
||||
else
|
||||
call s:CommandTRubyWarning()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function commandt#CommandTShowFileFinder(arg)
|
||||
if has('ruby')
|
||||
ruby $command_t.show_file_finder
|
||||
else
|
||||
call s:CommandTRubyWarning()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function commandt#CommandTShowJumpFinder()
|
||||
if has('ruby')
|
||||
ruby $command_t.show_jump_finder
|
||||
else
|
||||
call s:CommandTRubyWarning()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function commandt#CommandTShowMRUFinder()
|
||||
if has('ruby')
|
||||
ruby $command_t.show_mru_finder
|
||||
else
|
||||
call s:CommandTRubyWarning()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function commandt#CommandTShowTagFinder()
|
||||
if has('ruby')
|
||||
ruby $command_t.show_tag_finder
|
||||
else
|
||||
call s:CommandTRubyWarning()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function commandt#CommandTFlush()
|
||||
if has('ruby')
|
||||
ruby $command_t.flush
|
||||
else
|
||||
call s:CommandTRubyWarning()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function commandt#CommandTLoad()
|
||||
if !has('ruby')
|
||||
call s:CommandTRubyWarning()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
if !has('ruby')
|
||||
finish
|
||||
endif
|
||||
|
||||
function CommandTListMatches()
|
||||
ruby $command_t.list_matches
|
||||
endfunction
|
||||
|
||||
function CommandTHandleKey(arg)
|
||||
ruby $command_t.handle_key
|
||||
endfunction
|
||||
|
||||
function CommandTBackspace()
|
||||
ruby $command_t.backspace
|
||||
endfunction
|
||||
|
||||
function CommandTDelete()
|
||||
ruby $command_t.delete
|
||||
endfunction
|
||||
|
||||
function CommandTAcceptSelection()
|
||||
ruby $command_t.accept_selection
|
||||
endfunction
|
||||
|
||||
function CommandTAcceptSelectionTab()
|
||||
ruby $command_t.accept_selection :command => $command_t.tab_command
|
||||
endfunction
|
||||
|
||||
function CommandTAcceptSelectionSplit()
|
||||
ruby $command_t.accept_selection :command => $command_t.split_command
|
||||
endfunction
|
||||
|
||||
function CommandTAcceptSelectionVSplit()
|
||||
ruby $command_t.accept_selection :command => $command_t.vsplit_command
|
||||
endfunction
|
||||
|
||||
function CommandTQuickfix()
|
||||
ruby $command_t.quickfix
|
||||
endfunction
|
||||
|
||||
function CommandTRefresh()
|
||||
ruby $command_t.refresh
|
||||
endfunction
|
||||
|
||||
function CommandTToggleFocus()
|
||||
ruby $command_t.toggle_focus
|
||||
endfunction
|
||||
|
||||
function CommandTCancel()
|
||||
ruby $command_t.cancel
|
||||
endfunction
|
||||
|
||||
function CommandTSelectNext()
|
||||
ruby $command_t.select_next
|
||||
endfunction
|
||||
|
||||
function CommandTSelectPrev()
|
||||
ruby $command_t.select_prev
|
||||
endfunction
|
||||
|
||||
function CommandTClear()
|
||||
ruby $command_t.clear
|
||||
endfunction
|
||||
|
||||
function CommandTClearPrevWord()
|
||||
ruby $command_t.clear_prev_word
|
||||
endfunction
|
||||
|
||||
function CommandTCursorLeft()
|
||||
ruby $command_t.cursor_left
|
||||
endfunction
|
||||
|
||||
function CommandTCursorRight()
|
||||
ruby $command_t.cursor_right
|
||||
endfunction
|
||||
|
||||
function CommandTCursorEnd()
|
||||
ruby $command_t.cursor_end
|
||||
endfunction
|
||||
|
||||
function CommandTCursorStart()
|
||||
ruby $command_t.cursor_start
|
||||
endfunction
|
||||
|
||||
" note that we only start tracking buffers from first (autoloaded) use of Command-T
|
||||
augroup CommandTMRUBuffer
|
||||
autocmd BufEnter * ruby CommandT::MRU.touch
|
||||
autocmd BufDelete * ruby CommandT::MRU.delete
|
||||
augroup END
|
||||
|
||||
ruby << EOF
|
||||
# require Ruby files
|
||||
begin
|
||||
require 'command-t'
|
||||
$command_t = CommandT::Controller.new
|
||||
rescue LoadError
|
||||
load_path_modified = false
|
||||
::VIM::evaluate('&runtimepath').to_s.split(',').each do |path|
|
||||
lib = "#{path}/ruby"
|
||||
if !$LOAD_PATH.include?(lib) && File.exist?(lib)
|
||||
$LOAD_PATH << lib
|
||||
load_path_modified = true
|
||||
end
|
||||
end
|
||||
retry if load_path_modified
|
||||
|
||||
# could get here if C extension was not compiled, or was compiled
|
||||
# for the wrong architecture or Ruby version
|
||||
$command_t = CommandT::Stub.new
|
||||
end
|
||||
EOF
|
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# Copyright 2013-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
lib = File.expand_path('../../ruby', File.dirname(__FILE__))
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
|
||||
require 'command-t/ext'
|
||||
require 'command-t/util'
|
||||
require 'benchmark'
|
||||
require 'ostruct'
|
||||
require 'yaml'
|
||||
|
||||
yaml = File.expand_path('../../data/benchmark.yml', File.dirname(__FILE__))
|
||||
data = YAML.load_file(yaml)
|
||||
threads = CommandT::Util.processor_count
|
||||
|
||||
puts "Starting benchmark run (PID: #{Process.pid})"
|
||||
|
||||
Benchmark.bmbm do |b|
|
||||
data['tests'].each do |test|
|
||||
scanner = OpenStruct.new(:paths => test['paths'])
|
||||
matcher = CommandT::Matcher.new(scanner)
|
||||
b.report(test['name']) do
|
||||
test['times'].times do
|
||||
test['queries'].each do |query|
|
||||
matcher.sorted_matches_for(query, :threads => threads)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
lib = File.expand_path('../../ruby', File.dirname(__FILE__))
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
|
||||
require 'command-t/ext'
|
||||
require 'benchmark'
|
||||
require 'json'
|
||||
require 'pathname'
|
||||
require 'socket'
|
||||
|
||||
puts "Starting benchmark run (PID: #{Process.pid})"
|
||||
|
||||
TEST_TIMES = 10
|
||||
|
||||
Benchmark.bmbm do |b|
|
||||
b.report('watchman JSON') do
|
||||
TEST_TIMES.times do
|
||||
sockname = JSON[%x{watchman get-sockname}]['sockname']
|
||||
raise unless $?.exitstatus.zero?
|
||||
UNIXSocket.open(sockname) do |s|
|
||||
root = Pathname.new(ENV['PWD']).realpath
|
||||
s.puts JSON.generate(['watch-list'])
|
||||
if !JSON[s.gets]['roots'].include?(root)
|
||||
# this path isn't being watched yet; try to set up watch
|
||||
s.puts JSON.generate(['watch', root])
|
||||
|
||||
# root_restrict_files setting may prevent Watchman from working
|
||||
raise if JSON[s.gets].has_key?('error')
|
||||
end
|
||||
|
||||
s.puts JSON.generate(['query', root, {
|
||||
'expression' => ['type', 'f'],
|
||||
'fields' => ['name'],
|
||||
}])
|
||||
paths = JSON[s.gets]
|
||||
|
||||
# could return error if watch is removed
|
||||
raise if paths.has_key?('error')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
b.report('watchman binary') do
|
||||
TEST_TIMES.times do
|
||||
sockname = CommandT::Watchman::Utils.load(
|
||||
%x{watchman --output-encoding=bser get-sockname}
|
||||
)['sockname']
|
||||
raise unless $?.exitstatus.zero?
|
||||
|
||||
UNIXSocket.open(sockname) do |socket|
|
||||
root = Pathname.new(ENV['PWD']).realpath.to_s
|
||||
roots = CommandT::Watchman::Utils.query(['watch-list'], socket)['roots']
|
||||
if !roots.include?(root)
|
||||
# this path isn't being watched yet; try to set up watch
|
||||
result = CommandT::Watchman::Utils.query(['watch', root], socket)
|
||||
|
||||
# root_restrict_files setting may prevent Watchman from working
|
||||
raise if result.has_key?('error')
|
||||
end
|
||||
|
||||
query = ['query', root, {
|
||||
'expression' => ['type', 'f'],
|
||||
'fields' => ['name'],
|
||||
}]
|
||||
paths = CommandT::Watchman::Utils.query(query, socket)
|
||||
|
||||
# could return error if watch is removed
|
||||
raise if paths.has_key?('error')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,36 +0,0 @@
|
|||
Gem::Specification.new do |s|
|
||||
s.name = 'command-t'
|
||||
|
||||
# see note in the Rakefile about how intermediate version numbers
|
||||
# can break RubyGems
|
||||
v = `git describe --abbrev=0`.chomp
|
||||
s.version = v
|
||||
|
||||
s.authors = ['Greg Hurrell']
|
||||
s.email = 'greg@hurrell.net'
|
||||
|
||||
files =
|
||||
['README.txt', 'LICENSE', 'Gemfile', 'Rakefile'] +
|
||||
Dir.glob('{ruby,doc,plugin}/**/*')
|
||||
|
||||
files = files.reject { |f| f =~ /\.(rbc|o|log|plist|dSYM)/ }
|
||||
|
||||
s.files = files
|
||||
s.license = 'BSD'
|
||||
s.require_path = 'ruby'
|
||||
s.extensions = 'ruby/command-t/extconf.rb'
|
||||
|
||||
s.executables = []
|
||||
|
||||
s.has_rdoc = false
|
||||
s.homepage = 'https://wincent.com/products/command-t'
|
||||
|
||||
s.summary = 'The Command-T plug-in for VIM.'
|
||||
|
||||
s.description = <<-EOS
|
||||
Command-T provides a fast, intuitive mechanism for opening files with a
|
||||
minimal number of keystrokes. Its full functionality is only available when
|
||||
installed as a Vim plug-in, but it is also made available as a RubyGem so
|
||||
that other applications can make use of its searching algorithm.
|
||||
EOS
|
||||
end
|
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
|||
tags
|
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
|||
.
|
|
@ -1 +0,0 @@
|
|||
.
|
|
@ -1 +0,0 @@
|
|||
.
|
|
@ -1 +0,0 @@
|
|||
.
|
|
@ -1 +0,0 @@
|
|||
.
|
|
@ -1 +0,0 @@
|
|||
.
|
|
@ -1 +0,0 @@
|
|||
.
|
|
@ -1,23 +0,0 @@
|
|||
" Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
" Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
if exists("g:command_t_loaded") || &cp
|
||||
finish
|
||||
endif
|
||||
let g:command_t_loaded = 1
|
||||
|
||||
command CommandTBuffer call commandt#CommandTShowBufferFinder()
|
||||
command CommandTJump call commandt#CommandTShowJumpFinder()
|
||||
command CommandTMRU call commandt#CommandTShowMRUFinder()
|
||||
command CommandTTag call commandt#CommandTShowTagFinder()
|
||||
command -nargs=? -complete=dir CommandT call commandt#CommandTShowFileFinder(<q-args>)
|
||||
command CommandTFlush call commandt#CommandTFlush()
|
||||
command CommandTLoad call commandt#CommandTLoad()
|
||||
|
||||
if !hasmapto(':CommandT<CR>') && maparg('<Leader>t', 'n') == ''
|
||||
silent! nnoremap <unique> <silent> <Leader>t :CommandT<CR>
|
||||
endif
|
||||
|
||||
if !hasmapto(':CommandTBuffer<CR>') && maparg('<Leader>b', 'n') == ''
|
||||
silent! nnoremap <unique> <silent> <Leader>b :CommandTBuffer<CR>
|
||||
endif
|
|
@ -1,17 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
autoload :Controller, 'command-t/controller'
|
||||
autoload :Finder, 'command-t/finder'
|
||||
autoload :MRU, 'command-t/mru'
|
||||
autoload :MatchWindow, 'command-t/match_window'
|
||||
autoload :PathUtilities, 'command-t/path_utilities'
|
||||
autoload :Prompt, 'command-t/prompt'
|
||||
autoload :SCMUtilities, 'command-t/scm_utilities'
|
||||
autoload :Scanner, 'command-t/scanner'
|
||||
autoload :Settings, 'command-t/settings'
|
||||
autoload :Stub, 'command-t/stub'
|
||||
autoload :Util, 'command-t/util'
|
||||
autoload :VIM, 'command-t/vim'
|
||||
end # module CommandT
|
|
@ -1,6 +0,0 @@
|
|||
Makefile
|
||||
*.o
|
||||
*.log
|
||||
ext.*
|
||||
!ext.c
|
||||
!ext.h
|
|
@ -1,430 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Controller
|
||||
include PathUtilities
|
||||
include SCMUtilities
|
||||
|
||||
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) || VIM::pwd
|
||||
when 'dir'
|
||||
@path = nearest_ancestor(VIM::pwd, scm_markers) || VIM::pwd
|
||||
else
|
||||
@path = VIM::pwd
|
||||
end
|
||||
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 current_buffer_visible_in_other_window
|
||||
count = (0...::VIM::Window.count).to_a.inject(0) do |acc, i|
|
||||
acc += 1 if ::VIM::Window[i].buffer.number == $curbuf.number
|
||||
acc
|
||||
end
|
||||
count > 1
|
||||
end
|
||||
|
||||
def default_open_command
|
||||
if !VIM::get_bool('&modified') ||
|
||||
VIM::get_bool('&hidden') ||
|
||||
VIM::get_bool('&autowriteall') && !VIM::get_bool('&readonly') ||
|
||||
current_buffer_visible_in_other_window
|
||||
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::Finder::BufferFinder.new
|
||||
end
|
||||
|
||||
def mru_finder
|
||||
@mru_finder ||= CommandT::Finder::MRUBufferFinder.new
|
||||
end
|
||||
|
||||
def file_finder
|
||||
@file_finder ||= CommandT::Finder::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::Finder::JumpFinder.new
|
||||
end
|
||||
|
||||
def tag_finder
|
||||
@tag_finder ||= CommandT::Finder::TagFinder.new \
|
||||
:include_filenames => VIM::get_bool('g:CommandTTagIncludeFilenames')
|
||||
end
|
||||
end # class Controller
|
||||
end # module CommandT
|
|
@ -1,4 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
CFLAGS += -Wall -Wextra -Wno-unused-parameter
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include "matcher.h"
|
||||
#include "watchman.h"
|
||||
|
||||
VALUE mCommandT = 0; // module CommandT
|
||||
VALUE cCommandTMatcher = 0; // class CommandT::Matcher
|
||||
VALUE mCommandTWatchman = 0; // module CommandT::Watchman
|
||||
VALUE mCommandTWatchmanUtils = 0; // module CommandT::Watchman::Utils
|
||||
|
||||
VALUE CommandT_option_from_hash(const char *option, VALUE hash)
|
||||
{
|
||||
VALUE key;
|
||||
if (NIL_P(hash))
|
||||
return Qnil;
|
||||
key = ID2SYM(rb_intern(option));
|
||||
if (rb_funcall(hash, rb_intern("has_key?"), 1, key) == Qtrue)
|
||||
return rb_hash_aref(hash, key);
|
||||
else
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
void Init_ext()
|
||||
{
|
||||
// module CommandT
|
||||
mCommandT = rb_define_module("CommandT");
|
||||
|
||||
// class CommandT::Matcher
|
||||
cCommandTMatcher = rb_define_class_under(mCommandT, "Matcher", rb_cObject);
|
||||
rb_define_method(cCommandTMatcher, "initialize", CommandTMatcher_initialize, -1);
|
||||
rb_define_method(cCommandTMatcher, "sorted_matches_for", CommandTMatcher_sorted_matches_for, -1);
|
||||
|
||||
// module CommandT::Watchman::Utils
|
||||
mCommandTWatchman = rb_define_module_under(mCommandT, "Watchman");
|
||||
mCommandTWatchmanUtils = rb_define_module_under(mCommandTWatchman, "Utils");
|
||||
rb_define_singleton_method(mCommandTWatchmanUtils, "load", CommandTWatchmanUtils_load, 1);
|
||||
rb_define_singleton_method(mCommandTWatchmanUtils, "dump", CommandTWatchmanUtils_dump, 1);
|
||||
rb_define_singleton_method(mCommandTWatchmanUtils, "query", CommandTWatchmanUtils_query, 2);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include <ruby.h>
|
||||
|
||||
extern VALUE mCommandT; // module CommandT
|
||||
extern VALUE cCommandTMatcher; // class CommandT::Matcher
|
||||
extern VALUE mCommandTWatchman; // module CommandT::Watchman
|
||||
extern VALUE mCommandTWatchmanUtils; // module CommandT::Watchman::Utils
|
||||
|
||||
// Encapsulates common pattern of checking for an option in an optional
|
||||
// options hash. The hash itself may be nil, but an exception will be
|
||||
// raised if it is not nil and not a hash.
|
||||
VALUE CommandT_option_from_hash(const char *option, VALUE hash);
|
||||
|
||||
// Debugging macro.
|
||||
#define ruby_inspect(obj) rb_funcall(rb_mKernel, rb_intern("p"), 1, obj)
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
begin
|
||||
require 'mkmf'
|
||||
rescue LoadError
|
||||
puts <<-DOC.gsub(/^\s+/, '')
|
||||
Unable to require "mkmf"; you may need to install Ruby development tools
|
||||
(depending on your system, a "ruby-dev"/"ruby-devel" package or similar).
|
||||
[exiting]
|
||||
DOC
|
||||
exit 1
|
||||
end
|
||||
|
||||
def header(item)
|
||||
unless find_header(item)
|
||||
puts "couldn't find #{item} (required)"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# mandatory headers
|
||||
header('float.h')
|
||||
header('ruby.h')
|
||||
header('stdlib.h')
|
||||
header('string.h')
|
||||
|
||||
# optional headers (for CommandT::Watchman::Utils)
|
||||
if have_header('fcntl.h') &&
|
||||
have_header('stdint.h') &&
|
||||
have_header('sys/errno.h') &&
|
||||
have_header('sys/socket.h')
|
||||
RbConfig::MAKEFILE_CONFIG['DEFS'] ||= ''
|
||||
RbConfig::MAKEFILE_CONFIG['DEFS'] += ' -DWATCHMAN_BUILD'
|
||||
|
||||
have_header('ruby/st.h') # >= 1.9; sets HAVE_RUBY_ST_H
|
||||
have_header('st.h') # 1.8; sets HAVE_ST_H
|
||||
end
|
||||
|
||||
# optional
|
||||
if RbConfig::CONFIG['THREAD_MODEL'] == 'pthread'
|
||||
have_library('pthread', 'pthread_create') # sets HAVE_PTHREAD_H if found
|
||||
end
|
||||
|
||||
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
||||
|
||||
create_makefile('ext')
|
|
@ -1,40 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'command-t/ext' # CommandT::Matcher, CommandT::Watchman::Utils
|
||||
|
||||
module CommandT
|
||||
# Encapsulates a Scanner instance (which builds up a list of available files
|
||||
# in a directory) and a Matcher instance (which selects from that list based
|
||||
# on a search string).
|
||||
#
|
||||
# Specialized subclasses use different kinds of scanners adapted for
|
||||
# different kinds of search (files, buffers).
|
||||
class Finder
|
||||
autoload :BufferFinder, 'command-t/finder/buffer_finder'
|
||||
autoload :FileFinder, 'command-t/finder/file_finder'
|
||||
autoload :JumpFinder, 'command-t/finder/jump_finder'
|
||||
autoload :MRUBufferFinder, 'command-t/finder/mru_buffer_finder'
|
||||
autoload :TagFinder, 'command-t/finder/tag_finder'
|
||||
|
||||
include PathUtilities
|
||||
|
||||
def initialize(path = Dir.pwd, options = {})
|
||||
raise RuntimeError, 'Subclass responsibility'
|
||||
end
|
||||
|
||||
# Options:
|
||||
# :limit (integer): limit the number of returned matches
|
||||
def sorted_matches_for(str, options = {})
|
||||
@matcher.sorted_matches_for str, options
|
||||
end
|
||||
|
||||
def open_selection(command, selection, options = {})
|
||||
::VIM::command "silent #{command} #{selection}"
|
||||
end
|
||||
|
||||
def path=(path)
|
||||
@scanner.path = path
|
||||
end
|
||||
end # class Finder
|
||||
end # CommandT
|
|
@ -1,13 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Finder
|
||||
class BufferFinder < Finder
|
||||
def initialize
|
||||
@scanner = Scanner::BufferScanner.new
|
||||
@matcher = Matcher.new @scanner, :always_show_dot_files => true
|
||||
end
|
||||
end # class BufferFinder
|
||||
end # class Finder
|
||||
end # CommandT
|
|
@ -1,29 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Finder
|
||||
class FileFinder < Finder
|
||||
def initialize(path = Dir.pwd, options = {})
|
||||
case options.delete(:scanner)
|
||||
when 'ruby', nil # ruby is the default
|
||||
@scanner = Scanner::FileScanner::RubyFileScanner.new(path, options)
|
||||
when 'find'
|
||||
@scanner = Scanner::FileScanner::FindFileScanner.new(path, options)
|
||||
when 'watchman'
|
||||
@scanner = Scanner::FileScanner::WatchmanFileScanner.new(path, options)
|
||||
when 'git'
|
||||
@scanner = Scanner::FileScanner::GitFileScanner.new(path, options)
|
||||
else
|
||||
raise ArgumentError, "unknown scanner type '#{options[:scanner]}'"
|
||||
end
|
||||
|
||||
@matcher = Matcher.new @scanner, options
|
||||
end
|
||||
|
||||
def flush
|
||||
@scanner.flush
|
||||
end
|
||||
end # class FileFinder
|
||||
end # class Finder
|
||||
end # module CommandT
|
|
@ -1,13 +0,0 @@
|
|||
# Copyright 2011-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Finder
|
||||
class JumpFinder < Finder
|
||||
def initialize
|
||||
@scanner = Scanner::JumpScanner.new
|
||||
@matcher = Matcher.new @scanner, :always_show_dot_files => true
|
||||
end
|
||||
end # class JumpFinder
|
||||
end # class Finder
|
||||
end # module CommandT
|
|
@ -1,28 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Finder
|
||||
class MRUBufferFinder < BufferFinder
|
||||
def initialize
|
||||
@scanner = Scanner::MRUBufferScanner.new
|
||||
@matcher = Matcher.new @scanner, :always_show_dot_files => true
|
||||
end
|
||||
|
||||
# Override sorted_matches_for to prevent MRU ordered matches from being
|
||||
# ordered alphabetically.
|
||||
def sorted_matches_for(str, options = {})
|
||||
matches = super(str, options.merge(:sort => false))
|
||||
|
||||
# take current buffer (by definition, the most recently used) and move it
|
||||
# to the end of the results
|
||||
if MRU.last &&
|
||||
relative_path_under_working_directory(MRU.last.name) == matches.first
|
||||
matches[1..-1] + [matches.first]
|
||||
else
|
||||
matches
|
||||
end
|
||||
end
|
||||
end # class MRUBufferFinder
|
||||
end # class Finder
|
||||
end # CommandT
|
|
@ -1,26 +0,0 @@
|
|||
# Copyright 2011-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Finder
|
||||
class TagFinder < Finder
|
||||
def initialize(options = {})
|
||||
@scanner = Scanner::TagScanner.new options
|
||||
@matcher = Matcher.new @scanner, :always_show_dot_files => true
|
||||
end
|
||||
|
||||
def open_selection(command, selection, options = {})
|
||||
if @scanner.include_filenames
|
||||
selection = selection[0, selection.index(':')]
|
||||
end
|
||||
|
||||
# open the tag and center the screen on it
|
||||
::VIM::command "silent! tag #{selection} | :normal zz"
|
||||
end
|
||||
|
||||
def flush
|
||||
@scanner.flush
|
||||
end
|
||||
end # class TagFinder
|
||||
end # class Finder
|
||||
end # module CommandT
|
|
@ -1,173 +0,0 @@
|
|||
// Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include <float.h> /* for DBL_MAX */
|
||||
#include "match.h"
|
||||
#include "ext.h"
|
||||
#include "ruby_compat.h"
|
||||
|
||||
// use a struct to make passing params during recursion easier
|
||||
typedef struct {
|
||||
char *haystack_p; // pointer to the path string to be searched
|
||||
long haystack_len; // length of same
|
||||
char *needle_p; // pointer to search string (needle)
|
||||
long needle_len; // length of same
|
||||
double max_score_per_char;
|
||||
int always_show_dot_files; // boolean
|
||||
int never_show_dot_files; // boolean
|
||||
int case_sensitive; // boolean
|
||||
double *memo; // memoization
|
||||
} matchinfo_t;
|
||||
|
||||
double recursive_match(matchinfo_t *m, // sharable meta-data
|
||||
long haystack_idx, // where in the path string to start
|
||||
long needle_idx, // where in the needle string to start
|
||||
long last_idx, // location of last matched character
|
||||
double score) // cumulative score so far
|
||||
{
|
||||
double score_for_char;
|
||||
double seen_score = 0; // remember best score seen via recursion
|
||||
int found;
|
||||
long i, j, distance;
|
||||
long memo_idx = haystack_idx;
|
||||
|
||||
// do we have a memoized result we can return?
|
||||
double memoized = m->memo[needle_idx * m->needle_len + memo_idx];
|
||||
if (memoized != DBL_MAX)
|
||||
return memoized;
|
||||
|
||||
// bail early if not enough room (left) in haystack for (rest of) needle
|
||||
if (m->haystack_len - haystack_idx < m->needle_len - needle_idx) {
|
||||
score = 0.0;
|
||||
goto memoize;
|
||||
}
|
||||
|
||||
for (i = needle_idx; i < m->needle_len; i++) {
|
||||
char c = m->needle_p[i];
|
||||
found = 0;
|
||||
|
||||
// similar to above, we'll stop iterating when we know we're too close
|
||||
// to the end of the string to possibly match
|
||||
for (j = haystack_idx;
|
||||
j <= m->haystack_len - (m->needle_len - i);
|
||||
j++, haystack_idx++) {
|
||||
char d = m->haystack_p[j];
|
||||
if (d == '.') {
|
||||
if (j == 0 || m->haystack_p[j - 1] == '/') { // this is a dot-file
|
||||
int dot_search = (i == 0 && c == '.'); // searching for a dot
|
||||
if (m->never_show_dot_files || (!dot_search && !m->always_show_dot_files)) {
|
||||
score = 0.0;
|
||||
goto memoize;
|
||||
}
|
||||
}
|
||||
} else if (d >= 'A' && d <= 'Z' && !m->case_sensitive) {
|
||||
d += 'a' - 'A'; // add 32 to downcase
|
||||
}
|
||||
|
||||
if (c == d) {
|
||||
found = 1;
|
||||
|
||||
// calculate score
|
||||
score_for_char = m->max_score_per_char;
|
||||
distance = j - last_idx;
|
||||
|
||||
if (distance > 1) {
|
||||
double factor = 1.0;
|
||||
char last = m->haystack_p[j - 1];
|
||||
char curr = m->haystack_p[j]; // case matters, so get again
|
||||
if (last == '/')
|
||||
factor = 0.9;
|
||||
else if (last == '-' ||
|
||||
last == '_' ||
|
||||
last == ' ' ||
|
||||
(last >= '0' && last <= '9'))
|
||||
factor = 0.8;
|
||||
else if (last >= 'a' && last <= 'z' &&
|
||||
curr >= 'A' && curr <= 'Z')
|
||||
factor = 0.8;
|
||||
else if (last == '.')
|
||||
factor = 0.7;
|
||||
else
|
||||
// if no "special" chars behind char, factor diminishes
|
||||
// as distance from last matched char increases
|
||||
factor = (1.0 / distance) * 0.75;
|
||||
score_for_char *= factor;
|
||||
}
|
||||
|
||||
if (++j < m->haystack_len) {
|
||||
// bump cursor one char to the right and
|
||||
// use recursion to try and find a better match
|
||||
double sub_score = recursive_match(m, j, i, last_idx, score);
|
||||
if (sub_score > seen_score)
|
||||
seen_score = sub_score;
|
||||
}
|
||||
|
||||
score += score_for_char;
|
||||
last_idx = haystack_idx++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
score = 0.0;
|
||||
goto memoize;
|
||||
}
|
||||
}
|
||||
|
||||
score = score > seen_score ? score : seen_score;
|
||||
|
||||
memoize:
|
||||
m->memo[needle_idx * m->needle_len + memo_idx] = score;
|
||||
return score;
|
||||
}
|
||||
|
||||
void calculate_match(VALUE str,
|
||||
VALUE needle,
|
||||
VALUE case_sensitive,
|
||||
VALUE always_show_dot_files,
|
||||
VALUE never_show_dot_files,
|
||||
match_t *out)
|
||||
{
|
||||
long i, max;
|
||||
double score;
|
||||
matchinfo_t m;
|
||||
m.haystack_p = RSTRING_PTR(str);
|
||||
m.haystack_len = RSTRING_LEN(str);
|
||||
m.needle_p = RSTRING_PTR(needle);
|
||||
m.needle_len = RSTRING_LEN(needle);
|
||||
m.max_score_per_char = (1.0 / m.haystack_len + 1.0 / m.needle_len) / 2;
|
||||
m.always_show_dot_files = always_show_dot_files == Qtrue;
|
||||
m.never_show_dot_files = never_show_dot_files == Qtrue;
|
||||
m.case_sensitive = case_sensitive;
|
||||
|
||||
// calculate score
|
||||
score = 1.0;
|
||||
|
||||
// special case for zero-length search string
|
||||
if (m.needle_len == 0) {
|
||||
|
||||
// filter out dot files
|
||||
if (!m.always_show_dot_files) {
|
||||
for (i = 0; i < m.haystack_len; i++) {
|
||||
char c = m.haystack_p[i];
|
||||
|
||||
if (c == '.' && (i == 0 || m.haystack_p[i - 1] == '/')) {
|
||||
score = 0.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m.haystack_len > 0) { // normal case
|
||||
|
||||
// prepare for memoization
|
||||
double memo[m.haystack_len * m.needle_len];
|
||||
for (i = 0, max = m.haystack_len * m.needle_len; i < max; i++)
|
||||
memo[i] = DBL_MAX;
|
||||
m.memo = memo;
|
||||
|
||||
score = recursive_match(&m, 0, 0, 0, 0.0);
|
||||
}
|
||||
|
||||
// final book-keeping
|
||||
out->path = str;
|
||||
out->score = score;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include <ruby.h>
|
||||
|
||||
// struct for representing an individual match
|
||||
typedef struct {
|
||||
VALUE path;
|
||||
double score;
|
||||
} match_t;
|
||||
|
||||
extern void calculate_match(VALUE str,
|
||||
VALUE needle,
|
||||
VALUE case_sensitive,
|
||||
VALUE always_show_dot_files,
|
||||
VALUE never_show_dot_files,
|
||||
match_t *out);
|
|
@ -1,441 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'ostruct'
|
||||
|
||||
module CommandT
|
||||
class MatchWindow
|
||||
SELECTION_MARKER = '> '
|
||||
MARKER_LENGTH = SELECTION_MARKER.length
|
||||
UNSELECTED_MARKER = ' ' * MARKER_LENGTH
|
||||
MH_START = '<commandt>'
|
||||
MH_END = '</commandt>'
|
||||
@@buffer = nil
|
||||
|
||||
def initialize(options = {})
|
||||
@highlight_color = options[:highlight_color] || 'PmenuSel'
|
||||
@min_height = options[:min_height]
|
||||
@prompt = options[:prompt]
|
||||
@reverse_list = options[:match_window_reverse]
|
||||
|
||||
# save existing window dimensions so we can restore them later
|
||||
@windows = (0..(::VIM::Window.count - 1)).map do |i|
|
||||
OpenStruct.new(
|
||||
:index => i,
|
||||
:height => ::VIM::Window[i].height,
|
||||
:width => ::VIM::Window[i].width
|
||||
)
|
||||
end
|
||||
|
||||
set 'timeout', true # ensure mappings timeout
|
||||
set 'hlsearch', false # don't highlight search strings
|
||||
set 'insertmode', false # don't make Insert mode the default
|
||||
set 'showcmd', false # don't show command info on last line
|
||||
set 'equalalways', false # don't auto-balance window sizes
|
||||
set 'timeoutlen', 0 # respond immediately to mappings
|
||||
set 'report', 9999 # don't show "X lines changed" reports
|
||||
set 'scrolloff', 0 # don't scroll near buffer edges
|
||||
set 'sidescroll', 0 # don't sidescroll in jumps
|
||||
set 'sidescrolloff', 0 # don't sidescroll automatically
|
||||
set 'updatetime', options[:debounce_interval]
|
||||
|
||||
# show match window
|
||||
split_location = options[:match_window_at_top] ? 'topleft' : 'botright'
|
||||
if @@buffer # still have buffer from last time
|
||||
::VIM::command "silent! #{split_location} #{@@buffer.number}sbuffer"
|
||||
raise "Can't re-open GoToFile buffer" unless $curbuf.number == @@buffer.number
|
||||
$curwin.height = 1
|
||||
else # creating match window for first time and set it up
|
||||
::VIM::command "silent! #{split_location} 1split GoToFile"
|
||||
set 'bufhidden', 'unload' # unload buf when no longer displayed
|
||||
set 'buftype', 'nofile' # buffer is not related to any file
|
||||
set 'modifiable', false # prevent manual edits
|
||||
set 'swapfile', false # don't create a swapfile
|
||||
set 'wrap', false # don't soft-wrap
|
||||
set 'number', false # don't show line numbers
|
||||
set 'list', false # don't use List mode (visible tabs etc)
|
||||
set 'foldcolumn', 0 # don't show a fold column at side
|
||||
set 'foldlevel', 99 # don't fold anything
|
||||
set 'cursorline', false # don't highlight line cursor is on
|
||||
set 'spell', false # spell-checking off
|
||||
set 'buflisted', false # don't show up in the buffer list
|
||||
set 'textwidth', 0 # don't hard-wrap (break long lines)
|
||||
|
||||
# don't show the color column
|
||||
set 'colorcolumn', 0 if VIM::exists?('+colorcolumn')
|
||||
|
||||
# don't show relative line numbers
|
||||
set 'relativenumber', false if VIM::exists?('+relativenumber')
|
||||
|
||||
# sanity check: make sure the buffer really was created
|
||||
raise "Can't find GoToFile buffer" unless $curbuf.name.match /GoToFile\z/
|
||||
@@buffer = $curbuf
|
||||
end
|
||||
|
||||
# syntax coloring
|
||||
if VIM::has?('syntax')
|
||||
::VIM::command "syntax match CommandTSelection \"^#{SELECTION_MARKER}.\\+$\""
|
||||
::VIM::command 'syntax match CommandTNoEntries "^-- NO MATCHES --$"'
|
||||
::VIM::command 'syntax match CommandTNoEntries "^-- NO SUCH FILE OR DIRECTORY --$"'
|
||||
set 'synmaxcol', 9999
|
||||
|
||||
if VIM::has?('conceal')
|
||||
set 'conceallevel', 2
|
||||
set 'concealcursor', 'nvic'
|
||||
::VIM::command 'syntax region CommandTCharMatched ' \
|
||||
"matchgroup=CommandTCharMatched start=+#{MH_START}+ " \
|
||||
"matchgroup=CommandTCharMatchedEnd end=+#{MH_END}+ concealends"
|
||||
::VIM::command 'highlight def CommandTCharMatched ' \
|
||||
'term=bold,underline cterm=bold,underline ' \
|
||||
'gui=bold,underline'
|
||||
end
|
||||
|
||||
::VIM::command "highlight link CommandTSelection #{@highlight_color}"
|
||||
::VIM::command 'highlight link CommandTNoEntries Error'
|
||||
|
||||
# hide cursor
|
||||
@cursor_highlight = get_cursor_highlight
|
||||
hide_cursor
|
||||
end
|
||||
|
||||
# perform cleanup using an autocmd to ensure we don't get caught out
|
||||
# by some unexpected means of dismissing or leaving the Command-T window
|
||||
# (eg. <C-W q>, <C-W k> etc)
|
||||
::VIM::command 'autocmd! * <buffer>'
|
||||
::VIM::command 'autocmd BufLeave <buffer> silent! ruby $command_t.leave'
|
||||
::VIM::command 'autocmd BufUnload <buffer> silent! ruby $command_t.unload'
|
||||
|
||||
@has_focus = false
|
||||
@abbrev = ''
|
||||
@window = $curwin
|
||||
end
|
||||
|
||||
def close
|
||||
# Unlisted buffers like those provided by Netrw, NERDTree and Vim's help
|
||||
# don't actually appear in the buffer list; if they are the only such
|
||||
# buffers present when Command-T is invoked (for example, when invoked
|
||||
# immediately after starting Vim with a directory argument, like `vim .`)
|
||||
# then performing the normal clean-up will yield an "E90: Cannot unload
|
||||
# last buffer" error. We can work around that by doing a :quit first.
|
||||
if ::VIM::Buffer.count == 0
|
||||
::VIM::command 'silent quit'
|
||||
end
|
||||
|
||||
# Workaround for upstream bug in Vim 7.3 on some platforms
|
||||
#
|
||||
# On some platforms, $curbuf.number always returns 0. One workaround is
|
||||
# to build Vim with --disable-largefile, but as this is producing lots of
|
||||
# support requests, implement the following fallback to the buffer name
|
||||
# instead, at least until upstream gets fixed.
|
||||
#
|
||||
# For more details, see: https://wincent.com/issues/1617
|
||||
if $curbuf.number == 0
|
||||
# use bwipeout as bunload fails if passed the name of a hidden buffer
|
||||
::VIM::command 'silent! bwipeout! GoToFile'
|
||||
@@buffer = nil
|
||||
else
|
||||
::VIM::command "silent! bunload! #{@@buffer.number}"
|
||||
end
|
||||
end
|
||||
|
||||
def leave
|
||||
close
|
||||
unload
|
||||
end
|
||||
|
||||
def unload
|
||||
restore_window_dimensions
|
||||
@settings.restore
|
||||
@prompt.dispose
|
||||
show_cursor
|
||||
end
|
||||
|
||||
def add!(char)
|
||||
@abbrev += char
|
||||
end
|
||||
|
||||
def backspace!
|
||||
@abbrev.chop!
|
||||
end
|
||||
|
||||
def select_next
|
||||
@reverse_list ? _prev : _next
|
||||
end
|
||||
|
||||
def select_prev
|
||||
@reverse_list ? _next : _prev
|
||||
end
|
||||
|
||||
def matches=(matches)
|
||||
if matches != @matches
|
||||
@matches = matches
|
||||
@selection = 0
|
||||
print_matches
|
||||
move_cursor_to_selected_line
|
||||
end
|
||||
end
|
||||
|
||||
def focus
|
||||
unless @has_focus
|
||||
@has_focus = true
|
||||
if VIM::has?('syntax')
|
||||
::VIM::command 'highlight link CommandTSelection Search'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unfocus
|
||||
if @has_focus
|
||||
@has_focus = false
|
||||
if VIM::has?('syntax')
|
||||
::VIM::command "highlight link CommandTSelection #{@highlight_color}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find(char)
|
||||
# is this a new search or the continuation of a previous one?
|
||||
now = Time.now
|
||||
if @last_key_time.nil? || @last_key_time < (now - 0.5)
|
||||
@find_string = char
|
||||
else
|
||||
@find_string += char
|
||||
end
|
||||
@last_key_time = now
|
||||
|
||||
# see if there's anything up ahead that matches
|
||||
matches = @reverse_list ? @matches.reverse : @matches
|
||||
matches.each_with_index do |match, idx|
|
||||
if match[0, @find_string.length].casecmp(@find_string) == 0
|
||||
old_selection = @selection
|
||||
@selection = @reverse_list ? matches.length - idx - 1 : idx
|
||||
print_match(old_selection) # redraw old selection (removes marker)
|
||||
print_match(@selection) # redraw new selection (adds marker)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the currently selected item as a String.
|
||||
def selection
|
||||
@matches[@selection]
|
||||
end
|
||||
|
||||
def print_no_such_file_or_directory
|
||||
print_error 'NO SUCH FILE OR DIRECTORY'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _next
|
||||
if @selection < [@window.height, @matches.length].min - 1
|
||||
@selection += 1
|
||||
print_match(@selection - 1) # redraw old selection (removes marker)
|
||||
print_match(@selection) # redraw new selection (adds marker)
|
||||
move_cursor_to_selected_line
|
||||
end
|
||||
end
|
||||
|
||||
def _prev
|
||||
if @selection > 0
|
||||
@selection -= 1
|
||||
print_match(@selection + 1) # redraw old selection (removes marker)
|
||||
print_match(@selection) # redraw new selection (adds marker)
|
||||
move_cursor_to_selected_line
|
||||
end
|
||||
end
|
||||
|
||||
# Translate from a 0-indexed match index to a 1-indexed Vim line number.
|
||||
# Also takes into account reversed listings.
|
||||
def line(match_index)
|
||||
@reverse_list ? @window.height - match_index : match_index + 1
|
||||
end
|
||||
|
||||
def set(setting, value)
|
||||
@settings ||= Settings.new
|
||||
@settings.set(setting, value)
|
||||
end
|
||||
|
||||
def move_cursor_to_selected_line
|
||||
# on some non-GUI terminals, the cursor doesn't hide properly
|
||||
# so we move the cursor to prevent it from blinking away in the
|
||||
# upper-left corner in a distracting fashion
|
||||
@window.cursor = [line(@selection), 0]
|
||||
end
|
||||
|
||||
def print_error(msg)
|
||||
return unless VIM::Window.select(@window)
|
||||
unlock
|
||||
clear
|
||||
@window.height = [1, @min_height].min
|
||||
@@buffer[1] = "-- #{msg} --"
|
||||
lock
|
||||
end
|
||||
|
||||
def restore_window_dimensions
|
||||
# sort from tallest to shortest, tie-breaking on window width
|
||||
@windows.sort! do |a, b|
|
||||
order = b.height <=> a.height
|
||||
if order.zero?
|
||||
b.width <=> a.width
|
||||
else
|
||||
order
|
||||
end
|
||||
end
|
||||
|
||||
# starting with the tallest ensures that there are no constraints
|
||||
# preventing windows on the side of vertical splits from regaining
|
||||
# their original full size
|
||||
@windows.each do |w|
|
||||
# beware: window may be nil
|
||||
if window = ::VIM::Window[w.index]
|
||||
window.height = w.height
|
||||
window.width = w.width
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def match_text_for_idx(idx)
|
||||
match = truncated_match @matches[idx].to_s
|
||||
if idx == @selection
|
||||
prefix = SELECTION_MARKER
|
||||
suffix = padding_for_selected_match match
|
||||
else
|
||||
if VIM::has?('syntax') && VIM::has?('conceal')
|
||||
match = match_with_syntax_highlight match
|
||||
end
|
||||
prefix = UNSELECTED_MARKER
|
||||
suffix = ''
|
||||
end
|
||||
prefix + match + suffix
|
||||
end
|
||||
|
||||
# Highlight matching characters within the matched string.
|
||||
#
|
||||
# Note that this is only approximate; it will highlight the first matching
|
||||
# instances within the string, which may not actually be the instances that
|
||||
# were used by the matching/scoring algorithm to determine the best score
|
||||
# for the match.
|
||||
#
|
||||
def match_with_syntax_highlight(match)
|
||||
highlight_chars = @prompt.abbrev.downcase.scan(/./mu)
|
||||
match.scan(/./mu).inject([]) do |output, char|
|
||||
if char.downcase == highlight_chars.first
|
||||
highlight_chars.shift
|
||||
output.concat [MH_START, char, MH_END]
|
||||
else
|
||||
output << char
|
||||
end
|
||||
end.join
|
||||
end
|
||||
|
||||
# Print just the specified match.
|
||||
def print_match(idx)
|
||||
return unless VIM::Window.select(@window)
|
||||
unlock
|
||||
@@buffer[line(idx)] = match_text_for_idx idx
|
||||
lock
|
||||
end
|
||||
|
||||
def max_lines
|
||||
[1, VIM::Screen.lines - 5].max
|
||||
end
|
||||
|
||||
# Print all matches.
|
||||
def print_matches
|
||||
match_count = @matches.length
|
||||
if match_count == 0
|
||||
print_error 'NO MATCHES'
|
||||
else
|
||||
return unless VIM::Window.select(@window)
|
||||
unlock
|
||||
clear
|
||||
@window_width = @window.width # update cached value
|
||||
desired_lines = [match_count, @min_height].max
|
||||
desired_lines = [max_lines, desired_lines].min
|
||||
@window.height = desired_lines
|
||||
matches = []
|
||||
(0...@window.height).each do |idx|
|
||||
text = match_text_for_idx(idx)
|
||||
@reverse_list ? matches.unshift(text) : matches.push(text)
|
||||
end
|
||||
matches.each_with_index do |match, idx|
|
||||
if @@buffer.count > idx
|
||||
@@buffer[idx + 1] = match
|
||||
else
|
||||
@@buffer.append(idx, match)
|
||||
end
|
||||
end
|
||||
lock
|
||||
end
|
||||
end
|
||||
|
||||
# Prepare padding for match text (trailing spaces) so that selection
|
||||
# highlighting extends all the way to the right edge of the window.
|
||||
def padding_for_selected_match(str)
|
||||
len = str.length
|
||||
if len >= @window_width - MARKER_LENGTH
|
||||
''
|
||||
else
|
||||
' ' * (@window_width - MARKER_LENGTH - len)
|
||||
end
|
||||
end
|
||||
|
||||
# Convert "really/long/path" into "really...path" based on available
|
||||
# window width.
|
||||
def truncated_match(str)
|
||||
len = str.length
|
||||
available_width = @window_width - MARKER_LENGTH
|
||||
return str if len <= available_width
|
||||
left = (available_width / 2) - 1
|
||||
right = (available_width / 2) - 2 + (available_width % 2)
|
||||
str[0, left] + '...' + str[-right, right]
|
||||
end
|
||||
|
||||
def clear
|
||||
# range = % (whole buffer)
|
||||
# action = d (delete)
|
||||
# register = _ (black hole register, don't record deleted text)
|
||||
::VIM::command 'silent %d _'
|
||||
end
|
||||
|
||||
def get_cursor_highlight
|
||||
# there are 3 possible formats to check for, each needing to be
|
||||
# transformed in a certain way in order to reapply the highlight:
|
||||
# Cursor xxx guifg=bg guibg=fg -> :hi! Cursor guifg=bg guibg=fg
|
||||
# Cursor xxx links to SomethingElse -> :hi! link Cursor SomethingElse
|
||||
# Cursor xxx cleared -> :hi! clear Cursor
|
||||
highlight = VIM::capture 'silent! 0verbose highlight Cursor'
|
||||
|
||||
if highlight =~ /^Cursor\s+xxx\s+links to (\w+)/
|
||||
"link Cursor #{$~[1]}"
|
||||
elsif highlight =~ /^Cursor\s+xxx\s+cleared/
|
||||
'clear Cursor'
|
||||
elsif highlight =~ /Cursor\s+xxx\s+(.+)/
|
||||
"Cursor #{$~[1]}"
|
||||
else # likely cause E411 Cursor highlight group not found
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def hide_cursor
|
||||
if @cursor_highlight
|
||||
::VIM::command 'highlight Cursor NONE'
|
||||
end
|
||||
end
|
||||
|
||||
def show_cursor
|
||||
if @cursor_highlight
|
||||
::VIM::command "highlight #{@cursor_highlight}"
|
||||
end
|
||||
end
|
||||
|
||||
def lock
|
||||
set 'modifiable', false
|
||||
end
|
||||
|
||||
def unlock
|
||||
set 'modifiable', true
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,232 +0,0 @@
|
|||
// Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include <stdlib.h> /* for qsort() */
|
||||
#include <string.h> /* for strncmp() */
|
||||
#include "matcher.h"
|
||||
#include "match.h"
|
||||
#include "ext.h"
|
||||
#include "ruby_compat.h"
|
||||
|
||||
// order matters; we want this to be evaluated only after ruby.h
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
#include <pthread.h> /* for pthread_create, pthread_join etc */
|
||||
#endif
|
||||
|
||||
// comparison function for use with qsort
|
||||
int cmp_alpha(const void *a, const void *b)
|
||||
{
|
||||
match_t a_match = *(match_t *)a;
|
||||
match_t b_match = *(match_t *)b;
|
||||
VALUE a_str = a_match.path;
|
||||
VALUE b_str = b_match.path;
|
||||
char *a_p = RSTRING_PTR(a_str);
|
||||
long a_len = RSTRING_LEN(a_str);
|
||||
char *b_p = RSTRING_PTR(b_str);
|
||||
long b_len = RSTRING_LEN(b_str);
|
||||
int order = 0;
|
||||
|
||||
if (a_len > b_len) {
|
||||
order = strncmp(a_p, b_p, b_len);
|
||||
if (order == 0)
|
||||
order = 1; // shorter string (b) wins
|
||||
} else if (a_len < b_len) {
|
||||
order = strncmp(a_p, b_p, a_len);
|
||||
if (order == 0)
|
||||
order = -1; // shorter string (a) wins
|
||||
} else {
|
||||
order = strncmp(a_p, b_p, a_len);
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
// comparison function for use with qsort
|
||||
int cmp_score(const void *a, const void *b)
|
||||
{
|
||||
match_t a_match = *(match_t *)a;
|
||||
match_t b_match = *(match_t *)b;
|
||||
|
||||
if (a_match.score > b_match.score)
|
||||
return -1; // a scores higher, a should appear sooner
|
||||
else if (a_match.score < b_match.score)
|
||||
return 1; // b scores higher, a should appear later
|
||||
else
|
||||
return cmp_alpha(a, b);
|
||||
}
|
||||
|
||||
VALUE CommandTMatcher_initialize(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE always_show_dot_files;
|
||||
VALUE never_show_dot_files;
|
||||
VALUE options;
|
||||
VALUE scanner;
|
||||
|
||||
// process arguments: 1 mandatory, 1 optional
|
||||
if (rb_scan_args(argc, argv, "11", &scanner, &options) == 1)
|
||||
options = Qnil;
|
||||
if (NIL_P(scanner))
|
||||
rb_raise(rb_eArgError, "nil scanner");
|
||||
|
||||
rb_iv_set(self, "@scanner", scanner);
|
||||
|
||||
// check optional options hash for overrides
|
||||
always_show_dot_files = CommandT_option_from_hash("always_show_dot_files", options);
|
||||
never_show_dot_files = CommandT_option_from_hash("never_show_dot_files", options);
|
||||
|
||||
rb_iv_set(self, "@always_show_dot_files", always_show_dot_files);
|
||||
rb_iv_set(self, "@never_show_dot_files", never_show_dot_files);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int thread_count;
|
||||
int thread_index;
|
||||
int case_sensitive;
|
||||
match_t *matches;
|
||||
long path_count;
|
||||
VALUE paths;
|
||||
VALUE abbrev;
|
||||
VALUE always_show_dot_files;
|
||||
VALUE never_show_dot_files;
|
||||
} thread_args_t;
|
||||
|
||||
void *match_thread(void *thread_args)
|
||||
{
|
||||
long i;
|
||||
thread_args_t *args = (thread_args_t *)thread_args;
|
||||
for (i = args->thread_index; i < args->path_count; i += args->thread_count) {
|
||||
VALUE path = RARRAY_PTR(args->paths)[i];
|
||||
calculate_match(path,
|
||||
args->abbrev,
|
||||
args->case_sensitive,
|
||||
args->always_show_dot_files,
|
||||
args->never_show_dot_files,
|
||||
&args->matches[i]);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
long i, limit, path_count, thread_count;
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
long err;
|
||||
pthread_t *threads;
|
||||
#endif
|
||||
match_t *matches;
|
||||
thread_args_t *thread_args;
|
||||
VALUE abbrev;
|
||||
VALUE case_sensitive;
|
||||
VALUE always_show_dot_files;
|
||||
VALUE limit_option;
|
||||
VALUE never_show_dot_files;
|
||||
VALUE options;
|
||||
VALUE paths;
|
||||
VALUE results;
|
||||
VALUE scanner;
|
||||
VALUE sort_option;
|
||||
VALUE threads_option;
|
||||
|
||||
// process arguments: 1 mandatory, 1 optional
|
||||
if (rb_scan_args(argc, argv, "11", &abbrev, &options) == 1)
|
||||
options = Qnil;
|
||||
if (NIL_P(abbrev))
|
||||
rb_raise(rb_eArgError, "nil abbrev");
|
||||
|
||||
// check optional options has for overrides
|
||||
case_sensitive = CommandT_option_from_hash("case_sensitive", options);
|
||||
limit_option = CommandT_option_from_hash("limit", options);
|
||||
threads_option = CommandT_option_from_hash("threads", options);
|
||||
sort_option = CommandT_option_from_hash("sort", options);
|
||||
|
||||
abbrev = StringValue(abbrev);
|
||||
if (case_sensitive != Qtrue)
|
||||
abbrev = rb_funcall(abbrev, rb_intern("downcase"), 0);
|
||||
|
||||
// get unsorted matches
|
||||
scanner = rb_iv_get(self, "@scanner");
|
||||
paths = rb_funcall(scanner, rb_intern("paths"), 0);
|
||||
always_show_dot_files = rb_iv_get(self, "@always_show_dot_files");
|
||||
never_show_dot_files = rb_iv_get(self, "@never_show_dot_files");
|
||||
|
||||
path_count = RARRAY_LEN(paths);
|
||||
matches = malloc(path_count * sizeof(match_t));
|
||||
if (!matches)
|
||||
rb_raise(rb_eNoMemError, "memory allocation failed");
|
||||
|
||||
thread_count = NIL_P(threads_option) ? 1 : NUM2LONG(threads_option);
|
||||
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
#define THREAD_THRESHOLD 1000 /* avoid the overhead of threading when search space is small */
|
||||
if (path_count < THREAD_THRESHOLD)
|
||||
thread_count = 1;
|
||||
threads = malloc(sizeof(pthread_t) * thread_count);
|
||||
if (!threads)
|
||||
rb_raise(rb_eNoMemError, "memory allocation failed");
|
||||
#endif
|
||||
|
||||
thread_args = malloc(sizeof(thread_args_t) * thread_count);
|
||||
if (!thread_args)
|
||||
rb_raise(rb_eNoMemError, "memory allocation failed");
|
||||
for (i = 0; i < thread_count; i++) {
|
||||
thread_args[i].thread_count = thread_count;
|
||||
thread_args[i].thread_index = i;
|
||||
thread_args[i].case_sensitive = case_sensitive == Qtrue;
|
||||
thread_args[i].matches = matches;
|
||||
thread_args[i].path_count = path_count;
|
||||
thread_args[i].paths = paths;
|
||||
thread_args[i].abbrev = abbrev;
|
||||
thread_args[i].always_show_dot_files = always_show_dot_files;
|
||||
thread_args[i].never_show_dot_files = never_show_dot_files;
|
||||
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
if (i == thread_count - 1) {
|
||||
#endif
|
||||
// for the last "worker", we'll just use the main thread
|
||||
(void)match_thread(&thread_args[i]);
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
} else {
|
||||
err = pthread_create(&threads[i], NULL, match_thread, (void *)&thread_args[i]);
|
||||
if (err != 0)
|
||||
rb_raise(rb_eSystemCallError, "pthread_create() failure (%d)", (int)err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
for (i = 0; i < thread_count - 1; i++) {
|
||||
err = pthread_join(threads[i], NULL);
|
||||
if (err != 0)
|
||||
rb_raise(rb_eSystemCallError, "pthread_join() failure (%d)", (int)err);
|
||||
}
|
||||
free(threads);
|
||||
#endif
|
||||
|
||||
if (NIL_P(sort_option) || sort_option == Qtrue) {
|
||||
if (RSTRING_LEN(abbrev) == 0 ||
|
||||
(RSTRING_LEN(abbrev) == 1 && RSTRING_PTR(abbrev)[0] == '.'))
|
||||
// alphabetic order if search string is only "" or "."
|
||||
qsort(matches, path_count, sizeof(match_t), cmp_alpha);
|
||||
else
|
||||
// for all other non-empty search strings, sort by score
|
||||
qsort(matches, path_count, sizeof(match_t), cmp_score);
|
||||
}
|
||||
|
||||
results = rb_ary_new();
|
||||
|
||||
limit = NIL_P(limit_option) ? 0 : NUM2LONG(limit_option);
|
||||
if (limit == 0)
|
||||
limit = path_count;
|
||||
for (i = 0; i < path_count && limit > 0; i++) {
|
||||
if (matches[i].score > 0.0) {
|
||||
rb_funcall(results, rb_intern("push"), 1, matches[i].path);
|
||||
limit--;
|
||||
}
|
||||
}
|
||||
|
||||
free(matches);
|
||||
return results;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include <ruby.h>
|
||||
|
||||
extern VALUE CommandTMatcher_initialize(int argc, VALUE *argv, VALUE self);
|
||||
extern VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self);
|
|
@ -1,43 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
# Maintains a stack of seen buffers in MRU (most recently used) order.
|
||||
module MRU
|
||||
class << self
|
||||
# The stack of used buffers in MRU order.
|
||||
def stack
|
||||
@stack ||= []
|
||||
end
|
||||
|
||||
# The (last) most recent buffer in the stack, if any.
|
||||
def last
|
||||
stack.last
|
||||
end
|
||||
|
||||
# Mark the current buffer as having been used, effectively moving it to
|
||||
# the top of the stack.
|
||||
def touch
|
||||
return unless ::VIM::evaluate('buflisted(%d)' % $curbuf.number) == 1
|
||||
return unless $curbuf.name
|
||||
|
||||
stack.delete $curbuf
|
||||
stack.push $curbuf
|
||||
end
|
||||
|
||||
# Mark a buffer as deleted, removing it from the stack.
|
||||
def delete
|
||||
# Note that $curbuf does not point to the buffer that is being deleted;
|
||||
# we need to use Vim's <abuf> for the correct buffer number.
|
||||
stack.delete_if do |b|
|
||||
b.number == ::VIM::evaluate('expand("<abuf>")').to_i
|
||||
end
|
||||
end
|
||||
|
||||
# Returns `true` if `buffer` has been used (ie. is present in the stack).
|
||||
def used?(buffer)
|
||||
stack.include?(buffer)
|
||||
end
|
||||
end
|
||||
end # module MRU
|
||||
end # module CommandT
|
|
@ -1,17 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
module PathUtilities
|
||||
|
||||
private
|
||||
|
||||
def relative_path_under_working_directory(path)
|
||||
# any path under the working directory will be specified as a relative
|
||||
# path to improve the readability of the buffer list etc
|
||||
pwd = File.expand_path(VIM::pwd) + '/'
|
||||
path.index(pwd) == 0 ? path[pwd.length..-1] : path
|
||||
end
|
||||
|
||||
end # module PathUtilities
|
||||
end # module CommandT
|
|
@ -1,161 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
# Abuse the status line as a prompt.
|
||||
class Prompt
|
||||
attr_accessor :abbrev
|
||||
|
||||
def initialize
|
||||
@abbrev = '' # abbreviation entered so far
|
||||
@col = 0 # cursor position
|
||||
@has_focus = false
|
||||
end
|
||||
|
||||
# Erase whatever is displayed in the prompt line,
|
||||
# effectively disposing of the prompt
|
||||
def dispose
|
||||
::VIM::command 'echo'
|
||||
::VIM::command 'redraw'
|
||||
end
|
||||
|
||||
# Clear any entered text.
|
||||
def clear!
|
||||
@abbrev = ''
|
||||
@col = 0
|
||||
redraw
|
||||
end
|
||||
|
||||
# Remove word before cursor
|
||||
def clear_prev_word!
|
||||
suffix_length = @abbrev.length - @col
|
||||
@abbrev.match(
|
||||
%r{
|
||||
(.*?) # prefix
|
||||
\w*\s* # word to clear
|
||||
(.{#{suffix_length}}) # suffix
|
||||
\z
|
||||
}x
|
||||
)
|
||||
@abbrev = $~[1] + $~[2]
|
||||
@col = @abbrev.length - suffix_length
|
||||
redraw
|
||||
end
|
||||
|
||||
# Insert a character at (before) the current cursor position.
|
||||
def add!(char)
|
||||
left, cursor, right = abbrev_segments
|
||||
@abbrev = left + char + cursor + right
|
||||
@col += 1
|
||||
redraw
|
||||
end
|
||||
|
||||
# Delete a character to the left of the current cursor position.
|
||||
def backspace!
|
||||
if @col > 0
|
||||
left, cursor, right = abbrev_segments
|
||||
@abbrev = left.chop! + cursor + right
|
||||
@col -= 1
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
# Delete a character at the current cursor position.
|
||||
def delete!
|
||||
if @col < @abbrev.length
|
||||
left, cursor, right = abbrev_segments
|
||||
@abbrev = left + right
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
def cursor_left
|
||||
if @col > 0
|
||||
@col -= 1
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
def cursor_right
|
||||
if @col < @abbrev.length
|
||||
@col += 1
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
def cursor_end
|
||||
if @col < @abbrev.length
|
||||
@col = @abbrev.length
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
def cursor_start
|
||||
if @col != 0
|
||||
@col = 0
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
def focus
|
||||
unless @has_focus
|
||||
@has_focus = true
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
def unfocus
|
||||
if @has_focus
|
||||
@has_focus = false
|
||||
redraw
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def redraw
|
||||
if @has_focus
|
||||
prompt_highlight = 'Comment'
|
||||
normal_highlight = 'None'
|
||||
cursor_highlight = 'Underlined'
|
||||
else
|
||||
prompt_highlight = 'NonText'
|
||||
normal_highlight = 'NonText'
|
||||
cursor_highlight = 'NonText'
|
||||
end
|
||||
left, cursor, right = abbrev_segments
|
||||
components = [prompt_highlight, '>>', 'None', ' ']
|
||||
components += [normal_highlight, left] unless left.empty?
|
||||
components += [cursor_highlight, cursor] unless cursor.empty?
|
||||
components += [normal_highlight, right] unless right.empty?
|
||||
components += [cursor_highlight, ' '] if cursor.empty?
|
||||
set_status *components
|
||||
end
|
||||
|
||||
# Returns the @abbrev string divided up into three sections, any of
|
||||
# which may actually be zero width, depending on the location of the
|
||||
# cursor:
|
||||
# - left segment (to left of cursor)
|
||||
# - cursor segment (character at cursor)
|
||||
# - right segment (to right of cursor)
|
||||
def abbrev_segments
|
||||
left = @abbrev[0, @col]
|
||||
cursor = @abbrev[@col, 1]
|
||||
right = @abbrev[(@col + 1)..-1] || ''
|
||||
[left, cursor, right]
|
||||
end
|
||||
|
||||
def set_status(*args)
|
||||
# see ':help :echo' for why forcing a redraw here helps
|
||||
# prevent the status line from getting inadvertantly cleared
|
||||
# after our echo commands
|
||||
::VIM::command 'redraw'
|
||||
while (highlight = args.shift) && (text = args.shift)
|
||||
text = VIM::escape_for_single_quotes text
|
||||
::VIM::command "echohl #{highlight}"
|
||||
::VIM::command "echon '#{text}'"
|
||||
end
|
||||
::VIM::command 'echohl None'
|
||||
end
|
||||
end # class Prompt
|
||||
end # module CommandT
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include <ruby.h>
|
||||
|
||||
// for compatibility with older versions of Ruby which don't declare RSTRING_PTR
|
||||
#ifndef RSTRING_PTR
|
||||
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
||||
#endif
|
||||
|
||||
// for compatibility with older versions of Ruby which don't declare RSTRING_LEN
|
||||
#ifndef RSTRING_LEN
|
||||
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
||||
#endif
|
||||
|
||||
// for compatibility with older versions of Ruby which don't declare RARRAY_PTR
|
||||
#ifndef RARRAY_PTR
|
||||
#define RARRAY_PTR(a) (RARRAY(a)->ptr)
|
||||
#endif
|
||||
|
||||
// for compatibility with older versions of Ruby which don't declare RARRAY_LEN
|
||||
#ifndef RARRAY_LEN
|
||||
#define RARRAY_LEN(a) (RARRAY(a)->len)
|
||||
#endif
|
||||
|
||||
// for compatibility with older versions of Ruby which don't declare RFLOAT_VALUE
|
||||
#ifndef RFLOAT_VALUE
|
||||
#define RFLOAT_VALUE(f) (RFLOAT(f)->value)
|
||||
#endif
|
|
@ -1,12 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
autoload :BufferScanner, 'command-t/scanner/buffer_scanner'
|
||||
autoload :FileScanner, 'command-t/scanner/file_scanner'
|
||||
autoload :JumpScanner, 'command-t/scanner/jump_scanner'
|
||||
autoload :MRUBufferScanner, 'command-t/scanner/mru_buffer_scanner'
|
||||
autoload :TagScanner, 'command-t/scanner/tag_scanner'
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,20 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
# Returns a list of all open buffers.
|
||||
class BufferScanner < Scanner
|
||||
include PathUtilities
|
||||
|
||||
def paths
|
||||
(0..(::VIM::Buffer.count - 1)).map do |n|
|
||||
buffer = ::VIM::Buffer[n]
|
||||
if buffer.name # beware, may be nil
|
||||
relative_path_under_working_directory buffer.name
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
end # class BufferScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,93 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
# Reads the current directory recursively for the paths to all regular files.
|
||||
#
|
||||
# This is an abstract superclass; the real work is done by subclasses which
|
||||
# obtain file listings via different strategies (for examples, see the
|
||||
# RubyFileScanner and FindFileScanner subclasses).
|
||||
class FileScanner < Scanner
|
||||
# Errors
|
||||
autoload :FileLimitExceeded, 'command-t/scanner/file_scanner/file_limit_exceeded'
|
||||
|
||||
# Subclasses
|
||||
autoload :FindFileScanner, 'command-t/scanner/file_scanner/find_file_scanner'
|
||||
autoload :GitFileScanner, 'command-t/scanner/file_scanner/git_file_scanner'
|
||||
autoload :RubyFileScanner, 'command-t/scanner/file_scanner/ruby_file_scanner'
|
||||
autoload :WatchmanFileScanner, 'command-t/scanner/file_scanner/watchman_file_scanner'
|
||||
|
||||
attr_accessor :path
|
||||
|
||||
def initialize(path = Dir.pwd, options = {})
|
||||
@paths = {}
|
||||
@paths_keys = []
|
||||
@path = path
|
||||
@max_depth = options[:max_depth] || 15
|
||||
@max_files = options[:max_files] || 30_000
|
||||
@max_caches = options[:max_caches] || 1
|
||||
@scan_dot_directories = options[:scan_dot_directories] || false
|
||||
@wild_ignore = options[:wild_ignore]
|
||||
@base_wild_ignore = wild_ignore
|
||||
end
|
||||
|
||||
def paths
|
||||
@paths[@path] ||= begin
|
||||
ensure_cache_under_limit
|
||||
@prefix_len = @path.chomp('/').length + 1
|
||||
set_wild_ignore { paths! }
|
||||
end
|
||||
end
|
||||
|
||||
def flush
|
||||
@paths = {}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def wild_ignore
|
||||
VIM::exists?('&wildignore') && ::VIM::evaluate('&wildignore').to_s
|
||||
end
|
||||
|
||||
def paths!
|
||||
raise RuntimeError, 'Subclass responsibility'
|
||||
end
|
||||
|
||||
def ensure_cache_under_limit
|
||||
# Ruby 1.8 doesn't have an ordered hash, so use a separate stack to
|
||||
# track and expire the oldest entry in the cache
|
||||
if @max_caches > 0 && @paths_keys.length >= @max_caches
|
||||
@paths.delete @paths_keys.shift
|
||||
end
|
||||
@paths_keys << @path
|
||||
end
|
||||
|
||||
def path_excluded?(path, prefix_len = @prefix_len)
|
||||
if apply_wild_ignore?
|
||||
# first strip common prefix (@path) from path to match VIM's behavior
|
||||
path = path[prefix_len..-1]
|
||||
path = VIM::escape_for_single_quotes path
|
||||
::VIM::evaluate("empty(expand(fnameescape('#{path}')))").to_i == 1
|
||||
end
|
||||
end
|
||||
|
||||
def has_custom_wild_ignore?
|
||||
@wild_ignore && !@wild_ignore.empty?
|
||||
end
|
||||
|
||||
# Used to skip expensive calls to `expand()` when there is no applicable
|
||||
# wildignore.
|
||||
def apply_wild_ignore?
|
||||
has_custom_wild_ignore? || @base_wild_ignore
|
||||
end
|
||||
|
||||
def set_wild_ignore(&block)
|
||||
::VIM::command("set wildignore=#{@wild_ignore}") if has_custom_wild_ignore?
|
||||
yield
|
||||
ensure
|
||||
::VIM::command("set wildignore=#{@base_wild_ignore}") if has_custom_wild_ignore?
|
||||
end
|
||||
end # class FileScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,10 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
class FileScanner
|
||||
class FileLimitExceeded < ::RuntimeError; end
|
||||
end # class FileScanner
|
||||
end # class Scanner
|
||||
end # module Command-T
|
|
@ -1,50 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'open3'
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
class FileScanner
|
||||
# A FileScanner which shells out to the `find` executable in order to scan.
|
||||
class FindFileScanner < FileScanner
|
||||
include PathUtilities
|
||||
|
||||
def paths!
|
||||
# temporarily set field separator to NUL byte; this setting is
|
||||
# respected by both `readlines` and `chomp!` below, and makes it easier
|
||||
# to parse the output of `find -print0`
|
||||
separator = $/
|
||||
$/ = "\x00"
|
||||
|
||||
unless @scan_dot_directories
|
||||
dot_directory_filter = [
|
||||
'-not', '-path', "#{@path}/.*/*", # top-level dot dir
|
||||
'-and', '-not', '-path', "#{@path}/*/.*/*" # lower-level dot dir
|
||||
]
|
||||
end
|
||||
|
||||
paths = []
|
||||
Open3.popen3(*([
|
||||
'find', '-L', # follow symlinks
|
||||
@path, # anchor search here
|
||||
'-maxdepth', @max_depth.to_s, # limit depth of DFS
|
||||
'-type', 'f', # only show regular files (not dirs etc)
|
||||
dot_directory_filter, # possibly skip out dot directories
|
||||
'-print0' # NUL-terminate results
|
||||
].flatten.compact)) do |stdin, stdout, stderr|
|
||||
counter = 1
|
||||
stdout.readlines.each do |line|
|
||||
next if path_excluded?(line.chomp!)
|
||||
paths << line[@prefix_len..-1]
|
||||
break if (counter += 1) > @max_files
|
||||
end
|
||||
end
|
||||
paths
|
||||
ensure
|
||||
$/ = separator
|
||||
end
|
||||
end # class FindFileScanner
|
||||
end # class FileScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,34 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
class FileScanner
|
||||
# Uses git ls-files to scan for files
|
||||
class GitFileScanner < FindFileScanner
|
||||
def paths!
|
||||
Dir.chdir(@path) do
|
||||
stdin, stdout, stderr = Open3.popen3(*[
|
||||
'git',
|
||||
'ls-files',
|
||||
'--exclude-standard',
|
||||
@path
|
||||
])
|
||||
|
||||
all_files = stdout.readlines.
|
||||
map { |path| path.chomp }.
|
||||
reject { |path| path_excluded?(path, 0) }.
|
||||
take(@max_files).
|
||||
to_a
|
||||
|
||||
# will fall back to find if not a git repository or there's an error
|
||||
stderr.gets ? super : all_files
|
||||
end
|
||||
rescue Errno::ENOENT => e
|
||||
# git executable not present and executable
|
||||
super
|
||||
end
|
||||
end # class GitFileScanner
|
||||
end # class FileScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,55 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
class FileScanner
|
||||
# Pure Ruby implementation of a file scanner.
|
||||
class RubyFileScanner < FileScanner
|
||||
def paths!
|
||||
accumulator = []
|
||||
@depth = 0
|
||||
@files = 0
|
||||
add_paths_for_directory(@path, accumulator)
|
||||
accumulator
|
||||
rescue FileLimitExceeded
|
||||
accumulator
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def looped_symlink?(path)
|
||||
if File.symlink?(path)
|
||||
target = File.expand_path(File.readlink(path), File.dirname(path))
|
||||
target.include?(@path) || @path.include?(target)
|
||||
end
|
||||
end
|
||||
|
||||
def add_paths_for_directory(dir, accumulator)
|
||||
Dir.foreach(dir) do |entry|
|
||||
next if ['.', '..'].include?(entry)
|
||||
path = File.join(dir, entry)
|
||||
unless path_excluded?(path)
|
||||
if File.file?(path)
|
||||
@files += 1
|
||||
raise FileLimitExceeded if @files > @max_files
|
||||
accumulator << path[@prefix_len..-1]
|
||||
elsif File.directory?(path)
|
||||
next if @depth >= @max_depth
|
||||
next if (entry.match(/\A\./) && !@scan_dot_directories)
|
||||
next if looped_symlink?(path)
|
||||
@depth += 1
|
||||
add_paths_for_directory(path, accumulator)
|
||||
@depth -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Errno::EACCES
|
||||
# skip over directories for which we don't have access
|
||||
rescue ArgumentError
|
||||
# skip over bad file names
|
||||
end
|
||||
end # class RubyFileScanner
|
||||
end # class FileScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,55 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'pathname'
|
||||
require 'socket'
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
class FileScanner
|
||||
# A FileScanner which delegates the heavy lifting to Watchman
|
||||
# (https://github.com/facebook/watchman); useful for very large hierarchies.
|
||||
#
|
||||
# Inherits from FindFileScanner so that it can fall back to it in the event
|
||||
# that Watchman isn't available or able to fulfil the request.
|
||||
class WatchmanFileScanner < FindFileScanner
|
||||
# Exception raised when Watchman is unavailable or unable to process the
|
||||
# requested path.
|
||||
class WatchmanUnavailable < RuntimeError; end
|
||||
|
||||
def paths!
|
||||
sockname = Watchman::Utils.load(
|
||||
%x{watchman --output-encoding=bser get-sockname}
|
||||
)['sockname']
|
||||
raise WatchmanUnavailable unless $?.exitstatus.zero?
|
||||
|
||||
UNIXSocket.open(sockname) do |socket|
|
||||
root = Pathname.new(@path).realpath.to_s
|
||||
roots = Watchman::Utils.query(['watch-list'], socket)['roots']
|
||||
if !roots.include?(root)
|
||||
# this path isn't being watched yet; try to set up watch
|
||||
result = Watchman::Utils.query(['watch', root], socket)
|
||||
|
||||
# root_restrict_files setting may prevent Watchman from working
|
||||
raise WatchmanUnavailable if result.has_key?('error')
|
||||
end
|
||||
|
||||
query = ['query', root, {
|
||||
'expression' => ['type', 'f'],
|
||||
'fields' => ['name'],
|
||||
}]
|
||||
paths = Watchman::Utils.query(query, socket)
|
||||
|
||||
# could return error if watch is removed
|
||||
raise WatchmanUnavailable if paths.has_key?('error')
|
||||
|
||||
paths['files']
|
||||
end
|
||||
end
|
||||
rescue Errno::ENOENT, WatchmanUnavailable
|
||||
# watchman executable not present, or unable to fulfil request
|
||||
super
|
||||
end # class WatchmanFileScanner
|
||||
end # class FileScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,32 +0,0 @@
|
|||
# Copyright 2011-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
# Returns a list of files in the jumplist.
|
||||
class JumpScanner < Scanner
|
||||
include PathUtilities
|
||||
|
||||
def paths
|
||||
jumps_with_filename = jumps.lines.select do |line|
|
||||
line_contains_filename?(line)
|
||||
end
|
||||
filenames = jumps_with_filename[1..-2].map do |line|
|
||||
relative_path_under_working_directory line.split[3]
|
||||
end
|
||||
|
||||
filenames.sort.uniq
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def line_contains_filename?(line)
|
||||
line.split.count > 3
|
||||
end
|
||||
|
||||
def jumps
|
||||
VIM::capture 'silent jumps'
|
||||
end
|
||||
end # class JumpScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,27 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
# Returns a list of all open buffers, sorted in MRU order.
|
||||
class MRUBufferScanner < BufferScanner
|
||||
include PathUtilities
|
||||
|
||||
def paths
|
||||
# Collect all buffers that have not been used yet.
|
||||
unused_buffers = (0..(::VIM::Buffer.count - 1)).map do |n|
|
||||
buffer = ::VIM::Buffer[n]
|
||||
buffer if buffer.name && !MRU.used?(buffer)
|
||||
end
|
||||
|
||||
# Combine all most recently used buffers and all unused buffers, and
|
||||
# return all listed buffer paths.
|
||||
(unused_buffers + MRU.stack).map do |buffer|
|
||||
if buffer && buffer.name
|
||||
relative_path_under_working_directory buffer.name
|
||||
end
|
||||
end.compact.reverse
|
||||
end
|
||||
end # class MRUBufferScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,33 +0,0 @@
|
|||
# Copyright 2011-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Scanner
|
||||
class TagScanner < Scanner
|
||||
attr_reader :include_filenames
|
||||
|
||||
def initialize(options = {})
|
||||
@include_filenames = options[:include_filenames] || false
|
||||
@cached_tags = nil
|
||||
end
|
||||
|
||||
def paths
|
||||
@cached_tags ||= taglist.map do |tag|
|
||||
path = tag['name']
|
||||
path << ":#{tag['filename']}" if @include_filenames
|
||||
path
|
||||
end.uniq.sort
|
||||
end
|
||||
|
||||
def flush
|
||||
@cached_tags = nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def taglist
|
||||
::VIM::evaluate 'taglist(".")'
|
||||
end
|
||||
end # class TagScanner
|
||||
end # class Scanner
|
||||
end # module CommandT
|
|
@ -1,22 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
module SCMUtilities
|
||||
|
||||
private
|
||||
|
||||
def nearest_ancestor(starting_directory, markers)
|
||||
path = File.expand_path(starting_directory)
|
||||
while !markers.
|
||||
map { |dir| File.join(path, dir) }.
|
||||
map { |dir| File.exist?(dir) }.
|
||||
any?
|
||||
next_path = File.expand_path(File.join(path, '..'))
|
||||
return nil if next_path == path
|
||||
path = next_path
|
||||
end
|
||||
path
|
||||
end
|
||||
end # module SCMUtilities
|
||||
end # module CommandT
|
|
@ -1,97 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
# Convenience class for saving and restoring global settings.
|
||||
class Settings
|
||||
# Settings which apply globally and so must be manually saved and restored
|
||||
GLOBAL_SETTINGS = %w[
|
||||
equalalways
|
||||
hlsearch
|
||||
insertmode
|
||||
report
|
||||
showcmd
|
||||
scrolloff
|
||||
sidescroll
|
||||
sidescrolloff
|
||||
timeout
|
||||
timeoutlen
|
||||
updatetime
|
||||
]
|
||||
|
||||
# Settings which can be made locally to the Command-T buffer or window
|
||||
LOCAL_SETTINGS = %w[
|
||||
bufhidden
|
||||
buflisted
|
||||
buftype
|
||||
colorcolumn
|
||||
concealcursor
|
||||
conceallevel
|
||||
cursorline
|
||||
foldcolumn
|
||||
foldlevel
|
||||
list
|
||||
modifiable
|
||||
number
|
||||
relativenumber
|
||||
spell
|
||||
swapfile
|
||||
synmaxcol
|
||||
textwidth
|
||||
wrap
|
||||
]
|
||||
|
||||
KNOWN_SETTINGS = GLOBAL_SETTINGS + LOCAL_SETTINGS
|
||||
|
||||
def initialize
|
||||
@settings = []
|
||||
end
|
||||
|
||||
def set(setting, value)
|
||||
raise "Unknown setting #{setting}" unless KNOWN_SETTINGS.include?(setting)
|
||||
|
||||
case value
|
||||
when TrueClass, FalseClass
|
||||
@settings.push([setting, VIM::get_bool("&#{setting}")]) if global?(setting)
|
||||
set_bool setting, value
|
||||
when Numeric
|
||||
@settings.push([setting, VIM::get_number("&#{setting}")]) if global?(setting)
|
||||
set_number setting, value
|
||||
when String
|
||||
@settings.push([setting, VIM::get_string("&#{setting}")]) if global?(setting)
|
||||
set_string setting, value
|
||||
end
|
||||
end
|
||||
|
||||
def restore
|
||||
@settings.each do |setting, value|
|
||||
case value
|
||||
when TrueClass, FalseClass
|
||||
set_bool setting, value
|
||||
when Numeric
|
||||
set_number setting, value
|
||||
when String
|
||||
set_string setting, value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def global?(setting)
|
||||
GLOBAL_SETTINGS.include?(setting)
|
||||
end
|
||||
|
||||
def set_bool(setting, value)
|
||||
command = global?(setting) ? 'set' : 'setlocal'
|
||||
setting = value ? setting : "no#{setting}"
|
||||
::VIM::command "#{command} #{setting}"
|
||||
end
|
||||
|
||||
def set_number(setting, value)
|
||||
command = global?(setting) ? 'set' : 'setlocal'
|
||||
::VIM::command "#{command} #{setting}=#{value}"
|
||||
end
|
||||
alias set_string set_number
|
||||
end # class Settings
|
||||
end # module CommandT
|
|
@ -1,31 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
class Stub
|
||||
@@patch_level = defined?(RUBY_PATCHLEVEL) ? RUBY_PATCHLEVEL : '[unknown]'
|
||||
@@load_error = ['command-t.vim could not load the C extension',
|
||||
'Please see INSTALLATION and TROUBLE-SHOOTING in the help',
|
||||
"Vim Ruby version: #{RUBY_VERSION}-p#{@@patch_level}",
|
||||
'For more information type: :help command-t']
|
||||
|
||||
[
|
||||
:flush,
|
||||
:show_buffer_finder,
|
||||
:show_file_finder,
|
||||
:show_jump_finder,
|
||||
:show_mru_finder,
|
||||
:show_tag_finder
|
||||
].each do |method|
|
||||
define_method(method) { warn *@@load_error }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def warn(*msg)
|
||||
::VIM::command 'echohl WarningMsg'
|
||||
msg.each { |m| ::VIM::command "echo '#{m}'" }
|
||||
::VIM::command 'echohl none'
|
||||
end
|
||||
end # class Stub
|
||||
end # module CommandT
|
|
@ -1,94 +0,0 @@
|
|||
# Copyright 2013-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'rbconfig'
|
||||
|
||||
module CommandT
|
||||
module Util
|
||||
class << self
|
||||
def processor_count
|
||||
@processor_count ||= begin
|
||||
count = processor_count!
|
||||
count = 1 if count < 1 # sanity check
|
||||
count = 32 if count > 32 # sanity check
|
||||
count
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This method derived from:
|
||||
#
|
||||
# https://github.com/grosser/parallel/blob/d11e4a3c8c1a/lib/parallel.rb
|
||||
#
|
||||
# Number of processors seen by the OS and used for process scheduling.
|
||||
#
|
||||
# * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
|
||||
# * BSD: /sbin/sysctl
|
||||
# * Cygwin: /proc/cpuinfo
|
||||
# * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
|
||||
# * HP-UX: /usr/sbin/ioscan
|
||||
# * IRIX: /usr/sbin/sysconf
|
||||
# * Linux: /proc/cpuinfo
|
||||
# * Minix 3+: /proc/cpuinfo
|
||||
# * Solaris: /usr/sbin/psrinfo
|
||||
# * Tru64 UNIX: /usr/sbin/psrinfo
|
||||
# * UnixWare: /usr/sbin/psrinfo
|
||||
#
|
||||
# Copyright (C) 2013 Michael Grosser <michael@grosser.it>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
def processor_count!
|
||||
os_name = RbConfig::CONFIG['target_os']
|
||||
if os_name =~ /mingw|mswin/
|
||||
require 'win32ole'
|
||||
result = WIN32OLE.connect('winmgmts://').ExecQuery(
|
||||
'select NumberOfLogicalProcessors from Win32_Processor')
|
||||
result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
|
||||
elsif File.readable?('/proc/cpuinfo')
|
||||
IO.read('/proc/cpuinfo').scan(/^processor/).size
|
||||
elsif File.executable?('/usr/bin/hwprefs')
|
||||
IO.popen(%w[/usr/bin/hwprefs thread_count]).read.to_i
|
||||
elsif File.executable?('/usr/sbin/psrinfo')
|
||||
IO.popen('/usr/sbin/psrinfo').read.scan(/^.*on-*line/).size
|
||||
elsif File.executable?('/usr/sbin/ioscan')
|
||||
IO.popen(%w[/usr/sbin/ioscan -kC processor]) do |out|
|
||||
out.read.scan(/^.*processor/).size
|
||||
end
|
||||
elsif File.executable?('/usr/sbin/pmcycles')
|
||||
IO.popen(%w[/usr/sbin/pmcycles -m]).read.count("\n")
|
||||
elsif File.executable?('/usr/sbin/lsdev')
|
||||
IO.popen(%w[/usr/sbin/lsdev -Cc processor -S 1]).read.count("\n")
|
||||
elsif File.executable?('/usr/sbin/sysconf') && os_name =~ /irix/i
|
||||
IO.popen(%w[/usr/sbin/sysconf NPROC_ONLN]).read.to_i
|
||||
elsif File.executable?('/usr/sbin/sysctl')
|
||||
IO.popen(%w[/usr/sbin/sysctl -n hw.ncpu]).read.to_i
|
||||
elsif File.executable?('/sbin/sysctl')
|
||||
IO.popen(%w[/sbin/sysctl -n hw.ncpu]).read.to_i
|
||||
else # unknown platform
|
||||
1
|
||||
end
|
||||
rescue
|
||||
1
|
||||
end
|
||||
end
|
||||
end # module Util
|
||||
end # module CommandT
|
|
@ -1,71 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
module VIM
|
||||
autoload :Screen, 'command-t/vim/screen'
|
||||
autoload :Window, 'command-t/vim/window'
|
||||
|
||||
class << self
|
||||
# Check for the existence of a feature such as "conceal" or "syntax".
|
||||
def has?(feature)
|
||||
::VIM::evaluate(%{has("#{feature}")}).to_i != 0
|
||||
end
|
||||
|
||||
# Check for the presence of a setting such as:
|
||||
#
|
||||
# - g:CommandTSmartCase (plug-in setting)
|
||||
# - &wildignore (Vim setting)
|
||||
# - +cursorcolumn (Vim setting, that works)
|
||||
#
|
||||
def exists?(str)
|
||||
::VIM::evaluate(%{exists("#{str}")}).to_i != 0
|
||||
end
|
||||
|
||||
def get_number(name)
|
||||
exists?(name) ? ::VIM::evaluate("#{name}").to_i : nil
|
||||
end
|
||||
|
||||
def get_bool(name)
|
||||
exists?(name) ? ::VIM::evaluate("#{name}").to_i != 0 : nil
|
||||
end
|
||||
|
||||
def get_string(name)
|
||||
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 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
|
||||
|
||||
def pwd
|
||||
::VIM::evaluate 'getcwd()'
|
||||
end
|
||||
|
||||
def current_file_dir
|
||||
::VIM::evaluate 'expand("%:p:h")'
|
||||
end
|
||||
|
||||
# Execute cmd, capturing the output into a variable and returning it.
|
||||
def capture(cmd)
|
||||
::VIM::command 'silent redir => g:command_t_captured_output'
|
||||
::VIM::command cmd
|
||||
::VIM::command 'silent redir END'
|
||||
::VIM::evaluate 'g:command_t_captured_output'
|
||||
end
|
||||
|
||||
# Escape a string for safe inclusion in a Vim single-quoted string
|
||||
# (single quotes escaped by doubling, everything else is literal)
|
||||
def escape_for_single_quotes(str)
|
||||
str.gsub "'", "''"
|
||||
end
|
||||
end
|
||||
end # module VIM
|
||||
end # module CommandT
|
|
@ -1,14 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
module VIM
|
||||
module Screen
|
||||
class << self
|
||||
def lines
|
||||
::VIM::evaluate('&lines').to_i
|
||||
end
|
||||
end
|
||||
end # module Screen
|
||||
end # module VIM
|
||||
end # module CommandT
|
|
@ -1,20 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
module CommandT
|
||||
module VIM
|
||||
module Window
|
||||
class << self
|
||||
def select(window)
|
||||
return true if $curwin == window
|
||||
initial = $curwin
|
||||
while true do
|
||||
::VIM::command 'wincmd w' # cycle through windows
|
||||
return true if $curwin == window # have selected desired window
|
||||
return false if $curwin == initial # have already looped through all
|
||||
end
|
||||
end
|
||||
end
|
||||
end # module Window
|
||||
end # module VIM
|
||||
end # module CommandT
|
|
@ -1,660 +0,0 @@
|
|||
// Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include "watchman.h"
|
||||
|
||||
#ifdef WATCHMAN_BUILD
|
||||
|
||||
#if defined(HAVE_RUBY_ST_H)
|
||||
#include <ruby/st.h>
|
||||
#elif defined(HAVE_ST_H)
|
||||
#include <st.h>
|
||||
#else
|
||||
#error no st.h header found
|
||||
#endif
|
||||
|
||||
#include <stdint.h> /* for uint8_t */
|
||||
#include <fcntl.h> /* for fcntl() */
|
||||
#include <sys/errno.h> /* for errno */
|
||||
#include <sys/socket.h> /* for recv(), MSG_PEEK */
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data; // payload
|
||||
size_t cap; // total capacity
|
||||
size_t len; // current length
|
||||
} watchman_t;
|
||||
|
||||
// Forward declarations:
|
||||
VALUE watchman_load(char **ptr, char *end);
|
||||
void watchman_dump(watchman_t *w, VALUE serializable);
|
||||
|
||||
#define WATCHMAN_DEFAULT_STORAGE 4096
|
||||
|
||||
#define WATCHMAN_BINARY_MARKER "\x00\x01"
|
||||
#define WATCHMAN_ARRAY_MARKER 0x00
|
||||
#define WATCHMAN_HASH_MARKER 0x01
|
||||
#define WATCHMAN_STRING_MARKER 0x02
|
||||
#define WATCHMAN_INT8_MARKER 0x03
|
||||
#define WATCHMAN_INT16_MARKER 0x04
|
||||
#define WATCHMAN_INT32_MARKER 0x05
|
||||
#define WATCHMAN_INT64_MARKER 0x06
|
||||
#define WATCHMAN_FLOAT_MARKER 0x07
|
||||
#define WATCHMAN_TRUE 0x08
|
||||
#define WATCHMAN_FALSE 0x09
|
||||
#define WATCHMAN_NIL 0x0a
|
||||
#define WATCHMAN_TEMPLATE_MARKER 0x0b
|
||||
#define WATCHMAN_SKIP_MARKER 0x0c
|
||||
|
||||
#define WATCHMAN_HEADER \
|
||||
WATCHMAN_BINARY_MARKER \
|
||||
"\x06" \
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
static const char watchman_array_marker = WATCHMAN_ARRAY_MARKER;
|
||||
static const char watchman_hash_marker = WATCHMAN_HASH_MARKER;
|
||||
static const char watchman_string_marker = WATCHMAN_STRING_MARKER;
|
||||
static const char watchman_true = WATCHMAN_TRUE;
|
||||
static const char watchman_false = WATCHMAN_FALSE;
|
||||
static const char watchman_nil = WATCHMAN_NIL;
|
||||
|
||||
/**
|
||||
* Appends `len` bytes, starting at `data`, to the watchman_t struct `w`
|
||||
*
|
||||
* Will attempt to reallocate the underlying storage if it is not sufficient.
|
||||
*/
|
||||
void watchman_append(watchman_t *w, const char *data, size_t len) {
|
||||
if (w->len + len > w->cap) {
|
||||
w->cap += w->len + WATCHMAN_DEFAULT_STORAGE;
|
||||
REALLOC_N(w->data, uint8_t, w->cap);
|
||||
}
|
||||
memcpy(w->data + w->len, data, len);
|
||||
w->len += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new watchman_t struct
|
||||
*
|
||||
* The struct has a small amount of extra capacity preallocated, and a blank
|
||||
* header that can be filled in later to describe the PDU.
|
||||
*/
|
||||
watchman_t *watchman_init() {
|
||||
watchman_t *w = ALLOC(watchman_t);
|
||||
w->cap = WATCHMAN_DEFAULT_STORAGE;
|
||||
w->len = 0;
|
||||
w->data = ALLOC_N(uint8_t, WATCHMAN_DEFAULT_STORAGE);
|
||||
|
||||
watchman_append(w, WATCHMAN_HEADER, sizeof(WATCHMAN_HEADER) - 1);
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a watchman_t struct `w` that was previously allocated with
|
||||
* `watchman_init`
|
||||
*/
|
||||
void watchman_free(watchman_t *w) {
|
||||
xfree(w->data);
|
||||
xfree(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and appends the integer `num` to `w`
|
||||
*/
|
||||
void watchman_dump_int(watchman_t *w, int64_t num) {
|
||||
char encoded[1 + sizeof(int64_t)];
|
||||
|
||||
if (num == (int8_t)num) {
|
||||
encoded[0] = WATCHMAN_INT8_MARKER;
|
||||
encoded[1] = (int8_t)num;
|
||||
watchman_append(w, encoded, 1 + sizeof(int8_t));
|
||||
} else if (num == (int16_t)num) {
|
||||
encoded[0] = WATCHMAN_INT16_MARKER;
|
||||
*(int16_t *)(encoded + 1) = (int16_t)num;
|
||||
watchman_append(w, encoded, 1 + sizeof(int16_t));
|
||||
} else if (num == (int32_t)num) {
|
||||
encoded[0] = WATCHMAN_INT32_MARKER;
|
||||
*(int32_t *)(encoded + 1) = (int32_t)num;
|
||||
watchman_append(w, encoded, 1 + sizeof(int32_t));
|
||||
} else {
|
||||
encoded[0] = WATCHMAN_INT64_MARKER;
|
||||
*(int64_t *)(encoded + 1) = (int64_t)num;
|
||||
watchman_append(w, encoded, 1 + sizeof(int64_t));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and appends the string `string` to `w`
|
||||
*/
|
||||
void watchman_dump_string(watchman_t *w, VALUE string) {
|
||||
watchman_append(w, &watchman_string_marker, sizeof(watchman_string_marker));
|
||||
watchman_dump_int(w, RSTRING_LEN(string));
|
||||
watchman_append(w, RSTRING_PTR(string), RSTRING_LEN(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and appends the double `num` to `w`
|
||||
*/
|
||||
void watchman_dump_double(watchman_t *w, double num) {
|
||||
char encoded[1 + sizeof(double)];
|
||||
encoded[0] = WATCHMAN_FLOAT_MARKER;
|
||||
*(double *)(encoded + 1) = num;
|
||||
watchman_append(w, encoded, sizeof(encoded));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and appends the array `array` to `w`
|
||||
*/
|
||||
void watchman_dump_array(watchman_t *w, VALUE array) {
|
||||
long i;
|
||||
watchman_append(w, &watchman_array_marker, sizeof(watchman_array_marker));
|
||||
watchman_dump_int(w, RARRAY_LEN(array));
|
||||
for (i = 0; i < RARRAY_LEN(array); i++) {
|
||||
watchman_dump(w, rb_ary_entry(array, i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that encodes and appends a key/value pair (`key`, `value`) from
|
||||
* a hash to the watchman_t struct passed in via `data`
|
||||
*/
|
||||
int watchman_dump_hash_iterator(VALUE key, VALUE value, VALUE data) {
|
||||
watchman_t *w = (watchman_t *)data;
|
||||
watchman_dump_string(w, StringValue(key));
|
||||
watchman_dump(w, value);
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and appends the hash `hash` to `w`
|
||||
*/
|
||||
void watchman_dump_hash(watchman_t *w, VALUE hash) {
|
||||
watchman_append(w, &watchman_hash_marker, sizeof(watchman_hash_marker));
|
||||
watchman_dump_int(w, RHASH_SIZE(hash));
|
||||
rb_hash_foreach(hash, watchman_dump_hash_iterator, (VALUE)w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and appends the serialized Ruby object `serializable` to `w`
|
||||
*
|
||||
* Examples of serializable objects include arrays, hashes, strings, numbers
|
||||
* (integers, floats), booleans, and nil.
|
||||
*/
|
||||
void watchman_dump(watchman_t *w, VALUE serializable) {
|
||||
switch (TYPE(serializable)) {
|
||||
case T_ARRAY:
|
||||
return watchman_dump_array(w, serializable);
|
||||
case T_HASH:
|
||||
return watchman_dump_hash(w, serializable);
|
||||
case T_STRING:
|
||||
return watchman_dump_string(w, serializable);
|
||||
case T_FIXNUM: // up to 63 bits
|
||||
return watchman_dump_int(w, FIX2LONG(serializable));
|
||||
case T_BIGNUM:
|
||||
return watchman_dump_int(w, NUM2LL(serializable));
|
||||
case T_FLOAT:
|
||||
return watchman_dump_double(w, NUM2DBL(serializable));
|
||||
case T_TRUE:
|
||||
return watchman_append(w, &watchman_true, sizeof(watchman_true));
|
||||
case T_FALSE:
|
||||
return watchman_append(w, &watchman_false, sizeof(watchman_false));
|
||||
case T_NIL:
|
||||
return watchman_append(w, &watchman_nil, sizeof(watchman_nil));
|
||||
default:
|
||||
rb_raise(rb_eTypeError, "unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and return the int encoded at `ptr`
|
||||
*
|
||||
* Moves `ptr` past the extracted int.
|
||||
*
|
||||
* Will raise an ArgumentError if extracting the int would take us beyond the
|
||||
* end of the buffer indicated by `end`, or if there is no int encoded at `ptr`.
|
||||
*
|
||||
* @returns The extracted int
|
||||
*/
|
||||
int64_t watchman_load_int(char **ptr, char *end) {
|
||||
char *val_ptr = *ptr + sizeof(int8_t);
|
||||
int64_t val = 0;
|
||||
|
||||
if (val_ptr >= end) {
|
||||
rb_raise(rb_eArgError, "insufficient int storage");
|
||||
}
|
||||
|
||||
switch (*ptr[0]) {
|
||||
case WATCHMAN_INT8_MARKER:
|
||||
if (val_ptr + sizeof(int8_t) > end) {
|
||||
rb_raise(rb_eArgError, "overrun extracting int8_t");
|
||||
}
|
||||
val = *(int8_t *)val_ptr;
|
||||
*ptr = val_ptr + sizeof(int8_t);
|
||||
break;
|
||||
case WATCHMAN_INT16_MARKER:
|
||||
if (val_ptr + sizeof(int16_t) > end) {
|
||||
rb_raise(rb_eArgError, "overrun extracting int16_t");
|
||||
}
|
||||
val = *(int16_t *)val_ptr;
|
||||
*ptr = val_ptr + sizeof(int16_t);
|
||||
break;
|
||||
case WATCHMAN_INT32_MARKER:
|
||||
if (val_ptr + sizeof(int32_t) > end) {
|
||||
rb_raise(rb_eArgError, "overrun extracting int32_t");
|
||||
}
|
||||
val = *(int32_t *)val_ptr;
|
||||
*ptr = val_ptr + sizeof(int32_t);
|
||||
break;
|
||||
case WATCHMAN_INT64_MARKER:
|
||||
if (val_ptr + sizeof(int64_t) > end) {
|
||||
rb_raise(rb_eArgError, "overrun extracting int64_t");
|
||||
}
|
||||
val = *(int64_t *)val_ptr;
|
||||
*ptr = val_ptr + sizeof(int64_t);
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eArgError, "bad integer marker 0x%02x", (unsigned int)*ptr[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns a string encoded in the Watchman binary protocol format,
|
||||
* starting at `ptr` and finishing at or before `end`
|
||||
*/
|
||||
VALUE watchman_load_string(char **ptr, char *end) {
|
||||
int64_t len;
|
||||
VALUE string;
|
||||
if (*ptr >= end) {
|
||||
rb_raise(rb_eArgError, "unexpected end of input");
|
||||
}
|
||||
|
||||
if (*ptr[0] != WATCHMAN_STRING_MARKER) {
|
||||
rb_raise(rb_eArgError, "not a number");
|
||||
}
|
||||
|
||||
*ptr += sizeof(int8_t);
|
||||
if (*ptr >= end) {
|
||||
rb_raise(rb_eArgError, "invalid string header");
|
||||
}
|
||||
|
||||
len = watchman_load_int(ptr, end);
|
||||
if (len == 0) { // special case for zero-length strings
|
||||
return rb_str_new2("");
|
||||
} else if (*ptr + len > end) {
|
||||
rb_raise(rb_eArgError, "insufficient string storage");
|
||||
}
|
||||
|
||||
string = rb_str_new(*ptr, len);
|
||||
*ptr += len;
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns a double encoded in the Watchman binary protocol format,
|
||||
* starting at `ptr` and finishing at or before `end`
|
||||
*/
|
||||
double watchman_load_double(char **ptr, char *end) {
|
||||
double val;
|
||||
*ptr += sizeof(int8_t); // caller has already verified the marker
|
||||
if (*ptr + sizeof(double) > end) {
|
||||
rb_raise(rb_eArgError, "insufficient double storage");
|
||||
}
|
||||
val = *(double *)*ptr;
|
||||
*ptr += sizeof(double);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method which returns length of the array encoded in the Watchman
|
||||
* binary protocol format, starting at `ptr` and finishing at or before `end`
|
||||
*/
|
||||
int64_t watchman_load_array_header(char **ptr, char *end) {
|
||||
if (*ptr >= end) {
|
||||
rb_raise(rb_eArgError, "unexpected end of input");
|
||||
}
|
||||
|
||||
// verify and consume marker
|
||||
if (*ptr[0] != WATCHMAN_ARRAY_MARKER) {
|
||||
rb_raise(rb_eArgError, "not an array");
|
||||
}
|
||||
*ptr += sizeof(int8_t);
|
||||
|
||||
// expect a count
|
||||
if (*ptr + sizeof(int8_t) * 2 > end) {
|
||||
rb_raise(rb_eArgError, "incomplete array header");
|
||||
}
|
||||
return watchman_load_int(ptr, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns an array encoded in the Watchman binary protocol format,
|
||||
* starting at `ptr` and finishing at or before `end`
|
||||
*/
|
||||
VALUE watchman_load_array(char **ptr, char *end) {
|
||||
int64_t count, i;
|
||||
VALUE array;
|
||||
|
||||
count = watchman_load_array_header(ptr, end);
|
||||
array = rb_ary_new2(count);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
rb_ary_push(array, watchman_load(ptr, end));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns a hash encoded in the Watchman binary protocol format,
|
||||
* starting at `ptr` and finishing at or before `end`
|
||||
*/
|
||||
VALUE watchman_load_hash(char **ptr, char *end) {
|
||||
int64_t count, i;
|
||||
VALUE hash, key, value;
|
||||
|
||||
*ptr += sizeof(int8_t); // caller has already verified the marker
|
||||
|
||||
// expect a count
|
||||
if (*ptr + sizeof(int8_t) * 2 > end) {
|
||||
rb_raise(rb_eArgError, "incomplete hash header");
|
||||
}
|
||||
count = watchman_load_int(ptr, end);
|
||||
|
||||
hash = rb_hash_new();
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
key = watchman_load_string(ptr, end);
|
||||
value = watchman_load(ptr, end);
|
||||
rb_hash_aset(hash, key, value);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns a templated array encoded in the Watchman binary protocol
|
||||
* format, starting at `ptr` and finishing at or before `end`
|
||||
*
|
||||
* Templated arrays are arrays of hashes which have repetitive key information
|
||||
* pulled out into a separate "headers" prefix.
|
||||
*
|
||||
* @see https://github.com/facebook/watchman/blob/master/BSER.markdown
|
||||
*/
|
||||
VALUE watchman_load_template(char **ptr, char *end) {
|
||||
int64_t header_items_count, i, row_count;
|
||||
VALUE array, hash, header, key, value;
|
||||
|
||||
*ptr += sizeof(int8_t); // caller has already verified the marker
|
||||
|
||||
// process template header array
|
||||
header_items_count = watchman_load_array_header(ptr, end);
|
||||
header = rb_ary_new2(header_items_count);
|
||||
for (i = 0; i < header_items_count; i++) {
|
||||
rb_ary_push(header, watchman_load_string(ptr, end));
|
||||
}
|
||||
|
||||
// process row items
|
||||
row_count = watchman_load_int(ptr, end);
|
||||
array = rb_ary_new2(header_items_count);
|
||||
while (row_count--) {
|
||||
hash = rb_hash_new();
|
||||
for (i = 0; i < header_items_count; i++) {
|
||||
if (*ptr >= end) {
|
||||
rb_raise(rb_eArgError, "unexpected end of input");
|
||||
}
|
||||
|
||||
if (*ptr[0] == WATCHMAN_SKIP_MARKER) {
|
||||
*ptr += sizeof(uint8_t);
|
||||
} else {
|
||||
value = watchman_load(ptr, end);
|
||||
key = rb_ary_entry(header, i);
|
||||
rb_hash_aset(hash, key, value);
|
||||
}
|
||||
}
|
||||
rb_ary_push(array, hash);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns an object encoded in the Watchman binary protocol format,
|
||||
* starting at `ptr` and finishing at or before `end`
|
||||
*/
|
||||
VALUE watchman_load(char **ptr, char *end) {
|
||||
if (*ptr >= end) {
|
||||
rb_raise(rb_eArgError, "unexpected end of input");
|
||||
}
|
||||
|
||||
switch (*ptr[0]) {
|
||||
case WATCHMAN_ARRAY_MARKER:
|
||||
return watchman_load_array(ptr, end);
|
||||
case WATCHMAN_HASH_MARKER:
|
||||
return watchman_load_hash(ptr, end);
|
||||
case WATCHMAN_STRING_MARKER:
|
||||
return watchman_load_string(ptr, end);
|
||||
case WATCHMAN_INT8_MARKER:
|
||||
case WATCHMAN_INT16_MARKER:
|
||||
case WATCHMAN_INT32_MARKER:
|
||||
case WATCHMAN_INT64_MARKER:
|
||||
return LL2NUM(watchman_load_int(ptr, end));
|
||||
case WATCHMAN_FLOAT_MARKER:
|
||||
return rb_float_new(watchman_load_double(ptr, end));
|
||||
case WATCHMAN_TRUE:
|
||||
*ptr += 1;
|
||||
return Qtrue;
|
||||
case WATCHMAN_FALSE:
|
||||
*ptr += 1;
|
||||
return Qfalse;
|
||||
case WATCHMAN_NIL:
|
||||
*ptr += 1;
|
||||
return Qnil;
|
||||
case WATCHMAN_TEMPLATE_MARKER:
|
||||
return watchman_load_template(ptr, end);
|
||||
default:
|
||||
rb_raise(rb_eTypeError, "unsupported type");
|
||||
}
|
||||
|
||||
return Qnil; // keep the compiler happy
|
||||
}
|
||||
|
||||
/**
|
||||
* CommandT::Watchman::Utils.load(serialized)
|
||||
*
|
||||
* Converts the binary object, `serialized`, from the Watchman binary protocol
|
||||
* format into a normal Ruby object.
|
||||
*/
|
||||
VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) {
|
||||
char *ptr, *end;
|
||||
long len;
|
||||
uint64_t payload_size;
|
||||
VALUE loaded;
|
||||
serialized = StringValue(serialized);
|
||||
len = RSTRING_LEN(serialized);
|
||||
ptr = RSTRING_PTR(serialized);
|
||||
end = ptr + len;
|
||||
|
||||
// expect at least the binary marker and a int8_t length counter
|
||||
if ((size_t)len < sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) * 2) {
|
||||
rb_raise(rb_eArgError, "undersized header");
|
||||
}
|
||||
|
||||
if (memcmp(ptr, WATCHMAN_BINARY_MARKER, sizeof(WATCHMAN_BINARY_MARKER) - 1)) {
|
||||
rb_raise(rb_eArgError, "missing binary marker");
|
||||
}
|
||||
|
||||
// get size marker
|
||||
ptr += sizeof(WATCHMAN_BINARY_MARKER) - 1;
|
||||
payload_size = watchman_load_int(&ptr, end);
|
||||
if (!payload_size) {
|
||||
rb_raise(rb_eArgError, "empty payload");
|
||||
}
|
||||
|
||||
// sanity check length
|
||||
if (ptr + payload_size != end) {
|
||||
rb_raise(
|
||||
rb_eArgError,
|
||||
"payload size mismatch (%lu)",
|
||||
(unsigned long)(end - (ptr + payload_size))
|
||||
);
|
||||
}
|
||||
|
||||
loaded = watchman_load(&ptr, end);
|
||||
|
||||
// one more sanity check
|
||||
if (ptr != end) {
|
||||
rb_raise(
|
||||
rb_eArgError,
|
||||
"payload termination mismatch (%lu)",
|
||||
(unsigned long)(end - ptr)
|
||||
);
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* CommandT::Watchman::Utils.dump(serializable)
|
||||
*
|
||||
* Converts the Ruby object, `serializable`, into a binary string in the
|
||||
* Watchman binary protocol format.
|
||||
*
|
||||
* Examples of serializable objects include arrays, hashes, strings, numbers
|
||||
* (integers, floats), booleans, and nil.
|
||||
*/
|
||||
VALUE CommandTWatchmanUtils_dump(VALUE self, VALUE serializable) {
|
||||
uint64_t *len;
|
||||
VALUE serialized;
|
||||
watchman_t *w = watchman_init();
|
||||
watchman_dump(w, serializable);
|
||||
|
||||
// update header with final length information
|
||||
len = (uint64_t *)(w->data + sizeof(WATCHMAN_HEADER) - sizeof(uint64_t) - 1);
|
||||
*len = w->len - sizeof(WATCHMAN_HEADER) + 1;
|
||||
|
||||
// prepare final return value
|
||||
serialized = rb_str_buf_new(w->len);
|
||||
rb_str_buf_cat(serialized, (const char*)w->data, w->len);
|
||||
watchman_free(w);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for raising a SystemCallError wrapping a lower-level error code
|
||||
* coming from the `errno` global variable.
|
||||
*/
|
||||
void watchman_raise_system_call_error(int number) {
|
||||
VALUE error = INT2FIX(number);
|
||||
rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
|
||||
}
|
||||
|
||||
// How far we have to look to figure out the size of the PDU header
|
||||
#define WATCHMAN_SNIFF_BUFFER_SIZE sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t)
|
||||
|
||||
// How far we have to peek, at most, to figure out the size of the PDU itself
|
||||
#define WATCHMAN_PEEK_BUFFER_SIZE \
|
||||
sizeof(WATCHMAN_BINARY_MARKER) - 1 + \
|
||||
sizeof(WATCHMAN_INT64_MARKER) + \
|
||||
sizeof(int64_t)
|
||||
|
||||
/**
|
||||
* CommandT::Watchman::Utils.query(query, socket)
|
||||
*
|
||||
* Converts `query`, a Watchman query comprising Ruby objects, into the Watchman
|
||||
* binary protocol format, transmits it over socket, and unserializes and
|
||||
* returns the result.
|
||||
*/
|
||||
VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) {
|
||||
char *payload;
|
||||
int fileno, flags;
|
||||
int8_t peek[WATCHMAN_PEEK_BUFFER_SIZE];
|
||||
int8_t sizes[] = { 0, 0, 0, 1, 2, 4, 8 };
|
||||
int8_t sizes_idx;
|
||||
int8_t *pdu_size_ptr;
|
||||
int64_t payload_size;
|
||||
long query_len;
|
||||
ssize_t peek_size, sent, received;
|
||||
void *buffer;
|
||||
VALUE loaded, serialized;
|
||||
fileno = NUM2INT(rb_funcall(socket, rb_intern("fileno"), 0));
|
||||
|
||||
// do blocking I/O to simplify the following logic
|
||||
flags = fcntl(fileno, F_GETFL);
|
||||
if (fcntl(fileno, F_SETFL, flags & ~O_NONBLOCK) == -1) {
|
||||
rb_raise(rb_eRuntimeError, "unable to clear O_NONBLOCK flag");
|
||||
}
|
||||
|
||||
// send the message
|
||||
serialized = CommandTWatchmanUtils_dump(self, query);
|
||||
query_len = RSTRING_LEN(serialized);
|
||||
sent = send(fileno, RSTRING_PTR(serialized), query_len, 0);
|
||||
if (sent == -1) {
|
||||
watchman_raise_system_call_error(errno);
|
||||
} else if (sent != query_len) {
|
||||
rb_raise(rb_eRuntimeError, "expected to send %ld bytes but sent %ld",
|
||||
query_len, sent);
|
||||
}
|
||||
|
||||
// sniff to see how large the header is
|
||||
received = recv(fileno, peek, WATCHMAN_SNIFF_BUFFER_SIZE, MSG_PEEK | MSG_WAITALL);
|
||||
if (received == -1) {
|
||||
watchman_raise_system_call_error(errno);
|
||||
} else if (received != WATCHMAN_SNIFF_BUFFER_SIZE) {
|
||||
rb_raise(rb_eRuntimeError, "failed to sniff PDU header");
|
||||
}
|
||||
|
||||
// peek at size of PDU
|
||||
sizes_idx = peek[sizeof(WATCHMAN_BINARY_MARKER) - 1];
|
||||
if (sizes_idx < WATCHMAN_INT8_MARKER || sizes_idx > WATCHMAN_INT64_MARKER) {
|
||||
rb_raise(rb_eRuntimeError, "bad PDU size marker");
|
||||
}
|
||||
peek_size = sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) +
|
||||
sizes[sizes_idx];
|
||||
|
||||
received = recv(fileno, peek, peek_size, MSG_PEEK);
|
||||
if (received == -1) {
|
||||
watchman_raise_system_call_error(errno);
|
||||
} else if (received != peek_size) {
|
||||
rb_raise(rb_eRuntimeError, "failed to peek at PDU header");
|
||||
}
|
||||
pdu_size_ptr = peek + sizeof(WATCHMAN_BINARY_MARKER) - sizeof(int8_t);
|
||||
payload_size =
|
||||
peek_size +
|
||||
watchman_load_int((char **)&pdu_size_ptr, (char *)peek + peek_size);
|
||||
|
||||
// actually read the PDU
|
||||
buffer = xmalloc(payload_size);
|
||||
if (!buffer) {
|
||||
rb_raise(
|
||||
rb_eNoMemError,
|
||||
"failed to allocate %lld bytes",
|
||||
(long long int)payload_size
|
||||
);
|
||||
}
|
||||
received = recv(fileno, buffer, payload_size, MSG_WAITALL);
|
||||
if (received == -1) {
|
||||
watchman_raise_system_call_error(errno);
|
||||
} else if (received != payload_size) {
|
||||
rb_raise(rb_eRuntimeError, "failed to load PDU");
|
||||
}
|
||||
payload = (char *)buffer + peek_size;
|
||||
loaded = watchman_load(&payload, payload + payload_size);
|
||||
free(buffer);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
#else /* don't build Watchman utils; supply stubs only*/
|
||||
|
||||
VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) {
|
||||
rb_raise(rb_eRuntimeError, "unsupported operation");
|
||||
}
|
||||
|
||||
VALUE CommandTWatchmanUtils_dump(VALUE self, VALUE serializable) {
|
||||
rb_raise(rb_eRuntimeError, "unsupported operation");
|
||||
}
|
||||
|
||||
VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) {
|
||||
rb_raise(rb_eRuntimeError, "unsupported operation");
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
// Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
#include <ruby.h>
|
||||
|
||||
/**
|
||||
* @module CommandT::Watchman::Utils
|
||||
*
|
||||
* Methods for working with the Watchman binary protocol
|
||||
*
|
||||
* @see https://github.com/facebook/watchman/blob/master/BSER.markdown
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert an object serialized using the Watchman binary protocol[0] into an
|
||||
* unpacked Ruby object
|
||||
*/
|
||||
extern VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized);
|
||||
|
||||
/**
|
||||
* Serialize a Ruby object into the Watchman binary protocol format
|
||||
*/
|
||||
extern VALUE CommandTWatchmanUtils_dump(VALUE self, VALUE serializable);
|
||||
|
||||
/**
|
||||
* Issue `query` to the Watchman instance listening on `socket` (a `UNIXSocket`
|
||||
* instance) and return the result
|
||||
*
|
||||
* The query is serialized following the Watchman binary protocol and the
|
||||
* result is converted to native Ruby objects before returning to the caller.
|
||||
*/
|
||||
extern VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket);
|
|
@ -1,88 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CommandT::Controller do
|
||||
describe 'accept selection' do
|
||||
let(:controller) { CommandT::Controller.new }
|
||||
|
||||
before do
|
||||
check_ruby_1_9_2
|
||||
stub_finder
|
||||
stub_match_window 'path/to/selection'
|
||||
stub_prompt
|
||||
stub_vim '/working/directory'
|
||||
end
|
||||
|
||||
def set_string(name, value)
|
||||
stub(::VIM).evaluate(%{exists("#{name}")}).returns(1)
|
||||
stub(::VIM).evaluate(name).returns(value)
|
||||
end
|
||||
|
||||
it 'opens relative paths inside the working directory' do
|
||||
stub(::VIM).evaluate('a:arg').returns('')
|
||||
set_string('g:CommandTTraverseSCM', 'pwd')
|
||||
controller.show_file_finder
|
||||
mock(::VIM).command('silent e path/to/selection')
|
||||
controller.accept_selection
|
||||
end
|
||||
|
||||
it 'opens absolute paths outside the working directory' do
|
||||
stub(::VIM).evaluate('a:arg').returns('../outside')
|
||||
controller.show_file_finder
|
||||
mock(::VIM).command('silent e /working/outside/path/to/selection')
|
||||
controller.accept_selection
|
||||
end
|
||||
|
||||
it 'does not get confused by common directory prefixes' do
|
||||
stub(::VIM).evaluate('a:arg').returns('../directory-oops')
|
||||
controller.show_file_finder
|
||||
mock(::VIM).command('silent e /working/directory-oops/path/to/selection')
|
||||
controller.accept_selection
|
||||
end
|
||||
end
|
||||
|
||||
def check_ruby_1_9_2
|
||||
if RUBY_VERSION =~ /\A1\.9\.2/
|
||||
pending 'broken in Ruby 1.9.2 (see https://gist.github.com/455547)'
|
||||
end
|
||||
end
|
||||
|
||||
def stub_finder(sorted_matches=[])
|
||||
finder = CommandT::Finder::FileFinder.new
|
||||
stub(finder).path = anything
|
||||
stub(finder).sorted_matches_for(anything, anything).returns(sorted_matches)
|
||||
stub(CommandT::Finder::FileFinder).new.returns(finder)
|
||||
end
|
||||
|
||||
def stub_match_window(selection)
|
||||
match_window = Object.new
|
||||
stub(match_window).matches = anything
|
||||
stub(match_window).leave
|
||||
stub(match_window).selection.returns(selection)
|
||||
stub(CommandT::MatchWindow).new.returns(match_window)
|
||||
end
|
||||
|
||||
def stub_prompt(abbrev='')
|
||||
prompt = Object.new
|
||||
stub(prompt).focus
|
||||
stub(prompt).clear!
|
||||
stub(prompt).abbrev.returns(abbrev)
|
||||
stub(CommandT::Prompt).new.returns(prompt)
|
||||
end
|
||||
|
||||
def stub_vim(working_directory)
|
||||
stub($curbuf).number.returns('0')
|
||||
stub(::VIM).command(/noremap/)
|
||||
stub(::VIM).command('silent b 0')
|
||||
stub(::VIM).command(/augroup/)
|
||||
stub(::VIM).command('au!')
|
||||
stub(::VIM).command(/autocmd/)
|
||||
stub(::VIM).evaluate(/exists\(.+\)/).returns('0')
|
||||
stub(::VIM).evaluate('getcwd()').returns(working_directory)
|
||||
stub(::VIM).evaluate('&buflisted').returns('1')
|
||||
stub(::VIM).evaluate('&lines').returns('80')
|
||||
stub(::VIM).evaluate('&term').returns('vt100')
|
||||
end
|
||||
end
|
|
@ -1,55 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CommandT::Finder::BufferFinder do
|
||||
before do
|
||||
@paths = %w(.git/config .vim/notes .vimrc baz foo/beta)
|
||||
any_instance_of(CommandT::Scanner::BufferScanner, :paths => @paths)
|
||||
@finder = CommandT::Finder::BufferFinder.new
|
||||
end
|
||||
|
||||
describe 'sorted_matches_for method' do
|
||||
it 'returns an empty array when no matches' do
|
||||
@finder.sorted_matches_for('kung foo fighting').should == []
|
||||
end
|
||||
|
||||
it 'returns all files when query string is empty' do
|
||||
@finder.sorted_matches_for('').should == @paths
|
||||
end
|
||||
|
||||
it 'returns files in alphabetical order when query string is empty' do
|
||||
results = @finder.sorted_matches_for('')
|
||||
results.should == results.sort
|
||||
end
|
||||
|
||||
it 'returns matching files in score order' do
|
||||
@finder.sorted_matches_for('ba').should == %w(baz foo/beta)
|
||||
@finder.sorted_matches_for('a').should == %w(baz foo/beta)
|
||||
end
|
||||
|
||||
it 'returns matching dot files even when search term does not include a dot' do
|
||||
@finder.sorted_matches_for('i').should include('.vimrc')
|
||||
end
|
||||
|
||||
it 'returns matching files inside dot directories even when search term does not include a dot' do
|
||||
@finder.sorted_matches_for('i').should include('.vim/notes')
|
||||
end
|
||||
|
||||
it "does not use the Vim expand() function to consult the 'wildignore' setting" do
|
||||
do_not_allow(::VIM).evaluate
|
||||
@finder.sorted_matches_for('i')
|
||||
end
|
||||
|
||||
it 'obeys the :limit option for empty search strings' do
|
||||
@finder.sorted_matches_for('', :limit => 1).
|
||||
should == %w(.git/config)
|
||||
end
|
||||
|
||||
it 'obeys the :limit option for non-empty search strings' do
|
||||
@finder.sorted_matches_for('i', :limit => 2).
|
||||
should == %w(.vimrc .vim/notes)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,56 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CommandT::Finder::FileFinder do
|
||||
before :all do
|
||||
@finder = CommandT::Finder::FileFinder.new File.join(File.dirname(__FILE__), '..',
|
||||
'..', '..', 'fixtures')
|
||||
@all_fixtures = %w(
|
||||
bar/abc
|
||||
bar/xyz
|
||||
baz
|
||||
bing
|
||||
foo/alpha/t1
|
||||
foo/alpha/t2
|
||||
foo/beta
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
stub(::VIM).evaluate(/expand/) { 0 }
|
||||
end
|
||||
|
||||
describe 'sorted_matches_for method' do
|
||||
it 'returns an empty array when no matches' do
|
||||
@finder.sorted_matches_for('kung foo fighting').should == []
|
||||
end
|
||||
|
||||
it 'returns all files when query string is empty' do
|
||||
@finder.sorted_matches_for('').should == @all_fixtures
|
||||
end
|
||||
|
||||
it 'returns files in alphabetical order when query string is empty' do
|
||||
results = @finder.sorted_matches_for('')
|
||||
results.should == results.sort
|
||||
end
|
||||
|
||||
it 'returns matching files in score order' do
|
||||
@finder.sorted_matches_for('ba').
|
||||
should == %w(baz bar/abc bar/xyz foo/beta)
|
||||
@finder.sorted_matches_for('a').
|
||||
should == %w(baz bar/abc bar/xyz foo/alpha/t1 foo/alpha/t2 foo/beta)
|
||||
end
|
||||
|
||||
it 'obeys the :limit option for empty search strings' do
|
||||
@finder.sorted_matches_for('', :limit => 2).
|
||||
should == %w(bar/abc bar/xyz)
|
||||
end
|
||||
|
||||
it 'obeys the :limit option for non-empty search strings' do
|
||||
@finder.sorted_matches_for('a', :limit => 3).
|
||||
should == %w(baz bar/abc bar/xyz)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,218 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
require 'ostruct'
|
||||
require 'command-t/ext' # CommandT::Matcher
|
||||
|
||||
describe CommandT::Matcher do
|
||||
def matcher(*paths)
|
||||
scanner = OpenStruct.new(:paths => paths)
|
||||
CommandT::Matcher.new(scanner)
|
||||
end
|
||||
|
||||
describe 'initialization' do
|
||||
it 'raises an ArgumentError if passed nil' do
|
||||
expect { CommandT::Matcher.new(nil) }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#sorted_matches_for' do
|
||||
def ordered_matches(paths, query)
|
||||
matcher(*paths).sorted_matches_for(query)
|
||||
end
|
||||
|
||||
it 'raises an ArgumentError if passed nil' do
|
||||
expect { matcher.sorted_matches_for(nil) }.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it 'returns empty array when source array empty' do
|
||||
matcher.sorted_matches_for('foo').should == []
|
||||
matcher.sorted_matches_for('').should == []
|
||||
end
|
||||
|
||||
it 'returns empty array when no matches' do
|
||||
matcher = matcher(*%w[foo/bar foo/baz bing])
|
||||
matcher.sorted_matches_for('xyz').should == []
|
||||
end
|
||||
|
||||
it 'returns matching paths' do
|
||||
matcher = matcher(*%w[foo/bar foo/baz bing])
|
||||
matches = matcher.sorted_matches_for('z')
|
||||
matches.map { |m| m.to_s }.should == ['foo/baz']
|
||||
matches = matcher.sorted_matches_for('bg')
|
||||
matches.map { |m| m.to_s }.should == ['bing']
|
||||
end
|
||||
|
||||
it 'performs case-insensitive matching' do
|
||||
matches = matcher('Foo').sorted_matches_for('f')
|
||||
matches.map { |m| m.to_s }.should == ['Foo']
|
||||
end
|
||||
|
||||
it 'considers the empty string to match everything' do
|
||||
matches = matcher('foo').sorted_matches_for('')
|
||||
matches.map { |m| m.to_s }.should == ['foo']
|
||||
end
|
||||
|
||||
it 'does not consider mere substrings of the query string to be a match' do
|
||||
matcher('foo').sorted_matches_for('foo...').should == []
|
||||
end
|
||||
|
||||
it 'prioritizes shorter paths over longer ones' do
|
||||
ordered_matches(%w[
|
||||
articles_controller_spec.rb
|
||||
article.rb
|
||||
], 'art').should == %w[
|
||||
article.rb
|
||||
articles_controller_spec.rb
|
||||
]
|
||||
end
|
||||
|
||||
it 'prioritizes matches after "/"' do
|
||||
ordered_matches(%w[fooobar foo/bar], 'b').should == %w[foo/bar fooobar]
|
||||
|
||||
# note that / beats _
|
||||
ordered_matches(%w[foo_bar foo/bar], 'b').should == %w[foo/bar foo_bar]
|
||||
|
||||
# / also beats -
|
||||
ordered_matches(%w[foo-bar foo/bar], 'b').should == %w[foo/bar foo-bar]
|
||||
|
||||
# and numbers
|
||||
ordered_matches(%w[foo9bar foo/bar], 'b').should == %w[foo/bar foo9bar]
|
||||
|
||||
# and periods
|
||||
ordered_matches(%w[foo.bar foo/bar], 'b').should == %w[foo/bar foo.bar]
|
||||
|
||||
# and spaces
|
||||
ordered_matches(['foo bar', 'foo/bar'], 'b').should == ['foo/bar', 'foo bar']
|
||||
end
|
||||
|
||||
it 'prioritizes matches after "-"' do
|
||||
ordered_matches(%w[fooobar foo-bar], 'b').should == %w[foo-bar fooobar]
|
||||
|
||||
# - also beats .
|
||||
ordered_matches(%w[foo.bar foo-bar], 'b').should == %w[foo-bar foo.bar]
|
||||
end
|
||||
|
||||
it 'prioritizes matches after "_"' do
|
||||
ordered_matches(%w[fooobar foo_bar], 'b').should == %w[foo_bar fooobar]
|
||||
|
||||
# _ also beats .
|
||||
ordered_matches(%w[foo.bar foo_bar], 'b').should == %w[foo_bar foo.bar]
|
||||
end
|
||||
|
||||
it 'prioritizes matches after " "' do
|
||||
ordered_matches(['fooobar', 'foo bar'], 'b').should == ['foo bar', 'fooobar']
|
||||
|
||||
# " " also beats .
|
||||
ordered_matches(['foo.bar', 'foo bar'], 'b').should == ['foo bar', 'foo.bar']
|
||||
end
|
||||
|
||||
it 'prioritizes matches after numbers' do
|
||||
ordered_matches(%w[fooobar foo9bar], 'b').should == %w[foo9bar fooobar]
|
||||
|
||||
# numbers also beat .
|
||||
ordered_matches(%w[foo.bar foo9bar], 'b').should == %w[foo9bar foo.bar]
|
||||
end
|
||||
|
||||
it 'prioritizes matches after periods' do
|
||||
ordered_matches(%w[fooobar foo.bar], 'b').should == %w[foo.bar fooobar]
|
||||
end
|
||||
|
||||
it 'prioritizes matching capitals following lowercase' do
|
||||
ordered_matches(%w[foobar fooBar], 'b').should == %w[fooBar foobar]
|
||||
end
|
||||
|
||||
it 'prioritizes matches earlier in the string' do
|
||||
ordered_matches(%w[******b* **b*****], 'b').should == %w[**b***** ******b*]
|
||||
end
|
||||
|
||||
it 'prioritizes matches closer to previous matches' do
|
||||
ordered_matches(%w[**b***c* **bc****], 'bc').should == %w[**bc**** **b***c*]
|
||||
end
|
||||
|
||||
it 'scores alternative matches of same path differently' do
|
||||
# ie:
|
||||
# app/controllers/articles_controller.rb
|
||||
ordered_matches(%w[
|
||||
a**/****r******/**t*c***_*on*******.**
|
||||
***/***********/art*****_con*******.**
|
||||
], 'artcon').should == %w[
|
||||
***/***********/art*****_con*******.**
|
||||
a**/****r******/**t*c***_*on*******.**
|
||||
]
|
||||
end
|
||||
|
||||
it 'provides intuitive results for "artcon" and "articles_controller"' do
|
||||
ordered_matches(%w[
|
||||
app/controllers/heartbeat_controller.rb
|
||||
app/controllers/articles_controller.rb
|
||||
], 'artcon').should == %w[
|
||||
app/controllers/articles_controller.rb
|
||||
app/controllers/heartbeat_controller.rb
|
||||
]
|
||||
end
|
||||
|
||||
it 'provides intuitive results for "aca" and "a/c/articles_controller"' do
|
||||
ordered_matches(%w[
|
||||
app/controllers/heartbeat_controller.rb
|
||||
app/controllers/articles_controller.rb
|
||||
], 'aca').should == %w[
|
||||
app/controllers/articles_controller.rb
|
||||
app/controllers/heartbeat_controller.rb
|
||||
]
|
||||
end
|
||||
|
||||
it 'provides intuitive results for "d" and "doc/command-t.txt"' do
|
||||
ordered_matches(%w[
|
||||
TODO
|
||||
doc/command-t.txt
|
||||
], 'd').should == %w[
|
||||
doc/command-t.txt
|
||||
TODO
|
||||
]
|
||||
end
|
||||
|
||||
it 'provides intuitive results for "do" and "doc/command-t.txt"' do
|
||||
ordered_matches(%w[
|
||||
TODO
|
||||
doc/command-t.txt
|
||||
], 'do').should == %w[
|
||||
doc/command-t.txt
|
||||
TODO
|
||||
]
|
||||
end
|
||||
|
||||
it "doesn't incorrectly accept repeats of the last-matched character" do
|
||||
# https://github.com/wincent/Command-T/issues/82
|
||||
matcher = matcher(*%w[ash/system/user/config.h])
|
||||
matcher.sorted_matches_for('usercc').should == []
|
||||
|
||||
# simpler test case
|
||||
matcher = matcher(*%w[foobar])
|
||||
matcher.sorted_matches_for('fooooo').should == []
|
||||
|
||||
# minimal repro
|
||||
matcher = matcher(*%w[ab])
|
||||
matcher.sorted_matches_for('aa').should == []
|
||||
end
|
||||
|
||||
it 'ignores dotfiles by default' do
|
||||
matcher = matcher(*%w[.foo .bar])
|
||||
matcher.sorted_matches_for('foo').should == []
|
||||
end
|
||||
|
||||
it 'shows dotfiles if the query starts with a dot' do
|
||||
matcher = matcher(*%w[.foo .bar])
|
||||
matcher.sorted_matches_for('.fo').should == %w[.foo]
|
||||
end
|
||||
|
||||
it "doesn't show dotfiles if the query contains a non-leading dot" do
|
||||
matcher = matcher(*%w[.foo.txt .bar.txt])
|
||||
matcher.sorted_matches_for('f.t').should == []
|
||||
|
||||
# counter-example
|
||||
matcher.sorted_matches_for('.f.t').should == %w[.foo.txt]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
require 'ostruct'
|
||||
|
||||
describe CommandT::Scanner::BufferScanner do
|
||||
def buffer(name)
|
||||
b = OpenStruct.new
|
||||
b.name = name
|
||||
b
|
||||
end
|
||||
|
||||
before do
|
||||
@paths = %w(bar/abc bar/xyz baz bing foo/alpha/t1 foo/alpha/t2 foo/beta)
|
||||
@scanner = CommandT::Scanner::BufferScanner.new
|
||||
stub(@scanner).relative_path_under_working_directory(is_a(String)) { |arg| arg }
|
||||
stub(::VIM::Buffer).count { 7 }
|
||||
(0..6).each do |n|
|
||||
stub(::VIM::Buffer)[n].returns(buffer @paths[n])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'paths method' do
|
||||
it 'returns a list of regular files' do
|
||||
@scanner.paths.should =~ @paths
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,65 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CommandT::Scanner::FileScanner::RubyFileScanner do
|
||||
before do
|
||||
@dir = File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'fixtures')
|
||||
@all_fixtures = %w(
|
||||
bar/abc bar/xyz baz bing foo/alpha/t1 foo/alpha/t2 foo/beta
|
||||
)
|
||||
@scanner = CommandT::Scanner::FileScanner::RubyFileScanner.new(@dir)
|
||||
|
||||
stub(::VIM).evaluate(/exists/) { 1 }
|
||||
stub(::VIM).evaluate(/expand\(.+\)/) { '0' }
|
||||
stub(::VIM).evaluate(/wildignore/) { '' }
|
||||
end
|
||||
|
||||
describe 'paths method' do
|
||||
it 'returns a list of regular files' do
|
||||
@scanner.paths.should =~ @all_fixtures
|
||||
end
|
||||
end
|
||||
|
||||
describe 'path= method' do
|
||||
it 'allows repeated applications of scanner at different paths' do
|
||||
@scanner.paths.should =~ @all_fixtures
|
||||
|
||||
# drill down 1 level
|
||||
@scanner.path = File.join(@dir, 'foo')
|
||||
@scanner.paths.should =~ %w(alpha/t1 alpha/t2 beta)
|
||||
|
||||
# and another
|
||||
@scanner.path = File.join(@dir, 'foo', 'alpha')
|
||||
@scanner.paths.should =~ %w(t1 t2)
|
||||
end
|
||||
end
|
||||
|
||||
describe "'wildignore' exclusion" do
|
||||
context "when there is a 'wildignore' setting in effect" do
|
||||
it "calls on VIM's expand() function for pattern filtering" do
|
||||
stub(::VIM).command(/set wildignore/)
|
||||
scanner =
|
||||
CommandT::Scanner::FileScanner::RubyFileScanner.new @dir, :wild_ignore => '*.o'
|
||||
mock(::VIM).evaluate(/expand\(.+\)/).times(10)
|
||||
scanner.paths
|
||||
end
|
||||
end
|
||||
|
||||
context "when there is no 'wildignore' setting in effect" do
|
||||
it "does not call VIM's expand() function" do
|
||||
scanner = CommandT::Scanner::FileScanner::RubyFileScanner.new @dir
|
||||
mock(::VIM).evaluate(/expand\(.+\)/).never
|
||||
scanner.paths
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ':max_depth option' do
|
||||
it 'does not descend below "max_depth" levels' do
|
||||
@scanner = CommandT::Scanner::FileScanner::RubyFileScanner.new @dir, :max_depth => 1
|
||||
@scanner.paths.should =~ %w(bar/abc bar/xyz baz bing foo/beta)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CommandT::Scanner::FileScanner do
|
||||
before do
|
||||
dir = File.join(File.dirname(__FILE__), '..', '..', '..', 'fixtures')
|
||||
@scanner = CommandT::Scanner::FileScanner.new(dir)
|
||||
end
|
||||
|
||||
describe 'flush method' do
|
||||
it 'forces a rescan on next call to paths method' do
|
||||
expect { @scanner.flush }.
|
||||
to change { @scanner.instance_variable_get('@paths').object_id }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CommandT::VIM do
|
||||
describe '.escape_for_single_quotes' do
|
||||
it 'turns doubles all single quotes' do
|
||||
input = %{it's ''something''}
|
||||
expected = %{it''s ''''something''''}
|
||||
expect(CommandT::VIM.escape_for_single_quotes(input)).to eq(expected)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,416 +0,0 @@
|
|||
# Copyright 2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
require 'spec_helper'
|
||||
require 'command-t/ext' # for CommandT::Watchman::Utils
|
||||
|
||||
describe CommandT::Watchman::Utils do
|
||||
def binary(str)
|
||||
if str.respond_to?(:force_encoding) # Ruby >= 1.9
|
||||
str.force_encoding('ASCII-8BIT')
|
||||
else
|
||||
str
|
||||
end
|
||||
end
|
||||
|
||||
def little_endian?
|
||||
byte = [0xff00].pack('s')[0]
|
||||
if byte.is_a?(Fixnum) # ie. Ruby 1.8
|
||||
byte.zero?
|
||||
elsif byte.is_a?(String) # ie. Ruby >= 1.9
|
||||
byte == "\x00"
|
||||
else
|
||||
raise 'unable to determine endianness'
|
||||
end
|
||||
end
|
||||
|
||||
def roundtrip(value)
|
||||
described_class.load(described_class.dump(value))
|
||||
end
|
||||
|
||||
it 'roundtrips arrays' do
|
||||
value = [1, 2, ['three', false]]
|
||||
expect(roundtrip(value)).to eq(value)
|
||||
end
|
||||
|
||||
it 'roundtrips hashes' do
|
||||
value = {
|
||||
'foo' => 1,
|
||||
'bar' => {
|
||||
'baz' => 'bing',
|
||||
}
|
||||
}
|
||||
expect(roundtrip(value)).to eq(value)
|
||||
end
|
||||
|
||||
it 'roundtrips strings' do
|
||||
expect(roundtrip('')).to eq('')
|
||||
expect(roundtrip('/foo/bar/baz')).to eq('/foo/bar/baz')
|
||||
end
|
||||
|
||||
it 'roundtrips uint8_t integers' do
|
||||
expect(roundtrip(0)).to eq(0)
|
||||
expect(roundtrip(1)).to eq(1)
|
||||
expect(roundtrip(0xff)).to eq(0xff)
|
||||
end
|
||||
|
||||
it 'roundtrips uint16_t integers' do
|
||||
expect(roundtrip(0x1234)).to eq(0x1234)
|
||||
end
|
||||
|
||||
it 'roundtrips uint32_t integers' do
|
||||
expect(roundtrip(0x12345678)).to eq(0x12345678)
|
||||
end
|
||||
|
||||
it 'roundtrips uint64_t integers' do
|
||||
expect(roundtrip(0x12345678abcdef00)).to eq(0x12345678abcdef00)
|
||||
end
|
||||
|
||||
it 'roundtrips floats' do
|
||||
expect(roundtrip(1234.5678)).to eq(1234.5678)
|
||||
end
|
||||
|
||||
it 'roundtrips `true` booleans' do
|
||||
expect(roundtrip(true)).to be_true
|
||||
end
|
||||
|
||||
it 'roundtrips `false` booleans' do
|
||||
expect(roundtrip(false)).to be_false
|
||||
end
|
||||
|
||||
it 'roundtrips nil' do
|
||||
expect(roundtrip(nil)).to be_nil
|
||||
end
|
||||
|
||||
describe '.load' do
|
||||
it 'rejects undersized input' do
|
||||
expect { described_class.load('') }.
|
||||
to raise_error(ArgumentError, /undersized/i)
|
||||
end
|
||||
|
||||
it 'rejects input without a binary marker' do
|
||||
expect { described_class.load('gibberish') }.
|
||||
to raise_error(ArgumentError, /missing/i)
|
||||
end
|
||||
|
||||
it 'rejects a missing payload header' do
|
||||
# binary protocol marker, but nothing else
|
||||
input = binary("\x00\x01")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /undersized/i)
|
||||
end
|
||||
|
||||
it 'rejects empty payloads' do
|
||||
# uint8_t size marker of zero
|
||||
input = binary("\x00\x01\x03\x00")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /empty/i)
|
||||
end
|
||||
|
||||
it 'rejects unrecognized payload markers' do
|
||||
# 0x10 is not a valid integer marker
|
||||
input = binary("\x00\x01\x10\x00")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /bad integer/i)
|
||||
end
|
||||
|
||||
it 'rejects undersized payload markers' do
|
||||
# int16_t marker, but only storage for int8_t
|
||||
input = binary("\x00\x01\x04\x00")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /overrun\b.+\bint16_t/i)
|
||||
end
|
||||
|
||||
it 'loads array values' do
|
||||
input = binary(
|
||||
"\x00\x01\x03\x16\x00\x03\x05\x03\x01\x02\x03" \
|
||||
"\x06foobar\x08\x09\x00\x03\x02\x03\x0a\x0a"
|
||||
)
|
||||
expect(described_class.load(input)).
|
||||
to eq([1, 'foobar', true, false, [10, nil]])
|
||||
end
|
||||
|
||||
it 'handles empty arrays' do
|
||||
input = binary("\x00\x01\x03\x03\x00\x03\x00")
|
||||
expect(described_class.load(input)).to eq([])
|
||||
end
|
||||
|
||||
it 'rejects arrays with incomplete headers' do
|
||||
input = binary("\x00\x01\x03\x02\x00\x03")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /incomplete array header/i)
|
||||
end
|
||||
|
||||
it 'rejects arrays with incomplete entries' do
|
||||
input = binary("\x00\x01\x03\x05\x00\x03\x10\x0a\x0a")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /unexpected end/i)
|
||||
end
|
||||
|
||||
it 'loads hash values' do
|
||||
input = binary(
|
||||
"\x00\x01\x03\x1a\x01\x03\x02\x02\x03\x03foo\x0a" \
|
||||
"\x02\x03\x03bar\x01\x03\x01\x02\x03\x03baz\x08"
|
||||
)
|
||||
expected = {
|
||||
'foo' => nil,
|
||||
'bar' => {
|
||||
'baz' => true,
|
||||
}
|
||||
}
|
||||
expect(described_class.load(input)).to eq(expected)
|
||||
end
|
||||
|
||||
it 'handles empty hashes' do
|
||||
input = binary("\x00\x01\x03\x03\x01\x03\x00")
|
||||
expect(described_class.load(input)).to eq({})
|
||||
end
|
||||
|
||||
it 'rejects hashes with incomplete headers' do
|
||||
input = binary("\x00\x01\x03\x02\x01\x03")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /incomplete hash header/i)
|
||||
end
|
||||
|
||||
it 'rejects hashes with invalid keys' do
|
||||
# keys must be strings; this one uses uses a number instead
|
||||
input = binary("\x00\x01\x03\x05\x01\x03\x01\x03\x00")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /not a number/i)
|
||||
end
|
||||
|
||||
it 'rejects hashes with missing keys' do
|
||||
input = binary("\x00\x01\x03\x03\x01\x03\x01")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /unexpected end/i)
|
||||
end
|
||||
|
||||
it 'rejects hashes with missing values' do
|
||||
input = binary("\x00\x01\x03\x09\x01\x03\x01\x02\x03\x03foo")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /unexpected end/i)
|
||||
end
|
||||
|
||||
it 'loads string values' do
|
||||
input = binary("\x00\x01\x03\x06\x02\x03\x03foo")
|
||||
expect(described_class.load(input)).to eq('foo')
|
||||
end
|
||||
|
||||
it 'handles empty strings' do
|
||||
input = binary("\x00\x01\x03\x03\x02\x03\x00")
|
||||
expect(described_class.load(input)).to eq('')
|
||||
end
|
||||
|
||||
if String.new.respond_to?(:encoding) # ie. Ruby >= 1.9
|
||||
it 'loads string values as ASCII-8BIT encoded strings' do
|
||||
input = binary("\x00\x01\x03\x06\x02\x03\x03foo")
|
||||
expect(described_class.load(input).encoding.to_s).to eq('ASCII-8BIT')
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects string values with incomplete headers' do
|
||||
input = binary("\x00\x01\x03\x01\x02")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /invalid string header/i)
|
||||
end
|
||||
|
||||
it 'rejects string values with invalid headers' do
|
||||
# expect a number indicating the string length, get a boolean instead
|
||||
input = binary("\x00\x01\x03\x05\x02\x08foo")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /bad integer/i)
|
||||
end
|
||||
|
||||
it 'rejects string values with insufficient storage' do
|
||||
# expect 3 bytes, get 2 instead
|
||||
input = binary("\x00\x01\x03\x05\x02\x03\x03fo")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /insufficient string storage/i)
|
||||
end
|
||||
|
||||
it 'loads uint8_t values' do
|
||||
input = binary("\x00\x01\x03\x02\x03\x12")
|
||||
expect(described_class.load(input)).to eq(0x12)
|
||||
end
|
||||
|
||||
it 'loads uint16_t values' do
|
||||
if little_endian?
|
||||
input = binary("\x00\x01\x03\x03\x04\x34\x12")
|
||||
else
|
||||
input = binary("\x00\x01\x03\x03\x04\x12\x34")
|
||||
end
|
||||
|
||||
expect(described_class.load(input)).to eq(0x1234)
|
||||
end
|
||||
|
||||
it 'loads uint32_t values' do
|
||||
if little_endian?
|
||||
input = binary("\x00\x01\x03\x05\x05\x78\x56\x34\x12")
|
||||
else
|
||||
input = binary("\x00\x01\x03\x05\x05\x12\x34\x56\x78")
|
||||
end
|
||||
|
||||
expect(described_class.load(input)).to eq(0x12345678)
|
||||
end
|
||||
|
||||
it 'loads int uint64_t values' do
|
||||
if little_endian?
|
||||
input = binary("\x00\x01\x03\x09\x06\xef\xcd\xab\x90\x78\x56\x34\x12")
|
||||
else
|
||||
input = binary("\x00\x01\x03\x09\x06\x12\x34\x56\x78\x90\xab\xcd\xef")
|
||||
end
|
||||
expect(described_class.load(input)).to eq(0x1234567890abcdef)
|
||||
end
|
||||
|
||||
it 'rejects int markers with missing values' do
|
||||
# expect an integer, but hit the end of the buffer
|
||||
input = binary("\x00\x01\x03\x01\x05")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /insufficient int storage/i)
|
||||
end
|
||||
|
||||
it 'rejects double markers with insufficient storage' do
|
||||
# double with 7 bytes of storage instead of the required 8 bytes
|
||||
input = binary("\x00\x01\x03\x08\x07\x00\x00\x00\x00\x00\x00\x00")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /insufficient double storage/i)
|
||||
end
|
||||
|
||||
it 'loads boolean `true` values' do
|
||||
input = binary("\x00\x01\x03\x01\x08")
|
||||
expect(described_class.load(input)).to be_true
|
||||
end
|
||||
|
||||
it 'loads boolean `false` values' do
|
||||
input = binary("\x00\x01\x03\x01\x09")
|
||||
expect(described_class.load(input)).to be_false
|
||||
end
|
||||
|
||||
it 'loads nil' do
|
||||
input = binary("\x00\x01\x03\x01\x0a")
|
||||
expect(described_class.load(input)).to be_nil
|
||||
end
|
||||
|
||||
it 'loads templates' do
|
||||
# this example includes a "skip" marker
|
||||
input = binary(
|
||||
"\x00\x01\x03\x28\x0b\x00\x03\x02\x02\x03\x04name" \
|
||||
"\x02\x03\x03age\x03\x03\x02\x03\x04fred\x03" \
|
||||
"\x14\x02\x03\x04pete\x03\x1e\x0c\x03\x19"
|
||||
)
|
||||
expected = [
|
||||
{ 'name' => 'fred', 'age' => 20 },
|
||||
{ 'name' => 'pete', 'age' => 30 },
|
||||
{ 'age' => 25 },
|
||||
]
|
||||
expect(described_class.load(input)).to eq(expected)
|
||||
end
|
||||
|
||||
it 'handles empty templates' do
|
||||
input = binary(
|
||||
"\x00\x01\x03\x12\x0b\x00\x03\x02\x02" \
|
||||
"\x03\x03foo\x02\x03\x03bar\x03\x00"
|
||||
)
|
||||
expect(described_class.load(input)).to eq([])
|
||||
end
|
||||
|
||||
it 'rejects templates without a header array' do
|
||||
input = binary("\x00\x01\x03\x01\x0b")
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /unexpected end/i)
|
||||
end
|
||||
|
||||
it 'rejects templates without a row items array' do
|
||||
input = binary(
|
||||
"\x00\x01\x03\x10\x0b\x00\x03\x02\x02" \
|
||||
"\x03\x03foo\x02\x03\x03bar"
|
||||
)
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /insufficient/i)
|
||||
end
|
||||
|
||||
it 'rejects templates with non-string header items' do
|
||||
input = binary(
|
||||
"\x00\x01\x03\x0e\x0b\x00\x03\x02\x02" \
|
||||
"\x03\x03foo\x03\x03\x03\x00"
|
||||
)
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /not a number/)
|
||||
end
|
||||
|
||||
it 'rejects templates with a header item array count mismatch' do
|
||||
input = binary(
|
||||
"\x00\x01\x03\x0a\x0b\x00\x03\x02\x02" \
|
||||
"\x03\x03foo"
|
||||
)
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /unexpected end/)
|
||||
end
|
||||
|
||||
it 'rejects templates with a row item count mismatch' do
|
||||
input = binary(
|
||||
"\x00\x01\x03\x25\x0b\x00\x03\x02\x02\x03\x04name" \
|
||||
"\x02\x03\x03age\x03\x03\x02\x03\x04fred\x03" \
|
||||
"\x14\x02\x03\x04pete\x03\x1e"
|
||||
)
|
||||
expect { described_class.load(input) }.
|
||||
to raise_error(ArgumentError, /unexpected end/)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.dump' do
|
||||
let(:query) do
|
||||
# this is the typical kind of query that Command-T will actually issue
|
||||
['query', '/some/path', {
|
||||
'expression' => ['type', 'f'],
|
||||
'fields' => ['name'],
|
||||
}]
|
||||
end
|
||||
|
||||
it 'serializes' do
|
||||
expect { described_class.dump(query) }.to_not raise_error
|
||||
end
|
||||
|
||||
if String.new.respond_to?(:encoding) # ie. Ruby >= 1.9
|
||||
it 'serializes to an ASCII-8BIT string' do
|
||||
expect(described_class.dump(query).encoding.to_s).to eq('ASCII-8BIT')
|
||||
end
|
||||
end
|
||||
|
||||
it 'generates a correct serialization' do
|
||||
# in Ruby 1.8, hashes aren't ordered, so two serializations are possible
|
||||
if little_endian?
|
||||
expected = [
|
||||
binary(
|
||||
"\x00\x01\x06\x49\x00\x00\x00\x00\x00\x00\x00\x00\x03\x03\x02\x03" \
|
||||
"\x05query\x02\x03\x0a/some/path\x01\x03\x02\x02\x03\x0a" \
|
||||
"expression\x00\x03\x02\x02\x03\x04type\x02\x03\x01f\x02\x03\x06" \
|
||||
"fields\x00\x03\x01\x02\x03\x04name"
|
||||
),
|
||||
binary(
|
||||
"\x00\x01\x06\x49\x00\x00\x00\x00\x00\x00\x00\x00\x03\x03\x02\x03" \
|
||||
"\x05query\x02\x03\x0a/some/path\x01\x03\x02\x02\x03\x06fields" \
|
||||
"\x00\x03\x01\x02\x03\x04name\x02\x03\x0aexpression\x00\x03\x02" \
|
||||
"\x02\x03\x04type\x02\x03\x01f"
|
||||
)
|
||||
]
|
||||
else
|
||||
expected = [
|
||||
binary(
|
||||
"\x00\x01\x06\x00\x00\x00\x00\x00\x00\x00\x49\x00\x03\x03\x02\x03" \
|
||||
"\x05query\x02\x03\x0a/some/path\x01\x03\x02\x02\x03\x0a" \
|
||||
"expression\x00\x03\x02\x02\x03\x04type\x02\x03\x01f\x02\x03\x06" \
|
||||
"fields\x00\x03\x01\x02\x03\x04name"
|
||||
),
|
||||
binary(
|
||||
"\x00\x01\x06\x00\x00\x00\x00\x00\x00\x00\x49\x00\x03\x03\x02\x03" \
|
||||
"\x05query\x02\x03\x0a/some/path\x01\x03\x02\x02\x03\x06fields" \
|
||||
"\x00\x03\x01\x02\x03\x04name\x02\x03\x0aexpression\x00\x03\x02" \
|
||||
"\x02\x03\x04type\x02\x03\x01f"
|
||||
)
|
||||
]
|
||||
end
|
||||
expect(expected).to include(described_class.dump(query))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
# Copyright 2010-2014 Greg Hurrell. All rights reserved.
|
||||
# Licensed under the terms of the BSD 2-clause license.
|
||||
|
||||
if !Object.const_defined?('Bundler')
|
||||
require 'rubygems'
|
||||
require 'bundler'
|
||||
Bundler.setup
|
||||
end
|
||||
require 'rspec'
|
||||
|
||||
lib = File.expand_path('../ruby', File.dirname(__FILE__))
|
||||
unless $LOAD_PATH.include? lib
|
||||
$LOAD_PATH.unshift lib
|
||||
end
|
||||
|
||||
require 'command-t'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.mock_framework = :rr
|
||||
end
|
||||
|
||||
# Fake top-level VIM implementation, for stubbing.
|
||||
module VIM
|
||||
class << self
|
||||
def evaluate(*args); end
|
||||
end
|
||||
|
||||
class Buffer; end
|
||||
end
|
1
vim/vim.symlink/bundle/pick.vim
Submodule
1
vim/vim.symlink/bundle/pick.vim
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 7acb3bce8433d88f05b75ed4b4e1c7af5377ebf4
|
|
@ -25,6 +25,9 @@ set number
|
|||
" Set editor shell to bash, for Syntastic compatibility
|
||||
set shell=bash
|
||||
|
||||
" Find files
|
||||
nnoremap <leader>t :call PickFile()<cr>
|
||||
|
||||
" Tab config options
|
||||
set expandtab
|
||||
set tabstop=2
|
||||
|
@ -107,9 +110,8 @@ function! HLNext (blinktime)
|
|||
endfunction
|
||||
|
||||
" Disable match-paren
|
||||
" Re-enabled for now, to remember why I turned it off
|
||||
" let loaded_matchparen = 1
|
||||
set showmatch
|
||||
" It has really bad colours and it displays terribly.
|
||||
set noshowmatch
|
||||
|
||||
" Make searches case sensitive only if an upper case character has been typed
|
||||
set ignorecase smartcase
|
||||
|
@ -203,7 +205,6 @@ set statusline+=\ %{SyntasticStatuslineFlag()} " syntastic errors
|
|||
"""""""""""""""""""""
|
||||
|
||||
let g:syntastic_enable_signs = 0
|
||||
let g:syntastic_check_on_open = 1
|
||||
let g:syntastic_javascript_checkers = ['eslint']
|
||||
let g:syntastic_stl_format='[%t errors, first: %F]'
|
||||
|
||||
|
@ -212,7 +213,7 @@ let g:syntastic_stl_format='[%t errors, first: %F]'
|
|||
"""""""""""""""
|
||||
|
||||
noremap <leader>y "*y
|
||||
nnoremap <leader>. <c-^>
|
||||
nnoremap <leader><leader> <c-^>
|
||||
|
||||
" Move around splits with <c-hjkl>
|
||||
nnoremap <c-j> <c-w>j
|
||||
|
|
Loading…
Add table
Reference in a new issue