Skip to content

Commit 8a43af1

Browse files
authored
Merge pull request #3858 from eregon/various-fixes
Add Prism::Source#line_to_byte_offset, optimize Prism::Node#tunnel and fix docs of BlockNode
2 parents d3d3de9 + ff81a29 commit 8a43af1

File tree

5 files changed

+64
-18
lines changed

5 files changed

+64
-18
lines changed

config.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,17 +1269,17 @@ nodes:
12691269
- name: opening_loc
12701270
type: location
12711271
comment: |
1272-
Represents the location of the opening `|`.
1272+
Represents the location of the opening `{` or `do`.
12731273
12741274
[1, 2, 3].each { |i| puts x }
1275-
^
1275+
^
12761276
- name: closing_loc
12771277
type: location
12781278
comment: |
1279-
Represents the location of the closing `|`.
1279+
Represents the location of the closing `}` or `end`.
12801280
12811281
[1, 2, 3].each { |i| puts x }
1282-
^
1282+
^
12831283
comment: |
12841284
Represents a block of ruby code.
12851285

lib/prism/parse_result.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ def slice(byte_offset, length)
7676
source.byteslice(byte_offset, length) or raise
7777
end
7878

79+
# Converts the line number to a byte offset corresponding to the start of that line
80+
def line_to_byte_offset(line)
81+
l = line - @start_line
82+
if l < 0 || l >= offsets.size
83+
raise ArgumentError, "line #{line} is out of range"
84+
end
85+
offsets[l]
86+
end
87+
7988
# Binary search through the offsets to find the line number for the given
8089
# byte offset.
8190
def line(byte_offset)

sig/prism/parse_result.rbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module Prism
1414
def encoding: () -> Encoding
1515
def lines: () -> Array[String]
1616
def slice: (Integer byte_offset, Integer length) -> String
17+
def line_to_byte_offset: (Integer line) -> Integer
1718
def line: (Integer byte_offset) -> Integer
1819
def line_start: (Integer byte_offset) -> Integer
1920
def line_end: (Integer byte_offset) -> Integer

templates/lib/prism/node.rb.erb

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,24 +184,13 @@ module Prism
184184
queue = [self] #: Array[Prism::node]
185185
result = [] #: Array[Prism::node]
186186

187+
search_offset = source.line_to_byte_offset(line) + column
188+
187189
while (node = queue.shift)
188190
result << node
189191

190192
node.each_child_node do |child_node|
191-
child_location = child_node.location
192-
193-
start_line = child_location.start_line
194-
end_line = child_location.end_line
195-
196-
if start_line == end_line
197-
if line == start_line && column >= child_location.start_column && column < child_location.end_column
198-
queue << child_node
199-
break
200-
end
201-
elsif (line == start_line && column >= child_location.start_column) || (line == end_line && column < child_location.end_column)
202-
queue << child_node
203-
break
204-
elsif line > start_line && line < end_line
193+
if child_node.start_offset <= search_offset && search_offset < child_node.end_offset
205194
queue << child_node
206195
break
207196
end

test/prism/ruby/source_test.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../test_helper"
4+
5+
module Prism
6+
class SourceTest < TestCase
7+
def test_line_to_byte_offset
8+
parse_result = Prism.parse(<<~SRC)
9+
abcd
10+
efgh
11+
ijkl
12+
SRC
13+
source = parse_result.source
14+
15+
assert_equal 0, source.line_to_byte_offset(1)
16+
assert_equal 5, source.line_to_byte_offset(2)
17+
assert_equal 10, source.line_to_byte_offset(3)
18+
assert_equal 15, source.line_to_byte_offset(4)
19+
e = assert_raise(ArgumentError) { source.line_to_byte_offset(5) }
20+
assert_equal "line 5 is out of range", e.message
21+
e = assert_raise(ArgumentError) { source.line_to_byte_offset(0) }
22+
assert_equal "line 0 is out of range", e.message
23+
e = assert_raise(ArgumentError) { source.line_to_byte_offset(-1) }
24+
assert_equal "line -1 is out of range", e.message
25+
end
26+
27+
def test_line_to_byte_offset_with_start_line
28+
parse_result = Prism.parse(<<~SRC, line: 11)
29+
abcd
30+
efgh
31+
ijkl
32+
SRC
33+
source = parse_result.source
34+
35+
assert_equal 0, source.line_to_byte_offset(11)
36+
assert_equal 5, source.line_to_byte_offset(12)
37+
assert_equal 10, source.line_to_byte_offset(13)
38+
assert_equal 15, source.line_to_byte_offset(14)
39+
e = assert_raise(ArgumentError) { source.line_to_byte_offset(15) }
40+
assert_equal "line 15 is out of range", e.message
41+
e = assert_raise(ArgumentError) { source.line_to_byte_offset(10) }
42+
assert_equal "line 10 is out of range", e.message
43+
e = assert_raise(ArgumentError) { source.line_to_byte_offset(9) }
44+
assert_equal "line 9 is out of range", e.message
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)