From 2172e42c3910aa70ceaaaab0f01ab9e212e40c3a Mon Sep 17 00:00:00 2001 From: Ton van den Heuvel Date: Thu, 11 Oct 2012 14:17:48 +0200 Subject: [PATCH 01/29] Add support for MRU buffer ordering Originally submitted at: https://github.com/wincent/Command-T/pull/50 Modifications from the original pull request: - rebased (controller has changed quite a bit in the meantime) - removed duplicate definition of `#list_matches` method - replaced some `and`/`not` with `&&` and `!` - broke a long ternary across multiple lines Signed-off-by: Wincent Colaiuta --- ruby/command-t/controller.rb | 19 ++++++- ruby/command-t/finder/mru_buffer_finder.rb | 18 +++++++ ruby/command-t/scanner/mru_buffer_scanner.rb | 52 ++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 ruby/command-t/finder/mru_buffer_finder.rb create mode 100644 ruby/command-t/scanner/mru_buffer_scanner.rb diff --git a/ruby/command-t/controller.rb b/ruby/command-t/controller.rb index 3193fbec..13a9354c 100644 --- a/ruby/command-t/controller.rb +++ b/ruby/command-t/controller.rb @@ -24,6 +24,7 @@ require 'command-t/finder/buffer_finder' require 'command-t/finder/jump_finder' require 'command-t/finder/file_finder' +require 'command-t/finder/mru_buffer_finder' require 'command-t/finder/tag_finder' require 'command-t/match_window' require 'command-t/prompt' @@ -192,6 +193,16 @@ def list_matches(options = {}) ) @match_window.matches = @matches + # Select next entry in list in case MRU buffer ordering is enabled and + # buffers are currently being displayed. + if @active_finder == buffer_finder && get_bool('g:CommandTUseMruBufferOrder') + if get_bool('g:CommandTMatchWindowReverse') + @match_window.select_prev + else + @match_window.select_next + end + end + @needs_update = false end @@ -369,7 +380,13 @@ def match_limit end def buffer_finder - @buffer_finder ||= CommandT::BufferFinder.new + @buffer_finder ||= begin + if get_bool('g:CommandTUseMruBufferOrder') + CommandT::MruBufferFinder.new + else + CommandT::BufferFinder.new + end + end end def file_finder diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb new file mode 100644 index 00000000..500b14aa --- /dev/null +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -0,0 +1,18 @@ +require 'command-t/ext' # CommandT::Matcher +require 'command-t/scanner/mru_buffer_scanner' +require 'command-t/finder' + +module CommandT + class MruBufferFinder < Finder + # Override sorted_matches_for to prevent MRU ordered matches from being + # ordered alphabetically. + def sorted_matches_for str, options = {} + (@matcher.matches_for str).first(options[:limit]).map { |match| match.to_s } + end + + def initialize + @scanner = MruBufferScanner.new + @matcher = Matcher.new @scanner, :always_show_dot_files => true + end + end # class MruBufferFinder +end # CommandT diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb new file mode 100644 index 00000000..24ea4232 --- /dev/null +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -0,0 +1,52 @@ +require 'command-t/vim/path_utilities' +require 'command-t/scanner' + +module CommandT + # Returns a list of all open buffers. + class MruBufferScanner < Scanner + include VIM::PathUtilities + + def initialize + @mru_buffer_stack = [] + + $scanner = self + ::VIM::command 'augroup CommandTMruBufferScanner' + ::VIM::command 'autocmd!' + ::VIM::command 'autocmd BufEnter * ruby $scanner.mark_buffer_used' + ::VIM::command 'autocmd BufDelete * ruby $scanner.delete_buffer' + ::VIM::command 'augroup End' + end + + def delete_buffer + # Note that $curbuf does not point to the buffer that is being deleted, + # we need to use Vim's abuf for the correct buffer number. + @mru_buffer_stack.delete_if { |b| b.number == ::VIM::evaluate('expand("")').to_i } + end + + def mark_buffer_used + if $curbuf.name + # Mark the current buffer as the most recently used buffer by placing + # it in front of all other buffers listed in the list of most + # recently used buffers. + @mru_buffer_stack.delete $curbuf + @mru_buffer_stack.unshift $curbuf + end + end + + 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_buffer_stack.include?(buffer) + end.compact + + # Combine all most recently used buffers and all unused buffers, and + # return all listed buffer paths. + (@mru_buffer_stack + unused_buffers).map do |buffer| + if ::VIM::evaluate('buflisted(%d)' % buffer.number) == 1 + relative_path_under_working_directory buffer.name + end + end.compact + end + end # class MruBufferScanner +end # module CommandT From 63af0dcfdc6a2334e1a090db506f070c12548dcc Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 17:47:52 -0700 Subject: [PATCH 02/29] Add missing copyright headers Signed-off-by: Wincent Colaiuta --- ruby/command-t/finder/mru_buffer_finder.rb | 23 ++++++++++++++++++++ ruby/command-t/scanner/mru_buffer_scanner.rb | 23 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb index 500b14aa..bb96ae77 100644 --- a/ruby/command-t/finder/mru_buffer_finder.rb +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -1,3 +1,26 @@ +# Copyright 2014 Wincent Colaiuta. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + require 'command-t/ext' # CommandT::Matcher require 'command-t/scanner/mru_buffer_scanner' require 'command-t/finder' diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb index 24ea4232..c2ec8edf 100644 --- a/ruby/command-t/scanner/mru_buffer_scanner.rb +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -1,3 +1,26 @@ +# Copyright 2014 Wincent Colaiuta. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + require 'command-t/vim/path_utilities' require 'command-t/scanner' From 80f6cf33dcf4d0d0d0d60ada99e03c59db846feb Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 17:50:05 -0700 Subject: [PATCH 03/29] doc: Add Ton van den Heuvel to AUTHORS section Signed-off-by: Wincent Colaiuta --- doc/command-t.txt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/doc/command-t.txt b/doc/command-t.txt index aa39561b..2da21ffe 100644 --- a/doc/command-t.txt +++ b/doc/command-t.txt @@ -867,13 +867,13 @@ order): Anthony Panozzo Nadav Samet Steven Moazami Daniel Hahler Nate Kane Sung Pae Felix Tjandrawibawa Nicholas Alpi Thomas Pelletier - Gary Bernhardt Noon Silk Victor Hugo Borja - Ivan Ukhov Paul Jolly Vít Ondruch - Jeff Kreeftmeijer Pavel Sergeev Woody Peterson - Lucas de Vries Rainux Luo Yan Pritzker - Marcus Brito Roland Puntaier Yiding Jia - Marian Schubert Scott Bronson Zak Johnson - Matthew Todd Seth Fowler + Gary Bernhardt Noon Silk Ton van den Heuvel + Ivan Ukhov Paul Jolly Victor Hugo Borja + Jeff Kreeftmeijer Pavel Sergeev Vít Ondruch + Lucas de Vries Rainux Luo Woody Peterson + Marcus Brito Roland Puntaier Yan Pritzker + Marian Schubert Scott Bronson Yiding Jia + Matthew Todd Seth Fowler Zak Johnson 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 @@ -976,6 +976,10 @@ POSSIBILITY OF SUCH DAMAGE. HISTORY *command-t-history* +1.9 (not yet released) + +- added MRU (most-recently-used) buffer finder (patch from Ton van den Heuvel) + 1.8 (not yet released) - taught Watchman file scanner to use the binary protocol instead of JSON, From 8f7c7ad138bcdab90bfab4d8c42bdf8faf3edf02 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 18:32:34 -0700 Subject: [PATCH 04/29] Get rid of $scanner global variable Move the concern of tracking MRU buffers out of the MruBufferScanner and into a dedicated module, thus eliminating the $scanner global variable. It's true that the new (singleton) MRU module is a global in disguise, but at least it is a namespaced global which means that we're not polluting the Ruby environment that other plug-ins might be operating in with additional variables. As a bonus, this allows us to keep some noise out of the scanner, and encapsulate it in a well-contained module with a single purpose. Also, this means that we track seen buffers from the very beginning, as soon as the plug-in is loaded. Previously, tracking would only start as a side effect of a call to the `#list_matches` method in the controller (because it calls `#buffer_finder`, which instantiates the MruBufferFinder and memoizes it). Signed-off-by: Wincent Colaiuta --- plugin/command-t.vim | 7 ++- ruby/command-t/mru.rb | 57 ++++++++++++++++++++ ruby/command-t/scanner/mru_buffer_scanner.rb | 33 ++---------- 3 files changed, 66 insertions(+), 31 deletions(-) create mode 100644 ruby/command-t/mru.rb diff --git a/plugin/command-t.vim b/plugin/command-t.vim index 6a4a9113..66d809ff 100644 --- a/plugin/command-t.vim +++ b/plugin/command-t.vim @@ -168,12 +168,17 @@ function CommandTCursorStart() ruby $command_t.cursor_start endfunction +augroup CommandTMRUBuffer + autocmd BufEnter * ruby CommandT::MRU.touch + autocmd BufDelete * ruby CommandT::MRU.delete +augroup END + ruby << EOF # require Ruby files begin - # prepare controller require 'command-t/vim' require 'command-t/controller' + require 'command-t/mru' $command_t = CommandT::Controller.new rescue LoadError load_path_modified = false diff --git a/ruby/command-t/mru.rb b/ruby/command-t/mru.rb new file mode 100644 index 00000000..bb7ef38b --- /dev/null +++ b/ruby/command-t/mru.rb @@ -0,0 +1,57 @@ +# Copyright 2014 Wincent Colaiuta. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +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 + + # Mark the current buffer as having been used, effectively moving it to + # the bottom of the stack. + def touch + if $curbuf.name + stack.delete $curbuf + stack.unshift $curbuf + end + 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 # module MRU +end # module CommandT diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb index c2ec8edf..506209a2 100644 --- a/ruby/command-t/scanner/mru_buffer_scanner.rb +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -25,47 +25,20 @@ require 'command-t/scanner' module CommandT - # Returns a list of all open buffers. + # Returns a list of all open buffers, sorted in MRU order. class MruBufferScanner < Scanner include VIM::PathUtilities - def initialize - @mru_buffer_stack = [] - - $scanner = self - ::VIM::command 'augroup CommandTMruBufferScanner' - ::VIM::command 'autocmd!' - ::VIM::command 'autocmd BufEnter * ruby $scanner.mark_buffer_used' - ::VIM::command 'autocmd BufDelete * ruby $scanner.delete_buffer' - ::VIM::command 'augroup End' - end - - def delete_buffer - # Note that $curbuf does not point to the buffer that is being deleted, - # we need to use Vim's abuf for the correct buffer number. - @mru_buffer_stack.delete_if { |b| b.number == ::VIM::evaluate('expand("")').to_i } - end - - def mark_buffer_used - if $curbuf.name - # Mark the current buffer as the most recently used buffer by placing - # it in front of all other buffers listed in the list of most - # recently used buffers. - @mru_buffer_stack.delete $curbuf - @mru_buffer_stack.unshift $curbuf - end - end - 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_buffer_stack.include?(buffer) + buffer if buffer.name && !MRU.used?(buffer) end.compact # Combine all most recently used buffers and all unused buffers, and # return all listed buffer paths. - (@mru_buffer_stack + unused_buffers).map do |buffer| + (MRU.stack + unused_buffers).map do |buffer| if ::VIM::evaluate('buflisted(%d)' % buffer.number) == 1 relative_path_under_working_directory buffer.name end From 6a27e6764c9d7d4221136e52fe297d499bc1f833 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 18:39:55 -0700 Subject: [PATCH 05/29] Fix some cosmetic typos Noticed while refactoring the MRU buffer code. Signed-off-by: Wincent Colaiuta --- ruby/command-t/controller.rb | 2 +- ruby/command-t/util.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/command-t/controller.rb b/ruby/command-t/controller.rb index 13a9354c..f5839df7 100644 --- a/ruby/command-t/controller.rb +++ b/ruby/command-t/controller.rb @@ -410,4 +410,4 @@ def tag_finder :include_filenames => get_bool('g:CommandTTagIncludeFilenames') end end # class Controller -end # module commandT +end # module CommandT diff --git a/ruby/command-t/util.rb b/ruby/command-t/util.rb index 87362dfd..e9532f24 100644 --- a/ruby/command-t/util.rb +++ b/ruby/command-t/util.rb @@ -111,4 +111,4 @@ def processor_count! end end end # module Util -end # module commandT +end # module CommandT From 769fdd09115a12c28cc6ac95057929b49409a570 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 18:40:11 -0700 Subject: [PATCH 06/29] Add missing method to CommandT::Stub Noticed while refactoring the MRU buffer code. Signed-off-by: Wincent Colaiuta --- ruby/command-t/stub.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ruby/command-t/stub.rb b/ruby/command-t/stub.rb index 4628b61f..77229cdd 100644 --- a/ruby/command-t/stub.rb +++ b/ruby/command-t/stub.rb @@ -1,4 +1,4 @@ -# Copyright 2010-2011 Wincent Colaiuta. All rights reserved. +# Copyright 2010-2014 Wincent Colaiuta. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -27,8 +27,14 @@ class Stub 'Please see INSTALLATION and TROUBLE-SHOOTING in the help', 'For more information type: :help command-t'] - [:flush, :show_buffer_finder, :show_file_finder, :show_tag_finder].each do |method| - define_method(method.to_sym) { warn *@@load_error } + [ + :flush, + :show_buffer_finder, + :show_file_finder, + :show_jump_finder, + :show_tag_finder + ].each do |method| + define_method(method) { warn *@@load_error } end private From 61b5122b2a0dd8d31a7139316ee7ec1377de3e80 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 18:41:00 -0700 Subject: [PATCH 07/29] Improve diagnostic info in CommandT::Stub error message Was looking at this file while refactoring the MRU buffer code; took the opportunity to leave it better. Hopefully this is the first of several improvements that will make it easier to troubleshoot installation woes. Signed-off-by: Wincent Colaiuta --- ruby/command-t/stub.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ruby/command-t/stub.rb b/ruby/command-t/stub.rb index 77229cdd..f73453e4 100644 --- a/ruby/command-t/stub.rb +++ b/ruby/command-t/stub.rb @@ -25,6 +25,7 @@ module CommandT class Stub @@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#{RUBY_PATCHLEVEL}", 'For more information type: :help command-t'] [ From 8537a0c3a87a436c6040ddfb4c2bcfe79c38ac76 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 18:46:00 -0700 Subject: [PATCH 08/29] Make MruBufferFinder inherit from BufferFinder Even though they don't share any code, they are conceptually related (there is a stronger "is a" relationship between the MruBufferFinder and the BufferFinder than there is between the MruBufferFinder and the abstract Finder superclass). Signed-off-by: Wincent Colaiuta --- ruby/command-t/finder/mru_buffer_finder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb index bb96ae77..aa023e32 100644 --- a/ruby/command-t/finder/mru_buffer_finder.rb +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -23,10 +23,10 @@ require 'command-t/ext' # CommandT::Matcher require 'command-t/scanner/mru_buffer_scanner' -require 'command-t/finder' +require 'command-t/finder/buffer_finder' module CommandT - class MruBufferFinder < Finder + class MruBufferFinder < BufferFinder # Override sorted_matches_for to prevent MRU ordered matches from being # ordered alphabetically. def sorted_matches_for str, options = {} From e31b5a5f5f4758aa63c5bb425df193c446fcdaf9 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 19:06:48 -0700 Subject: [PATCH 09/29] Make the MRU buffer stack an actual stack It was really being used as a queue; if we're going to call it a stack, let's make it one for consistency. The added `#reverse` may look like we're doing more work here, but getting rid of the repeated `#unshift` operations probably make this a net win. Signed-off-by: Wincent Colaiuta --- ruby/command-t/finder/mru_buffer_finder.rb | 4 +++- ruby/command-t/mru.rb | 4 ++-- ruby/command-t/scanner/mru_buffer_scanner.rb | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb index aa023e32..b93397f0 100644 --- a/ruby/command-t/finder/mru_buffer_finder.rb +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -30,7 +30,9 @@ class MruBufferFinder < BufferFinder # Override sorted_matches_for to prevent MRU ordered matches from being # ordered alphabetically. def sorted_matches_for str, options = {} - (@matcher.matches_for str).first(options[:limit]).map { |match| match.to_s } + (@matcher.matches_for str).last(options[:limit]).map do |match| + match.to_s + end end def initialize diff --git a/ruby/command-t/mru.rb b/ruby/command-t/mru.rb index bb7ef38b..7c1eaee9 100644 --- a/ruby/command-t/mru.rb +++ b/ruby/command-t/mru.rb @@ -31,11 +31,11 @@ def stack end # Mark the current buffer as having been used, effectively moving it to - # the bottom of the stack. + # the top of the stack. def touch if $curbuf.name stack.delete $curbuf - stack.unshift $curbuf + stack.push $curbuf end end diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb index 506209a2..dfd1ecd5 100644 --- a/ruby/command-t/scanner/mru_buffer_scanner.rb +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -38,11 +38,11 @@ def paths # Combine all most recently used buffers and all unused buffers, and # return all listed buffer paths. - (MRU.stack + unused_buffers).map do |buffer| + (unused_buffers + MRU.stack).map do |buffer| if ::VIM::evaluate('buflisted(%d)' % buffer.number) == 1 relative_path_under_working_directory buffer.name end - end.compact + end.compact.reverse end end # class MruBufferScanner end # module CommandT From e5bf44ca984a9178e53af1ddb0b25c684b4750f8 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 19:08:14 -0700 Subject: [PATCH 10/29] Make MruBufferScanner inherit from BufferScanner Much like we did for the MruBufferFinder, make the MruBufferScanner inherit from the BufferScanner, and for the same reasons. Signed-off-by: Wincent Colaiuta --- ruby/command-t/scanner/mru_buffer_scanner.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb index dfd1ecd5..5912adbc 100644 --- a/ruby/command-t/scanner/mru_buffer_scanner.rb +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -22,11 +22,11 @@ # POSSIBILITY OF SUCH DAMAGE. require 'command-t/vim/path_utilities' -require 'command-t/scanner' +require 'command-t/scanner/buffer_scanner' module CommandT # Returns a list of all open buffers, sorted in MRU order. - class MruBufferScanner < Scanner + class MruBufferScanner < BufferScanner include VIM::PathUtilities def paths From 480d51233e44500fa125bb06546cd3aeb1cf9993 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 21:48:42 -0700 Subject: [PATCH 11/29] Rename Mru -> MRU For consistency, let's use "MRU" consistently over "Mru", because it is more idiomatic. Signed-off-by: Wincent Colaiuta --- ruby/command-t/controller.rb | 6 +++--- ruby/command-t/finder/mru_buffer_finder.rb | 6 +++--- ruby/command-t/scanner/mru_buffer_scanner.rb | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ruby/command-t/controller.rb b/ruby/command-t/controller.rb index f5839df7..97ef2cdb 100644 --- a/ruby/command-t/controller.rb +++ b/ruby/command-t/controller.rb @@ -195,7 +195,7 @@ def list_matches(options = {}) # Select next entry in list in case MRU buffer ordering is enabled and # buffers are currently being displayed. - if @active_finder == buffer_finder && get_bool('g:CommandTUseMruBufferOrder') + if @active_finder == buffer_finder && get_bool('g:CommandTUseMRUBufferOrder') if get_bool('g:CommandTMatchWindowReverse') @match_window.select_prev else @@ -381,8 +381,8 @@ def match_limit def buffer_finder @buffer_finder ||= begin - if get_bool('g:CommandTUseMruBufferOrder') - CommandT::MruBufferFinder.new + if get_bool('g:CommandTUseMRUBufferOrder') + CommandT::MRUBufferFinder.new else CommandT::BufferFinder.new end diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb index b93397f0..0fd3bf23 100644 --- a/ruby/command-t/finder/mru_buffer_finder.rb +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -26,7 +26,7 @@ require 'command-t/finder/buffer_finder' module CommandT - class MruBufferFinder < BufferFinder + class MRUBufferFinder < BufferFinder # Override sorted_matches_for to prevent MRU ordered matches from being # ordered alphabetically. def sorted_matches_for str, options = {} @@ -36,8 +36,8 @@ def sorted_matches_for str, options = {} end def initialize - @scanner = MruBufferScanner.new + @scanner = MRUBufferScanner.new @matcher = Matcher.new @scanner, :always_show_dot_files => true end - end # class MruBufferFinder + end # class MRUBufferFinder end # CommandT diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb index 5912adbc..540a045c 100644 --- a/ruby/command-t/scanner/mru_buffer_scanner.rb +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -26,7 +26,7 @@ module CommandT # Returns a list of all open buffers, sorted in MRU order. - class MruBufferScanner < BufferScanner + class MRUBufferScanner < BufferScanner include VIM::PathUtilities def paths @@ -44,5 +44,5 @@ def paths end end.compact.reverse end - end # class MruBufferScanner + end # class MRUBufferScanner end # module CommandT From 57aaa37c8567b0c2ab360e1a7fdeb8357a42e463 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 21:55:56 -0700 Subject: [PATCH 12/29] Drop an unnecessary `#compact` call We `#compact` further down in the method, so this first call isn't necessary. Signed-off-by: Wincent Colaiuta --- ruby/command-t/scanner/mru_buffer_scanner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb index 540a045c..ae3ee81f 100644 --- a/ruby/command-t/scanner/mru_buffer_scanner.rb +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -34,7 +34,7 @@ def paths unused_buffers = (0..(::VIM::Buffer.count - 1)).map do |n| buffer = ::VIM::Buffer[n] buffer if buffer.name && !MRU.used?(buffer) - end.compact + end # Combine all most recently used buffers and all unused buffers, and # return all listed buffer paths. From bd7adba8d49a095ab4d9c645e3d89cf5d0a175ad Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 22:10:18 -0700 Subject: [PATCH 13/29] Split MRU functionality into separate command Instead of having an option that changes the behavior of the existing buffer finder, make a separate command so you can do either `:CommandTBuffer` or `:CommandTMRU`. If a user prefers the MRU approach, they can bind the existing buffer finder to something else: nnoremap f :CommentTBuffer nnoremap b :CommentTMRU (or similar...) Signed-off-by: Wincent Colaiuta --- plugin/command-t.vim | 9 +++++++ ruby/command-t/controller.rb | 28 +++++++++------------- ruby/command-t/finder/mru_buffer_finder.rb | 6 ++++- ruby/command-t/stub.rb | 1 + 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/plugin/command-t.vim b/plugin/command-t.vim index 66d809ff..6b6fac51 100644 --- a/plugin/command-t.vim +++ b/plugin/command-t.vim @@ -29,6 +29,7 @@ let g:command_t_loaded = 1 command CommandTBuffer call CommandTShowBufferFinder() command CommandTJump call CommandTShowJumpFinder() +command CommandTMRU call CommandTShowMRUFinder() command CommandTTag call CommandTShowTagFinder() command -nargs=? -complete=dir CommandT call CommandTShowFileFinder() command CommandTFlush call CommandTFlush() @@ -72,6 +73,14 @@ function s:CommandTShowJumpFinder() endif endfunction +function s:CommandTShowMRUFinder() + if has('ruby') + ruby $command_t.show_mru_finder + else + call s:CommandTRubyWarning() + endif +endfunction + function s:CommandTShowTagFinder() if has('ruby') ruby $command_t.show_tag_finder diff --git a/ruby/command-t/controller.rb b/ruby/command-t/controller.rb index 97ef2cdb..4a28b24c 100644 --- a/ruby/command-t/controller.rb +++ b/ruby/command-t/controller.rb @@ -51,6 +51,12 @@ def show_jump_finder show end + def show_mru_finder + @path = VIM::pwd + @active_finder = mru_finder + show + end + def show_tag_finder @path = VIM::pwd @active_finder = tag_finder @@ -193,16 +199,6 @@ def list_matches(options = {}) ) @match_window.matches = @matches - # Select next entry in list in case MRU buffer ordering is enabled and - # buffers are currently being displayed. - if @active_finder == buffer_finder && get_bool('g:CommandTUseMRUBufferOrder') - if get_bool('g:CommandTMatchWindowReverse') - @match_window.select_prev - else - @match_window.select_next - end - end - @needs_update = false end @@ -380,13 +376,11 @@ def match_limit end def buffer_finder - @buffer_finder ||= begin - if get_bool('g:CommandTUseMRUBufferOrder') - CommandT::MRUBufferFinder.new - else - CommandT::BufferFinder.new - end - end + @buffer_finder ||= CommandT::BufferFinder.new + end + + def mru_finder + @mru_finder ||= CommandT::MRUBufferFinder.new end def file_finder diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb index 0fd3bf23..3d91a0a3 100644 --- a/ruby/command-t/finder/mru_buffer_finder.rb +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -30,9 +30,13 @@ class MRUBufferFinder < BufferFinder # Override sorted_matches_for to prevent MRU ordered matches from being # ordered alphabetically. def sorted_matches_for str, options = {} - (@matcher.matches_for str).last(options[:limit]).map do |match| + matches = (@matcher.matches_for str).last(options[:limit]).map do |match| match.to_s end + + # take current buffer (by definition, the most recently used) and move it + # to the end of the results + (matches[1..-1] || []) + [matches.first].compact end def initialize diff --git a/ruby/command-t/stub.rb b/ruby/command-t/stub.rb index f73453e4..bf0bd47b 100644 --- a/ruby/command-t/stub.rb +++ b/ruby/command-t/stub.rb @@ -33,6 +33,7 @@ class Stub :show_buffer_finder, :show_file_finder, :show_jump_finder, + :show_mru_finder, :show_tag_finder ].each do |method| define_method(method) { warn *@@load_error } From 3c265db48cf32ea8b439b85176114edcf92b6083 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 22:23:59 -0700 Subject: [PATCH 14/29] Work around missing matches_for method Back when the MRU pull request was submitted there used to be a matches_for method. It went away during some performance optimizations around the time of the move towards phtreads. Make it so that `sorted_matches_for` can optionally take an override for how things should be sorted. Signed-off-by: Wincent Colaiuta --- ruby/command-t/finder/mru_buffer_finder.rb | 4 +--- ruby/command-t/matcher.c | 18 +++++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb index 3d91a0a3..76b1a472 100644 --- a/ruby/command-t/finder/mru_buffer_finder.rb +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -30,9 +30,7 @@ class MRUBufferFinder < BufferFinder # Override sorted_matches_for to prevent MRU ordered matches from being # ordered alphabetically. def sorted_matches_for str, options = {} - matches = (@matcher.matches_for str).last(options[:limit]).map do |match| - match.to_s - end + 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 diff --git a/ruby/command-t/matcher.c b/ruby/command-t/matcher.c index 9417f16a..104ab0a6 100644 --- a/ruby/command-t/matcher.c +++ b/ruby/command-t/matcher.c @@ -144,6 +144,7 @@ VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self) VALUE paths; VALUE results; VALUE scanner; + VALUE sort_option; VALUE threads_option; // process arguments: 1 mandatory, 1 optional @@ -158,6 +159,7 @@ VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self) // check optional options has for overrides limit_option = CommandT_option_from_hash("limit", options); threads_option = CommandT_option_from_hash("threads", options); + sort_option = CommandT_option_from_hash("sort", options); // get unsorted matches scanner = rb_iv_get(self, "@scanner"); @@ -217,13 +219,15 @@ VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self) free(threads); #endif - if (RSTRING_LEN(abbrev) == 0 || - (RSTRING_LEN(abbrev) == 1 && RSTRING_PTR(abbrev)[0] == '.')) - // alphabetic order if search string is only "" or "." - qsort(matches, path_count, sizeof(match_t), cmp_alpha); - else - // for all other non-empty search strings, sort by score - qsort(matches, path_count, sizeof(match_t), cmp_score); + if (NIL_P(sort_option) || sort_option == Qtrue) { + if (RSTRING_LEN(abbrev) == 0 || + (RSTRING_LEN(abbrev) == 1 && RSTRING_PTR(abbrev)[0] == '.')) + // alphabetic order if search string is only "" or "." + qsort(matches, path_count, sizeof(match_t), cmp_alpha); + else + // for all other non-empty search strings, sort by score + qsort(matches, path_count, sizeof(match_t), cmp_score); + } results = rb_ary_new(); From 0b84b36d929a0c873bd28df42768b2fd711b2eb0 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 22:28:50 -0700 Subject: [PATCH 15/29] Teach MRU module to ignore unlisted buffers The MRU module itself feels like a better place to put this concern. Signed-off-by: Wincent Colaiuta --- ruby/command-t/mru.rb | 9 +++++---- ruby/command-t/scanner/mru_buffer_scanner.rb | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ruby/command-t/mru.rb b/ruby/command-t/mru.rb index 7c1eaee9..fad80a27 100644 --- a/ruby/command-t/mru.rb +++ b/ruby/command-t/mru.rb @@ -33,10 +33,11 @@ def stack # Mark the current buffer as having been used, effectively moving it to # the top of the stack. def touch - if $curbuf.name - stack.delete $curbuf - stack.push $curbuf - end + 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. diff --git a/ruby/command-t/scanner/mru_buffer_scanner.rb b/ruby/command-t/scanner/mru_buffer_scanner.rb index ae3ee81f..6da28556 100644 --- a/ruby/command-t/scanner/mru_buffer_scanner.rb +++ b/ruby/command-t/scanner/mru_buffer_scanner.rb @@ -39,7 +39,7 @@ def paths # Combine all most recently used buffers and all unused buffers, and # return all listed buffer paths. (unused_buffers + MRU.stack).map do |buffer| - if ::VIM::evaluate('buflisted(%d)' % buffer.number) == 1 + if buffer && buffer.name relative_path_under_working_directory buffer.name end end.compact.reverse From ff4bbdf6bb712425eae82f7430ab8d73251743be Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 30 Mar 2014 22:43:56 -0700 Subject: [PATCH 16/29] Update docs with info about :CommandTMRU Signed-off-by: Wincent Colaiuta --- doc/command-t.txt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/doc/command-t.txt b/doc/command-t.txt index 2da21ffe..641bf91c 100644 --- a/doc/command-t.txt +++ b/doc/command-t.txt @@ -369,18 +369,26 @@ paths which begin with the same prefix. COMMANDS *command-t-commands* *:CommandT* -|:CommandT| Brings up the Command-T file window, starting in the +|: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. +|: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. + *: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 + *:CommandTJumps* -|:CommandTJump| Brings up the Command-T jumplist window. +|: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 @@ -388,13 +396,13 @@ COMMANDS *command-t-commands* documentation for more info). *:CommandTTag* -|:CommandTTag| Brings up the Command-T window tags window, which can +|: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 +|: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 From 40a0b8177a22986e2e5db8bc7c32d28042870cac Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Mon, 31 Mar 2014 07:48:46 -0700 Subject: [PATCH 17/29] Only move MRU results if appropriate We had a bug where we would unconditionally move the first search result to the end, thinking that it was the current buffer. This is not necessarily always for the case. For example, if I've visited: foo1 foo2 bar baz in that order, then it's correct to move baz to the end of the list. But if I filter my query by typing "f", only "foo1" and "foo2" will be shown, and it would be incorrect to attempt moving either of them. I think this bug was likely present in the original pull request too -- although it was moving the selection rather than moving items within the list -- but I didn't notice it. Signed-off-by: Wincent Colaiuta --- ruby/command-t/finder/mru_buffer_finder.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ruby/command-t/finder/mru_buffer_finder.rb b/ruby/command-t/finder/mru_buffer_finder.rb index 76b1a472..98f67547 100644 --- a/ruby/command-t/finder/mru_buffer_finder.rb +++ b/ruby/command-t/finder/mru_buffer_finder.rb @@ -34,7 +34,12 @@ def sorted_matches_for str, options = {} # take current buffer (by definition, the most recently used) and move it # to the end of the results - (matches[1..-1] || []) + [matches.first].compact + if MRU.stack.last && + relative_path_under_working_directory(MRU.stack.last.name) == matches.first + matches[1..-1] + [matches.first] + else + matches + end end def initialize From 809dfd566c847df900de49272f9f1d439db7a886 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Thu, 22 May 2014 07:49:39 -0700 Subject: [PATCH 18/29] Never overwrite other mappings Use `maparg()` to ensure we don't ever overwrite existing mappings. There's also `mapcheck()` which will find partial matches, but I think the more stricter check is appropriate here (`mapcheck()` is good for finding prefix collisions which could make mappings slow). Signed-off-by: Greg Hurrell --- plugin/command-t.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/command-t.vim b/plugin/command-t.vim index 6b6fac51..6aaaf234 100644 --- a/plugin/command-t.vim +++ b/plugin/command-t.vim @@ -34,11 +34,11 @@ command CommandTTag call CommandTShowTagFinder() command -nargs=? -complete=dir CommandT call CommandTShowFileFinder() command CommandTFlush call CommandTFlush() -if !hasmapto(':CommandT') +if !hasmapto(':CommandT') && maparg('t', 'n') == '' silent! nnoremap t :CommandT endif -if !hasmapto(':CommandTBuffer') +if !hasmapto(':CommandTBuffer') && maparg('b', 'n') == '' silent! nnoremap b :CommandTBuffer endif From 1bfb3d8b28181ddbc4571a89459b7b80aa5f13ef Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Thu, 22 May 2014 08:07:49 -0700 Subject: [PATCH 19/29] Use vim's autoload feature By autoloading, command-t's startup time is reduced from 66ms to 0ms on my system (see vim --startuptime). Originally submitted as: https://github.com/wincent/Command-T/pull/81 Signed-off-by: Greg Hurrell --- autoload/commandt.vim | 193 ++++++++++++++++++++++++++++++++++++++++++ plugin/command-t.vim | 174 ++----------------------------------- 2 files changed, 199 insertions(+), 168 deletions(-) create mode 100644 autoload/commandt.vim diff --git a/autoload/commandt.vim b/autoload/commandt.vim new file mode 100644 index 00000000..2505fe5a --- /dev/null +++ b/autoload/commandt.vim @@ -0,0 +1,193 @@ +" Copyright 2010-2014 Wincent Colaiuta. All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. + +if exists("g:command_t_autoloaded") || &cp + finish +endif +let g:command_t_autoloaded = 1 + +function s:CommandTRubyWarning() + echohl WarningMsg + echo "command-t.vim requires Vim to be compiled with Ruby support" + echo "For more information type: :help command-t" + echohl none +endfunction + +function commandt#CommandTShowBufferFinder() + if has('ruby') + ruby $command_t.show_buffer_finder + else + call s:CommandTRubyWarning() + endif +endfunction + +function commandt#CommandTShowFileFinder(arg) + if has('ruby') + ruby $command_t.show_file_finder + else + call s:CommandTRubyWarning() + endif +endfunction + +function commandt#CommandTShowJumpFinder() + if has('ruby') + ruby $command_t.show_jump_finder + else + call s:CommandTRubyWarning() + endif +endfunction + +function commandt#CommandTShowMRUFinder() + if has('ruby') + ruby $command_t.show_mru_finder + else + call s:CommandTRubyWarning() + endif +endfunction + +function commandt#CommandTShowTagFinder() + if has('ruby') + ruby $command_t.show_tag_finder + else + call s:CommandTRubyWarning() + endif +endfunction + +function commandt#CommandTFlush() + if has('ruby') + ruby $command_t.flush + else + call s:CommandTRubyWarning() + endif +endfunction + +if !has('ruby') + finish +endif + +function CommandTListMatches() + ruby $command_t.list_matches +endfunction + +function CommandTHandleKey(arg) + ruby $command_t.handle_key +endfunction + +function CommandTBackspace() + ruby $command_t.backspace +endfunction + +function CommandTDelete() + ruby $command_t.delete +endfunction + +function CommandTAcceptSelection() + ruby $command_t.accept_selection +endfunction + +function CommandTAcceptSelectionTab() + ruby $command_t.accept_selection :command => 'tabe' +endfunction + +function CommandTAcceptSelectionSplit() + ruby $command_t.accept_selection :command => 'sp' +endfunction + +function CommandTAcceptSelectionVSplit() + ruby $command_t.accept_selection :command => 'vs' +endfunction + +function CommandTQuickfix() + ruby $command_t.quickfix +endfunction + +function CommandTRefresh() + ruby $command_t.refresh +endfunction + +function CommandTToggleFocus() + ruby $command_t.toggle_focus +endfunction + +function CommandTCancel() + ruby $command_t.cancel +endfunction + +function CommandTSelectNext() + ruby $command_t.select_next +endfunction + +function CommandTSelectPrev() + ruby $command_t.select_prev +endfunction + +function CommandTClear() + ruby $command_t.clear +endfunction + +function CommandTCursorLeft() + ruby $command_t.cursor_left +endfunction + +function CommandTCursorRight() + ruby $command_t.cursor_right +endfunction + +function CommandTCursorEnd() + ruby $command_t.cursor_end +endfunction + +function CommandTCursorStart() + ruby $command_t.cursor_start +endfunction + +" note that we only start tracking buffers from first (autoloaded) use of Command-T +augroup CommandTMRUBuffer + autocmd BufEnter * ruby CommandT::MRU.touch + autocmd BufDelete * ruby CommandT::MRU.delete +augroup END + +ruby << EOF + # require Ruby files + begin + require 'command-t/vim' + require 'command-t/controller' + require 'command-t/mru' + $command_t = CommandT::Controller.new + rescue LoadError + load_path_modified = false + ::VIM::evaluate('&runtimepath').to_s.split(',').each do |path| + lib = "#{path}/ruby" + if !$LOAD_PATH.include?(lib) and File.exist?(lib) + $LOAD_PATH << lib + load_path_modified = true + end + end + retry if load_path_modified + + # could get here if C extension was not compiled, or was compiled + # for the wrong architecture or Ruby version + require 'command-t/stub' + $command_t = CommandT::Stub.new + end +EOF diff --git a/plugin/command-t.vim b/plugin/command-t.vim index 6aaaf234..af23e0ef 100644 --- a/plugin/command-t.vim +++ b/plugin/command-t.vim @@ -1,4 +1,3 @@ -" command-t.vim " Copyright 2010-2014 Wincent Colaiuta. All rights reserved. " " Redistribution and use in source and binary forms, with or without @@ -27,12 +26,12 @@ if exists("g:command_t_loaded") || &cp endif let g:command_t_loaded = 1 -command CommandTBuffer call CommandTShowBufferFinder() -command CommandTJump call CommandTShowJumpFinder() -command CommandTMRU call CommandTShowMRUFinder() -command CommandTTag call CommandTShowTagFinder() -command -nargs=? -complete=dir CommandT call CommandTShowFileFinder() -command CommandTFlush call CommandTFlush() +command CommandTBuffer call commandt#CommandTShowBufferFinder() +command CommandTJump call commandt#CommandTShowJumpFinder() +command CommandTMRU call commandt#CommandTShowMRUFinder() +command CommandTTag call commandt#CommandTShowTagFinder() +command -nargs=? -complete=dir CommandT call commandt#CommandTShowFileFinder() +command CommandTFlush call commandt#CommandTFlush() if !hasmapto(':CommandT') && maparg('t', 'n') == '' silent! nnoremap t :CommandT @@ -42,167 +41,6 @@ if !hasmapto(':CommandTBuffer') && maparg('b', 'n') == '' silent! nnoremap b :CommandTBuffer endif -function s:CommandTRubyWarning() - echohl WarningMsg - echo "command-t.vim requires Vim to be compiled with Ruby support" - echo "For more information type: :help command-t" - echohl none -endfunction - -function s:CommandTShowBufferFinder() - if has('ruby') - ruby $command_t.show_buffer_finder - else - call s:CommandTRubyWarning() - endif -endfunction - -function s:CommandTShowFileFinder(arg) - if has('ruby') - ruby $command_t.show_file_finder - else - call s:CommandTRubyWarning() - endif -endfunction - -function s:CommandTShowJumpFinder() - if has('ruby') - ruby $command_t.show_jump_finder - else - call s:CommandTRubyWarning() - endif -endfunction - -function s:CommandTShowMRUFinder() - if has('ruby') - ruby $command_t.show_mru_finder - else - call s:CommandTRubyWarning() - endif -endfunction - -function s:CommandTShowTagFinder() - if has('ruby') - ruby $command_t.show_tag_finder - else - call s:CommandTRubyWarning() - endif -endfunction - -function s:CommandTFlush() - if has('ruby') - ruby $command_t.flush - else - call s:CommandTRubyWarning() - endif -endfunction - if !has('ruby') finish endif - -function CommandTListMatches() - ruby $command_t.list_matches -endfunction - -function CommandTHandleKey(arg) - ruby $command_t.handle_key -endfunction - -function CommandTBackspace() - ruby $command_t.backspace -endfunction - -function CommandTDelete() - ruby $command_t.delete -endfunction - -function CommandTAcceptSelection() - ruby $command_t.accept_selection -endfunction - -function CommandTAcceptSelectionTab() - ruby $command_t.accept_selection :command => 'tabe' -endfunction - -function CommandTAcceptSelectionSplit() - ruby $command_t.accept_selection :command => 'sp' -endfunction - -function CommandTAcceptSelectionVSplit() - ruby $command_t.accept_selection :command => 'vs' -endfunction - -function CommandTQuickfix() - ruby $command_t.quickfix -endfunction - -function CommandTRefresh() - ruby $command_t.refresh -endfunction - -function CommandTToggleFocus() - ruby $command_t.toggle_focus -endfunction - -function CommandTCancel() - ruby $command_t.cancel -endfunction - -function CommandTSelectNext() - ruby $command_t.select_next -endfunction - -function CommandTSelectPrev() - ruby $command_t.select_prev -endfunction - -function CommandTClear() - ruby $command_t.clear -endfunction - -function CommandTCursorLeft() - ruby $command_t.cursor_left -endfunction - -function CommandTCursorRight() - ruby $command_t.cursor_right -endfunction - -function CommandTCursorEnd() - ruby $command_t.cursor_end -endfunction - -function CommandTCursorStart() - ruby $command_t.cursor_start -endfunction - -augroup CommandTMRUBuffer - autocmd BufEnter * ruby CommandT::MRU.touch - autocmd BufDelete * ruby CommandT::MRU.delete -augroup END - -ruby << EOF - # require Ruby files - begin - require 'command-t/vim' - require 'command-t/controller' - require 'command-t/mru' - $command_t = CommandT::Controller.new - rescue LoadError - load_path_modified = false - ::VIM::evaluate('&runtimepath').to_s.split(',').each do |path| - lib = "#{path}/ruby" - if !$LOAD_PATH.include?(lib) and File.exist?(lib) - $LOAD_PATH << lib - load_path_modified = true - end - end - retry if load_path_modified - - # could get here if C extension was not compiled, or was compiled - # for the wrong architecture or Ruby version - require 'command-t/stub' - $command_t = CommandT::Stub.new - end -EOF From b5868e2bcf81171de8d4dcd18fcc678f279f9212 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Thu, 22 May 2014 08:14:08 -0700 Subject: [PATCH 20/29] Update docs with info about autoload optimization Signed-off-by: Greg Hurrell --- doc/command-t.txt | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/doc/command-t.txt b/doc/command-t.txt index bffd028c..ea3a2da9 100644 --- a/doc/command-t.txt +++ b/doc/command-t.txt @@ -387,6 +387,10 @@ COMMANDS *command-t-commands* :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. + *:CommandTJumps* |:CommandTJump| Brings up the Command-T jumplist window. This works exactly like the standard file window, @@ -871,17 +875,18 @@ Command-T is written and maintained by Wincent Colaiuta . Other contributors that have submitted patches include (in alphabetical order): - Andy Waite Mike Lundy Shlomi Fish - Anthony Panozzo Nadav Samet Steven Moazami - Daniel Hahler Nate Kane Sung Pae - Felix Tjandrawibawa Nicholas Alpi Thomas Pelletier - Gary Bernhardt Noon Silk Ton van den Heuvel - Ivan Ukhov Paul Jolly Victor Hugo Borja - Jeff Kreeftmeijer Pavel Sergeev Vít Ondruch - Lucas de Vries Rainux Luo Woody Peterson - Marcus Brito Roland Puntaier Yan Pritzker - Marian Schubert Scott Bronson Yiding Jia - Matthew Todd Seth Fowler Zak Johnson + Andy Waite Nadav Samet Steven Moazami + Anthony Panozzo Nate Kane Sung Pae + Daniel Hahler Nicholas Alpi Thomas Pelletier + Felix Tjandrawibawa Noon Silk Ton van den Heuvel + Gary Bernhardt Paul Jolly Victor Hugo Borja + Ivan Ukhov Pavel Sergeev Vít Ondruch + Jeff Kreeftmeijer Rainux Luo Woody Peterson + Lucas de Vries Roland Puntaier Yan Pritzker + Marcus Brito Ross Lagerwall Yiding Jia + Marian Schubert Scott Bronson Zak Johnson + Matthew Todd Seth Fowler + Mike Lundy Shlomi Fish 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 @@ -986,6 +991,8 @@ HISTORY *command-t-history* 1.9 (not yet released) +- 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) 1.8 (31 March 2014) From f2e729df238977504ce6deca8f6c7bc376374168 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Fri, 23 May 2014 07:54:29 -0700 Subject: [PATCH 21/29] Fix compiler warnings on CentOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested with a compiler on a CentOS box and got these warnings. Harmless, but noisy, so let's fix them. compiling matcher.c matcher.c: In function ‘CommandTMatcher_sorted_matches_for’: matcher.c:181: warning: ISO C90 forbids mixed declarations and code compiling watchman.c watchman.c: In function ‘watchman_load_string’: watchman.c:313: warning: ISO C90 forbids mixed declarations and code watchman.c:320: warning: ISO C90 forbids mixed declarations and code watchman.c: In function ‘watchman_load_double’: watchman.c:334: warning: ISO C90 forbids mixed declarations and code watchman.c: In function ‘CommandTWatchmanUtils_load’: watchman.c:500: warning: ISO C90 forbids mixed declarations and code watchman.c:515: warning: ISO C90 forbids mixed declarations and code watchman.c:525: warning: ISO C90 forbids mixed declarations and code watchman.c: In function ‘CommandTWatchmanUtils_dump’: watchman.c:549: warning: ISO C90 forbids mixed declarations and code watchman.c:553: warning: ISO C90 forbids mixed declarations and code watchman.c: In function ‘CommandTWatchmanUtils_query’: watchman.c:594: warning: ISO C90 forbids mixed declarations and code watchman.c:605: warning: ISO C90 forbids mixed declarations and code watchman.c:614: warning: ISO C90 forbids mixed declarations and code watchman.c:624: warning: ISO C90 forbids mixed declarations and code watchman.c:632: warning: format ‘%lld’ expects type ‘long long int’, but argument 3 has type ‘int64_t’ watchman.c:640: warning: pointer of type ‘void *’ used in arithmetic watchman.c:640: warning: ISO C90 forbids mixed declarations and code Signed-off-by: Greg Hurrell --- ruby/command-t/matcher.c | 3 +- ruby/command-t/watchman.c | 71 +++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/ruby/command-t/matcher.c b/ruby/command-t/matcher.c index 104ab0a6..527fc4e1 100644 --- a/ruby/command-t/matcher.c +++ b/ruby/command-t/matcher.c @@ -133,6 +133,7 @@ VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self) long i, limit, path_count, thread_count; #ifdef HAVE_PTHREAD_H long err; + pthread_t *threads; #endif match_t *matches; thread_args_t *thread_args; @@ -178,7 +179,7 @@ VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self) #define THREAD_THRESHOLD 1000 /* avoid the overhead of threading when search space is small */ if (path_count < THREAD_THRESHOLD) thread_count = 1; - pthread_t *threads = malloc(sizeof(pthread_t) * thread_count); + threads = malloc(sizeof(pthread_t) * thread_count); if (!threads) rb_raise(rb_eNoMemError, "memory allocation failed"); #endif diff --git a/ruby/command-t/watchman.c b/ruby/command-t/watchman.c index ce18e9a8..4e63f8c3 100644 --- a/ruby/command-t/watchman.c +++ b/ruby/command-t/watchman.c @@ -297,6 +297,8 @@ int64_t watchman_load_int(char **ptr, char *end) { * 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"); } @@ -310,14 +312,14 @@ VALUE watchman_load_string(char **ptr, char *end) { rb_raise(rb_eArgError, "invalid string header"); } - int64_t len = watchman_load_int(ptr, end); + 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"); } - VALUE string = rb_str_new(*ptr, len); + string = rb_str_new(*ptr, len); *ptr += len; return string; } @@ -327,11 +329,12 @@ VALUE watchman_load_string(char **ptr, char *end) { * 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"); } - double val = *(double *)*ptr; + val = *(double *)*ptr; *ptr += sizeof(double); return val; } @@ -496,10 +499,14 @@ VALUE watchman_load(char **ptr, char *end) { * 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); - long len = RSTRING_LEN(serialized); - char *ptr = RSTRING_PTR(serialized); - char *end = ptr + len; + 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) { @@ -512,7 +519,7 @@ VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) { // get size marker ptr += sizeof(WATCHMAN_BINARY_MARKER) - 1; - uint64_t payload_size = watchman_load_int(&ptr, end); + payload_size = watchman_load_int(&ptr, end); if (!payload_size) { rb_raise(rb_eArgError, "empty payload"); } @@ -522,7 +529,7 @@ VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) { rb_raise(rb_eArgError, "payload size mismatch (%lu)", end - (ptr + payload_size)); } - VALUE loaded = watchman_load(&ptr, end); + loaded = watchman_load(&ptr, end); // one more sanity check if (ptr != end) { @@ -542,15 +549,17 @@ VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) { * (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 - uint64_t *len = (uint64_t *)(w->data + sizeof(WATCHMAN_HEADER) - sizeof(uint64_t) - 1); + len = (uint64_t *)(w->data + sizeof(WATCHMAN_HEADER) - sizeof(uint64_t) - 1); *len = w->len - sizeof(WATCHMAN_HEADER) + 1; // prepare final return value - VALUE serialized = rb_str_buf_new(w->len); + serialized = rb_str_buf_new(w->len); rb_str_buf_cat(serialized, (const char*)w->data, w->len); watchman_free(w); return serialized; @@ -582,18 +591,28 @@ void watchman_raise_system_call_error(int number) { * returns the result. */ VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) { - int fileno = NUM2INT(rb_funcall(socket, rb_intern("fileno"), 0)); + char *payload; + int fileno, flags; + int8_t peek[WATCHMAN_PEEK_BUFFER_SIZE]; + int8_t sizes[] = { 0, 0, 0, 1, 2, 4, 8 }; + 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 - int flags = fcntl(fileno, F_GETFL); + 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 - VALUE serialized = CommandTWatchmanUtils_dump(self, query); - long query_len = RSTRING_LEN(serialized); - ssize_t sent = send(fileno, RSTRING_PTR(serialized), query_len, 0); + 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) { @@ -602,8 +621,7 @@ VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) { } // sniff to see how large the header is - int8_t peek[WATCHMAN_PEEK_BUFFER_SIZE]; - ssize_t received = recv(fileno, peek, WATCHMAN_SNIFF_BUFFER_SIZE, MSG_PEEK | MSG_WAITALL); + 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) { @@ -611,8 +629,7 @@ VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) { } // peek at size of PDU - int8_t sizes[] = { 0, 0, 0, 1, 2, 4, 8 }; - ssize_t peek_size = sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) + + peek_size = sizeof(WATCHMAN_BINARY_MARKER) - 1 + sizeof(int8_t) + sizes[peek[sizeof(WATCHMAN_BINARY_MARKER) - 1]]; received = recv(fileno, peek, peek_size, MSG_PEEK); @@ -621,15 +638,19 @@ VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) { } else if (received != peek_size) { rb_raise(rb_eRuntimeError, "failed to peek at PDU header"); } - int8_t *pdu_size_ptr = peek + sizeof(WATCHMAN_BINARY_MARKER) - sizeof(int8_t); - int64_t payload_size = + 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 - void *buffer = xmalloc(payload_size); + buffer = xmalloc(payload_size); if (!buffer) { - rb_raise(rb_eNoMemError, "failed to allocate %lld bytes", payload_size); + 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) { @@ -637,8 +658,8 @@ VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) { } else if (received != payload_size) { rb_raise(rb_eRuntimeError, "failed to load PDU"); } - char *payload = buffer + peek_size; - VALUE loaded = watchman_load(&payload, payload + payload_size); + payload = (char *)buffer + peek_size; + loaded = watchman_load(&payload, payload + payload_size); free(buffer); return loaded; } From b22fcea7c757b76357ec967acedb9eb55e3a1b86 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Fri, 23 May 2014 08:02:37 -0700 Subject: [PATCH 22/29] Add casts to silence Clang warnings Alas, I can't repro these warnings, even with CC=clang and explicitly adding `-Wformat` to the args. Oh well. Reported here: https://github.com/wincent/Command-T/issues/80 Signed-off-by: Greg Hurrell --- ruby/command-t/watchman.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ruby/command-t/watchman.c b/ruby/command-t/watchman.c index 4e63f8c3..dd7214b1 100644 --- a/ruby/command-t/watchman.c +++ b/ruby/command-t/watchman.c @@ -526,14 +526,22 @@ VALUE CommandTWatchmanUtils_load(VALUE self, VALUE serialized) { // sanity check length if (ptr + payload_size != end) { - rb_raise(rb_eArgError, "payload size mismatch (%lu)", end - (ptr + payload_size)); + 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)", end - ptr); + rb_raise( + rb_eArgError, + "payload termination mismatch (%lu)", + (unsigned long)(end - ptr) + ); } return loaded; From 777113ca9de74f59f87c9f698340547dd997cbf6 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Fri, 23 May 2014 08:20:04 -0700 Subject: [PATCH 23/29] Add key xterm info to FAQ This is fruit of an email exchange I had with a user; he suggested adding this to the docs. Signed-off-by: Greg Hurrell --- doc/command-t.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/command-t.txt b/doc/command-t.txt index ea3a2da9..6e514fae 100644 --- a/doc/command-t.txt +++ b/doc/command-t.txt @@ -754,6 +754,19 @@ To disable flow control, add the following to your `.zshrc` or 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* From 26fca4c4076386f51f1d6f1881d17e279b1ab84c Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Sat, 24 May 2014 08:19:31 -0700 Subject: [PATCH 24/29] Fix egregious matching bug This was introduced six months ago in 301fd661831 ("Fix off-by-one error in memoization indexing"). It basically meant that you could repeat any matching character and it would be considered to match, as long as your query string didn't exceed the available space in the haystack. For example, given a file "foobar", the following query strings would all match it: "foo", "fooo", "foooo", "fooooo", "fffbar" etc; but strings such as "ffffbar" and "foooooo" would not (due to being overlength). This is the most egregious bug in the matcher ever, so I'm very grateful to Pavel Sergeev (@dzhioev) for reporting it. Alas, the fix comes with a price; while the fix brings a marginal performance increase for most of our test cases, the "pathological" benchmark gets distinctly slower: Before: user system total real pathological 0.010000 0.000000 0.010000 ( 0.003380) command-t 0.410000 0.000000 0.410000 ( 0.413851) chromium (subset) 1.190000 0.010000 1.200000 ( 0.552776) chromium (whole) 4.540000 0.010000 4.550000 ( 1.557351) After: user system total real pathological 1.750000 0.000000 1.750000 ( 1.751254) command-t 0.380000 0.000000 0.380000 ( 0.375418) chromium (subset) 1.170000 0.010000 1.180000 ( 0.498126) chromium (whole) 4.470000 0.010000 4.480000 ( 1.537441) This reverses the win reported in 301fd661831, which at the time I described as "so marked that I am almost suspicious of it". I should have been more suspicious... I'll keep digging into this to see if I can improve the speed of the pathological case, but for now I just want to get this fix out as correctness is more important than speed. Fixes: https://github.com/wincent/Command-T/issues/82 Signed-off-by: Greg Hurrell --- ruby/command-t/match.c | 5 ++--- ruby/command-t/matcher.c | 1 - spec/command-t/matcher_spec.rb | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/ruby/command-t/match.c b/ruby/command-t/match.c index 03644677..df7a6460 100644 --- a/ruby/command-t/match.c +++ b/ruby/command-t/match.c @@ -1,4 +1,4 @@ -// Copyright 2010-2013 Wincent Colaiuta. All rights reserved. +// Copyright 2010-2014 Wincent Colaiuta. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: @@ -125,11 +125,10 @@ double recursive_match(matchinfo_t *m, // sharable meta-data } score += score_for_char; - last_idx = haystack_idx + 1; + last_idx = ++haystack_idx; break; } } - if (!found) { score = 0.0; goto memoize; diff --git a/ruby/command-t/matcher.c b/ruby/command-t/matcher.c index 527fc4e1..8f7a0369 100644 --- a/ruby/command-t/matcher.c +++ b/ruby/command-t/matcher.c @@ -127,7 +127,6 @@ void *match_thread(void *thread_args) return NULL; } - VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self) { long i, limit, path_count, thread_count; diff --git a/spec/command-t/matcher_spec.rb b/spec/command-t/matcher_spec.rb index bb95eef5..db21ebea 100644 --- a/spec/command-t/matcher_spec.rb +++ b/spec/command-t/matcher_spec.rb @@ -1,4 +1,4 @@ -# Copyright 2010-2013 Wincent Colaiuta. All rights reserved. +# Copyright 2010-2014 Wincent Colaiuta. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -203,5 +203,19 @@ def ordered_matches(paths, query) TODO ] end + + it "doesn't incorrectly accept repeats of the last-matched character" do + # https://github.com/wincent/Command-T/issues/82 + matcher = matcher(*%w[ash/system/user/config.h]) + matcher.sorted_matches_for('usercc').should == [] + + # simpler test case + matcher = matcher(*%w[foobar]) + matcher.sorted_matches_for('fooooo').should == [] + + # minimal repro + matcher = matcher(*%w[ab]) + matcher.sorted_matches_for('aa').should == [] + end end end From 4ed8de465f151b95ec70a8b07bfec3129f486747 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Sat, 24 May 2014 20:02:12 -0700 Subject: [PATCH 25/29] Fix off-by-one error in last_idx management This is a relatively benign error; causing our last_idx calculations to be slightly more favorable than they should have been, leading to slightly inflated match scores. I think this was likely harmless, because all matches would have benefited from this bias. This is how the code used to look before the ill-fated 301fd661831, which introduced a nasty matching bug. I fixed this in 5621c4e2185, but introduced this other error in my haste; I probably should have realized it was wrong by comparison with the pre-301fd661831 state. Signed-off-by: Greg Hurrell --- ruby/command-t/match.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/command-t/match.c b/ruby/command-t/match.c index df7a6460..ef4175e2 100644 --- a/ruby/command-t/match.c +++ b/ruby/command-t/match.c @@ -125,7 +125,7 @@ double recursive_match(matchinfo_t *m, // sharable meta-data } score += score_for_char; - last_idx = ++haystack_idx; + last_idx = haystack_idx++; break; } } From 94bd7d0806bdc60d42b14dbfa770decdaa257494 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Sat, 24 May 2014 20:08:53 -0700 Subject: [PATCH 26/29] Fix memoization Commit 5621c4e21854 fixed a terrible bug that affected the correctness of the matching algorithm, but it did so at the cost of the speed of our pathological test case (although other test cases seemed slightly better). Buggy version: user system total real pathological 0.000000 0.000000 0.000000 ( 0.003640) command-t 0.390000 0.000000 0.390000 ( 0.383473) chromium (subset) 1.170000 0.010000 1.180000 ( 0.525694) chromium (whole) 4.550000 0.010000 4.560000 ( 1.579863) Fixed version: user system total real pathological 1.880000 0.010000 1.890000 ( 1.887996) command-t 0.350000 0.000000 0.350000 ( 0.348788) chromium (subset) 1.140000 0.010000 1.150000 ( 0.504924) chromium (whole) 4.430000 0.000000 4.430000 ( 1.516979) After fixing this, I went back and audited the memoization code to see if we could "have our cake and eat it too"; that is, get robust/correct matching and keep some or all of the speed gains that we got from memoization in the buggy version. It turns out, we can: user system total real pathological 0.000000 0.000000 0.000000 ( 0.002413) command-t 0.380000 0.000000 0.380000 ( 0.380633) chromium (subset) 0.850000 0.010000 0.860000 ( 0.441269) chromium (whole) 3.400000 0.010000 3.410000 ( 1.342450) So, we got faster on every test case in the benchmark suite. Not sure what I was smoking when I originally wrote the memoization code; instead of memoizing a known result at the earliest point in the haystack string, I was doing so at the latest, which reduces the value of the memoization. Signed-off-by: Greg Hurrell --- ruby/command-t/match.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ruby/command-t/match.c b/ruby/command-t/match.c index ef4175e2..7e716e20 100644 --- a/ruby/command-t/match.c +++ b/ruby/command-t/match.c @@ -51,9 +51,10 @@ double recursive_match(matchinfo_t *m, // sharable meta-data long i, j, distance; int found; double score_for_char; + long memo_idx = haystack_idx; // do we have a memoized result we can return? - double memoized = m->memo[needle_idx * m->needle_len + haystack_idx]; + double memoized = m->memo[needle_idx * m->needle_len + memo_idx]; if (memoized != DBL_MAX) return memoized; @@ -144,7 +145,7 @@ double recursive_match(matchinfo_t *m, // sharable meta-data score = score > seen_score ? score : seen_score; memoize: - m->memo[needle_idx * m->needle_len + haystack_idx] = score; + m->memo[needle_idx * m->needle_len + memo_idx] = score; return score; } From 406a16f3a348b51e1d2fd70639a4123af6342833 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Sat, 24 May 2014 20:18:25 -0700 Subject: [PATCH 27/29] Establish new benchmark baseline The pathological benchmark runs too fast to be meaningful, and because it only contains a single path, it doesn't make use of multi-threading like the other test cases do. So, let's set up a new baseline by adding more paths to the test case and running it enough times for it to be within the same order of magnitude as its immediate neighbors. Before: user system total real pathological 0.000000 0.000000 0.000000 ( 0.002413) command-t 0.380000 0.000000 0.380000 ( 0.380633) chromium (subset) 0.850000 0.010000 0.860000 ( 0.441269) chromium (whole) 3.400000 0.010000 3.410000 ( 1.342450) After: user system total real pathological 0.110000 0.000000 0.110000 ( 0.112132) command-t 0.340000 0.000000 0.340000 ( 0.340255) chromium (subset) 0.800000 0.010000 0.810000 ( 0.398784) chromium (whole) 3.320000 0.010000 3.330000 ( 1.236914) As you can see there is some flutter between runs (the timings for the other test cases shouldn't have changed), but I am doing best-of-three runs for all of these, so that's as close as I can get to scientific. Signed-off-by: Greg Hurrell --- data/benchmark.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/data/benchmark.yml b/data/benchmark.yml index e0d2e247..7d6cff2f 100644 --- a/data/benchmark.yml +++ b/data/benchmark.yml @@ -1,9 +1,24 @@ --- tests: - name: pathological - times: 20 + times: 200 paths: - - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaa + - aaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa queries: - a - aaa From 0dda67065a43da98c3c607f5ddddf15595f4d2f8 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Sat, 24 May 2014 20:26:14 -0700 Subject: [PATCH 28/29] Doc: update HISTORY section Once I've tested the recent changes for a while I'll be updating the master branch and cutting a release, so let's prep the docs for that. Signed-off-by: Greg Hurrell --- doc/command-t.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/command-t.txt b/doc/command-t.txt index 6e514fae..11433191 100644 --- a/doc/command-t.txt +++ b/doc/command-t.txt @@ -1007,6 +1007,12 @@ HISTORY *command-t-history* - 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, yield a more than 10% speed + boost 1.8 (31 March 2014) From 7a86796bb7cca96638ff55190ec06a147c63ef35 Mon Sep 17 00:00:00 2001 From: Greg Hurrell Date: Sun, 25 May 2014 11:05:56 -0700 Subject: [PATCH 29/29] Guard against overflow when peeking at PDU size This is a partial cherry-pick from my commit to the Facebook Watchman project[0]. I noticed while reviewing the corresponding pull request[1] that a malformed header could cause us to index beyond the range of the `sizes` array, so this commit pulls in the equivalent guard. Note there is a bunch of other stuff in that pull-request and that specific commit which I am not pulling down here. That code is in a gem and so needs to be more defensive; in Command-T itself, if something goes wrong with Watchman we want to fail noisily and loudly. So we're not going to worry about freeing memory before throwing an exception (effectively crashing) nor do we care about restoring the flags to their pre-`fcntl` state (as we're the only users of the socket). [0]: https://github.com/facebook/watchman/commit/5b0aa521ae [1]: https://github.com/facebook/watchman/pull/37 [2]: https://github.com/facebook/watchman/pull/37#discussion_r12498885 Signed-off-by: Greg Hurrell --- ruby/command-t/watchman.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ruby/command-t/watchman.c b/ruby/command-t/watchman.c index dd7214b1..e274f762 100644 --- a/ruby/command-t/watchman.c +++ b/ruby/command-t/watchman.c @@ -603,6 +603,7 @@ VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) { 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; @@ -637,8 +638,12 @@ VALUE CommandTWatchmanUtils_query(VALUE self, VALUE query, VALUE socket) { } // 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[peek[sizeof(WATCHMAN_BINARY_MARKER) - 1]]; + sizes[sizes_idx]; received = recv(fileno, peek, peek_size, MSG_PEEK); if (received == -1) {