Skip to content
Closed
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
3 changes: 3 additions & 0 deletions rust/ruby-prism-sys/build/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings {
// Structs
.allowlist_type("pm_comment_t")
.allowlist_type("pm_diagnostic_t")
.allowlist_type("pm_line_column_t")
.allowlist_type("pm_list_t")
.allowlist_type("pm_magic_comment_t")
.allowlist_type("pm_node_t")
Expand All @@ -140,6 +141,8 @@ fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings {
// Functions
.allowlist_function("pm_list_empty_p")
.allowlist_function("pm_list_free")
.allowlist_function("pm_newline_list_line")
.allowlist_function("pm_newline_list_line_column")
.allowlist_function("pm_node_destroy")
.allowlist_function("pm_pack_parse")
.allowlist_function("pm_parse")
Expand Down
41 changes: 41 additions & 0 deletions rust/ruby-prism/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,47 @@ mod tests {
assert_eq!(slice, "222");
}

#[test]
fn location_line_column_test() {
let source = "foo\nbar\nbaz";
let result = parse(source.as_ref());

let node = result.node();
let program = node.as_program_node().unwrap();
let statements = program.statements().body();
let mut iter = statements.iter();
let _foo = iter.next().unwrap();
let bar = iter.next().unwrap();
let baz = iter.next().unwrap();

let bar_loc = bar.location();
assert_eq!(bar_loc.start_line(), 2);
assert_eq!(bar_loc.end_line(), 2);
assert_eq!(bar_loc.start_column(), 0);
assert_eq!(bar_loc.end_column(), 3);

let baz_loc = baz.location();
assert_eq!(baz_loc.start_line(), 3);
assert_eq!(baz_loc.end_line(), 3);
assert_eq!(baz_loc.start_column(), 0);
assert_eq!(baz_loc.end_column(), 3);
}

#[test]
fn test_chop() {
let result = parse(b"foo");
let mut location = result.node().as_program_node().unwrap().location();

assert_eq!(location.chop().as_slice(), b"fo");
assert_eq!(location.chop().chop().chop().as_slice(), b"");

// Check that we don't go negative.
for _ in 0..10 {
location = location.chop();
}
assert_eq!(location.as_slice(), b"");
}

#[test]
fn visitor_test() {
use super::{visit_interpolated_regular_expression_node, visit_regular_expression_node, InterpolatedRegularExpressionNode, RegularExpressionNode, Visit};
Expand Down
54 changes: 53 additions & 1 deletion rust/ruby-prism/src/parse_result/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod diagnostics;

use std::ptr::NonNull;

use ruby_prism_sys::{pm_comment_t, pm_diagnostic_t, pm_location_t, pm_magic_comment_t, pm_node_destroy, pm_node_t, pm_parser_free, pm_parser_t};
use ruby_prism_sys::{pm_comment_t, pm_diagnostic_t, pm_location_t, pm_magic_comment_t, pm_newline_list_line_column, pm_node_destroy, pm_node_t, pm_parser_free, pm_parser_t};

pub use self::comments::{Comment, CommentType, Comments, MagicComment, MagicComments};
pub use self::diagnostics::{Diagnostic, Diagnostics};
Expand Down Expand Up @@ -66,6 +66,58 @@ impl<'pr> Location<'pr> {
})
}
}

/// Returns the line number where this location starts.
#[must_use]
pub fn start_line(&self) -> i32 {
self.line_column_at(self.start).line
}

/// Returns the line number where this location ends.
#[must_use]
pub fn end_line(&self) -> i32 {
self.line_column_at(self.end()).line
}

/// Returns the column number in bytes where this location starts from the
/// start of the line.
#[must_use]
pub fn start_column(&self) -> u32 {
self.line_column_at(self.start).column
}

/// Returns the column number in bytes where this location ends from the
/// start of the line.
#[must_use]
pub fn end_column(&self) -> u32 {
self.line_column_at(self.end()).column
}

/// Returns a new location that is the result of chopping off the last byte.
#[must_use]
pub const fn chop(&self) -> Self {
Location {
parser: self.parser,
start: self.start,
length: if self.length == 0 { 0 } else { self.length - 1 },
marker: std::marker::PhantomData,
}
}

fn line_column_at(&self, offset: u32) -> LineColumn {
unsafe {
let parser = self.parser.as_ptr();
let newline_list = &(*parser).newline_list;
let start_line = (*parser).start_line;
let result = pm_newline_list_line_column(newline_list, offset, start_line);
LineColumn { line: result.line, column: result.column }
}
}
}

struct LineColumn {
line: i32,
column: u32,
}

impl std::fmt::Debug for Location<'_> {
Expand Down
Loading