ruber 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
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
data/CHANGES CHANGED
@@ -1,9 +1,48 @@
1
1
  h1. Changes
2
2
 
3
- h2. Ruber 0.0.9
3
+ h2. Ruber 0.0.10
4
4
 
5
5
  h3. Features
6
6
 
7
+ * Added a RI plugin. You can now insert the class or method name in the RI tool widget
8
+ and see the documentation in a nice HTML format inside the tool widget itself.
9
+ This plugin requires RDoc version 3.x (it isn't tested with version 2.x).
10
+ * Added an IRB plugin
11
+ * The syntax checker plugin has been rewritten. Now other plugins can check the
12
+ syntax of a document
13
+ * Ruby and YAML syntax checker now are in their own plugin, rather than being part
14
+ of the syntax checker plugin
15
+ * The auto end plugin doesn't use indentation anymore to decide whether the end keyword
16
+ should be inserted or not. Instead, it performs a syntax check and only inserts
17
+ end if ruby reports a missing end keyword. This should improve the accuracy of
18
+ the plugin
19
+ * The Auto End plugin now inserts the @end@ keyword after a @case@ expression
20
+ * Clicking on a file name in an output widget with the middle mouse button now
21
+ prevents the tool widget from being hidden, as if the pin button were clicked
22
+ * Changed the interface to specify patterns in the RSpec plugin
23
+ * The RSpec plugin now supports specs inside subdirectories. You can also specify
24
+ subdirectories inside the pattern to match code file and the respective spec
25
+ file
26
+ * When choosing the Run Specs for Current File action in the RSpec plugin, if the current
27
+ document is associated with a code file, all the specs associated with it are
28
+ run (previously, only one of them was run)
29
+ * When choosing the Run Current Spec action in the RSpec plugin, if the current
30
+ document is a code file, the plugin will look at the most recently activated
31
+ editor containing a spec file for the current file to determine which is the
32
+ current spec.
33
+ * The behaviour of output widgets when clicking on a file name which requires
34
+ opening a new editor has changed. The choice made by the user to open the editor
35
+ in the current tab by splitting the current editor is ignored if the current
36
+ tab already contains more than one editor. The reason is that usually there
37
+ won't be enough space on the sceen to comfortably accomodate more than two
38
+ editors.
39
+
40
+ h3. Bug fixes
41
+
42
+ * Do not crash when closing with unsaved files
43
+ * Horizontal scrollbar in the RSpec tool widget will be displayed when the spec
44
+ description is larger than the widget but the rest of the text isn't
45
+
7
46
  h2. Ruber 0.0.9
8
47
 
9
48
  h3. Features
@@ -20,6 +59,8 @@ h3. Bug fixes
20
59
  * The RSpec plugin now correctly displays pending examples
21
60
  * Fixed a crash when creating a new document and saving it as a ruby file while
22
61
  using the Syntax Checker plugin
62
+ * When clicking on a file name in an output widget, give focus to the editor instead
63
+ of keeping it in the tool widget
23
64
 
24
65
  h2. Ruber 0.0.8
25
66
 
@@ -62,7 +62,8 @@ The default plugins to load
62
62
 
63
63
  Currently, they are: ruby_development, find_in_files, syntax_checker, command and state
64
64
  =end
65
- DEFAULT_PLUGINS = %w[ruby_development find_in_files rake command syntax_checker state auto_end project_browser]
65
+ DEFAULT_PLUGINS = %w[ruby_development find_in_files rake command syntax_checker
66
+ state auto_end project_browser ruby_syntax_checker yaml_syntax_checker]
66
67
 
67
68
  include PluginLike
68
69
 
@@ -74,7 +75,6 @@ Currently, they are: ruby_development, find_in_files, syntax_checker, command an
74
75
  attr_reader :cmd_line_options
75
76
 
76
77
  =begin rdoc
77
- <<<<<<< HEAD
78
78
  =======
79
79
  The state Ruber is in
80
80
 
@@ -87,7 +87,6 @@ Ruber can be in three states:
87
87
  - *quitting*:= from when the user chooses to quit Ruber onwards
88
88
  - *asking_to_quit*:= while asking the user to confirm quitting Ruber
89
89
 
90
- >>>>>>> master
91
90
  @return [Symbol] the status of the application. It can be: @:starting@, @:running@
92
91
  or @:quitting@
93
92
  =end
@@ -117,6 +116,7 @@ and is fired as soon as the event loop starts.
117
116
  @plugin_dirs = []
118
117
  load_core_components
119
118
  @status = :starting
119
+ @chdir_lock = Mutex.new
120
120
  Qt::Timer.single_shot(0, self, SLOT(:setup))
121
121
  end
122
122
 
@@ -251,6 +251,25 @@ or sorting dependencies.
251
251
  @components.load_plugins plugins, dirs || @plugin_dirs, &blk
252
252
 
253
253
  end
254
+
255
+ =begin rdoc
256
+ Thread-safe Dir.chdir
257
+
258
+ @Dir.chdir@ is not thread-safe, meaning that a Dir.chdir done in a thread will
259
+ change the directory in all other threads. To avoid this unpleasant behaviour,
260
+ don't use Dir.chdir. Instead, use this method, which uses a mutex to control calls
261
+ to Dir.chdir.
262
+ @param [String] dir the directory to change to
263
+ @yield the block to execute from within _dir_. The current directory will
264
+ be changed back after executing the block.
265
+ @raise [LocalJumpError] if no block is given
266
+ @return [Object] the value returned by the block otherwise
267
+ =end
268
+ def chdir dir
269
+ @chdir_lock.synchronize do
270
+ Dir.chdir(dir){yield}
271
+ end
272
+ end
254
273
 
255
274
  private
256
275
 
@@ -304,7 +323,7 @@ At the end, the main window is shown
304
323
  #{e.message}
305
324
 
306
325
  Ruber will start with no plugin loaded. Please, use the Choose Plugins menu entry in the Settings menu to solve the issue.
307
- EOS
326
+ EOS
308
327
  end
309
328
  begin
310
329
  availlable_plugins = ComponentManager.find_plugins @plugin_dirs, true
@@ -316,7 +335,7 @@ At the end, the main window is shown
316
335
  Ruber couldn't find some plugins it has been configured to automatically load at startup. They are:
317
336
  #{missing.join("\n")}
318
337
  Do you want to start the application without them or to quit Ruber?
319
- EOS
338
+ EOS
320
339
  ans = KDE::MessageBox.question_yes_no nil, question, 'Missing plugins',
321
340
  KDE::GuiItem.new('Start Ruber'), KDE::GuiItem.new('Quit')
322
341
  exit if ans == KDE::MessageBox::No
@@ -397,6 +416,7 @@ necessary to to them later
397
416
  def register_with_config
398
417
  config = Ruber[:config]
399
418
  @plugin_description.config_options.each_value{|o| config.add_option o}
419
+ @plugin_description.config_widgets.each{|w| config.add_widget w}
400
420
  load_settings
401
421
  connect config, SIGNAL(:settings_changed), self, SLOT(:load_settings)
402
422
  nil
@@ -1,16 +1,8 @@
1
1
  name: app
2
2
  description: The application itself
3
- require: [application, project_files_widget, project_files_list]
3
+ require: [application]
4
4
  class: 'Ruber::Application'
5
5
  config_options:
6
6
  :general:
7
7
  :plugin_dirs: {:default: Ruber::Application::DEFAULT_PLUGIN_PATHS}
8
- :plugins: {:default: Ruber::Application::DEFAULT_PLUGINS}
9
- :auto_load_project: {:default: false }
10
- config_widgets:
11
- - {:caption: General, :pixmap: configure, :code: "w=Qt::CheckBox.new('&Open last project at startup');w.object_name='kcfg_general_auto_load_project';w"}
12
- project_options:
13
- general:
14
- project_files: {default: {:include: [], :exclude: [], :extensions: ["*.rb"]}}
15
- extensions:
16
- project_files: {class: ProjectFilesList}
8
+ :plugins: {:default: Ruber::Application::DEFAULT_PLUGINS}
@@ -429,7 +429,7 @@ possible kinds of dependency errors.
429
429
  def self.find_plugins dirs, info = false
430
430
  res = {}
431
431
  dirs.each do |dir|
432
- Dir.entries(dir)[2..-1].each do |name|
432
+ Dir.entries(dir).sort[2..-1].each do |name|
433
433
  next if res[name.to_sym]
434
434
  d = File.join dir, name
435
435
  if File.directory?(d) and File.exist?(File.join d, 'plugin.yaml')
@@ -861,7 +861,7 @@ Method required for the Plugin interface. Does nothing
861
861
  def locate_plugins dirs
862
862
  plugin_files = {}
863
863
  dirs.reverse.each do |d|
864
- Dir.entries(d)[2..-1].each do |f|
864
+ Dir.entries(d).sort[2..-1].each do |f|
865
865
  full_dir = File.join d, f
866
866
  if File.directory?(full_dir) and File.exist?(File.join(full_dir, 'plugin.yaml'))
867
867
  plugin_files[f] = File.join full_dir, 'plugin.yaml'
@@ -54,7 +54,6 @@ didn't exist. This means that it will be overwritten when the project is saved.
54
54
  The reason for this behaviour is that there should be no user file in the directory
55
55
  where document projects are saved.
56
56
  =end
57
- # $CALLED = 0
58
57
  def initialize file
59
58
  @old_files = []
60
59
  begin super file_for(file)
@@ -157,9 +156,12 @@ The comparison is made using <tt>Document#file_type_match?</tt>. This method ret
157
156
  rule's scope includes +:document+
158
157
  =end
159
158
  def match_rule? obj
159
+ doc_place = if !@document.path.empty?
160
+ @document.url.local_file? ? :local : :remote
161
+ else :local
162
+ end
160
163
  if !super then false
161
- elsif !obj.place.include?(@document.url.local_file? ? :local : :remote)
162
- false
164
+ elsif !obj.place.include? doc_place then false
163
165
  elsif !@document.file_type_match? obj.mimetype, obj.file_extension then false
164
166
  else true
165
167
  end
@@ -92,7 +92,8 @@ module Ruber
92
92
  'view_created(QObject*, QObject*)', 'closing(QObject*)', :activated, :deactivated,
93
93
  'modified_on_disk(QObject*, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason)',
94
94
  'sig_query_close(bool*, bool*)', 'canceled(QString)', 'completed()', 'completed1(bool)',
95
- 'started(KIO::Job*)', 'set_status_bar_text(QString)', 'setWindowCaption(QString)'
95
+ 'started(KIO::Job*)', 'set_status_bar_text(QString)', 'setWindowCaption(QString)',
96
+ 'document_saved_or_uploaded(QObject*, bool)'
96
97
 
97
98
  =begin rdoc
98
99
  Signal emitted before a view associated with the document is closed
@@ -127,7 +128,7 @@ Creates a new Ruber::Document.
127
128
  interface('modification_interface').modified_on_disk_warning = true
128
129
  @modified_on_disk = false
129
130
  @project = DocumentProject.new self
130
-
131
+ @project.finalize
131
132
  @doc.connect(SIGNAL('modifiedChanged(KTextEditor::Document*)')) do |doc|
132
133
  emit modified_changed(@doc.modified?, self)
133
134
  end
@@ -155,7 +156,7 @@ Creates a new Ruber::Document.
155
156
  begin
156
157
  emit text_inserted(r, self)
157
158
  rescue ArgumentError => e
158
- ExceptionDialog.new e, nil, true, "An exception was raised when writing text. See issue number 6 at http://github.com/stcrocco/ruber/issues"
159
+ ExceptionDialog.new e, nil, true, "An exception was raised from emit text_inserted. See issue number 6 at http://github.com/stcrocco/ruber/issues"
159
160
  end
160
161
  end
161
162
 
@@ -357,7 +358,7 @@ Return the project with wider scope the document belongs to. This is:
357
358
  def project
358
359
  prj = Ruber[:world].active_project
359
360
  return @project if path.empty? or !prj
360
- prj.project_files.file_in_project?(url.to_encoded.to_s) ? prj : @project
361
+ prj.file_in_project?(url.to_encoded.to_s) ? prj : @project
361
362
  end
362
363
 
363
364
  =begin rdoc
@@ -133,7 +133,7 @@ containing the information for the signal and slot to create.
133
133
  def __emit_#{name}_signal(#{old_args.size.times.map{|i| "a#{i}"}.join ', '})
134
134
  emit #{name}(#{d[1].map{|i| i ? "a#{i}" : 'self'}.join ', '})
135
135
  end
136
- EOS
136
+ EOS
137
137
  cls.class_eval method_def
138
138
  end
139
139
  res
@@ -233,7 +233,7 @@ are suitable for use by a block passed to {ComponentManager#load_plugins}.
233
233
  def exec
234
234
  res = super
235
235
  case res
236
- when KDE::Dialog::Yes then dlg.silently? ? :silent : true
236
+ when KDE::Dialog::Yes then silently? ? :silent : true
237
237
  when KDE::Dialog::No then false
238
238
  when KDE::Dialog::Cancel then :skip
239
239
  else res
@@ -1,5 +1,5 @@
1
1
  =begin
2
- Copyright (C) 2010 by Stefano Crocco
2
+ Copyright (C) 2010, 2011 by Stefano Crocco
3
3
  stefano.crocco@alice.it
4
4
 
5
5
  This program is free software; you can redistribute it andor modify
@@ -538,13 +538,14 @@ In the second and third cases, the method simply returns respectively *true* or
538
538
  can be carried on and *false* if the user chose to abort it
539
539
  =end
540
540
  def save_documents docs = nil
541
- docs ||= Ruber[:docs]
541
+ docs ||= Ruber[:world].documents
542
542
  to_save = docs.select{|d| d.modified?}
543
543
  until to_save.empty?
544
544
  dlg = SaveModifiedFilesDlg.new to_save, self
545
+ to_save = dlg.to_save
545
546
  case dlg.exec
546
547
  when KDE::Dialog::Yes
547
- to_save = Ruber[:docs].save_documents dlg.to_save
548
+ to_save.delete_if{|doc| doc.save}
548
549
  unless to_save.empty?
549
550
  msg = "The following documents couldn't be saved: #{to_save.join "\n"}\nPlease, choose how to proceed"
550
551
  KDE::MessageBox.sorry nil, KDE.i18n(msg)
@@ -1,5 +1,5 @@
1
1
  =begin
2
- Copyright (C) 2010 by Stefano Crocco
2
+ Copyright (C) 2010, 2011 by Stefano Crocco
3
3
  stefano.crocco@alice.it
4
4
 
5
5
  This program is free software; you can redistribute it andor modify
@@ -280,7 +280,7 @@ informing the user
280
280
  pl.save_settings
281
281
  loaded << pl.plugin_name
282
282
  end
283
- loaded.each{|pl| Ruber[:components].unload_plugin pl}
283
+ loaded.reverse_each{|pl| Ruber[:components].unload_plugin pl}
284
284
  res = Ruber[:app].safe_load_plugins dlg.plugins.keys.map(&:to_s)
285
285
  close unless res
286
286
  nil
@@ -1,5 +1,5 @@
1
1
  =begin
2
- Copyright (C) 2010 by Stefano Crocco
2
+ Copyright (C) 2010, 2011 by Stefano Crocco
3
3
  stefano.crocco@alice.it
4
4
 
5
5
  This program is free software; you can redistribute it andor modify
@@ -757,7 +757,9 @@ option is used.
757
757
  existing = (Qt::MetaModifier & modifiers) == 0 ? :always : :never
758
758
  display_hints = hints.merge(:line => line, :existing => existing)
759
759
  ed = Ruber[:main_window].display_document file, display_hints
760
- Ruber[:main_window].hide_tool self unless pinned_down?
760
+ hide_tool = pinned_down? and Qt::Application.mouse_buttons != Qt::MidButton
761
+ Ruber[:main_window].hide_tool self if hide_tool
762
+ ed.set_focus if ed
761
763
  ed
762
764
  end
763
765
 
@@ -771,11 +773,21 @@ however, that the @:existing@ entry won't be used.
771
773
  @return [Hash] see the description for the _hints_ argument of {MainWindow#editor_for!}
772
774
  =end
773
775
  def hints
774
- case Ruber[:config][:general, :tool_open_files]
775
- when :split_horizontally then {:new => :current_tab, :split => :horizontal}
776
- when :split_vertically then {:new => :current_tab, :split => :vertical}
777
- else {:new => :new_tab}
776
+ hints = {:new => :new_tab}
777
+ choice = Ruber[:config][:general, :tool_open_files]
778
+ case choice
779
+ when :split_horizontally then hints[:split] = :horizontal
780
+ when :split_vertically then hints[:split] = :vertical
778
781
  end
782
+ if hints[:split]
783
+ env = Ruber[:world].active_environment
784
+ view = env.active_editor
785
+ n_views = view ? env.tab(view).views.count : 0
786
+ if n_views == 1 then hints[:new] = :current_tab
787
+ else hints.delete :split
788
+ end
789
+ end
790
+ hints
779
791
  end
780
792
 
781
793
  =begin rdoc
@@ -24,6 +24,7 @@ require 'yaml'
24
24
  require 'ruber/plugin'
25
25
  require 'ruber/settings_container'
26
26
  require 'ruber/project_backend'
27
+ require 'ruber/project_dir_scanner'
27
28
 
28
29
  module Ruber
29
30
 
@@ -130,7 +131,6 @@ When the project is created, it's not active.
130
131
  @project_extensions = {}
131
132
  Ruber[:components].named_connect(SIGNAL('component_loaded(QObject*)'), "register_component_with_project #{object_id}"){|c| c.register_with_project self}
132
133
  Ruber[:components].named_connect(SIGNAL('unloading_component(QObject*)'), "remove_component_from_project #{object_id}"){|c| c.remove_from_project self}
133
- Ruber[:components].each_component{|c| c.register_with_project self}
134
134
  end
135
135
 
136
136
  =begin rdoc
@@ -324,6 +324,22 @@ will be removed (calling their @remove_from_project@ method if they have one).
324
324
  true
325
325
  end
326
326
 
327
+ =begin rdoc
328
+ Registers each component with the project
329
+
330
+ This is done in {#initialize} because, at least for {DocumentProject}, the extensions
331
+ may try to access the project (directly or not) before it has fully been created.
332
+
333
+ This method should only be called from the object calling {.new}
334
+
335
+ @note This method has nothing to do with finalizers
336
+ @return [nil]
337
+ =end
338
+ def finalize
339
+ Ruber[:components].each_component{|c| c.register_with_project self}
340
+ nil
341
+ end
342
+
327
343
  end
328
344
 
329
345
  =begin rdoc
@@ -378,6 +394,16 @@ AbstractProject::InvalidProjectFile will be raised.
378
394
  raise Ruber::AbstractProject::InvalidProjectFile, e.message
379
395
  end
380
396
  super Ruber[:world], back, name
397
+ finalize
398
+ @dir_scanner = ProjectDirScanner.new self
399
+ @dir_scanner.connect(SIGNAL('file_added(QString)')) do |f|
400
+ @files << f if @files
401
+ end
402
+ @dir_scanner.connect(SIGNAL('file_removed(QString)')) do |f|
403
+ @files.delete f if @files
404
+ end
405
+ @dir_scanner.connect(SIGNAL(:rules_changed)){@files = nil}
406
+ @files = nil
381
407
  end
382
408
 
383
409
  =begin rdoc
@@ -417,9 +443,14 @@ to the project.
417
443
  <b>Note:</b> this method uses the <tt>project_files</tt> extension
418
444
  =end
419
445
  def files
420
- @project_extensions[:project_files].project_files
446
+ @files ||= @dir_scanner.project_files
447
+ ProjectFiles.new project_directory, @files
448
+ end
449
+ alias_method :project_files, :files
450
+
451
+ def file_in_project? file
452
+ @dir_scanner.file_in_project? file
421
453
  end
422
-
423
454
  end
424
455
 
425
456
  =begin rdoc
@@ -0,0 +1,171 @@
1
+ =begin
2
+ Copyright (C) 2011 by Stefano Crocco
3
+ stefano.crocco@alice.it
4
+
5
+ This program is free software; you can redistribute it andor modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program; if not, write to the
17
+ Free Software Foundation, Inc.,
18
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
+ =end
20
+
21
+ require 'set'
22
+ require 'find'
23
+ require 'delegate'
24
+
25
+ require 'kio'
26
+
27
+ module Ruber
28
+
29
+ class ProjectDirScanner < Qt::Object
30
+
31
+ signals :rules_changed
32
+
33
+ signals 'file_added(QString)'
34
+
35
+ signals 'file_removed(QString)'
36
+
37
+ def initialize prj
38
+ super
39
+ @project = prj
40
+ #The /? at the end is there to avoid depending on whether Project#project_directory
41
+ #returns a string ending with / or not
42
+ @regexp = %r[^#{Regexp.quote @project.project_directory}/?]
43
+ make_rules
44
+ @watcher = KDE::DirWatch.new self do
45
+ add_dir prj.project_directory,
46
+ KDE::DirWatch::WatchFiles | KDE::DirWatch::WatchSubDirs
47
+ end
48
+ @watcher.connect(SIGNAL('created(QString)')) do |f|
49
+ emit file_added(f) if file_in_project? f
50
+ end
51
+ @watcher.connect(SIGNAL('deleted(QString)')) do |f|
52
+ emit file_removed(f) if file_in_project? f
53
+ end
54
+ @project.connect(SIGNAL('option_changed(QString, QString)')) do |g, n|
55
+ if g == 'general' and n == 'project_files'
56
+ if @project[:general, :project_files] != @rules
57
+ make_rules
58
+ emit rules_changed
59
+ end
60
+ end
61
+ end
62
+ @watcher.start_scan false
63
+ end
64
+
65
+ def file_in_project? file
66
+ if file.start_with? '/'
67
+ file = file.dup
68
+ return false unless file.sub! @regexp, ''
69
+ end
70
+ return nil if file.end_with? '/'
71
+ if file =~ %r{^([\w+-.]+)://(.+)}
72
+ if $1 == 'file' then file = $2
73
+ else return false
74
+ end
75
+ end
76
+ return false if @exclude_regexp =~ file
77
+ return false if @exclude_files.include? file
78
+ return true if @extensions.any?{|e| File.fnmatch?(e, file, File::FNM_DOTMATCH)}
79
+ return true if @include_regexp =~ file or @include_files.include? file
80
+ false
81
+ end
82
+
83
+ def project_files
84
+ res = Set.new
85
+ dir = @project.project_directory
86
+ Ruber[:app].chdir dir do
87
+ Find.find '.' do |f|
88
+ next if File.directory? f
89
+ #remove the leading './'
90
+ f = f[2..-1]
91
+ res << File.join(dir, f) if file_in_project? f
92
+ end
93
+ end
94
+ res
95
+ end
96
+
97
+ private
98
+
99
+ def make_rules
100
+ rules = @project[:general, :project_files]
101
+ @include_regexp = Regexp.union(*(rules[:include].select{|r| r.is_a?(Regexp)}))
102
+ @exclude_regexp = Regexp.union(*(rules[:exclude].select{|r| r.is_a?(Regexp)}))
103
+ @exclude_files = rules[:exclude].select{|rule| rule.is_a? String}.map{|f| f.sub(%r{^\./}, '')}
104
+ @include_files = rules[:include].select{|rule| rule.is_a? String}.map{|f| f.sub(%r{^\./}, '')}
105
+ @include_files-= @exclude_files
106
+ @extensions = rules[:extensions]
107
+ @rules = YAML.load(YAML.dump rules)
108
+ end
109
+
110
+ end
111
+
112
+ class ProjectFiles < Delegator
113
+
114
+ include Enumerable
115
+
116
+ def initialize project_dir, set
117
+ super set
118
+ @set = set
119
+ @project_dir = project_dir.dup
120
+ @project_dir << '/' unless @project_dir.end_with? '/'
121
+ end
122
+
123
+ def __getobj__
124
+ @set
125
+ end
126
+
127
+ def __setobj__ obj
128
+ @set = obj
129
+ end
130
+
131
+ def dup
132
+ self.class.new @project_dir, @set
133
+ end
134
+
135
+ def clone
136
+ res = dup
137
+ dup.freeze if frozen?
138
+ res
139
+ end
140
+
141
+ def to_set
142
+ Set.new self
143
+ end
144
+
145
+ def each_relative
146
+ if block_given?
147
+ l = @project_dir.size
148
+ @set.each{|f| yield f[l, f.size-l]}
149
+ else self.to_enum :each_relative
150
+ end
151
+ end
152
+ alias_method :rel, :each_relative
153
+
154
+ def each
155
+ if block_given?
156
+ @set.each{|f| yield f}
157
+ else self.to_enum
158
+ end
159
+ end
160
+ alias_method :abs, :each
161
+
162
+ [:<<, :add, :clear, :collect!, :delete, :delete_if, :flatten!, :keep_if,
163
+ :map!, :merge, :reject!, :replace, :subtract].each do |m|
164
+ define_method m do |*args|
165
+ raise NoMethodError
166
+ end
167
+ end
168
+
169
+ end
170
+
171
+ end