ruber 0.0.1.1

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 (166) hide show
  1. data/COPYING +339 -0
  2. data/INSTALL +137 -0
  3. data/LICENSE +8 -0
  4. data/bin/ruber +65 -0
  5. data/data/share/apps/ruber/core_components.yaml +31 -0
  6. data/data/share/apps/ruber/ruberui.rc +109 -0
  7. data/data/share/icons/ruber.png +0 -0
  8. data/data/share/pixmaps/ruby.png +0 -0
  9. data/icons/ruber-16.png +0 -0
  10. data/icons/ruber-32.png +0 -0
  11. data/icons/ruber-48.png +0 -0
  12. data/icons/ruber-8.png +0 -0
  13. data/lib/ruber/application/application.rb +288 -0
  14. data/lib/ruber/application/plugin.yaml +11 -0
  15. data/lib/ruber/component_manager.rb +899 -0
  16. data/lib/ruber/config/config.rb +82 -0
  17. data/lib/ruber/config/plugin.yaml +3 -0
  18. data/lib/ruber/document_project.rb +209 -0
  19. data/lib/ruber/documents/document_list.rb +416 -0
  20. data/lib/ruber/documents/plugin.yaml +4 -0
  21. data/lib/ruber/editor/document.rb +506 -0
  22. data/lib/ruber/editor/editor_view.rb +167 -0
  23. data/lib/ruber/editor/ktexteditor_wrapper.rb +202 -0
  24. data/lib/ruber/exception_widgets.rb +245 -0
  25. data/lib/ruber/external_program_plugin.rb +397 -0
  26. data/lib/ruber/filtered_output_widget.rb +342 -0
  27. data/lib/ruber/gui_states_handler.rb +231 -0
  28. data/lib/ruber/kde_config_option_backend.rb +167 -0
  29. data/lib/ruber/kde_sugar.rb +249 -0
  30. data/lib/ruber/main_window/choose_plugins_dlg.rb +353 -0
  31. data/lib/ruber/main_window/main_window.rb +524 -0
  32. data/lib/ruber/main_window/main_window_actions.rb +537 -0
  33. data/lib/ruber/main_window/main_window_internal.rb +239 -0
  34. data/lib/ruber/main_window/open_file_in_project_dlg.rb +212 -0
  35. data/lib/ruber/main_window/output_color_widget.rb +35 -0
  36. data/lib/ruber/main_window/plugin.yaml +58 -0
  37. data/lib/ruber/main_window/save_modified_files_dlg.rb +89 -0
  38. data/lib/ruber/main_window/status_bar.rb +156 -0
  39. data/lib/ruber/main_window/ui/choose_plugins_widget.rb +90 -0
  40. data/lib/ruber/main_window/ui/choose_plugins_widget.ui +77 -0
  41. data/lib/ruber/main_window/ui/main_window_settings_widget.rb +108 -0
  42. data/lib/ruber/main_window/ui/main_window_settings_widget.ui +89 -0
  43. data/lib/ruber/main_window/ui/new_project_widget.rb +119 -0
  44. data/lib/ruber/main_window/ui/new_project_widget.ui +178 -0
  45. data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +109 -0
  46. data/lib/ruber/main_window/ui/open_file_in_project_dlg.ui +168 -0
  47. data/lib/ruber/main_window/ui/output_color_widget.rb +241 -0
  48. data/lib/ruber/main_window/ui/output_color_widget.ui +204 -0
  49. data/lib/ruber/main_window/workspace.rb +442 -0
  50. data/lib/ruber/output_widget.rb +1093 -0
  51. data/lib/ruber/plugin.rb +264 -0
  52. data/lib/ruber/plugin_like.rb +589 -0
  53. data/lib/ruber/plugin_specification.rb +106 -0
  54. data/lib/ruber/plugin_specification_reader.rb +451 -0
  55. data/lib/ruber/project.rb +493 -0
  56. data/lib/ruber/project_backend.rb +105 -0
  57. data/lib/ruber/projects/plugin.yaml +11 -0
  58. data/lib/ruber/projects/project_files_list.rb +314 -0
  59. data/lib/ruber/projects/project_files_widget.rb +301 -0
  60. data/lib/ruber/projects/project_list.rb +314 -0
  61. data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +74 -0
  62. data/lib/ruber/projects/ui/project_files_rule_chooser_widget.ui +61 -0
  63. data/lib/ruber/projects/ui/project_files_widget.rb +117 -0
  64. data/lib/ruber/projects/ui/project_files_widget.ui +123 -0
  65. data/lib/ruber/qt_sugar.rb +673 -0
  66. data/lib/ruber/settings_container.rb +515 -0
  67. data/lib/ruber/settings_dialog.rb +244 -0
  68. data/lib/ruber/settings_dialog_manager.rb +503 -0
  69. data/lib/ruber/utils.rb +414 -0
  70. data/lib/ruber/yaml_option_backend.rb +159 -0
  71. data/outsider_files +15 -0
  72. data/plugins/autosave/autosave.rb +404 -0
  73. data/plugins/autosave/plugin.yaml +16 -0
  74. data/plugins/autosave/ui/autosave_config_widget.rb +83 -0
  75. data/plugins/autosave/ui/autosave_config_widget.ui +68 -0
  76. data/plugins/command/command.png +0 -0
  77. data/plugins/command/command.rb +74 -0
  78. data/plugins/command/plugin.yaml +11 -0
  79. data/plugins/find_in_files/find_in_files.rb +337 -0
  80. data/plugins/find_in_files/find_in_files_dlg.rb +411 -0
  81. data/plugins/find_in_files/find_in_files_ui.rc +11 -0
  82. data/plugins/find_in_files/find_in_files_widgets.rb +485 -0
  83. data/plugins/find_in_files/plugin.yaml +23 -0
  84. data/plugins/find_in_files/ui/config_widget.rb +58 -0
  85. data/plugins/find_in_files/ui/config_widget.ui +41 -0
  86. data/plugins/find_in_files/ui/find_in_files_widget.rb +260 -0
  87. data/plugins/find_in_files/ui/find_in_files_widget.ui +324 -0
  88. data/plugins/project_browser/plugin.yaml +10 -0
  89. data/plugins/project_browser/project_browser.rb +245 -0
  90. data/plugins/rake/plugin.yaml +39 -0
  91. data/plugins/rake/rake.png +0 -0
  92. data/plugins/rake/rake.rb +567 -0
  93. data/plugins/rake/rake_extension.rb +153 -0
  94. data/plugins/rake/rake_widgets.rb +615 -0
  95. data/plugins/rake/rakeui.rc +27 -0
  96. data/plugins/rake/ui/add_quick_task_widget.rb +71 -0
  97. data/plugins/rake/ui/add_quick_task_widget.ui +59 -0
  98. data/plugins/rake/ui/choose_task_widget.rb +77 -0
  99. data/plugins/rake/ui/choose_task_widget.ui +72 -0
  100. data/plugins/rake/ui/config_widget.rb +127 -0
  101. data/plugins/rake/ui/config_widget.ui +123 -0
  102. data/plugins/rake/ui/project_widget.rb +217 -0
  103. data/plugins/rake/ui/project_widget.ui +246 -0
  104. data/plugins/rspec/plugin.yaml +30 -0
  105. data/plugins/rspec/rspec.png +0 -0
  106. data/plugins/rspec/rspec.rb +945 -0
  107. data/plugins/rspec/rspec.svg +90 -0
  108. data/plugins/rspec/rspecui.rc +20 -0
  109. data/plugins/rspec/ruber_rspec_formatter.rb +312 -0
  110. data/plugins/rspec/ui/rspec_project_widget.rb +170 -0
  111. data/plugins/rspec/ui/rspec_project_widget.ui +193 -0
  112. data/plugins/ruby_development/plugin.yaml +27 -0
  113. data/plugins/ruby_development/ruby_development.png +0 -0
  114. data/plugins/ruby_development/ruby_development.rb +453 -0
  115. data/plugins/ruby_development/ruby_developmentui.rc +19 -0
  116. data/plugins/ruby_development/ui/project_widget.rb +112 -0
  117. data/plugins/ruby_development/ui/project_widget.ui +108 -0
  118. data/plugins/ruby_runner/config_widget.rb +116 -0
  119. data/plugins/ruby_runner/plugin.yaml +26 -0
  120. data/plugins/ruby_runner/project_widget.rb +62 -0
  121. data/plugins/ruby_runner/ruby.png +0 -0
  122. data/plugins/ruby_runner/ruby_interpretersui.rc +26 -0
  123. data/plugins/ruby_runner/ruby_runner.rb +411 -0
  124. data/plugins/ruby_runner/ui/config_widget.rb +92 -0
  125. data/plugins/ruby_runner/ui/config_widget.ui +91 -0
  126. data/plugins/ruby_runner/ui/project_widget.rb +60 -0
  127. data/plugins/ruby_runner/ui/project_widget.ui +48 -0
  128. data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +59 -0
  129. data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.ui +44 -0
  130. data/plugins/state/plugin.yaml +28 -0
  131. data/plugins/state/state.rb +520 -0
  132. data/plugins/state/ui/config_widget.rb +92 -0
  133. data/plugins/state/ui/config_widget.ui +89 -0
  134. data/plugins/syntax_checker/plugin.yaml +18 -0
  135. data/plugins/syntax_checker/syntax_checker.rb +662 -0
  136. data/ruber.desktop +10 -0
  137. data/spec/annotation_model_spec.rb +174 -0
  138. data/spec/common.rb +119 -0
  139. data/spec/component_manager_spec.rb +1259 -0
  140. data/spec/document_list_spec.rb +626 -0
  141. data/spec/document_project_spec.rb +373 -0
  142. data/spec/document_spec.rb +779 -0
  143. data/spec/editor_view_spec.rb +167 -0
  144. data/spec/external_program_plugin_spec.rb +676 -0
  145. data/spec/filtered_output_widget_spec.rb +642 -0
  146. data/spec/gui_states_handler_spec.rb +304 -0
  147. data/spec/kde_config_option_backend_spec.rb +214 -0
  148. data/spec/kde_sugar_spec.rb +101 -0
  149. data/spec/ktexteditor_wrapper_spec.rb +305 -0
  150. data/spec/output_widget_spec.rb +1703 -0
  151. data/spec/plugin_spec.rb +1393 -0
  152. data/spec/plugin_specification_reader_spec.rb +1765 -0
  153. data/spec/plugin_specification_spec.rb +401 -0
  154. data/spec/project_backend_spec.rb +172 -0
  155. data/spec/project_files_list_spec.rb +401 -0
  156. data/spec/project_list_spec.rb +511 -0
  157. data/spec/project_spec.rb +990 -0
  158. data/spec/qt_sugar_spec.rb +328 -0
  159. data/spec/settings_container_spec.rb +617 -0
  160. data/spec/settings_dialog_manager_spec.rb +773 -0
  161. data/spec/settings_dialog_spec.rb +419 -0
  162. data/spec/state_spec.rb +991 -0
  163. data/spec/utils_spec.rb +406 -0
  164. data/spec/workspace_spec.rb +869 -0
  165. data/spec/yaml_option_backend_spec.rb +246 -0
  166. metadata +284 -0
@@ -0,0 +1,105 @@
1
+ =begin
2
+ Copyright (C) 2010 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 'ruber/yaml_option_backend'
22
+
23
+ module Ruber
24
+
25
+ =begin rdoc
26
+ Backend for SettingsContainer used by the project class. It is similar to YamlSettingsBackend,
27
+ except for the fact that it produces three files instead of one: one only contains
28
+ options of type +:global+ (and is referred to as the <i>project file</i>),
29
+ another one contains only options of type +:user+ and
30
+ the last contains only options of type +:session+
31
+ =end
32
+ class ProjectBackend
33
+
34
+ =begin rdoc
35
+ Associates each option type (+:global+, +:user+ and +:session+) with the extension
36
+ of the file where those options will be stored
37
+ =end
38
+ EXTENSIONS = {:global => '.ruprj', :user => '.ruusr', :session => '.ruses'}
39
+
40
+ =begin rdoc
41
+ Creates a new ProjectBackend. <i>file</i> is the name of the project file. The
42
+ name of the file for the user options and the session options is obtained by
43
+ appending the appropriate extension to the project file:
44
+ * +.ruusr+ for the user options file
45
+ * +.ruses+ for the session options file
46
+ If _file_ has the +.ruprj+ extension, that extension will be removed before
47
+ adding the extensions above. If it has any other extension (or no extension at
48
+ all), they'll be kept.
49
+
50
+ For example, if _file_ is <tt>/home/stefano/xyz.ruprj</tt>, the other two files
51
+ will be <tt>/home/stefano/xyz.ruusr</tt> and <tt>/home/stefano/xyz.ruses</tt>.
52
+ Instead, if _file_ is <tt>/home/stefano/xyz.abc</tt>, the other two files
53
+ will be <tt>/home/stefano/xyz.abc.ruusr</tt> and <tt>/home/stefano/xyz.abc.ruses</tt>
54
+
55
+ If either the file for the global or the user options already exists but doesn't
56
+ have the correct format, <tt>YamlSettingsBackend::InvalidSettingsFile</tt> will be
57
+ raised. If the same happens for the session options file, instead, a warning
58
+ will be emitted, default values will be used for the session options and they
59
+ will never be saved to file.
60
+ =end
61
+ def initialize file
62
+ @backends = {}
63
+ base_file = file.sub(/#{EXTENSIONS[:global]}$/, '')
64
+ @backends[:global] = YamlSettingsBackend.new file
65
+ @backends[:user] = YamlSettingsBackend.new base_file + EXTENSIONS[:user]
66
+ @backends[:session] = begin YamlSettingsBackend.new base_file + EXTENSIONS[:session]
67
+ rescue YamlSettingsBackend::InvalidSettingsFile
68
+ warn "The file #{base_file + '.ruses'} already exists but it's not a valid session file. Session options won't be saved"
69
+ YamlSettingsBackend.new ''
70
+ end
71
+ end
72
+
73
+ =begin rdoc
74
+ Returns the path of the file were the global options are stored
75
+ =end
76
+ def file
77
+ @backends[:global].file
78
+ end
79
+
80
+ =begin rdoc
81
+ Returns the value of the option described by the option object _opt_
82
+ (see SettingsContainer#add_option).
83
+
84
+ The value will be retrieved from the backend corresponding to the type of the option
85
+ =end
86
+ def [] opt
87
+ @backends[opt.type][opt]
88
+ end
89
+
90
+ =begin rdoc
91
+ Writes the options _opts_ back to disk. The options contained in _opts_ will be
92
+ grouped according to their types and the appropriate backend will be used to
93
+ save each group.
94
+ =end
95
+ def write opts
96
+ options = {:global => {}, :user => {}, :session => {}}
97
+ opts.each{|k, v| options[k.type][k] = v}
98
+ @backends[:global].write options[:global]
99
+ @backends[:user].write options[:user]
100
+ @backends[:session].write options[:session] unless @backends[:session].file.empty?
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -0,0 +1,11 @@
1
+ name: projects
2
+ require: [project_list, project_files_widget, project_files_list]
3
+ class: Ruber::ProjectList
4
+ project_options:
5
+ general:
6
+ project_files: {default: {:include: [], :exclude: [], :extensions: ["*.rb"]}}
7
+ open_documents: {default: [], type: session, scope: [global, document]}
8
+ project_widgets:
9
+ - {class: Ruber::ProjectFilesWidget, caption: 'General', pixmap: configure}
10
+ extensions:
11
+ project_files: {class: ProjectFilesList}
@@ -0,0 +1,314 @@
1
+ =begin
2
+ Copyright (C) 2010 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 'thread'
22
+ require 'facets/kernel/deep_copy'
23
+ require 'find'
24
+
25
+ require 'ruber/project'
26
+
27
+ module Ruber
28
+
29
+ =begin rdoc
30
+ Project extension to access the name of the files belonging to the project.
31
+
32
+ The file belonging to the projects are determined using the contents of the @general/project_files@
33
+ project option. This option is a hash which contains rules (in the form of hashes)
34
+ telling which files are part of the project and which aren't.
35
+
36
+ Files are kept up to date, both regarding the contents of the filesystem (using a
37
+ KDE::DirWatch) and the rules specified in the project.
38
+
39
+ To avoid needlessly freezing the application in case projects with a large number of files,
40
+ this class only scans the project directory when a list of project files is actually
41
+ requested, that is when one of the methods {#each}, {#abs}, {#rel} and {#project_files}
42
+ is called. Even in that case, the project directory is only scanned if a file has
43
+ been added or removed since the last scan, or if the rules have changed since then.
44
+ A scan can be forced by using the {#update} method, but you usually don't need this.
45
+
46
+ h3. Rules
47
+
48
+ The @general/project_files@ setting is a hash containing the rules to use to decide
49
+ whether a given file belongs to the project or not. There are two groups of rules:
50
+ inclusive rules and exclusive rules. Inclusive rules specify files which belong
51
+ to the project, while exclusive rules specify files which do not belong to the
52
+ project. Exclusive rules have precedence on inclusive rules, meaning that if a
53
+ file matches both an inclusive and an exclusive rule, it's excluded from the project.
54
+
55
+ There are three kinds of rules:
56
+
57
+
58
+ Project extension which scans the project directory and finds out which files belong
59
+ to the project and which don't (according to rules specified by the user in the
60
+ project options), returning an array of the former.
61
+
62
+ It also watches the project directory to update the list of project files when a
63
+ file in the project directory is added or removed.
64
+
65
+ This class provides an {#each} method, which yields all the project files to the
66
+ block or, if called without a block, returns an <tt>Enumerable::Enumerator</tt>
67
+ which does the same.
68
+
69
+ <b>Note:</b> the list of project files is created lazily, only when a method
70
+ explicitly needs it and a file or directory (or the rules) have changed since
71
+ the last time it was generated. A <tt>KDE::DirWatch</tt> object is used to find
72
+ out when a file or directory changes. Also, after a file has changed, the watcher
73
+ is stopped until the list is updated again (a single change is enough to rebuild
74
+ the whole list, so there's no point in keeping watching).
75
+
76
+ The methods which can cause the list to be rebuild are: +each+, +abs+, +rel+ and
77
+ <tt>project_files</tt>.
78
+
79
+ ===Rules
80
+ To decide whether a file belongs to the project or not, ProjectFilesList uses
81
+ the general/project_files project option. It is a hash made of three keys, each
82
+ specifying a rule telling whether a file is part of the project or not. In application
83
+ order, they are
84
+ <tt>extensions</tt>::
85
+ an array of shell globs. Any file matching one of them (according to <tt>File.fnmatch</tt>)
86
+ will be considered a project file, unless another rule says otherwise
87
+ <tt>include</tt>::
88
+ an array of strings and/or regexps corresponding to files which belong to the
89
+ project. Each string entry is the name (relative to the project directory) of
90
+ a file which belongs to the project. Each regexp is a pattern which should be
91
+ tested against all the names (still relative to the project directory) of the
92
+ files in the project directory (and its subdirectories). Each matching file will
93
+ be a project file (unless another rule says otherwise).
94
+ <tt>exclude</tt>::
95
+ as the <tt>include</tt> entry, but the matching files will *not* be project
96
+ files. This entry has the precedence with respect to the other two. This means
97
+ that if a file is a project file according to one of them but also matches a
98
+ rule here, it <i>won't</i> be considered a project file.
99
+ =end
100
+ class ProjectFilesList < Qt::Object
101
+
102
+ include Enumerable
103
+
104
+ include Extension
105
+
106
+ =begin rdoc
107
+ Creates a new ProjectFilesList for the Project _prj_.
108
+
109
+ The new object will read the <tt>general/project_files</tt> option from the project
110
+ and immediately scan the project directory to find the all the project files.
111
+ Besides, it will start monitoring the directory for changes, so as to be
112
+ able to update the list of project files when a new file is created or a file
113
+ is deleted.
114
+ =end
115
+ def initialize prj
116
+ super
117
+ @lock = Mutex.new
118
+ @project = prj
119
+ @project_files = nil
120
+ @watcher = KDE::DirWatch.new self
121
+ @watcher.add_dir @project.project_dir, KDE::DirWatch::WatchSubDirs
122
+ @up_to_date = false
123
+ make_rules
124
+ @project_files = []
125
+ @watcher.connect(SIGNAL('dirty(QString)')) do
126
+ @up_to_date = false
127
+ @watcher.stop_scan
128
+ end
129
+ @project.connect(SIGNAL('option_changed(QString, QString)')) do |g, n|
130
+ if g == 'general' and n == 'project_files'
131
+ if @project[:general, :project_files] != @rules
132
+ @up_to_date = false
133
+ make_rules
134
+ scan_project
135
+ end
136
+ end
137
+ end
138
+ @watcher.start_scan false
139
+ end
140
+
141
+ =begin rdoc
142
+ Returns an array with the name of the files in the project (in arbitrary order).
143
+ If _abs_ is *false*, the file names will be relative to the project directory;
144
+ if it is *true* they'll be absolute. It is the same as calling <tt>list.abs.to_a</tt>
145
+ or <tt>list.rel.to_a</tt>
146
+
147
+ <b>Note:</b> if the list isn't up to date, the project will be re-scanned
148
+ =end
149
+ def project_files abs = true
150
+ refresh unless @up_to_date
151
+ if abs
152
+ dir = @project.project_dir
153
+ @project_files.map{|f| File.join dir, f}
154
+ else @project_files.deep_copy
155
+ end
156
+ end
157
+
158
+ =begin rdoc
159
+ If called with a block, calls the block yielding the names of the files in the
160
+ project. If _abs_ is true, absolute file names will be used, otherwise the file
161
+ names will be relative to the project directory.
162
+
163
+ If called without a block, returns an <tt>Enumerable::Enumerator</tt> which does
164
+ the same as above.
165
+
166
+ <b>Note:</b> when called with a block and the list isn't up to date,
167
+ the project will be re-scanned
168
+ =end
169
+ def each abs = true
170
+ if block_given?
171
+ refresh unless @up_to_date
172
+ dir = @project.project_dir
173
+ @project_files.each do |f|
174
+ yield abs ? File.join( dir, f) : f
175
+ end
176
+ self
177
+ else
178
+ return self.to_enum(:each, abs)
179
+ end
180
+ end
181
+
182
+ =begin rdoc
183
+ Returns an enumerator as that yields the names of the project files relative to
184
+ the project directory. It's just a shortcut for <tt>each(false)</tt>.
185
+
186
+ <b>Note:</b> if the list isn't up to date, the project will be re-scanned when
187
+ any enumerable method returned by the object is called
188
+ =end
189
+ def rel
190
+ self.each false
191
+ end
192
+ alias_method :relative_paths, :rel
193
+
194
+ =begin rdoc
195
+ Returns an enumerator as that yields the absolute names of the project files.
196
+ It's just a shortcut for <tt>each(true)</tt>.
197
+
198
+ <b>Note:</b> if the list isn't up to date, the project will be re-scanned when
199
+ any enumerable method returned by the object is called
200
+ =end
201
+ def abs
202
+ self.each true
203
+ end
204
+ alias_method :absolute_paths, :abs
205
+
206
+ =begin rdoc
207
+ Updates the list, so that it reflects the current status of the project directory.
208
+
209
+ Usually you don't need to call this method, as it's automatically called as needed.
210
+ =end
211
+ def refresh
212
+ scan_project
213
+ @up_to_date = true
214
+ @watcher.start_scan
215
+ end
216
+
217
+ =begin rdoc
218
+ Tells whether the list is up to date or needs to be rebuilt.
219
+
220
+ Usually you don't need this method, as the list is automatically updated when needed.
221
+ =end
222
+ def up_to_date?
223
+ @up_to_date
224
+ end
225
+
226
+ =begin rdoc
227
+ Tells whether the given file belongs to the project or not. If _file_ is a relative
228
+ path, it is considered relative to the project directory.
229
+
230
+ Returns *true* if _file_ belongs to the project and *false* otherwise. As this method
231
+ doesn't access the filesystem, the behaviour in the case _file_ is a directory will
232
+ be undefined. If _file_ ends with a slash (which makes it clear it represents a
233
+ directory) then *nil* will be returned
234
+ <b>Note:</b> this method doesn't use the file list to tell whether the file is in
235
+ the project. Rather, it compares the file name with the include and exclude rules
236
+ and the extensions.
237
+ =end
238
+ def file_in_project? file
239
+ file = file.sub %r[^#{Regexp.quote(@project.project_directory)}/], ''
240
+ return false if file.start_with? '/'
241
+ return nil if file.end_with? '/'
242
+ # I don't know why I added the following line
243
+ # file = File.basename(file)
244
+
245
+ return false if @exclude_files.include? file
246
+ return true if @include_files.any?{|f| f == file}
247
+
248
+ if @exclude_regexp.match(file) then false
249
+ elsif @include_regexp.match file then true
250
+ elsif @extensions.any?{|e| File.fnmatch?(e, file, File::FNM_DOTMATCH)} then true
251
+ else false
252
+ end
253
+ end
254
+
255
+ private
256
+
257
+ =begin rdoc
258
+ Applies the rules stored in the <tt>general/project_files</tt> project option
259
+ to all the files in the project directory and fills the internal cache with the
260
+ project files.
261
+
262
+ ---
263
+ The <tt>@lock</tt> instance variable is used to (hopefully) avoid issues with
264
+ two threads calling this method together, since Dir.chdir doesn't nest inside
265
+ threads. This created problems with syntax checker, for example.
266
+ =end
267
+ def scan_project
268
+ @lock.synchronize do
269
+ Dir.chdir(@project.project_dir) do
270
+ res = @include_files.select{|f| File.exist?(f) and File.file?(f)}
271
+ # remove the leading ./ from the names of the files
272
+ res.map!{|f| f.sub(%r{^./}, '')}
273
+ Find.find('.') do |f|
274
+ # let's skip the current directory, least it somehow matches one of
275
+ # the exclude rules
276
+ next if f == '.'
277
+ if File.directory?(f)
278
+ f = File.join(f, '')
279
+ Find.prune if f.match @exclude_regexp
280
+ else
281
+ # We remove the leading ./
282
+ f = f[2..-1]
283
+ next if f.match @exclude_regexp or @exclude_files.include?(f)
284
+ res << f if @extensions.any?{|ext| File.fnmatch?(ext, f, File::FNM_DOTMATCH)} or
285
+ f.match(@include_regexp)
286
+ end
287
+ end
288
+ res.uniq!
289
+ @project_files = res
290
+ end
291
+ end
292
+ end
293
+
294
+ =begin rdoc
295
+ Creates an internal version of the rules from the values in the
296
+ <tt>general/project_files</tt> project option
297
+ =end
298
+ def make_rules
299
+ rules = @project[:general, :project_files]
300
+ @include_regexp = Regexp.union(*(rules[:include].select{|r| r.is_a?(Regexp)}))
301
+ @exclude_regexp = Regexp.union(*(rules[:exclude].select{|r| r.is_a?(Regexp)}))
302
+ @exclude_files = rules[:exclude].select{|rule| rule.is_a? String}.map{|f| f.sub(%r{^\./}, '')}
303
+ @include_files = rules[:include].select{|rule| rule.is_a? String}.map{|f| f.sub(%r{^\./}, '')}
304
+ @include_files-= @exclude_files
305
+ @extensions = rules[:extensions]
306
+ @rules = YAML.load(YAML.dump rules)
307
+ # TODO Uncomment the following line and remove the previous when found out why Marshal
308
+ # fails
309
+ # @rules = rules.deep_copy
310
+ end
311
+
312
+ end
313
+
314
+ end
@@ -0,0 +1,301 @@
1
+ =begin
2
+ Copyright (C) 2010 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 'pathname'
22
+
23
+ require_relative 'ui/project_files_rule_chooser_widget.rb'
24
+ require_relative 'ui/project_files_widget.rb'
25
+
26
+ module Ruber
27
+
28
+ =begin rdoc
29
+ Widget which displays and allows to modify the rules which determine whether a
30
+ file belongs to a project or not.
31
+
32
+ It contains a @Qt::TreeView@ where the rules are displayed and some buttons to add
33
+ and remove rules. The tree view has two columns: the first displays the contents
34
+ of the rules, while the second contains the rules' type (whether they're regexp
35
+ rules or file rules).
36
+
37
+ @see Ruber::ProjectFilesList
38
+ =end
39
+ class ProjectFilesRuleChooser < Qt::Widget
40
+
41
+ =begin rdoc
42
+ Signal emitted when the rules have been changed
43
+ =end
44
+ signals 'rules_edited()'
45
+
46
+ =begin rdoc
47
+ @return [Ruber::Project] the project the widget displays rules for
48
+ =end
49
+ attr_writer :project
50
+
51
+ =begin rdoc
52
+ Validator class for regexp rules
53
+
54
+ It marks the text as @Qt::Validator::Intermediate@ if a valid regexp can't be
55
+ constructed from it and as @Qt::Validator::Acceptable@ if it can
56
+ =end
57
+ class RegexpRuleValidator < Qt::Validator
58
+
59
+ =begin rdoc
60
+ Override of @Qt::Validator#validate@
61
+
62
+ @param [String] input the text in the widget
63
+ @param [Integer] _pos the cursor position (unused)
64
+ @return [Integer] @Qt::Validator::Acceptable@ if the text is a valid regexp and
65
+ @Qt::Validator::Intermediate@ otherwise
66
+ =end
67
+ def validate input, _pos
68
+ begin
69
+ Regexp.new(input)
70
+ Acceptable
71
+ rescue RegexpError
72
+ Intermediate
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ slots :remove_rule, :add_regexp_rule, :add_path_rule, :edit_rule, :change_button_state
79
+
80
+ =begin rdoc
81
+ @param [Qt::Widget] parent the parent widget
82
+ =end
83
+ def initialize parent = nil
84
+ super
85
+ @project = nil
86
+ @ui = Ui::ProjectFilesRuleChooser.new
87
+ @ui.setupUi self
88
+ model = Qt::StandardItemModel.new @ui.rules_widget
89
+ @ui.rules_widget.model = model
90
+ model.horizontal_header_labels = %w[Pattern Type]
91
+ connect @ui.add_regexp_btn, SIGNAL('clicked()'), self, SLOT('add_regexp_rule()')
92
+ connect @ui.add_path_btn, SIGNAL('clicked()'), self, SLOT('add_path_rule()')
93
+ connect @ui.remove_rule_btn, SIGNAL('clicked()'), self, SLOT('remove_rule()')
94
+ connect @ui.rules_widget.selection_model, SIGNAL('selectionChanged(QItemSelection, QItemSelection)'), self, SLOT('change_button_state()')
95
+ @ui.remove_rule_btn.enabled = false
96
+ end
97
+
98
+ =begin rdoc
99
+ Override of @Qt::Widget#sizeHint@
100
+
101
+ @return [Qt::Size] the optimal size to use (350x200)
102
+ =end
103
+ def sizeHint
104
+ Qt::Size.new(350,200)
105
+ end
106
+
107
+ =begin rdoc
108
+ The rules chosen by the user
109
+
110
+ @return [Array<String,Regexp>] an array containing the rules the user has chosen.
111
+ String entries correspond to file rules, while regexp entries correspond to regexp
112
+ rules
113
+ =end
114
+ def rules
115
+ @ui.rules_widget.model.enum_for(:each_row).map do |rule, type|
116
+ if type.text == 'Path' then rule.text
117
+ else Regexp.new rule.text
118
+ end
119
+ end
120
+ end
121
+
122
+ =begin rdoc
123
+ Fills the tree view
124
+
125
+ *Note:* calling this method won't trigger the {#rules_edited} signal
126
+
127
+ @param [Array<String,Regexp>] list a list of the rules to insert in the view. String
128
+ entries will be considered as file rules, while regexp entries will be considered
129
+ regexp rules
130
+ @return [nil]
131
+ =end
132
+ def rules= list
133
+ model = @ui.rules_widget.model
134
+ model.clear
135
+ model.horizontal_header_labels = %w[Pattern Type]
136
+ list.each do |rule|
137
+ type = 'Path'
138
+ if rule.is_a? Regexp
139
+ type = 'Regexp'
140
+ rule = rule.source
141
+ end
142
+ model.append_row [Qt::StandardItem.new(rule), Qt::StandardItem.new(type)]
143
+ end
144
+ nil
145
+ end
146
+
147
+ private
148
+
149
+ =begin rdoc
150
+ Adds a rule to the tree view
151
+
152
+ The {#rules_edited} signal will be emitted after the rule has been inserted
153
+
154
+ @param [String] text the text to insert in the first column of the view. It must
155
+ be the path of the file for a file rule and the source of the regexp for a regexp
156
+ rule
157
+ @param [String] the name of the type of rule. Currently, this should be either
158
+ @Regexp@ or @File@
159
+ @return [(Qt::StandardItem, Qt::StandardItem)] the new items inserted in the view's
160
+ model
161
+ =end
162
+ def add_rule text, type
163
+ row = [Qt::StandardItem.new(text), Qt::StandardItem.new(type)]
164
+ @ui.rules_widget.model.append_row row
165
+ @ui.rules_widget.selection_model.select row[0].index,
166
+ Qt::ItemSelectionModel::SelectCurrent| Qt::ItemSelectionModel::Rows |
167
+ Qt::ItemSelectionModel::Clear
168
+ emit rules_edited
169
+ row
170
+ end
171
+
172
+ =begin rdoc
173
+ Enables or disables the the Remove button
174
+
175
+ The button is enabled if a rule is selected and disabled otherwise
176
+
177
+ @return [nil]
178
+ =end
179
+ def change_button_state
180
+ @ui.remove_rule_btn.enabled =
181
+ !@ui.rules_widget.selection_model.selected_rows.first.nil?
182
+ nil
183
+ end
184
+
185
+ =begin rdoc
186
+ Adds a file rule to the list
187
+
188
+ A dialog is shown to the user to select the file to add to the project
189
+
190
+ @return [nil]
191
+ =end
192
+ def add_path_rule
193
+ rule = KDE::FileDialog.get_open_file_name KDE::Url.from_path(@project.project_dir),
194
+ '', self, "Add files to project"
195
+ return unless rule
196
+ path = Pathname.new(rule)
197
+ if path.child_of?(@project.project_dir)
198
+ prj_dir = Pathname.new(@project.project_dir)
199
+ rule = path.relative_path_from(prj_dir).to_s
200
+ end
201
+ add_rule rule, 'Path'
202
+ nil
203
+ end
204
+
205
+ =begin rdoc
206
+ Adds a regexp rule to the list
207
+
208
+ The user is shown a dialog to choose the regexp
209
+
210
+ @return [nil]
211
+ =end
212
+ def add_regexp_rule
213
+ rule = KDE::InputDialog.get_text "Add regexp rule", "Regexp",
214
+ :validator => RegexpRuleValidator.new(self)
215
+ return unless rule
216
+ add_rule rule, 'Regexp'
217
+ nil
218
+ end
219
+
220
+ =begin rdoc
221
+ Removes the selected rule from the list
222
+
223
+ The {#rules_edited} signal is emitted after removing the rule
224
+
225
+ *Note:* this method doesn't check whether there's an item selected and will fail
226
+ if it isn't so
227
+ @return [nil]
228
+ =end
229
+ def remove_rule
230
+ row = @ui.rules_widget.selection_model.selected_rows.first
231
+ @ui.rules_widget.model.remove_row row.row
232
+ emit rules_edited
233
+ nil
234
+ end
235
+
236
+ end
237
+
238
+ =begin rdoc
239
+ Widget where the user can choose the rules for files to include and exclude from
240
+ a project.
241
+
242
+ This widget contains two {ProjectFilesRuleChooser} widgets, one for the files to
243
+ include in the project and another for the files to exclude, and one lineedit where
244
+ the user chooses the extensions of the files to add by default to the project.
245
+ =end
246
+ class ProjectFilesWidget < ProjectConfigWidget
247
+
248
+ =begin rdoc
249
+ Signal emitted whenever the contents of the widgets change so that the Apply button
250
+ in the dialg can be enabled accordingly
251
+ =end
252
+ signals 'settings_changed()'
253
+
254
+ slots 'read_settings(QObject*)', 'store_settings(QObject*)',
255
+ 'read_default_settings(QObject*)'
256
+
257
+ =begin rdoc
258
+ @param [Ruber::Project] prj the project the widget is for
259
+ =end
260
+ def initialize prj
261
+ super
262
+ @ui = Ui::ProjectFilesWidget.new
263
+ @ui.setupUi self
264
+ @ui.include_rules.project = prj
265
+ @ui.exclude_rules.project = prj
266
+ connect @ui.include_rules, SIGNAL('rules_edited()'), self, SIGNAL('settings_changed()')
267
+ connect @ui.extensions, SIGNAL('textChanged(QString)'), self, SIGNAL('settings_changed()')
268
+ end
269
+
270
+ =begin rdoc
271
+ Reads the settings from the project
272
+
273
+ @return [nil]
274
+ =end
275
+ def read_settings
276
+ rules = project[:general, :project_files]
277
+ @ui.include_rules.rules = rules[:include]
278
+ @ui.exclude_rules.rules = rules[:exclude]
279
+ exts = rules[:extensions]
280
+ @ui.extensions.text = exts.join ' '
281
+ nil
282
+ end
283
+
284
+ =begin rdoc
285
+ Writes the settings to the project
286
+
287
+ @return [nil]
288
+ =end
289
+ def store_settings
290
+ res = {
291
+ :include => @ui.include_rules.rules,
292
+ :exclude => @ui.exclude_rules.rules,
293
+ :extensions => @ui.extensions.text.split(/[\s,;]/)
294
+ }
295
+ project[:general, :project_files] = res
296
+ nil
297
+ end
298
+
299
+ end
300
+
301
+ end