Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1269,17 +1269,17 @@ nodes:
- name: opening_loc
type: location
comment: |
Represents the location of the opening `|`.
Represents the location of the opening `{` or `do`.
[1, 2, 3].each { |i| puts x }
^
^
- name: closing_loc
type: location
comment: |
Represents the location of the closing `|`.
Represents the location of the closing `}` or `end`.
[1, 2, 3].each { |i| puts x }
^
^
comment: |
Represents a block of ruby code.
Expand Down
9 changes: 9 additions & 0 deletions lib/prism/parse_result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ def slice(byte_offset, length)
source.byteslice(byte_offset, length) or raise
end

# Converts the line number to a byte offset corresponding to the start of that line
def line_to_byte_offset(line)
l = line - @start_line
if l < 0 || l >= offsets.size
raise ArgumentError, "line #{line} is out of range"
end
offsets[l]
end

# Binary search through the offsets to find the line number for the given
# byte offset.
def line(byte_offset)
Expand Down
1 change: 1 addition & 0 deletions sig/prism/parse_result.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Prism
def encoding: () -> Encoding
def lines: () -> Array[String]
def slice: (Integer byte_offset, Integer length) -> String
def line_to_byte_offset: (Integer line) -> Integer
def line: (Integer byte_offset) -> Integer
def line_start: (Integer byte_offset) -> Integer
def line_end: (Integer byte_offset) -> Integer
Expand Down
17 changes: 3 additions & 14 deletions templates/lib/prism/node.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -184,24 +184,13 @@ module Prism
queue = [self] #: Array[Prism::node]
result = [] #: Array[Prism::node]

search_offset = source.line_to_byte_offset(line) + column

while (node = queue.shift)
result << node

node.each_child_node do |child_node|
child_location = child_node.location

start_line = child_location.start_line
end_line = child_location.end_line

if start_line == end_line
if line == start_line && column >= child_location.start_column && column < child_location.end_column
queue << child_node
break
end
elsif (line == start_line && column >= child_location.start_column) || (line == end_line && column < child_location.end_column)
queue << child_node
break
elsif line > start_line && line < end_line
if child_node.start_offset <= search_offset && search_offset < child_node.end_offset
queue << child_node
break
end
Expand Down
47 changes: 47 additions & 0 deletions test/prism/ruby/source_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

require_relative "../test_helper"

module Prism
class SourceTest < TestCase
def test_line_to_byte_offset
parse_result = Prism.parse(<<~SRC)
abcd
efgh
ijkl
SRC
source = parse_result.source

assert_equal 0, source.line_to_byte_offset(1)
assert_equal 5, source.line_to_byte_offset(2)
assert_equal 10, source.line_to_byte_offset(3)
assert_equal 15, source.line_to_byte_offset(4)
e = assert_raise(ArgumentError) { source.line_to_byte_offset(5) }
assert_equal "line 5 is out of range", e.message
e = assert_raise(ArgumentError) { source.line_to_byte_offset(0) }
assert_equal "line 0 is out of range", e.message
e = assert_raise(ArgumentError) { source.line_to_byte_offset(-1) }
assert_equal "line -1 is out of range", e.message
end

def test_line_to_byte_offset_with_start_line
parse_result = Prism.parse(<<~SRC, line: 11)
abcd
efgh
ijkl
SRC
source = parse_result.source

assert_equal 0, source.line_to_byte_offset(11)
assert_equal 5, source.line_to_byte_offset(12)
assert_equal 10, source.line_to_byte_offset(13)
assert_equal 15, source.line_to_byte_offset(14)
e = assert_raise(ArgumentError) { source.line_to_byte_offset(15) }
assert_equal "line 15 is out of range", e.message
e = assert_raise(ArgumentError) { source.line_to_byte_offset(10) }
assert_equal "line 10 is out of range", e.message
e = assert_raise(ArgumentError) { source.line_to_byte_offset(9) }
assert_equal "line 9 is out of range", e.message
end
end
end