Fix off-by one error in the LineIndex::offset calculation (#13407)

This commit is contained in:
Micha Reiser 2024-09-19 13:58:45 +02:00 committed by GitHub
parent a8d9104fa3
commit afdb659111
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 5 deletions

View file

@ -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() {
@ -233,7 +284,7 @@ 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())
}
@ -241,7 +292,7 @@ impl LineIndex {
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