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.
- 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,92 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
** Form generated from reading ui file 'config_widget.ui'
|
|
3
|
+
**
|
|
4
|
+
** Created: ven ott 29 17:39:04 2010
|
|
5
|
+
** by: Qt User Interface Compiler version 4.7.0
|
|
6
|
+
**
|
|
7
|
+
** WARNING! All changes made in this file will be lost when recompiling ui file!
|
|
8
|
+
=end
|
|
9
|
+
|
|
10
|
+
class Ui_StateConfigWidget
|
|
11
|
+
attr_reader :verticalLayout
|
|
12
|
+
attr_reader :groupBox
|
|
13
|
+
attr_reader :gridLayout
|
|
14
|
+
attr_reader :_state__restore_cursor_position
|
|
15
|
+
attr_reader :_state__restore_project_files
|
|
16
|
+
attr_reader :horizontalLayout_2
|
|
17
|
+
attr_reader :label_2
|
|
18
|
+
attr_reader :_state__startup_behaviour
|
|
19
|
+
|
|
20
|
+
def setupUi(stateConfigWidget)
|
|
21
|
+
if stateConfigWidget.objectName.nil?
|
|
22
|
+
stateConfigWidget.objectName = "stateConfigWidget"
|
|
23
|
+
end
|
|
24
|
+
stateConfigWidget.resize(400, 128)
|
|
25
|
+
@verticalLayout = Qt::VBoxLayout.new(stateConfigWidget)
|
|
26
|
+
@verticalLayout.objectName = "verticalLayout"
|
|
27
|
+
@groupBox = Qt::GroupBox.new(stateConfigWidget)
|
|
28
|
+
@groupBox.objectName = "groupBox"
|
|
29
|
+
@gridLayout = Qt::GridLayout.new(@groupBox)
|
|
30
|
+
@gridLayout.objectName = "gridLayout"
|
|
31
|
+
@_state__restore_cursor_position = Qt::CheckBox.new(@groupBox)
|
|
32
|
+
@_state__restore_cursor_position.objectName = "_state__restore_cursor_position"
|
|
33
|
+
|
|
34
|
+
@gridLayout.addWidget(@_state__restore_cursor_position, 0, 0, 1, 1)
|
|
35
|
+
|
|
36
|
+
@_state__restore_project_files = Qt::CheckBox.new(@groupBox)
|
|
37
|
+
@_state__restore_project_files.objectName = "_state__restore_project_files"
|
|
38
|
+
|
|
39
|
+
@gridLayout.addWidget(@_state__restore_project_files, 1, 0, 1, 1)
|
|
40
|
+
|
|
41
|
+
@horizontalLayout_2 = Qt::HBoxLayout.new()
|
|
42
|
+
@horizontalLayout_2.objectName = "horizontalLayout_2"
|
|
43
|
+
@label_2 = Qt::Label.new(@groupBox)
|
|
44
|
+
@label_2.objectName = "label_2"
|
|
45
|
+
|
|
46
|
+
@horizontalLayout_2.addWidget(@label_2)
|
|
47
|
+
|
|
48
|
+
@_state__startup_behaviour = KDE::ComboBox.new(@groupBox)
|
|
49
|
+
@_state__startup_behaviour.objectName = "_state__startup_behaviour"
|
|
50
|
+
|
|
51
|
+
@horizontalLayout_2.addWidget(@_state__startup_behaviour)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@gridLayout.addLayout(@horizontalLayout_2, 2, 0, 1, 1)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@verticalLayout.addWidget(@groupBox)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
retranslateUi(stateConfigWidget)
|
|
61
|
+
|
|
62
|
+
Qt::MetaObject.connectSlotsByName(stateConfigWidget)
|
|
63
|
+
end # setupUi
|
|
64
|
+
|
|
65
|
+
def setup_ui(stateConfigWidget)
|
|
66
|
+
setupUi(stateConfigWidget)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def retranslateUi(stateConfigWidget)
|
|
70
|
+
stateConfigWidget.windowTitle = Qt::Application.translate("StateConfigWidget", "Form", nil, Qt::Application::UnicodeUTF8)
|
|
71
|
+
@groupBox.title = Qt::Application.translate("StateConfigWidget", "State", nil, Qt::Application::UnicodeUTF8)
|
|
72
|
+
@_state__restore_cursor_position.text = Qt::Application.translate("StateConfigWidget", "Restore cursor position when opening documents", nil, Qt::Application::UnicodeUTF8)
|
|
73
|
+
@_state__restore_project_files.text = Qt::Application.translate("StateConfigWidget", "Restore open files when opening a project", nil, Qt::Application::UnicodeUTF8)
|
|
74
|
+
@label_2.text = Qt::Application.translate("StateConfigWidget", "On startup", nil, Qt::Application::UnicodeUTF8)
|
|
75
|
+
@_state__startup_behaviour.insertItems(0, [Qt::Application.translate("StateConfigWidget", "Restore open project and files", nil, Qt::Application::UnicodeUTF8),
|
|
76
|
+
Qt::Application.translate("StateConfigWidget", "Restore open files only", nil, Qt::Application::UnicodeUTF8),
|
|
77
|
+
Qt::Application.translate("StateConfigWidget", "Restore open project only", nil, Qt::Application::UnicodeUTF8),
|
|
78
|
+
Qt::Application.translate("StateConfigWidget", "Keep empty workspace", nil, Qt::Application::UnicodeUTF8)])
|
|
79
|
+
@_state__startup_behaviour.setProperty("access", Qt::Variant.new(Qt::Application.translate("StateConfigWidget", "$startup_behaviour", nil, Qt::Application::UnicodeUTF8)))
|
|
80
|
+
end # retranslateUi
|
|
81
|
+
|
|
82
|
+
def retranslate_ui(stateConfigWidget)
|
|
83
|
+
retranslateUi(stateConfigWidget)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
module Ui
|
|
89
|
+
class StateConfigWidget < Ui_StateConfigWidget
|
|
90
|
+
end
|
|
91
|
+
end # module Ui
|
|
92
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<ui version="4.0">
|
|
3
|
+
<class>StateConfigWidget</class>
|
|
4
|
+
<widget class="QWidget" name="StateConfigWidget">
|
|
5
|
+
<property name="geometry">
|
|
6
|
+
<rect>
|
|
7
|
+
<x>0</x>
|
|
8
|
+
<y>0</y>
|
|
9
|
+
<width>400</width>
|
|
10
|
+
<height>128</height>
|
|
11
|
+
</rect>
|
|
12
|
+
</property>
|
|
13
|
+
<property name="windowTitle">
|
|
14
|
+
<string>Form</string>
|
|
15
|
+
</property>
|
|
16
|
+
<layout class="QVBoxLayout" name="verticalLayout">
|
|
17
|
+
<item>
|
|
18
|
+
<widget class="QGroupBox" name="groupBox">
|
|
19
|
+
<property name="title">
|
|
20
|
+
<string>State</string>
|
|
21
|
+
</property>
|
|
22
|
+
<layout class="QGridLayout" name="gridLayout">
|
|
23
|
+
<item row="0" column="0">
|
|
24
|
+
<widget class="QCheckBox" name="_state__restore_cursor_position">
|
|
25
|
+
<property name="text">
|
|
26
|
+
<string>Restore cursor position when opening documents</string>
|
|
27
|
+
</property>
|
|
28
|
+
</widget>
|
|
29
|
+
</item>
|
|
30
|
+
<item row="1" column="0">
|
|
31
|
+
<widget class="QCheckBox" name="_state__restore_project_files">
|
|
32
|
+
<property name="text">
|
|
33
|
+
<string>Restore open files when opening a project</string>
|
|
34
|
+
</property>
|
|
35
|
+
</widget>
|
|
36
|
+
</item>
|
|
37
|
+
<item row="2" column="0">
|
|
38
|
+
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
39
|
+
<item>
|
|
40
|
+
<widget class="QLabel" name="label_2">
|
|
41
|
+
<property name="text">
|
|
42
|
+
<string>On startup</string>
|
|
43
|
+
</property>
|
|
44
|
+
</widget>
|
|
45
|
+
</item>
|
|
46
|
+
<item>
|
|
47
|
+
<widget class="KComboBox" name="_state__startup_behaviour">
|
|
48
|
+
<property name="access" stdset="0">
|
|
49
|
+
<string>$startup_behaviour</string>
|
|
50
|
+
</property>
|
|
51
|
+
<item>
|
|
52
|
+
<property name="text">
|
|
53
|
+
<string>Restore open project and files</string>
|
|
54
|
+
</property>
|
|
55
|
+
</item>
|
|
56
|
+
<item>
|
|
57
|
+
<property name="text">
|
|
58
|
+
<string>Restore open files only</string>
|
|
59
|
+
</property>
|
|
60
|
+
</item>
|
|
61
|
+
<item>
|
|
62
|
+
<property name="text">
|
|
63
|
+
<string>Restore open project only</string>
|
|
64
|
+
</property>
|
|
65
|
+
</item>
|
|
66
|
+
<item>
|
|
67
|
+
<property name="text">
|
|
68
|
+
<string>Keep empty workspace</string>
|
|
69
|
+
</property>
|
|
70
|
+
</item>
|
|
71
|
+
</widget>
|
|
72
|
+
</item>
|
|
73
|
+
</layout>
|
|
74
|
+
</item>
|
|
75
|
+
</layout>
|
|
76
|
+
</widget>
|
|
77
|
+
</item>
|
|
78
|
+
</layout>
|
|
79
|
+
</widget>
|
|
80
|
+
<customwidgets>
|
|
81
|
+
<customwidget>
|
|
82
|
+
<class>KComboBox</class>
|
|
83
|
+
<extends>QComboBox</extends>
|
|
84
|
+
<header>kcombobox.h</header>
|
|
85
|
+
</customwidget>
|
|
86
|
+
</customwidgets>
|
|
87
|
+
<resources/>
|
|
88
|
+
<connections/>
|
|
89
|
+
</ui>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: syntax_checker
|
|
2
|
+
version: 0.0.1
|
|
3
|
+
about:
|
|
4
|
+
authors: [Stefano Crocco, stefano.crocco@alice.it]
|
|
5
|
+
license: :gpl
|
|
6
|
+
description: Checks the syntax of the current document
|
|
7
|
+
icon: tools-check-spelling
|
|
8
|
+
bug_address: http://github.com/stcrocco/ruber/issues
|
|
9
|
+
class: Ruber::SyntaxChecker::SyntaxCheckerPlugin
|
|
10
|
+
require: syntax_checker
|
|
11
|
+
deps: ruby_development
|
|
12
|
+
config_options:
|
|
13
|
+
syntax_checker:
|
|
14
|
+
automatic_check: {default: true}
|
|
15
|
+
config_widgets:
|
|
16
|
+
{caption: Syntax, code: 'Qt::CheckBox.new("&Automatically check syntax"){self.object_name = "_syntax_checker__automatic_check"}', pixmap: tools-check-spelling.png}
|
|
17
|
+
extensions:
|
|
18
|
+
syntax_checker: {class: Ruber::SyntaxChecker::SyntaxCheckerExtension, scope: document}
|
|
@@ -0,0 +1,662 @@
|
|
|
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 'tempfile'
|
|
22
|
+
require 'open3'
|
|
23
|
+
|
|
24
|
+
require 'facets/boolean'
|
|
25
|
+
|
|
26
|
+
module Ruber
|
|
27
|
+
|
|
28
|
+
=begin rdoc
|
|
29
|
+
Module for the *Syntax checker* plugin.
|
|
30
|
+
|
|
31
|
+
This plugin provides a framework to let the user know whether the file in the
|
|
32
|
+
current editor view is syntactically correct or not (according to the most appropriate
|
|
33
|
+
sytnax for that kind of file). The result of the syntax check is displayed in a
|
|
34
|
+
widget at the right end of the status bar (this will be referred to as the *syntax
|
|
35
|
+
result widget). This implementation of the @syntax_checker@ feature uses a @KDE::Led@
|
|
36
|
+
whose colour changes according to the result of the syntax check, but other plugins
|
|
37
|
+
may use other widgets. If the user right clikcs on the widget, a menu showing a
|
|
38
|
+
list of syntax errors is displayed. Clicking on it will jump to the corresponding
|
|
39
|
+
like, while clicking on the widget itself jumps to the line corresponding to the
|
|
40
|
+
first error.
|
|
41
|
+
|
|
42
|
+
The syntax check is run every time the file is activated or saved and the user can choose to
|
|
43
|
+
have it run automatically when one second has passed since he last edited the
|
|
44
|
+
file. The reasoning is that it makes little sense to check the syntax while the
|
|
45
|
+
user is writing text: it will most likely be wrong (because the user hasn't finished
|
|
46
|
+
writing what he wants), may quickly change from valid to invalid depending on the
|
|
47
|
+
point the user has reached in writing, and the user won't look at it because he's
|
|
48
|
+
busy writing.
|
|
49
|
+
|
|
50
|
+
The kind of syntax check to perform on a document is decided according to the mimetype
|
|
51
|
+
of the document (this means that only documents associated with a file can be
|
|
52
|
+
syntax-checked). Currently, this plugin contains syntax checkers for ruby files
|
|
53
|
+
and YAML files, but syntax checkers for other kind of files can be added by other
|
|
54
|
+
plugins (see the documentation for {SyntaxCheckerPlugin#register_syntax_checker register_syntax_checker}
|
|
55
|
+
for details)
|
|
56
|
+
|
|
57
|
+
@api feature syntax_checker
|
|
58
|
+
@plugin
|
|
59
|
+
@config_option syntax_checker automatic_check [Boolean] Whether to perform automatic
|
|
60
|
+
syntax checks after one second of inactivity
|
|
61
|
+
=end
|
|
62
|
+
module SyntaxChecker
|
|
63
|
+
|
|
64
|
+
=begin rdoc
|
|
65
|
+
Plugin object for the @sytax_checker@ feature
|
|
66
|
+
|
|
67
|
+
@api class SyntaxChecker::SyntaxCheckerPlugin
|
|
68
|
+
@api_method #register_syntax_checker
|
|
69
|
+
@api_method #remove_syntax_checker
|
|
70
|
+
=end
|
|
71
|
+
class SyntaxCheckerPlugin < Plugin
|
|
72
|
+
|
|
73
|
+
=begin rdoc
|
|
74
|
+
Class containing the information about a syntax error. It has four attributes:
|
|
75
|
+
line:= the line at which the syntax error occurred (note that this starts from
|
|
76
|
+
1, while Document and EditorView count lines from 0)
|
|
77
|
+
message:= a string describing the syntax error
|
|
78
|
+
code:= a string with the code around the point where the syntax error occurred
|
|
79
|
+
column:= the column at which the syntax error occurred
|
|
80
|
+
|
|
81
|
+
This class'constructor can take up to four parameters, corresponding (in
|
|
82
|
+
the same order) to the four attributes described above.
|
|
83
|
+
=end
|
|
84
|
+
ErrorDescription = Struct.new(:line, :message, :code, :column)
|
|
85
|
+
|
|
86
|
+
=begin rdoc
|
|
87
|
+
Colors used to display the different states of the document
|
|
88
|
+
=end
|
|
89
|
+
COLORS = {
|
|
90
|
+
:correct => Qt::Color.new(Qt.green),
|
|
91
|
+
:error => Qt::Color.new(Qt.red),
|
|
92
|
+
:unknown => Qt::Color.new(Qt.gray)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
=begin rdoc
|
|
96
|
+
Signal emitted when it's time to perform an automatic syntax check
|
|
97
|
+
=end
|
|
98
|
+
signals :timeout
|
|
99
|
+
|
|
100
|
+
=begin rdoc
|
|
101
|
+
Creates an instance of the plugin
|
|
102
|
+
|
|
103
|
+
It also registers the two built-in syntax checkers
|
|
104
|
+
|
|
105
|
+
@param [Ruber::PluginSpecification] the specification associated to the
|
|
106
|
+
plugin
|
|
107
|
+
=end
|
|
108
|
+
def initialize psf
|
|
109
|
+
super
|
|
110
|
+
@availlable_syntax_checkers = {}
|
|
111
|
+
register_syntax_checker RubySyntaxChecker, 'application/x-ruby',
|
|
112
|
+
%w[*.rb rakefile Rakefile]
|
|
113
|
+
register_syntax_checker YamlSyntaxChecker, [], %w[*.yml *.yaml]
|
|
114
|
+
@led = SyntaxResultWidget.new
|
|
115
|
+
Ruber[:main_window].status_bar.add_permanent_widget @led
|
|
116
|
+
mark_document_as :unknown
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
=begin rdoc
|
|
120
|
+
Instantiates an appropriate syntax checker for a document
|
|
121
|
+
|
|
122
|
+
@param [Document] doc the document to create the syntax checker for
|
|
123
|
+
@return [Object,nil] a syntax checker suitable for _doc_ or *nil* if _doc_ is not
|
|
124
|
+
associated with a file or no syntax checker has been registered for <i>doc</i>'s extension
|
|
125
|
+
or mimetype
|
|
126
|
+
=end
|
|
127
|
+
def syntax_checker_for doc
|
|
128
|
+
checker_cls = @availlable_syntax_checkers.find! do |cls, data|
|
|
129
|
+
doc.file_type_match?(*data) ? cls : nil
|
|
130
|
+
end
|
|
131
|
+
checker_cls ? checker_cls.new(doc) : nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
=begin rdoc
|
|
135
|
+
Tells the plugin to display the result of the syntax check for the current document
|
|
136
|
+
|
|
137
|
+
@param [Symbol] status the result of the syntax check. It can be one of @:correct@,
|
|
138
|
+
@:error@ or @:unknown@ (if no syntax checker was availlable for the document)
|
|
139
|
+
@param [Array<SyntaxCheckerPlugin::ErrorDescription>] errors an array containing
|
|
140
|
+
instances of class {SyntaxCheckerPlugin::ErrorDescription} describing each of the
|
|
141
|
+
syntax error which where found in the document. This argument is only used if
|
|
142
|
+
_status_ is @:error@
|
|
143
|
+
@return [SyntaxCheckerPlugin] *self*
|
|
144
|
+
=end
|
|
145
|
+
def mark_document_as status, errors = []
|
|
146
|
+
msg = case status
|
|
147
|
+
when :correct then 'Syntax OK'
|
|
148
|
+
when :unknown then 'Document type unknown'
|
|
149
|
+
when :error then errors.map{|e| format_error_message e}.join "\n"
|
|
150
|
+
end
|
|
151
|
+
@led.tool_tip = msg
|
|
152
|
+
@led.color = COLORS[status]
|
|
153
|
+
self
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
=begin rdoc
|
|
157
|
+
Registers a new syntax checker.
|
|
158
|
+
|
|
159
|
+
A syntax checker is an object which analyzes the contents of a document and tells
|
|
160
|
+
whether its syntax is correct or not. To register it with the plugin, you need
|
|
161
|
+
to pass the object's class, toghether with the mimetypes and file patterns it
|
|
162
|
+
should be used to check syntax for, to this method. When a new document with the
|
|
163
|
+
appropriate mimetype or file name is created, a new instances of the syntax checker
|
|
164
|
+
class will be created.
|
|
165
|
+
|
|
166
|
+
The syntax checker class must have the following characteristics:
|
|
167
|
+
* its constructor should take the document as only parameter
|
|
168
|
+
* it should have a @check@ method which takes a string (corresponding to the text
|
|
169
|
+
of the document) and performs the syntax check on it. It must return a string
|
|
170
|
+
with all the information needed to retrieve the information about the single
|
|
171
|
+
errors.
|
|
172
|
+
* it should have a @convert_check_result@ method, which takes the string
|
|
173
|
+
returned by the @check@ method and converts it to an array of {ErrorDescription}
|
|
174
|
+
objects, with each object containing the information about a single error. If
|
|
175
|
+
the document doesn't contain any syntax error, this method should return an
|
|
176
|
+
empty array.
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@param [Class] cls the class to instantiate to create the syntax checker
|
|
180
|
+
@param [String, <String>] mimetypes the mimetypes to use the new syntax checker
|
|
181
|
+
for. It has the format described in {Document#file_type_match?}
|
|
182
|
+
@param [String, <String>] patterns the file patterns to use the new syntax checker
|
|
183
|
+
for. It has the format described in {Document#file_type_match?}
|
|
184
|
+
@return [SyntaxCheckerPlugin] *self*
|
|
185
|
+
@raise ArgumentError if _cls_ had already been registered as a syntax checker.
|
|
186
|
+
=end
|
|
187
|
+
def register_syntax_checker cls, mimetypes, patterns = []
|
|
188
|
+
if @availlable_syntax_checkers.include? cls
|
|
189
|
+
raise ArgumentError, "class #{cls} has already been registered as syntax checker"
|
|
190
|
+
else
|
|
191
|
+
@availlable_syntax_checkers[cls] = [mimetypes, patterns] end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
=begin rdoc
|
|
195
|
+
Removes a registered syntax checker.
|
|
196
|
+
|
|
197
|
+
Nothing is done if the syntax checker hadn't been registered.
|
|
198
|
+
|
|
199
|
+
@param [Class] cls the class of the syntax checker to remove
|
|
200
|
+
@return [Boolean] *true* if the syntax checker was removed and *false* if it wasn't
|
|
201
|
+
registered
|
|
202
|
+
=end
|
|
203
|
+
def remove_syntax_checker cls
|
|
204
|
+
res = @availlable_syntax_checkers.delete cls
|
|
205
|
+
res.to_bool
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
=begin rdoc
|
|
209
|
+
Prepares the plugin for application shutdown
|
|
210
|
+
@return [SyntaxCheckerPlugin] *self*
|
|
211
|
+
=end
|
|
212
|
+
def shutdown
|
|
213
|
+
@timer.stop
|
|
214
|
+
self
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
=begin rdoc
|
|
218
|
+
Prepares the plugin to be unloaded
|
|
219
|
+
|
|
220
|
+
@return [SyntaxCheckerPlugin] *self*
|
|
221
|
+
=end
|
|
222
|
+
def unload
|
|
223
|
+
Ruber[:main_window].status_bar.remove_widget @led
|
|
224
|
+
super
|
|
225
|
+
self
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
=begin rdoc
|
|
229
|
+
Generates an error message from an error object
|
|
230
|
+
|
|
231
|
+
The returned string contains the error message together with information about
|
|
232
|
+
the line and the column (if known) where the error happened
|
|
233
|
+
|
|
234
|
+
@param [ErrorDescription] error the object containing the error message
|
|
235
|
+
@return [String] a string containing the error message, including line and row
|
|
236
|
+
where the error happened, formatted in a standard way
|
|
237
|
+
=end
|
|
238
|
+
def format_error_message error
|
|
239
|
+
res = "Line #{error.line}"
|
|
240
|
+
res += " Col #{error.column}" if error.column
|
|
241
|
+
res += ": #{error.message}"
|
|
242
|
+
res
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
=begin
|
|
246
|
+
Starts the timer for the automatic check
|
|
247
|
+
|
|
248
|
+
@return [SyntaxCheckerPlugin] *self*
|
|
249
|
+
=end
|
|
250
|
+
def start_timer
|
|
251
|
+
if @timer
|
|
252
|
+
@timer.stop
|
|
253
|
+
@timer.start
|
|
254
|
+
end
|
|
255
|
+
self
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
=begin
|
|
259
|
+
Stops the timer for the automatic check
|
|
260
|
+
|
|
261
|
+
@return [SyntaxCheckerPlugin] *self*
|
|
262
|
+
=end
|
|
263
|
+
def stop_timer
|
|
264
|
+
@timer.stop if @timer
|
|
265
|
+
self
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
=begin rdoc
|
|
269
|
+
Loads the settings
|
|
270
|
+
|
|
271
|
+
If automatic checking has been enabled, a timer is created; if it has been disabled,
|
|
272
|
+
the timer is destroyed
|
|
273
|
+
|
|
274
|
+
@return [nil]
|
|
275
|
+
=end
|
|
276
|
+
def load_settings
|
|
277
|
+
auto_check = Ruber[:config][:syntax_checker, :automatic_check]
|
|
278
|
+
if auto_check and !@timer
|
|
279
|
+
@timer = Qt::Timer.new self
|
|
280
|
+
@timer.interval = 1000
|
|
281
|
+
connect @timer, SIGNAL(:timeout), self, SIGNAL(:timeout)
|
|
282
|
+
@timer.start if Ruber[:main_window].current_document
|
|
283
|
+
elsif !auto_check
|
|
284
|
+
@timer.disconnect self
|
|
285
|
+
@timer.dispose
|
|
286
|
+
@timer = nil
|
|
287
|
+
end
|
|
288
|
+
nil
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
=begin rdoc
|
|
294
|
+
Document extension which checks the syntax for a document. The syntax check happens:
|
|
295
|
+
* when the document becomes active
|
|
296
|
+
* when the document is saved
|
|
297
|
+
* one second after the last modification (if the user has enabled automatic checks)
|
|
298
|
+
|
|
299
|
+
The check can only be done if a syntax checker for the document's mimetype
|
|
300
|
+
or file extension exists. New syntax checkers must be added to the syntax checker
|
|
301
|
+
plugin using the {SyntaxCheckerPlugin#register_syntax_checker} method,
|
|
302
|
+
and can be removed using {SyntaxCheckerPlugin#remove_syntax_checker}.
|
|
303
|
+
|
|
304
|
+
The appropriate syntax checker for the document is chosen when the extension is
|
|
305
|
+
added to the document, and is changed (if needed) whenever the @document_name@
|
|
306
|
+
of the document changes.
|
|
307
|
+
|
|
308
|
+
*Note:* in the documentation of this class, the term _document_ will refer
|
|
309
|
+
to the Document passed as argument to the constructor.
|
|
310
|
+
=end
|
|
311
|
+
class SyntaxCheckerExtension < Qt::Object
|
|
312
|
+
|
|
313
|
+
include Extension
|
|
314
|
+
|
|
315
|
+
=begin rdoc
|
|
316
|
+
Signal emitted after a syntax check has been completed
|
|
317
|
+
|
|
318
|
+
@param [String] result a string containing the results of the syntax check
|
|
319
|
+
=end
|
|
320
|
+
signals 'check_done(QString)'
|
|
321
|
+
|
|
322
|
+
slots 'update_ui(QString)', :check, :document_activated,
|
|
323
|
+
:document_deactivated, :document_name_changed, :create_syntax_checker
|
|
324
|
+
|
|
325
|
+
=begin rdoc
|
|
326
|
+
A list of the syntax errors found in the document
|
|
327
|
+
|
|
328
|
+
The list is empty if the document doesn't contain syntax errors or if it hasn't
|
|
329
|
+
been checked yet
|
|
330
|
+
|
|
331
|
+
@return [<SyntaxCheckerPlugin::ErrorDescription>] the syntax errors found in the
|
|
332
|
+
document
|
|
333
|
+
=end
|
|
334
|
+
attr_reader :errors
|
|
335
|
+
|
|
336
|
+
=begin rdoc
|
|
337
|
+
Creates a new instance
|
|
338
|
+
|
|
339
|
+
@param [DocumentProject] prj the project associated with the document
|
|
340
|
+
=end
|
|
341
|
+
def initialize prj
|
|
342
|
+
super
|
|
343
|
+
@plugin = Ruber[:syntax_checker]
|
|
344
|
+
@doc = prj.document
|
|
345
|
+
@errors = []
|
|
346
|
+
@checker = nil
|
|
347
|
+
connect @doc, SIGNAL(:activated), self, SLOT(:document_activated)
|
|
348
|
+
connect @doc, SIGNAL(:deactivated), self, SLOT(:document_deactivated)
|
|
349
|
+
@doc.connect(SIGNAL('modified_changed(bool, QObject*)')) do |mod, _|
|
|
350
|
+
check if !mod
|
|
351
|
+
end
|
|
352
|
+
connect @doc, SIGNAL('document_name_changed(QString, QObject*)'), self, SLOT(:create_syntax_checker)
|
|
353
|
+
@doc.connect SIGNAL('text_changed(QObject*)') do
|
|
354
|
+
if @doc.active?
|
|
355
|
+
@plugin.stop_timer
|
|
356
|
+
@plugin.start_timer
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
@thread = nil
|
|
360
|
+
create_syntax_checker
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
=begin rdoc
|
|
364
|
+
Sets the syntax status according to the results of the last syntax check
|
|
365
|
+
|
|
366
|
+
It marks the current document as having correct or incorrect syntax depending on
|
|
367
|
+
the contents of _str_.
|
|
368
|
+
|
|
369
|
+
@param [String] str the string containing the results of the syntax check (such as
|
|
370
|
+
the one that a syntax checker's @check@ method). It is passed to the syntax checker's
|
|
371
|
+
@convert_check_result@ method
|
|
372
|
+
@return [nil]
|
|
373
|
+
=end
|
|
374
|
+
def update_ui str
|
|
375
|
+
@errors = @checker.convert_check_result str
|
|
376
|
+
if @errors.empty? then @plugin.mark_document_as :correct
|
|
377
|
+
else @plugin.mark_document_as :error, @errors
|
|
378
|
+
end
|
|
379
|
+
nil
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
=begin rdoc
|
|
383
|
+
Starts a syntax check for the document
|
|
384
|
+
|
|
385
|
+
The syntax check can be synchronous or asynchronous, according to the value of
|
|
386
|
+
the argument. In the first case, this method won't return until the syntax check
|
|
387
|
+
has finished and the UI has been updated. In the second case, the method will
|
|
388
|
+
start the syntax check in a new thread and return immediately. The {#check_done}
|
|
389
|
+
signal will be emitted when the syntax check has been finished.
|
|
390
|
+
|
|
391
|
+
Nothing will be done if no checker exists for the document.
|
|
392
|
+
|
|
393
|
+
*Note:* while an asynchronous syntax check avoids freezing the UI if it takes
|
|
394
|
+
a long time, it seems that it takes much longer than a synchronous check.
|
|
395
|
+
|
|
396
|
+
@param [Boolean] async whether the syntax check should or not be asynchronous
|
|
397
|
+
@todo the decision on whether the check should be synchronous or asynchronous
|
|
398
|
+
should be delegated to the checker
|
|
399
|
+
@return [nil]
|
|
400
|
+
=end
|
|
401
|
+
def check async = false
|
|
402
|
+
return unless @checker
|
|
403
|
+
@plugin.stop_timer
|
|
404
|
+
if async
|
|
405
|
+
@threak.kill if @thread
|
|
406
|
+
@thread = Thread.new(@doc.text) do |str|
|
|
407
|
+
res = @checker.check @doc.text
|
|
408
|
+
emit check_done res
|
|
409
|
+
end
|
|
410
|
+
else
|
|
411
|
+
res = @checker.check @doc.text
|
|
412
|
+
update_ui res
|
|
413
|
+
end
|
|
414
|
+
nil
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
private
|
|
418
|
+
|
|
419
|
+
=begin rdoc
|
|
420
|
+
Creates a syntax checker for the document
|
|
421
|
+
|
|
422
|
+
If needed, it also removes the old one and immediately performs a syntax check
|
|
423
|
+
|
|
424
|
+
@return [nil]
|
|
425
|
+
=end
|
|
426
|
+
def create_syntax_checker
|
|
427
|
+
new_checker = Ruber[:syntax_checker].syntax_checker_for @doc
|
|
428
|
+
if @checker.class != new_checker.class
|
|
429
|
+
@checker.disconnect if @checker
|
|
430
|
+
@checker = nil
|
|
431
|
+
if new_checker
|
|
432
|
+
@checker = new_checker
|
|
433
|
+
connect self, SIGNAL('check_done(QString)'), self, SLOT('update_ui(QString)')
|
|
434
|
+
end
|
|
435
|
+
check if @doc.active?
|
|
436
|
+
end
|
|
437
|
+
nil
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
=begin rdoc
|
|
441
|
+
Informs the extension that the document is not active anymore
|
|
442
|
+
|
|
443
|
+
@return [nil]
|
|
444
|
+
=end
|
|
445
|
+
def document_deactivated
|
|
446
|
+
@thread.kill if @thread
|
|
447
|
+
@plugin.stop_timer
|
|
448
|
+
@plugin.disconnect SIGNAL(:timeout), self, SLOT(:check)
|
|
449
|
+
Ruber[:syntax_checker].mark_document_as :unknown
|
|
450
|
+
nil
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
=begin rdoc
|
|
454
|
+
Informs the extension that the document became active
|
|
455
|
+
|
|
456
|
+
@return [nil]
|
|
457
|
+
=end
|
|
458
|
+
def document_activated
|
|
459
|
+
connect @plugin, SIGNAL(:timeout), self, SLOT(:check)
|
|
460
|
+
check
|
|
461
|
+
nil
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
=begin rdoc
|
|
467
|
+
Class which checks the syntax of a ruby file.
|
|
468
|
+
|
|
469
|
+
To do so, it runs a separate ruby process (using the ruby interpreter set by the
|
|
470
|
+
Ruby Runner plugin) passing it the @-c@ and the @-e@ options with the document's
|
|
471
|
+
content as argument.
|
|
472
|
+
|
|
473
|
+
The process is executed using @Open3.popen3@
|
|
474
|
+
=end
|
|
475
|
+
class RubySyntaxChecker
|
|
476
|
+
|
|
477
|
+
=begin rdoc
|
|
478
|
+
Creates a new instance.
|
|
479
|
+
|
|
480
|
+
@param [Document] doc the document to check
|
|
481
|
+
=end
|
|
482
|
+
def initialize doc
|
|
483
|
+
@doc = doc
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
=begin rdoc
|
|
487
|
+
Checks the syntax of the given string.
|
|
488
|
+
|
|
489
|
+
@param [String] str the string to check (usually, it'll be the document's text)
|
|
490
|
+
@return [String] a string containing the lines of output produced by ruby concerning
|
|
491
|
+
syntax errors or an empty string if there were no syntax error
|
|
492
|
+
=end
|
|
493
|
+
def check str
|
|
494
|
+
ruby = Ruber[:ruby_development].interpreter_for @doc
|
|
495
|
+
Open3.popen3(ruby, '-c', '-e', str) do |in_s, out_s, err_s|
|
|
496
|
+
error = err_s.read
|
|
497
|
+
error.gsub! %r{^-e(?=:\d+:\s+syntax error,)}, @doc.path
|
|
498
|
+
out_s.read.strip != 'Syntax OK' ? error : ''
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
=begin rdoc
|
|
503
|
+
Parses the output of {#check}
|
|
504
|
+
|
|
505
|
+
@param [String] str the string to parse.
|
|
506
|
+
@return [<SyntaxCheckerPlugin::ErrorDescription>] a list of {SyntaxCheckerPlugin::ErrorDescription ErrorDescription}s
|
|
507
|
+
corresponding to the syntax errors mentioned in _str_
|
|
508
|
+
=end
|
|
509
|
+
def convert_check_result str
|
|
510
|
+
groups = [[]]
|
|
511
|
+
lines = str.split "\n"
|
|
512
|
+
lines.each do |l|
|
|
513
|
+
if l.match(/^#{Regexp.quote(@doc.path||'')}:\d+:\s+syntax error,\s+/) then groups << [l]
|
|
514
|
+
else groups[-1] << l
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
groups.delete_if{|g| g.empty?}
|
|
518
|
+
groups.map do |a|
|
|
519
|
+
a.shift.match(/^#{Regexp.quote(@doc.path||'')}:(\d+):\s+syntax error,\s+(.*)/)
|
|
520
|
+
msgs = [$2]
|
|
521
|
+
error = SyntaxCheckerPlugin::ErrorDescription.new $1.to_i
|
|
522
|
+
if a[-1] and a[-1].match(/^\s*\^\s*$/)
|
|
523
|
+
error.code = a[-2]
|
|
524
|
+
# Sometimes, ruby doesn't report the whole line where the error occurs, but only
|
|
525
|
+
# the part nearest it. In this case, the beginning of the line is replaced with ... .
|
|
526
|
+
# In this case, to obtain the correct column number, we try a regexp match
|
|
527
|
+
# between the part of code reported by ruby and the whole line. If it works, we
|
|
528
|
+
# add that position to the one returned by ruby. If it doesn't (for example because
|
|
529
|
+
# the user changed the document in the meantime), we'll just report what ruby reports
|
|
530
|
+
col = a[-1].index('^')
|
|
531
|
+
if a[-2].match(/^\.\.\./)
|
|
532
|
+
lines = @doc.text.split("\n")
|
|
533
|
+
# error.line is 1-based
|
|
534
|
+
l = lines[error.line-1] || ''
|
|
535
|
+
pos = (l =~ /#{Regexp.quote(a[-2][3..-1])}/)
|
|
536
|
+
error.column = pos ? col + pos - 1 : col
|
|
537
|
+
else error.column = col
|
|
538
|
+
end
|
|
539
|
+
a.pop 2
|
|
540
|
+
end
|
|
541
|
+
a.each{|l| msgs << l}
|
|
542
|
+
error.message = msgs.join ' '
|
|
543
|
+
error
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
=begin rdoc
|
|
550
|
+
Class which checks the syntax of a ruby file.
|
|
551
|
+
|
|
552
|
+
It calls the @YAML.load@ method on the document's content within a begin/rescue
|
|
553
|
+
block, rescuing any ArgumentError exception. The message of the exception is used
|
|
554
|
+
to find information about the error
|
|
555
|
+
=end
|
|
556
|
+
class YamlSyntaxChecker
|
|
557
|
+
|
|
558
|
+
=begin rdoc
|
|
559
|
+
Creates a new instance
|
|
560
|
+
|
|
561
|
+
@param [Document] doc the document to check
|
|
562
|
+
=end
|
|
563
|
+
def initialize doc
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
=begin rdoc
|
|
567
|
+
Checks the syntax of the given string.
|
|
568
|
+
|
|
569
|
+
@param [String] str the string to check (usually, it'll be the document's text)
|
|
570
|
+
@return [String] a string containing the lines of output produced by @YAML.load@
|
|
571
|
+
concerning syntax errors or an empty string if there were no syntax error
|
|
572
|
+
=end
|
|
573
|
+
def check str
|
|
574
|
+
begin
|
|
575
|
+
YAML.load str
|
|
576
|
+
''
|
|
577
|
+
rescue ArgumentError => e
|
|
578
|
+
e.message
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
=begin rdoc
|
|
583
|
+
Parses the output of {#check}
|
|
584
|
+
|
|
585
|
+
@param [String] str the string to parse.
|
|
586
|
+
@return [<SyntaxCheckerPlugin::ErrorDescription>] a list of {SyntaxCheckerPlugin::ErrorDescription ErrorDescription}s
|
|
587
|
+
corresponding to the syntax errors mentioned in _str_
|
|
588
|
+
=end
|
|
589
|
+
def convert_check_result str
|
|
590
|
+
return [] if str.empty?
|
|
591
|
+
str.match(/^syntax error on line (\d+), col (\d+): `(.*)'$/)
|
|
592
|
+
error = SyntaxCheckerPlugin::ErrorDescription.new $1.to_i, 'Syntax error', $3.to_s, $2.to_i
|
|
593
|
+
[error]
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
=begin rdoc
|
|
599
|
+
@KDE::Led@ with the ability to jump to the first syntax error on left or middle mouse
|
|
600
|
+
click and to popup a menu with a list of all syntax errors in the current document
|
|
601
|
+
on right click
|
|
602
|
+
=end
|
|
603
|
+
class SyntaxResultWidget < KDE::Led
|
|
604
|
+
|
|
605
|
+
=begin rdoc
|
|
606
|
+
Override of @Qt::Widget#mouseReleaseEvent@
|
|
607
|
+
|
|
608
|
+
If the event refers to the left button and the current document contains syntax
|
|
609
|
+
errors, it moves the cursor in the editor view to the position of the first error
|
|
610
|
+
|
|
611
|
+
@return [nil]
|
|
612
|
+
=end
|
|
613
|
+
def mouseReleaseEvent e
|
|
614
|
+
return unless e.button == Qt::LeftButton or e.button == Qt::MidButton
|
|
615
|
+
doc = Ruber[:main_window].current_document
|
|
616
|
+
view = Ruber[:main_window].active_editor
|
|
617
|
+
return unless view and doc and doc.extension(:syntax_checker)
|
|
618
|
+
checker = doc.extension(:syntax_checker)
|
|
619
|
+
err = checker.errors.first
|
|
620
|
+
if err
|
|
621
|
+
view.go_to err.line - 1, (err.column || 0)
|
|
622
|
+
Ruber[:main_window].status_bar.show_message Ruber[:syntax_checker].format_error_message(err), 4000
|
|
623
|
+
end
|
|
624
|
+
nil
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
=begin rdoc
|
|
628
|
+
Override of @Qt::Widget#contextMenuEvent@
|
|
629
|
+
|
|
630
|
+
If the current document contains syntax errors, it displays a menu listing them.
|
|
631
|
+
Each action in the menu moves the cursor in the editor view to display to the line
|
|
632
|
+
and column of the corresponding error.
|
|
633
|
+
|
|
634
|
+
If the current document doesn't contain syntax errors, the menu will only contain
|
|
635
|
+
an entry with text @Syntax OK@ which does nothing when activated.
|
|
636
|
+
|
|
637
|
+
@return [nil]
|
|
638
|
+
=end
|
|
639
|
+
def contextMenuEvent event
|
|
640
|
+
doc = Ruber[:main_window].current_document
|
|
641
|
+
view = Ruber[:main_window].active_editor
|
|
642
|
+
return unless doc and view and (checker = doc.extension :syntax_checker)
|
|
643
|
+
errors = checker.errors
|
|
644
|
+
actions = errors.map do |e|
|
|
645
|
+
a = KDE::Action.new Ruber[:syntax_checker].format_error_message(e), self
|
|
646
|
+
end
|
|
647
|
+
actions << KDE::Action.new('Syntax OK', self) if actions.empty?
|
|
648
|
+
res = Qt::Menu.exec(actions, event.global_pos)
|
|
649
|
+
return if !res or errors.empty?
|
|
650
|
+
idx = actions.index res
|
|
651
|
+
return unless idx
|
|
652
|
+
error = errors[idx]
|
|
653
|
+
view.go_to error.line - 1, (error.column || 0)
|
|
654
|
+
Ruber[:main_window].status_bar.show_message res.text, 4000
|
|
655
|
+
nil
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
end
|