ruber 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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