eturem 0.3.2 → 0.5.2
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.
- checksums.yaml +5 -5
- data/Gemfile +3 -2
- data/LICENSE.txt +1 -1
- data/README.ja.md +92 -3
- data/README.md +4 -0
- data/Rakefile +1 -0
- data/eturem.gemspec +11 -17
- data/exe/eturem +15 -0
- data/lib/eturem.rb +61 -21
- data/lib/eturem/base.rb +260 -228
- data/lib/eturem/en.rb +1 -215
- data/lib/eturem/en/main.rb +213 -0
- data/lib/eturem/en/warning.rb +11 -0
- data/lib/eturem/ja.rb +1 -345
- data/lib/eturem/ja/argument_error.rb +56 -0
- data/lib/eturem/ja/dxruby_error.rb +13 -0
- data/lib/eturem/ja/error_enoent.rb +19 -0
- data/lib/eturem/ja/interrupt.rb +12 -0
- data/lib/eturem/ja/load_error.rb +14 -0
- data/lib/eturem/ja/main.rb +220 -0
- data/lib/eturem/ja/name_error.rb +50 -0
- data/lib/eturem/ja/no_memory_error.rb +12 -0
- data/lib/eturem/ja/syntax_error.rb +88 -0
- data/lib/eturem/ja/system_stack_error.rb +10 -0
- data/lib/eturem/ja/type_error.rb +13 -0
- data/lib/eturem/ja/uncaught_throw_error.rb +11 -0
- data/lib/eturem/ja/warning.rb +42 -0
- data/lib/eturem/ja/zero_division_error.rb +10 -0
- data/lib/eturem/version.rb +1 -1
- data/lib/eturem/warning.rb +13 -0
- metadata +28 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 02f58a271e2dc5649f9a44f03c338609d7484a21c117765f015b37b2fa987a0e
|
4
|
+
data.tar.gz: f64968cfff79d69874addbe967624ee89b3c064d5039f6b97878037f52c38337
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c4caaf3140eff2837ed1aa13517a6955d4a7039fc2266d46420c14738d7eae20ce418cb470b1fdaabdad3f4f17c4a454fb4bcab1483223a7ea4c0af1eee4fc6
|
7
|
+
data.tar.gz: f3b5c7db9d90865be0705b7193d1fd7dd9feeb103c2eba394342486eef6e9a08ce460bae42d9e4bea07b385a82b6f9974c91fc7b788b8aa84e8477b786fb5c39
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.ja.md
CHANGED
@@ -12,6 +12,10 @@ Easy To Understand Ruby Error Message の略。
|
|
12
12
|
|
13
13
|
$ ruby -returem/ja <your_script.rb>
|
14
14
|
|
15
|
+
または
|
16
|
+
|
17
|
+
$ eturem lang=ja <your_script.rb>
|
18
|
+
|
15
19
|
と使用すればよいのですが、最初に書いたとおり初心者が使用することを想定した gem ですので、そんなことを初心者に強いるのは酷というもの。だれか詳しい人が、事前に ```gem install eturem``` した上で、環境変数 RUBYOPT に ```-returem/ja``` を追加しておいてあげましょう。
|
16
20
|
|
17
21
|
## 使用するとどうなるか
|
@@ -41,7 +45,7 @@ example1.rb:5: syntax error, unexpected end-of-input, expecting keyword_end
|
|
41
45
|
Eturem を使用すると、次のようなエラー表示になります。
|
42
46
|
|
43
47
|
```
|
44
|
-
|
48
|
+
【エラー】ファイル"example1.rb" 5行目:
|
45
49
|
(ただし、実際のエラーの原因はおそらくもっと前にあります。)
|
46
50
|
構文エラーです。endが足りません。「if」に対応する「end」があるか確認してください。
|
47
51
|
3: puts "なんたらかんたら"
|
@@ -74,7 +78,7 @@ did_you_mean のおかげで昔より格段にわかりやすくなったとは
|
|
74
78
|
Eturem を使用すると、次のようなエラー表示になります。(実際には色付き)
|
75
79
|
|
76
80
|
```
|
77
|
-
|
81
|
+
【エラー】ファイル"example2.rb" 5行目:
|
78
82
|
変数/メソッド「player_life」は存在しません。「prayer_life」の入力ミスではありませんか?
|
79
83
|
1: prayer_life = 100
|
80
84
|
:
|
@@ -110,7 +114,7 @@ example3.rb:1:in `foo': wrong number of arguments (given 1, expected 2) (Argumen
|
|
110
114
|
Eturem を使用すると、次のようなエラー表示になります。
|
111
115
|
|
112
116
|
```
|
113
|
-
|
117
|
+
【エラー】ファイル"example3.rb" 4行目:
|
114
118
|
引数の数が正しくありません。「foo」は本来2個の引数を取りますが、1個の引数が渡されています。
|
115
119
|
2: end
|
116
120
|
3: # 中略
|
@@ -119,6 +123,91 @@ Eturem を使用すると、次のようなエラー表示になります。
|
|
119
123
|
|
120
124
|
このように、呼び出し行をエラー発生行として表示してくれます。
|
121
125
|
|
126
|
+
### 例4:warning
|
127
|
+
|
128
|
+
0.4.0 から、warning に対してもサポートができるようになりました。
|
129
|
+
|
130
|
+
```
|
131
|
+
【警告】ファイル"example4.rb" 2行目:
|
132
|
+
条件式部分で「 = 」が使われています。「 == 」の間違いではありませんか?
|
133
|
+
=> 2: puts "a" if a = 1
|
134
|
+
```
|
135
|
+
|
136
|
+
現状あまり多くの種類の警告に対応できてはいませんが、上記のように日本語での警告に加えて該当行を表示することができます。
|
137
|
+
|
138
|
+
### 例5:エラー発生時に自動で binding.irb
|
139
|
+
|
140
|
+
変数の値が不適切だったためにエラーが起きた可能性がある場合、エラーが起きた瞬間の変数の値を調べる必要があります。そうした際には「エラー発生箇所の直前に binding.irb を入れて調べる」という手がありますが、そうするとたまにしか起きないエラーの場合何度も binding.irb が動いてしまって面倒です。
|
141
|
+
|
142
|
+
そこで、後述する設定ファイル .eturem で設定するか、あるいはプログラム中に `$eturem_repl = "irb"` と入れておくことで、エラー発生時に自動的に binding.irb することができます。
|
143
|
+
|
144
|
+
なお、この機能だけを切り出した gem「[autoirb](https://github.com/nodai2hITC/autoirb)」もあります。
|
145
|
+
|
146
|
+
### その他細かなこと
|
147
|
+
|
148
|
+
#### 全角空白・全角記号による NameError
|
149
|
+
|
150
|
+
日本人ならきっと誰もがやったことがあり、そして意外と気付けない全角空白や全角記号によるエラー。通常の NameError の場合とは異なるメッセージでわかりやすくしています。(下の表示ではわかりませんが、実際には全角空白部分に下線が引かれています。)
|
151
|
+
|
152
|
+
```
|
153
|
+
【エラー】ファイル"example5.rb" 1行目:(NoMethodError)
|
154
|
+
スクリプト中に全角空白が混じっています。
|
155
|
+
=> 1: puts "こんにちは世界"
|
156
|
+
```
|
157
|
+
|
158
|
+
#### 大文字・小文字を間違えた場合の NameError
|
159
|
+
|
160
|
+
did_you_mean は、例えば `Time.now` と書くべきところを `time.now` と間違えて小文字で書いてしまった場合、 `Time` をサジェストしないようになっています。(サジェスト範囲を広げると誤反応が多くなるため、敢えてそうしているのだそうです。)
|
161
|
+
|
162
|
+
とはいえ、初心者は大文字小文字が区別されるという意識が無く、つい小文字で入力してしまうことが少なからずあるように思えます。そこでスペルが同一で大文字小文字のみが異なる場合に限り、定数もサジェスト対象になるようにしています。
|
163
|
+
|
164
|
+
```
|
165
|
+
【エラー】ファイル"example6.rb" 1行目:(NameError)
|
166
|
+
変数/メソッド「time」は存在しません。「Time」の入力ミスではありませんか?
|
167
|
+
=> 1: start_time = time.now
|
168
|
+
```
|
169
|
+
|
170
|
+
## .eturem ファイルによる設定
|
171
|
+
|
172
|
+
HOME またはカレントディレクトリに「.eturem」というファイルを用意すると、詳細設定ができます。(下の例の設定が、各項目を省略した際に使用される初期値です。)
|
173
|
+
|
174
|
+
```
|
175
|
+
enable: true
|
176
|
+
debug: false
|
177
|
+
lang: en
|
178
|
+
output_backtrace: true
|
179
|
+
output_original: true
|
180
|
+
output_script: true
|
181
|
+
override_warning: true
|
182
|
+
use_coderay: false
|
183
|
+
before_line_num: 2
|
184
|
+
after_line_num: 2
|
185
|
+
repl: nil
|
186
|
+
```
|
187
|
+
|
188
|
+
- enable: eturem の使用/不使用を切り替えます。
|
189
|
+
- debug: eturem 自体をデバッグするための機能なので、通常は使う必要はありません。
|
190
|
+
- lang: 表示言語を切り替えます。ただし `-returem/ja` のように言語を指定して使用した場合、この設定よりもそちらが優先されます。
|
191
|
+
- output_backtrace: バックトレース表示の有無を切り替えます。
|
192
|
+
- output_original: Ruby 本来のエラーメッセージの表示の有無を切り替えます。
|
193
|
+
- output_script: エラーの起きた個所のスクリプト表示の有無を切り替えます。
|
194
|
+
- override_warning: 警告に対してもわかりやすいメッセージを表示するかを設定します。
|
195
|
+
- before_line_num / after_line_num: スクリプト表示の際、エラー行の前後何行を表示するかを設定します。
|
196
|
+
- repl: irb または pry と書いておくと、エラー発生時に自動的に binding.irb / binding.pry します。
|
197
|
+
|
198
|
+
## 簡単な仕組み
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
begin
|
202
|
+
load($PROGRAM_NAME)
|
203
|
+
rescue Exception => e
|
204
|
+
# エラー表示処理
|
205
|
+
end
|
206
|
+
exit
|
207
|
+
```
|
208
|
+
|
209
|
+
実際はもう少しいろいろやっていますが、このように本来のスクリプト実行が始まる前に `-returem` オプションで eturem を呼び出し、そこから `load` でスクリプトを実行しているのがポイントです。これにより、SyntaxError 等も取得できるようになっています。
|
210
|
+
|
122
211
|
## Contributing
|
123
212
|
|
124
213
|
「こう表示した方がよりわかりやすいのでは?」等のご意見ありましたら、よろしく御願いします。
|
data/README.md
CHANGED
@@ -14,6 +14,10 @@ install it yourself as:
|
|
14
14
|
|
15
15
|
$ ruby -returem <your_script.rb>
|
16
16
|
|
17
|
+
or
|
18
|
+
|
19
|
+
$ eturem <your_script.rb>
|
20
|
+
|
17
21
|
## Contributing
|
18
22
|
|
19
23
|
Bug reports and pull requests are welcome on GitHub at https://github.com/nodai2hITC/eturem. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/Rakefile
CHANGED
data/eturem.gemspec
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "eturem/version"
|
2
|
+
require_relative 'lib/eturem/version'
|
5
3
|
|
6
4
|
Gem::Specification.new do |spec|
|
7
5
|
spec.name = "eturem"
|
@@ -13,24 +11,20 @@ Gem::Specification.new do |spec|
|
|
13
11
|
spec.description = %q{Easy To Understand Ruby Error Message.}
|
14
12
|
spec.homepage = "https://github.com/nodai2hITC/eturem"
|
15
13
|
spec.license = "MIT"
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
-
"public gem pushes."
|
24
|
-
end
|
16
|
+
# spec.metadata["allowed_push_host"] = "Set to 'http://mygemserver.com'"
|
17
|
+
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
20
|
+
# spec.metadata["changelog_uri"] = spec.homepage
|
25
21
|
|
26
|
-
|
27
|
-
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
28
26
|
end
|
29
27
|
spec.bindir = "exe"
|
30
28
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
29
|
spec.require_paths = ["lib"]
|
32
|
-
|
33
|
-
spec.add_development_dependency "bundler", "~> 1.16"
|
34
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
35
|
-
spec.add_development_dependency "minitest", "~> 5.0"
|
36
30
|
end
|
data/exe/eturem
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lang = nil
|
4
|
+
if ARGV.first.to_s =~ /^lang=(.+)$/
|
5
|
+
lang = $1
|
6
|
+
ARGV.shift
|
7
|
+
end
|
8
|
+
|
9
|
+
if ARGV.empty?
|
10
|
+
puts "usage: eturem (lang=**) script.rb"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
$PROGRAM_NAME = ARGV.shift
|
15
|
+
require lang ? "eturem/#{lang}" : "eturem"
|
data/lib/eturem.rb
CHANGED
@@ -1,39 +1,79 @@
|
|
1
1
|
enable = true
|
2
|
+
debug = false
|
2
3
|
lang = "en"
|
3
4
|
output_backtrace = true
|
4
5
|
output_original = true
|
5
6
|
output_script = true
|
7
|
+
override_warning = true
|
6
8
|
use_coderay = false
|
7
|
-
max_backtrace = 16
|
8
9
|
before_line_num = 2
|
9
10
|
after_line_num = 2
|
11
|
+
repl = nil
|
10
12
|
|
11
|
-
config_file = File.join(Dir.home, ".eturem")
|
13
|
+
config_file = File.exist?("./.eturem") ? "./.eturem" : File.join(Dir.home, ".eturem")
|
12
14
|
if File.exist?(config_file)
|
13
|
-
config = File.read(config_file)
|
15
|
+
config = File.read(config_file).gsub(/#.*/, "")
|
14
16
|
enable = false if config.match(/^enable\s*\:\s*(?:false|off|0)/i)
|
17
|
+
debug = true if config.match(/^debug\s*\:\s*(?:true|on|1)/i)
|
15
18
|
lang = Regexp.last_match(:lang) if config.match(/^lang\s*\:\s*(?<lang>\S+)/i)
|
16
|
-
output_backtrace = false
|
17
|
-
output_original = false
|
18
|
-
output_script = false
|
19
|
-
|
20
|
-
|
19
|
+
output_backtrace = false if config.match(/^output_backtrace\s*\:\s*(?:false|off|0)/i)
|
20
|
+
output_original = false if config.match( /^output_original\s*\:\s*(?:false|off|0)/i)
|
21
|
+
output_script = false if config.match( /^output_script\s*\:\s*(?:false|off|0)/i)
|
22
|
+
override_warning = false if config.match(/^override_warning\s*\:\s*(?:false|off|0)/i)
|
23
|
+
use_coderay = true if config.match( /^use_coderay\s*\:\s*(?:true|on|1)/i)
|
21
24
|
before_line_num = Regexp.last_match(:num).to_i if config.match(/^before_line_num\s*\:\s*(?<num>\d+)/i)
|
22
25
|
after_line_num = Regexp.last_match(:num).to_i if config.match( /^after_line_num\s*\:\s*(?<num>\d+)/i)
|
26
|
+
repl = Regexp.last_match(:repl).downcase if config.match(/^repl\s*\:\s*(?<repl>irb|pry)/i)
|
23
27
|
end
|
24
|
-
require "eturem/#{lang}" if enable && !defined?(Eturem)
|
25
28
|
|
26
|
-
if
|
27
|
-
|
28
|
-
|
29
|
-
Eturem.
|
30
|
-
Eturem.
|
31
|
-
Eturem.
|
32
|
-
Eturem.
|
33
|
-
Eturem.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
29
|
+
if enable
|
30
|
+
require "eturem/#{lang}/main" unless defined?(Eturem)
|
31
|
+
require "eturem/warning" if override_warning
|
32
|
+
Eturem::Base.output_backtrace = output_backtrace
|
33
|
+
Eturem::Base.output_original = output_original
|
34
|
+
Eturem::Base.output_script = output_script
|
35
|
+
Eturem::Base.use_coderay = use_coderay
|
36
|
+
Eturem::Base.before_line_num = before_line_num
|
37
|
+
Eturem::Base.after_line_num = after_line_num
|
38
|
+
|
39
|
+
if File.exist?($PROGRAM_NAME)
|
40
|
+
program_file = File.open($PROGRAM_NAME, "rb")
|
41
|
+
script = program_file.read
|
42
|
+
if script.match(/^__END__\R/)
|
43
|
+
program_file.pos = Regexp.last_match.end(0)
|
44
|
+
encoding = "utf-8"
|
45
|
+
if script.match(/\A(?:#!.*\R)?#.*coding *[:=] *(?<encoding>[^\s:]+)/)
|
46
|
+
encoding = Regexp.last_match(:encoding)
|
47
|
+
end
|
48
|
+
program_file.set_encoding(encoding)
|
49
|
+
Object.const_set(:DATA, program_file)
|
50
|
+
program_file
|
51
|
+
else
|
52
|
+
program_file.close
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
eturem_path = File.expand_path("..", __FILE__)
|
57
|
+
last_binding = nil
|
58
|
+
tracepoint = TracePoint.trace(:raise) do |tp|
|
59
|
+
last_binding = tp.binding unless File.expand_path(tp.path).start_with?(eturem_path)
|
60
|
+
end
|
61
|
+
exception = Eturem.load(File.expand_path(Eturem.program_name))
|
62
|
+
tracepoint.disable
|
63
|
+
|
64
|
+
if exception.is_a?(Exception)
|
65
|
+
begin
|
66
|
+
Eturem.extend_exception(exception)
|
67
|
+
$stderr.write exception.eturem_full_message
|
68
|
+
rescue Exception => e
|
69
|
+
raise debug ? e : exception
|
70
|
+
end
|
71
|
+
|
72
|
+
repl ||= $eturem_repl if defined?($eturem_repl)
|
73
|
+
if repl && last_binding && exception.is_a?(StandardError)
|
74
|
+
require repl
|
75
|
+
last_binding.public_send(repl)
|
76
|
+
end
|
77
|
+
end
|
38
78
|
exit
|
39
79
|
end
|
data/lib/eturem/base.rb
CHANGED
@@ -1,280 +1,312 @@
|
|
1
1
|
require "eturem/version"
|
2
2
|
|
3
3
|
module Eturem
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
@program_name = $PROGRAM_NAME.encode("utf-8")
|
5
|
+
|
6
|
+
def self.rescue
|
7
|
+
yield
|
8
|
+
rescue Exception => exception
|
9
|
+
raise exception if exception.is_a? SystemExit
|
10
|
+
exception
|
11
|
+
end
|
12
|
+
|
13
|
+
# load script and return exception if exception raised
|
14
|
+
# @param [String] filename script file
|
15
|
+
# @return [Exception] if exception raised
|
16
|
+
# @return [true] if exception did not raise
|
17
|
+
def self.load(filename, wrap = false)
|
18
|
+
self.rescue do
|
19
|
+
Kernel.load(filename, wrap)
|
13
20
|
end
|
14
|
-
return nil
|
15
21
|
end
|
16
|
-
|
22
|
+
|
17
23
|
def self.eval(expr, bind = nil, fname = "(eval)", lineno = 1)
|
18
|
-
|
24
|
+
self.rescue do
|
19
25
|
bind ? Kernel.eval(expr, bind, fname, lineno) : Kernel.eval(expr)
|
20
|
-
rescue Exception => exception
|
21
|
-
return @eturem_class.new(exception) unless exception.is_a? SystemExit
|
22
26
|
end
|
23
|
-
return nil
|
24
27
|
end
|
25
|
-
|
26
|
-
def self.
|
27
|
-
|
28
|
+
|
29
|
+
def self.extend_exception(exception)
|
30
|
+
ext = _extend_exception(exception) ||
|
31
|
+
case exception
|
32
|
+
when SyntaxError then SyntaxErrorExt
|
33
|
+
when NameError then NameErrorExt
|
34
|
+
when ArgumentError then ArgumentErrorExt
|
35
|
+
else ExceptionExt
|
36
|
+
end
|
37
|
+
exception.extend ext
|
38
|
+
exception.eturem_prepare
|
28
39
|
end
|
29
|
-
|
30
|
-
def self.
|
31
|
-
@
|
40
|
+
|
41
|
+
def self.program_name
|
42
|
+
@program_name
|
32
43
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
@@
|
38
|
-
@@
|
39
|
-
@@
|
40
|
-
@@
|
41
|
-
@@
|
42
|
-
|
43
|
-
@@after_line_num = 2
|
44
|
-
|
45
|
-
@inspect_methods = {}
|
46
|
-
|
47
|
-
def self.inspect_methods
|
48
|
-
return @inspect_methods
|
49
|
-
end
|
50
|
-
|
44
|
+
|
45
|
+
|
46
|
+
module Base
|
47
|
+
@@eturem_output_backtrace = true
|
48
|
+
@@eturem_output_original = true
|
49
|
+
@@eturem_output_script = true
|
50
|
+
@@eturem_use_coderay = false
|
51
|
+
@@eturem_before_line_num = 2
|
52
|
+
@@eturem_after_line_num = 2
|
53
|
+
|
51
54
|
def self.output_backtrace=(value)
|
52
|
-
@@
|
55
|
+
@@eturem_output_backtrace = value
|
53
56
|
end
|
54
|
-
|
57
|
+
|
55
58
|
def self.output_original=(value)
|
56
|
-
@@
|
59
|
+
@@eturem_output_original = value
|
57
60
|
end
|
58
|
-
|
61
|
+
|
59
62
|
def self.output_script=(value)
|
60
|
-
@@
|
63
|
+
@@eturem_output_script = value
|
61
64
|
end
|
62
|
-
|
65
|
+
|
63
66
|
def self.use_coderay=(value)
|
64
|
-
@@
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.max_backtrace=(value)
|
68
|
-
@@max_backtrace = value
|
67
|
+
@@eturem_use_coderay = value
|
69
68
|
end
|
70
|
-
|
69
|
+
|
71
70
|
def self.before_line_num=(value)
|
72
|
-
@@
|
71
|
+
@@eturem_before_line_num = value
|
73
72
|
end
|
74
|
-
|
73
|
+
|
75
74
|
def self.after_line_num=(value)
|
76
|
-
@@
|
75
|
+
@@eturem_after_line_num = value
|
77
76
|
end
|
78
|
-
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
77
|
+
|
78
|
+
def self.highlight(str, pattern, highlight)
|
79
|
+
str.sub!(pattern) { "#{highlight}#{$1 || $&}\e[0m#{$2}" } if str
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.unhighlight(str)
|
83
|
+
str.gsub(/\e\[[0-9;]*m/, "")
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.read_script(filename)
|
87
|
+
return [] unless File.exist?(filename)
|
88
|
+
script = File.binread(filename)
|
89
|
+
encoding = "utf-8"
|
90
|
+
if script.match(/\A(?:#!.*\R)?#.*coding *[:=] *(?<encoding>[^\s:]+)/)
|
91
|
+
encoding = Regexp.last_match(:encoding)
|
94
92
|
end
|
95
|
-
|
96
|
-
if
|
97
|
-
|
98
|
-
|
99
|
-
else
|
100
|
-
backtrace_locations_shift
|
93
|
+
script.force_encoding(encoding).encode!("utf-8")
|
94
|
+
if @@eturem_use_coderay
|
95
|
+
require "coderay"
|
96
|
+
script = CodeRay.scan(script, :ruby).terminal
|
101
97
|
end
|
102
|
-
|
103
|
-
load_script
|
104
|
-
prepare
|
98
|
+
[""] + script.lines(chomp: true)
|
105
99
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
100
|
+
|
101
|
+
def self.script(lines, linenos, lineno)
|
102
|
+
str = ""
|
103
|
+
max_lineno_length = linenos.max.to_s.length
|
104
|
+
last_i = linenos.min.to_i - 1
|
105
|
+
linenos.uniq.sort.each do |i|
|
106
|
+
line = lines[i]
|
107
|
+
next unless line
|
108
|
+
str += " #{' ' * max_lineno_length} :\n" if last_i + 1 != i
|
109
|
+
str += (lineno == i ? " => " : " ")
|
110
|
+
str += sprintf("\e[1;34m%#{max_lineno_length}d\e[0m: %s\e[0m\n", i, line)
|
111
|
+
last_i = i
|
116
112
|
end
|
117
|
-
|
113
|
+
str
|
118
114
|
end
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
115
|
+
|
116
|
+
|
117
|
+
def eturem_prepare
|
118
|
+
this_dirpath = File.dirname(File.expand_path(__FILE__))
|
119
|
+
@eturem_backtrace_locations = (self.backtrace_locations || []).reject do |location|
|
120
|
+
File.expand_path(location.path).start_with?(this_dirpath) ||
|
121
|
+
location.path.end_with?("/rubygems/core_ext/kernel_require.rb")
|
126
122
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
return method ? public_send(method) : nil
|
123
|
+
|
124
|
+
program_filepath = File.expand_path(Eturem.program_name)
|
125
|
+
@eturem_backtrace_locations.each do |location|
|
126
|
+
if File.expand_path(location.path) == program_filepath
|
127
|
+
def location.path; Eturem.program_name; end
|
128
|
+
if location.label == "<top (required)>"
|
129
|
+
def location.label; "<main>"; end
|
130
|
+
end
|
136
131
|
end
|
137
132
|
end
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
else
|
145
|
-
location_str = "#{@path}:#{@lineno}:in `#{@label}'"
|
146
|
-
if @exception_s.match(/\A(?<first_line>.*?)\n/)
|
147
|
-
return "#{location_str}: #{Regexp.last_match(:first_line)} (#{@exception.class})\n" +
|
148
|
-
"#{Regexp.last_match.post_match}"
|
149
|
-
else
|
150
|
-
return "#{location_str}: #{@exception_s} (#{@exception.class})"
|
133
|
+
|
134
|
+
@eturem_message = self.message
|
135
|
+
unless @eturem_message.encoding == Encoding::UTF_8
|
136
|
+
@eturem_message.force_encoding("utf-8")
|
137
|
+
unless @eturem_message.valid_encoding?
|
138
|
+
@eturem_message.force_encoding(Encoding.locale_charmap).encode!("utf-8")
|
151
139
|
end
|
152
140
|
end
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
puts sprintf("\e[0m => \e[1;34m%#{max_lineno_length}d\e[0m: %s", i, @script_lines[i])
|
162
|
-
elsif i
|
163
|
-
puts sprintf("\e[0m \e[1;34m%#{max_lineno_length}d\e[0m: %s", i, @script_lines[i])
|
164
|
-
else
|
165
|
-
puts "\e[0m #{" " * max_lineno_length} :"
|
166
|
-
end
|
141
|
+
|
142
|
+
if self.is_a?(SyntaxError) && @eturem_message.match(/\A(?<path>.+?)\:(?<lineno>\d+):\s*/)
|
143
|
+
@eturem_path = Regexp.last_match(:path)
|
144
|
+
@eturem_lineno = Regexp.last_match(:lineno).to_i
|
145
|
+
@eturem_message = Regexp.last_match.post_match
|
146
|
+
@eturem_path = Eturem.program_name if @eturem_path == program_filepath
|
147
|
+
else
|
148
|
+
eturem_backtrace_locations_shift
|
167
149
|
end
|
150
|
+
|
151
|
+
@eturem_script_lines = Eturem::Base.read_script(@eturem_path)
|
152
|
+
@eturem_output_linenos = eturem_default_output_linenos
|
168
153
|
end
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
when NameError then prepare_name_error
|
176
|
-
when ArgumentError then prepare_argument_error
|
177
|
-
when TypeError then prepare_type_error
|
178
|
-
end
|
154
|
+
|
155
|
+
def eturem_original_error_message()
|
156
|
+
@eturem_message.match(/\A(?<first_line>.*)/)
|
157
|
+
"#{@eturem_path}:#{@eturem_lineno}:in `#{@eturem_label}': " +
|
158
|
+
"\e[1m#{Regexp.last_match(:first_line)} (\e[4m#{self.class}\e[0;1m)" +
|
159
|
+
"#{Regexp.last_match.post_match.chomp}\e[0m\n"
|
179
160
|
end
|
180
|
-
|
181
|
-
def
|
182
|
-
|
183
|
-
|
184
|
-
@expected = @exception_s.match(/[,\s]expecting (?<expected>\S+)/) ?
|
185
|
-
Regexp.last_match(:expected) : nil
|
186
|
-
if !@expected && @exception_s.match(/(?<invalid>(?:break|next|retry|redo|yield))/)
|
187
|
-
@invalid = Regexp.last_match(:invalid)
|
188
|
-
end
|
161
|
+
|
162
|
+
def eturem_backtrace_str(order = :bottom)
|
163
|
+
str = @eturem_backtrace_locations.empty? ? "" : eturem_traceback(order)
|
164
|
+
str + (order == :top ? eturem_backtrace_str_top : eturem_backtrace_str_bottom)
|
189
165
|
end
|
190
|
-
|
191
|
-
def
|
192
|
-
|
193
|
-
|
194
|
-
@did_you_mean = Regexp.last_match.post_match.strip.scan(/\S+/)
|
195
|
-
return if @script.empty?
|
196
|
-
|
197
|
-
new_range = []
|
198
|
-
@did_you_mean.each do |name|
|
199
|
-
index = @script_lines.index { |line| line.include?(name) }
|
200
|
-
next unless index
|
201
|
-
highlight!(@script_lines[index], name, "\e[1;33m")
|
202
|
-
new_range.push(index)
|
203
|
-
end
|
204
|
-
new_range.sort!
|
205
|
-
before = new_range.select { |i| i < @output_lines.first }
|
206
|
-
after = new_range.select { |i| i > @output_lines.last }
|
207
|
-
unless before.empty?
|
208
|
-
@output_lines.unshift(nil) if before.last + 1 != @output_lines.first
|
209
|
-
@output_lines.unshift(*before)
|
210
|
-
end
|
211
|
-
unless after.empty?
|
212
|
-
@output_lines.push(nil) if @output_lines.last + 1 != after.first
|
213
|
-
@output_lines.push(*after)
|
166
|
+
|
167
|
+
def eturem_backtrace
|
168
|
+
eturem_backtrace_locations.map do |location|
|
169
|
+
eturem_location_to_s(location)
|
214
170
|
end
|
215
171
|
end
|
216
|
-
|
217
|
-
def
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
@
|
223
|
-
|
224
|
-
|
225
|
-
|
172
|
+
|
173
|
+
def eturem_location_to_s(location)
|
174
|
+
"#{location.path}:#{location.lineno}:in `#{location.label}'"
|
175
|
+
end
|
176
|
+
|
177
|
+
def eturem_backtrace_locations
|
178
|
+
@eturem_backtrace_locations
|
179
|
+
end
|
180
|
+
|
181
|
+
def eturem_message
|
182
|
+
""
|
183
|
+
end
|
184
|
+
|
185
|
+
def eturem_script
|
186
|
+
Eturem::Base.script(@eturem_script_lines, @eturem_output_linenos, @eturem_lineno)
|
187
|
+
end
|
188
|
+
|
189
|
+
def eturem_full_message(highlight: true, order: :bottom)
|
190
|
+
unless $stderr == STDERR && $stderr.tty?
|
191
|
+
highlight = false
|
192
|
+
order = :top
|
226
193
|
end
|
194
|
+
|
195
|
+
str = @@eturem_output_backtrace ? eturem_backtrace_str(order) : ""
|
196
|
+
ext_message = eturem_message
|
197
|
+
if ext_message.empty?
|
198
|
+
str += eturem_original_error_message
|
199
|
+
else
|
200
|
+
str = "#{eturem_original_error_message}\n#{str}" if @@eturem_output_original
|
201
|
+
str += "#{ext_message}\n"
|
202
|
+
end
|
203
|
+
str += eturem_script if @@eturem_output_script
|
204
|
+
|
205
|
+
highlight ? str : Eturem::Base.unhighlight(str)
|
227
206
|
end
|
228
|
-
|
229
|
-
|
230
|
-
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def eturem_backtrace_locations_shift
|
211
|
+
location = @eturem_backtrace_locations.shift
|
212
|
+
@eturem_label = location.label
|
213
|
+
@eturem_path = location.path
|
214
|
+
@eturem_lineno = location.lineno
|
231
215
|
end
|
232
|
-
|
233
|
-
def
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
@backtrace_locations.shift
|
216
|
+
|
217
|
+
def eturem_default_output_linenos
|
218
|
+
from = [1, @eturem_lineno - @@eturem_before_line_num].max
|
219
|
+
to = [@eturem_script_lines.size - 1, @eturem_lineno + @@eturem_after_line_num].min
|
220
|
+
(from..to).to_a
|
238
221
|
end
|
239
|
-
|
240
|
-
def
|
241
|
-
"
|
222
|
+
|
223
|
+
def eturem_traceback(order = :bottom)
|
224
|
+
order == :top ? "" : "\e[1mTraceback\e[0m (most recent call last):\n"
|
242
225
|
end
|
243
|
-
|
244
|
-
def
|
245
|
-
|
226
|
+
|
227
|
+
def eturem_backtrace_str_bottom
|
228
|
+
lines = []
|
229
|
+
backtrace = eturem_backtrace
|
230
|
+
size = backtrace.size
|
231
|
+
format = "%#{8 + size.to_s.length}d: %s\n"
|
232
|
+
backtrace.reverse.each_with_index do |bt, i|
|
233
|
+
lines.push(sprintf(format, size - i, bt))
|
234
|
+
end
|
235
|
+
|
236
|
+
if @eturem_message == "stack level too deep"
|
237
|
+
lines = lines[-4..-1] +
|
238
|
+
[" ... #{lines.size - 12} levels...\n"] +
|
239
|
+
lines[0..7]
|
240
|
+
end
|
241
|
+
lines.join
|
246
242
|
end
|
247
|
-
|
248
|
-
def
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
@script.force_encoding(encoding)
|
243
|
+
|
244
|
+
def eturem_backtrace_str_top
|
245
|
+
lines = eturem_backtrace.map do |bt|
|
246
|
+
" from #{bt}\n"
|
247
|
+
end
|
248
|
+
if @eturem_message == "stack level too deep"
|
249
|
+
lines = lines[0..7] +
|
250
|
+
[" ... #{lines.size - 12} levels...\n"] +
|
251
|
+
lines[-4..-1]
|
257
252
|
end
|
258
|
-
|
259
|
-
@script_lines = @script.lines
|
260
|
-
@script_lines.unshift("")
|
261
|
-
@output_lines = default_output_lines
|
253
|
+
lines.join
|
262
254
|
end
|
263
|
-
|
264
|
-
|
265
|
-
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
module ExceptionExt
|
259
|
+
include Base
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
module NameErrorExt
|
264
|
+
include ExceptionExt
|
265
|
+
|
266
|
+
def eturem_prepare()
|
267
|
+
@eturem_corrections = self.respond_to?(:corrections) ? self.corrections : []
|
268
|
+
@eturem_corrections += Object.constants.map(&:to_s).select do |const|
|
269
|
+
const.casecmp?(self.name)
|
270
|
+
end
|
271
|
+
@eturem_corrections.uniq!
|
272
|
+
def self.corrections; @eturem_corrections; end
|
273
|
+
super
|
274
|
+
uname = self.name.to_s.encode("utf-8")
|
275
|
+
if @eturem_script_lines[@eturem_lineno]
|
276
|
+
Eturem::Base.highlight(@eturem_script_lines[@eturem_lineno], uname, "\e[1;4;31m")
|
277
|
+
end
|
278
|
+
@eturem_corrections.each do |name|
|
279
|
+
index = @eturem_script_lines.index { |line| line.include?(name.to_s.encode("utf-8")) }
|
280
|
+
next unless index
|
281
|
+
Eturem::Base.highlight(@eturem_script_lines[index], name.to_s.encode("utf-8"), "\e[1;33m")
|
282
|
+
@eturem_output_linenos.push(index)
|
283
|
+
end
|
266
284
|
end
|
267
|
-
|
268
|
-
|
269
|
-
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
module ArgumentErrorExt
|
289
|
+
include ExceptionExt
|
290
|
+
|
291
|
+
def eturem_prepare()
|
292
|
+
super
|
293
|
+
@eturem_method = @eturem_label
|
294
|
+
eturem_backtrace_locations_shift
|
295
|
+
@eturem_script_lines = Eturem::Base.read_script(@eturem_path)
|
296
|
+
@eturem_output_linenos = eturem_default_output_linenos
|
270
297
|
end
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
module SyntaxErrorExt
|
302
|
+
include ExceptionExt
|
303
|
+
|
304
|
+
def eturem_original_error_message()
|
305
|
+
ret = "#{@eturem_path}:#{@eturem_lineno}: #{@eturem_message}"
|
306
|
+
unless @eturem_path == Eturem.program_name
|
307
|
+
ret = "\e[1m#{ret} (\e[4m#{self.class}\e[0;1m)\e[0m"
|
308
|
+
end
|
309
|
+
ret + "\n"
|
276
310
|
end
|
277
|
-
|
278
|
-
Eturem.eturem_class = self
|
279
311
|
end
|
280
312
|
end
|