pax_global_header00006660000000000000000000000064127165262630014524gustar00rootroot0000000000000052 comment=99d06fa02753be74cc44f487c255d9326eca8c9a command-t-4.0/000077500000000000000000000000001271652626300132465ustar00rootroot00000000000000command-t-4.0/.ctags000066400000000000000000000000631271652626300143470ustar00rootroot00000000000000--exclude=.git --exclude=Makefile --exclude=vendor command-t-4.0/.gitattributes000066400000000000000000000000411271652626300161340ustar00rootroot00000000000000data/benchmark.yml export-ignore command-t-4.0/.gitignore000066400000000000000000000001061271652626300152330ustar00rootroot00000000000000.release-notes.txt .ruby-version /.bundle /data/log.yml vendor/bundle command-t-4.0/.gitmodules000066400000000000000000000003351271652626300154240ustar00rootroot00000000000000[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 command-t-4.0/.mailmap000066400000000000000000000007471271652626300146770ustar00rootroot00000000000000Greg Hurrell Jerome Castaneda Kevin Webster Kien Nguyen Duc Nicholas T. Nicolas Alpi Nikolai Aleksandrovich Pavlov Noon Silk Sherzod Gapirov Sung Pae Sung Pae guns command-t-4.0/.rspec000066400000000000000000000000111271652626300143530ustar00rootroot00000000000000--colour command-t-4.0/.vim_org.yml000066400000000000000000000000101271652626300155000ustar00rootroot00000000000000--- {} command-t-4.0/Gemfile000066400000000000000000000001361271652626300145410ustar00rootroot00000000000000source 'https://rubygems.org' gem 'nokogiri' gem 'mechanize' gem 'rake' gem 'rr' gem 'rspec' command-t-4.0/Gemfile.lock000066400000000000000000000020431271652626300154670ustar00rootroot00000000000000GEM 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 command-t-4.0/LICENSE000066400000000000000000000024301271652626300142520ustar00rootroot00000000000000Copyright 2010-present 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. command-t-4.0/README.md000066400000000000000000000021451271652626300145270ustar00rootroot00000000000000# Command-T ![](https://raw.githubusercontent.com/wincent/command-t/media/command-t.gif) Command-T is a Vim plug-in that provides an extremely fast "fuzzy" mechanism for: - Opening files and buffers - Jumping to tags and help - Running commands, or previous searches and commands with a minimal number of keystrokes. Files are selected by typing characters that appear in their paths, and are ranked by an algorithm which knows that characters that appear in certain locations (for example, immediately after a path separator) should be given more weight. Files can be opened in the current window, or in splits or tabs. Many configuration options are provided. Speed is the primary design goal, along with providing high-quality, intuitive match ordering. The hand-crafted matching algorithm, implemented in low-level C and combined with parallelized search, input debouncing, integration with Watchman and many other optimizations, mean that Command-T is the fastest fuzzy file finder bar none. --- For more information, see [the documentation](https://github.com/wincent/command-t/blob/master/doc/command-t.txt). command-t-4.0/Rakefile000066400000000000000000000077731271652626300147310ustar00rootroot00000000000000require '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 task :default => :help 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 For a full list of available tasks: rake -T END end desc 'Run specs' task :spec do system 'bundle exec rspec spec' bail_on_failure end desc 'Create archive' task :archive => :check_tag do system "git archive -o command-t-#{version}.zip HEAD -- ." bail_on_failure 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 '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 'Verify that required dependencies are installed' task :check_deps do begin require 'rubygems' require 'mechanize' rescue LoadError warn 'mechanize not installed (`gem install mechanize` in order to upload)' end end desc 'Run checks prior to release' task :prerelease => [:make, :spec, :archive, :check_tag, :check_deps] desc 'Prepare release notes from HISTORY' task :notes do File.open('.release-notes.txt', 'w') do |out| lines = File.readlines('doc/command-t.txt').each(&: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 = File.readlines('.release-notes.txt').reject do |line| line =~ /^(#.*|\s*)$/ # filter comment lines and blank lines end.join File.open('.release-notes.txt', 'w') do |out| out.print filtered end end namespace :upload do desc 'Upload current archive to Amazon S3' task :s3 => :archive do sh 'aws --curl-options=--insecure put ' + "s3.wincent.com/command-t/releases/command-t-#{version}.zip " + "command-t-#{version}.zip" sh 'aws --curl-options=--insecure put ' + "s3.wincent.com/command-t/releases/command-t-#{version}.zip?acl " + '--public' end desc 'Upload current archive to www.vim.org' task :vim => [:archive, :notes] do sh "vendor/vimscriptuploader/vimscriptuploader.rb \ --id 3025 \ --file command-t-#{version}.zip \ --message-file .release-notes.txt \ --version #{version} \ --config ~/.vim_org.yml \ .vim_org.yml" end desc 'Upload current archive 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 command-t-4.0/appstream/000077500000000000000000000000001271652626300152425ustar00rootroot00000000000000command-t-4.0/appstream/vim-command-t.metainfo.xml000066400000000000000000000010261271652626300222340ustar00rootroot00000000000000 vim-command-t gvim.desktop command-t Provides an extremely fast, intuitive mechanism for opening files with a minimal number of keystrokes https://github.com/wincent/command-t CC0-1.0 BSD v.ondruch@gmail.com command-t-4.0/autoload/000077500000000000000000000000001271652626300150565ustar00rootroot00000000000000command-t-4.0/autoload/commandt.vim000066400000000000000000000076321271652626300174050ustar00rootroot00000000000000" Copyright 2010-present 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 " " Functions " function! s:RubyWarning() abort 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#BufferFinder() abort if has('ruby') ruby $command_t.show_buffer_finder else call s:RubyWarning() endif endfunction function! commandt#CommandFinder() abort if has('ruby') ruby $command_t.show_command_finder else call s:RubyWarning() endif endfunction function! commandt#FileFinder(arg) abort if has('ruby') ruby $command_t.show_file_finder else call s:RubyWarning() endif endfunction function! commandt#JumpFinder() abort if has('ruby') ruby $command_t.show_jump_finder else call s:RubyWarning() endif endfunction function! commandt#MRUFinder() abort if has('ruby') ruby $command_t.show_mru_finder else call s:RubyWarning() endif endfunction function! commandt#HelpFinder() abort if has('ruby') ruby $command_t.show_help_finder else call s:RubyWarning() endif endfunction function! commandt#HistoryFinder() abort if has('ruby') ruby $command_t.show_history_finder else call s:RubyWarning() endif endfunction function! commandt#LineFinder() abort if has('ruby') let g:CommandTCurrentBuffer=bufnr('%') ruby $command_t.show_line_finder else call s:RubyWarning() endif endfunction function! commandt#SearchFinder() abort if has('ruby') ruby $command_t.show_search_finder else call s:RubyWarning() endif endfunction function! commandt#TagFinder() abort if has('ruby') ruby $command_t.show_tag_finder else call s:RubyWarning() endif endfunction function! commandt#Flush() abort if has('ruby') ruby $command_t.flush else call s:RubyWarning() endif endfunction function! commandt#Load() abort if !has('ruby') call s:RubyWarning() endif endfunction " For possible use in status lines. function! commandt#ActiveFinder() abort if has('ruby') ruby ::VIM::command "return '#{$command_t.active_finder}'" else return '' endif endfunction " For possible use in status lines. function! commandt#Path() abort if has('ruby') ruby ::VIM::command "return '#{($command_t.path || '').gsub(/'/, "''")}'" else return '' endif endfunction " For possible use in status lines. function! commandt#CheckBuffer(buffer_number) abort if has('ruby') execute 'ruby $command_t.return_is_own_buffer' a:buffer_number else return 0 endif endfunction if !has('ruby') finish endif " note that we only start tracking buffers from first (autoloaded) use of Command-T augroup CommandTMRUBuffer autocmd! autocmd BufEnter * ruby CommandT::MRU.touch autocmd BufDelete * ruby CommandT::MRU.delete augroup END ruby << EOF # require Ruby files begin require 'command-t' # Make sure we're running with the same version of Ruby that Command-T was # compiled with. patchlevel = defined?(RUBY_PATCHLEVEL) ? RUBY_PATCHLEVEL : nil if CommandT::Metadata::UNKNOWN == true || ( CommandT::Metadata::EXPECTED_RUBY_VERSION == RUBY_VERSION && CommandT::Metadata::EXPECTED_RUBY_PATCHLEVEL == patchlevel ) require 'command-t/ext' # eager load, to catch compilation problems early $command_t = CommandT::Controller.new else $command_t = CommandT::Stub.new end 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 $command_t = CommandT::Stub.new end EOF command-t-4.0/autoload/commandt/000077500000000000000000000000001271652626300166605ustar00rootroot00000000000000command-t-4.0/autoload/commandt/private.vim000066400000000000000000000043361271652626300210550ustar00rootroot00000000000000" Copyright 2010-present Greg Hurrell. All rights reserved. " Licensed under the terms of the BSD 2-clause license. function! commandt#private#ListMatches() abort ruby $command_t.list_matches endfunction function! commandt#private#HandleKey(arg) abort ruby $command_t.handle_key endfunction function! commandt#private#Backspace() abort ruby $command_t.backspace endfunction function! commandt#private#Delete() abort ruby $command_t.delete endfunction function! commandt#private#AcceptSelection() abort ruby $command_t.accept_selection endfunction function! commandt#private#AcceptSelectionTab() abort ruby $command_t.accept_selection :command => $command_t.tab_command endfunction function! commandt#private#AcceptSelectionSplit() abort ruby $command_t.accept_selection :command => $command_t.split_command endfunction function! commandt#private#AcceptSelectionVSplit() abort ruby $command_t.accept_selection :command => $command_t.vsplit_command endfunction function! commandt#private#Quickfix() abort ruby $command_t.quickfix endfunction function! commandt#private#Refresh() abort ruby $command_t.refresh endfunction function! commandt#private#ToggleFocus() abort ruby $command_t.toggle_focus endfunction function! commandt#private#Cancel() abort ruby $command_t.cancel endfunction function! commandt#private#SelectNext() abort ruby $command_t.select_next endfunction function! commandt#private#SelectPrev() abort ruby $command_t.select_prev endfunction function! commandt#private#Clear() abort ruby $command_t.clear endfunction function! commandt#private#ClearPrevWord() abort ruby $command_t.clear_prev_word endfunction function! commandt#private#CursorLeft() abort ruby $command_t.cursor_left endfunction function! commandt#private#CursorRight() abort ruby $command_t.cursor_right endfunction function! commandt#private#CursorEnd() abort ruby $command_t.cursor_end endfunction function! commandt#private#CursorStart() abort ruby $command_t.cursor_start endfunction function! commandt#private#RunAutocmd(cmd) abort if v:version > 703 || v:version == 703 && has('patch438') execute 'silent doautocmd User ' . a:cmd else execute 'silent doautocmd User ' . a:cmd endif endfunction command-t-4.0/bin/000077500000000000000000000000001271652626300140165ustar00rootroot00000000000000command-t-4.0/bin/benchmarks/000077500000000000000000000000001271652626300161335ustar00rootroot00000000000000command-t-4.0/bin/benchmarks/matcher.rb000077500000000000000000000151361271652626300201140ustar00rootroot00000000000000#!/usr/bin/env ruby # # Copyright 2013-present 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' data = YAML.load_file( File.expand_path('../../data/benchmark.yml', File.dirname(__FILE__)) ) log = File.expand_path('../../data/log.yml', File.dirname(__FILE__)) log_data = File.exist?(log) ? YAML.load_file(log) : [] threads = CommandT::Util.processor_count puts "Starting benchmark run (PID: #{Process.pid})" now = Time.now.to_s TIMES = ENV.fetch('TIMES', 20).to_i results = TIMES.times.map do 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| query.split(//).reduce('') do |acc, char| query = acc + char matcher.sorted_matches_for( query, :threads => threads, :recurse => ENV.fetch('RECURSE', '1') == '1' ) query end end end end end end end DIFFERENCE = 0 ABSOLUTE = 1 SIGN = 2 # Test for significance via Wilcoxon Signed Rank test. # # @see http://vassarstats.net/textbook/ch12a.html def significance(last, current) return 0.0 if last.length != current.length table = last.zip(current).map do |l, c| [ l - c, # difference (l - c).abs, # absolute difference (l - c).zero? ? nil : (l - c) / (l - c).abs, # signedness (-1 or +1) ] end table = table.select { |diff, abs, sig| !diff.zero? } table = table.sort do |(a_diff, a_abs, a_sig), (b_diff, b_abs, b_sig)| a_abs <=> b_abs end rank = 1 table = table.map.with_index do |row, i| count = 0 rank = table.map.with_index do |(diff, abs, sig), i| if abs == row[ABSOLUTE] count += 1 i + 1 else nil end end.compact.reduce(0) { |acc, val| acc + val }.to_f / count row + [row[SIGN] * rank] end n = table.length w = table.reduce(0) { |acc, (diff, abs, sig, signed_rank)| acc + signed_rank } if n < 10 p_value = 0 thresholds = [ [], [], [], [], [], [[15, 0.05]], [[17, 0.05], [21, 0.025]], [[22, 0.05], [25, 0.025], [28, 0.01]], [[26, 0.05], [30, 0.025], [34, 0.01], [36, 0.005]], [[29, 0.05], [35, 0.025], [39, 0.01], [43, 0.005]], ][n] while limit = thresholds.pop do if w.abs >= limit[0] p_value = limit[1] break end end else sd = Math.sqrt(n * (n + 1) * (2 * n + 1) / 6) z = ((w - 0.5) / sd).abs if z >= 3.291 p_value = 0.0005 elsif z >= 2.576 p_value = 0.005 elsif z >= 2.326 p_value = 0.01 elsif z >= 1.960 p_value = 0.025 elsif z >= 1.645 p_value = 0.05 else p_value = 0 end end p_value end results = results.reduce({}) do |acc, run| run.each do |result| acc[result.label] ||= {} acc[result.label]['real'] ||= [] acc[result.label]['real'] << result.real acc[result.label]['total'] ||= [] acc[result.label]['total'] << result.total end acc end previous = YAML.load_file(log).last['results'] rescue nil results.keys.each do |label| test = results[label] test['real (best)'] = test['real'].min test['total (best)'] = test['total'].min test['real (avg)'] = test['real'].reduce(:+) / test['real'].length test['real (+/-)'] = previous && previous[label] && (test['real (avg)'] - previous[label]['real (avg)']) / test['real (avg)'] * 100 test['real (significance)'] = significance(previous[label]['real'], test['real']) if previous && previous[label] test['total (avg)'] = test['total'].reduce(:+) / test['total'].length test['total (+/-)'] = previous && previous[label] && (test['total (avg)'] - previous[label]['total (avg)']) / test['total (avg)'] * 100 test['total (significance)'] = significance(previous[label]['total'], test['total']) if previous && previous[label] test['real (variance)'] = test['real'].reduce(0) { |acc, value| acc + (test['real (avg)'] - value) ** 2 } / test['real'].length test['total (variance)'] = test['total'].reduce(0) { |acc, value| acc + (test['total (avg)'] - value) ** 2 } / test['total'].length test['real (sd)'] = Math.sqrt(test['real (variance)']) test['total (sd)'] = Math.sqrt(test['total (variance)']) end log_data.push({ 'time' => now, 'results' => results, }) File.open(log, 'w') { |f| f.write(log_data.to_yaml) } def print_table(rows) rows.each do |row| row.each.with_index do |cell, i| width = rows.reduce(0) { |acc, row| row[i].length > acc ? row[i].length : acc } if i.zero? print align(cell, width) else print(' ' + align(cell, width)) end end puts end end def align(str, width) if str.respond_to?(:justify) case str.justify when :center ('%*s%s%*s' % [ ((width - str.length) / 2.0).round, '', str, ((width - str.length) / 2.0).round, '', ])[0...width] when :left '%-*s' % [width, str] else '%*s' % [width, str] end else '%*s' % [width, str] end end AnnotatedString = Struct.new(:length, :to_s, :justify) def center(str) AnnotatedString.new(str.length, str, :center) end def float(x) '%.5f' % x end def parens(x) "(#{x})" end def trim(str) str.sub(/0+\z/, '') end def maybe(value, default = '') if value yield value else default end end puts "\n\nSummary of cpu time and (wall-clock time):\n" headers = [ [ '', center('best'), center('avg'), center('sd'), center('+/-'), center('p'), center('(best)'), center('(avg)'), center('(sd)'), center('+/-'), center('p'), ] ] rows = headers + results.map do |(label, data)| [ label, float(data['total (best)']), float(data['total (avg)']), float(data['total (sd)']), maybe(data['total (+/-)'], center('?')) { |value| '[%+0.1f%%]' % value }, maybe(data['total (significance)']) { |value| value > 0 ? trim(float(value)) : '' }, parens(float(data['real (best)'])), parens(float(data['real (avg)'])), parens(float(data['real (sd)'])), maybe(data['real (+/-)'], center('?')) { |value| '[%+0.1f%%]' % value }, maybe(data['real (significance)']) { |value| value > 0 ? trim(float(value)) : '' }, ] end print_table(rows) command-t-4.0/bin/benchmarks/watchman.rb000077500000000000000000000044331271652626300202710ustar00rootroot00000000000000#!/usr/bin/env ruby # # Copyright 2014-present 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 command-t-4.0/command-t.gemspec000066400000000000000000000016561271652626300165020ustar00rootroot00000000000000Gem::Specification.new do |s| s.name = 'command-t' # see note in the Rakefile about how intermediate version numbers # can break RubyGems s.version = `git describe --abbrev=0`.chomp s.authors = ['Greg Hurrell'] s.email = 'greg@hurrell.net' s.files = ['README.md', 'LICENSE', 'Gemfile', 'Rakefile'] + `git ls-files -z ruby doc`.split("\x0") s.license = 'BSD' s.require_path = 'ruby' s.extensions = 'ruby/command-t/extconf.rb' s.executables = [] s.has_rdoc = false s.homepage = 'https://github.com/wincent/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 command-t-4.0/data/000077500000000000000000000000001271652626300141575ustar00rootroot00000000000000command-t-4.0/doc/000077500000000000000000000000001271652626300140135ustar00rootroot00000000000000command-t-4.0/doc/.gitignore000066400000000000000000000000051271652626300157760ustar00rootroot00000000000000tags command-t-4.0/doc/command-t.txt000066400000000000000000002416071271652626300164450ustar00rootroot00000000000000*command-t.txt* Command-T plug-in for Vim *command-t* CONTENTS *command-t-contents* 1. Introduction |command-t-intro| 2. Requirements |command-t-requirements| 3. Installation |command-t-installation| 4. Trouble-shooting |command-t-trouble-shooting| 5. Usage |command-t-usage| 6. Commands |command-t-commands| 7. Mappings |command-t-mappings| 8. Options |command-t-options| 9. Integration and Extensibility |command-t-integration-and-extensibility| 10. FAQ |command-t-faq| 11. Tips |command-t-tips| 12. Authors |command-t-authors| 13. Development |command-t-development| 14. Website |command-t-website| 15. Related projects |command-t-related-projects| 16. License |command-t-license| 17. History |command-t-history| INTRODUCTION *command-t-intro* The Command-T plug-in provides an extremely fast, intuitive mechanism for opening files and buffers with a minimal number of keystrokes. It's named "Command-T" because it is inspired by the "Go to File" window bound to Command-T in TextMate. Files are selected by typing characters that appear in their paths, and are ordered by an algorithm which knows that characters that appear in certain locations (for example, immediately after a path separator) should be given more weight. To search efficiently, especially in large projects, you should adopt a "path-centric" rather than a "filename-centric" mentality. That is you should think more about where the desired file is found rather than what it is called. This means narrowing your search down by including some characters from the upper path components rather than just entering characters from the filename itself. REQUIREMENTS *command-t-requirements* The plug-in requires Vim compiled with Ruby support, a compatible Ruby installation at the operating system level, and a C compiler to build the Ruby extension. 1. Vim compiled with Ruby support ~ You can check for Ruby support by launching Vim with the --version switch: vim --version If "+ruby" appears in the version information then your version of Vim has Ruby support. Another way to check is to simply try using the :ruby command from within Vim itself: :ruby 1 If your Vim lacks support you'll see an error message like this: E319: Sorry, the command is not available in this version The version of Vim distributed with OS X may not include Ruby support (for example, Snow Leopard, which was the current version of OS X when Command-T was first released, did not support Ruby in the system Vim, but the current version of OS X at the time of writing, Mavericks, does). All recent versions of MacVim come with Ruby support; it is available from: http://github.com/b4winckler/macvim/downloads For Windows users, the Vim 7.2 executable available from www.vim.org does include Ruby support, and is recommended over version 7.3 (which links against Ruby 1.9, but apparently has some bugs that need to be resolved). 2. Ruby ~ In addition to having Ruby support in Vim, your system itself must have a compatible Ruby install. "Compatible" means the same version as Vim itself links against. If you use a different version then Command-T is unlikely to work (see |command-t-trouble-shooting| below). On OS X Snow Leopard, Lion and Mountain Lion, the system comes with Ruby 1.8.7 and all recent versions of MacVim (the 7.2 snapshots and 7.3) are linked against it. On OS X Mavericks, the default system Ruby is 2.0, but MacVim continues to link against 1.8.7, as does the Apple-provided Vim. Ruby 1.8.7 is present on the system at: /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby On OS X Yosemite, the default system Ruby is 2.0, and the Vim that comes with the system links against it. On Linux and similar platforms, the linked version of Ruby will depend on your distribution. You can usually find this out by examining the compilation and linking flags displayed by the |:version| command in Vim, and by looking at the output of: :ruby puts "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" Or, for very old versions of Ruby which don't define `RUBY_PATCHLEVEL`: :ruby puts RUBY_VERSION Some Linux distributions package Ruby development tools separately from Ruby itself; if you're using such a system you may need to install the "ruby-dev", "ruby-devel" or similar package using your system's package manager in order to build Command-T. A suitable Ruby environment for Windows can be installed using the Ruby 1.8.7-p299 RubyInstaller available at: http://rubyinstaller.org/downloads/archives If using RubyInstaller be sure to download the installer executable, not the 7-zip archive. When installing mark the checkbox "Add Ruby executables to your PATH" so that Vim can find them. 3. C compiler ~ Part of Command-T is implemented in C as a Ruby extension for speed, allowing it to work responsively even on directory hierarchies containing enormous numbers of files. As such, a C compiler is required in order to build the extension and complete the installation. On OS X, this can be obtained by installing the Xcode Tools from the App Store. On Windows, the RubyInstaller Development Kit can be used to conveniently install the necessary tool chain: http://rubyinstaller.org/downloads/archives At the time of writing, the appropriate development kit for use with Ruby 1.8.7 is DevKit-3.4.5r3-20091110. To use the Development Kit extract the archive contents to your C:\Ruby folder. INSTALLATION *command-t-installation* You install Command-T by obtaining the source files and building the C extension. The recommended way to get the source is by using a plug-in management system. There are several such systems available, and my preferred one is Pathogen (https://github.com/tpope/vim-pathogen) due to its simplicity and robustness. Other plug-in managers include: - Vundle: https://github.com/gmarik/Vundle.vim (see |command-t-vundle|) - NeoBundle: https://github.com/Shougo/neobundle.vim (see |command-t-neobundle|) - VAM: https://github.com/MarcWeber/vim-addon-manager (see |command-t-vam|) - vim-plug: https://github.com/junegunn/vim-plug (see |command-t-vim-plug|) The following sections outline how to use each of these managers to download Command-T, and finally |command-t-compile| describes how to compile it. *command-t-pathogen* Obtaining the source using Pathogen ~ Pathogen is a plugin that allows you to maintain plugin installations in separate, isolated subdirectories under the "bundle" directory in your |'runtimepath'|. The following examples assume that you already have Pathogen installed and configured, and that you are installing into `~/.vim/bundle`. If you manage your entire `~/.vim` folder using Git then you can add the Command-T repository as a submodule: cd ~/.vim git submodule add https://github.com/wincent/command-t.git bundle/command-t git submodule init Or if you just wish to do a simple clone instead of using submodules: cd ~/.vim git clone https://github.com/wincent/command-t.git bundle/command-t Once you have a local copy of the repository you can update it at any time with: cd ~/.vim/bundle/command-t git pull Or you can switch to a specific release with: cd ~/.vim/bundle/command-t git checkout 1.10 To generate the help tags under Pathogen it is necessary to do so explicitly from inside Vim: :call pathogen#helptags() For more information about Pathogen, see: https://github.com/tpope/vim-pathogen *command-t-vundle* Obtaining the source using Vundle ~ Anywhere between the calls to `vundle#begin` and `vundle#end` in your |.vimrc|, add a `Plugin` directive telling Vundle of your desire to use Command-T: call vundle#begin() Plugin 'wincent/command-t' call vundle#end() To actually install the plug-in run `:PluginInstall` from inside Vim. After this, you can proceed to compile Command-T (see |command-t-compile|). For more information about Vundle, see: https://github.com/gmarik/Vundle.vim *command-t-neobundle* Obtaining the source using NeoBundle ~ Anywhere between the calls to `neobundle#begin` and `neobundle#end` in your |.vimrc|, add a `NeoBundle` directive telling NeoBundle of your desire to use Command-T: call neobundle#begin(expand('~/.vim/bundle/')) NeoBundle 'wincent/command-t' call neobundle#end() To actually install the plug-in run `:NeoBundleInstall` from inside Vim. After this, you can proceed to compile Command-T (see |command-t-compile|) manually, or if you wish to have NeoBundle attempt to do so automatically, you can use a configuration like this: call neobundle#begin(expand('~/.vim/bundle/')) NeoBundle 'wincent/command-t', { \ 'build_commands': ['make', 'ruby'], \ 'build': { \ 'unix': 'cd ruby/command-t && { make clean; ruby extconf.rb && make }' \ } \ } call neobundle#end() For more information about NeoBundle, see: https://github.com/Shougo/neobundle.vim *command-t-vam* Obtaining the source using VAM ~ After the call to `vam#ActivateAddons` in your |.vimrc|, add Command-T to the `VAMActivate` call: call vam#ActivateAddons([]) VAMActivate github:wincent/command-t After VAM has downloaded Command-T, you can proceed to compile it (see |command-t-compile|). For more information about VAM, see: https://github.com/MarcWeber/vim-addon-manager *command-t-vim-plug* Obtaining the source using vim-plug ~ Anywhere between the calls to `plug#begin` and `plug#end` in your |.vimrc|, add a `Plug` directive telling vim-plug of your desire to use Command-T: call plug#begin() Plugin 'wincent/command-t' call plug#end() To actually install the plug-in run `:PlugInstall` from inside Vim. After this, you can proceed to compile Command-T (see |command-t-compile|) manually, or if you wish to have vim-plug attempt to do so automatically, you can use a configuration like this: call plug#begin() Plug 'wincent/command-t', { \ 'do': 'cd ruby/command-t && ruby extconf.rb && make' \ } call plug#end() For more information about vim-plug, see: https://github.com/junegunn/vim-plug *command-t-compile* Compiling Command-T ~ The C extension must be built, which can be done from the shell. If you use a typical Pathogen, Vundle or NeoBundle set-up then the files were installed inside `~/.vim/bundle/command-t`. A typical VAM installation path might be `~/.vim/vim-addons/command-t`, and vim-plug would be `~/.vim/plugged/Command-T/`. Wherever the Command-T files were installed, you can build the extension by changing to the `ruby/command-t` subdirectory and running a couple of commands as follows: cd ~/.vim/bundle/command-t/ruby/command-t ruby extconf.rb make Note: If you are an RVM or rbenv user, you must build CommandT using the same version of Ruby that Vim itself is linked against. You can find out the version that Vim is linked against by issuing following command inside Vim: :ruby puts "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" Or, for very old versions of Ruby which don't define `RUBY_PATCHLEVEL`: :ruby puts RUBY_VERSION You can either set your version of Ruby to the output of the above command and then build Command-T, or re-build Vim with a version of Ruby you prefer. To set the version of Ruby, issue one of the following commands before the `make` command: rvm use VERSION # where "VERSION" is the Ruby version Vim is linked against rbenv local VERSION If you decide to re-build Vim, for OS X, you can simply use Homebrew to uninstall and re-install Vim with following commands: brew uninstall vim brew install vim For more information about Homebrew, see: http://brew.sh Note: If you are on OS X Mavericks and compiling against MacVim, the default system Ruby is 2.0 but MacVim still links against the older 1.8.7 Ruby that is also bundled with the system; in this case the build command becomes: cd ~/.vim/bundle/command-t/ruby/command-t /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb make Note: Make sure you compile targeting the same architecture Vim was built for. For instance, MacVim binaries are built for i386, but sometimes GCC compiles for x86_64. First you have to check the platform Vim was built for: vim --version ... Compilation: gcc ... -arch i386 ... ... and make sure you use the correct ARCHFLAGS during compilation: export ARCHFLAGS="-arch i386" make Note: If you are on Fedora 17+, you can install Command-T from the system repository with: su -c 'yum install vim-command-t' *command-t-appstream* AppStream Metadata ~ When preparing a Command-T package for distribution on Linux using Gnome Software or another AppStream compatible application, there is a metafile in appstream directory. You can find more about AppStream specification at: http://www.freedesktop.org/software/appstream/docs/ TROUBLE-SHOOTING *command-t-trouble-shooting* Most installation problems are caused by a mismatch between the version of Ruby on the host operating system, and the version of Ruby that Vim itself linked against at compile time. For example, if one is 32-bit and the other is 64-bit, or one is from the Ruby 1.9 series and the other is from the 1.8 series, then the plug-in is not likely to work. On OS X, Apple tends to change the version of Ruby that comes with the system with each major release. See |command-t-requirements| above for details about specific versions. If you wish to use custom builds of Ruby or of MacVim then you will have to take extra care to ensure that the exact same Ruby environment is in effect when building Ruby, Vim and the Command-T extension. For Windows, the following combination is known to work: - Vim 7.2 from http://www.vim.org/download.php: ftp://ftp.vim.org/pub/vim/pc/gvim72.exe - Ruby 1.8.7-p299 from http://rubyinstaller.org/downloads/archives: http://rubyforge.org/frs/download.php/71492/rubyinstaller-1.8.7-p299.exe - DevKit 3.4.5r3-20091110 from http://rubyinstaller.org/downloads/archives: http://rubyforge.org/frs/download.php/66888/devkit-3.4.5r3-20091110.7z If a problem occurs the first thing you should do is inspect the output of: ruby extconf.rb make During the installation, and: vim --version And compare the compilation and linker flags that were passed to the extension and to Vim itself when they were built. If the Ruby-related flags or architecture flags are different then it is likely that something has changed in your Ruby environment and the extension may not work until you eliminate the discrepancy. From inside Vim, you can confirm the version of Ruby that it is using by issuing this command: :ruby puts "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" Or, for very old versions of Ruby which don't define `RUBY_PATCHLEVEL`: :ruby puts RUBY_VERSION Additionally, beware that if you change your installation method for Command-T (for example, switching from one plugin manager to another) you should verify that you remove all of the files installed by the previous installation method; if you fail to do this, Vim may end up executing the old code, invalidating all your attempts to get Vim and Command-T using the same version of Ruby. Finally, if you end up changing Ruby versions or upgrading other parts of the system (the operating system itself, or Vim, for example), you may need to issue an additional "make clean" before re-building Command-T; this ensures that potentially incompatible build products are disposed of and re-created during the build: make clean ruby extconf.rb make USAGE *command-t-usage* Bring up the Command-T file window by typing: t This mapping is set up automatically for you, provided you do not already have a mapping for t or |:CommandT|. You can also bring up the file window by issuing the command: :CommandT A prompt will appear at the bottom of the screen along with a file window showing all of the files in the current project (the project directory is determined according to the value of the |g:CommandTTraverseSCM| setting, which defaults to the SCM root of the current file). You can pass in an optional path argument to |:CommandT| (relative to the current working directory (|:pwd|) or absolute): :CommandT ../path/to/other/files Type letters in the prompt to narrow down the selection, showing only the files whose paths contain those letters in the specified order. Letters do not need to appear consecutively in a path in order for it to be classified as a match. Once the desired file has been selected it can be opened by pressing . (By default files are opened in the current window, but there are other mappings that you can use to open in a vertical or horizontal split, or in a new tab.) Note that if you have |'nohidden'| set and there are unsaved changes in the current window when you press then opening in the current window would fail; in this case Command-T will open the file in a new split. The following mappings are active when the prompt has focus: delete the character to the left of the cursor delete the character at the cursor move the cursor one character to the left move the cursor one character to the left move the cursor one character to the right move the cursor one character to the right move the cursor to the start (left) move the cursor to the end (right) clear the contents of the prompt change focus to the file listing The following mappings are active when the file listing has focus: change focus to the prompt The following mappings are active when either the prompt or the file listing has focus: open the selected file open the selected file in a new split window open the selected file in a new split window open the selected file in a new vertical split window open the selected file in a new tab select next file in the file listing select next file in the file listing select next file in the file listing select previous file in the file listing select previous file in the file listing select previous file in the file listing flush the cache (see |:CommandTFlush| for details) place the current matches in the quickfix window cancel (dismisses file listing) The following is also available on terminals which support it: cancel (dismisses file listing) Note that the default mappings can be overriden by setting options in your |.vimrc| file (see the OPTIONS section for a full list of available options). In addition, when the file listing has focus, typing a character will cause the selection to jump to the first path which begins with that character. Typing multiple characters consecutively can be used to distinguish between paths which begin with the same prefix. COMMANDS *command-t-commands* *:CommandT* |:CommandT| Brings up the Command-T file window, starting in the current working directory as returned by the|:pwd| command. *:CommandTBuffer* |:CommandTBuffer| Brings up the Command-T buffer window. This works exactly like the standard file window, except that the selection is limited to files that you already have open in buffers. *:CommandTCommand* |:CommandTCommand| Brings up the Command-T command window. This works exactly like the standard file window, except that it shows the available commands to run. *:CommandTHelp* |:CommandTHelp| Brings up the Command-T help search window. This works exactly like the standard file window, except that it shows help topics found in any documentation under Vim's |'runtimepath'|. *:CommandTHistory* |:CommandTHistory| Brings up the Command-T history window. This works exactly like the standard file window, except that it shows the history of previously entered commands. *:CommandTLine* |:CommandTLine| Brings up the Command-T line search window. This works exactly like the standard file window, except that it shows the lines in the current buffer. *:CommandTMRU* |:CommandTMRU| Brings up the Command-T buffer window, except that matches are shown in MRU (most recently used) order. If you prefer to use this over the normal buffer finder, I suggest overwriting the standard mapping with a command like: :nnoremap b :CommandTMRU Note that Command-T only starts recording most recently used buffers when you first use a Command-T command or mapping; this is an optimization to improve startup time, which you can override by using either |:CommandTLoad| or |commandt#Load|. *:CommandTJumps* |:CommandTJump| Brings up the Command-T jumplist window. This works exactly like the standard file window, except that the selection is limited to files that you already have in the jumplist. Note that jumps can persist across Vim sessions (see Vim's |jumplist| documentation for more info). *:CommandTSearch* |:CommandTSearch| Brings up the Command-T search window. This works exactly like the standard file window, except that it shows the history of previously used searches. *:CommandTTag* |:CommandTTag| Brings up the Command-T window tags window, which can be used to select from the tags, if any, returned by Vim's |taglist()| function. See Vim's |tag| documentation for general info on tags. *:CommandTFlush* |:CommandTFlush| Instructs the plug-in to flush its path cache, causing the directory to be rescanned for new or deleted paths the next time the file window is shown (pressing when a match listing is visible flushes the cache immediately; this mapping is configurable via the |g:CommandTRefreshMap| setting). In addition, all configuration settings are re-evaluated, causing any changes made to settings via the |:let| command to be picked up. *:CommandTLoad* |:CommandTLoad| Immediately loads the plug-in files, if they haven't been loaded already (normally, the files are loaded lazily the first time you run a Command-T command or use a Command-T mapping). This command may be useful for people wishing to extend Command-T by "monkey patching" its functionality. MAPPINGS *command-t-mappings* By default Command-T comes with only three mappings: *(CommandT)* t bring up the Command-T file window *(CommandTBuffer)* b bring up the Command-T buffer window *(CommandTJump)* j bring up the Command-T jumplist window Note that Command-T won't overwrite a pre-existing mapping so if you prefer to define different mappings use lines like these in your |.vimrc|: nnoremap t (CommandT) nnoremap b (CommandTBuffer) nnoremap j (CommandTJump) Replacing "t", "b" or "j" with your mapping of choice. Note that in the case of MacVim you actually can map to Command-T (written as in Vim) in your |.gvimrc| file if you first unmap the existing menu binding of Command-T to "New Tab": if has("gui_macvim") macmenu &File.New\ Tab key= map (CommandT) endif Additional mappings are available to target, but are not set up by default: *(CommandTCommand)* Brings up the Command-T command window. *(CommandTHelp)* Brings up the Command-T help search window. *(CommandTHistory)* Brings up the Command-T command history search window. *(CommandTLine)* Brings up the Command-T line-finder window. *(CommandTMRU)* Brings up the Command-T most-recently-used buffer window. *(CommandTSearch)* Brings up the Command-T search history window. *(CommandTTag)* Brings up the Command-T tag search window. Set up a mapping for any of these like so: nnoremap h (CommandTHelp) When the Command-T window is active a number of other additional mappings become available for doing things like moving between and selecting matches. These are fully described above in the USAGE section, and settings for overriding the mappings are listed below under OPTIONS. OPTIONS *command-t-options* A number of options may be set in your |.vimrc| to influence the behaviour of the plug-in. To set an option, you include a line like this in your |.vimrc|: let g:CommandTMaxFiles=20000 To have Command-T pick up new settings immediately (that is, without having to restart Vim) you can issue the |:CommandTFlush| command after making changes via |:let|. Following is a list of all available options: *g:CommandTMaxFiles* |g:CommandTMaxFiles| number (default 100000) The maximum number of files that will be considered when scanning the current directory. Upon reaching this number scanning stops. This limit applies only to file listings and is ignored for buffer listings. *g:CommandTSuppressMaxFilesWarning* |g:CommandTSuppressMaxFilesWarning| boolean (default 0) Suppress the warning that Command-T shows when hitting the |g:CommandTMaxFiles| limit during a scan. *g:CommandTMaxDepth* |g:CommandTMaxDepth| number (default 15) The maximum depth (levels of recursion) to be explored when scanning the current directory. Any directories at levels beyond this depth will be skipped. *g:CommandTMaxCachedDirectories* |g:CommandTMaxCachedDirectories| number (default 1) The maximum number of directories whose contents should be cached when recursively scanning. With the default value of 1, each time you change directories the cache will be emptied and Command-T will have to rescan. Higher values will make Command-T hold more directories in the cache, bringing performance at the cost of memory usage. If set to 0, there is no limit on the number of cached directories. *g:CommandTMaxHeight* |g:CommandTMaxHeight| number (default: 15) The maximum height in lines the match window is allowed to expand to. If set to 0, the window will occupy as much of the available space as needed to show matching entries. *g:CommandTInputDebounce* |g:CommandTInputDebounce| number (default: 0) The number of milliseconds to wait before updating the match listing following a key-press. This can be used to avoid wasteful recomputation when making a rapid series of key-presses in a directory with many tens (or hundreds) of thousands of files. *g:CommandTFileScanner* |g:CommandTFileScanner| string (default: 'ruby') The underlying scanner implementation that should be used to explore the filesystem. Possible values are: - "ruby": uses built-in Ruby and should work everywhere, albeit slowly on large (many tens of thousands of files) hierarchies. - "find": uses the command-line tool of the same name, which can be much faster on large projects because it is written in pure C, but may not work on systems without the tool or with an incompatible version of the tool. - "git": uses `git ls-files` to quickly produce a list of files; when Git isn't available or the path being searched is not inside a Git repository falls back to "find". - "watchman": uses Watchman (https://github.com/facebook/watchman) if available; otherwise falls back to "find". Note that this scanner is intended for use with very large hierarchies (hundreds of thousands of files) and so the task of deciding which files should be included is entirely delegated to Watchman; this means that settings which Command-T would usually consult, such as |g:CommandTScanDotDirectories| are ignored. *g:CommandTTraverseSCM* |g:CommandTTraverseSCM| string (default: 'file') Instructs Command-T how to choose a root path when opening a file finder without an explicit path argument. Possible values are: - "file": starting from the file currently being edited, traverse upwards through the filesystem hierarchy until you find an SCM root (as indicated by the presence of a ".git", ".hg" or similar directory) and use that as the base path. If no such root is found, fall back to using Vim's present working directory as a root. The list of SCM directories that Command-T uses to detect an SCM root can be customized with the |g:CommandTSCMDirectories| option. - "dir": traverse upwards looking for an SCM root just like the "file" setting (above), but instead of starting from the file currently being edited, start from Vim's present working directory instead. - "pwd": use Vim's present working directory as a root (ie. attempt no traversal). *g:CommandTGitScanSubmodules* |g:CommandTGitScanSubmodules| boolean (default: 0) If set to 1, Command-T will scan submodules (recursively) when using the "git" file scanner (see |g:CommandTFileScanner|). *g:CommandTSCMDirectories* |g:CommandTSCMDirectories| string (default: '.git,.hg,.svn,.bzr,_darcs') The marker directories that Command-T will use to identify SCM roots during traversal (see |g:CommandTTraverseSCM| above). *g:CommandTMinHeight* |g:CommandTMinHeight| number (default: 0) The minimum height in lines the match window is allowed to shrink to. If set to 0, will default to a single line. If set above the max height, will default to |g:CommandTMaxHeight|. *g:CommandTAlwaysShowDotFiles* |g:CommandTAlwaysShowDotFiles| boolean (default: 0) When showing the file listing Command-T will by default show dot-files only if the entered search string contains a dot that could cause a dot-file to match. When set to a non-zero value, this setting instructs Command-T to always include matching dot-files in the match list regardless of whether the search string contains a dot. See also |g:CommandTNeverShowDotFiles|. Note that this setting only influences the file listing; the buffer listing treats dot-files like any other file. *g:CommandTNeverShowDotFiles* |g:CommandTNeverShowDotFiles| boolean (default: 0) In the file listing, Command-T will by default show dot-files if the entered search string contains a dot that could cause a dot-file to match. When set to a non-zero value, this setting instructs Command-T to never show dot-files under any circumstances. Note that it is contradictory to set both this setting and |g:CommandTAlwaysShowDotFiles| to true, and if you do so Vim will suffer from headaches, nervous twitches, and sudden mood swings. This setting has no effect in buffer listings, where dot files are treated like any other file. *g:CommandTScanDotDirectories* |g:CommandTScanDotDirectories| boolean (default: 0) Normally Command-T will not recurse into "dot-directories" (directories whose names begin with a dot) while performing its initial scan. Set this setting to a non-zero value to override this behavior and recurse. Note that this setting is completely independent of the |g:CommandTAlwaysShowDotFiles| and |g:CommandTNeverShowDotFiles| settings; those apply only to the selection and display of matches (after scanning has been performed), whereas |g:CommandTScanDotDirectories| affects the behaviour at scan-time. Note also that even with this setting off you can still use Command-T to open files inside a "dot-directory" such as `~/.vim`, but you have to use the |:cd| command to change into that directory first. For example: :cd ~/.vim :CommandT *g:CommandTMatchWindowAtTop* |g:CommandTMatchWindowAtTop| boolean (default: 0) When this setting is off (the default) the match window will appear at the bottom so as to keep it near to the prompt. Turning it on causes the match window to appear at the top instead. This may be preferable if you if you have |g:CommandTMatchWindowReverse| set to 0 and want the best match (usually the first one) to appear in a fixed location on the screen rather than moving as the number of matches changes during typing. *g:CommandTMatchWindowReverse* |g:CommandTMatchWindowReverse| boolean (default: 1) When this setting is on (the default), matches will be shown in reverse order, with the best match at the bottom (ie. closest to the prompt). The initially selected match will be the bottom-most, which is preferable if you want the best match to appear in a fixed location on the screen (independently of the number of results). When set to 0, matches will appear from top to bottom with the topmost being selected. *g:CommandTTagIncludeFilenames* |g:CommandTTagIncludeFilenames| boolean (default: 0) When this setting is off (the default) the matches in the |:CommandTTag| listing do not include filenames. *g:CommandTCursorColor* string (default: 'Underlined') |g:CommandTCursorColor| Specifies the |:highlight| color that will be applied to the cursor in the Command-T prompt. *g:CommandTHighlightColor* |g:CommandTHighlightColor| string (default: 'PmenuSel') Specifies the |:highlight| color that will be used to show the currently selected item in the match listing window. *g:CommandTWildIgnore* |g:CommandTWildIgnore| string (default: none) Optionally override Vim's global |'wildignore'| setting during Command-T searches. If you wish to supplement rather than replace the global setting, you can use a syntax like: let g:CommandTWildIgnore=&wildignore . ",*/bower_components" To disable all |'wildignore'|-based filtering, set to an empty string: let g:CommandTWildIgnore='' See also |command-t-wildignore|. *g:CommandTIgnoreCase* |g:CommandTIgnoreCase| boolean (default: 1) Ignore case when searching. Defaults to on, which means that searching is case-insensitive by default. See also |g:CommandTSmartCase|. *g:CommandTRecursiveMatch* |g:CommandTRecursiveMatch| boolean (default: 1) Use this to downgrade Command-T's match-scoring algorithm to its older, cruder, less expensive form. Doing so may improve the responsiveness of the match listing, but at the cost of slightly less precision in the ranking of results. *g:CommandTSmartCase* |g:CommandTSmartCase| boolean (default: none) Override the |g:CommandTIgnoreCase| setting if the search pattern contains uppercase characters, forcing the match to be case-sensitive. If unset (which is the default), the value of the Vim |'smartcase'| setting will be used instead. *g:CommandTAcceptSelectionCommand* |g:CommandTAcceptSelectionCommand| string (default: 'e') The Vim command that will be used to open a selection from the match listing (via |g:CommandTAcceptSelectionMap|). For an example of how this can be used to apply arbitrarily complex logic, see the example in |g:CommandTAcceptSelectionTabCommand| below. *g:CommandTAcceptSelectionTabCommand* |g:CommandTAcceptSelectionTabCommand| string (default: 'tabe') The Vim command that will be used to open a selection from the match listing in a new tab (via |g:CommandTAcceptSelectionSplitMap|). For example, this can be used to switch to an existing buffer (rather than opening a duplicate buffer with the selection in a new tab) with configuration such as the following: set switchbuf=usetab function! GotoOrOpen(...) for file in a:000 if bufwinnr(file) != -1 exec "sb " . file else exec "tabe " . file endif endfor endfunction command! -nargs=+ GotoOrOpen call GotoOrOpen("") let g:CommandTAcceptSelectionTabCommand = 'GotoOrOpen' For a slightly more comprehensive example, see: https://wt.pe/e *g:CommandTAcceptSelectionSplitCommand* |g:CommandTAcceptSelectionSplitCommand| string (default: 'sp') The Vim command that will be used to open a selection from the match listing in a split (via |g:CommandTAcceptSelectionVSplitMap|). For an example of how this can be used to apply arbitrarily complex logic, see the example in |g:CommandTAcceptSelectionTabCommand| above. *g:CommandTAcceptSelectionVsplitCommand* string (default: 'vs') |g:CommandTAcceptSelectionVSplitCommand| The Vim command that will be used to open a selection from the match listing in a vertical split (via |g:CommandTAcceptSelectionVSplitMap|). For an example of how this can be used to apply arbitrarily complex logic, see the example in |g:CommandTAcceptSelectionTabCommand| above. *g:CommandTEncoding* |g:CommandTEncoding| string (default: none) In most environments Command-T will work just fine using the character encoding settings from your local environment. This setting can be used to force Command-T to use a specific encoding, such as "UTF-8", if your environment ends up defaulting to an undesired encoding, such as "ASCII-8BIT". *g:CommandTIgnoreSpaces* |g:CommandTIgnoreSpaces| boolean (default: 1) When typing a search term into Command-T, ignore spaces. When set to 0, Command-T will search for literal spaces inside file names. As well as the basic options listed above, there are a number of settings that can be used to override the default key mappings used by Command-T. For example, to set as the mapping for cancelling (dismissing) the Command-T window, you would add the following to your |.vimrc|: let g:CommandTCancelMap='' Multiple, alternative mappings may be specified using list syntax: let g:CommandTCancelMap=['', ''] Following is a list of all map settings and their defaults: Setting Default mapping(s) *g:CommandTBackspaceMap* |g:CommandTBackspaceMap| *g:CommandTDeleteMap* |g:CommandTDeleteMap| *g:CommandTAcceptSelectionMap* |g:CommandTAcceptSelectionMap| *g:CommandTAcceptSelectionSplitMap* |g:CommandTAcceptSelectionSplitMap| *g:CommandTAcceptSelectionTabMap* |g:CommandTAcceptSelectionTabMap| *g:CommandTAcceptSelectionVSplitMap* |g:CommandTAcceptSelectionVSplitMap| *g:CommandTToggleFocusMap* |g:CommandTToggleFocusMap| *g:CommandTCancelMap* |g:CommandTCancelMap| (not on all terminals) *g:CommandTSelectNextMap* |g:CommandTSelectNextMap| *g:CommandTSelectPrevMap* |g:CommandTSelectPrevMap| *g:CommandTClearMap* |g:CommandTClearMap| *g:CommandTClearPrevWordMap* |g:CommandTClearPrevWordMap| *g:CommandTRefreshMap* |g:CommandTRefreshMap| *g:CommandTQuickfixMap* |g:CommandTQuickfixMap| *g:CommandTCursorLeftMap* |g:CommandTCursorLeftMap| *g:CommandTCursorRightMap* |g:CommandTCursorRightMap| *g:CommandTCursorEndMap* |g:CommandTCursorEndMap| *g:CommandTCursorStartMap* |g:CommandTCursorStartMap| In addition to the options provided by Command-T itself, some of Vim's own settings can be used to control behavior: *command-t-wildignore* |'wildignore'| string (default: '') Vim's |'wildignore'| setting is used to determine which files should be excluded from listings. This is a comma-separated list of glob patterns. It defaults to the empty string, but common settings include "*.o,*.obj" (to exclude object files) or "*/.git,*/.svn" (to exclude SCM metadata directories). For example: :set wildignore+=*.o,*.obj A pattern such as "*/vendor/rails" would exclude all files and subdirectories inside the "vendor/rails" directory. See the |'wildignore'| documentation for more information. Here is an overview of how different common patterns affect Vim and Command-T behavior: wildignore File File Dir Dir Dir + File Pattern "foo" "a/foo" "foo" "a/foo" "a/foo/b" --------------------------------------------------------------- `foo` match match miss miss miss `foo/` miss miss miss miss miss `foo/*` miss miss miss miss miss `*/foo` match match match match match `*/foo/` miss miss match miss miss `*/foo/*` miss miss match match match In short, in general you want to use patterns like: - `foo`: to match a file at any level. - `*.foo`: to match a file with an extension at any level. - `*/foo`: to match a file or directory at any level. - `*/foo/*`: to match a directory at any level. If you want to influence Command-T's file exclusion behavior without changing your global |'wildignore'| setting, you can use the |g:CommandTWildIgnore| setting to apply an override that takes effect only during Command-T searches. Note that there are some differences among file scanners (see |g:CommandTFileScanner|) with respect to 'wildignore' handling: - The default "ruby" scanner explores the filesystem recursively using a depth-first search, and any directory (or subdirectory) which matches the 'wildignore' pattern is not explored. So, if your 'wildignore' contains "*/node_modules" then that entire sub-hierarchy will be ignored. - The "git" and "find" scanners apply 'wildignore' filtering only after completing their scans. - The "watchman" scanner is intended for use with massive hierarchies where speed is of the utmost import, so it doesn't make use of Vim's very slow 'wildignore' filtering at all. Instead, it constructs a regular expression on the fly based on 'wildignore' and/or |g:CommandTWildIgnore| and uses that. To suppress this behavior, set |g:CommandTWildIgnore| to an empty string. INTEGRATION AND EXTENSIBILITY *command-t-integration-and-extensibility* For the purposes of integrating with other plug-ins, or otherwise extending Command-T's functionality, a number of hooks and features are provided. Note that making use of any of these extension points is considered to be advanced configuration, and the more invasive the modifications, the greater the risk that they will interact poorly with future versions of Command-T. Proceed with caution. *command-t-filetype* command-t filetype ~ Command-T displays its match listing using a special Vim buffer with a filetype of `command-t`. This can be used in |ftplugins| or elsewhere to set up an |autocommand| or apply custom configuration. For example of an |ftplugin| that sets a custom 'statusline' for the Command-T match listing buffer, see: https://wt.pe/f *command-t-load* *commandt#Load* Eager loading with commandt#CommandTLoad ~ In order to minimize Vim startup time, most of Command-T's functionality is only loaded on first use, via Vim's |autoload| mechanism. If you should wish to monkey patch some Command-T functionality, the `commandt#Load()` function (also exposed via the |:CommandTLoad| command) is provided as a means of eager-loading Command-T's code, so that you can go ahead and patch it. *commandt#ActiveFinder* *commandt#CheckBuffer* *commandt#Path* Statusline hooks ~ A number of methods are provided that may be useful for creating a context-sensitive 'statusline'. - `commandt#ActiveFinder()`: Returns the class name of the currently active finder. - `commandt#Path()`: Returns the path that Command-T is currently searching. - `commandt#CheckBuffer()`: Takes a buffer number and returns true if it is the Command-T match listing buffer. *command-t-custom-autocommands* Custom autocommands ~ *CommandTWillShowMatchListing* *CommandTDidHideMatchListing* Command-T runs the following "User" autocommands just prior to showing the match listing window and just after hiding it: - CommandTWillShowMatchListing - CommandTDidHideMatchListing For example, to call a pair of custom functions in response to these events, you might do: > autocmd! User CommandTWillShowMatchListing autocmd User CommandTWillShowMatchListing call CustomWillShow() autocmd! User CommandTDidHideMatchListing autocmd User CommandTDidHideMatchListing call CustomDidHide() FAQ *command-t-faq* Why does my build fail with "unknown argument -multiply_definedsuppress"? ~ You may see this on OS X Mavericks when building with the Clang compiler against the system Ruby. This is an unfortunate Apple bug that breaks compilation of many Ruby gems with native extensions on Mavericks. It has been worked around in the upstream Ruby version, but won't be fixed in OS X until Apple updates their supplied version of Ruby (most likely this won't be until the next major release): https://bugs.ruby-lang.org/issues/9624 Workarounds include building your own Ruby (and then your own Vim and Command-T), or more simply, building with the following `ARCHFLAGS` set: ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future ruby extconf.rb make Why can't I open in a split with and in the terminal? ~ It's likely that won't work in most terminals, because the keycode that is sent to processes running inside them is identical to ; when you type , terminal Vim literally "sees" . Unfortunately, there is no workaround for this. If you find that also doesn't work the most likely explanation is that XON/XOFF flow control is enabled; this is the default in many environments. This means that when you press all input to the terminal is suspended until you release it by hitting . While input is suspended you may think your terminal has frozen, but it hasn't. To disable flow control, add the following to your `.zshrc` or `.bash_profile`: stty -ixon -ixoff See the `stty` man page for more details. Why doesn't the Escape key close the match listing in terminal Vim? ~ In some terminals such as xterm the Escape key misbehaves, so Command-T doesn't set up a mapping for it. If you want to try using the escape key anyway, you can add something like the following to your |.vimrc| file: if &term =~ "xterm" || &term =~ "screen" let g:CommandTCancelMap = ['', ''] endif This configuration has worked for me with recent versions of Vim on multiple platforms (OS X, CentOS etc). TIPS *command-t-tips* Working with very large repositories ~ One of the primary motivations for writing Command-T was to get fast, robust high-quality matches even on large hierarchies. The larger the hierarchy, the more important having good file navigation becomes. This is why Command-T's performance-critical sections are written in C. This requires a compilation step and makes Command-T harder to install than similar plug-ins which are written in pure Vimscript, and can be a disincentive against use. This is a conscious trade-off; the goal isn't to have as many users as possible, but rather to provide the best performance at the highest quality. The speed of the core is high enough that Command-T can afford to burn a bunch of extra cycles -- using its recursive matching algorithm -- looking for a higher-quality, more intuitive ranking of search results. Again, the larger the hierarchy, the more important the quality of result ranking becomes. Nevertheless, for extremely large hierarchies (of the order of 500,000 files) some tuning is required in order to get useful and usable performance levels. Here are some useful example settings: let g:CommandTMaxHeight = 30 You want the match listing window to be large enough that you can get useful feedback about how your search query is going; in large hierarchies there may be many, many matches for a given query. At the same time, you don't want Vim wasting valuable cycles repainting a large portion of the screen area, especially on a large display. Setting the limit to 30 or similar is a reasonable compromise. let g:CommandTMaxFiles = 500000 The default limit of 30,000 files prevents Command-T from "seeing" many of the files in a large directory hierarchy so you need to increase this limit. let g:CommandTInputDebounce = 50 Wait for 50ms of keyboard inactivity before computing search results. For example, if you are enter "foobar" quickly (ie. within 1 second), there is little sense in fetching the results for "f", "fo", "foo", "foob", "fooba" and finally "foobar". Instead, we can just fetch the results for "foobar". This setting trades off some immediate responsiveness at the micro level for better performance (real and perceived) and a better search experience overall. let g:CommandTFileScanner = 'watchman' On a large hierarchy with of the order of 500,000 files, scanning a directory tree with a tool like the `find` executable may take literally minutes with a cold cache. Once the cache is warm, the same `find` run may take only a second or two. Command-T provides a "find" scanner to leverage this performance, but there is still massive overhead in passing the results through Vim internal functions that apply 'wildignore' settings and such, so for truly immense repos the "watchman" scanner is the tool of choice. This scanner delegates the task of finding files to Facebook's `watchman` tool (https://github.com/facebook/watchman), which can return results for a 500,000 file hierarchy within about a second. Note that Watchman has a range of configuration options that can be applied by files such as `/etc/watchman.json` or per-direcory `.watchmanconfig` files and which may affect how Command-T works. For example, if your configuration has a `root_restrict_files` setting that makes Watchman only work with roots that look like Git or Mercurial repos, then Command-T will fall back to using the "find" scanner any time you invoke it on a non-repo directory. For simplicity's sake, it is probably a good idea to use Vim and Command-T anchored at the root level of your repository in any case. let g:CommandTMaxCachedDirectories = 10 Command-T will internally cache up to 10 different directories, so even if you |cd| repeatedly, it should only need to scan each directory once. It's advisable to keep a long-running Vim instance in place and let it cache the directory listings rather than repeatedly closing and re-opening Vim in order to edit every file. On those occasions when you do need to flush the cache (ie. with |CommandTFlush| or in the match listing window), use of the Watchman scanner should make the delay barely noticeable. let g:CommandTSmartCase = 1 Makes Command-T perform case-sensitive matching whenever the search pattern includes an uppercase letter. This allows you to narrow the search results listing with fewer keystrokes. See also |g:CommandTIgnoreCase|. let g:CommandTRecursiveMatch = 0 This one is a little more subjective. You may want to downgrade Command-T's match-scoring algorithm to a cheaper but less precise method to save some cycles. This finds matches more quickly at the possible cost of ranking them slightly less precisely. Whether applying this setting helps or hurts you in a very large repo is probably repo-dependent. For reference, I do not use this setting, even in repos of over 500,000 files. As noted in the introduction, Command-T works best when you adopt a "path-centric" mentality. This is especially true on very large hierarchies. For example, if you're looking for a file at: lib/third-party/adapters/restful-services/foobar/foobar-manager.js you'll be able to narrow your search results down more narrowly if you search with a query like "librestfoofooman" than "foobar-manager.js". This evidently requires that you know where the file you're wanting to open exists, but again, this is a concious design decision: Command-T is made to enable people who know what they want to open and where it is to open it as quickly as possible; other tools such as NERDTree exist for visually exploring an unknown hierarchy. Over time, you will get a feel for how economical you can afford to be with your search queries in a given repo. In the example above, if "foo" is not a very common pattern in your hierarchy, then you may find that you can find what you need with a very concise query such as "foomanjs". With time, this kind of ongoing calibration will come quite naturally. Finally, it is important to be on a relatively recent version of Command-T to fully benefit from the available performance enhancements: - Version 3.1 (March 2016) sped up the matching algorithm by 17x. - Version 2.0 (December 2015) added the |g:CommandTRecursiveMatch| option. - Version 1.10 (July 2014) added the |g:CommandTIgnoreCase| and |g:CommandTSmartCase| options. - Version 1.9 (May 2014) tweaked memoization algorithm for a 10% speed boost. - Version 1.8 (March 2014) sped up the Watchman file scanner by switching its communication from the JSON to the binary Watchman protocol. - Version 1.7 (February 2014) added the |g:CommandTInputDebounce| and |g:CommandTFileScanner| settings, along with support for the Watchman file scanner. - Version 1.6 (December 2013) added parallelized search. - Version 1.5 (September 2013) added memoization to the matching algorithm, improving general performance on large hierarchies, but delivering spectacular gains on hierarchies with "pathological" characteristics that lead the algorithm to exhibit degenerate behavior. AUTHORS *command-t-authors* Command-T is written and maintained by Greg Hurrell . Other contributors that have submitted patches include (in alphabetical order, via `git shortlog -s | cut -f 2-3 | column -c 72`): Abhinav Gupta Nicolas Alpi Aleksandrs Ļedovskis Nikolai Aleksandrovich Pavlov Andy Waite Nilo César Teixeira Anthony Panozzo Noon Silk Artem Nezvigin Ole Petter Bang Ben Boeckel Patrick Hayes Daniel Burgess Paul Jolly Daniel Hahler Pavel Sergeev David Szotten Rainux Luo Emily Strickland Richard Feldman Felix Tjandrawibawa Roland Puntaier Gary Bernhardt Ross Lagerwall Greg Hurrell Sam Morris Ivan Ukhov Scott Bronson Jeff Kreeftmeijer Seth Fowler Jerome Castaneda Sherzod Gapirov Joe Lencioni Shlomi Fish KJ Tsanaktsidis Steven Moazami Kevin Webster Sung Pae Kien Nguyen Duc Thomas Pelletier Lucas de Vries Ton van den Heuvel Marcus Brito Victor Hugo Borja Marian Schubert Vlad Seghete Matthew Todd Vít Ondruch Mike Lundy Woody Peterson Nadav Samet Yan Pritzker Nate Kane Zak Johnson Nicholas T. Additionally, Hanson Wang, Jacek Wysocki and Yiding Jia wrote patches which were not directly included but which served as a model for changes that did end up making it in. As this was the first Vim plug-in I had ever written I was heavily influenced by the design of the LustyExplorer plug-in by Stephen Bach, which I understand was one of the largest Ruby-based Vim plug-ins at the time. While the Command-T codebase doesn't contain any code directly copied from LustyExplorer, I did use it as a reference for answers to basic questions (like "How do you do 'X' in a Ruby-based Vim plug-in?"), and also copied some basic architectural decisions (like the division of the code into Prompt, Settings and MatchWindow classes). LustyExplorer is available from: http://www.vim.org/scripts/script.php?script_id=1890 DEVELOPMENT *command-t-development* Development in progress can be inspected at: https://github.com/wincent/command-t the clone URL for which is: https://github.com/wincent/command-t.git Mirrors exist on GitLab and BitBucket, and are automatically updated once per hour: https://gitlab.com/wincent/command-t https://bitbucket.org/ghurrell/command-t Patches are welcome via the usual mechanisms (pull requests, email, posting to the project issue tracker etc). As many users choose to track Command-T using Pathogen or similar, which often means running a version later than the last official release, the intention is that the "master" branch should be kept in a stable and reliable state as much as possible. Riskier changes are first cooked on the "next" branch for a period before being merged into master. You can track this branch if you're feeling wild and experimental, but note that the "next" branch may periodically be rewound (force-updated) to keep it in sync with the "master" branch after each official release. WEBSITE *command-t-website* The official website for Command-T is: https://github.com/wincent/command-t The latest release will always be available from: https://github.com/wincent/command-t/releases A copy of each release is also available from the official Vim scripts site at: http://www.vim.org/scripts/script.php?script_id=3025 Bug reports should be submitted to the issue tracker at: https://github.com/wincent/command-t/issues RELATED PROJECTS *command-t-related-projects* fuzzy-native ~ Command-T's matching algorithm ported to C++ and wrapped inside a Node NPM module: https://github.com/hansonw/fuzzy-native ctrlp-cmatcher ~ Command-T's matching algorithm wrapped in a Python extension, for use with the CtrlP Vim plug-in: https://github.com/JazzCore/ctrlp-cmatcher LICENSE *command-t-license* Copyright 2010-present 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. HISTORY *command-t-history* 4.0 (16 May 2016) - A non-leading dot in the search query can now match against dot-files and "dot-directories" in non-leading path components. - Matching algorithm sped up by about 17x (with help from Hanson Wang). - |g:CommandTInputDebounce| now defaults to 0, as the recent optimizations make debouncing largely unnecessary. - Added |:CommandTHelp| for jumping to locations in the help, and an accompanying mapping, |(CommandTHelp)|. - Added |:CommandTLine| for jumping to lines within the current buffer, and a corresponding mapping, |(CommandTLine)|. - Added |:CommandTHistory| for jumping to previously entered commands, and a corresponding mapping, |(CommandTHistory)|. - Added |:CommandTSearch| for jumping to previously entered searches, and a corresponding mapping, |(CommandTSearch)|. - Added |:CommandTCommand| for finding commands, and a corresponding mapping, |(CommandTCommand)|. - Added |(CommandTMRU)| and |(CommandTTag)| mappings. - The "ruby" and "find" scanners now show numerical progress in the prompt area during their scans. - Removed functionality that was previously deprecated in 2.0. - Fix inability to type "^" and "|" at the prompt. - Make it possible to completely disable |'wildignore'|-based filtering by setting |g:CommandTWildIgnore| to an empty string. - The "watchman" file scanner now respects |'wildignore'| and |g:CommandTWildIgnore| by construcing an equivalent regular expression and using that for filtering. - Show a warning when hitting |g:CommandTMaxFiles|, and add a corresponding |g:CommandTSuppressMaxFilesWarning| setting to suppress the warning. 3.0.2 (9 February 2016) - Minimize flicker on opening and closing the match listing in MacVim. - Add |CommandTWillShowMatchListing| and |CommandTDidHideMatchListing| "User" autocommands. 3.0.1 (25 January 2016) - restore compatibility with Ruby 1.8.7. 3.0 (19 January 2016) - change |g:CommandTIgnoreSpaces| default value to 1. - change |g:CommandTMatchWindowReverse| default value to 1. - change |g:CommandTMaxHeight| default value to 15. - try harder to avoid scrolling other buffer when showing or hiding the match listing 2.0 (28 December 2015) - add |:CommandTIgnoreSpaces| option (patch from KJ Tsanaktsidis) - make Command-T resilient to people deleting its hidden, unlisted buffer - the match listing buffer now has filetype "command-t", which may be useful for detectability/extensibility - Command-T now sets the name of the match listing buffer according to how it was invoked (ie. for the file finder, the name is "Command-T [Files]", for the buffer finder, the name is "Command-T [Buffers]", and so on); previously the name was a fixed as "GoToFile" regardless of the active finder type - Many internal function names have changed, so if you or your plug-ins are calling those internals they will need to be updated: - `commandt#CommandTFlush()` is now `commandt#Flush()` - `commandt#CommandTLoad()` is now `commandt#Load()` - `commandt#CommandTShowBufferFinder()` is now `commandt#BufferFinder()` - `commandt#CommandTShowFileFinder()` is now `commandt#FileFinder()` - `commandt#CommandTShowJumpFinder()` is now `commandt#JumpFinder()` - `commandt#CommandTShowMRUFinder()` is now `commandt#MRUFinder()` - `commandt#CommandTShowTagFinder()` is now `commandt#TagFinder()` - A number of functions have been turned into "private" autoloaded functions, to make it clear that they are intended only for internal use: - `CommandTAcceptSelection()` is now `commandt#private#AcceptSelection()` - `CommandTAcceptSelectionSplit()` is now `commandt#private#AcceptSelectionSplit()` - `CommandTAcceptSelectionTab()` is now `commandt#private#AcceptSelectionTab()` - `CommandTAcceptSelectionVSplit()` is now `commandt#private#AcceptSelectionVSplit()` - `CommandTBackspace()` is now `commandt#private#Backspace()` - `CommandTCancel()` is now `commandt#private#Cancel()` - `CommandTClear()` is now `commandt#private#Clear()` - `CommandTClearPrevWord()` is now `commandt#private#ClearPrevWord()` - `CommandTCursorEnd()` is now `commandt#private#CursorEnd()` - `CommandTCursorLeft()` is now `commandt#private#CursorLeft()` - `CommandTCursorRight()` is now `commandt#private#CursorRight()` - `CommandTCursorStart()` is now `commandt#private#CursorStart()` - `CommandTDelete()` is now `commandt#private#Delete()` - `CommandTHandleKey()` is now `commandt#private#HandleKey()` - `CommandTListMatches()` is now `commandt#private#ListMatches()` - `CommandTQuickfix()` is now `commandt#private#Quickfix()` - `CommandTRefresh()` is now `commandt#private#Refresh()` - `CommandTSelectNext()` is now `commandt#private#SelectNext()` - `CommandTSelectPrev()` is now `commandt#private#SelectPrev()` - `CommandTToggleFocus()` is now `commandt#private#ToggleFocus()` - add |g:CommandTRecursiveMatch| option - stop distribution as a vimball in favor of a zip archive - don't clobber |alternate-file| name when opening Command-T match listing (patch from Jerome Castaneda) - add |g:CommandTCursorColor| option - expose mappings for |:CommandT| and |:CommandTBuffer| using `` mappings |(CommandT)| and |(CommandT)| - add `j` mapping to |:CommandTJump|, via |(CommandTJump)| (defined only if no pre-existing mapping exists) 1.13 (29 April 2015) - avoid "W10: Warning: Changing a readonly file" when starting Vim in read-only mode (ie. as `view` or with the `-R` option) - fix infinite loop on || (regression introduced in 1.12) 1.12 (9 April 2015) - add |:CommandTLoad| command - fix rare failure to restore cursor color after closing Command-T (patch from Vlad Seghete) - doc fixes and updates (patches from Daniel Hahler and Nicholas T.) - make it possible to force reloading of the plug-in (patch from Daniel Hahler) - add |g:CommandTEncoding| option, to work around rare encoding compatibility issues - fix error restoring cursor highlights involving some configurations (patch from Daniel Hahler) - skip set-up of || key mapping on rxvt terminals (patch from Daniel Hahler) - add |g:CommandTGitScanSubmodules| option, which can be used to recursively scan submodules when the "git" file scanner is used (patch from Ben Boeckel) - fix for not falling back to "find"-based scanner when a Watchman-related error occurs 1.11.4 (4 November 2014) - fix infinite loop on Windows when |g:CommandTTraverseSCM| is set to a value other than "pwd" (bug present since 1.11) - handle unwanted split edgecase when |'hidden'| is set, the current buffer is modified, and it is visible in more than one window 1.11.3 (10 October 2014) - ignore impromperly encoded filenames (patch from Sherzod Gapirov) - fix failure to update path when using |:cd| in conjunction with |g:CommandTTraverseSCM| set to "pwd" (bug present since 1.11.2) 1.11.2 (2 September 2014) - fix error while using Command-T outside of an SCM repo (bug present since 1.11.1) 1.11.1 (29 August 2014) - compatibility fixes with Ruby 1.8.6 (patch from Emily Strickland) - compatibility fixes with Ruby 1.8.5 - fix 'wildignore' being ignored (bug present since 1.11) - fix current working directory being ignored when |g:CommandTTraverseSCM| is set to "pwd" (bug present since 1.11) - performance improvements 1.11 (15 August 2014) - improve edge-case handling in match results window code (patches from Richard Feldman) - add "git" file scanner (patch from Patrick Hayes) - speed-up when 'wildignore' is unset (patch from Patrick Hayes) - add |g:CommandTTraverseSCM| setting which anchors Command-T's file finder to the nearest SCM directory (based on patches from David Szotten and Ben Osheroff) - add AppStream metadata (patch from Vít Ondruch) 1.10 (15 July 2014) - improve tag finder performance by caching tag lists (patch from Artem Nezvigin) - consider the |'autowriteall'| option when deciding whether to open a file in a split - make selection acceptance commands configurable (patch from Ole Petter Bang) - add mapping to delete previous word of the match prompt (patch from Kevin Webster) - try harder to always clear status line after closing the match listing (patch from Ton van den Heuvel) - don't allow MRU autocommands to produce errors when the extension has not been compiled - add |g:CommandTIgnoreCase| and |g:CommandTSmartCase| options, providing support for case-sensitive matching (based on patch from Jacek Wysocki) 1.9.1 (30 May 2014) - include the file in the release archive that was missing from the 1.9 release 1.9 (25 May 2014) - improved startup time using Vim's autload mechanism (patch from Ross Lagerwall) - added MRU (most-recently-used) buffer finder (patch from Ton van den Heuvel) - fixed edge case in matching algorithm which could cause spurious matches with queries containing repeated characters - fixed slight positive bias in the match scoring algorithm's weighting of matching characters based on distance from last match - tune memoization in match scoring algorithm, yielding a more than 10% speed boost 1.8 (31 March 2014) - taught Watchman file scanner to use the binary protocol instead of JSON, roughly doubling its speed - build changes to accommodate MinGW (patch from Roland Puntaier) 1.7 (9 March 2014) - added |g:CommandTInputDebounce|, which can be used to improve responsiveness in large file hierarchies (based on patch from Yiding Jia) - added a potentially faster file scanner which uses the `find` executable (based on patch from Yiding Jia) - added a file scanner that knows how to talk to Watchman (https://github.com/facebook/watchman) - added |g:CommandTFileScanner|, which can be used to switch file scanners - fix processor count detection on some platforms (patch from Pavel Sergeev) 1.6.1 (22 December 2013) - defer processor count detection until runtime (makes it possible to sensibly build Command-T on one machine and use it on another) 1.6 (16 December 2013) - on systems with POSIX threads (such as OS X and Linux), Command-T will use threads to compute match results in parallel, resulting in a large speed boost that is especially noticeable when navigating large projects 1.5.1 (23 September 2013) - exclude large benchmark fixture file from source exports (patch from Vít Ondruch) 1.5 (18 September 2013) - don't scan "pathological" filesystem structures (ie. circular or self-referential symlinks; patch from Marcus Brito) - gracefully handle files starting with "+" (patch from Ivan Ukhov) - switch default selection highlight color for better readability (suggestion from André Arko), but make it possible to configure via the |g:CommandTHighlightColor| setting - added a mapping to take the current matches and put then in the quickfix window - performance improvements, particularly noticeable with large file hierarchies - added |g:CommandTWildIgnore| setting (patch from Paul Jolly) 1.4 (20 June 2012) - added |:CommandTTag| command (patches from Noon Silk) - turn off |'colorcolumn'| and |'relativenumber'| in the match window (patch from Jeff Kreeftmeijer) - documentation update (patch from Nicholas Alpi) - added |:CommandTMinHeight| option (patch from Nate Kane) - highlight (by underlining) matched characters in the match listing (requires Vim to have been compiled with the +conceal feature, which is available in Vim 7.3 or later; patch from Steven Moazami) - added the ability to flush the cache while the match window is open using 1.3.1 (18 December 2011) - fix jumplist navigation under Ruby 1.9.x (patch from Woody Peterson) 1.3 (27 November 2011) - added the option to maintain multiple caches when changing among directories; see the accompanying |g:CommandTMaxCachedDirectories| setting - added the ability to navigate using the Vim jumplist (patch from Marian Schubert) 1.2.1 (30 April 2011) - Remove duplicate copy of the documentation that was causing "Duplicate tag" errors - Mitigate issue with distracting blinking cursor in non-GUI versions of Vim (patch from Steven Moazami) 1.2 (30 April 2011) - added |g:CommandTMatchWindowReverse| option, to reverse the order of items in the match listing (patch from Steven Moazami) 1.1b2 (26 March 2011) - fix a glitch in the release process; the plugin itself is unchanged since 1.1b 1.1b (26 March 2011) - add |:CommandTBuffer| command for quickly selecting among open buffers 1.0.1 (5 January 2011) - work around bug when mapping |:CommandTFlush|, wherein the default mapping for |:CommandT| would not be set up - clean up when leaving the Command-T buffer via unexpected means (such as with or similar) 1.0 (26 November 2010) - make relative path simplification work on Windows 1.0b (5 November 2010) - work around platform-specific Vim 7.3 bug seen by some users (wherein Vim always falsely reports to Ruby that the buffer numbers is 0) - re-use the buffer that is used to show the match listing, rather than throwing it away and recreating it each time Command-T is shown; this stops the buffer numbers from creeping up needlessly 0.9 (8 October 2010) - use relative paths when opening files inside the current working directory in order to keep buffer listings as brief as possible (patch from Matthew Todd) 0.8.1 (14 September 2010) - fix mapping issues for users who have set |'notimeout'| (patch from Sung Pae) 0.8 (19 August 2010) - overrides for the default mappings can now be lists of strings, allowing multiple mappings to be defined for any given action - t mapping only set up if no other map for |:CommandT| exists (patch from Scott Bronson) - prevent folds from appearing in the match listing - tweaks to avoid the likelihood of "Not enough room" errors when trying to open files - watch out for "nil" windows when restoring window dimensions - optimizations (avoid some repeated downcasing) - move all Ruby files under the "command-t" subdirectory and avoid polluting the "Vim" module namespace 0.8b (11 July 2010) - large overhaul of the scoring algorithm to make the ordering of returned results more intuitive; given the scope of the changes and room for optimization of the new algorithm, this release is labelled as "beta" 0.7 (10 June 2010) - handle more |'wildignore'| patterns by delegating to Vim's own |expand()| function; with this change it is now viable to exclude patterns such as 'vendor/rails/**' in addition to filename-only patterns like '*.o' and '.git' (patch from Mike Lundy) - always sort results alphabetically for empty search strings; this eliminates filesystem-specific variations (patch from Mike Lundy) 0.6 (28 April 2010) - |:CommandT| now accepts an optional parameter to specify the starting directory, temporarily overriding the usual default of Vim's |:pwd| - fix truncated paths when operating from root directory 0.5.1 (11 April 2010) - fix for Ruby 1.9 compatibility regression introduced in 0.5 - documentation enhancements, specifically targetted at Windows users 0.5 (3 April 2010) - |:CommandTFlush| now re-evaluates settings, allowing changes made via |let| to be picked up without having to restart Vim - fix premature abort when scanning very deep directory hierarchies - remove broken || key mapping on vt100 and xterm terminals - provide settings for overriding default mappings - minor performance optimization 0.4 (27 March 2010) - add |g:CommandTMatchWindowAtTop| setting (patch from Zak Johnson) - documentation fixes and enhancements - internal refactoring and simplification 0.3 (24 March 2010) - add |g:CommandTMaxHeight| setting for controlling the maximum height of the match window (patch from Lucas de Vries) - fix bug where |'list'| setting might be inappropriately set after dismissing Command-T - compatibility fix for different behaviour of "autoload" under Ruby 1.9.1 - avoid "highlight group not found" warning when run under a version of Vim that does not have syntax highlighting support - open in split when opening normally would fail due to |'hidden'| and |'modified'| values 0.2 (23 March 2010) - compatibility fixes for compilation under Ruby 1.9 series - compatibility fixes for compilation under Ruby 1.8.5 - compatibility fixes for Windows and other non-UNIX platforms - suppress "mapping already exists" message if t mapping is already defined when plug-in is loaded - exclude paths based on |'wildignore'| setting rather than a hardcoded regular expression 0.1 (22 March 2010) - initial public release ------------------------------------------------------------------------------ vim:ts=8:tw=78:ft=help: command-t-4.0/fixtures/000077500000000000000000000000001271652626300151175ustar00rootroot00000000000000command-t-4.0/fixtures/bar/000077500000000000000000000000001271652626300156635ustar00rootroot00000000000000command-t-4.0/fixtures/bar/abc000066400000000000000000000000021271652626300163230ustar00rootroot00000000000000. command-t-4.0/fixtures/bar/xyz000066400000000000000000000000021271652626300164300ustar00rootroot00000000000000. command-t-4.0/fixtures/baz000066400000000000000000000000021271652626300156060ustar00rootroot00000000000000. command-t-4.0/fixtures/bing000066400000000000000000000000021271652626300157510ustar00rootroot00000000000000. command-t-4.0/fixtures/foo/000077500000000000000000000000001271652626300157025ustar00rootroot00000000000000command-t-4.0/fixtures/foo/alpha/000077500000000000000000000000001271652626300167675ustar00rootroot00000000000000command-t-4.0/fixtures/foo/alpha/t1000066400000000000000000000000021271652626300172260ustar00rootroot00000000000000. command-t-4.0/fixtures/foo/alpha/t2000066400000000000000000000000021271652626300172270ustar00rootroot00000000000000. command-t-4.0/fixtures/foo/beta000066400000000000000000000000021271652626300165300ustar00rootroot00000000000000. command-t-4.0/plugin/000077500000000000000000000000001271652626300145445ustar00rootroot00000000000000command-t-4.0/plugin/command-t.vim000066400000000000000000000034271271652626300171460ustar00rootroot00000000000000" Copyright 2010-present Greg Hurrell. All rights reserved. " Licensed under the terms of the BSD 2-clause license. if exists('g:command_t_loaded') || &compatible finish endif let g:command_t_loaded = 1 command! CommandTBuffer call commandt#BufferFinder() command! CommandTCommand call commandt#CommandFinder() command! CommandTHelp call commandt#HelpFinder() command! CommandTHistory call commandt#HistoryFinder() command! CommandTJump call commandt#JumpFinder() command! CommandTLine call commandt#LineFinder() command! CommandTMRU call commandt#MRUFinder() command! CommandTSearch call commandt#SearchFinder() command! CommandTTag call commandt#TagFinder() command! -nargs=? -complete=dir CommandT call commandt#FileFinder() command! CommandTFlush call commandt#Flush() command! CommandTLoad call commandt#Load() if !hasmapto('(CommandT)') && maparg('t', 'n') ==# '' nmap t (CommandT) endif nnoremap (CommandT) :CommandT if !hasmapto('(CommandTBuffer)') && maparg('b', 'n') ==# '' nmap b (CommandTBuffer) endif nnoremap (CommandTBuffer) :CommandTBuffer nnoremap (CommandTHelp) :CommandTHelp nnoremap (CommandTHistory) :CommandTHistory if has('jumplist') if !hasmapto('(CommandTJump)') && maparg('j', 'n') ==# '' nmap j (CommandTJump) endif nnoremap (CommandTJump) :CommandTJump endif nnoremap (CommandTCommand) :CommandTCommand nnoremap (CommandTLine) :CommandTLine nnoremap (CommandTMRU) :CommandTMRU nnoremap (CommandTSearch) :CommandTSearch nnoremap (CommandTTag) :CommandTTag command-t-4.0/ruby/000077500000000000000000000000001271652626300142275ustar00rootroot00000000000000command-t-4.0/ruby/command-t.rb000066400000000000000000000014471271652626300164410ustar00rootroot00000000000000# Copyright 2014-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT begin require 'command-t/metadata' rescue LoadError require 'command-t/metadata/fallback' end 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 :ProgressReporter, 'command-t/progress_reporter' 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 command-t-4.0/ruby/command-t/000077500000000000000000000000001271652626300161065ustar00rootroot00000000000000command-t-4.0/ruby/command-t/.gitignore000066400000000000000000000000721271652626300200750ustar00rootroot00000000000000/*.log /*.o /Makefile /ext.* /metadata.rb !/ext.c !/ext.h command-t-4.0/ruby/command-t/controller.rb000066400000000000000000000377641271652626300206370ustar00rootroot00000000000000# Copyright 2010-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Controller include PathUtilities include SCMUtilities # Wraps `method` in a `rescue` clause that attempts to print some useful # information to the screen before re-raising any exception. Without this, # most of the useful output is unhelpfully swallowed by Vim. def self.guard(method) class_eval <<-END alias original_#{method} #{method} def #{method}(*args, &block) original_#{method}(*args, &block) rescue Exception => e backtrace = e.backtrace trimmed = backtrace.take(backtrace.length - 2) text = VIM::escape_for_single_quotes trimmed.join("\n") ::VIM::command "echo '\#{text}'" raise e end END end def initialize encoding = VIM::get_string('g:CommandTEncoding') if encoding begin encoding = Encoding.find(encoding) Encoding.default_external = encoding Encoding.default_internal = encoding rescue end end end # For possible use in status lines. def active_finder @active_finder && @active_finder.class.name end # For possible use in status lines. def path @path end # For possible use in status lines. def is_own_buffer(buffer_number) @match_window && buffer_number == @match_window.buffer_number end # For possible use in status lines. def return_is_own_buffer(buffer_number) if is_own_buffer(buffer_number) ::VIM::command 'return 1' else ::VIM::command 'return 0' end end def show_buffer_finder @path = VIM::pwd @active_finder = buffer_finder show end guard :show_buffer_finder def show_command_finder @path = VIM::pwd @active_finder = command_finder show end guard :show_command_finder def show_help_finder @path = VIM::pwd @active_finder = help_finder show end guard :show_help_finder def show_history_finder @path = VIM::pwd @active_finder = history_finder show end guard :show_history_finder def show_jump_finder @path = VIM::pwd @active_finder = jump_finder show end guard :show_jump_finder def show_line_finder @path = VIM::pwd @active_finder = line_finder show end guard :show_line_finder def show_mru_finder @path = VIM::pwd @active_finder = mru_finder show end guard :show_mru_finder def show_search_finder @path = VIM::pwd @active_finder = search_finder show end guard :show_search_finder def show_tag_finder @path = VIM::pwd @active_finder = tag_finder show end guard :show_tag_finder 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 guard :show_file_finder 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 guard :quickfix def refresh return unless @active_finder && @active_finder.respond_to?(:flush) @active_finder.flush list_matches! end guard :refresh def flush @file_finder = nil @max_height = nil @min_height = nil @prompt = nil @tag_finder = nil end guard :flush def handle_key key = ::VIM::evaluate('a:arg').to_i.chr if @focus == prompt prompt.add! key update else @match_window.find key end end guard :handle_key def backspace if @focus == prompt prompt.backspace! update end end guard :backspace def delete if @focus == prompt prompt.delete! update end end guard :delete def accept_selection(options = {}) selection = @match_window.selection hide open_selection(selection, options) unless selection.nil? end guard :accept_selection def toggle_focus @focus.unfocus # old focus @focus = @focus == prompt ? @match_window : prompt @focus.focus # new focus end guard :toggle_focus def cancel hide end guard :cancel def select_next @match_window.select_next end guard :select_next def select_prev @match_window.select_prev end guard :select_prev def clear prompt.clear! list_matches! end guard :clear def clear_prev_word prompt.clear_prev_word! list_matches! end guard :clear_prev_word def cursor_left prompt.cursor_left if @focus == prompt end guard :cursor_left def cursor_right prompt.cursor_right if @focus == prompt end guard :cursor_right def cursor_end prompt.cursor_end if @focus == prompt end guard :cursor_end def cursor_start prompt.cursor_start if @focus == prompt end guard :cursor_start 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, :ignore_spaces => VIM::get_bool('g:CommandTIgnoreSpaces', true), :recurse => VIM::get_bool('g:CommandTRecursiveMatch', true) ) @match_window.matches = @matches # Scanner may have overwritten prompt to show progress. prompt.redraw @needs_update = false end guard :list_matches 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 update if @debounce_interval > 0 @needs_update = true else list_matches! end end def prompt @prompt ||= Prompt.new( :cursor_color => VIM::get_string('g:CommandTCursorColor') ) end 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.any? markers end def list_matches! list_matches(:force => true) end def show @initial_window = $curwin @initial_buffer = $curbuf @debounce_interval = VIM::get_number('g:CommandTInputDebounce') || 0 @match_window = MatchWindow.new \ :encoding => VIM::get_string('g:CommandTEncoding'), :highlight_color => VIM::get_string('g:CommandTHighlightColor'), :match_window_at_top => VIM::get_bool('g:CommandTMatchWindowAtTop'), :match_window_reverse => VIM::get_bool('g:CommandTMatchWindowReverse', true), :min_height => min_height, :debounce_interval => @debounce_interval, :prompt => prompt, :name => "Command-T [#{@active_finder.name}]" @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') || 15 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 #{key} " \ ":call commandt#private##{function}(#{param})" 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 = '<>`@#~!"$%^&/()=+*-_.,;:?\\|\'{}[]' space = ' ' (numbers + lowercase + uppercase + punctuation + space).each_byte do |b| map "", 'HandleKey', b end # "special" keys (overridable by settings) { 'AcceptSelection' => '', 'AcceptSelectionSplit' => ['', ''], 'AcceptSelectionTab' => '', 'AcceptSelectionVSplit' => '', 'Backspace' => '', 'Cancel' => ['', ''], 'Clear' => '', 'ClearPrevWord' => '', 'CursorEnd' => '', 'CursorLeft' => ['', ''], 'CursorRight' => ['', ''], 'CursorStart' => '', 'Delete' => '', 'Quickfix' => '', 'Refresh' => '', 'SelectNext' => ['', '', ''], 'SelectPrev' => ['', '', ''], 'ToggleFocus' => '', }.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 == '' && term =~ /\A(rxvt|screen|vt100|xterm)/ map mapping, key end end end end end def set_up_autocmds if @debounce_interval > 0 ::VIM::command 'augroup CommandTController' ::VIM::command 'autocmd!' ::VIM::command 'autocmd CursorHold :call commandt#private#ListMatches()' ::VIM::command 'augroup END' 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 command_finder @command_finder ||= CommandT::Finder::CommandFinder.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'), :git_scan_submodules => VIM::get_bool('g:CommandTGitScanSubmodules') end def help_finder @jump_finder ||= CommandT::Finder::HelpFinder.new end def history_finder CommandT::Finder::HistoryFinder.new(:history_type => ':') end def jump_finder @jump_finder ||= CommandT::Finder::JumpFinder.new end def line_finder CommandT::Finder::LineFinder.new end def search_finder CommandT::Finder::HistoryFinder.new(:history_type => '/') end def tag_finder @tag_finder ||= CommandT::Finder::TagFinder.new \ :include_filenames => VIM::get_bool('g:CommandTTagIncludeFilenames') end end end command-t-4.0/ruby/command-t/depend000066400000000000000000000003101271652626300172620ustar00rootroot00000000000000# Copyright 2010-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. CFLAGS += -Wall -Wextra -Wno-unused-parameter ifdef DEBUG CFLAGS += -DDEBUG endif command-t-4.0/ruby/command-t/ext.c000066400000000000000000000030531271652626300170530ustar00rootroot00000000000000// Copyright 2010-present 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); } command-t-4.0/ruby/command-t/ext.h000066400000000000000000000015601271652626300170610ustar00rootroot00000000000000// Copyright 2010-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include 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 macros. #define L(...) { \ fprintf(stdout, __VA_ARGS__); \ fflush(stdout); \ } while (0) #define RUBY_INSPECT(obj) do { \ rb_funcall(rb_mKernel, rb_intern("p"), 1, obj); \ fflush(stdout); \ } while (0) command-t-4.0/ruby/command-t/extconf.rb000066400000000000000000000033131271652626300201010ustar00rootroot00000000000000# Copyright 2010-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. require 'pathname' 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') # Create `metadata.rb`, which is used to diagnose installation problems. basedir = Pathname.new(__FILE__).dirname (basedir + 'metadata.rb').open('w') do |f| f.puts <<-END.gsub(/^ /, '') # This file was generated by #{(basedir + 'extconf.rb').to_s} module CommandT module Metadata EXPECTED_RUBY_VERSION = #{RUBY_VERSION.inspect} EXPECTED_RUBY_PATCHLEVEL = #{ defined?(RUBY_PATCHLEVEL) ? RUBY_PATCHLEVEL.inspect : nil.inspect } UNKNOWN = false end end END end command-t-4.0/ruby/command-t/finder.rb000066400000000000000000000034151271652626300177050ustar00rootroot00000000000000# Copyright 2010-present 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 :CommandFinder, 'command-t/finder/command_finder' autoload :FileFinder, 'command-t/finder/file_finder' autoload :HelpFinder, 'command-t/finder/help_finder' autoload :HistoryFinder, 'command-t/finder/history_finder' autoload :JumpFinder, 'command-t/finder/jump_finder' autoload :LineFinder, 'command-t/finder/line_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 # Returns a human-readable name describing the finder, for display in the # statusline attached to the MatchWindow buffer. def name 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 end command-t-4.0/ruby/command-t/finder/000077500000000000000000000000001271652626300173555ustar00rootroot00000000000000command-t-4.0/ruby/command-t/finder/buffer_finder.rb000066400000000000000000000006071271652626300225050ustar00rootroot00000000000000# Copyright 2010-present 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 def name 'Buffers' end end end end command-t-4.0/ruby/command-t/finder/command_finder.rb000066400000000000000000000010631271652626300226470ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Finder class CommandFinder < Finder def initialize(options = {}) @scanner = Scanner::CommandScanner.new @matcher = Matcher.new @scanner, :always_show_dot_files => true end def open_selection(command, selection, options = {}) ::VIM::command "call feedkeys(':#{selection} ', 'nt')" end def flush; end def name 'Commands' end end end end command-t-4.0/ruby/command-t/finder/file_finder.rb000066400000000000000000000017041271652626300221520ustar00rootroot00000000000000# Copyright 2010-present 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 def name 'Files' end end end end command-t-4.0/ruby/command-t/finder/help_finder.rb000066400000000000000000000010611271652626300221570ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Finder class HelpFinder < Finder def initialize(options = {}) @scanner = Scanner::HelpScanner.new @matcher = Matcher.new @scanner, :always_show_dot_files => true end def open_selection(command, selection, options = {}) ::VIM::command "help #{selection}" end def flush @scanner.flush end def name 'Help' end end end end command-t-4.0/ruby/command-t/finder/history_finder.rb000066400000000000000000000015711271652626300227360ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Finder class HistoryFinder < Finder def initialize(options = {}) @history_type = options[:history_type] # / or : @scanner = Scanner::HistoryScanner.new("silent history #{@history_type}") @matcher = Matcher.new @scanner, :always_show_dot_files => true end def open_selection(command, selection, options = {}) # Need to unescape to reverse the work done by `#sanitize_path_string`. unescaped = selection.gsub(/\\(.)/, '\1') escaped = VIM.escape_for_single_quotes unescaped ::VIM::command "call feedkeys('#{@history_type}#{escaped} ', 'nt')" end def flush; end def name @history_type == ':' ? 'History' : 'Searches' end end end end command-t-4.0/ruby/command-t/finder/jump_finder.rb000066400000000000000000000006011271652626300222010ustar00rootroot00000000000000# Copyright 2011-present 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 def name 'Jumps' end end end end command-t-4.0/ruby/command-t/finder/line_finder.rb000066400000000000000000000010501271652626300221540ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Finder class LineFinder < Finder def initialize(options = {}) @scanner = Scanner::LineScanner.new @matcher = Matcher.new @scanner, :always_show_dot_files => true end def open_selection(command, selection, options = {}) ::VIM::command "#{selection.sub(/.+:(\d+)$/, '\1')}" end def flush; end def name 'Lines' end end end end command-t-4.0/ruby/command-t/finder/mru_buffer_finder.rb000066400000000000000000000016531271652626300233720ustar00rootroot00000000000000# Copyright 2014-present 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 def name 'Recent' end end end end command-t-4.0/ruby/command-t/finder/tag_finder.rb000066400000000000000000000013531271652626300220060ustar00rootroot00000000000000# Copyright 2011-present 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 def name 'Tags' end end end end command-t-4.0/ruby/command-t/heap.c000066400000000000000000000064541271652626300172000ustar00rootroot00000000000000// Copyright 2016-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include /* for free(), malloc(), NULL */ #include "heap.h" #define HEAP_PARENT(index) ((index - 1) / 2) #define HEAP_LEFT(index) (2 * index + 1) #define HEAP_RIGHT(index) (2 * index + 2) /** * Returns a new heap, or NULL on failure. */ heap_t *heap_new(long capacity, heap_compare_entries comparator) { heap_t *heap = malloc(sizeof(heap_t)); if (!heap) { return NULL; } heap->capacity = capacity; heap->comparator = comparator; heap->count = 0; heap->entries = malloc(capacity * sizeof(void *)); if (!heap->entries) { free(heap); return NULL; } return heap; } /** * Frees a previously created heap. */ void heap_free(heap_t *heap) { free(heap->entries); free(heap); } /** * @internal * * Compare values at indices `a_idx` and `b_idx` using the heap's comparator * function. */ int heap_compare(heap_t *heap, long a_idx, long b_idx) { const void *a = heap->entries[a_idx]; const void *b = heap->entries[b_idx]; return heap->comparator(a, b); } /** * @internal * * Returns 1 if the heap property holds (ie. parent < child). */ int heap_property(heap_t *heap, long parent_idx, long child_idx) { return heap_compare(heap, parent_idx, child_idx) > 0; } /** * @internal * * Swaps the values at indexes `a` and `b` within `heap`. */ void heap_swap(heap_t *heap, long a, long b) { void *tmp = heap->entries[a]; heap->entries[a] = heap->entries[b]; heap->entries[b] = tmp; } /** * Inserts `value` into `heap`. */ void heap_insert(heap_t *heap, void *value) { long idx, parent_idx; // If at capacity, ignore. if (heap->count == heap->capacity) { return; } // Insert into first empty slot. idx = heap->count; heap->entries[idx] = value; heap->count++; // Bubble upwards until heap property is restored. parent_idx = HEAP_PARENT(idx); while (idx && !heap_property(heap, parent_idx, idx)) { heap_swap(heap, idx, parent_idx); idx = parent_idx; parent_idx = HEAP_PARENT(idx); } } /** * @internal * * Restores the heap property starting at `idx`. */ void heap_heapify(heap_t *heap, long idx) { long left_idx = HEAP_LEFT(idx); long right_idx = HEAP_RIGHT(idx); long smallest_idx = right_idx < heap->count ? // Right (and therefore left) child exists. (heap_compare(heap, left_idx, right_idx) > 0 ? left_idx : right_idx) : left_idx < heap->count ? // Only left child exists. left_idx : // No children exist. idx; if ( smallest_idx != idx && !heap_property(heap, idx, smallest_idx) ) { // Swap with smallest_idx child. heap_swap(heap, idx, smallest_idx); heap_heapify(heap, smallest_idx); } } /** * Extracts the minimum value from `heap`. */ void *heap_extract(heap_t *heap) { void *extracted = NULL; if (heap->count) { // Grab root value. extracted = heap->entries[0]; // Move last item to root. heap->entries[0] = heap->entries[heap->count - 1]; heap->count--; // Restore heap property. heap_heapify(heap, 0); } return extracted; } command-t-4.0/ruby/command-t/heap.h000066400000000000000000000011001271652626300171640ustar00rootroot00000000000000// Copyright 2016-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. /** * A fixed size min-heap implementation. */ typedef int (*heap_compare_entries)(const void *a, const void *b); typedef struct { long count; long capacity; void **entries; heap_compare_entries comparator; } heap_t; #define HEAP_PEEK(heap) (heap->entries[0]) heap_t *heap_new(long capacity, heap_compare_entries comparator); void heap_free(heap_t *heap); void heap_insert(heap_t *heap, void *value); void *heap_extract(heap_t *heap); command-t-4.0/ruby/command-t/match.c000066400000000000000000000210751271652626300173530ustar00rootroot00000000000000// Copyright 2010-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include /* for DBL_MAX */ #include "match.h" #include "ext.h" #include "ruby_compat.h" #define UNSET_SCORE FLT_MAX // 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. long *rightmost_match_p; // Rightmost match for each char in needle. float max_score_per_char; int always_show_dot_files; // Boolean. int never_show_dot_files; // Boolean. int case_sensitive; // Boolean. int recurse; // Boolean. float *memo; // Memoization. } matchinfo_t; float 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. float score // Cumulative score so far. ) { long distance, i, j; float *memoized = NULL; float score_for_char; float seen_score = 0; // Iterate over needle. for (i = needle_idx; i < m->needle_len; i++) { // Iterate over (valid range of) haystack. for (j = haystack_idx; j <= m->rightmost_match_p[i]; j++) { char c, d; // Do we have a memoized result we can return? memoized = &m->memo[j * m->needle_len + i]; if (*memoized != UNSET_SCORE) { return *memoized > seen_score ? *memoized : seen_score; } c = m->needle_p[i]; d = m->haystack_p[j]; if (d == '.') { if (j == 0 || m->haystack_p[j - 1] == '/') { // This is a dot-file. int dot_search = c == '.'; // Searching for a dot. if ( m->never_show_dot_files || (!dot_search && !m->always_show_dot_files) ) { return *memoized = 0.0; } } } else if (d >= 'A' && d <= 'Z' && !m->case_sensitive) { d += 'a' - 'A'; // Add 32 to downcase. } if (c == d) { // Calculate score. float sub_score = 0; score_for_char = m->max_score_per_char; distance = j - last_idx; if (distance > 1) { float 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->rightmost_match_p[i] && m->recurse) { sub_score = recursive_match(m, j + 1, i, last_idx, score); if (sub_score > seen_score) { seen_score = sub_score; } } last_idx = j; haystack_idx = last_idx + 1; score += score_for_char; *memoized = seen_score > score ? seen_score : score; if (i == m->needle_len - 1) { // Whole string matched. return *memoized; } if (!m->recurse) { break; } } } } return *memoized = score; } float calculate_match( VALUE haystack, VALUE needle, VALUE case_sensitive, VALUE always_show_dot_files, VALUE never_show_dot_files, VALUE recurse, long needle_bitmask, long *haystack_bitmask ) { matchinfo_t m; long i; float score = 1.0; int compute_bitmasks = *haystack_bitmask == UNSET_BITMASK; m.haystack_p = RSTRING_PTR(haystack); m.haystack_len = RSTRING_LEN(haystack); m.needle_p = RSTRING_PTR(needle); m.needle_len = RSTRING_LEN(needle); m.rightmost_match_p = NULL; 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 = (int)case_sensitive; m.recurse = recurse == Qtrue; // Special case for zero-length search string. if (m.needle_len == 0) { // Filter out dot files. if (m.never_show_dot_files || !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] == '/')) { return 0.0; } } } } else { long haystack_limit; long memo_size; long needle_idx; long mask; long rightmost_match_p[m.needle_len]; if (*haystack_bitmask != UNSET_BITMASK) { if ((needle_bitmask & *haystack_bitmask) != needle_bitmask) { return 0.0; } } // Pre-scan string: // - Bail if it can't match at all. // - Record rightmost match for each character (prune search space). // - Record bitmask for haystack to speed up future searches. m.rightmost_match_p = rightmost_match_p; needle_idx = m.needle_len - 1; mask = 0; for (i = m.haystack_len - 1; i >= 0; i--) { char c = m.haystack_p[i]; char lower = c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c; if (!m.case_sensitive) { c = lower; } if (compute_bitmasks) { mask |= (1 << (lower - 'a')); } if (needle_idx >= 0) { char d = m.needle_p[needle_idx]; if (c == d) { rightmost_match_p[needle_idx] = i; needle_idx--; } } } if (compute_bitmasks) { *haystack_bitmask = mask; } if (needle_idx != -1) { return 0.0; } // Prepare for memoization. haystack_limit = rightmost_match_p[m.needle_len - 1] + 1; memo_size = m.needle_len * haystack_limit; { float memo[memo_size]; for (i = 0; i < memo_size; i++) { memo[i] = UNSET_SCORE; } m.memo = memo; score = recursive_match(&m, 0, 0, 0, 0.0); #ifdef DEBUG fprintf(stdout, " "); for (i = 0; i < m.needle_len; i++) { fprintf(stdout, " %c ", m.needle_p[i]); } fprintf(stdout, "\n"); for (i = 0; i < memo_size; i++) { char formatted[8]; if (i % m.needle_len == 0) { long haystack_idx = i / m.needle_len; fprintf(stdout, "%c: ", m.haystack_p[haystack_idx]); } if (memo[i] == UNSET_SCORE) { snprintf(formatted, sizeof(formatted), " - "); } else { snprintf(formatted, sizeof(formatted), " %-.4f", memo[i]); } fprintf(stdout, "%s", formatted); if ((i + 1) % m.needle_len == 0) { fprintf(stdout, "\n"); } else { fprintf(stdout, " "); } } fprintf(stdout, "Final score: %f\n\n", score); #endif } } return score; } command-t-4.0/ruby/command-t/match.h000066400000000000000000000010121271652626300173450ustar00rootroot00000000000000// Copyright 2010-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include #define UNSET_BITMASK (-1) // Struct for representing an individual match. typedef struct { VALUE path; long bitmask; float score; } match_t; extern float calculate_match( VALUE str, VALUE needle, VALUE case_sensitive, VALUE always_show_dot_files, VALUE never_show_dot_files, VALUE recurse, long needle_bitmask, long *haystack_bitmask ); command-t-4.0/ruby/command-t/match_window.rb000066400000000000000000000431261271652626300211240ustar00rootroot00000000000000# Copyright 2010-present 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 = '' MH_END = '' @@buffer = nil Highlight = Struct.new(:highlight, :bang) def initialize(options = {}) @encoding = options[:encoding] @highlight_color = options[:highlight_color] || 'PmenuSel' @min_height = options[:min_height] @prompt = options[:prompt] @reverse_list = options[:match_window_reverse] quoted_name = VIM::escape_for_single_quotes(options[:name]) escaped_name = ::VIM::evaluate("fnameescape('#{quoted_name}')") run_will_show_autocmd 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 if options[:debounce_interval] > 0 set 'updatetime', options[:debounce_interval] end # Save existing window views so we can restore them later. current_window = ::VIM::evaluate('winnr()') @windows = (0..(::VIM::Window.count - 1)).map do |i| focus_window(i + 1) view = ::VIM::evaluate('winsaveview()') window = OpenStruct.new( :index => i, :height => ::VIM::Window[i].height, :width => ::VIM::Window[i].width, :lnum => view['lnum'], :col => view['col'], :coladd => view['coladd'], :curswant => view['curswant'], :topline => view['topline'], :topfill => view['topfill'], :leftcol => view['leftcol'], :skipcol => view['skipcol'] ) # When creating a split for the match window, move the cursor to the # opposite side of the window's viewport to prevent unwanted scrolling. boundary_line = options[:match_window_at_top] ? ::VIM::evaluate("line('w$')") : view['topline'] ::VIM::evaluate("winrestview({'lnum': #{boundary_line}})") window end focus_window(current_window) # show match window split_location = options[:match_window_at_top] ? 'topleft' : 'botright' if ((number = buffer_number)) # still have buffer from last time ::VIM::command "silent! #{split_location} #{number}sbuffer" if $curbuf.number != number raise "Can't re-open Command-T match listing buffer" end $curwin.height = 1 ::VIM::command "0file" ::VIM::command "keepalt file #{escaped_name}" else # creating match window for first time and set it up ::VIM::command "silent! keepalt #{split_location} 1split #{escaped_name}" set 'bufhidden', 'unload' # unload buf when no longer displayed set 'buftype', 'nofile' # buffer is not related to any file set 'filetype', 'command-t' # provide for detectability/extensibility set 'modifiable', false # prevent manual edits set 'readonly', false # avoid W10 "Changing a readonly file" 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 if File.basename($curbuf.name) != options[:name] raise "Can't find Command-T match listing buffer" end @@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. , etc) ::VIM::command 'augroup CommandTMatchWindow' ::VIM::command 'autocmd!' ::VIM::command 'autocmd BufLeave silent! ruby $command_t.leave' ::VIM::command 'autocmd BufUnload silent! ruby $command_t.unload' ::VIM::command 'augroup END' @has_focus = false @abbrev = '' @window = $curwin end def buffer_number @@buffer && @@buffer.number rescue Vim::DeletedBufferError # Beware of people manually deleting Command-T's hidden, unlisted buffer. @@buffer = nil 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 base = File.basename($curbuf.name) escaped_name = ::VIM::evaluate("fnameescape('#{base}')") ::VIM::command "silent! bwipeout! #{escaped_name}" @@buffer = nil else ::VIM::command "silent! bunload! #{@@buffer.number}" end end def leave close unload end def unload restore_window_views @prompt.dispose @settings.restore show_cursor run_did_hide_autocmd 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 focus_window(number) ::VIM::command("noautocmd #{number}wincmd w") end 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_views # 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 current_window = ::VIM::evaluate('winnr()') @windows.each do |w| # beware: window may be nil if window = ::VIM::Window[w.index] window.height = w.height window.width = w.width focus_window(w.index + 1) ::VIM::evaluate("winrestview({" + "'lnum': #{w.lnum}," + "'col': #{w.col}, " + "'coladd': #{w.coladd}, " + "'curswant': #{w.curswant}, " + "'topline': #{w.topline}, " + "'topfill': #{w.topfill}, " + "'leftcol': #{w.leftcol}, " + "'skipcol': #{w.skipcol}" + "})") end end focus_window(current_window) end def run_will_show_autocmd run_autocmd('CommandTWillShowMatchListing') end def run_did_hide_autocmd run_autocmd('CommandTDidHideMatchListing') end def run_autocmd(cmd) ::VIM::command("call commandt#private#RunAutocmd('#{cmd}')") 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) if @encoding && match.respond_to?(:force_encoding) && match.encoding != @encoding match = match.force_encoding(@encoding) end 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 4 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 [definition] # links to VisualNOS -> both of the above # Cursor xxx cleared -> :hi! clear Cursor highlight = VIM::capture 'silent! 0verbose highlight Cursor' if highlight =~ /^Cursor\s+xxx\s+(.+)\blinks to (\w+)/m [ Highlight.new("Cursor #{$~[1]}"), Highlight.new("link Cursor #{$~[2]}", '!') ] elsif highlight =~ /^Cursor\s+xxx\s+links to (\w+)/m [Highlight.new("link Cursor #{$~[1]}")] elsif highlight =~ /^Cursor\s+xxx\s+cleared/m [Highlight.new('clear Cursor')] elsif highlight =~ /Cursor\s+xxx\s+(.+)/m [Highlight.new("Cursor #{$~[1]}")] else # likely cause E411 Cursor highlight group not found [] end end def hide_cursor if @cursor_highlight ::VIM::command 'highlight Cursor NONE' end end def show_cursor if @cursor_highlight @cursor_highlight.each do |highlight| config = highlight.highlight.gsub(/\s+/, ' ') ::VIM::command "highlight#{highlight.bang} #{config}" end end end def lock set 'modifiable', false end def unlock set 'modifiable', true end end end command-t-4.0/ruby/command-t/matcher.c000066400000000000000000000274561271652626300177130ustar00rootroot00000000000000// Copyright 2010-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include /* for qsort() */ #include /* for strncmp() */ #include "match.h" #include "matcher.h" #include "heap.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 /* 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 { long thread_count; long thread_index; long case_sensitive; long limit; match_t *matches; long path_count; VALUE haystacks; VALUE needle; VALUE always_show_dot_files; VALUE never_show_dot_files; VALUE recurse; long needle_bitmask; } thread_args_t; void *match_thread(void *thread_args) { long i; float score; heap_t *heap = NULL; thread_args_t *args = (thread_args_t *)thread_args; if (args->limit) { // Reserve one extra slot so that we can do an insert-then-extract even // when "full" (effectively allows use of min-heap to maintain a // top-"limit" list of items). heap = heap_new(args->limit + 1, cmp_score); } for ( i = args->thread_index; i < args->path_count; i += args->thread_count ) { args->matches[i].path = RARRAY_PTR(args->haystacks)[i]; if (args->needle_bitmask == UNSET_BITMASK) { args->matches[i].bitmask = UNSET_BITMASK; } args->matches[i].score = calculate_match( args->matches[i].path, args->needle, args->case_sensitive, args->always_show_dot_files, args->never_show_dot_files, args->recurse, args->needle_bitmask, &args->matches[i].bitmask ); if (heap) { if (heap->count == args->limit) { score = ((match_t *)HEAP_PEEK(heap))->score; if (args->matches[i].score >= score) { heap_insert(heap, &args->matches[i]); (void)heap_extract(heap); } } else { heap_insert(heap, &args->matches[i]); } } } return heap; } long calculate_bitmask(VALUE string) { char *str = RSTRING_PTR(string); long len = RSTRING_LEN(string); long i; long mask = 0; for (i = 0; i < len; i++) { if (str[i] >= 'a' && str[i] <= 'z') { mask |= (1 << (str[i] - 'a')); } else if (str[i] >= 'A' && str[i] <= 'Z') { mask |= (1 << (str[i] - 'A')); } } return mask; } VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self) { long i, j, limit, path_count, thread_count; #ifdef HAVE_PTHREAD_H long err; pthread_t *threads; #endif long needle_bitmask = UNSET_BITMASK; long heap_matches_count; int use_heap; int sort; match_t *matches; match_t *heap_matches = NULL; heap_t *heap; thread_args_t *thread_args; VALUE always_show_dot_files; VALUE case_sensitive; VALUE recurse; VALUE ignore_spaces; VALUE limit_option; VALUE needle; VALUE never_show_dot_files; VALUE new_paths_object_id; VALUE options; VALUE paths; VALUE paths_object_id; VALUE results; VALUE scanner; VALUE sort_option; VALUE threads_option; VALUE wrapped_matches; // Process arguments: 1 mandatory, 1 optional. if (rb_scan_args(argc, argv, "11", &needle, &options) == 1) options = Qnil; if (NIL_P(needle)) rb_raise(rb_eArgError, "nil needle"); // Check optional options hash 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); ignore_spaces = CommandT_option_from_hash("ignore_spaces", options); always_show_dot_files = rb_iv_get(self, "@always_show_dot_files"); never_show_dot_files = rb_iv_get(self, "@never_show_dot_files"); recurse = CommandT_option_from_hash("recurse", options); limit = NIL_P(limit_option) ? 15 : NUM2LONG(limit_option); sort = NIL_P(sort_option) || sort_option == Qtrue; use_heap = limit && sort; heap_matches_count = 0; needle = StringValue(needle); if (case_sensitive != Qtrue) needle = rb_funcall(needle, rb_intern("downcase"), 0); if (ignore_spaces == Qtrue) needle = rb_funcall(needle, rb_intern("delete"), 1, rb_str_new2(" ")); // Get unsorted matches. scanner = rb_iv_get(self, "@scanner"); paths = rb_funcall(scanner, rb_intern("paths"), 0); path_count = RARRAY_LEN(paths); // Cached C data, not visible to Ruby layer. paths_object_id = rb_ivar_get(self, rb_intern("paths_object_id")); new_paths_object_id = rb_funcall(paths, rb_intern("object_id"), 0); rb_ivar_set(self, rb_intern("paths_object_id"), new_paths_object_id); if ( NIL_P(paths_object_id) || NUM2LONG(new_paths_object_id) != NUM2LONG(paths_object_id) ) { // `paths` changed, need to replace matches array. paths_object_id = new_paths_object_id; matches = malloc(path_count * sizeof(match_t)); if (!matches) { rb_raise(rb_eNoMemError, "memory allocation failed"); } wrapped_matches = Data_Wrap_Struct( rb_cObject, 0, free, matches ); rb_ivar_set(self, rb_intern("matches"), wrapped_matches); } else { // Get existing array. Data_Get_Struct( rb_ivar_get(self, rb_intern("matches")), match_t, matches ); // Will compare against previously computed haystack bitmasks. needle_bitmask = calculate_bitmask(needle); } thread_count = NIL_P(threads_option) ? 1 : NUM2LONG(threads_option); if (use_heap) { heap_matches = malloc(thread_count * limit * sizeof(match_t)); if (!heap_matches) { rb_raise(rb_eNoMemError, "memory allocation failed"); } } #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].limit = use_heap ? limit : 0; thread_args[i].path_count = path_count; thread_args[i].haystacks = paths; thread_args[i].needle = needle; thread_args[i].always_show_dot_files = always_show_dot_files; thread_args[i].never_show_dot_files = never_show_dot_files; thread_args[i].recurse = recurse; thread_args[i].needle_bitmask = needle_bitmask; #ifdef HAVE_PTHREAD_H if (i == thread_count - 1) { #endif // For the last "worker", we'll just use the main thread. heap = match_thread(&thread_args[i]); if (heap) { for (j = 0; j < heap->count; j++) { heap_matches[heap_matches_count++] = *(match_t *)heap->entries[j]; } heap_free(heap); } #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], (void **)&heap); if (err != 0) { rb_raise(rb_eSystemCallError, "pthread_join() failure (%d)", (int)err); } if (heap) { for (j = 0; j < heap->count; j++) { heap_matches[heap_matches_count++] = *(match_t *)heap->entries[j]; } heap_free(heap); } } free(threads); #endif if (sort) { if ( RSTRING_LEN(needle) == 0 || (RSTRING_LEN(needle) == 1 && RSTRING_PTR(needle)[0] == '.') ) { // Alphabetic order if search string is only "" or "." // TODO: make those semantics fully apply to heap case as well // (they don't because the heap itself calls cmp_score, which means // that the items which stay in the top [limit] may (will) be // different). qsort( use_heap ? heap_matches : matches, use_heap ? heap_matches_count : path_count, sizeof(match_t), cmp_alpha ); } else { qsort( use_heap ? heap_matches : matches, use_heap ? heap_matches_count : path_count, sizeof(match_t), cmp_score ); } } results = rb_ary_new(); if (limit == 0) limit = path_count; for ( i = 0; i < (use_heap ? heap_matches_count : path_count) && limit > 0; i++ ) { if ((use_heap ? heap_matches : matches)[i].score > 0.0) { rb_funcall( results, rb_intern("push"), 1, (use_heap ? heap_matches : matches)[i].path ); limit--; } } if (use_heap) { free(heap_matches); } return results; } command-t-4.0/ruby/command-t/matcher.h000066400000000000000000000004521271652626300177030ustar00rootroot00000000000000// Copyright 2010-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include extern VALUE CommandTMatcher_initialize(int argc, VALUE *argv, VALUE self); extern VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self); command-t-4.0/ruby/command-t/metadata/000077500000000000000000000000001271652626300176665ustar00rootroot00000000000000command-t-4.0/ruby/command-t/metadata/fallback.rb000066400000000000000000000006001271652626300217460ustar00rootroot00000000000000# Copyright 2015-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT module Metadata # This file gets overwritten with actual data during the installation # process (when `ruby extconf.rb` is run). EXPECTED_RUBY_VERSION = '[unknown]' EXPECTED_RUBY_PATCHLEVEL = '[unknown]' UNKNOWN = true end end command-t-4.0/ruby/command-t/mru.rb000066400000000000000000000023431271652626300172400ustar00rootroot00000000000000# Copyright 2014-present 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 for the correct buffer number. stack.delete_if do |b| b.number == ::VIM::evaluate('expand("")').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 end command-t-4.0/ruby/command-t/path_utilities.rb000066400000000000000000000007411271652626300214640ustar00rootroot00000000000000# Copyright 2010-present 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 end command-t-4.0/ruby/command-t/progress_reporter.rb000066400000000000000000000022231271652626300222200ustar00rootroot00000000000000# Copyright 2010-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT # Simple class for displaying scan progress to the user. # # The active scanner calls the `#update` method with a `count` to inform it of # progress, the reporter updates the UI and then returns a suggested count at # which to invoke `#update` again in the future (the suggested count is based # on a heuristic that seeks to update the UI about 5 times per second). class ProgressReporter SPINNER = %w[^ > v <] def initialize @spinner ||= SPINNER.first end def update(count) @spinner = SPINNER[(SPINNER.index(@spinner) + 1) % SPINNER.length] ::VIM::command "echon '#{@spinner} #{count}'" ::VIM::command 'redraw' # Aim for 5 updates per second. now = Time.now.to_f if @last_time time_diff = now - @last_time count_diff = count - @last_count next_count = count + ((0.2 / time_diff) * count_diff).to_i else next_count = count + 100 end @last_time = now @last_count = count next_count end end end command-t-4.0/ruby/command-t/prompt.rb000066400000000000000000000076661271652626300177730ustar00rootroot00000000000000# Copyright 2010-present 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(options = {}) @abbrev = '' # abbreviation entered so far @col = 0 # cursor position @cursor_color = options[:cursor_color] || 'Underlined' @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 def redraw if @has_focus prompt_highlight = 'Comment' normal_highlight = 'None' cursor_highlight = @cursor_color 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 private # 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 end command-t-4.0/ruby/command-t/ruby_compat.h000066400000000000000000000015631271652626300206100ustar00rootroot00000000000000// Copyright 2010-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include // 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 command-t-4.0/ruby/command-t/scanner.rb000066400000000000000000000022451271652626300200670ustar00rootroot00000000000000# Copyright 2010-present 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 :CommandScanner, 'command-t/scanner/command_scanner' autoload :FileScanner, 'command-t/scanner/file_scanner' autoload :HelpScanner, 'command-t/scanner/help_scanner' autoload :HistoryScanner, 'command-t/scanner/history_scanner' autoload :JumpScanner, 'command-t/scanner/jump_scanner' autoload :LineScanner, 'command-t/scanner/line_scanner' autoload :MRUBufferScanner, 'command-t/scanner/mru_buffer_scanner' autoload :TagScanner, 'command-t/scanner/tag_scanner' # Subclasses implement this method to return the list of paths that should # be searched. # # Note that as an optimization, the C extension will record the # `Object#object_id` of the returned array and assumes it will not be # mutated. def paths raise RuntimeError, 'Subclass responsibility' end private def progress_reporter @progress_reporter ||= ProgressReporter.new end end end command-t-4.0/ruby/command-t/scanner/000077500000000000000000000000001271652626300175375ustar00rootroot00000000000000command-t-4.0/ruby/command-t/scanner/buffer_scanner.rb000066400000000000000000000010161271652626300230440ustar00rootroot00000000000000# Copyright 2010-present 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 end end command-t-4.0/ruby/command-t/scanner/command_scanner.rb000066400000000000000000000015071271652626300232160ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Scanner class CommandScanner < Scanner def paths @paths ||= paths! end private def paths! # Get user commands. commands = VIM.capture('silent command').split("\n")[2..-1].map do |line| line.sub(/\A.{4}(\S+).+/, '\1') end # Get built-in commands from `ex-cmd-index`. ex_cmd_index = ::VIM.evaluate('expand(findfile("doc/index.txt", &runtimepath))') if File.readable?(ex_cmd_index) File.readlines(ex_cmd_index).each do |line| if line =~ %r{\A\|:([^|]+)\|\s+} commands << $~[1] end end end commands.uniq end end end end command-t-4.0/ruby/command-t/scanner/file_scanner.rb000066400000000000000000000076531271652626300225270ustar00rootroot00000000000000# Copyright 2010-present 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 # 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] || 100_000 @max_caches = options[:max_caches] || 1 @scan_dot_directories = options[:scan_dot_directories] || false @wild_ignore = options[:wild_ignore] @scan_submodules = options[:git_scan_submodules] || false @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 show_max_files_warning unless VIM::get_bool('g:CommandTSuppressMaxFilesWarning', false) ::VIM::command('redraw!') ::VIM::command('echohl ErrorMsg') warning = "Warning: maximum file limit reached\n" + "\n" + "Increase it by setting a higher value in $MYVIMRC; eg:\n" + " let g:CommandTMaxFiles=#{@max_files * 2}\n" + "Or suppress this warning by setting:\n" + " let g:CommandTSuppressMaxFilesWarning=1\n" + "For best performance, consider using a fast scanner; see:\n" + " :help g:CommandTFileScanner\n" + "\n" + "Press ENTER to continue." ::VIM::evaluate(%{input("#{warning}")}) ::VIM::command('echohl None') end end 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 end # Used to skip expensive calls to `expand()` when there is no applicable # wildignore. def apply_wild_ignore? if has_custom_wild_ignore? !@wild_ignore.empty? else !!@base_wild_ignore end 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 end end command-t-4.0/ruby/command-t/scanner/file_scanner/000077500000000000000000000000001271652626300221675ustar00rootroot00000000000000command-t-4.0/ruby/command-t/scanner/file_scanner/find_file_scanner.rb000066400000000000000000000036461271652626300261550ustar00rootroot00000000000000# Copyright 2014-present 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 `each_line` 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 next_progress = progress_reporter.update(counter) stdout.each_line do |line| next if path_excluded?(line.chomp!) paths << line[@prefix_len..-1] next_progress = progress_reporter.update(counter) if counter == next_progress if (counter += 1) > @max_files show_max_files_warning break end end end paths ensure $/ = separator end end end end end command-t-4.0/ruby/command-t/scanner/file_scanner/git_file_scanner.rb000066400000000000000000000031171271652626300260110ustar00rootroot00000000000000# Copyright 2014-present 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 LsFilesError = Class.new(::RuntimeError) def paths! Dir.chdir(@path) do all_files = list_files(%w[git ls-files --exclude-standard -z]) if @scan_submodules base = nil list_files(%w[ git submodule foreach --recursive git ls-files --exclude-standard -z ]).each do |path| if path =~ /\AEntering '(.*)'\n(.*)\z/ base = $~[1] path = $~[2] end all_files.push(base + File::SEPARATOR + path) end end filtered = all_files. map { |path| path.chomp }. reject { |path| path_excluded?(path, 0) } truncated = filtered.take(@max_files) if truncated.count < filtered.count show_max_files_warning end truncated.to_a end rescue LsFilesError super rescue Errno::ENOENT # git executable not present and executable super end private def list_files(command) stdin, stdout, stderr = Open3.popen3(*command) stdout.read.split("\0") ensure raise LsFilesError if stderr && stderr.gets end end end end end command-t-4.0/ruby/command-t/scanner/file_scanner/ruby_file_scanner.rb000066400000000000000000000036411271652626300262110ustar00rootroot00000000000000# Copyright 2010-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Scanner class FileScanner FileLimitExceeded = Class.new(::RuntimeError) # Pure Ruby implementation of a file scanner. class RubyFileScanner < FileScanner def paths! accumulator = [] @depth = 0 @files = 0 @next_progress = progress_reporter.update(@files) add_paths_for_directory(@path, accumulator) accumulator rescue FileLimitExceeded show_max_files_warning 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 @next_progress = progress_reporter.update(@files) if @files == @next_progress 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 end end end command-t-4.0/ruby/command-t/scanner/file_scanner/watchman_file_scanner.rb000066400000000000000000000050021271652626300270230ustar00rootroot00000000000000# Copyright 2014-present 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. WatchmanError = Class.new(::RuntimeError) def paths! sockname = extract_value( Watchman::Utils.load(get_raw_sockname), 'sockname' ) 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 # or enforce_root_files/root_files (>= version 3.1) extract_value(result) end query = ['query', root, { 'expression' => ['type', 'f'], 'fields' => ['name'], }] paths = Watchman::Utils.query(query, socket) # could return error if watch is removed extracted = extract_value(paths, 'files') if ( apply_wild_ignore? && (regex = VIM::wildignore_to_regexp(@wild_ignore || @base_wild_ignore)) ) extracted.select { |path| path !~ regex } else extracted end end rescue Errno::ENOENT, WatchmanError # watchman executable not present, or unable to fulfil request super end private def extract_value(object, key = nil) raise WatchmanError, object['error'] if object.has_key?('error') object[key] end def get_raw_sockname raw_sockname = %x{watchman --output-encoding=bser get-sockname} raise WatchmanError, 'get-sockname failed' if !$?.exitstatus.zero? raw_sockname end end end end end command-t-4.0/ruby/command-t/scanner/help_scanner.rb000066400000000000000000000021721271652626300225270ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Scanner class HelpScanner < Scanner def paths @cached_tags ||= paths! end def flush @cached_tags = nil end private def paths! # Vim doesn't provide an easy way to get a list of all help tags. # `tagfiles()` only shows the tagfiles for the current buffer, so you # need to already be in a buffer of `'buftype'` `help` for that to work. # Likewise, `taglist()` only shows tags that apply to the current file # type, and `:tag` has the same restriction. # # So, we look for a "doc/tags" file at every location in the # `'runtimepath'` and try to manually parse it. tags = [] ::VIM::evaluate('findfile("doc/tags", &runtimepath, -1)').each do |path| if File.readable?(path) File.readlines(path).each do |tag| tags << tag.split.first if tag.split.first end end end tags end end end end command-t-4.0/ruby/command-t/scanner/history_scanner.rb000066400000000000000000000010171271652626300232750ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Scanner class HistoryScanner < Scanner def initialize(history_command) @history_command = history_command end def paths @paths ||= paths! end private def paths! VIM.capture(@history_command).split("\n")[2..-1].map do |line| line.sub(/\A>?\s*\d+\s*(.+)/, '\1').strip end.uniq end end end end command-t-4.0/ruby/command-t/scanner/jump_scanner.rb000066400000000000000000000013341271652626300225510ustar00rootroot00000000000000# Copyright 2011-present 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 end end command-t-4.0/ruby/command-t/scanner/line_scanner.rb000066400000000000000000000022761271652626300225330ustar00rootroot00000000000000# Copyright 2011-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Scanner class LineScanner < Scanner def paths @lines ||= paths! end private def paths! # $curbuf is the Command-T match listing; we actually want the last # buffer, but passing `$`, `#`, `%` etc to `bufnr()` returns the wrong # value. number = ::VIM.evaluate("g:CommandTCurrentBuffer").to_i return [] unless number > 0 buffer = nil (0...(::VIM::Buffer.count)).each do |n| buffer = ::VIM::Buffer[n] if buffer_number(buffer) == number break else buffer = nil end end return [] unless buffer (1..(buffer.length)).map do |n| line = buffer[n] unless line.match(/\A\s*\z/) line.sub(/\A\s*/, '') + ':' + n.to_s end end.compact end def buffer_number(buffer) buffer && buffer.number rescue Vim::DeletedBufferError # Beware of people manually deleting Command-T's hidden, unlisted buffer. end end end end command-t-4.0/ruby/command-t/scanner/mru_buffer_scanner.rb000066400000000000000000000015441271652626300237350ustar00rootroot00000000000000# Copyright 2014-present 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 end end command-t-4.0/ruby/command-t/scanner/tag_scanner.rb000066400000000000000000000013061271652626300223500ustar00rootroot00000000000000# Copyright 2011-present 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 end end command-t-4.0/ruby/command-t/scm_utilities.rb000066400000000000000000000010521271652626300213060ustar00rootroot00000000000000# Copyright 2014-present 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 end command-t-4.0/ruby/command-t/settings.rb000066400000000000000000000044671271652626300203060ustar00rootroot00000000000000# Copyright 2010-present 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 filetype foldcolumn foldlevel list modifiable number readonly 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 end command-t-4.0/ruby/command-t/stub.rb000066400000000000000000000023561271652626300174160ustar00rootroot00000000000000# Copyright 2010-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. module CommandT class Stub @@expected_version = Metadata::EXPECTED_RUBY_VERSION @@expected_patchlevel = Metadata::EXPECTED_RUBY_PATCHLEVEL @@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}", "Expected version: #{@@expected_version}-p#{@@expected_patchlevel}", 'For more information type: :help command-t'] [ :flush, :show_buffer_finder, :show_command_finder, :show_file_finder, :show_history_finder, :show_help_finder, :show_jump_finder, :show_line_finder, :show_mru_finder, :show_search_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 end command-t-4.0/ruby/command-t/util.rb000066400000000000000000000074621271652626300174210ustar00rootroot00000000000000# Copyright 2013-present 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 # # 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 end command-t-4.0/ruby/command-t/vim.rb000066400000000000000000000062451271652626300172350ustar00rootroot00000000000000# Copyright 2010-present 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, default = nil) exists?(name) ? ::VIM::evaluate("#{name}").to_i != 0 : default 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 # Conservatively convert wildignore patterns that we understand to a # regex. Supported patterns noted in the inline comments below. # # If this ends up doing something wrong, set `g:CommandTWildIgnore` to '' # to opt out or otherwise override to produce a conforming pattern. def wildignore_to_regexp(str) patterns = str.split(',') regex = patterns.map do |pattern| if pattern.match(%r{\A([^*/]+)\z}) # something (match file at any level) '(\A|/)' + Regexp.escape($~[1]) + '\z' elsif pattern.match(%r{\A\*\.([^*]+)\z}) # *.something (match file with extension at any level) '\.' + Regexp.escape($~[1]) + '\z' elsif pattern.match(%r{\A\*/(.+)\z}) # */something (match files or directories at any level) '(\A|/)' + Regexp.escape($~[1]) + '(/|\z)' elsif pattern.match(%r{\A\*/([^*]+)/*\z}) # */something/* (match directories at any level) '(\A|/)' + Regexp.escape($~[1]) + '(/|\z)' end end.compact.join('|') Regexp.new(regex) unless regex.empty? end end end end command-t-4.0/ruby/command-t/vim/000077500000000000000000000000001271652626300167015ustar00rootroot00000000000000command-t-4.0/ruby/command-t/vim/screen.rb000066400000000000000000000004331271652626300205050ustar00rootroot00000000000000# Copyright 2010-present 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 end end command-t-4.0/ruby/command-t/vim/window.rb000066400000000000000000000011161271652626300205340ustar00rootroot00000000000000# Copyright 2010-present 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 end end command-t-4.0/ruby/command-t/watchman.c000066400000000000000000000503231271652626300200570ustar00rootroot00000000000000// Copyright 2014-present 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 #elif defined(HAVE_ST_H) #include #else #error no st.h header found #endif #include /* for uint8_t */ #include /* for fcntl() */ #include /* for errno */ #include /* 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/website/_docs/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 %zd", 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 command-t-4.0/ruby/command-t/watchman.h000066400000000000000000000017771271652626300200750ustar00rootroot00000000000000// Copyright 2014-present Greg Hurrell. All rights reserved. // Licensed under the terms of the BSD 2-clause license. #include /** * @module CommandT::Watchman::Utils * * Methods for working with the Watchman binary protocol * * @see https://github.com/facebook/watchman/blob/master/website/_docs/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); command-t-4.0/spec/000077500000000000000000000000001271652626300142005ustar00rootroot00000000000000command-t-4.0/spec/command-t/000077500000000000000000000000001271652626300160575ustar00rootroot00000000000000command-t-4.0/spec/command-t/controller_spec.rb000066400000000000000000000062021271652626300216010ustar00rootroot00000000000000# Copyright 2010-present 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 it 'does not enter an infinite loop when toggling focus' do # https://github.com/wincent/command-t/issues/157 stub(::VIM).evaluate('a:arg').returns('') set_string('g:CommandTTraverseSCM', 'pwd') controller.show_file_finder expect { controller.toggle_focus }.to_not raise_error 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).focus 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).unfocus stub(prompt).clear! stub(prompt).redraw 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 command-t-4.0/spec/command-t/finder/000077500000000000000000000000001271652626300173265ustar00rootroot00000000000000command-t-4.0/spec/command-t/finder/buffer_finder_spec.rb000066400000000000000000000035631271652626300234740ustar00rootroot00000000000000# Copyright 2010-present 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 expect(@finder.sorted_matches_for('kung foo fighting')).to eq([]) end it 'returns all files when query string is empty' do expect(@finder.sorted_matches_for('')).to eq(@paths) end it 'returns files in alphabetical order when query string is empty' do results = @finder.sorted_matches_for('') expect(results).to eq(results.sort) end it 'returns matching files in score order' do expect(@finder.sorted_matches_for('ba')).to eq(%w(baz foo/beta)) expect(@finder.sorted_matches_for('a')).to eq(%w(baz foo/beta)) end it 'returns matching dot files even when search term does not include a dot' do expect(@finder.sorted_matches_for('i')).to include('.vimrc') end it 'returns matching files inside dot directories even when search term does not include a dot' do expect(@finder.sorted_matches_for('i')).to 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 expect(@finder.sorted_matches_for('', :limit => 1)). to eq(%w(.git/config)) end it 'obeys the :limit option for non-empty search strings' do expect(@finder.sorted_matches_for('i', :limit => 2)). to eq(%w(.vimrc .vim/notes)) end end end command-t-4.0/spec/command-t/finder/file_finder_spec.rb000066400000000000000000000032221271652626300231320ustar00rootroot00000000000000# Copyright 2010-present 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 } stub(::VIM).command(/echon/) stub(::VIM).command('redraw') end describe 'sorted_matches_for method' do it 'returns an empty array when no matches' do expect(@finder.sorted_matches_for('kung foo fighting')).to eq([]) end it 'returns all files when query string is empty' do expect(@finder.sorted_matches_for('')).to eq(@all_fixtures) end it 'returns files in alphabetical order when query string is empty' do results = @finder.sorted_matches_for('') expect(results).to eq(results.sort) end it 'returns matching files in score order' do expect(@finder.sorted_matches_for('ba')). to eq(%w(baz bar/abc bar/xyz foo/beta)) expect(@finder.sorted_matches_for('a')). to eq(%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 expect(@finder.sorted_matches_for('', :limit => 2)). to eq(%w(bar/abc bar/xyz)) end it 'obeys the :limit option for non-empty search strings' do expect(@finder.sorted_matches_for('a', :limit => 3)). to eq(%w(baz bar/abc bar/xyz)) end end end command-t-4.0/spec/command-t/matcher_spec.rb000066400000000000000000000245721271652626300210530ustar00rootroot00000000000000# Copyright 2010-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. require 'spec_helper' require 'ostruct' 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, :recurse => true) 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 expect(matcher.sorted_matches_for('foo')).to eq([]) expect(matcher.sorted_matches_for('')).to eq([]) end it 'returns empty array when no matches' do matcher = matcher(*%w[foo/bar foo/baz bing]) expect(matcher.sorted_matches_for('xyz')).to eq([]) end it 'returns matching paths' do matcher = matcher(*%w[foo/bar foo/baz bing]) matches = matcher.sorted_matches_for('z') expect(matches.map { |m| m.to_s }).to eq(['foo/baz']) matches = matcher.sorted_matches_for('bg') expect(matches.map { |m| m.to_s }).to eq(['bing']) end it 'performs case-insensitive matching' do matches = matcher('Foo').sorted_matches_for('f') expect(matches.map { |m| m.to_s }).to eq(['Foo']) end it 'considers the space character to match a literal space' do paths = ['path_no_space', 'path with/space'] matches = matcher(*paths).sorted_matches_for('path space') expect(matches.map { |m| m.to_s }).to eq(['path with/space']) end context 'when the ignore_spaces option in specified' do it 'ignores the space character' do paths = ['path_no_space', 'path with/space'] matches = matcher(*paths).sorted_matches_for('path space', :ignore_spaces => true) expect(matches.map { |m| m.to_s }).to eq(['path_no_space', 'path with/space']) end end it 'considers the empty string to match everything' do matches = matcher('foo').sorted_matches_for('') expect(matches.map { |m| m.to_s }).to eq(['foo']) end # Can't imagine this happening in practice, but want to handle it in case. it 'gracefully handles empty haystacks' do expect(matcher('', 'foo').sorted_matches_for('').map { |m| m.to_s }).to eq(['', 'foo']) expect(matcher('', 'foo').sorted_matches_for('f').map { |m| m.to_s }).to eq(['foo']) end it 'does not consider mere substrings of the query string to be a match' do expect(matcher('foo').sorted_matches_for('foo...')).to eq([]) end it 'prioritizes shorter paths over longer ones' do expect(ordered_matches(%w[ articles_controller_spec.rb article.rb ], 'art')).to eq(%w[ article.rb articles_controller_spec.rb ]) end it 'prioritizes matches after "/"' do expect(ordered_matches(%w[fooobar foo/bar], 'b')).to eq(%w[foo/bar fooobar]) # note that / beats _ expect(ordered_matches(%w[foo_bar foo/bar], 'b')).to eq(%w[foo/bar foo_bar]) # / also beats - expect(ordered_matches(%w[foo-bar foo/bar], 'b')).to eq(%w[foo/bar foo-bar]) # and numbers expect(ordered_matches(%w[foo9bar foo/bar], 'b')).to eq(%w[foo/bar foo9bar]) # and periods expect(ordered_matches(%w[foo.bar foo/bar], 'b')).to eq(%w[foo/bar foo.bar]) # and spaces expect(ordered_matches(['foo bar', 'foo/bar'], 'b')).to eq(['foo/bar', 'foo bar']) end it 'prioritizes matches after "-"' do expect(ordered_matches(%w[fooobar foo-bar], 'b')).to eq(%w[foo-bar fooobar]) # - also beats . expect(ordered_matches(%w[foo.bar foo-bar], 'b')).to eq(%w[foo-bar foo.bar]) end it 'prioritizes matches after "_"' do expect(ordered_matches(%w[fooobar foo_bar], 'b')).to eq(%w[foo_bar fooobar]) # _ also beats . expect(ordered_matches(%w[foo.bar foo_bar], 'b')).to eq(%w[foo_bar foo.bar]) end it 'prioritizes matches after " "' do expect(ordered_matches(['fooobar', 'foo bar'], 'b')).to eq(['foo bar', 'fooobar']) # " " also beats . expect(ordered_matches(['foo.bar', 'foo bar'], 'b')).to eq(['foo bar', 'foo.bar']) end it 'prioritizes matches after numbers' do expect(ordered_matches(%w[fooobar foo9bar], 'b')).to eq(%w[foo9bar fooobar]) # numbers also beat . expect(ordered_matches(%w[foo.bar foo9bar], 'b')).to eq(%w[foo9bar foo.bar]) end it 'prioritizes matches after periods' do expect(ordered_matches(%w[fooobar foo.bar], 'b')).to eq(%w[foo.bar fooobar]) end it 'prioritizes matching capitals following lowercase' do expect(ordered_matches(%w[foobar fooBar], 'b')).to eq(%w[fooBar foobar]) end it 'prioritizes matches earlier in the string' do expect(ordered_matches(%w[******b* **b*****], 'b')).to eq(%w[**b***** ******b*]) end it 'prioritizes matches closer to previous matches' do expect(ordered_matches(%w[**b***c* **bc****], 'bc')).to eq(%w[**bc**** **b***c*]) end it 'scores alternative matches of same path differently' do # ie: # app/controllers/articles_controller.rb expect(ordered_matches(%w[ a**/****r******/**t*c***_*on*******.** ***/***********/art*****_con*******.** ], 'artcon')).to eq(%w[ ***/***********/art*****_con*******.** a**/****r******/**t*c***_*on*******.** ]) end it 'provides intuitive results for "artcon" and "articles_controller"' do expect(ordered_matches(%w[ app/controllers/heartbeat_controller.rb app/controllers/articles_controller.rb ], 'artcon')).to eq(%w[ app/controllers/articles_controller.rb app/controllers/heartbeat_controller.rb ]) end it 'provides intuitive results for "aca" and "a/c/articles_controller"' do expect(ordered_matches(%w[ app/controllers/heartbeat_controller.rb app/controllers/articles_controller.rb ], 'aca')).to eq(%w[ app/controllers/articles_controller.rb app/controllers/heartbeat_controller.rb ]) end it 'provides intuitive results for "d" and "doc/command-t.txt"' do expect(ordered_matches(%w[ TODO doc/command-t.txt ], 'd')).to eq(%w[ doc/command-t.txt TODO ]) end it 'provides intuitive results for "do" and "doc/command-t.txt"' do expect(ordered_matches(%w[ TODO doc/command-t.txt ], 'do')).to eq(%w[ doc/command-t.txt TODO ]) end it 'provides intuitive results for "matchh" search' do # Regression introduced in 187bc18. expect(ordered_matches(%w[ vendor/bundle/ruby/1.8/gems/rspec-expectations-2.14.5/spec/rspec/matchers/has_spec.rb ruby/command-t/match.h ], 'matchh')).to eq(%w[ ruby/command-t/match.h vendor/bundle/ruby/1.8/gems/rspec-expectations-2.14.5/spec/rspec/matchers/has_spec.rb ]) end it 'provides intuitive results for "relqpath" search' do # Another regression. expect(ordered_matches(%w[ *l**/e*t*t*/atla*/patter**/E*tAtla***el****q*e*e***al**at***HelperTra*t.php static_upstream/relay/query/RelayQueryPath.js ], 'relqpath')).to eq(%w[ static_upstream/relay/query/RelayQueryPath.js *l**/e*t*t*/atla*/patter**/E*tAtla***el****q*e*e***al**at***HelperTra*t.php ]) end it 'provides intuitive results for "controller" search' do # Another regression. expect(ordered_matches(%w[ spec/command-t/controller_spec.rb ruby/command-t/controller.rb ], 'controller')).to eq(%w[ ruby/command-t/controller.rb spec/command-t/controller_spec.rb ]) 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]) expect(matcher.sorted_matches_for('usercc')).to eq([]) # simpler test case matcher = matcher(*%w[foobar]) expect(matcher.sorted_matches_for('fooooo')).to eq([]) # minimal repro matcher = matcher(*%w[ab]) expect(matcher.sorted_matches_for('aa')).to eq([]) end it 'ignores dotfiles by default' do matcher = matcher(*%w[.foo .bar]) expect(matcher.sorted_matches_for('foo')).to eq([]) end it 'shows dotfiles if the query starts with a dot' do matcher = matcher(*%w[.foo .bar]) expect(matcher.sorted_matches_for('.fo')).to eq(%w[.foo]) end it "doesn't show dotfiles if the query contains a non-leading dot" do matcher = matcher(*%w[.foo.txt .bar.txt]) expect(matcher.sorted_matches_for('f.t')).to eq([]) # counter-example expect(matcher.sorted_matches_for('.f.t')).to eq(%w[.foo.txt]) end it "shows dotfiles when there is a non-leading dot that matches a leading dot within a path component" do matcher = matcher(*%w[this/.secret/stuff.txt something.else]) expect(matcher.sorted_matches_for('t.sst')).to eq(%w[this/.secret/stuff.txt]) end it "doesn't show a dotfile just because there was a match at index 0" do pending 'fix' matcher = matcher(*%w[src/.flowconfig]) expect(matcher.sorted_matches_for('s')).to eq([]) end it 'correctly computes non-recursive match score' do # Non-recursive match was incorrectly inflating some scores. # Related: https://github.com/wincent/command-t/issues/209 matcher = matcher(*%w[ app/assets/components/App/index.jsx app/assets/components/PrivacyPage/index.jsx app/views/api/docs/pagination/_index.md ]) # You might want the second match here to come first, but in the # non-recursive case we greedily match the "app" in "app", the "a" in # "assets", the "p" in "components", and the first "p" in "App". This # doesn't score as favorably as matching the "app" in "app", the "ap" in # "api", and the "p" in "pagination". expect(matcher.sorted_matches_for('appappind')).to eq(%w[ app/views/api/docs/pagination/_index.md app/assets/components/App/index.jsx app/assets/components/PrivacyPage/index.jsx ]) end end end command-t-4.0/spec/command-t/scanner/000077500000000000000000000000001271652626300175105ustar00rootroot00000000000000command-t-4.0/spec/command-t/scanner/buffer_scanner_spec.rb000066400000000000000000000014061271652626300240320ustar00rootroot00000000000000# Copyright 2010-present 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 expect(@scanner.paths).to match_array(@paths) end end end command-t-4.0/spec/command-t/scanner/file_scanner/000077500000000000000000000000001271652626300221405ustar00rootroot00000000000000command-t-4.0/spec/command-t/scanner/file_scanner/ruby_file_scanner_spec.rb000066400000000000000000000043251271652626300271740ustar00rootroot00000000000000# Copyright 2010-present 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/) { '' } stub(::VIM).command(/echon/) stub(::VIM).command('redraw') end describe 'paths method' do it 'returns a list of regular files' do expect(@scanner.paths).to match_array(@all_fixtures) end end describe 'path= method' do it 'allows repeated applications of scanner at different paths' do expect(@scanner.paths).to match_array(@all_fixtures) # drill down 1 level @scanner.path = File.join(@dir, 'foo') expect(@scanner.paths).to match_array(%w(alpha/t1 alpha/t2 beta)) # and another @scanner.path = File.join(@dir, 'foo', 'alpha') expect(@scanner.paths).to match_array(%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 expect(@scanner.paths).to match_array(%w(bar/abc bar/xyz baz bing foo/beta)) end end end command-t-4.0/spec/command-t/scanner/file_scanner/watchman_file_scanner_spec.rb000066400000000000000000000011711271652626300300110ustar00rootroot00000000000000# Copyright 2015-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. require 'spec_helper' describe CommandT::Scanner::FileScanner::WatchmanFileScanner do context 'when an error occurs' do it 'falls back to the FindFileScanner' do # fake an error scanner = described_class.new stub(scanner).get_raw_sockname do raise described_class::WatchmanError end # expect call on superclass any_instance_of(CommandT::Scanner::FileScanner::FindFileScanner) do |klass| mock(klass).paths! end scanner.paths! end end end command-t-4.0/spec/command-t/scanner/file_scanner_spec.rb000066400000000000000000000010351271652626300234760ustar00rootroot00000000000000# Copyright 2010-present 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 command-t-4.0/spec/command-t/vim_spec.rb000066400000000000000000000006521271652626300202140ustar00rootroot00000000000000# Copyright 2014-present 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 command-t-4.0/spec/command-t/watchman/000077500000000000000000000000001271652626300176615ustar00rootroot00000000000000command-t-4.0/spec/command-t/watchman/utils_spec.rb000066400000000000000000000313611271652626300223640ustar00rootroot00000000000000# Copyright 2014-present Greg Hurrell. All rights reserved. # Licensed under the terms of the BSD 2-clause license. require 'spec_helper' 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 command-t-4.0/spec/spec_helper.rb000066400000000000000000000011261271652626300170160ustar00rootroot00000000000000# Copyright 2010-present 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' require 'command-t/ext' 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 Buffer = Class.new end command-t-4.0/vendor/000077500000000000000000000000001271652626300145435ustar00rootroot00000000000000command-t-4.0/vendor/vimscriptuploader/000077500000000000000000000000001271652626300203175ustar00rootroot00000000000000command-t-4.0/vendor/vroom/000077500000000000000000000000001271652626300157055ustar00rootroot00000000000000