byebug 3.5.1 → 4.0.0

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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rubocop.yml +18 -1
  4. data/.travis.yml +21 -1
  5. data/CHANGELOG.md +356 -308
  6. data/CONTRIBUTING.md +31 -15
  7. data/GUIDE.md +859 -475
  8. data/Gemfile +8 -10
  9. data/LICENSE +1 -1
  10. data/README.md +41 -45
  11. data/Rakefile +30 -28
  12. data/byebug.gemspec +18 -18
  13. data/ext/byebug/breakpoint.c +88 -75
  14. data/ext/byebug/byebug.c +253 -252
  15. data/ext/byebug/byebug.h +53 -53
  16. data/ext/byebug/context.c +188 -159
  17. data/ext/byebug/extconf.rb +9 -6
  18. data/ext/byebug/locker.c +53 -11
  19. data/ext/byebug/threads.c +137 -39
  20. data/lib/byebug/attacher.rb +7 -2
  21. data/lib/byebug/breakpoint.rb +30 -0
  22. data/lib/byebug/command.rb +36 -32
  23. data/lib/byebug/commands/break.rb +49 -48
  24. data/lib/byebug/commands/catch.rb +64 -0
  25. data/lib/byebug/commands/condition.rb +13 -9
  26. data/lib/byebug/commands/continue.rb +8 -4
  27. data/lib/byebug/commands/delete.rb +10 -4
  28. data/lib/byebug/commands/display.rb +33 -25
  29. data/lib/byebug/commands/edit.rb +18 -13
  30. data/lib/byebug/commands/enable_disable.rb +26 -24
  31. data/lib/byebug/commands/eval.rb +77 -35
  32. data/lib/byebug/commands/finish.rb +9 -5
  33. data/lib/byebug/commands/frame.rb +66 -125
  34. data/lib/byebug/commands/help.rb +14 -21
  35. data/lib/byebug/commands/history.rb +5 -1
  36. data/lib/byebug/commands/info.rb +41 -106
  37. data/lib/byebug/commands/interrupt.rb +6 -2
  38. data/lib/byebug/commands/irb.rb +5 -2
  39. data/lib/byebug/commands/kill.rb +6 -2
  40. data/lib/byebug/commands/list.rb +21 -14
  41. data/lib/byebug/commands/method.rb +17 -9
  42. data/lib/byebug/commands/pry.rb +13 -3
  43. data/lib/byebug/commands/quit.rb +10 -5
  44. data/lib/byebug/commands/restart.rb +12 -19
  45. data/lib/byebug/commands/save.rb +10 -6
  46. data/lib/byebug/commands/set.rb +15 -14
  47. data/lib/byebug/commands/show.rb +8 -8
  48. data/lib/byebug/commands/source.rb +14 -8
  49. data/lib/byebug/commands/stepping.rb +15 -29
  50. data/lib/byebug/commands/threads.rb +73 -49
  51. data/lib/byebug/commands/tracevar.rb +56 -0
  52. data/lib/byebug/commands/undisplay.rb +8 -4
  53. data/lib/byebug/commands/untracevar.rb +38 -0
  54. data/lib/byebug/commands/var.rb +107 -0
  55. data/lib/byebug/context.rb +78 -42
  56. data/lib/byebug/core.rb +78 -40
  57. data/lib/byebug/helper.rb +58 -42
  58. data/lib/byebug/history.rb +12 -1
  59. data/lib/byebug/interface.rb +91 -11
  60. data/lib/byebug/interfaces/local_interface.rb +12 -19
  61. data/lib/byebug/interfaces/remote_interface.rb +12 -15
  62. data/lib/byebug/interfaces/script_interface.rb +14 -18
  63. data/lib/byebug/interfaces/test_interface.rb +54 -0
  64. data/lib/byebug/printers/base.rb +64 -0
  65. data/lib/byebug/printers/plain.rb +53 -0
  66. data/lib/byebug/processor.rb +20 -1
  67. data/lib/byebug/processors/command_processor.rb +57 -172
  68. data/lib/byebug/processors/control_command_processor.rb +16 -43
  69. data/lib/byebug/remote.rb +13 -7
  70. data/lib/byebug/runner.rb +102 -54
  71. data/lib/byebug/setting.rb +45 -68
  72. data/lib/byebug/settings/autoeval.rb +2 -0
  73. data/lib/byebug/settings/autoirb.rb +3 -0
  74. data/lib/byebug/settings/autolist.rb +3 -0
  75. data/lib/byebug/settings/autosave.rb +2 -0
  76. data/lib/byebug/settings/basename.rb +2 -0
  77. data/lib/byebug/settings/callstyle.rb +2 -0
  78. data/lib/byebug/settings/fullpath.rb +2 -0
  79. data/lib/byebug/settings/histfile.rb +2 -0
  80. data/lib/byebug/settings/histsize.rb +2 -0
  81. data/lib/byebug/settings/linetrace.rb +2 -0
  82. data/lib/byebug/settings/listsize.rb +2 -0
  83. data/lib/byebug/settings/post_mortem.rb +7 -2
  84. data/lib/byebug/settings/stack_on_error.rb +2 -0
  85. data/lib/byebug/settings/verbose.rb +2 -0
  86. data/lib/byebug/settings/width.rb +2 -0
  87. data/lib/byebug/state.rb +12 -0
  88. data/lib/byebug/states/control_state.rb +26 -0
  89. data/lib/byebug/states/regular_state.rb +178 -0
  90. data/lib/byebug/version.rb +1 -1
  91. metadata +24 -109
  92. data/lib/byebug/commands/catchpoint.rb +0 -53
  93. data/lib/byebug/commands/reload.rb +0 -29
  94. data/lib/byebug/commands/trace.rb +0 -50
  95. data/lib/byebug/commands/variables.rb +0 -206
  96. data/lib/byebug/options.rb +0 -46
  97. data/lib/byebug/settings/autoreload.rb +0 -12
  98. data/lib/byebug/settings/forcestep.rb +0 -14
  99. data/lib/byebug/settings/testing.rb +0 -12
  100. data/lib/byebug/settings/tracing_plus.rb +0 -11
  101. data/test/commands/break_test.rb +0 -364
  102. data/test/commands/condition_test.rb +0 -85
  103. data/test/commands/continue_test.rb +0 -47
  104. data/test/commands/delete_test.rb +0 -26
  105. data/test/commands/display_test.rb +0 -37
  106. data/test/commands/edit_test.rb +0 -52
  107. data/test/commands/eval_test.rb +0 -89
  108. data/test/commands/finish_test.rb +0 -74
  109. data/test/commands/frame_test.rb +0 -223
  110. data/test/commands/help_test.rb +0 -66
  111. data/test/commands/history_test.rb +0 -61
  112. data/test/commands/info_test.rb +0 -238
  113. data/test/commands/interrupt_test.rb +0 -45
  114. data/test/commands/irb_test.rb +0 -28
  115. data/test/commands/kill_test.rb +0 -50
  116. data/test/commands/list_test.rb +0 -174
  117. data/test/commands/method_test.rb +0 -52
  118. data/test/commands/post_mortem_test.rb +0 -71
  119. data/test/commands/pry_test.rb +0 -26
  120. data/test/commands/quit_test.rb +0 -53
  121. data/test/commands/reload_test.rb +0 -39
  122. data/test/commands/restart_test.rb +0 -46
  123. data/test/commands/save_test.rb +0 -67
  124. data/test/commands/set_test.rb +0 -140
  125. data/test/commands/show_test.rb +0 -76
  126. data/test/commands/source_test.rb +0 -46
  127. data/test/commands/stepping_test.rb +0 -192
  128. data/test/commands/thread_test.rb +0 -164
  129. data/test/commands/trace_test.rb +0 -71
  130. data/test/commands/undisplay_test.rb +0 -75
  131. data/test/commands/variables_test.rb +0 -105
  132. data/test/debugger_alias_test.rb +0 -7
  133. data/test/runner_test.rb +0 -150
  134. data/test/support/matchers.rb +0 -65
  135. data/test/support/test_interface.rb +0 -59
  136. data/test/support/utils.rb +0 -122
  137. data/test/test_helper.rb +0 -58
@@ -1,14 +1,19 @@
1
+ require 'byebug/command'
2
+
3
+ #
4
+ # TODO: Implement thread commands as a single command with subcommands, just
5
+ # like `info`, `var`, `enable` and `disable`. This allows consistent help
6
+ # format and we can go back to showing help for a single command in the `help`
7
+ # command.
8
+ #
1
9
  module Byebug
2
10
  #
3
11
  # Utilities to assist commands related to threads.
4
12
  #
5
13
  module ThreadFunctions
6
14
  def display_context(context, should_show_top_frame = true)
7
- args = thread_arguments(context, should_show_top_frame)
8
- interp = format("%s%s%d %s\t%s",
9
- args[:status_flag], args[:debug_flag], args[:id],
10
- args[:thread], args[:file_line])
11
- puts interp
15
+ puts pr('thread.context',
16
+ thread_arguments(context, should_show_top_frame))
12
17
  end
13
18
 
14
19
  def thread_arguments(context, should_show_top_frame = true)
@@ -18,50 +23,47 @@ module Byebug
18
23
  context.thread == Thread.current ? '+' : ' '
19
24
  end
20
25
  debug_flag = context.ignored? ? '!' : ' '
26
+
21
27
  if should_show_top_frame
22
- if context.thread == Thread.current && !context.dead?
23
- file = context.frame_file(0)
24
- line = context.frame_line(0)
28
+ if context == Byebug.current_context
29
+ file_line = "#{@state.file}:#{@state.line}"
25
30
  else
26
- if context.thread.backtrace_locations &&
27
- context.thread.backtrace_locations[0]
28
- file = context.thread.backtrace_locations[0].path
29
- line = context.thread.backtrace_locations[0].lineno
31
+ backtrace = context.thread.backtrace_locations
32
+ if backtrace && backtrace[0]
33
+ file_line = "#{backtrace[0].path}:#{backtrace[0].lineno}"
30
34
  end
31
35
  end
32
- file_line = "#{file}:#{line}"
33
36
  end
34
37
  {
35
38
  status_flag: status_flag,
36
39
  debug_flag: debug_flag,
37
40
  id: context.thnum,
38
41
  thread: context.thread.inspect,
39
- file_line: file_line ? file_line : ''
42
+ file_line: file_line || ''
40
43
  }
41
44
  end
42
45
 
43
46
  def parse_thread_num(subcmd, arg)
44
- return errmsg("\"#{subcmd}\" needs a thread number") if '' == arg
45
-
46
- thread_num, err = get_int(arg, subcmd, 1)
47
- return errmsg(err) unless thread_num
47
+ thnum, err = get_int(arg, subcmd, 1)
48
+ return [nil, err] unless thnum
48
49
 
49
50
  Byebug.contexts.find { |c| c.thnum == thnum }
50
51
  end
51
52
 
52
53
  def parse_thread_num_for_cmd(subcmd, arg)
53
- c = parse_thread_num(subcmd, arg)
54
- return unless c
54
+ c, err = parse_thread_num(subcmd, arg)
55
55
 
56
56
  case
57
- when nil == c
58
- errmsg 'No such thread'
57
+ when err
58
+ [c, err]
59
+ when c.nil?
60
+ [nil, pr('thread.errors.no_thread')]
59
61
  when @state.context == c
60
- errmsg "It's the current thread"
62
+ [c, pr('thread.errors.current_thread')]
61
63
  when c.ignored?
62
- errmsg "Can't #{subcmd} thread #{arg}"
64
+ [c, pr('thread.errors.wrong_action', subcmd: subcmd, arg: arg)]
63
65
  else
64
- c
66
+ [c, nil]
65
67
  end
66
68
  end
67
69
  end
@@ -70,6 +72,8 @@ module Byebug
70
72
  # List current threads.
71
73
  #
72
74
  class ThreadListCommand < Command
75
+ include ThreadFunctions
76
+
73
77
  self.allow_in_control = true
74
78
 
75
79
  def regexp
@@ -77,8 +81,13 @@ module Byebug
77
81
  end
78
82
 
79
83
  def execute
80
- Byebug.contexts.select { |c| Thread.list.include?(c.thread) }
81
- .sort_by(&:thnum).each { |c| display_context(c) }
84
+ contexts = Byebug.contexts.sort_by(&:thnum)
85
+
86
+ thread_list = prc('thread.context', contexts) do |context, _|
87
+ thread_arguments(context)
88
+ end
89
+
90
+ print(thread_list)
82
91
  end
83
92
 
84
93
  class << self
@@ -87,7 +96,9 @@ module Byebug
87
96
  end
88
97
 
89
98
  def description
90
- %(th[read] l[ist] Lists all threads.)
99
+ prettify <<-EOD
100
+ th[read] l[ist] Lists all threads.
101
+ EOD
91
102
  end
92
103
  end
93
104
  end
@@ -96,6 +107,8 @@ module Byebug
96
107
  # Show current thread.
97
108
  #
98
109
  class ThreadCurrentCommand < Command
110
+ include ThreadFunctions
111
+
99
112
  def regexp
100
113
  /^\s* th(?:read)? \s+ (?:cur(?:rent)?)? \s*$/x
101
114
  end
@@ -110,7 +123,9 @@ module Byebug
110
123
  end
111
124
 
112
125
  def description
113
- %(th[read] [cur[rent]] Shows current thread.)
126
+ prettify <<-EOD
127
+ th[read] cur[rent] Shows current thread.
128
+ EOD
114
129
  end
115
130
  end
116
131
  end
@@ -119,7 +134,9 @@ module Byebug
119
134
  # Stop execution of a thread.
120
135
  #
121
136
  class ThreadStopCommand < Command
122
- self.allow_in_control = true
137
+ include ThreadFunctions
138
+
139
+ self.allow_in_control = true
123
140
  self.allow_in_post_mortem = false
124
141
 
125
142
  def regexp
@@ -127,8 +144,8 @@ module Byebug
127
144
  end
128
145
 
129
146
  def execute
130
- c = parse_thread_num_for_cmd('thread stop', @match[1])
131
- return unless c
147
+ c, err = parse_thread_num_for_cmd('thread stop', @match[1])
148
+ return errmsg(err) if err
132
149
 
133
150
  c.suspend
134
151
  display_context(c)
@@ -140,7 +157,9 @@ module Byebug
140
157
  end
141
158
 
142
159
  def description
143
- %(th[read] stop <n> Stops thread <n>.)
160
+ prettify <<-EOD
161
+ th[read] stop <n> Stops thread <n>.
162
+ EOD
144
163
  end
145
164
  end
146
165
  end
@@ -149,7 +168,9 @@ module Byebug
149
168
  # Resume execution of a thread.
150
169
  #
151
170
  class ThreadResumeCommand < Command
152
- self.allow_in_control = true
171
+ include ThreadFunctions
172
+
173
+ self.allow_in_control = true
153
174
  self.allow_in_post_mortem = false
154
175
 
155
176
  def regexp
@@ -157,9 +178,10 @@ module Byebug
157
178
  end
158
179
 
159
180
  def execute
160
- c = parse_thread_num_for_cmd('thread resume', @match[1])
161
- return unless c
162
- return errmsg('Already running') unless c.suspended?
181
+ c, err = parse_thread_num_for_cmd('thread resume', @match[1])
182
+ return errmsg(err) if err
183
+ return errmsg pr('thread.errors.already_running') unless c.suspended?
184
+
163
185
  c.resume
164
186
  display_context(c)
165
187
  end
@@ -170,7 +192,9 @@ module Byebug
170
192
  end
171
193
 
172
194
  def description
173
- %(th[read] resume <n> Resumes thread <n>.)
195
+ prettify <<-EOD
196
+ th[read] resume <n> Resumes thread <n>.
197
+ EOD
174
198
  end
175
199
  end
176
200
  end
@@ -179,24 +203,22 @@ module Byebug
179
203
  # Switch execution to a different thread.
180
204
  #
181
205
  class ThreadSwitchCommand < Command
182
- self.allow_in_control = true
206
+ include ThreadFunctions
207
+
208
+ self.allow_in_control = true
183
209
  self.allow_in_post_mortem = false
184
210
 
185
211
  def regexp
186
- /^\s* th(?:read)? \s+ (?:sw(?:itch)?\s+)? (\S+) \s*$/x
212
+ /^\s* th(?:read)? \s+ sw(?:itch)? (?:\s+(\S+))? \s*$/x
187
213
  end
188
214
 
189
215
  def execute
190
- if @match[1] =~ /switch/
191
- return errmsg('"thread switch" needs a thread number')
192
- end
193
-
194
- c = parse_thread_num_for_cmd('thread switch', @match[1])
195
- return unless c
216
+ c, err = parse_thread_num_for_cmd('thread switch', @match[1])
217
+ return errmsg(err) if err
196
218
 
197
219
  display_context(c)
198
- c.step_into 1
199
- c.thread.run
220
+
221
+ c.switch
200
222
  @state.proceed
201
223
  end
202
224
 
@@ -206,7 +228,9 @@ module Byebug
206
228
  end
207
229
 
208
230
  def description
209
- %(th[read] [sw[itch]] <nnn> Switches thread context to <n>.)
231
+ prettify <<-EOD
232
+ th[read] sw[itch] <n> Switches thread context to <n>.
233
+ EOD
210
234
  end
211
235
  end
212
236
  end
@@ -0,0 +1,56 @@
1
+ require 'byebug/command'
2
+
3
+ module Byebug
4
+ #
5
+ # Show (and possibily stop) at every line that changes a global variable.
6
+ #
7
+ class TracevarCommand < Command
8
+ self.allow_in_post_mortem = false
9
+
10
+ def regexp
11
+ /^\s* tr(?:acevar)? (?: \s+ (\S+))? # (variable-name)?
12
+ (?: \s+ (stop|nostop))?
13
+ \s*$/x
14
+ end
15
+
16
+ def execute
17
+ var = @match[1]
18
+ return errmsg(pr('trace.errors.needs_global_variable')) unless var
19
+
20
+ unless global_variables.include?(:"#{var}")
21
+ return errmsg(pr('trace.errors.var_is_not_global', name: var))
22
+ end
23
+
24
+ stop = @match[2] && @match[2] !~ /nostop/
25
+
26
+ instance_eval do
27
+ trace_var(:"#{var}") { |val| on_change(var, val, stop) }
28
+ end
29
+
30
+ puts pr('trace.messages.success', var: var)
31
+ end
32
+
33
+ def on_change(name, value, stop)
34
+ puts pr('trace.messages.on_change', name: name, value: value)
35
+ byebug(1, false) if stop
36
+ end
37
+
38
+ class << self
39
+ def names
40
+ %w(tracevar)
41
+ end
42
+
43
+ def description
44
+ prettify <<-EOD
45
+ tr[acevar] <variable> [[no]stop]
46
+
47
+ Start tracing variable <variable>.
48
+
49
+ If "stop" is specified, execution will stop every time the variable
50
+ changes its value. If nothing or "nostop" is specified, execution
51
+ won't stop, changes will just be logged in byebug's output.
52
+ EOD
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,3 +1,5 @@
1
+ require 'byebug/command'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Remove expressions from display list.
@@ -15,12 +17,12 @@ module Byebug
15
17
  return errmsg(err) unless err.nil?
16
18
 
17
19
  unless @state.display[pos - 1]
18
- return errmsg("Display expression #{pos} is not defined.")
20
+ return errmsg(pr('display.errors.undefined', expr: pos))
19
21
  end
20
22
 
21
23
  @state.display[pos - 1][0] = nil
22
24
  else
23
- return unless confirm('Clear all expressions? (y/n) ')
25
+ return unless confirm(pr('display.confirmations.clear_all'))
24
26
 
25
27
  @state.display.each { |d| d[0] = false }
26
28
  end
@@ -32,13 +34,15 @@ module Byebug
32
34
  end
33
35
 
34
36
  def description
35
- %(undisp[lay][ nnn]
37
+ prettify <<-EOD
38
+ undisp[lay][ nnn]
36
39
 
37
40
  Cancel some expressions to be displayed when program stops. Arguments
38
41
  are the code numbers of the expressions to stop displaying. No
39
42
  argument means cancel all automatic-display expressions. "delete
40
43
  display" has the same effect as this command. Do "info display" to see
41
- the current list of code numbers.)
44
+ the current list of code numbers.
45
+ EOD
42
46
  end
43
47
  end
44
48
  end
@@ -0,0 +1,38 @@
1
+ require 'byebug/command'
2
+
3
+ module Byebug
4
+ #
5
+ # Stop tracing a global variable.
6
+ #
7
+ class UntracevarCommand < Command
8
+ self.allow_in_post_mortem = false
9
+
10
+ def regexp
11
+ /^\s* untr(?:acevar)? (?:\s+ (\S+))? \s*$/x
12
+ end
13
+
14
+ def execute
15
+ var = @match[1]
16
+ if global_variables.include?(:"#{var}")
17
+ eval("untrace_var(:\"#{var}\")")
18
+ puts pr('trace.messages.undo', var: var)
19
+ else
20
+ errmsg pr('trace.errors.not_global', var: var)
21
+ end
22
+ end
23
+
24
+ class << self
25
+ def names
26
+ %w(untracevar)
27
+ end
28
+
29
+ def description
30
+ prettify <<-EOD
31
+ untr[acevar] <variable>
32
+
33
+ Stop tracing global variable <variable>.
34
+ EOD
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,107 @@
1
+ require 'byebug/command'
2
+
3
+ module Byebug
4
+ #
5
+ # Utilities for the var command.
6
+ #
7
+ module VarFunctions
8
+ def var_list(ary, b = get_binding)
9
+ vars = ary.sort.map do |v|
10
+ s = begin
11
+ b.eval(v.to_s).inspect
12
+ rescue
13
+ begin
14
+ b.eval(v.to_s).to_s
15
+ rescue
16
+ '*Error in evaluation*'
17
+ end
18
+ end
19
+ [v, s]
20
+ end
21
+ puts prv(vars)
22
+ end
23
+
24
+ def var_global(_str = nil)
25
+ globals = global_variables.reject do |v|
26
+ [:$IGNORECASE, :$=, :$KCODE, :$-K, :$binding].include?(v)
27
+ end
28
+
29
+ var_list(globals)
30
+ end
31
+
32
+ def var_instance(str)
33
+ obj = bb_warning_eval(str || 'self')
34
+ var_list(obj.instance_variables, obj.instance_eval { binding })
35
+ end
36
+
37
+ def var_constant(str)
38
+ str ||= 'self.class'
39
+ obj = bb_warning_eval(str)
40
+ is_mod = obj.is_a?(Module)
41
+ return errmsg(pr('variable.errors.not_module', object: str)) unless is_mod
42
+
43
+ constants = bb_eval("#{str}.constants")
44
+ puts prv(constants.sort.map { |c| [c, obj.const_get(c)] })
45
+ end
46
+
47
+ def var_local(_str = nil)
48
+ _self = @state.context.frame_self(@state.frame)
49
+ locals = @state.context.frame_locals
50
+ puts prv(locals.keys.sort.map { |k| [k, locals[k]] })
51
+ end
52
+
53
+ def var_all(_str = nil)
54
+ var_global
55
+ var_instance('self')
56
+ var_local
57
+ end
58
+ end
59
+
60
+ #
61
+ # Show variables and its values.
62
+ #
63
+ class VarCommand < Command
64
+ include VarFunctions
65
+
66
+ Subcommands = [
67
+ ['constant', 2, 'Show constants of an object'],
68
+ ['global', 1, 'Show global variables'],
69
+ ['instance', 1, 'Show instance variables of self or a specific object'],
70
+ ['local', 1, 'Show local variables in current scope'],
71
+ ['all', 1, 'Shows local, global and instance variables of self']
72
+ ].map do |name, min, help|
73
+ Subcmd.new(name, min, help)
74
+ end
75
+
76
+ def regexp
77
+ /^\s* v(?:ar)? (?: \s+(\S+) (?:\s(\S+))? )? \s*$/x
78
+ end
79
+
80
+ def execute
81
+ return puts(self.class.help) unless @match[1]
82
+
83
+ subcmd = Command.find(Subcommands, @match[1])
84
+ return errmsg("Unknown var command #{@match[1]}\n") unless subcmd
85
+
86
+ if @state.context
87
+ send("var_#{subcmd.name}", @match[2])
88
+ else
89
+ errmsg "'var #{subcmd.name}' not available without a context.\n"
90
+ end
91
+ end
92
+
93
+ class << self
94
+ def names
95
+ %w(var)
96
+ end
97
+
98
+ def description
99
+ prettify <<-EOD
100
+ [v]ar
101
+
102
+ Show variables and its values.
103
+ EOD
104
+ end
105
+ end
106
+ end
107
+ end