Skip to content

Commit

Permalink
Fix off-by one error in the LineIndex::offset calculation (#13407)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser authored Sep 19, 2024
1 parent a8d9104 commit afdb659
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 5 deletions.
5 changes: 2 additions & 3 deletions crates/ruff/tests/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1945,11 +1945,10 @@ fn range_end_only() {
def foo(arg1, arg2,):
print("Should format this" )
"#), @r###"
"#), @r#"
success: true
exit_code: 0
----- stdout -----
def foo(
arg1,
arg2,
Expand All @@ -1958,7 +1957,7 @@ def foo(arg1, arg2,):
----- stderr -----
"###);
"#);
}

#[test]
Expand Down
55 changes: 53 additions & 2 deletions crates/ruff_source_file/src/line_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,57 @@ impl LineIndex {
}

/// Returns the [byte offset](TextSize) at `line` and `column`.
///
/// ## Examples
///
/// ### ASCII
///
/// ```
/// use ruff_source_file::{LineIndex, OneIndexed};
/// use ruff_text_size::TextSize;
/// let source = r#"a = 4
/// c = "some string"
/// x = b"#;
///
/// let index = LineIndex::from_source_text(source);
///
/// // First line, first column
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(0), OneIndexed::from_zero_indexed(0), source), TextSize::new(0));
///
/// // Second line, 4th column
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(1), OneIndexed::from_zero_indexed(4), source), TextSize::new(10));
///
/// // Offset past the end of the first line
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(0), OneIndexed::from_zero_indexed(10), source), TextSize::new(6));
///
/// // Offset past the end of the file
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(3), OneIndexed::from_zero_indexed(0), source), TextSize::new(29));
/// ```
///
/// ### UTF8
///
/// ```
/// use ruff_source_file::{LineIndex, OneIndexed};
/// use ruff_text_size::TextSize;
/// let source = r#"a = 4
/// c = "❤️"
/// x = b"#;
///
/// let index = LineIndex::from_source_text(source);
///
/// // First line, first column
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(0), OneIndexed::from_zero_indexed(0), source), TextSize::new(0));
///
/// // Third line, 2nd column, after emoji
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(2), OneIndexed::from_zero_indexed(1), source), TextSize::new(20));
///
/// // Offset past the end of the second line
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(1), OneIndexed::from_zero_indexed(10), source), TextSize::new(19));
///
/// // Offset past the end of the file
/// assert_eq!(index.offset(OneIndexed::from_zero_indexed(3), OneIndexed::from_zero_indexed(0), source), TextSize::new(24));
/// ```
///
pub fn offset(&self, line: OneIndexed, column: OneIndexed, contents: &str) -> TextSize {
// If start-of-line position after last line
if line.to_zero_indexed() > self.line_starts().len() {
Expand All @@ -233,15 +284,15 @@ impl LineIndex {
match self.kind() {
IndexKind::Ascii => {
line_range.start()
+ TextSize::try_from(column.get())
+ TextSize::try_from(column.to_zero_indexed())
.unwrap_or(line_range.len())
.clamp(TextSize::new(0), line_range.len())
}
IndexKind::Utf8 => {
let rest = &contents[line_range];
let column_offset: TextSize = rest
.chars()
.take(column.get())
.take(column.to_zero_indexed())
.map(ruff_text_size::TextLen::text_len)
.sum();
line_range.start() + column_offset
Expand Down

0 comments on commit afdb659

Please sign in to comment.