ver-command_t 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,49 @@
1
+ // Copyright 2010 Wincent Colaiuta. All rights reserved.
2
+ //
3
+ // Redistribution and use in source and binary forms, with or without
4
+ // modification, are permitted provided that the following conditions are met:
5
+ //
6
+ // 1. Redistributions of source code must retain the above copyright notice,
7
+ // this list of conditions and the following disclaimer.
8
+ // 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ // this list of conditions and the following disclaimer in the documentation
10
+ // and/or other materials provided with the distribution.
11
+ //
12
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ // POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ #include <ruby.h>
25
+
26
+ // for compatibility with older versions of Ruby which don't declare RSTRING_PTR
27
+ #ifndef RSTRING_PTR
28
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
29
+ #endif
30
+
31
+ // for compatibility with older versions of Ruby which don't declare RSTRING_LEN
32
+ #ifndef RSTRING_LEN
33
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
34
+ #endif
35
+
36
+ // for compatibility with older versions of Ruby which don't declare RARRAY_PTR
37
+ #ifndef RARRAY_PTR
38
+ #define RARRAY_PTR(a) (RARRAY(a)->ptr)
39
+ #endif
40
+
41
+ // for compatibility with older versions of Ruby which don't declare RARRAY_LEN
42
+ #ifndef RARRAY_LEN
43
+ #define RARRAY_LEN(a) (RARRAY(a)->len)
44
+ #endif
45
+
46
+ // for compatibility with older versions of Ruby which don't declare RFLOAT_VALUE
47
+ #ifndef RFLOAT_VALUE
48
+ #define RFLOAT_VALUE(f) (RFLOAT(f)->value)
49
+ #endif
@@ -0,0 +1,34 @@
1
+ require 'command_t/finder'
2
+ class VER::Executor::ExCommandT < VER::Executor::Entry
3
+ def pwd
4
+ @pwd || Dir.pwd
5
+ end
6
+ attr_writer :pwd
7
+
8
+ def options
9
+ @options || {}
10
+ end
11
+ attr_writer :options
12
+
13
+ def finder
14
+ @finder ||= CommandT::Finder.new(pwd, options)
15
+ end
16
+
17
+ def choices(text)
18
+ finder.sorted_matches_for(text, options)
19
+ end
20
+
21
+ def action(selected)
22
+ if selected
23
+ throw :invalid if File.directory? path
24
+ VER.find_or_create_buffer(path)
25
+ else
26
+ tree.children.each do |child|
27
+ path = Array(child).first # if more is added later
28
+ next if File.directory? path
29
+ VER.find_or_create_buffer path
30
+ end
31
+ end
32
+ callback.destroy(false)
33
+ end
34
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright 2010 Wincent Colaiuta. All rights reserved.
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice,
7
+ # this list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ #
12
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ # POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ require 'command_t/ext' # CommandT::Matcher
25
+ require 'command_t/scanner'
26
+
27
+ module CommandT
28
+ # Encapsulates a Scanner instance (which builds up a list of available files
29
+ # in a directory) and a Matcher instance (which selects from that list based
30
+ # on a search string).
31
+ class Finder
32
+ def initialize path = Dir.pwd, options = {}
33
+ @scanner = Scanner.new path, options
34
+ @matcher = Matcher.new @scanner, options
35
+ end
36
+
37
+ # Options:
38
+ # :limit (integer): limit the number of returned matches
39
+ def sorted_matches_for str, options = {}
40
+ @matcher.sorted_matches_for str, options
41
+ end
42
+
43
+ def flush
44
+ @scanner.flush
45
+ end
46
+
47
+ def path= path
48
+ @scanner.path = path
49
+ end
50
+ end # class Finder
51
+ end # CommandT
@@ -0,0 +1,89 @@
1
+ # Copyright 2010 Wincent Colaiuta. All rights reserved.
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice,
7
+ # this list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ #
12
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ # POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ module CommandT
25
+ # Reads the current directory recursively for the paths to all regular files.
26
+ class Scanner
27
+ class FileLimitExceeded < ::RuntimeError; end
28
+
29
+ def initialize path = Dir.pwd, options = {}
30
+ @path = path
31
+ @max_depth = options[:max_depth] || 15
32
+ @max_files = options[:max_files] || 10_000
33
+ @scan_dot_directories = options[:scan_dot_directories] || false
34
+ end
35
+
36
+ def paths
37
+ return @paths unless @paths.nil?
38
+ begin
39
+ @paths = []
40
+ @depth = 0
41
+ @files = 0
42
+ @prefix_len = @path.chomp('/').length
43
+ add_paths_for_directory @path, @paths
44
+ rescue FileLimitExceeded
45
+ end
46
+ @paths
47
+ end
48
+
49
+ def flush
50
+ @paths = nil
51
+ end
52
+
53
+ def path= str
54
+ if @path != str
55
+ @path = str
56
+ flush
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def path_excluded? path
63
+ path = path[(@prefix_len + 1)..-1]
64
+ false # until we find something
65
+ end
66
+
67
+ def add_paths_for_directory dir, accumulator
68
+ Dir.foreach(dir) do |entry|
69
+ next if ['.', '..'].include?(entry)
70
+ path = File.join(dir, entry)
71
+ unless path_excluded?(path)
72
+ if File.file?(path)
73
+ @files += 1
74
+ raise FileLimitExceeded if @files > @max_files
75
+ accumulator << path[@prefix_len + 1..-1]
76
+ elsif File.directory?(path)
77
+ next if @depth >= @max_depth
78
+ next if (entry.match(/\A\./) && !@scan_dot_directories)
79
+ @depth += 1
80
+ add_paths_for_directory path, accumulator
81
+ @depth -= 1
82
+ end
83
+ end
84
+ end
85
+ rescue Errno::EACCES
86
+ # skip over directories for which we don't have access
87
+ end
88
+ end # class Scanner
89
+ end # module CommandT
@@ -0,0 +1,75 @@
1
+ # Copyright 2010 Wincent Colaiuta. All rights reserved.
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice,
7
+ # this list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ #
12
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ # POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ module CommandT
25
+ # Convenience class for saving and restoring global settings.
26
+ class Settings
27
+ def initialize
28
+ save
29
+ end
30
+
31
+ def save
32
+ @timeoutlen = get_number 'timeoutlen'
33
+ @report = get_number 'report'
34
+ @sidescroll = get_number 'sidescroll'
35
+ @sidescrolloff = get_number 'sidescrolloff'
36
+ @equalalways = get_bool 'equalalways'
37
+ @hlsearch = get_bool 'hlsearch'
38
+ @insertmode = get_bool 'insertmode'
39
+ @showcmd = get_bool 'showcmd'
40
+ end
41
+
42
+ def restore
43
+ set_number 'timeoutlen', @timeoutlen
44
+ set_number 'report', @report
45
+ set_number 'sidescroll', @sidescroll
46
+ set_number 'sidescrolloff', @sidescrolloff
47
+ set_bool 'equalalways', @equalalways
48
+ set_bool 'hlsearch', @hlsearch
49
+ set_bool 'insertmode', @insertmode
50
+ set_bool 'showcmd', @showcmd
51
+ end
52
+
53
+ private
54
+
55
+ def get_number setting
56
+ ::VIM::evaluate("&#{setting}").to_i
57
+ end
58
+
59
+ def get_bool setting
60
+ ::VIM::evaluate("&#{setting}").to_i == 1
61
+ end
62
+
63
+ def set_number setting, value
64
+ ::VIM::set_option "#{setting}=#{value}"
65
+ end
66
+
67
+ def set_bool setting, value
68
+ if value
69
+ ::VIM::set_option setting
70
+ else
71
+ ::VIM::set_option "no#{setting}"
72
+ end
73
+ end
74
+ end # class Settings
75
+ end # module CommandT
@@ -0,0 +1,16 @@
1
+ # All the ver integration stuff is in here.
2
+ require 'ver'
3
+ require 'command_t/ex/command_t'
4
+
5
+ VER::Executor::ExLabel::COMPLETERS.merge!(
6
+ 'command_t' => :ExCommandT
7
+ )
8
+
9
+ VER::startup_hook do
10
+ module VER
11
+ minor_mode :open do
12
+ handler Methods::Control
13
+ map [:ex, :command_t], '<Control-t>'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,72 @@
1
+ # Copyright 2010 Wincent Colaiuta. All rights reserved.
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice,
7
+ # this list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ #
12
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ # POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
25
+ require 'command_t/finder'
26
+
27
+ describe CommandT::Finder do
28
+ before :all do
29
+ @finder = CommandT::Finder.new File.join(File.dirname(__FILE__), '..', 'fixtures')
30
+ @all_fixtures = %w(
31
+ bar/abc
32
+ bar/xyz
33
+ baz
34
+ bing
35
+ foo/alpha/t1
36
+ foo/alpha/t2
37
+ foo/beta
38
+ )
39
+ end
40
+
41
+ describe 'sorted_matches_for method' do
42
+ it 'should return an empty array when no matches' do
43
+ @finder.sorted_matches_for('kung foo fighting').should == []
44
+ end
45
+
46
+ it 'should return all files when query string is empty' do
47
+ @finder.sorted_matches_for('').should == @all_fixtures
48
+ end
49
+
50
+ it 'should return files in alphabetical order when query string is empty' do
51
+ results = @finder.sorted_matches_for('')
52
+ results.should == results.sort
53
+ end
54
+
55
+ it 'should return matching files in score order' do
56
+ @finder.sorted_matches_for('ba').
57
+ should == %w(baz bar/abc bar/xyz foo/beta)
58
+ @finder.sorted_matches_for('a').
59
+ should == %w(baz bar/abc bar/xyz foo/alpha/t1 foo/alpha/t2 foo/beta)
60
+ end
61
+
62
+ it 'should obey the :limit option for empty search strings' do
63
+ @finder.sorted_matches_for('', :limit => 2).
64
+ should == %w(bar/abc bar/xyz)
65
+ end
66
+
67
+ it 'should obey the :limit option for non-empty search strings' do
68
+ @finder.sorted_matches_for('a', :limit => 3).
69
+ should == %w(baz bar/abc bar/xyz)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,236 @@
1
+ # Copyright 2010 Wincent Colaiuta. All rights reserved.
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice,
7
+ # this list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ #
12
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
+ # POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
25
+ require 'command_t/ext'
26
+
27
+ describe CommandT::Match do
28
+ def match_for path, pattern
29
+ CommandT::Match.new path, pattern
30
+ end
31
+
32
+ it 'requires pattern to be lowercase' do
33
+ # this is an optimization: we ask our caller (the Matcher class) to
34
+ # downcase once before calling us, rather than downcase repeatedly
35
+ # during looping, recursion, and initialization of thousands of Match
36
+ # instances
37
+ match_for('foo', 'Foo').matches?.should == false
38
+ end
39
+
40
+ describe '#matches?' do
41
+ it 'returns false for non-matches' do
42
+ match_for('foo', 'bar').matches?.should == false
43
+ end
44
+
45
+ it 'returns true for matches' do
46
+ match_for('foo', 'foo').matches?.should == true
47
+ end
48
+
49
+ it 'returns true for empty search strings' do
50
+ match_for('foo', '').matches?.should == true
51
+ end
52
+
53
+ it 'returns false for overlength matches' do
54
+ match_for('foo', 'foo...').matches?.should == false
55
+ end
56
+ end
57
+
58
+ describe 'score method' do
59
+ it 'assigns a score of 1.0 for empty search string' do
60
+ match_for('foo', '').score.should == 1.0
61
+ end
62
+
63
+ it 'assigns a score of zero for a non-match' do
64
+ match_for('foo', 'bar').score.should == 0.0
65
+ end
66
+
67
+ it 'assigns a score of zero for an overlength match' do
68
+ match_for('foo', 'foo...').score.should == 0.0
69
+ end
70
+
71
+ it 'assigns perfect matches a score of one' do
72
+ match_for('foo', 'foo').score.should == 1.0
73
+ end
74
+
75
+ it 'assigns perfect but incomplete matches a score of less than one' do
76
+ match_for('foo', 'f').score.should < 1.0
77
+ end
78
+
79
+ it 'prioritizes matches with more matching characters' do
80
+ few_matches = match_for('foobar', 'fb')
81
+ many_matches = match_for('foobar', 'fbar')
82
+ many_matches.score.should > few_matches.score
83
+ end
84
+
85
+ it 'prioritizes shorter paths over longer ones' do
86
+ short_path = match_for('article.rb', 'art')
87
+ long_path = match_for('articles_controller_spec.rb', 'art')
88
+ short_path.score.should > long_path.score
89
+ end
90
+
91
+ it 'prioritizes matches after "/"' do
92
+ normal_match = match_for('fooobar', 'b')
93
+ special_match = match_for('foo/bar', 'b')
94
+ special_match.score.should > normal_match.score
95
+
96
+ # note that / beats _
97
+ normal_match = match_for('foo_bar', 'b')
98
+ special_match = match_for('foo/bar', 'b')
99
+ special_match.score.should > normal_match.score
100
+
101
+ # / also beats -
102
+ normal_match = match_for('foo-bar', 'b')
103
+ special_match = match_for('foo/bar', 'b')
104
+ special_match.score.should > normal_match.score
105
+
106
+ # and numbers
107
+ normal_match = match_for('foo9bar', 'b')
108
+ special_match = match_for('foo/bar', 'b')
109
+ special_match.score.should > normal_match.score
110
+
111
+ # and periods
112
+ normal_match = match_for('foo.bar', 'b')
113
+ special_match = match_for('foo/bar', 'b')
114
+ special_match.score.should > normal_match.score
115
+
116
+ # and spaces
117
+ normal_match = match_for('foo bar', 'b')
118
+ special_match = match_for('foo/bar', 'b')
119
+ special_match.score.should > normal_match.score
120
+ end
121
+
122
+ it 'prioritizes matches after "-"' do
123
+ normal_match = match_for('fooobar', 'b')
124
+ special_match = match_for('foo-bar', 'b')
125
+ special_match.score.should > normal_match.score
126
+
127
+ # - also beats .
128
+ normal_match = match_for('foo.bar', 'b')
129
+ special_match = match_for('foo-bar', 'b')
130
+ special_match.score.should > normal_match.score
131
+ end
132
+
133
+ it 'prioritizes matches after "_"' do
134
+ normal_match = match_for('fooobar', 'b')
135
+ special_match = match_for('foo_bar', 'b')
136
+ special_match.score.should > normal_match.score
137
+
138
+ # _ also beats .
139
+ normal_match = match_for('foo.bar', 'b')
140
+ special_match = match_for('foo_bar', 'b')
141
+ special_match.score.should > normal_match.score
142
+ end
143
+
144
+ it 'prioritizes matches after " "' do
145
+ normal_match = match_for('fooobar', 'b')
146
+ special_match = match_for('foo bar', 'b')
147
+ special_match.score.should > normal_match.score
148
+
149
+ # " " also beats .
150
+ normal_match = match_for('foo.bar', 'b')
151
+ special_match = match_for('foo bar', 'b')
152
+ special_match.score.should > normal_match.score
153
+ end
154
+
155
+ it 'prioritizes matches after numbers' do
156
+ normal_match = match_for('fooobar', 'b')
157
+ special_match = match_for('foo9bar', 'b')
158
+ special_match.score.should > normal_match.score
159
+
160
+ # numbers also beat .
161
+ normal_match = match_for('foo.bar', 'b')
162
+ special_match = match_for('foo9bar', 'b')
163
+ special_match.score.should > normal_match.score
164
+ end
165
+
166
+ it 'prioritizes matches after periods' do
167
+ normal_match = match_for('fooobar', 'b')
168
+ special_match = match_for('foo.bar', 'b')
169
+ special_match.score.should > normal_match.score
170
+ end
171
+
172
+ it 'prioritizes matching capitals following lowercase' do
173
+ normal_match = match_for('foobar', 'b')
174
+ special_match = match_for('fooBar', 'b')
175
+ special_match.score.should > normal_match.score
176
+ end
177
+
178
+ it 'prioritizes matches earlier in the string' do
179
+ early_match = match_for('**b*****', 'b')
180
+ late_match = match_for('******b*', 'b')
181
+ early_match.score.should > late_match.score
182
+ end
183
+
184
+ it 'prioritizes matches closer to previous matches' do
185
+ early_match = match_for('**bc****', 'bc')
186
+ late_match = match_for('**b***c*', 'bc')
187
+ early_match.score.should > late_match.score
188
+ end
189
+
190
+ it 'scores alternative matches of same path differently' do
191
+ # given path: app/controllers/articles_controller.rb
192
+ left_to_right_match = match_for('a**/****r******/**t*c***_*on*******.**', 'artcon')
193
+ best_match = match_for('***/***********/art*****_con*******.**', 'artcon')
194
+ best_match.score.should > left_to_right_match.score
195
+ end
196
+
197
+ it 'returns the best possible score among alternatives' do
198
+ # given path: app/controllers/articles_controller.rb
199
+ best_match = match_for('***/***********/art*****_con*******.**', 'artcon')
200
+ chosen_match = match_for('app/controllers/articles_controller.rb', 'artcon')
201
+ chosen_match.score.should == best_match.score
202
+ end
203
+
204
+ it 'provides intuitive results for "artcon" and "articles_controller"' do
205
+ low = match_for('app/controllers/heartbeat_controller.rb', 'artcon')
206
+ high = match_for('app/controllers/articles_controller.rb', 'artcon')
207
+ high.score.should > low.score
208
+ end
209
+
210
+ it 'provides intuitive results for "aca" and "a/c/articles_controller"' do
211
+ low = match_for 'app/controllers/heartbeat_controller.rb', 'aca'
212
+ high = match_for 'app/controllers/articles_controller.rb', 'aca'
213
+ best_match = match_for 'a**/c**********/a******************.**', 'aca'
214
+ high.score.should > low.score
215
+ high.score.should == best_match.score
216
+ end
217
+
218
+ it 'provides intuitive results for "d" and "doc/command-t.txt"' do
219
+ low = match_for 'TODO', 'd'
220
+ high = match_for 'doc/command-t.txt', 'd'
221
+ high.score.should > low.score
222
+ end
223
+
224
+ it 'provides intuitive results for "do" and "doc/command-t.txt"' do
225
+ low = match_for 'TODO', 'do'
226
+ high = match_for 'doc/command-t.txt', 'do'
227
+ high.score.should > low.score
228
+ end
229
+ end
230
+
231
+ describe 'to_s method' do
232
+ it 'returns the entire matched string' do
233
+ match_for('abc', 'abc').to_s.should == 'abc'
234
+ end
235
+ end
236
+ end