File: list.rb

package info (click to toggle)
ruby-byebug 12.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,304 kB
  • sloc: ruby: 9,013; ansic: 1,678; sh: 5; makefile: 4
file content (159 lines) | stat: -rw-r--r-- 3,777 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# frozen_string_literal: true

require_relative "../command"
require_relative "../source_file_formatter"
require_relative "../helpers/file"
require_relative "../helpers/parse"

module Byebug
  #
  # List parts of the source code.
  #
  class ListCommand < Command
    include Helpers::FileHelper
    include Helpers::ParseHelper

    self.allow_in_post_mortem = true

    def self.regexp
      /^\s* l(?:ist)? (?:\s*([-=])|\s+(\S+))? \s*$/x
    end

    def self.description
      <<-DESCRIPTION
        l[ist][[-=]][ nn-mm]

        #{short_description}

        Lists lines forward from current line or from the place where code was
        last listed. If "list-" is specified, lists backwards instead. If
        "list=" is specified, lists from current line regardless of where code
        was last listed. A line range can also be specified to list specific
        sections of code.
      DESCRIPTION
    end

    def self.short_description
      "Lists lines of source code"
    end

    def execute
      msg = "No sourcefile available for #{frame.file}"
      raise(msg) unless File.exist?(frame.file)

      b, e = range(@match[2])

      display_lines(b, e)

      processor.prev_line = b
    end

    private

    #
    # Line range to be printed by `list`.
    #
    # If <input> is set, range is parsed from it.
    #
    # Otherwise it's automatically chosen.
    #
    def range(input)
      return auto_range(@match[1] || "+") unless input

      b, e = parse_range(input)
      raise("Invalid line range") unless valid_range?(b, e)

      [b, e]
    end

    def valid_range?(first, last)
      first <= last && (1..max_line).cover?(first) && (1..max_line).cover?(last)
    end

    #
    # Set line range to be printed by list
    #
    # @return first line number to list
    # @return last line number to list
    #
    def auto_range(direction)
      prev_line = processor.prev_line

      if direction == "=" || prev_line.nil?
        source_file_formatter.range_around(frame.line)
      else
        source_file_formatter.range_from(move(prev_line, size, direction))
      end
    end

    def parse_range(input)
      first, err = get_int(lower_bound(input), "List", 1, max_line)
      raise(err) unless first

      if upper_bound(input)
        last, err = get_int(upper_bound(input), "List", 1, max_line)
        raise(err) unless last

        last = amend_final(last)
      else
        first -= (size / 2)
      end

      [first, last || move(first, size - 1)]
    end

    def move(line, size, direction = "+")
      line.send(direction, size)
    end

    #
    # Show a range of lines in the current file.
    #
    # @param min [Integer] Lower bound
    # @param max [Integer] Upper bound
    #
    def display_lines(min, max)
      puts "\n[#{min}, #{max}] in #{frame.file}"

      puts source_file_formatter.lines(min, max).join
    end

    #
    # @param range [String] A string with an integer range format
    #
    # @return [String] The lower bound of the given range
    #
    def lower_bound(range)
      split_range(range)[0]
    end

    #
    # @param range [String] A string with an integer range format
    #
    # @return [String] The upper bound of the given range
    #
    def upper_bound(range)
      split_range(range)[1]
    end

    #
    # @param str [String] A string with an integer range format
    #
    # @return [Array] The upper & lower bounds of the given range
    #
    def split_range(str)
      str.split(/[-,]/)
    end

    extend Forwardable

    def_delegators :source_file_formatter, :amend_final, :size, :max_line

    def source_file_formatter
      @source_file_formatter ||= SourceFileFormatter.new(
        frame.file,
        ->(n) { n == frame.line ? "=>" : "  " }
      )
    end
  end
end