ruber 0.0.9 → 0.0.10
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/CHANGES +42 -1
- data/lib/ruber/application/application.rb +25 -5
- data/lib/ruber/application/plugin.yaml +2 -10
- data/lib/ruber/component_manager.rb +2 -2
- data/lib/ruber/document_project.rb +5 -3
- data/lib/ruber/editor/document.rb +5 -4
- data/lib/ruber/editor/ktexteditor_wrapper.rb +1 -1
- data/lib/ruber/exception_widgets.rb +1 -1
- data/lib/ruber/main_window/main_window.rb +4 -3
- data/lib/ruber/main_window/main_window_actions.rb +2 -2
- data/lib/ruber/main_window/main_window_internal.rb +1 -1
- data/lib/ruber/output_widget.rb +17 -5
- data/lib/ruber/project.rb +34 -3
- data/lib/ruber/project_dir_scanner.rb +171 -0
- data/lib/ruber/settings_container.rb +7 -7
- data/lib/ruber/settings_dialog.rb +24 -24
- data/lib/ruber/version.rb +1 -1
- data/lib/ruber/world/environment.rb +1 -0
- data/lib/ruber/world/plugin.yaml +7 -2
- data/lib/ruber/{application → world}/project_files_widget.rb +0 -0
- data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.rb +2 -2
- data/lib/ruber/{application → world}/ui/project_files_rule_chooser_widget.ui +0 -0
- data/lib/ruber/{application → world}/ui/project_files_widget.rb +2 -2
- data/lib/ruber/{application → world}/ui/project_files_widget.ui +0 -0
- data/plugins/auto_end/auto_end.rb +21 -18
- data/plugins/autosave/autosave.rb +1 -1
- data/plugins/find_in_files/find_in_files.rb +1 -1
- data/plugins/irb/irb.png +0 -0
- data/plugins/irb/irb.rb +142 -0
- data/plugins/irb/irb.svg +240 -0
- data/plugins/irb/irb_controller.rb +541 -0
- data/plugins/irb/irbrc.rb +21 -0
- data/plugins/irb/plugin.yaml +19 -0
- data/plugins/irb/ui/irb_config_widget.rb +151 -0
- data/plugins/irb/ui/irb_config_widget.ui +123 -0
- data/plugins/irb/ui/irb_tool_widget.rb +97 -0
- data/plugins/irb/ui/irb_tool_widget.ui +86 -0
- data/plugins/project_browser/project_browser.rb +1 -1
- data/plugins/rspec/plugin.yaml +6 -3
- data/plugins/rspec/rspec.rb +172 -473
- data/plugins/rspec/tool_widget.rb +462 -0
- data/plugins/rspec/ui/rspec_project_widget.rb +58 -38
- data/plugins/rspec/ui/rspec_project_widget.ui +68 -64
- data/plugins/ruberri/class_formatter.rb +126 -0
- data/plugins/ruberri/method_formatter.rb +90 -0
- data/plugins/ruberri/plugin.yaml +13 -0
- data/plugins/ruberri/ruberri.rb +226 -0
- data/plugins/ruberri/search.rb +111 -0
- data/plugins/ruberri/ui/tool_widget.rb +73 -0
- data/plugins/ruberri/ui/tool_widget.ui +49 -0
- data/plugins/ruby_runner/ruby_runner.rb +2 -2
- data/plugins/ruby_syntax_checker/plugin.yaml +11 -0
- data/plugins/ruby_syntax_checker/ruby_syntax_checker.rb +147 -0
- data/plugins/syntax_checker/plugin.yaml +10 -6
- data/plugins/syntax_checker/syntax_checker.rb +216 -520
- data/plugins/syntax_checker/ui/config_widget.rb +61 -0
- data/plugins/syntax_checker/ui/config_widget.ui +44 -0
- data/plugins/yaml_syntax_checker/plugin.yaml +11 -0
- data/plugins/yaml_syntax_checker/yaml_syntax_checker.rb +62 -0
- data/ruber.desktop +0 -0
- data/spec/auto_end_spec.rb +224 -186
- data/spec/document_project_spec.rb +9 -1
- data/spec/document_spec.rb +9 -0
- data/spec/environment_spec.rb +12 -0
- data/spec/output_widget_spec.rb +69 -2
- data/spec/project_dir_scanner_spec.rb +195 -0
- data/spec/project_spec.rb +43 -73
- data/spec/ruby_syntax_checker_spec.rb +361 -0
- data/spec/syntax_checker_spec.rb +1132 -0
- data/spec/yaml_syntax_checker_spec.rb +130 -0
- metadata +232 -225
- data/lib/ruber/application/project_files_list.rb +0 -320
- data/spec/project_files_list_spec.rb +0 -411
@@ -0,0 +1,49 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ui version="4.0">
|
3
|
+
<class>RIToolWidget</class>
|
4
|
+
<widget class="QWidget" name="RIToolWidget">
|
5
|
+
<property name="geometry">
|
6
|
+
<rect>
|
7
|
+
<x>0</x>
|
8
|
+
<y>0</y>
|
9
|
+
<width>676</width>
|
10
|
+
<height>300</height>
|
11
|
+
</rect>
|
12
|
+
</property>
|
13
|
+
<property name="windowTitle">
|
14
|
+
<string>Form</string>
|
15
|
+
</property>
|
16
|
+
<layout class="QGridLayout" name="gridLayout">
|
17
|
+
<item row="0" column="0">
|
18
|
+
<widget class="QLabel" name="label">
|
19
|
+
<property name="text">
|
20
|
+
<string>Find</string>
|
21
|
+
</property>
|
22
|
+
<property name="buddy">
|
23
|
+
<cstring>search_term</cstring>
|
24
|
+
</property>
|
25
|
+
</widget>
|
26
|
+
</item>
|
27
|
+
<item row="0" column="1">
|
28
|
+
<widget class="KLineEdit" name="search_term"/>
|
29
|
+
</item>
|
30
|
+
<item row="0" column="2">
|
31
|
+
<widget class="QPushButton" name="search">
|
32
|
+
<property name="text">
|
33
|
+
<string>&Search</string>
|
34
|
+
</property>
|
35
|
+
</widget>
|
36
|
+
</item>
|
37
|
+
<item row="1" column="0" colspan="3">
|
38
|
+
<widget class="QTextBrowser" name="content"/>
|
39
|
+
</item>
|
40
|
+
</layout>
|
41
|
+
</widget>
|
42
|
+
<tabstops>
|
43
|
+
<tabstop>search_term</tabstop>
|
44
|
+
<tabstop>search</tabstop>
|
45
|
+
<tabstop>content</tabstop>
|
46
|
+
</tabstops>
|
47
|
+
<resources/>
|
48
|
+
<connections/>
|
49
|
+
</ui>
|
@@ -346,7 +346,7 @@ object
|
|
346
346
|
current project, even if _target_ is a {Document} or the name of a file belonging
|
347
347
|
to it
|
348
348
|
@param [Boolean] abs it has the same meaning as in {SettingsContainer#[]}
|
349
|
-
@raise
|
349
|
+
@raise [IndexError] if the option can't be found
|
350
350
|
=end
|
351
351
|
def option_for target, group, name, ignore_project = false, abs = false
|
352
352
|
cont = []
|
@@ -363,7 +363,7 @@ to it
|
|
363
363
|
abs = :abs if abs
|
364
364
|
cont.each_with_index do |c, i|
|
365
365
|
begin return c[group, name, abs]
|
366
|
-
rescue
|
366
|
+
rescue IndexError
|
367
367
|
raise if i == cont.size - 1
|
368
368
|
end
|
369
369
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
name: ruby_syntax_checker
|
2
|
+
version: 0.0.1
|
3
|
+
about:
|
4
|
+
authors: [Stefano Crocco, stefano.crocco@alice.it]
|
5
|
+
license: gpl
|
6
|
+
description: Syntax checker for ruby documents. Requires the syntax checker plugin
|
7
|
+
bug_address: http://github.com/stcrocco/ruber/issues
|
8
|
+
icon: tools-check-spelling
|
9
|
+
deps: [syntax_checker, ruby_development]
|
10
|
+
class: Ruber::RubySyntaxChecker::Plugin
|
11
|
+
require: ruby_syntax_checker
|
@@ -0,0 +1,147 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2010, 2011 by Stefano Crocco
|
3
|
+
stefano.crocco@alice.it
|
4
|
+
|
5
|
+
This program is free software; you can redistribute it andor modify
|
6
|
+
it under the terms of the GNU General Public License as published by
|
7
|
+
the Free Software Foundation; either version 2 of the License, or
|
8
|
+
(at your option) any later version.
|
9
|
+
|
10
|
+
This program is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with this program; if not, write to the
|
17
|
+
Free Software Foundation, Inc.,
|
18
|
+
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
+
=end
|
20
|
+
|
21
|
+
module Ruber
|
22
|
+
|
23
|
+
module RubySyntaxChecker
|
24
|
+
|
25
|
+
SyntaxError = Struct.new :line, :column, :message, :formatted_message, :error_type
|
26
|
+
|
27
|
+
class Plugin < Ruber::Plugin
|
28
|
+
|
29
|
+
def initialize psf
|
30
|
+
super
|
31
|
+
Ruber[:syntax_checker].register_syntax_checker RubySyntaxChecker::Checker, ['application/x-ruby'],
|
32
|
+
%w[*.rb rakefile Rakefile]
|
33
|
+
end
|
34
|
+
|
35
|
+
def unload
|
36
|
+
Ruber[:syntax_checker].remove_syntax_checker RubySyntaxChecker::Checker
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
class Checker
|
42
|
+
|
43
|
+
SPECIAL_ERROR_STRINGS = [
|
44
|
+
'unmatched close parenthesis:',
|
45
|
+
'end pattern with unmatched parenthesis:',
|
46
|
+
'unknown regexp option -',
|
47
|
+
'class|module definition in method body',
|
48
|
+
'dynamic constant assignment',
|
49
|
+
'unterminated string meets end of file',
|
50
|
+
'premature end of char-class:',
|
51
|
+
'embedded document meets end of file',
|
52
|
+
'can\'t find string "[^"]+" anywhere before EOF'
|
53
|
+
]
|
54
|
+
|
55
|
+
ERROR_TYPES=[
|
56
|
+
[/\bunexpected \$end,\s+expecting (?:keyword_end|kEND)/, :missing_end],
|
57
|
+
[/\bunexpected (?:keyword_end|kEND),\s+expecting \$end/, :extra_end],
|
58
|
+
[/\bexpecting '\)/, :missing_close_paren],
|
59
|
+
[/\bunexpected '\)/, :extra_close_paren],
|
60
|
+
[/\bexpecting '\]'/, :missing_close_bracket],
|
61
|
+
[/\bunexpected '\]/, :extra_close_bracket],
|
62
|
+
[/\bexpecting '\}'/, :missing_close_brace],
|
63
|
+
[/\bunexpected '\}/, :extra_close_brace],
|
64
|
+
[/\bunexpected (?:keyword_else|kELSE)/, :extra_else],
|
65
|
+
[/\bunexpected (?:keyword_when|kWHEN)/, :extra_when],
|
66
|
+
[/\bunexpected (?:keyword_rescue|kRESCUE)/, :extra_rescue],
|
67
|
+
[/\bunexpected (?:keyword_ensure|kENSURE)/, :extra_ensure],
|
68
|
+
[/\bunterminated string/, :missing_quote],
|
69
|
+
[/\bunexpected (?:keyword_end|kEND)/, :misplaced_end],
|
70
|
+
[/end pattern with unmatched parenthesis/, :missing_regexp_close_paren],
|
71
|
+
[/unmatched close parenthesis/, :extra_regexp_close_paren],
|
72
|
+
[/premature end of char-class/, :missing_regexp_close_bracket],
|
73
|
+
[/unknown regexp option/, :unknown_regexp_option],
|
74
|
+
[/dynamic constant assignment/, :dynamic_constant_assignment],
|
75
|
+
[/embedded document meets end of file/, :missing_block_comment_end],
|
76
|
+
[/can't find string "[^"]+" anywhere before EOF/, :missing_heredoc_end]
|
77
|
+
]
|
78
|
+
|
79
|
+
def initialize doc
|
80
|
+
@doc = doc
|
81
|
+
@regexp = %r{^-e:(\d+):\s+(?:syntax error,|(#{SPECIAL_ERROR_STRINGS.join '|'}))(?:\s+(.*)|$)}
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_syntax text, formatted
|
85
|
+
ruby = Ruber[:ruby_development].interpreter_for @doc
|
86
|
+
begin
|
87
|
+
msg = Open3.popen3(ruby, '-c', '-e', text) do |in_s, out_s, err_s|
|
88
|
+
error = err_s.read
|
89
|
+
out_s.read.strip != 'Syntax OK' ? error : ''
|
90
|
+
end
|
91
|
+
rescue SystemCallError
|
92
|
+
raise Ruber::SyntaxChecker::SyntaxNotChecked
|
93
|
+
end
|
94
|
+
parse_output msg, formatted
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def parse_output str, formatted
|
100
|
+
# The inner array is needed in case the first message doesn\'t use a
|
101
|
+
# recognized format (for example, regexp syntax errors don\'t have a standard
|
102
|
+
# format). Without this, in the lins cycle, the else clause would be
|
103
|
+
# executed and would fail because the error_lines array is empty.
|
104
|
+
error_lines = [ [ [] ] ]
|
105
|
+
lines = str.split_lines
|
106
|
+
return if lines.empty?
|
107
|
+
lines.each do |l|
|
108
|
+
if l.match @regexp
|
109
|
+
error = [[$2 ? "#{$2} #{$3}" : $3], $1.to_i - 1]
|
110
|
+
error_type = ERROR_TYPES.find{|a| a[0] =~ l}
|
111
|
+
error << error_type[1] if error_type
|
112
|
+
error_lines << error
|
113
|
+
else error_lines[-1][0] << l
|
114
|
+
end
|
115
|
+
end
|
116
|
+
error_lines.shift if error_lines.first.first.empty?
|
117
|
+
errors = error_lines.map do |a, number, type|
|
118
|
+
error = SyntaxError.new number, nil, a.shift, nil, type
|
119
|
+
a.each_with_index do |l, i|
|
120
|
+
if l.match %r{^\s*\^\s*$}
|
121
|
+
error.column = l.index '^'
|
122
|
+
previous_line = a[i-1]
|
123
|
+
if previous_line and previous_line.match /^\.{3}/
|
124
|
+
offset = ( @doc.line(error.line) =~ /#{Regexp.quote(previous_line[3..-1])}/)
|
125
|
+
error.column += offset if offset
|
126
|
+
end
|
127
|
+
else error.message << "\n" << l
|
128
|
+
end
|
129
|
+
end
|
130
|
+
if formatted
|
131
|
+
msg = error.message.dup
|
132
|
+
msg.gsub! /expect(ed|ing)\s+\$end/, 'expect\1 end of file'
|
133
|
+
msg.gsub! /expect(ed|ing)\s+kEND/, 'expect\1 `end` keyword'
|
134
|
+
msg.gsub! /expect(ed|ing)\s+keyword_end/, 'expect\1 `end` keyword'
|
135
|
+
error.formatted_message = msg
|
136
|
+
else error.formatted_message = error.message.dup
|
137
|
+
end
|
138
|
+
error
|
139
|
+
end
|
140
|
+
errors
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -3,16 +3,20 @@ version: 0.0.1
|
|
3
3
|
about:
|
4
4
|
authors: [Stefano Crocco, stefano.crocco@alice.it]
|
5
5
|
license: :gpl
|
6
|
-
description:
|
6
|
+
description: Infrastructure to check the syntax of documents (needs other plugins to perform the actual syntax check)
|
7
7
|
icon: tools-check-spelling
|
8
8
|
bug_address: http://github.com/stcrocco/ruber/issues
|
9
|
-
class: Ruber::SyntaxChecker::
|
9
|
+
class: Ruber::SyntaxChecker::Plugin
|
10
10
|
require: syntax_checker
|
11
|
-
deps: ruby_development
|
12
11
|
config_options:
|
13
12
|
syntax_checker:
|
14
|
-
|
13
|
+
time_interval: {default: 1}
|
14
|
+
project_options:
|
15
|
+
syntax_checker:
|
16
|
+
auto_check: {scope: document, default: true}
|
15
17
|
config_widgets:
|
16
|
-
{caption: Syntax,
|
18
|
+
{caption: Syntax, class: Ruber::SyntaxChecker::ConfigWidget, pixmap: tools-check-spelling.png}
|
19
|
+
project_widgets:
|
20
|
+
{caption: Syntax, code: 'Qt::CheckBox.new("&Automatically check syntax for this document"){self.object_name = "_syntax_checker__auto_check"}', pixmap: tools-check-spelling.png, scope: document}
|
17
21
|
extensions:
|
18
|
-
syntax_checker: {class: Ruber::SyntaxChecker::
|
22
|
+
syntax_checker: {class: Ruber::SyntaxChecker::Extension, scope: document}
|
@@ -22,6 +22,7 @@ require 'tempfile'
|
|
22
22
|
require 'open3'
|
23
23
|
|
24
24
|
require 'facets/boolean'
|
25
|
+
require_relative 'ui/config_widget'
|
25
26
|
|
26
27
|
module Ruber
|
27
28
|
|
@@ -61,604 +62,299 @@ syntax checks after one second of inactivity
|
|
61
62
|
=end
|
62
63
|
module SyntaxChecker
|
63
64
|
|
65
|
+
# SyntaxError = Struct.new :line, :column, :message, :formatted_message
|
66
|
+
|
67
|
+
class SyntaxNotChecked < Exception
|
68
|
+
end
|
69
|
+
#
|
70
|
+
# class SyntaxError
|
71
|
+
#
|
72
|
+
# def format
|
73
|
+
# res = ""
|
74
|
+
# res << KDE.i18n("Line %d") % (line + 1) if line
|
75
|
+
# res << KDE.i18n(", column %d") % (column + 1) if line and column
|
76
|
+
# msg = formatted_message || message
|
77
|
+
# res << ": " if msg and !res.empty?
|
78
|
+
# res << msg
|
79
|
+
# res
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# end
|
83
|
+
|
64
84
|
=begin rdoc
|
65
|
-
Plugin object for the @
|
85
|
+
Plugin object for the @syntax_checker@ feature
|
66
86
|
|
67
87
|
@api class SyntaxChecker::SyntaxCheckerPlugin
|
68
88
|
@api_method #register_syntax_checker
|
69
89
|
@api_method #remove_syntax_checker
|
70
90
|
=end
|
71
|
-
class
|
91
|
+
class Plugin < Ruber::Plugin
|
72
92
|
|
73
|
-
|
74
|
-
|
75
|
-
|
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)
|
93
|
+
attr_reader :current_status
|
94
|
+
|
95
|
+
attr_reader :current_errors
|
85
96
|
|
86
|
-
=begin rdoc
|
87
|
-
Colors used to display the different states of the document
|
88
|
-
=end
|
89
97
|
COLORS = {
|
90
98
|
:correct => Qt::Color.new(Qt.green),
|
91
|
-
:
|
92
|
-
:
|
93
|
-
|
99
|
+
:unknown => Qt::Color.new(Qt.gray),
|
100
|
+
:incorrect => Qt::Color.new(Qt.red)
|
101
|
+
}
|
94
102
|
|
95
|
-
=
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
=begin rdoc
|
101
|
-
Creates an instance of the plugin
|
102
|
-
|
103
|
-
It also registers the two built-in syntax checkers
|
103
|
+
MESSAGES = {
|
104
|
+
:correct => 'No syntax errors',
|
105
|
+
:unknown => 'Unknown document type',
|
106
|
+
:incorrect => 'There are syntax errors'
|
107
|
+
}
|
104
108
|
|
105
|
-
|
106
|
-
|
107
|
-
|
109
|
+
class Led < KDE::Led
|
110
|
+
|
111
|
+
signals 'context_menu_requested(QPoint)'
|
112
|
+
|
113
|
+
signals :left_clicked
|
114
|
+
|
115
|
+
def contextMenuEvent e
|
116
|
+
emit context_menu_requested(e.global_pos)
|
117
|
+
end
|
118
|
+
|
119
|
+
def mouseReleaseEvent e
|
120
|
+
emit left_clicked if e.button == Qt::LeftButton
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
signals :settings_changed
|
126
|
+
|
127
|
+
signals :syntax_checker_added
|
128
|
+
|
129
|
+
signals :syntax_checker_removed
|
130
|
+
|
108
131
|
def initialize psf
|
109
132
|
super
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
@led =
|
133
|
+
self.connect(SIGNAL('extension_added(QString, QObject*)')) do |name, prj|
|
134
|
+
connect prj.extension(name.to_sym), SIGNAL('syntax_checked(QObject*)'), self, SLOT('document_checked(QObject*)')
|
135
|
+
end
|
136
|
+
@syntax_checkers = {}
|
137
|
+
@led = Led.new
|
138
|
+
connect @led, SIGNAL('context_menu_requested(QPoint)'), self, SLOT('display_context_menu(QPoint)')
|
139
|
+
connect @led, SIGNAL(:left_clicked), self, SLOT(:jump_to_first_error)
|
115
140
|
Ruber[:main_window].status_bar.add_permanent_widget @led
|
116
|
-
|
141
|
+
set_current_status :unknown
|
117
142
|
end
|
118
143
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
144
|
+
def unload
|
145
|
+
Ruber[:main_window].status_bar.remove_widget @led
|
146
|
+
super
|
147
|
+
self
|
132
148
|
end
|
133
149
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
+
def format_error error
|
151
|
+
msg = ''
|
152
|
+
if error.line
|
153
|
+
msg << KDE.i18n("Line %d") % (error.line + 1)
|
154
|
+
msg << (error.column ? (KDE.i18n(", column %d: ")) % (error.column + 1) : ': ')
|
150
155
|
end
|
151
|
-
|
156
|
+
msg << KDE.i18n(error.formatted_message || error.message || 'UNKNOWN ERROR')
|
157
|
+
msg
|
158
|
+
end
|
159
|
+
|
160
|
+
def set_current_status status, errors = []
|
152
161
|
@led.color = COLORS[status]
|
153
|
-
|
162
|
+
if status == :incorrect and !errors.empty?
|
163
|
+
tool_tip = errors.map do |e|
|
164
|
+
format_error e
|
165
|
+
end.join "\n"
|
166
|
+
@led.tool_tip = tool_tip
|
167
|
+
else @led.tool_tip = i18n(MESSAGES[status])
|
168
|
+
end
|
169
|
+
@current_status = status
|
170
|
+
if @current_status == :incorrect
|
171
|
+
@current_errors = errors.dup
|
172
|
+
else @current_errors = nil
|
173
|
+
end
|
154
174
|
end
|
155
175
|
|
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
176
|
def register_syntax_checker cls, mimetypes, patterns = []
|
188
|
-
if @
|
189
|
-
raise ArgumentError, "
|
190
|
-
|
191
|
-
|
177
|
+
if @syntax_checkers.include? cls
|
178
|
+
raise ArgumentError, "#{cls} has already been registered as syntax checker"
|
179
|
+
end
|
180
|
+
@syntax_checkers[cls] = [mimetypes, patterns]
|
181
|
+
emit syntax_checker_added
|
192
182
|
end
|
193
183
|
|
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
184
|
def remove_syntax_checker cls
|
204
|
-
|
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
|
185
|
+
emit syntax_checker_removed if @syntax_checkers.delete cls
|
215
186
|
end
|
216
187
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
Ruber[:main_window].status_bar.remove_widget @led
|
224
|
-
super
|
225
|
-
self
|
188
|
+
def syntax_checker_for doc
|
189
|
+
cls = @syntax_checkers.find do |_, rules|
|
190
|
+
doc.file_type_match?(rules[0], rules[1])
|
191
|
+
end
|
192
|
+
return unless cls
|
193
|
+
cls[0]
|
226
194
|
end
|
227
195
|
|
228
|
-
|
229
|
-
|
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
|
196
|
+
def load_settings
|
197
|
+
emit settings_changed
|
243
198
|
end
|
244
199
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
@timer.stop
|
253
|
-
@timer.start
|
200
|
+
private
|
201
|
+
|
202
|
+
def display_context_menu pt
|
203
|
+
return if @current_status == :unknown
|
204
|
+
if !@current_errors || @current_errors.empty?
|
205
|
+
actions = [KDE::Action.new(i18n(MESSAGES[@current_status]), @led)]
|
206
|
+
else actions = @current_errors.map{|e| KDE::Action.new format_error(e), @led}
|
254
207
|
end
|
255
|
-
|
208
|
+
choice = Qt::Menu.exec actions, pt
|
209
|
+
return if !choice or !@current_errors or @current_errors.empty?
|
210
|
+
error = @current_errors[actions.index(choice)]
|
211
|
+
line = error.line
|
212
|
+
col = error.column || 0
|
213
|
+
Ruber[:world].active_environment.active_editor.go_to line, col if line
|
256
214
|
end
|
215
|
+
slots 'display_context_menu(QPoint)'
|
257
216
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
self
|
217
|
+
def jump_to_first_error
|
218
|
+
return unless @current_errors and !@current_errors.empty?
|
219
|
+
e = @current_errors[0]
|
220
|
+
col = e.column || 0
|
221
|
+
if e.line
|
222
|
+
Ruber[:world].active_environment.active_editor.go_to e.line, col
|
223
|
+
end
|
266
224
|
end
|
225
|
+
slots :jump_to_first_error
|
267
226
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
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
|
227
|
+
def document_checked doc
|
228
|
+
return unless doc.active?
|
229
|
+
ext = doc.extension(:syntax_checker)
|
230
|
+
set_current_status ext.status, ext.errors
|
289
231
|
end
|
232
|
+
slots 'document_checked(QObject*)'
|
290
233
|
|
291
234
|
end
|
292
|
-
|
293
|
-
|
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
|
235
|
+
|
236
|
+
class Extension < Qt::Object
|
312
237
|
|
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)'
|
238
|
+
include Ruber::Extension
|
321
239
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
=begin rdoc
|
326
|
-
A list of the syntax errors found in the document
|
240
|
+
SyntaxErrorMark = KTextEditor::MarkInterface.markType09
|
241
|
+
|
242
|
+
DEFAULT_CHECK_OPTIONS = {:format => true, :update => true}
|
327
243
|
|
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
244
|
attr_reader :errors
|
335
245
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
=end
|
246
|
+
attr_reader :status
|
247
|
+
|
248
|
+
signals 'syntax_checked(QObject*)'
|
249
|
+
|
341
250
|
def initialize prj
|
342
251
|
super
|
343
|
-
@
|
252
|
+
@status = :unknown
|
253
|
+
@errors = nil
|
344
254
|
@doc = prj.document
|
345
|
-
@
|
346
|
-
@
|
255
|
+
@project = prj
|
256
|
+
connect @doc, SIGNAL('document_url_changed(QObject*)'), self, SLOT(:create_syntax_checker)
|
257
|
+
connect @doc, SIGNAL('document_saved_or_uploaded(QObject*, bool)'), self,
|
258
|
+
SLOT(:auto_check)
|
259
|
+
connect @doc, SIGNAL('text_changed(QObject*)'), self, SLOT(:start_waiting)
|
260
|
+
create_syntax_checker
|
347
261
|
connect @doc, SIGNAL(:activated), self, SLOT(:document_activated)
|
348
|
-
connect @doc, SIGNAL(:deactivated), self, SLOT(:
|
349
|
-
|
350
|
-
|
262
|
+
connect @doc, SIGNAL(:deactivated), self, SLOT(:delete_timer)
|
263
|
+
connect Ruber[:syntax_checker], SIGNAL(:settings_changed), self, SLOT(:load_settings)
|
264
|
+
connect Ruber[:syntax_checker], SIGNAL(:syntax_checker_added), self, SLOT(:create_syntax_checker_if_needed)
|
265
|
+
connect Ruber[:syntax_checker], SIGNAL(:syntax_checker_removed), self, SLOT(:create_syntax_checker)
|
266
|
+
load_settings
|
267
|
+
document_activated false if @doc.active?
|
268
|
+
end
|
269
|
+
|
270
|
+
def check_syntax options = DEFAULT_CHECK_OPTIONS
|
271
|
+
options = DEFAULT_CHECK_OPTIONS.merge options
|
272
|
+
if @checker
|
273
|
+
begin
|
274
|
+
errors = @checker.check_syntax @doc.text, options[:format]
|
275
|
+
res = {:errors => errors, :result => errors ? :incorrect : :correct}
|
276
|
+
rescue SyntaxNotChecked
|
277
|
+
res = {:result => :unknown, :errors => nil}
|
278
|
+
end
|
279
|
+
else res = {:result => :unknown, :errors => nil}
|
351
280
|
end
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
281
|
+
if options[:format] and options[:update]
|
282
|
+
@errors = errors
|
283
|
+
@status = res[:result]
|
284
|
+
if errors
|
285
|
+
#Uncomment the following lines when KTextEditor::MarkInterface#marks works
|
286
|
+
# iface = @doc.interface('mark_interface')
|
287
|
+
# errors.each do |e|
|
288
|
+
# iface.add_mark e.line, SyntaxErrorMark if e.line
|
289
|
+
# end
|
357
290
|
end
|
291
|
+
emit syntax_checked(@doc) if options[:format]
|
358
292
|
end
|
359
|
-
|
360
|
-
create_syntax_checker
|
293
|
+
res
|
361
294
|
end
|
295
|
+
slots :check_syntax
|
362
296
|
|
363
|
-
|
364
|
-
|
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
|
297
|
+
def remove_from_project
|
298
|
+
super
|
299
|
+
@timer.stop if @timer
|
380
300
|
end
|
381
301
|
|
382
|
-
|
383
|
-
|
384
|
-
end
|
302
|
+
private
|
385
303
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
The syntax check can be synchronous or asynchronous, according to the value of
|
390
|
-
the argument. In the first case, this method won't return until the syntax check
|
391
|
-
has finished and the UI has been updated. In the second case, the method will
|
392
|
-
start the syntax check in a new thread and return immediately. The {#check_done}
|
393
|
-
signal will be emitted when the syntax check has been finished.
|
394
|
-
|
395
|
-
Nothing will be done if no checker exists for the document.
|
396
|
-
|
397
|
-
*Note:* while an asynchronous syntax check avoids freezing the UI if it takes
|
398
|
-
a long time, it seems that it takes much longer than a synchronous check.
|
399
|
-
|
400
|
-
@param [Boolean] async whether the syntax check should or not be asynchronous
|
401
|
-
@todo the decision on whether the check should be synchronous or asynchronous
|
402
|
-
should be delegated to the checker
|
403
|
-
@return [nil]
|
404
|
-
=end
|
405
|
-
def check async = false
|
406
|
-
return unless @checker
|
407
|
-
@plugin.stop_timer
|
408
|
-
if async
|
409
|
-
@threak.kill if @thread
|
410
|
-
@thread = Thread.new(@doc.text) do |str|
|
411
|
-
res = @checker.check @doc.text
|
412
|
-
emit check_done res
|
413
|
-
end
|
414
|
-
else
|
415
|
-
res = @checker.check @doc.text
|
416
|
-
update_ui res
|
304
|
+
def create_syntax_checker_if_needed
|
305
|
+
unless @checker
|
306
|
+
create_syntax_checker
|
417
307
|
end
|
418
|
-
nil
|
419
308
|
end
|
309
|
+
slots :create_syntax_checker_if_needed
|
420
310
|
|
421
|
-
private
|
422
|
-
|
423
|
-
=begin rdoc
|
424
|
-
Creates a syntax checker for the document
|
425
|
-
|
426
|
-
If needed, it also removes the old one and immediately performs a syntax check
|
427
|
-
|
428
|
-
@return [nil]
|
429
|
-
=end
|
430
311
|
def create_syntax_checker
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
@checker = nil
|
435
|
-
if new_checker
|
436
|
-
@checker = new_checker
|
437
|
-
connect self, SIGNAL('check_done(QString)'), self, SLOT('update_ui(QString)')
|
438
|
-
end
|
439
|
-
check if @doc.active?
|
440
|
-
end
|
441
|
-
nil
|
312
|
+
checker_cls = Ruber[:syntax_checker].syntax_checker_for @doc
|
313
|
+
@checker = checker_cls ? checker_cls.new(@doc) : nil
|
314
|
+
auto_check
|
442
315
|
end
|
316
|
+
slots :create_syntax_checker
|
443
317
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
@return [nil]
|
448
|
-
=end
|
449
|
-
def document_deactivated
|
450
|
-
@thread.kill if @thread
|
451
|
-
@plugin.stop_timer
|
452
|
-
@plugin.disconnect SIGNAL(:timeout), self, SLOT(:check)
|
453
|
-
Ruber[:syntax_checker].mark_document_as :unknown
|
454
|
-
nil
|
318
|
+
def auto_check
|
319
|
+
check_syntax if @project[:syntax_checker, :auto_check]
|
455
320
|
end
|
321
|
+
slots :auto_check
|
456
322
|
|
457
|
-
=
|
458
|
-
|
459
|
-
|
460
|
-
@
|
461
|
-
|
462
|
-
def document_activated
|
463
|
-
connect @plugin, SIGNAL(:timeout), self, SLOT(:check)
|
464
|
-
check
|
465
|
-
nil
|
323
|
+
def document_activated check_syntax = true
|
324
|
+
@timer = Qt::Timer.new self
|
325
|
+
@timer.singleShot = true
|
326
|
+
connect @timer, SIGNAL(:timeout), self, SLOT(:auto_check)
|
327
|
+
auto_check if check_syntax
|
466
328
|
end
|
329
|
+
slots :document_activated
|
467
330
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
To do so, it runs a separate ruby process (using the ruby interpreter set by the
|
474
|
-
Ruby Runner plugin) passing it the @-c@ and the @-e@ options with the document's
|
475
|
-
content as argument.
|
476
|
-
|
477
|
-
The process is executed using @Open3.popen3@
|
478
|
-
=end
|
479
|
-
class RubySyntaxChecker
|
480
|
-
|
481
|
-
=begin rdoc
|
482
|
-
Creates a new instance.
|
483
|
-
|
484
|
-
@param [Document] doc the document to check
|
485
|
-
=end
|
486
|
-
def initialize doc
|
487
|
-
@doc = doc
|
331
|
+
def delete_timer
|
332
|
+
@timer.stop
|
333
|
+
@timer.delete_later
|
334
|
+
@timer = nil
|
488
335
|
end
|
336
|
+
slots :delete_timer
|
489
337
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
@param [String] str the string to check (usually, it'll be the document's text)
|
494
|
-
@return [String] a string containing the lines of output produced by ruby concerning
|
495
|
-
syntax errors or an empty string if there were no syntax error
|
496
|
-
=end
|
497
|
-
def check str
|
498
|
-
ruby = Ruber[:ruby_development].interpreter_for @doc
|
499
|
-
Open3.popen3(ruby, '-c', '-e', str) do |in_s, out_s, err_s|
|
500
|
-
error = err_s.read
|
501
|
-
error.gsub! %r{^-e(?=:\d+:\s+syntax error,)}, @doc.path
|
502
|
-
out_s.read.strip != 'Syntax OK' ? error : ''
|
503
|
-
end
|
338
|
+
def load_settings
|
339
|
+
@time_interval = Ruber[:config][:syntax_checker, :time_interval]
|
504
340
|
end
|
341
|
+
slots :load_settings
|
505
342
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
@param [String] str the string to parse.
|
510
|
-
@return [<SyntaxCheckerPlugin::ErrorDescription>] a list of {SyntaxCheckerPlugin::ErrorDescription ErrorDescription}s
|
511
|
-
corresponding to the syntax errors mentioned in _str_
|
512
|
-
=end
|
513
|
-
def convert_check_result str
|
514
|
-
groups = [[]]
|
515
|
-
lines = str.split "\n"
|
516
|
-
lines.each do |l|
|
517
|
-
if l.match(/^#{Regexp.quote(@doc.path||'')}:\d+:\s+syntax error,\s+/) then groups << [l]
|
518
|
-
else groups[-1] << l
|
519
|
-
end
|
520
|
-
end
|
521
|
-
groups.delete_if{|g| g.empty?}
|
522
|
-
groups.map do |a|
|
523
|
-
a.shift.match(/^#{Regexp.quote(@doc.path||'')}:(\d+):\s+syntax error,\s+(.*)/)
|
524
|
-
msgs = [$2]
|
525
|
-
error = SyntaxCheckerPlugin::ErrorDescription.new $1.to_i
|
526
|
-
if a[-1] and a[-1].match(/^\s*\^\s*$/)
|
527
|
-
error.code = a[-2]
|
528
|
-
# Sometimes, ruby doesn't report the whole line where the error occurs, but only
|
529
|
-
# the part nearest it. In this case, the beginning of the line is replaced with ... .
|
530
|
-
# In this case, to obtain the correct column number, we try a regexp match
|
531
|
-
# between the part of code reported by ruby and the whole line. If it works, we
|
532
|
-
# add that position to the one returned by ruby. If it doesn't (for example because
|
533
|
-
# the user changed the document in the meantime), we'll just report what ruby reports
|
534
|
-
col = a[-1].index('^')
|
535
|
-
if a[-2].match(/^\.\.\./)
|
536
|
-
lines = @doc.text.split("\n")
|
537
|
-
# error.line is 1-based
|
538
|
-
l = lines[error.line-1] || ''
|
539
|
-
pos = (l =~ /#{Regexp.quote(a[-2][3..-1])}/)
|
540
|
-
error.column = pos ? col + pos - 1 : col
|
541
|
-
else error.column = col
|
542
|
-
end
|
543
|
-
a.pop 2
|
544
|
-
end
|
545
|
-
a.each{|l| msgs << l}
|
546
|
-
error.message = msgs.join ' '
|
547
|
-
error
|
548
|
-
end
|
343
|
+
def start_waiting
|
344
|
+
@timer.start 1_000 * @time_interval if @time_interval > 0 and @doc.active?
|
549
345
|
end
|
346
|
+
slots :start_waiting
|
550
347
|
|
551
348
|
end
|
552
|
-
|
553
|
-
|
554
|
-
Class which checks the syntax of a ruby file.
|
555
|
-
|
556
|
-
It calls the @YAML.load@ method on the document's content within a begin/rescue
|
557
|
-
block, rescuing any ArgumentError exception. The message of the exception is used
|
558
|
-
to find information about the error
|
559
|
-
=end
|
560
|
-
class YamlSyntaxChecker
|
561
|
-
|
562
|
-
=begin rdoc
|
563
|
-
Creates a new instance
|
564
|
-
|
565
|
-
@param [Document] doc the document to check
|
566
|
-
=end
|
567
|
-
def initialize doc
|
568
|
-
end
|
569
|
-
|
570
|
-
=begin rdoc
|
571
|
-
Checks the syntax of the given string.
|
572
|
-
|
573
|
-
@param [String] str the string to check (usually, it'll be the document's text)
|
574
|
-
@return [String] a string containing the lines of output produced by @YAML.load@
|
575
|
-
concerning syntax errors or an empty string if there were no syntax error
|
576
|
-
=end
|
577
|
-
def check str
|
578
|
-
begin
|
579
|
-
YAML.load str
|
580
|
-
''
|
581
|
-
rescue ArgumentError => e
|
582
|
-
e.message
|
583
|
-
end
|
584
|
-
end
|
349
|
+
|
350
|
+
class ConfigWidget < Qt::Widget
|
585
351
|
|
586
|
-
=
|
587
|
-
|
588
|
-
|
589
|
-
@
|
590
|
-
@return [<SyntaxCheckerPlugin::ErrorDescription>] a list of {SyntaxCheckerPlugin::ErrorDescription ErrorDescription}s
|
591
|
-
corresponding to the syntax errors mentioned in _str_
|
592
|
-
=end
|
593
|
-
def convert_check_result str
|
594
|
-
return [] if str.empty?
|
595
|
-
str.match(/^syntax error on line (\d+), col (\d+): `(.*)'$/)
|
596
|
-
error = SyntaxCheckerPlugin::ErrorDescription.new $1.to_i, 'Syntax error', $3.to_s, $2.to_i
|
597
|
-
[error]
|
352
|
+
def initialize parent = nil
|
353
|
+
super
|
354
|
+
@ui = Ui::SyntaxCheckerConfigWidget.new
|
355
|
+
@ui.setup_ui self
|
598
356
|
end
|
599
357
|
|
600
|
-
end
|
601
|
-
|
602
|
-
=begin rdoc
|
603
|
-
@KDE::Led@ with the ability to jump to the first syntax error on left or middle mouse
|
604
|
-
click and to popup a menu with a list of all syntax errors in the current document
|
605
|
-
on right click
|
606
|
-
=end
|
607
|
-
class SyntaxResultWidget < KDE::Led
|
608
|
-
|
609
|
-
=begin rdoc
|
610
|
-
Override of @Qt::Widget#mouseReleaseEvent@
|
611
|
-
|
612
|
-
If the event refers to the left button and the current document contains syntax
|
613
|
-
errors, it moves the cursor in the editor view to the position of the first error
|
614
|
-
|
615
|
-
@return [nil]
|
616
|
-
=end
|
617
|
-
def mouseReleaseEvent e
|
618
|
-
return unless e.button == Qt::LeftButton or e.button == Qt::MidButton
|
619
|
-
doc = Ruber[:main_window].current_document
|
620
|
-
view = Ruber[:main_window].active_editor
|
621
|
-
return unless view and doc and doc.extension(:syntax_checker)
|
622
|
-
checker = doc.extension(:syntax_checker)
|
623
|
-
err = checker.errors.first
|
624
|
-
if err
|
625
|
-
view.go_to err.line - 1, (err.column || 0)
|
626
|
-
Ruber[:main_window].status_bar.show_message Ruber[:syntax_checker].format_error_message(err), 4000
|
627
|
-
end
|
628
|
-
nil
|
629
|
-
end
|
630
|
-
|
631
|
-
=begin rdoc
|
632
|
-
Override of @Qt::Widget#contextMenuEvent@
|
633
|
-
|
634
|
-
If the current document contains syntax errors, it displays a menu listing them.
|
635
|
-
Each action in the menu moves the cursor in the editor view to display to the line
|
636
|
-
and column of the corresponding error.
|
637
|
-
|
638
|
-
If the current document doesn't contain syntax errors, the menu will only contain
|
639
|
-
an entry with text @Syntax OK@ which does nothing when activated.
|
640
|
-
|
641
|
-
@return [nil]
|
642
|
-
=end
|
643
|
-
def contextMenuEvent event
|
644
|
-
doc = Ruber[:main_window].current_document
|
645
|
-
view = Ruber[:main_window].active_editor
|
646
|
-
return unless doc and view and (checker = doc.extension :syntax_checker)
|
647
|
-
errors = checker.errors
|
648
|
-
actions = errors.map do |e|
|
649
|
-
a = KDE::Action.new Ruber[:syntax_checker].format_error_message(e), self
|
650
|
-
end
|
651
|
-
actions << KDE::Action.new('Syntax OK', self) if actions.empty?
|
652
|
-
res = Qt::Menu.exec(actions, event.global_pos)
|
653
|
-
return if !res or errors.empty?
|
654
|
-
idx = actions.index res
|
655
|
-
return unless idx
|
656
|
-
error = errors[idx]
|
657
|
-
view.go_to error.line - 1, (error.column || 0)
|
658
|
-
Ruber[:main_window].status_bar.show_message res.text, 4000
|
659
|
-
nil
|
660
|
-
end
|
661
|
-
|
662
358
|
end
|
663
359
|
|
664
360
|
end
|