ruber 0.0.9 → 0.0.10
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.
- data/CHANGES +42 -1
- data/lib/ruber/application/application.rb +25 -5
- data/lib/ruber/application/plugin.yaml +2 -10
- data/lib/ruber/component_manager.rb +2 -2
- data/lib/ruber/document_project.rb +5 -3
- data/lib/ruber/editor/document.rb +5 -4
- data/lib/ruber/editor/ktexteditor_wrapper.rb +1 -1
- data/lib/ruber/exception_widgets.rb +1 -1
- data/lib/ruber/main_window/main_window.rb +4 -3
- data/lib/ruber/main_window/main_window_actions.rb +2 -2
- data/lib/ruber/main_window/main_window_internal.rb +1 -1
- data/lib/ruber/output_widget.rb +17 -5
- data/lib/ruber/project.rb +34 -3
- data/lib/ruber/project_dir_scanner.rb +171 -0
- data/lib/ruber/settings_container.rb +7 -7
- data/lib/ruber/settings_dialog.rb +24 -24
- data/lib/ruber/version.rb +1 -1
- data/lib/ruber/world/environment.rb +1 -0
- data/lib/ruber/world/plugin.yaml +7 -2
- data/lib/ruber/{application → world}/project_files_widget.rb +0 -0
- data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.rb +2 -2
- data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.ui +0 -0
- data/lib/ruber/{application → world}/ui/project_files_widget.rb +2 -2
- data/lib/ruber/{application → world}/ui/project_files_widget.ui +0 -0
- data/plugins/auto_end/auto_end.rb +21 -18
- data/plugins/autosave/autosave.rb +1 -1
- data/plugins/find_in_files/find_in_files.rb +1 -1
- data/plugins/irb/irb.png +0 -0
- data/plugins/irb/irb.rb +142 -0
- data/plugins/irb/irb.svg +240 -0
- data/plugins/irb/irb_controller.rb +541 -0
- data/plugins/irb/irbrc.rb +21 -0
- data/plugins/irb/plugin.yaml +19 -0
- data/plugins/irb/ui/irb_config_widget.rb +151 -0
- data/plugins/irb/ui/irb_config_widget.ui +123 -0
- data/plugins/irb/ui/irb_tool_widget.rb +97 -0
- data/plugins/irb/ui/irb_tool_widget.ui +86 -0
- data/plugins/project_browser/project_browser.rb +1 -1
- data/plugins/rspec/plugin.yaml +6 -3
- data/plugins/rspec/rspec.rb +172 -473
- data/plugins/rspec/tool_widget.rb +462 -0
- data/plugins/rspec/ui/rspec_project_widget.rb +58 -38
- data/plugins/rspec/ui/rspec_project_widget.ui +68 -64
- data/plugins/ruberri/class_formatter.rb +126 -0
- data/plugins/ruberri/method_formatter.rb +90 -0
- data/plugins/ruberri/plugin.yaml +13 -0
- data/plugins/ruberri/ruberri.rb +226 -0
- data/plugins/ruberri/search.rb +111 -0
- data/plugins/ruberri/ui/tool_widget.rb +73 -0
- data/plugins/ruberri/ui/tool_widget.ui +49 -0
- data/plugins/ruby_runner/ruby_runner.rb +2 -2
- data/plugins/ruby_syntax_checker/plugin.yaml +11 -0
- data/plugins/ruby_syntax_checker/ruby_syntax_checker.rb +147 -0
- data/plugins/syntax_checker/plugin.yaml +10 -6
- data/plugins/syntax_checker/syntax_checker.rb +216 -520
- data/plugins/syntax_checker/ui/config_widget.rb +61 -0
- data/plugins/syntax_checker/ui/config_widget.ui +44 -0
- data/plugins/yaml_syntax_checker/plugin.yaml +11 -0
- data/plugins/yaml_syntax_checker/yaml_syntax_checker.rb +62 -0
- data/ruber.desktop +0 -0
- data/spec/auto_end_spec.rb +224 -186
- data/spec/document_project_spec.rb +9 -1
- data/spec/document_spec.rb +9 -0
- data/spec/environment_spec.rb +12 -0
- data/spec/output_widget_spec.rb +69 -2
- data/spec/project_dir_scanner_spec.rb +195 -0
- data/spec/project_spec.rb +43 -73
- data/spec/ruby_syntax_checker_spec.rb +361 -0
- data/spec/syntax_checker_spec.rb +1132 -0
- data/spec/yaml_syntax_checker_spec.rb +130 -0
- metadata +232 -225
- data/lib/ruber/application/project_files_list.rb +0 -320
- data/spec/project_files_list_spec.rb +0 -411
@@ -0,0 +1,361 @@
|
|
1
|
+
require 'spec/framework'
|
2
|
+
require 'spec/common'
|
3
|
+
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
require 'plugins/ruby_syntax_checker/ruby_syntax_checker'
|
7
|
+
|
8
|
+
describe Ruber::RubySyntaxChecker::Plugin do
|
9
|
+
|
10
|
+
RSpec::Matchers.define :be_same_error_as do |other_err|
|
11
|
+
match do |err|
|
12
|
+
err.line == other_err.line and err.column == other_err.column and
|
13
|
+
err.message == other_err.message and err.formatted_message ==
|
14
|
+
other_err.formatted_message
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
NEEDED_PLUGINS = [:syntax_checker, :autosave, :ruby_runner, :ruby_development]
|
19
|
+
|
20
|
+
before :all do
|
21
|
+
NEEDED_PLUGINS.each do |pl|
|
22
|
+
dir = File.join 'plugins', pl.to_s
|
23
|
+
Ruber[:components].load_plugin dir
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
Ruber[:components].load_plugin 'plugins/ruby_syntax_checker'
|
29
|
+
end
|
30
|
+
|
31
|
+
after do
|
32
|
+
Ruber[:components].unload_plugin :ruby_syntax_checker if Ruber[:ruby_syntax_checker]
|
33
|
+
end
|
34
|
+
|
35
|
+
after :all do
|
36
|
+
NEEDED_PLUGINS.reverse_each{|pl| Ruber[:components].unload_plugin pl}
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'when created' do
|
40
|
+
|
41
|
+
before do
|
42
|
+
@file = Tempfile.open ['', '.rb']
|
43
|
+
end
|
44
|
+
|
45
|
+
after do
|
46
|
+
@file.close
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'registers a syntax checker of class Ruber::RubySyntaxChecker::Checker for files ending in .rb with the syntax checker' do
|
50
|
+
checker = Ruber::RubySyntaxChecker::Checker.new flexmock
|
51
|
+
flexmock(Ruber::RubySyntaxChecker::Checker).should_receive(:new).once.with(Ruber::Document).and_return checker
|
52
|
+
doc = Ruber[:world].document @file.path
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'when unloaded' do
|
58
|
+
|
59
|
+
before do
|
60
|
+
@file = Tempfile.open ['', '.rb']
|
61
|
+
end
|
62
|
+
|
63
|
+
after do
|
64
|
+
@file.close
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'removes the Ruber::RubySyntaxChecker::Checker syntax checker' do
|
68
|
+
Ruber[:components].unload_plugin :ruby_syntax_checker
|
69
|
+
flexmock(Ruber::RubySyntaxChecker::Checker).should_receive(:new).never
|
70
|
+
doc = Ruber[:world].document @file.path
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe Ruber::RubySyntaxChecker::Checker do
|
78
|
+
NEEDED_PLUGINS = [:syntax_checker, :autosave, :ruby_runner, :ruby_development]
|
79
|
+
|
80
|
+
before :all do
|
81
|
+
NEEDED_PLUGINS.each do |pl|
|
82
|
+
dir = File.join 'plugins', pl.to_s
|
83
|
+
Ruber[:components].load_plugin dir
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
after :all do
|
88
|
+
NEEDED_PLUGINS.reverse_each{|pl| Ruber[:components].unload_plugin pl}
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#check_syntax' do
|
92
|
+
|
93
|
+
before do
|
94
|
+
@file = Tempfile.open ['', '.rb']
|
95
|
+
@doc = Ruber[:world].document @file.path
|
96
|
+
@checker = Ruber::RubySyntaxChecker::Checker.new @doc
|
97
|
+
end
|
98
|
+
|
99
|
+
after do
|
100
|
+
@doc.close false
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when the first argument is valid ruby' do
|
104
|
+
|
105
|
+
it 'returns nil' do
|
106
|
+
str = <<-EOS
|
107
|
+
class X
|
108
|
+
def y
|
109
|
+
if z
|
110
|
+
1 + 2
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
EOS
|
115
|
+
@doc.text = str
|
116
|
+
@checker.check_syntax(str, true).should be_nil
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when the first argument is not valid ruby' do
|
122
|
+
|
123
|
+
it 'returns an array of Ruber::RubySyntaxChecker::SyntaxError with the line of the error if the column is unknown' do
|
124
|
+
str = <<-EOS
|
125
|
+
class X
|
126
|
+
def y
|
127
|
+
1 +
|
128
|
+
end
|
129
|
+
end
|
130
|
+
EOS
|
131
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(3, nil, 'unexpected keyword_end', 'unexpected keyword_end')
|
132
|
+
@checker.check_syntax(str, false)[0].should be_same_error_as(exp)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'determines the column number from lines containing all spaces and a single ^' do
|
136
|
+
str = <<-EOS
|
137
|
+
def x
|
138
|
+
Y = 1
|
139
|
+
end
|
140
|
+
EOS
|
141
|
+
@doc.text = str
|
142
|
+
@checker.check_syntax(str, false)[0].column.should == 5
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'attempts to compute the correct position if the error line before the one with the ^ starts with ...' do
|
146
|
+
pending 'find an error which causes the above situation'
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when ruby reports a syntax error' do
|
150
|
+
|
151
|
+
error_types = [
|
152
|
+
[:missing_end, "ruby reports a missing end keyword", 'class X;def y;end'],
|
153
|
+
[:extra_end, "ruby reports an unexpected end keyword at EOF", 'class X;def y;end;end;end'],
|
154
|
+
[:misplaced_end, "ruby reports an unexpected end keyword not at EOF", 'def x;1+end'],
|
155
|
+
[:missing_close_paren, "ruby reports an expected )", 'def x;1*(2+1;end'],
|
156
|
+
[:extra_close_paren, "ruby reports an unexpected )", 'x;1*2+1)'],
|
157
|
+
[:missing_close_bracket, "ruby reports an expected ]", 'x=['],
|
158
|
+
[:extra_close_bracket, "ruby reports an unexpected ]", 'x=]'],
|
159
|
+
[:missing_close_brace, "ruby reports an expected }", 'x={'],
|
160
|
+
[:extra_close_brace, "ruby reports an unexpected }", 'x=}'],
|
161
|
+
[:extra_else, "ruby reports an unexpected else", "x;else;end"],
|
162
|
+
[:missing_quote, "ruby reports an unterminated string", "def x;'"],
|
163
|
+
[:missing_regexp_close_paren, "ruby reports unmatched open parenthesis in regexp", '/xy ( a/'],
|
164
|
+
[:extra_regexp_close_paren, "ruby reports unmatched close parenthesis in regexp", '/xy ) a/'],
|
165
|
+
[:missing_regexp_close_bracket, "ruby reports premature end of char-class", '/ xy [/'],
|
166
|
+
[:unknown_regexp_option, "ruby reports an unknown regexp option", '/a/t'],
|
167
|
+
[:dynamic_constant_assignment , "ruby reports a dynamic constant assignment", 'def x;X=2;end'],
|
168
|
+
[:extra_when, "ruby reports an unexpected when keyword", 'when 2 then 3'],
|
169
|
+
[:extra_rescue, "ruby reports an unexpected rescue keyword", 'rescue'],
|
170
|
+
[:extra_ensure, "ruby reports an unexpected ensure keyword", 'ensure'],
|
171
|
+
[:missing_block_comment_end, "ruby reports an embedded document meets end of file", '=begin'],
|
172
|
+
[:missing_heredoc_end, "ruby reports it can't find the end of a heredoc", "str=<<EOS"]
|
173
|
+
]
|
174
|
+
|
175
|
+
error_types.each do |type, cond, code|
|
176
|
+
it "sets the error_type attribute of the error object to #{type} if #{cond}" do
|
177
|
+
errors = @checker.check_syntax(code, false)
|
178
|
+
errors[0].error_type.should == type
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'works correctly when ruby reports unmatched open parenthesis in a regexp' do
|
185
|
+
str = '/xy ( a/'
|
186
|
+
@doc.text = str
|
187
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(0, nil, 'end pattern with unmatched parenthesis: /xy ( a/', 'end pattern with unmatched parenthesis: /xy ( a/')
|
188
|
+
@checker.check_syntax(str, false)[0].should be_same_error_as(exp)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'works correctly when ruby reports the premature end of a character class' do
|
192
|
+
str = '/xy [ a/'
|
193
|
+
@doc.text = str
|
194
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(0, nil, 'premature end of char-class: /xy [ a/', 'premature end of char-class: /xy [ a/')
|
195
|
+
@checker.check_syntax(str, false)[0].should be_same_error_as(exp)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'works correctly when ruby reports an invalid regexp option' do
|
199
|
+
str = '/xy/t'
|
200
|
+
@doc.text = str
|
201
|
+
exp = [Ruber::RubySyntaxChecker::SyntaxError.new(0, nil, 'unknown regexp option - t', 'unknown regexp option - t')]
|
202
|
+
@checker.check_syntax(str, false).should be_same_error_as(exp)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'works correctly when ruby reports a class definition in a method body' do
|
206
|
+
str = <<-EOS
|
207
|
+
def x
|
208
|
+
class Y
|
209
|
+
end
|
210
|
+
end
|
211
|
+
EOS
|
212
|
+
@doc.text = str
|
213
|
+
msg = 'class definition in method body'
|
214
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(1, nil, msg, msg)
|
215
|
+
@checker.check_syntax(str, false)[0].should == exp
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'works correctly when ruby reports a module definition in a method body' do
|
219
|
+
str = <<-EOS
|
220
|
+
def x
|
221
|
+
module Y
|
222
|
+
end
|
223
|
+
end
|
224
|
+
EOS
|
225
|
+
@doc.text = str
|
226
|
+
msg = 'module definition in method body '
|
227
|
+
exp = [Ruber::RubySyntaxChecker::SyntaxError.new(1, nil, msg, msg)]
|
228
|
+
@checker.check_syntax(str, false).should == exp
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'works correctly when ruby reports an unterminated string' do
|
232
|
+
@doc.text = 'def x;"'
|
233
|
+
msg = "unterminated string meets end of file "
|
234
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(0, nil, msg, msg)
|
235
|
+
@checker.check_syntax(@doc.text, false)[0].should be_same_error_as(exp)
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'works correctly when ruby reports an unterminated block comment' do
|
239
|
+
@doc.text = '=begin x'
|
240
|
+
msg = "embedded document meets end of file "
|
241
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(0, nil, msg, msg)
|
242
|
+
@checker.check_syntax(@doc.text, false)[0].should be_same_error_as(exp)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'works correctly when ruby reports an unterminated heredoc' do
|
246
|
+
@doc.text = "str=<<EOS"
|
247
|
+
msg = 'can\'t find string "EOS" anywhere before EOF '
|
248
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(0, nil, msg, msg)
|
249
|
+
@checker.check_syntax(@doc.text, false)[0].should be_same_error_as(exp)
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'works correctly when ruby reports a dynamic constant assignment' do
|
253
|
+
str = <<-EOS
|
254
|
+
def x
|
255
|
+
Y = 1
|
256
|
+
end
|
257
|
+
EOS
|
258
|
+
@doc.text = str
|
259
|
+
msg = "dynamic constant assignment \n Y = 1"
|
260
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(1, 5, msg, msg)
|
261
|
+
@checker.check_syntax(str, false)[0].should be_same_error_as(exp)
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'works correctly with unknown syntax errors' do
|
265
|
+
error_msg = '-e:10:xyz'
|
266
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
267
|
+
exp = Ruber::RubySyntaxChecker::SyntaxError.new(nil, nil, error_msg, error_msg)
|
268
|
+
@checker.check_syntax('', false)[0].should be_same_error_as(exp)
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'uses the ruby interpreter stored in the ruby/ruby project option' do
|
274
|
+
# To check whether the correct interpreter is being used, a syntax which is
|
275
|
+
# correct in ruby 1.8 and incorrect in 1.9 is used
|
276
|
+
@doc.own_project[:ruby, :ruby] = '/usr/bin/ruby18'
|
277
|
+
str = <<-EOS
|
278
|
+
if x: y
|
279
|
+
else z
|
280
|
+
end
|
281
|
+
EOS
|
282
|
+
@doc.text = str
|
283
|
+
@checker.check_syntax(str, false).should be_nil
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'raises Ruber::SyntaxChecker::SyntaxNotChecked if the given interpreter doesn\'t exist' do
|
287
|
+
@doc.own_project[:ruby, :ruby] = '/usr/bin/nonexisting-ruby'
|
288
|
+
lambda{@checker.check_syntax('', false)}.should raise_error(Ruber::SyntaxChecker::SyntaxNotChecked)
|
289
|
+
end
|
290
|
+
|
291
|
+
context 'if the second argument is true' do
|
292
|
+
|
293
|
+
it 'replaces instances of "expected $end" with "expected end of file" in the formatter error message' do
|
294
|
+
error_msg = '-e:12: syntax error, expected $end'
|
295
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
296
|
+
res = @checker.check_syntax('', true)
|
297
|
+
res[0].formatted_message.should == 'expected end of file'
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'replaces instances of "expecting $end" with "expecting end of file" in the formatter error message' do
|
301
|
+
error_msg = '-e:12: syntax error, expecting $end'
|
302
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
303
|
+
res = @checker.check_syntax('', true)
|
304
|
+
res[0].formatted_message.should == 'expecting end of file'
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'replaces instances of "unexpected $end" with "unexpected end of file" in the formatter error message' do
|
308
|
+
error_msg = '-e:12: syntax error, unexpected $end'
|
309
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
310
|
+
res = @checker.check_syntax('', true)
|
311
|
+
res[0].formatted_message.should == 'unexpected end of file'
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'replaces instances of "expected kEND" with "expected `end` keyword" in the formatter error message' do
|
315
|
+
error_msg = '-e:12: syntax error, expected kEND'
|
316
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
317
|
+
res = @checker.check_syntax('', true)
|
318
|
+
res[0].formatted_message.should == 'expected `end` keyword'
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'replaces instances of "expecting kEND" with "expecting `end` keyword" in the formatter error message' do
|
322
|
+
error_msg = '-e:12: syntax error, expecting kEND'
|
323
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
324
|
+
res = @checker.check_syntax('', true)
|
325
|
+
res[0].formatted_message.should == 'expecting `end` keyword'
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'replaces instances of "unexpected kEND" with "unexpected `end` keyword" in the formatter error message' do
|
329
|
+
error_msg = '-e:12: syntax error, unexpected kEND'
|
330
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
331
|
+
res = @checker.check_syntax('', true)
|
332
|
+
res[0].formatted_message.should == 'unexpected `end` keyword'
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'replaces instances of "expected keyword_end" with "expected `end` keyword" in the formatter error message' do
|
336
|
+
error_msg = '-e:12: syntax error, expected keyword_end'
|
337
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
338
|
+
res = @checker.check_syntax('', true)
|
339
|
+
res[0].formatted_message.should == 'expected `end` keyword'
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'replaces instances of "expecting keyword_end" with "expecting `end` keyword" in the formatter error message' do
|
343
|
+
error_msg = '-e:12: syntax error, expecting keyword_end'
|
344
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
345
|
+
res = @checker.check_syntax('', true)
|
346
|
+
res[0].formatted_message.should == 'expecting `end` keyword'
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'replaces instances of "unexpected keyword_end" with "unexpected `end` keyword" in the formatter error message' do
|
350
|
+
error_msg = '-e:12: syntax error, unexpected keyword_end'
|
351
|
+
flexmock(Open3).should_receive(:popen3).once.and_return error_msg
|
352
|
+
res = @checker.check_syntax('', true)
|
353
|
+
res[0].formatted_message.should == 'unexpected `end` keyword'
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
end
|
@@ -0,0 +1,1132 @@
|
|
1
|
+
require 'spec/framework'
|
2
|
+
require 'spec/common'
|
3
|
+
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
require 'plugins/syntax_checker/syntax_checker'
|
7
|
+
|
8
|
+
describe Ruber::SyntaxChecker::Plugin do
|
9
|
+
|
10
|
+
before do
|
11
|
+
Ruber[:world].close_all :documents, :discard
|
12
|
+
Ruber[:components].load_plugin 'plugins/syntax_checker/'
|
13
|
+
@plug = Ruber[:syntax_checker]
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
Ruber[:world].close_all :documents, :discard
|
18
|
+
Ruber[:components].unload_plugin :syntax_checker
|
19
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
20
|
+
led.delete_later
|
21
|
+
Qt::Application.send_posted_events led, Qt::Event::DeferredDelete
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'inherits Ruber::Plugin' do
|
25
|
+
Ruber::SyntaxChecker::Plugin.ancestors.should include(Ruber::Plugin)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when created' do
|
29
|
+
|
30
|
+
it 'has no registered syntax checkers' do
|
31
|
+
@plug.instance_variable_get(:@syntax_checkers).should == {}
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'inserts a KDE::Led in the status bar' do
|
35
|
+
Ruber[:main_window].status_bar.find_children(KDE::Led).should_not be_empty
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'sets the current status to :unknown' do
|
39
|
+
@plug.current_status.should == :unknown
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when unloaded' do
|
45
|
+
|
46
|
+
it 'removes the led from the status bar' do
|
47
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
48
|
+
led_id = led.object_id
|
49
|
+
flexmock(Ruber[:main_window].status_bar).should_receive(:remove_widget).with(FlexMock.on{|arg| arg.object_id == led_id}).once
|
50
|
+
#The plugin will be unloaded in the after block
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'format_error' do
|
56
|
+
|
57
|
+
context 'if the argument\'s line, column and formatted_message methods all return non nil' do
|
58
|
+
|
59
|
+
it 'returns a string of the form "Line lineno+1, column columnno+1: formatted_message"' do
|
60
|
+
err = OpenStruct.new :line => 5, :column => 3, :formatted_message => 'Xyz'
|
61
|
+
@plug.format_error(err).should == 'Line 6, column 4: Xyz'
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'if the argument\'s line and formatted_message methods all return non nil but the column method returns nil' do
|
67
|
+
|
68
|
+
it 'returns a string of the form "Line lineno+1: formatted_message"' do
|
69
|
+
err = OpenStruct.new :line => 5, :formatted_message => 'Xyz'
|
70
|
+
@plug.format_error(err).should == 'Line 6: Xyz'
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'if the line method of the argument returns nil and the formatted_message method does not' do
|
76
|
+
|
77
|
+
it 'returns a string containing only the formatted message' do
|
78
|
+
err = OpenStruct.new :formatted_message => 'Xyz'
|
79
|
+
@plug.format_error(err).should == 'Xyz'
|
80
|
+
err = OpenStruct.new :column => 4, :formatted_message => 'Xyz'
|
81
|
+
@plug.format_error(err).should == 'Xyz'
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'if the formatted_message method of the argument returns nil' do
|
87
|
+
|
88
|
+
it 'uses the value returned by the message method of the argument if not nil' do
|
89
|
+
err = OpenStruct.new :line => 5, :column => 3, :message => 'Xyz'
|
90
|
+
@plug.format_error(err).should == 'Line 6, column 4: Xyz'
|
91
|
+
err = OpenStruct.new :line => 5, :message => 'Xyz'
|
92
|
+
@plug.format_error(err).should == 'Line 6: Xyz'
|
93
|
+
err = OpenStruct.new :message => 'Xyz'
|
94
|
+
@plug.format_error(err).should == 'Xyz'
|
95
|
+
err = OpenStruct.new :column => 5, :message => 'Xyz'
|
96
|
+
@plug.format_error(err).should == 'Xyz'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'uses the string "UNKNOWN ERROR" if the message method also returns nil' do
|
100
|
+
err = OpenStruct.new :line => 5, :column => 3
|
101
|
+
@plug.format_error(err).should == 'Line 6, column 4: UNKNOWN ERROR'
|
102
|
+
err = OpenStruct.new :line => 5
|
103
|
+
@plug.format_error(err).should == 'Line 6: UNKNOWN ERROR'
|
104
|
+
err = OpenStruct.new
|
105
|
+
@plug.format_error(err).should == 'UNKNOWN ERROR'
|
106
|
+
err = OpenStruct.new :column => 5
|
107
|
+
@plug.format_error(err).should == 'UNKNOWN ERROR'
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'translates all messages using KDE.i18n' do
|
113
|
+
flexmock(KDE).should_receive(:i18n).once.with('Line %d').and_return('Riga %d')
|
114
|
+
flexmock(KDE).should_receive(:i18n).once.with(', column %d: ').and_return(', colonna %d: ')
|
115
|
+
flexmock(KDE).should_receive(:i18n).once.with('Xyz').and_return('Abc')
|
116
|
+
flexmock(KDE).should_receive(:i18n).once.with('UNKNOWN ERROR').and_return('ERRORE SCONOSCIUTO')
|
117
|
+
err = OpenStruct.new :line => 5, :column => 3, :formatted_message => 'Xyz'
|
118
|
+
@plug.format_error(err).should == 'Riga 6, colonna 4: Abc'
|
119
|
+
@plug.format_error(OpenStruct.new).should == 'ERRORE SCONOSCIUTO'
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#set_current_status' do
|
125
|
+
|
126
|
+
context 'if the first argument is :correct' do
|
127
|
+
|
128
|
+
it 'sets the current_errors attribute to nil' do
|
129
|
+
@plug.instance_variable_set :@current_errors, []
|
130
|
+
@plug.set_current_status :correct
|
131
|
+
@plug.current_errors.should be_nil
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'sets the led to the color contained in the COLORS hash under the :correct key' do
|
135
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
136
|
+
led.color = Qt::Color.new Qt.black
|
137
|
+
@plug.set_current_status :correct
|
138
|
+
led.color.should == Ruber::SyntaxChecker::Plugin::COLORS[:correct]
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'sets the tooltip of the led to "No syntax errors"' do
|
142
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
143
|
+
@plug.set_current_status :correct
|
144
|
+
led.tool_tip.should == "No syntax errors"
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'sets the current_status attribute to :correct' do
|
148
|
+
@plug.set_current_status :correct
|
149
|
+
@plug.current_status.should == :correct
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'if the first argument is :unknown' do
|
155
|
+
|
156
|
+
it 'sets the current_errors attribute to nil' do
|
157
|
+
@plug.instance_variable_set :@current_errors, []
|
158
|
+
@plug.set_current_status :unknown
|
159
|
+
@plug.current_errors.should be_nil
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'sets the led to the color contained in the COLORS hash under the :unknown key' do
|
163
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
164
|
+
led.color = Qt::Color.new Qt.black
|
165
|
+
@plug.set_current_status :unknown
|
166
|
+
led.color.should == Ruber::SyntaxChecker::Plugin::COLORS[:unknown]
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'sets the tooltip of the led to "Unknown document type"' do
|
170
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
171
|
+
@plug.set_current_status :unknown
|
172
|
+
led.tool_tip.should == "Unknown document type"
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'sets the current_status attribute to :unknown' do
|
176
|
+
@plug.set_current_status :unknown
|
177
|
+
@plug.current_status.should == :unknown
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'if the first argument is :incorrect' do
|
183
|
+
|
184
|
+
before do
|
185
|
+
@errors = [
|
186
|
+
OpenStruct.new( :line => 15, :column => 2, :message => 'xyz', :formatted_message=> 'XYZ'),
|
187
|
+
OpenStruct.new( :line => 15, :column => 0, :message => 'abc', :formatted_message=> 'ABC'),
|
188
|
+
]
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'and the second argument is an array with at least one element' do
|
192
|
+
|
193
|
+
it 'stores the contents of the second argument in the current_errors attribute' do
|
194
|
+
@plug.set_current_status :incorrect, @errors
|
195
|
+
@plug.current_errors.should == @errors
|
196
|
+
@plug.current_errors.should_not equal(@errors)
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'sets the tooltip of the led to a list of the formatted_message for the errors given as second argument, preceded by lines and columns (increased by 1)' do
|
200
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
201
|
+
@plug.set_current_status :incorrect, @errors
|
202
|
+
led.tool_tip.should == "Line 16, column 3: XYZ\nLine 16, column 1: ABC"
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'and the second argument is an empty array' do
|
208
|
+
|
209
|
+
it 'stores an empty array in the current_errors attribute' do
|
210
|
+
@plug.set_current_status :incorrect, []
|
211
|
+
@plug.current_errors.should == []
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'sets the tooltip of the led to "There are syntax errors"' do
|
215
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[-1]
|
216
|
+
@plug.set_current_status :incorrect, []
|
217
|
+
led.tool_tip.should == "There are syntax errors"
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'sets the led to the color contained in the COLORS hash under the :incorrect key' do
|
223
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
224
|
+
led.color = Qt::Color.new Qt.black
|
225
|
+
@plug.set_current_status :incorrect, @errors
|
226
|
+
led.color.should == Ruber::SyntaxChecker::Plugin::COLORS[:incorrect]
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'sets the current_status attribute to :incorrect' do
|
230
|
+
@plug.set_current_status :incorrect
|
231
|
+
@plug.current_status.should == :incorrect
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'when the user right-clicks on the led' do
|
239
|
+
|
240
|
+
before do
|
241
|
+
@led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
242
|
+
end
|
243
|
+
|
244
|
+
context 'if the current_status attribute is :correct' do
|
245
|
+
|
246
|
+
it 'displays a menu with the entry "No syntax errors"' do
|
247
|
+
@plug.set_current_status :correct
|
248
|
+
actions_prc = Proc.new do |list|
|
249
|
+
list.count == 1 && list[0].text == 'No syntax errors'
|
250
|
+
end
|
251
|
+
flexmock(Qt::Menu).should_receive(:exec).once.with(FlexMock.on(&actions_prc), Qt::Point)
|
252
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
253
|
+
Qt::Application.send_event(@led, ev)
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
context 'if the current_status attribute is :unknown' do
|
259
|
+
|
260
|
+
it 'doesn\'t display any menu' do
|
261
|
+
@plug.set_current_status :unknown
|
262
|
+
flexmock(Qt::Menu).should_receive(:exec).never
|
263
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
264
|
+
Qt::Application.send_event(@led, ev)
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
context 'if the current_status attribute is :incorrect' do
|
270
|
+
|
271
|
+
context 'and the current_errors attribute is empty' do
|
272
|
+
|
273
|
+
it 'displays a menu with the entry "There are syntax errors"' do
|
274
|
+
@plug.set_current_status :incorrect
|
275
|
+
actions_prc = Proc.new do |list|
|
276
|
+
list.count == 1 && list[0].text == 'There are syntax errors'
|
277
|
+
end
|
278
|
+
flexmock(Qt::Menu).should_receive(:exec).once.with(FlexMock.on(&actions_prc), Qt::Point)
|
279
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
280
|
+
Qt::Application.send_event(@led, ev)
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
context 'and the current_errors attribute is not empty' do
|
286
|
+
|
287
|
+
before do
|
288
|
+
@errors = [
|
289
|
+
OpenStruct.new(:line => 15, :column => 2, :message => 'xyz', :formatted_message => 'XYZ'),
|
290
|
+
OpenStruct.new(:line => 15, :mesage => 'abc', :formatted_message => 'ABC'),
|
291
|
+
]
|
292
|
+
@plug.set_current_status :incorrect, @errors
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'displays a menu with an entry for each error (increasing lines and columns by 1)' do
|
296
|
+
exp_texts = ["Line 16, column 3: XYZ", "Line 16: ABC"]
|
297
|
+
actions_prc = Proc.new{|list| list.map{|a| a.text} == exp_texts}
|
298
|
+
flexmock(Qt::Menu).should_receive(:exec).once.with(FlexMock.on(&actions_prc), Qt::Point)
|
299
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
300
|
+
Qt::Application.send_event(@led, ev)
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
context 'when the user clicks on an entry in the context menu' do
|
310
|
+
|
311
|
+
before do
|
312
|
+
@led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
313
|
+
@docs = Array.new(3){Ruber[:world].new_document}
|
314
|
+
@views = @docs.map{|d| Ruber[:world].active_environment.editor_for! d}
|
315
|
+
Ruber[:world].active_environment.activate_editor @views[0]
|
316
|
+
end
|
317
|
+
|
318
|
+
context 'and the current_errors attribute contains errors' do
|
319
|
+
|
320
|
+
before do
|
321
|
+
@errors = [
|
322
|
+
OpenStruct.new(:line => 15, :column => 2, :message => 'xyz', :formatted_message=> 'XYZ'),
|
323
|
+
OpenStruct.new(:line => 15, :message => 'abc', :formatted_message => 'ABC'),
|
324
|
+
OpenStruct.new(:message =>'fgh'),
|
325
|
+
]
|
326
|
+
@plug.set_current_status :incorrect, @errors
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'moves the cursor of the current view to the line and column of the error corresponding to the chosen entry' do
|
330
|
+
actions = @errors.map{|e| KDE::Action.new(e.format, nil)}
|
331
|
+
actions.each do |a|
|
332
|
+
flexmock(KDE::Action).should_receive(:new).once.and_return(a)
|
333
|
+
end
|
334
|
+
flexmock(Qt::Menu).should_receive(:exec).once.and_return(actions[0])
|
335
|
+
flexmock(@views[0]).should_receive(:go_to).with(15, 2).once
|
336
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
337
|
+
Qt::Application.send_event(@led, ev)
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'moves the cursor to column 0 of the appropriate line if the error doesn\'t specify a column number' do
|
341
|
+
actions = @errors.map{|e| KDE::Action.new(e.format, nil)}
|
342
|
+
actions.each do |a|
|
343
|
+
flexmock(KDE::Action).should_receive(:new).once.and_return(a)
|
344
|
+
end
|
345
|
+
flexmock(Qt::Menu).should_receive(:exec).once.and_return(actions[1])
|
346
|
+
flexmock(@views[0]).should_receive(:go_to).with(15, 0).once
|
347
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
348
|
+
Qt::Application.send_event(@led, ev)
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'doesn\'t move the cursor if the error doesn\'t specify a line number' do
|
352
|
+
actions = @errors.map{|e| KDE::Action.new(e.format, nil)}
|
353
|
+
actions.each do |a|
|
354
|
+
flexmock(KDE::Action).should_receive(:new).once.and_return(a)
|
355
|
+
end
|
356
|
+
flexmock(Qt::Menu).should_receive(:exec).once.and_return(actions[2])
|
357
|
+
flexmock(@views[0]).should_receive(:go_to).never
|
358
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
359
|
+
Qt::Application.send_event(@led, ev)
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
363
|
+
|
364
|
+
context 'and the current_errors attribute is nil or empty' do
|
365
|
+
|
366
|
+
it 'does nothing' do
|
367
|
+
action = KDE::Action.new 'Text', nil
|
368
|
+
flexmock(KDE::Action).should_receive(:new).and_return(action)
|
369
|
+
flexmock(Qt::Menu).should_receive(:exec).and_return(action)
|
370
|
+
flexmock(@views[0]).should_receive(:go_to).never
|
371
|
+
@plug.set_current_status :correct
|
372
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
373
|
+
Qt::Application.send_event(@led, ev)
|
374
|
+
@plug.set_current_status :unknown
|
375
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
376
|
+
Qt::Application.send_event(@led, ev)
|
377
|
+
@plug.set_current_status :incorrect
|
378
|
+
ev = Qt::ContextMenuEvent.new Qt::ContextMenuEvent::Mouse, Qt::Point.new(0,0)
|
379
|
+
Qt::Application.send_event(@led, ev)
|
380
|
+
end
|
381
|
+
|
382
|
+
end
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
context 'when the user clicks on the led' do
|
387
|
+
|
388
|
+
before do
|
389
|
+
@led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
390
|
+
@docs = Array.new(3){Ruber[:world].new_document}
|
391
|
+
@views = @docs.map{|d| Ruber[:world].active_environment.editor_for! d}
|
392
|
+
Ruber[:world].active_environment.activate_editor @views[0]
|
393
|
+
end
|
394
|
+
|
395
|
+
context 'and the current_errors attribute contains errors' do
|
396
|
+
|
397
|
+
before do
|
398
|
+
@errors = [
|
399
|
+
OpenStruct.new(:line => 15, :column => 2, :message => 'xyz', :formatted_message=> 'XYZ'),
|
400
|
+
OpenStruct.new(:line => 15, :message => 'abc', :formatted_message => 'ABC'),
|
401
|
+
OpenStruct.new(:message =>'fgh'),
|
402
|
+
]
|
403
|
+
@plug.set_current_status :incorrect, @errors
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'moves the cursor to the line and column associated with the first error' do
|
407
|
+
flexmock(@views[0]).should_receive(:go_to).with(15, 2).once
|
408
|
+
ev = Qt::MouseEvent.new Qt::Event::MouseButtonRelease, Qt::Point.new(0,0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier
|
409
|
+
Qt::Application.send_event(@led, ev)
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'moves the cursor to column 0 of the appropriate line if the error doesn\'t specify a column number' do
|
413
|
+
@errors[0].column = nil
|
414
|
+
flexmock(@views[0]).should_receive(:go_to).with(15, 0).once
|
415
|
+
ev = Qt::MouseEvent.new Qt::Event::MouseButtonRelease, Qt::Point.new(0,0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier
|
416
|
+
Qt::Application.send_event(@led, ev)
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'does nothing if the first error doesn\'t specify a line number' do
|
420
|
+
@errors[0].line = nil
|
421
|
+
flexmock(@views[0]).should_receive(:go_to).never
|
422
|
+
ev = Qt::MouseEvent.new Qt::Event::MouseButtonRelease, Qt::Point.new(0,0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier
|
423
|
+
Qt::Application.send_event(@led, ev)
|
424
|
+
end
|
425
|
+
|
426
|
+
end
|
427
|
+
|
428
|
+
context 'and the current_errors attribute is nil or empty' do
|
429
|
+
|
430
|
+
it 'does nothing' do
|
431
|
+
flexmock(@views[0]).should_receive(:go_to).never
|
432
|
+
@plug.set_current_status :correct
|
433
|
+
ev = Qt::MouseEvent.new Qt::Event::MouseButtonRelease, Qt::Point.new(0,0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier
|
434
|
+
Qt::Application.send_event(@led, ev)
|
435
|
+
@plug.set_current_status :unknown
|
436
|
+
ev = Qt::MouseEvent.new Qt::Event::MouseButtonRelease, Qt::Point.new(0,0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier
|
437
|
+
Qt::Application.send_event(@led, ev)
|
438
|
+
@plug.set_current_status :incorrect
|
439
|
+
ev = Qt::MouseEvent.new Qt::Event::MouseButtonRelease, Qt::Point.new(0,0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier
|
440
|
+
Qt::Application.send_event(@led, ev)
|
441
|
+
end
|
442
|
+
|
443
|
+
end
|
444
|
+
|
445
|
+
end
|
446
|
+
|
447
|
+
describe '#register_syntax_checker' do
|
448
|
+
|
449
|
+
before do
|
450
|
+
@cls = Class.new
|
451
|
+
@mimetypes = %w[application/x-ruby text/x-python]
|
452
|
+
@patterns = %w[*.rb *.py]
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'stores the given class with the associated rules' do
|
456
|
+
@plug.register_syntax_checker @cls, @mimetypes, @patterns
|
457
|
+
@plug.instance_variable_get(:@syntax_checkers)[@cls].should == [@mimetypes, @patterns]
|
458
|
+
end
|
459
|
+
|
460
|
+
it 'emits the syntax_checker_added signal' do
|
461
|
+
mk = flexmock{|m| m.should_receive(:syntax_checker_added).once}
|
462
|
+
@plug.connect(SIGNAL(:syntax_checker_added)){mk.syntax_checker_added}
|
463
|
+
@plug.register_syntax_checker @cls, @mimetypes, @patterns
|
464
|
+
end
|
465
|
+
|
466
|
+
it 'raises ArgumentError if the given syntax checker has already been registered' do
|
467
|
+
cls = Class.new
|
468
|
+
mimetypes = %w[application/x-ruby]
|
469
|
+
patterns = %w[*.rb]
|
470
|
+
@plug.register_syntax_checker cls, mimetypes, patterns
|
471
|
+
lambda{@plug.register_syntax_checker cls, ['text/plain']}.should raise_error(ArgumentError, "#{cls} has already been registered as syntax checker")
|
472
|
+
end
|
473
|
+
|
474
|
+
end
|
475
|
+
|
476
|
+
describe '#syntax_checker_for' do
|
477
|
+
|
478
|
+
context 'if a syntax checker for which the document\'s file_type_match? method returns true exists' do
|
479
|
+
|
480
|
+
it 'returns the syntax checker class' do
|
481
|
+
doc = Ruber[:world].new_document
|
482
|
+
checkers = Array.new(2){Class.new}
|
483
|
+
@plug.register_syntax_checker checkers[0], ['text/x-python']
|
484
|
+
@plug.register_syntax_checker checkers[1], ['application/x-ruby']
|
485
|
+
flexmock(doc).should_receive(:file_type_match?).with(['text/x-python'], []).and_return false
|
486
|
+
flexmock(doc).should_receive(:file_type_match?).with(['application/x-ruby'], []).and_return true
|
487
|
+
checker = @plug.syntax_checker_for doc
|
488
|
+
checker.should == checkers[1]
|
489
|
+
end
|
490
|
+
|
491
|
+
it 'returns the first syntax checker with matches if there are more than one of them' do
|
492
|
+
doc = Ruber[:world].new_document
|
493
|
+
checkers = Array.new(2){Class.new}
|
494
|
+
@plug.register_syntax_checker checkers[0], ['text/x-python']
|
495
|
+
@plug.register_syntax_checker checkers[1], ['application/x-ruby']
|
496
|
+
flexmock(doc).should_receive(:file_type_match?).with(['text/x-python'], []).and_return true
|
497
|
+
flexmock(doc).should_receive(:file_type_match?).with(['application/x-ruby'], []).and_return true
|
498
|
+
checker = @plug.syntax_checker_for doc
|
499
|
+
checker.should == checkers[0]
|
500
|
+
end
|
501
|
+
|
502
|
+
end
|
503
|
+
|
504
|
+
context 'if the document\'s file_type_match? method returns false for all syntax checkers' do
|
505
|
+
|
506
|
+
it 'returns nil' do
|
507
|
+
doc = Ruber[:world].new_document
|
508
|
+
checkers = Array.new(2){Class.new}
|
509
|
+
@plug.register_syntax_checker checkers[0], ['text/x-python']
|
510
|
+
@plug.register_syntax_checker checkers[1], ['application/x-ruby']
|
511
|
+
flexmock(doc).should_receive(:file_type_match?).with(['text/x-python'], []).and_return false
|
512
|
+
flexmock(doc).should_receive(:file_type_match?).with(['application/x-ruby'], []).and_return false
|
513
|
+
checker = @plug.syntax_checker_for doc
|
514
|
+
checker.should be_nil
|
515
|
+
end
|
516
|
+
|
517
|
+
end
|
518
|
+
|
519
|
+
end
|
520
|
+
|
521
|
+
describe '#remove_syntax_checker' do
|
522
|
+
|
523
|
+
before do
|
524
|
+
@cls = Class.new
|
525
|
+
@plug.register_syntax_checker @cls, ['application/x-ruby']
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'removes the given syntax checker from the list' do
|
529
|
+
@plug.remove_syntax_checker @cls
|
530
|
+
@plug.instance_variable_get(:@syntax_checkers).should_not include(@cls)
|
531
|
+
end
|
532
|
+
|
533
|
+
it 'emits the syntax_checker_removed signal' do
|
534
|
+
mk = flexmock{|m| m.should_receive(:syntax_checker_removed).once}
|
535
|
+
@plug.connect(SIGNAL(:syntax_checker_removed)){mk.syntax_checker_removed}
|
536
|
+
@plug.remove_syntax_checker @cls
|
537
|
+
end
|
538
|
+
|
539
|
+
it 'does nothing if the given syntax checker has not been registered' do
|
540
|
+
cls = Class.new
|
541
|
+
mk = flexmock{|m| m.should_receive(:syntax_checker_removed).never}
|
542
|
+
@plug.connect(SIGNAL(:syntax_checker_removed)){mk.syntax_checker_removed}
|
543
|
+
lambda{@plug.remove_syntax_checker cls}.should_not raise_error
|
544
|
+
end
|
545
|
+
|
546
|
+
end
|
547
|
+
|
548
|
+
describe '#load_settings ' do
|
549
|
+
|
550
|
+
it 'emits the :settings_changed signal' do
|
551
|
+
mk = flexmock{|m| m.should_receive(:settings_changed).once}
|
552
|
+
@plug.connect(SIGNAL(:settings_changed)){mk.settings_changed}
|
553
|
+
@plug.load_settings
|
554
|
+
end
|
555
|
+
|
556
|
+
end
|
557
|
+
|
558
|
+
context 'when the syntax checker extension complete a syntax check' do
|
559
|
+
|
560
|
+
before do
|
561
|
+
@doc = Ruber[:world].document __FILE__
|
562
|
+
@ext = @doc.extension(:syntax_checker)
|
563
|
+
@errors = [OpenStruct.new(:line => 2, :column => 5, :message => 'X', :formatted_message=> 'x')]
|
564
|
+
end
|
565
|
+
|
566
|
+
after do
|
567
|
+
@doc.close false
|
568
|
+
end
|
569
|
+
|
570
|
+
it 'updates the current state accordingly if the extension is associated with the active document' do
|
571
|
+
flexmock(@doc).should_receive(:active?).and_return true
|
572
|
+
flexmock(@ext).should_receive(:status).and_return(:incorrect)
|
573
|
+
flexmock(@ext).should_receive(:errors).and_return @errors
|
574
|
+
flexmock(@plug).should_receive(:set_current_status).once.with(:incorrect, @errors)
|
575
|
+
@ext.instance_eval{emit syntax_checked(@doc)}
|
576
|
+
end
|
577
|
+
|
578
|
+
it 'does nothing if the extension is not associated with the active document' do
|
579
|
+
flexmock(@doc).should_receive(:active?).and_return false
|
580
|
+
flexmock(@ext).should_receive(:status).and_return(:incorrect)
|
581
|
+
flexmock(@ext).should_receive(:errors).and_return @errors
|
582
|
+
flexmock(@plug).should_receive(:set_current_status).never
|
583
|
+
@ext.instance_eval{emit syntax_checked(@doc)}
|
584
|
+
end
|
585
|
+
|
586
|
+
end
|
587
|
+
|
588
|
+
end
|
589
|
+
|
590
|
+
describe Ruber::SyntaxChecker::Extension do
|
591
|
+
|
592
|
+
before do
|
593
|
+
Ruber[:world].close_all :documents, :discard
|
594
|
+
Ruber[:components].load_plugin 'plugins/syntax_checker/'
|
595
|
+
@plug = Ruber[:syntax_checker]
|
596
|
+
@file = Tempfile.new ['', '.rb']
|
597
|
+
@doc = Ruber[:world].document @file.path
|
598
|
+
@ext = @doc.own_project.extension(:syntax_checker)
|
599
|
+
end
|
600
|
+
|
601
|
+
after do
|
602
|
+
Ruber[:components].unload_plugin :syntax_checker
|
603
|
+
led = Ruber[:main_window].status_bar.find_children(KDE::Led)[0]
|
604
|
+
led.delete_later
|
605
|
+
Qt::Application.send_posted_events led, Qt::Event::DeferredDelete
|
606
|
+
@file.close
|
607
|
+
end
|
608
|
+
|
609
|
+
it 'inherits Qt::Object' do
|
610
|
+
Ruber::SyntaxChecker::Extension.ancestors.should include(Qt::Object)
|
611
|
+
end
|
612
|
+
|
613
|
+
it 'includes the Ruber::Extension module' do
|
614
|
+
Ruber::SyntaxChecker::Extension.ancestors.should include(Ruber::Extension)
|
615
|
+
end
|
616
|
+
|
617
|
+
context 'when created' do
|
618
|
+
|
619
|
+
it 'creates a new syntax checker as appropriate for the document' do
|
620
|
+
cls = Class.new{def initialize doc;end; def check_syntax *args;end}
|
621
|
+
flexmock(Ruber[:syntax_checker]).should_receive(:syntax_checker_for).with(@doc).once.and_return cls
|
622
|
+
ext = Ruber::SyntaxChecker::Extension.new @doc.own_project
|
623
|
+
ext.instance_variable_get(:@checker).should be_a(cls)
|
624
|
+
end
|
625
|
+
|
626
|
+
it 'doesn\'t attempt to create a syntax checker if there is none suitable for the document associated with the extension' do
|
627
|
+
doc = Ruber[:world].new_document
|
628
|
+
cls = Class.new{def check_syntax *args;end}
|
629
|
+
flexmock(Ruber[:syntax_checker]).should_receive(:syntax_checker_for).with(doc).once.and_return nil
|
630
|
+
flexmock(doc.own_project).should_receive(:[]).with(:syntax_checker, :auto_check).and_return true
|
631
|
+
ext = Ruber::SyntaxChecker::Extension.new doc.own_project
|
632
|
+
ext.instance_variable_get(:@checker).should be_nil
|
633
|
+
end
|
634
|
+
|
635
|
+
it 'reads the syntax_checker/time_interval option from the config' do
|
636
|
+
flexmock(Ruber[:config]).should_receive(:[]).with(:syntax_checker, :time_interval).once.and_return 10
|
637
|
+
ext = Ruber::SyntaxChecker::Extension.new @doc.own_project
|
638
|
+
end
|
639
|
+
|
640
|
+
it 'has the errors attribute set to nil' do
|
641
|
+
@ext.errors.should be_nil
|
642
|
+
end
|
643
|
+
|
644
|
+
it 'has the status attribute set to unknown' do
|
645
|
+
@ext.status.should == :unknown
|
646
|
+
end
|
647
|
+
|
648
|
+
context 'if the document is active' do
|
649
|
+
|
650
|
+
before do
|
651
|
+
flexmock(@doc).should_receive(:active?).and_return true
|
652
|
+
end
|
653
|
+
|
654
|
+
it 'creates a single shot timer' do
|
655
|
+
ext = Ruber::SyntaxChecker::Extension.new @doc.own_project
|
656
|
+
timer = ext.instance_variable_get :@timer
|
657
|
+
timer.should be_a Qt::Timer
|
658
|
+
timer.singleShot.should be_true
|
659
|
+
end
|
660
|
+
|
661
|
+
end
|
662
|
+
|
663
|
+
end
|
664
|
+
|
665
|
+
describe 'when the document\'s URL changes' do
|
666
|
+
|
667
|
+
before do
|
668
|
+
@cls = Class.new{def check_syntax *args;end}
|
669
|
+
flexmock(Ruber[:syntax_checker]).should_receive(:syntax_checker_for).by_default.and_return @cls
|
670
|
+
end
|
671
|
+
|
672
|
+
it 'creates a new syntax checker if needed' do
|
673
|
+
@doc.instance_eval{emit document_url_changed(self)}
|
674
|
+
@ext.instance_variable_get(:@checker).should be_a(@cls)
|
675
|
+
end
|
676
|
+
|
677
|
+
it 'removes the old syntax checker if no valid syntax checkers are found' do
|
678
|
+
@ext.instance_variable_set :@checker, @cls.new
|
679
|
+
flexmock(Ruber[:syntax_checker]).should_receive(:syntax_checker_for).once.and_return nil
|
680
|
+
@doc.instance_eval{emit document_url_changed(self)}
|
681
|
+
@ext.instance_variable_get(:@checker).should be_nil
|
682
|
+
end
|
683
|
+
|
684
|
+
end
|
685
|
+
|
686
|
+
context 'when the plugin emits the syntax_checker_added signal' do
|
687
|
+
|
688
|
+
context 'and the extension has no syntax checker' do
|
689
|
+
|
690
|
+
it 'creates a syntax checker if possible' do
|
691
|
+
cls = Class.new{def check_syntax *args;end}
|
692
|
+
flexmock(Ruber[:syntax_checker]).should_receive(:syntax_checker_for).with(@doc).once.and_return cls
|
693
|
+
@plug.instance_eval{emit syntax_checker_added}
|
694
|
+
@ext.instance_variable_get(:@checker).should be_a(cls)
|
695
|
+
end
|
696
|
+
|
697
|
+
end
|
698
|
+
|
699
|
+
context 'and the extension alredy has a syntax checker' do
|
700
|
+
|
701
|
+
it 'does nothing' do
|
702
|
+
@ext.instance_variable_set :@checker, Object.new
|
703
|
+
flexmock(Ruber[:syntax_checker]).should_receive(:syntax_checker_for).with(@doc).never
|
704
|
+
@plug.instance_eval{emit syntax_checker_added}
|
705
|
+
end
|
706
|
+
|
707
|
+
end
|
708
|
+
|
709
|
+
end
|
710
|
+
|
711
|
+
context 'when the plugin emits the syntax_checker_removed signal' do
|
712
|
+
|
713
|
+
it 'attempts to create a new syntax checker if possible' do
|
714
|
+
cls = Class.new{def check_syntax *args;end}
|
715
|
+
flexmock(Ruber[:syntax_checker]).should_receive(:syntax_checker_for).with(@doc).once.and_return cls
|
716
|
+
@plug.instance_eval{emit syntax_checker_removed}
|
717
|
+
@ext.instance_variable_get(:@checker).should be_a(cls)
|
718
|
+
end
|
719
|
+
|
720
|
+
end
|
721
|
+
|
722
|
+
context 'when the syntax checker is changed' do
|
723
|
+
|
724
|
+
context 'and the syntax_checker/auto_check document option is true' do
|
725
|
+
|
726
|
+
before do
|
727
|
+
@doc.own_project[:syntax_checker, :auto_check] = true
|
728
|
+
end
|
729
|
+
|
730
|
+
it 'performs a syntax check' do
|
731
|
+
cls = Class.new
|
732
|
+
flexmock(@plug).should_receive(:syntax_checker_for).and_return cls
|
733
|
+
mk = flexmock{|m| m.should_receive(:check_syntax).once}
|
734
|
+
flexmock(cls).should_receive(:new).once.and_return mk
|
735
|
+
@plug.instance_eval{emit syntax_checker_added}
|
736
|
+
end
|
737
|
+
|
738
|
+
end
|
739
|
+
|
740
|
+
context 'and the syntax_checker/auto_check document option is false' do
|
741
|
+
|
742
|
+
before do
|
743
|
+
@doc.own_project[:syntax_checker, :auto_check] = false
|
744
|
+
end
|
745
|
+
|
746
|
+
it 'performs a syntax check' do
|
747
|
+
cls = Class.new
|
748
|
+
flexmock(@plug).should_receive(:syntax_checker_for).and_return cls
|
749
|
+
mk = flexmock{|m| m.should_receive(:check_syntax).never}
|
750
|
+
flexmock(cls).should_receive(:new).once.and_return mk
|
751
|
+
@plug.instance_eval{emit syntax_checker_added}
|
752
|
+
end
|
753
|
+
|
754
|
+
end
|
755
|
+
|
756
|
+
end
|
757
|
+
|
758
|
+
describe '#check_syntax' do
|
759
|
+
|
760
|
+
context 'if there\'s a syntax checker' do
|
761
|
+
|
762
|
+
before do
|
763
|
+
@checker = flexmock{|mk| mk.should_receive(:check_syntax).by_default}
|
764
|
+
@ext.instance_variable_set :@checker, @checker
|
765
|
+
end
|
766
|
+
|
767
|
+
it 'calls the check_syntax method of the checker passing it the text of the document and the value of the :format key' do
|
768
|
+
@doc.text = 'xyz'
|
769
|
+
@checker.should_receive(:check_syntax).once.with 'xyz', true
|
770
|
+
@ext.check_syntax :format => true
|
771
|
+
end
|
772
|
+
|
773
|
+
context 'and the #check_syntax method of the syntax checker returns nil' do
|
774
|
+
|
775
|
+
it 'returns a hash with the :result entry set to :correct and the :errors entry set to nil' do
|
776
|
+
@checker.should_receive(:check_syntax).and_return nil
|
777
|
+
@ext.check_syntax.should == {:result => :correct, :errors => nil}
|
778
|
+
end
|
779
|
+
|
780
|
+
end
|
781
|
+
|
782
|
+
context 'and the #check_syntax method of the syntax checker returns an array' do
|
783
|
+
|
784
|
+
it 'returns a hash with the :result entry set to :incorrect and the :errors entry set to the array' do
|
785
|
+
errors = [OpenStruct.new(:line => 0, :column => 2)]
|
786
|
+
@checker.should_receive(:check_syntax).and_return errors
|
787
|
+
@ext.check_syntax.should == {:result => :incorrect, :errors => errors}
|
788
|
+
end
|
789
|
+
|
790
|
+
end
|
791
|
+
|
792
|
+
context 'and the #check_syntax method of the syntax checker raises Ruber::SyntaxChecker::SyntaxNotChecked' do
|
793
|
+
|
794
|
+
it 'returns a hash with the :result entry set to :unknown and the :errors entry set to nil' do
|
795
|
+
flexmock(@checker).should_receive(:check_syntax).and_raise(Ruber::SyntaxChecker::SyntaxNotChecked)
|
796
|
+
@ext.check_syntax.should == {:result => :unknown, :errors => nil}
|
797
|
+
end
|
798
|
+
|
799
|
+
end
|
800
|
+
|
801
|
+
end
|
802
|
+
|
803
|
+
context 'and there isn\'t a syntax checker' do
|
804
|
+
|
805
|
+
it 'doesn\'t attempt to call the check_syntax method of the syntax checker' do
|
806
|
+
lambda{@ext.check_syntax}.should_not raise_error
|
807
|
+
end
|
808
|
+
|
809
|
+
it 'returns a hash with the :result entry set to :unknown and the :errors entry set to nil' do
|
810
|
+
@ext.check_syntax.should == {:result => :unknown, :errors => nil}
|
811
|
+
end
|
812
|
+
|
813
|
+
end
|
814
|
+
|
815
|
+
context 'if the :format and :update options are true' do
|
816
|
+
|
817
|
+
before do
|
818
|
+
@checker = flexmock{|mk| mk.should_receive(:check_syntax).by_default}
|
819
|
+
@ext.instance_variable_set :@checker, @checker
|
820
|
+
end
|
821
|
+
|
822
|
+
it 'sets the :status entry to the same value of the :result entry in the returned hash' do
|
823
|
+
@checker.should_receive(:check_syntax).once.and_return nil
|
824
|
+
@checker.should_receive(:check_syntax).once.and_return []
|
825
|
+
@ext.check_syntax :format => true, :update => true
|
826
|
+
@ext.status.should == :correct
|
827
|
+
@ext.check_syntax :format => true, :update => true
|
828
|
+
@ext.status.should == :incorrect
|
829
|
+
@ext.instance_variable_set :@checker, nil
|
830
|
+
@ext.check_syntax :format => true, :update => true
|
831
|
+
@ext.status.should == :unknown
|
832
|
+
end
|
833
|
+
|
834
|
+
it 'sets the errors attribute to the value returned by the #check_syntax method of the checker' do
|
835
|
+
errors = [
|
836
|
+
OpenStruct.new(:line => 0, :column => 2),
|
837
|
+
OpenStruct.new(:line => 1, :column => 0),
|
838
|
+
]
|
839
|
+
flexmock(@checker).should_receive(:check_syntax).once.and_return errors
|
840
|
+
flexmock(@checker).should_receive(:check_syntax).once.and_return nil
|
841
|
+
@ext.check_syntax
|
842
|
+
@ext.errors.should == errors
|
843
|
+
@ext.check_syntax
|
844
|
+
@ext.errors.should be_nil
|
845
|
+
end
|
846
|
+
|
847
|
+
it 'sets the errors attribute to nil if there\'s no syntax checker' do
|
848
|
+
@ext.instance_variable_set :@checker, nil
|
849
|
+
@ext.check_syntax :format => true, :update => true
|
850
|
+
@ext.errors.should be_nil
|
851
|
+
end
|
852
|
+
|
853
|
+
it 'emits the syntax_checked signal passing the document as argument' do
|
854
|
+
mk = flexmock{|m| m.should_receive(:syntax_checked).once.with(@doc)}
|
855
|
+
@ext.connect(SIGNAL('syntax_checked(QObject*)')){|doc| mk.syntax_checked doc}
|
856
|
+
@ext.check_syntax
|
857
|
+
end
|
858
|
+
|
859
|
+
context 'and the #check_syntax method of the checker returns non-nil' do
|
860
|
+
|
861
|
+
it 'adds a mark of a custom type for each found error to the document' do
|
862
|
+
pending "Use marks only when KTextEditor::MarkInterface actually works"
|
863
|
+
@doc.text = "abc\ncde\nfgh"
|
864
|
+
errors = [
|
865
|
+
Ruber::SyntaxChecker::SyntaxError.new(0,2),
|
866
|
+
Ruber::SyntaxChecker::SyntaxError.new(1,0)
|
867
|
+
]
|
868
|
+
flexmock(@checker).should_receive(:check_syntax).and_return errors
|
869
|
+
@ext.check_syntax
|
870
|
+
iface = @doc.interface('mark_interface')
|
871
|
+
(iface.mark(0) & @ext.class::SyntaxErrorMark).should_not == 0
|
872
|
+
(iface.mark(1) & @ext.class::SyntaxErrorMark).should_not == 0
|
873
|
+
(iface.mark(2) & @ext.class::SyntaxErrorMark).should == 0
|
874
|
+
end
|
875
|
+
|
876
|
+
it 'doesn\'t add marks for errors without a line number' do
|
877
|
+
pending "Use marks only when KTextEditor::MarkInterface#marks actually works"
|
878
|
+
@doc.text = "abc\ncde\nfgh"
|
879
|
+
checker = Object.new
|
880
|
+
@ext.instance_variable_set :@checker, checker
|
881
|
+
errors = [
|
882
|
+
Ruber::SyntaxChecker::SyntaxError.new(0,2),
|
883
|
+
Ruber::SyntaxChecker::SyntaxError.new(nil, 2)
|
884
|
+
]
|
885
|
+
flexmock(checker).should_receive(:check_syntax).and_return errors
|
886
|
+
@ext.check_syntax
|
887
|
+
iface = @doc.interface('mark_interface')
|
888
|
+
(iface.mark(0) & @ext.class::SyntaxErrorMark).should_not == 0
|
889
|
+
(iface.mark(1) & @ext.class::SyntaxErrorMark).should == 0
|
890
|
+
(iface.mark(2) & @ext.class::SyntaxErrorMark).should == 0
|
891
|
+
end
|
892
|
+
|
893
|
+
end
|
894
|
+
|
895
|
+
context 'and the #check_syntax method of the checker returns nil' do
|
896
|
+
|
897
|
+
it 'clears all the marks put by the extension' do
|
898
|
+
pending "Use marks only when KTextEditor::MarkInterface actually works"
|
899
|
+
@doc.text = "abc\ncde\nfgh"
|
900
|
+
iface = @doc.interface('mark_interface')
|
901
|
+
iface.add_mark 0, @ext.class::SyntaxErrorMark
|
902
|
+
iface.add_mark 2, @ext.class::SyntaxErrorMark
|
903
|
+
flexmock(@checker).should_receive(:check_syntax).and_return nil
|
904
|
+
iface.marks.should be_empty
|
905
|
+
end
|
906
|
+
|
907
|
+
end
|
908
|
+
|
909
|
+
end
|
910
|
+
|
911
|
+
shared_examples_for 'the syntax checker extension when it should not update after a syntax check' do
|
912
|
+
|
913
|
+
before do
|
914
|
+
@checker = Object.new
|
915
|
+
flexmock(@checker).should_receive(:check_syntax).by_default
|
916
|
+
@ext.instance_variable_set :@checker, @checker
|
917
|
+
end
|
918
|
+
|
919
|
+
it 'doesn\'t change the marks' do
|
920
|
+
pending "Use marks only when KTextEditor::MarkInterface actually works"
|
921
|
+
end
|
922
|
+
|
923
|
+
it 'doesn\'t emit the syntax_checked signal' do
|
924
|
+
mk = flexmock{|m| m.should_receive(:syntax_checked).never}
|
925
|
+
@ext.connect(SIGNAL('syntax_checked(QObject*)')){|doc| mk.syntax_checked doc}
|
926
|
+
@ext.check_syntax @options
|
927
|
+
end
|
928
|
+
|
929
|
+
it 'doesn\'t change the errors attribute' do
|
930
|
+
errors = [
|
931
|
+
OpenStruct.new(:line => 0, :column => 2),
|
932
|
+
OpenStruct.new(:column => 2)
|
933
|
+
]
|
934
|
+
@ext.instance_variable_set :@errors, errors
|
935
|
+
flexmock(@checker).should_receive(:check_syntax).and_return nil
|
936
|
+
@ext.check_syntax @options
|
937
|
+
@ext.errors.should == errors
|
938
|
+
end
|
939
|
+
|
940
|
+
end
|
941
|
+
|
942
|
+
context 'when the :format option is false' do
|
943
|
+
|
944
|
+
before do
|
945
|
+
@options = {:format => false, :update => true}
|
946
|
+
end
|
947
|
+
|
948
|
+
it_behaves_like 'the syntax checker extension when it should not update after a syntax check'
|
949
|
+
|
950
|
+
end
|
951
|
+
|
952
|
+
context 'when the :update option is false' do
|
953
|
+
|
954
|
+
before do
|
955
|
+
@options = {:format => true, :update => false}
|
956
|
+
end
|
957
|
+
|
958
|
+
it_behaves_like 'the syntax checker extension when it should not update after a syntax check'
|
959
|
+
|
960
|
+
end
|
961
|
+
|
962
|
+
end
|
963
|
+
|
964
|
+
shared_examples_for 'the syntax checker extension doing an auto syntax check' do
|
965
|
+
|
966
|
+
context 'if the syntax_checker/check document option is true' do
|
967
|
+
|
968
|
+
before do
|
969
|
+
@doc.own_project[:syntax_checker, :auto_check] = true
|
970
|
+
end
|
971
|
+
|
972
|
+
it 'checks the document syntax passing no arguments to #check_syntax' do
|
973
|
+
flexmock(@ext).should_receive(:check_syntax).once.with_no_args
|
974
|
+
@proc.call
|
975
|
+
end
|
976
|
+
|
977
|
+
end
|
978
|
+
|
979
|
+
context 'if the syntax_checker/auto_check document option is false' do
|
980
|
+
|
981
|
+
before do
|
982
|
+
@doc.own_project[:syntax_checker, :auto_check] = false
|
983
|
+
end
|
984
|
+
|
985
|
+
it 'doesn\'t check the document syntax' do
|
986
|
+
flexmock(@ext).should_receive(:check_syntax).never
|
987
|
+
@proc.call
|
988
|
+
end
|
989
|
+
|
990
|
+
end
|
991
|
+
|
992
|
+
end
|
993
|
+
|
994
|
+
context 'when the document is saved' do
|
995
|
+
|
996
|
+
before do
|
997
|
+
@proc = Proc.new{@doc.instance_eval{emit document_saved_or_uploaded(self, false)}}
|
998
|
+
end
|
999
|
+
|
1000
|
+
it_behaves_like 'the syntax checker extension doing an auto syntax check'
|
1001
|
+
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
context 'when the document becomes active' do
|
1005
|
+
|
1006
|
+
before do
|
1007
|
+
@proc = Proc.new{@doc.activate}
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
it_behaves_like 'the syntax checker extension doing an auto syntax check'
|
1011
|
+
|
1012
|
+
it 'creates a single shot timer' do
|
1013
|
+
@doc.activate
|
1014
|
+
timer = @ext.instance_variable_get :@timer
|
1015
|
+
timer.should be_a(Qt::Timer)
|
1016
|
+
timer.singleShot.should be_true
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
context 'when the document becomes inactive' do
|
1022
|
+
|
1023
|
+
before do
|
1024
|
+
@doc.activate
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
it 'stops the timer' do
|
1028
|
+
flexmock(@ext.instance_variable_get(:@timer)).should_receive(:stop).once
|
1029
|
+
@doc.deactivate
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
it 'calls the #delete_later method of the timer' do
|
1033
|
+
flexmock(@ext.instance_variable_get(:@timer)).should_receive(:delete_later).once
|
1034
|
+
@doc.deactivate
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
it 'removes the timer' do
|
1038
|
+
@doc.deactivate
|
1039
|
+
@ext.instance_variable_get(:@timer).should be_nil
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
context 'when the plugin emits the settings_changed signal' do
|
1045
|
+
|
1046
|
+
it 'reads the syntax_checker/time_interval option from the config' do
|
1047
|
+
flexmock(Ruber[:config]).should_receive(:[]).with(:syntax_checker, :time_interval).once.and_return 10
|
1048
|
+
@plug.instance_eval{emit settings_changed}
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
context 'when the text in the document changes' do
|
1054
|
+
|
1055
|
+
context 'and the document is active' do
|
1056
|
+
|
1057
|
+
before do
|
1058
|
+
@doc.deactivate
|
1059
|
+
@doc.activate
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
context 'and the time interval is greater than 0' do
|
1063
|
+
|
1064
|
+
it 'starts the timer using the given time interval multiplied by 1000' do
|
1065
|
+
@ext.instance_variable_set :@time_interval, 10
|
1066
|
+
timer = @ext.instance_variable_get :@timer
|
1067
|
+
flexmock(timer).should_receive(:start).with(10_000).once
|
1068
|
+
@doc.text = 'y'
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
context 'and the time interval is 0' do
|
1074
|
+
|
1075
|
+
it 'doesn\'t start the timer' do
|
1076
|
+
@ext.instance_variable_set :@time_interval, 0
|
1077
|
+
timer = @ext.instance_variable_get :@timer
|
1078
|
+
flexmock(timer).should_receive(:start).never
|
1079
|
+
@doc.text = 'x'
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
context 'and the document is not active' do
|
1088
|
+
|
1089
|
+
before do
|
1090
|
+
@doc.deactivate
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
it 'doesn\'t attempt to start the timer' do
|
1094
|
+
@ext.instance_variable_set :@time_interval, 10
|
1095
|
+
timer = @ext.instance_variable_get :@timer
|
1096
|
+
flexmock(timer).should_receive(:start).never
|
1097
|
+
@doc.text = 'x'
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
context 'when the timer times out' do
|
1105
|
+
|
1106
|
+
it 'calls the auto_check method' do
|
1107
|
+
@doc.activate
|
1108
|
+
flexmock(@ext).should_receive(:auto_check).once
|
1109
|
+
@ext.instance_variable_get(:@timer).instance_eval{emit timeout}
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
describe '#remove_from_project' do
|
1115
|
+
|
1116
|
+
it 'stops the timer' do
|
1117
|
+
@doc.activate
|
1118
|
+
flexmock(@ext.instance_variable_get(:@timer)).should_receive(:stop).once
|
1119
|
+
@ext.remove_from_project
|
1120
|
+
#needed becouse remove_from_project is called in the after block when
|
1121
|
+
#unloading the plugin. Without this, the method is called twice
|
1122
|
+
flexmock(@ext).should_receive(:remove_from_project)
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
it 'doesn\'t stop the timer if it doesn\'t exist' do
|
1126
|
+
@ext.instance_variable_set(:@timer, nil)
|
1127
|
+
lambda{@ext.remove_from_project}.should_not raise_error
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
end
|