Repository: ruby/debug (Ruby)

Issue:
debug does not show context when navigating up & down stack
**Your environment**

* `ruby -v`: any
* `rdbg -v`:

**Describe the bug**
A clear and concise description of what the bug is.

When navigating up and down the stack, debug does not show any context around the source code.

**To Reproduce**

Type `up` or `down`

```
irb:rdbg([{"error":0,"meta":{"_dd.p.dm...):008> up
=> 109|             encoder.encode(serializable_trace).tap do |encoded|
=>#2	Datadog::Tracing::Transport::Traces::Encoder.encode_trace(encoder=Datadog::Core::Encoding::JSONEncoder, trace=#<Datadog::Tracing::TraceSegment:0x00007e..., logger=nil, native_events_supported=true) at ~/apps/dd-trace-rb/lib/datadog/tracing/transport/traces.rb:109
```

**Expected behavior**
A clear and concise description of what you expected to happen.

I expect to see context around the current line, as all other debuggers I have ever used show.

**Additional context**
Add any other context about the problem here.

There was a pull request fixing this bug - https://github.com/ruby/debug/pull/1094. It was closed without being merged while there was apparent agreement that the current behavior is not good.

There was also no reason provided as to why debug does not currently show any context when navigating up and down stack frames.

Inspecting the code, the lack of context is hardcoded:

```
lib/debug/thread_client.rb:              show_src max_lines: 1
lib/debug/thread_client.rb:              show_src max_lines: 1
lib/debug/thread_client.rb:            show_src max_lines: 1
```




Fix (patch):
diff --git a/README.md b/README.md
index 2cd3ddf51..00ae01b9d 100644
--- a/README.md
+++ b/README.md
@@ -471,6 +471,7 @@ config set no_color true
 * UI
   * `RUBY_DEBUG_LOG_LEVEL` (`log_level`): Log level same as Logger (default: WARN)
   * `RUBY_DEBUG_SHOW_SRC_LINES` (`show_src_lines`): Show n lines source code on breakpoint (default: 10)
+  * `RUBY_DEBUG_SHOW_SRC_LINES_FRAME` (`show_src_lines_frame`): Show n lines source code on frame operations (default: 1)
   * `RUBY_DEBUG_SHOW_EVALEDSRC` (`show_evaledsrc`): Show actually evaluated source (default: false)
   * `RUBY_DEBUG_SHOW_FRAMES` (`show_frames`): Show n frames on breakpoint (default: 2)
   * `RUBY_DEBUG_USE_SHORT_PATH` (`use_short_path`): Show shorten PATH (like $(Gem)/foo.rb) (default: false)
diff --git a/lib/debug/config.rb b/lib/debug/config.rb
index 0fcbea7ec..b083a5ca3 100644
--- a/lib/debug/config.rb
+++ b/lib/debug/config.rb
@@ -12,50 +12,51 @@ module DEBUGGER__
 
   CONFIG_SET = {
     # UI setting
-    log_level:      ['RUBY_DEBUG_LOG_LEVEL',      "UI: Log level same as Logger",               :loglevel, "WARN"],
-    show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
-    show_evaledsrc: ['RUBY_DEBUG_SHOW_EVALEDSRC', "UI: Show actually evaluated source",         :bool, "false"],
-    show_frames:    ['RUBY_DEBUG_SHOW_FRAMES',    "UI: Show n frames on breakpoint",            :int, "2"],
-    use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
-    no_color:       ['RUBY_DEBUG_NO_COLOR',       "UI: Do not use colorize",                    :bool, "false"],
-    no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT",               :bool, "false"],
-    no_reline:      ['RUBY_DEBUG_NO_RELINE',      "UI: Do not use Reline library",              :bool, "false"],
-    no_hint:        ['RUBY_DEBUG_NO_HINT',        "UI: Do not show the hint on the REPL",       :bool, "false"],
-    no_lineno:      ['RUBY_DEBUG_NO_LINENO',      "UI: Do not show line numbers",               :bool, "false"],
-    irb_console:    ["RUBY_DEBUG_IRB_CONSOLE",    "UI: Use IRB as the console",                 :bool, "false"],
+    log_level:           ['RUBY_DEBUG_LOG_LEVEL',      "UI: Log level same as Logger",               :loglevel, "WARN"],
+    show_src_lines:      ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
+    show_src_lines_frame:['RUBY_DEBUG_SHOW_SRC_LINES_FRAME', "UI: Show n lines source code on frame operations", :int, "1"],
+    show_evaledsrc:      ['RUBY_DEBUG_SHOW_EVALEDSRC', "UI: Show actually evaluated source",         :bool, "false"],
+    show_frames:         ['RUBY_DEBUG_SHOW_FRAMES',    "UI: Show n frames on breakpoint",            :int, "2"],
+    use_short_path:      ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
+    no_color:            ['RUBY_DEBUG_NO_COLOR',       "UI: Do not use colorize",                    :bool, "false"],
+    no_sigint_hook:      ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT",               :bool, "false"],
+    no_reline:           ['RUBY_DEBUG_NO_RELINE',      "UI: Do not use Reline library",              :bool, "false"],
+    no_hint:             ['RUBY_DEBUG_NO_HINT',        "UI: Do not show the hint on the REPL",       :bool, "false"],
+    no_lineno:           ['RUBY_DEBUG_NO_LINENO',      "UI: Do not show line numbers",               :bool, "false"],
+    irb_console:         ["RUBY_DEBUG_IRB_CONSOLE",    "UI: Use IRB as the console",                 :bool, "false"],
 
     # control setting
-    skip_path:      ['RUBY_DEBUG_SKIP_PATH',      "CONTROL: Skip showing/entering frames for given paths", :path],
-    skip_nosrc:     ['RUBY_DEBUG_SKIP_NOSRC',     "CONTROL: Skip on no source code lines",              :bool, "false"],
-    keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it",   :bool, "false"],
-    postmortem:     ['RUBY_DEBUG_POSTMORTEM',     "CONTROL: Enable postmortem debug",                   :bool, "false"],
-    fork_mode:      ['RUBY_DEBUG_FORK_MODE',      "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
-    sigdump_sig:    ['RUBY_DEBUG_SIGDUMP_SIG',    "CONTROL: Sigdump signal", :bool, "false"],
+    skip_path:           ['RUBY_DEBUG_SKIP_PATH',      "CONTROL: Skip showing/entering frames for given paths", :path],
+    skip_nosrc:          ['RUBY_DEBUG_SKIP_NOSRC',     "CONTROL: Skip on no source code lines",              :bool, "false"],
+    keep_alloc_site:     ['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it",   :bool, "false"],
+    postmortem:          ['RUBY_DEBUG_POSTMORTEM',     "CONTROL: Enable postmortem debug",                   :bool, "false"],
+    fork_mode:           ['RUBY_DEBUG_FORK_MODE',      "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
+    sigdump_sig:         ['RUBY_DEBUG_SIGDUMP_SIG',    "CONTROL: Sigdump signal", :bool, "false"],
 
     # boot setting
-    nonstop:        ['RUBY_DEBUG_NONSTOP',     "BOOT: Nonstop mode",                                                :bool, "false"],
-    stop_at_load:   ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location",                               :bool, "false"],
-    init_script:    ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
-    commands:       ['RUBY_DEBUG_COMMANDS',    "BOOT: debug commands invoked at first stop. Commands should be separated by `;;`"],
-    no_rc:          ['RUBY_DEBUG_NO_RC',       "BOOT: ignore loading ~/.rdbgrc(.rb)",                               :bool, "false"],
-    history_file:   ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file",               :string, "~/.rdbg_history"],
-    save_history:   ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
+    nonstop:             ['RUBY_DEBUG_NONSTOP',     "BOOT: Nonstop mode",                                                :bool, "false"],
+    stop_at_load:        ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location",                               :bool, "false"],
+    init_script:         ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
+    commands:            ['RUBY_DEBUG_COMMANDS',    "BOOT: debug commands invoked at first stop. Commands should be separated by `;;`"],
+    no_rc:               ['RUBY_DEBUG_NO_RC',       "BOOT: ignore loading ~/.rdbgrc(.rb)",                               :bool, "false"],
+    history_file:        ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file",               :string, "~/.rdbg_history"],
+    save_history:        ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
 
     # remote setting
-    open:           ['RUBY_DEBUG_OPEN',         "REMOTE: Open remote port (same as `rdbg --open` option)"],
-    port:           ['RUBY_DEBUG_PORT',         "REMOTE: TCP/IP remote debugging: port"],
-    port_range:     ['RUBY_DEBUG_PORT_RANGE',   "REMOTE: TCP/IP remote debugging: length of port range"],
-    host:           ['RUBY_DEBUG_HOST',         "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
-    sock_path:      ['RUBY_DEBUG_SOCK_PATH',    "REMOTE: UNIX Domain Socket remote debugging: socket path"],
-    sock_dir:       ['RUBY_DEBUG_SOCK_DIR',     "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
-    local_fs_map:   ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
-    skip_bp:        ['RUBY_DEBUG_SKIP_BP',      "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
-    cookie:         ['RUBY_DEBUG_COOKIE',       "REMOTE: Cookie for negotiation"],
-    session_name:   ['RUBY_DEBUG_SESSION_NAME', "REMOTE: Session name for differentiating multiple sessions"],
-    chrome_path:    ['RUBY_DEBUG_CHROME_PATH',  "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
+    open:                ['RUBY_DEBUG_OPEN',         "REMOTE: Open remote port (same as `rdbg --open` option)"],
+    port:                ['RUBY_DEBUG_PORT',         "REMOTE: TCP/IP remote debugging: port"],
+    port_range:          ['RUBY_DEBUG_PORT_RANGE',   "REMOTE: TCP/IP remote debugging: length of port range"],
+    host:                ['RUBY_DEBUG_HOST',         "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
+    sock_path:           ['RUBY_DEBUG_SOCK_PATH',    "REMOTE: UNIX Domain Socket remote debugging: socket path"],
+    sock_dir:            ['RUBY_DEBUG_SOCK_DIR',     "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
+    local_fs_map:        ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
+    skip_bp:             ['RUBY_DEBUG_SKIP_BP',      "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
+    cookie:              ['RUBY_DEBUG_COOKIE',       "REMOTE: Cookie for negotiation"],
+    session_name:        ['RUBY_DEBUG_SESSION_NAME', "REMOTE: Session name for differentiating multiple sessions"],
+    chrome_path:         ['RUBY_DEBUG_CHROME_PATH',  "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
 
     # obsolete
-    parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork",     :bool, "false"],
+    parent_on_fork:      ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork",     :bool, "false"],
   }.freeze
 
   CONFIG_MAP = CONFIG_SET.map{|k, (ev, _)| [k, ev]}.to_h.freeze
diff --git a/lib/debug/thread_client.rb b/lib/debug/thread_client.rb
index 1d35d754a..259d69fcc 100644
--- a/lib/debug/thread_client.rb
+++ b/lib/debug/thread_client.rb
@@ -1079,13 +1079,13 @@ def wait_next_action_
           when :up
             if @current_frame_index + 1 < @target_frames.size
               @current_frame_index += 1
-              show_src max_lines: 1
+              show_src max_lines: CONFIG[:show_src_lines_frame]
               show_frame(@current_frame_index)
             end
           when :down
             if @current_frame_index > 0
               @current_frame_index -= 1
-              show_src max_lines: 1
+              show_src max_lines: CONFIG[:show_src_lines_frame]
               show_frame(@current_frame_index)
             end
           when :set
@@ -1097,7 +1097,7 @@ def wait_next_action_
                 puts "out of frame index: #{index}"
               end
             end
-            show_src max_lines: 1
+            show_src max_lines: CONFIG[:show_src_lines_frame]
             show_frame(@current_frame_index)
           else
             raise "unsupported frame operation: #{arg.inspect}"