Class: Tryouts::CLI::LineSpecParser

Inherits:
Object
  • Object
show all
Defined in:
lib/tryouts/cli/line_spec_parser.rb

Class Method Summary collapse

Class Method Details

.matches?(test_case, line_spec) ⇒ Boolean

Check if a test case at the given line range matches the line specification Test case line ranges are 0-based, user line specs are 1-based

Returns:

  • (Boolean)


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
# File 'lib/tryouts/cli/line_spec_parser.rb', line 76

def self.matches?(test_case, line_spec)
  return true if line_spec.nil?

  # Convert user's 1-based line spec to 0-based for comparison
  zero_based_spec = case line_spec
  when Integer
    line_spec - 1
  when Range
    (line_spec.begin - 1)..(line_spec.end - 1)
  else
    line_spec
  end

  # Test case line_range is already 0-based
  test_range = test_case.line_range

  case zero_based_spec
  when Integer
    # Single line - check if it falls within the test's range
    test_range.cover?(zero_based_spec)
  when Range
    # Range - check if there's any overlap
    spec_start = zero_based_spec.begin
    spec_end = zero_based_spec.end
    test_start = test_range.begin
    test_end = test_range.end

    # Check for overlap: ranges overlap if start of one is before end of other
    !(test_end < spec_start || spec_end < test_start)
  else
    true
  end
end

.parse(path_with_spec) ⇒ Object

Parse a file path with optional line specification Supports formats:

- file.rb:19        (single line)
- file.rb:19-45     (range)
- file.rb:L19       (GitHub-style single line)
- file.rb:L19-45    (GitHub-style range)
- file.rb:L19-L45   (GitHub-style range with L on both)

Returns [filepath, line_spec] where line_spec is nil or a Range/Integer



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/tryouts/cli/line_spec_parser.rb', line 17

def self.parse(path_with_spec)
  return [path_with_spec, nil] unless path_with_spec.include?(':')

  # Split on the last colon to handle paths with colons
  parts = path_with_spec.rpartition(':')
  filepath = parts[0]
  line_spec_str = parts[2]

  # If the filepath is empty, it means there was no colon or the input started with colon
  return [path_with_spec, nil] if filepath.empty?

  # If the "line spec" part looks like a Windows drive letter, this isn't a line spec
  return [path_with_spec, nil] if line_spec_str =~ /\A[a-zA-Z]\z/

  # Parse the line specification
  line_spec = parse_line_spec(line_spec_str)

  # If we couldn't parse it, treat the whole thing as a filepath
  return [path_with_spec, nil] if line_spec.nil?

  [filepath, line_spec]
end

.parse_line_spec(spec) ⇒ Object



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
# File 'lib/tryouts/cli/line_spec_parser.rb', line 42

def self.parse_line_spec(spec)
  return nil if spec.nil? || spec.empty?

  # Remove 'L' prefix if present (GitHub style)
  spec = spec.gsub(/L/i, '')

  # Handle range (e.g., "19-80")
  if spec.include?('-')
    parts = spec.split('-', 2)

    # Validate that both parts are numeric
    return nil unless parts[0] =~ /\A\d+\z/ && parts[1] =~ /\A\d+\z/

    start_line = parts[0].to_i
    end_line = parts[1].to_i

    # Validate the numbers
    return nil if start_line <= 0 || end_line <= 0
    return nil if start_line > end_line

    start_line..end_line
  else
    # Single line number - validate it's numeric
    return nil unless spec =~ /\A\d+\z/

    line = spec.to_i
    return nil if line <= 0

    line
  end
end