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.
Files changed (73) hide show
  1. data/CHANGES +42 -1
  2. data/lib/ruber/application/application.rb +25 -5
  3. data/lib/ruber/application/plugin.yaml +2 -10
  4. data/lib/ruber/component_manager.rb +2 -2
  5. data/lib/ruber/document_project.rb +5 -3
  6. data/lib/ruber/editor/document.rb +5 -4
  7. data/lib/ruber/editor/ktexteditor_wrapper.rb +1 -1
  8. data/lib/ruber/exception_widgets.rb +1 -1
  9. data/lib/ruber/main_window/main_window.rb +4 -3
  10. data/lib/ruber/main_window/main_window_actions.rb +2 -2
  11. data/lib/ruber/main_window/main_window_internal.rb +1 -1
  12. data/lib/ruber/output_widget.rb +17 -5
  13. data/lib/ruber/project.rb +34 -3
  14. data/lib/ruber/project_dir_scanner.rb +171 -0
  15. data/lib/ruber/settings_container.rb +7 -7
  16. data/lib/ruber/settings_dialog.rb +24 -24
  17. data/lib/ruber/version.rb +1 -1
  18. data/lib/ruber/world/environment.rb +1 -0
  19. data/lib/ruber/world/plugin.yaml +7 -2
  20. data/lib/ruber/{application → world}/project_files_widget.rb +0 -0
  21. data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.rb +2 -2
  22. data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.ui +0 -0
  23. data/lib/ruber/{application → world}/ui/project_files_widget.rb +2 -2
  24. data/lib/ruber/{application → world}/ui/project_files_widget.ui +0 -0
  25. data/plugins/auto_end/auto_end.rb +21 -18
  26. data/plugins/autosave/autosave.rb +1 -1
  27. data/plugins/find_in_files/find_in_files.rb +1 -1
  28. data/plugins/irb/irb.png +0 -0
  29. data/plugins/irb/irb.rb +142 -0
  30. data/plugins/irb/irb.svg +240 -0
  31. data/plugins/irb/irb_controller.rb +541 -0
  32. data/plugins/irb/irbrc.rb +21 -0
  33. data/plugins/irb/plugin.yaml +19 -0
  34. data/plugins/irb/ui/irb_config_widget.rb +151 -0
  35. data/plugins/irb/ui/irb_config_widget.ui +123 -0
  36. data/plugins/irb/ui/irb_tool_widget.rb +97 -0
  37. data/plugins/irb/ui/irb_tool_widget.ui +86 -0
  38. data/plugins/project_browser/project_browser.rb +1 -1
  39. data/plugins/rspec/plugin.yaml +6 -3
  40. data/plugins/rspec/rspec.rb +172 -473
  41. data/plugins/rspec/tool_widget.rb +462 -0
  42. data/plugins/rspec/ui/rspec_project_widget.rb +58 -38
  43. data/plugins/rspec/ui/rspec_project_widget.ui +68 -64
  44. data/plugins/ruberri/class_formatter.rb +126 -0
  45. data/plugins/ruberri/method_formatter.rb +90 -0
  46. data/plugins/ruberri/plugin.yaml +13 -0
  47. data/plugins/ruberri/ruberri.rb +226 -0
  48. data/plugins/ruberri/search.rb +111 -0
  49. data/plugins/ruberri/ui/tool_widget.rb +73 -0
  50. data/plugins/ruberri/ui/tool_widget.ui +49 -0
  51. data/plugins/ruby_runner/ruby_runner.rb +2 -2
  52. data/plugins/ruby_syntax_checker/plugin.yaml +11 -0
  53. data/plugins/ruby_syntax_checker/ruby_syntax_checker.rb +147 -0
  54. data/plugins/syntax_checker/plugin.yaml +10 -6
  55. data/plugins/syntax_checker/syntax_checker.rb +216 -520
  56. data/plugins/syntax_checker/ui/config_widget.rb +61 -0
  57. data/plugins/syntax_checker/ui/config_widget.ui +44 -0
  58. data/plugins/yaml_syntax_checker/plugin.yaml +11 -0
  59. data/plugins/yaml_syntax_checker/yaml_syntax_checker.rb +62 -0
  60. data/ruber.desktop +0 -0
  61. data/spec/auto_end_spec.rb +224 -186
  62. data/spec/document_project_spec.rb +9 -1
  63. data/spec/document_spec.rb +9 -0
  64. data/spec/environment_spec.rb +12 -0
  65. data/spec/output_widget_spec.rb +69 -2
  66. data/spec/project_dir_scanner_spec.rb +195 -0
  67. data/spec/project_spec.rb +43 -73
  68. data/spec/ruby_syntax_checker_spec.rb +361 -0
  69. data/spec/syntax_checker_spec.rb +1132 -0
  70. data/spec/yaml_syntax_checker_spec.rb +130 -0
  71. metadata +232 -225
  72. data/lib/ruber/application/project_files_list.rb +0 -320
  73. 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