ruber 0.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +339 -0
- data/INSTALL +137 -0
- data/LICENSE +8 -0
- data/bin/ruber +65 -0
- data/data/share/apps/ruber/core_components.yaml +31 -0
- data/data/share/apps/ruber/ruberui.rc +109 -0
- data/data/share/icons/ruber.png +0 -0
- data/data/share/pixmaps/ruby.png +0 -0
- data/icons/ruber-16.png +0 -0
- data/icons/ruber-32.png +0 -0
- data/icons/ruber-48.png +0 -0
- data/icons/ruber-8.png +0 -0
- data/lib/ruber/application/application.rb +288 -0
- data/lib/ruber/application/plugin.yaml +11 -0
- data/lib/ruber/component_manager.rb +899 -0
- data/lib/ruber/config/config.rb +82 -0
- data/lib/ruber/config/plugin.yaml +3 -0
- data/lib/ruber/document_project.rb +209 -0
- data/lib/ruber/documents/document_list.rb +416 -0
- data/lib/ruber/documents/plugin.yaml +4 -0
- data/lib/ruber/editor/document.rb +506 -0
- data/lib/ruber/editor/editor_view.rb +167 -0
- data/lib/ruber/editor/ktexteditor_wrapper.rb +202 -0
- data/lib/ruber/exception_widgets.rb +245 -0
- data/lib/ruber/external_program_plugin.rb +397 -0
- data/lib/ruber/filtered_output_widget.rb +342 -0
- data/lib/ruber/gui_states_handler.rb +231 -0
- data/lib/ruber/kde_config_option_backend.rb +167 -0
- data/lib/ruber/kde_sugar.rb +249 -0
- data/lib/ruber/main_window/choose_plugins_dlg.rb +353 -0
- data/lib/ruber/main_window/main_window.rb +524 -0
- data/lib/ruber/main_window/main_window_actions.rb +537 -0
- data/lib/ruber/main_window/main_window_internal.rb +239 -0
- data/lib/ruber/main_window/open_file_in_project_dlg.rb +212 -0
- data/lib/ruber/main_window/output_color_widget.rb +35 -0
- data/lib/ruber/main_window/plugin.yaml +58 -0
- data/lib/ruber/main_window/save_modified_files_dlg.rb +89 -0
- data/lib/ruber/main_window/status_bar.rb +156 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.rb +90 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.ui +77 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.rb +108 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.ui +89 -0
- data/lib/ruber/main_window/ui/new_project_widget.rb +119 -0
- data/lib/ruber/main_window/ui/new_project_widget.ui +178 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +109 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.ui +168 -0
- data/lib/ruber/main_window/ui/output_color_widget.rb +241 -0
- data/lib/ruber/main_window/ui/output_color_widget.ui +204 -0
- data/lib/ruber/main_window/workspace.rb +442 -0
- data/lib/ruber/output_widget.rb +1093 -0
- data/lib/ruber/plugin.rb +264 -0
- data/lib/ruber/plugin_like.rb +589 -0
- data/lib/ruber/plugin_specification.rb +106 -0
- data/lib/ruber/plugin_specification_reader.rb +451 -0
- data/lib/ruber/project.rb +493 -0
- data/lib/ruber/project_backend.rb +105 -0
- data/lib/ruber/projects/plugin.yaml +11 -0
- data/lib/ruber/projects/project_files_list.rb +314 -0
- data/lib/ruber/projects/project_files_widget.rb +301 -0
- data/lib/ruber/projects/project_list.rb +314 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +74 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.ui +61 -0
- data/lib/ruber/projects/ui/project_files_widget.rb +117 -0
- data/lib/ruber/projects/ui/project_files_widget.ui +123 -0
- data/lib/ruber/qt_sugar.rb +673 -0
- data/lib/ruber/settings_container.rb +515 -0
- data/lib/ruber/settings_dialog.rb +244 -0
- data/lib/ruber/settings_dialog_manager.rb +503 -0
- data/lib/ruber/utils.rb +414 -0
- data/lib/ruber/yaml_option_backend.rb +159 -0
- data/outsider_files +15 -0
- data/plugins/autosave/autosave.rb +404 -0
- data/plugins/autosave/plugin.yaml +16 -0
- data/plugins/autosave/ui/autosave_config_widget.rb +83 -0
- data/plugins/autosave/ui/autosave_config_widget.ui +68 -0
- data/plugins/command/command.png +0 -0
- data/plugins/command/command.rb +74 -0
- data/plugins/command/plugin.yaml +11 -0
- data/plugins/find_in_files/find_in_files.rb +337 -0
- data/plugins/find_in_files/find_in_files_dlg.rb +411 -0
- data/plugins/find_in_files/find_in_files_ui.rc +11 -0
- data/plugins/find_in_files/find_in_files_widgets.rb +485 -0
- data/plugins/find_in_files/plugin.yaml +23 -0
- data/plugins/find_in_files/ui/config_widget.rb +58 -0
- data/plugins/find_in_files/ui/config_widget.ui +41 -0
- data/plugins/find_in_files/ui/find_in_files_widget.rb +260 -0
- data/plugins/find_in_files/ui/find_in_files_widget.ui +324 -0
- data/plugins/project_browser/plugin.yaml +10 -0
- data/plugins/project_browser/project_browser.rb +245 -0
- data/plugins/rake/plugin.yaml +39 -0
- data/plugins/rake/rake.png +0 -0
- data/plugins/rake/rake.rb +567 -0
- data/plugins/rake/rake_extension.rb +153 -0
- data/plugins/rake/rake_widgets.rb +615 -0
- data/plugins/rake/rakeui.rc +27 -0
- data/plugins/rake/ui/add_quick_task_widget.rb +71 -0
- data/plugins/rake/ui/add_quick_task_widget.ui +59 -0
- data/plugins/rake/ui/choose_task_widget.rb +77 -0
- data/plugins/rake/ui/choose_task_widget.ui +72 -0
- data/plugins/rake/ui/config_widget.rb +127 -0
- data/plugins/rake/ui/config_widget.ui +123 -0
- data/plugins/rake/ui/project_widget.rb +217 -0
- data/plugins/rake/ui/project_widget.ui +246 -0
- data/plugins/rspec/plugin.yaml +30 -0
- data/plugins/rspec/rspec.png +0 -0
- data/plugins/rspec/rspec.rb +945 -0
- data/plugins/rspec/rspec.svg +90 -0
- data/plugins/rspec/rspecui.rc +20 -0
- data/plugins/rspec/ruber_rspec_formatter.rb +312 -0
- data/plugins/rspec/ui/rspec_project_widget.rb +170 -0
- data/plugins/rspec/ui/rspec_project_widget.ui +193 -0
- data/plugins/ruby_development/plugin.yaml +27 -0
- data/plugins/ruby_development/ruby_development.png +0 -0
- data/plugins/ruby_development/ruby_development.rb +453 -0
- data/plugins/ruby_development/ruby_developmentui.rc +19 -0
- data/plugins/ruby_development/ui/project_widget.rb +112 -0
- data/plugins/ruby_development/ui/project_widget.ui +108 -0
- data/plugins/ruby_runner/config_widget.rb +116 -0
- data/plugins/ruby_runner/plugin.yaml +26 -0
- data/plugins/ruby_runner/project_widget.rb +62 -0
- data/plugins/ruby_runner/ruby.png +0 -0
- data/plugins/ruby_runner/ruby_interpretersui.rc +26 -0
- data/plugins/ruby_runner/ruby_runner.rb +411 -0
- data/plugins/ruby_runner/ui/config_widget.rb +92 -0
- data/plugins/ruby_runner/ui/config_widget.ui +91 -0
- data/plugins/ruby_runner/ui/project_widget.rb +60 -0
- data/plugins/ruby_runner/ui/project_widget.ui +48 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +59 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.ui +44 -0
- data/plugins/state/plugin.yaml +28 -0
- data/plugins/state/state.rb +520 -0
- data/plugins/state/ui/config_widget.rb +92 -0
- data/plugins/state/ui/config_widget.ui +89 -0
- data/plugins/syntax_checker/plugin.yaml +18 -0
- data/plugins/syntax_checker/syntax_checker.rb +662 -0
- data/ruber.desktop +10 -0
- data/spec/annotation_model_spec.rb +174 -0
- data/spec/common.rb +119 -0
- data/spec/component_manager_spec.rb +1259 -0
- data/spec/document_list_spec.rb +626 -0
- data/spec/document_project_spec.rb +373 -0
- data/spec/document_spec.rb +779 -0
- data/spec/editor_view_spec.rb +167 -0
- data/spec/external_program_plugin_spec.rb +676 -0
- data/spec/filtered_output_widget_spec.rb +642 -0
- data/spec/gui_states_handler_spec.rb +304 -0
- data/spec/kde_config_option_backend_spec.rb +214 -0
- data/spec/kde_sugar_spec.rb +101 -0
- data/spec/ktexteditor_wrapper_spec.rb +305 -0
- data/spec/output_widget_spec.rb +1703 -0
- data/spec/plugin_spec.rb +1393 -0
- data/spec/plugin_specification_reader_spec.rb +1765 -0
- data/spec/plugin_specification_spec.rb +401 -0
- data/spec/project_backend_spec.rb +172 -0
- data/spec/project_files_list_spec.rb +401 -0
- data/spec/project_list_spec.rb +511 -0
- data/spec/project_spec.rb +990 -0
- data/spec/qt_sugar_spec.rb +328 -0
- data/spec/settings_container_spec.rb +617 -0
- data/spec/settings_dialog_manager_spec.rb +773 -0
- data/spec/settings_dialog_spec.rb +419 -0
- data/spec/state_spec.rb +991 -0
- data/spec/utils_spec.rb +406 -0
- data/spec/workspace_spec.rb +869 -0
- data/spec/yaml_option_backend_spec.rb +246 -0
- 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
|