|
1 | 1 | pub(crate) trait RopeExt {
|
2 | 2 | /// Get the index of (line, column) (0-based) from the byte offset (0-based).
|
3 | 3 | /// If the offset is out of bounds, return the last line and column.
|
| 4 | + /// |
| 5 | + /// If the `offset` is out of bounds, it returns (0, 0). |
4 | 6 | fn line_column(&self, byte_offset: usize) -> (usize, usize);
|
5 | 7 | /// Get the byte offset (0-based) from the line, column (0-based).
|
| 8 | + /// |
| 9 | + /// Return the last line, if line is out of bounds. |
| 10 | + /// Return the end column of line, if the column is out of bounds. |
6 | 11 | fn line_column_to_byte(&self, line_ix: usize, column_ix: usize) -> usize;
|
7 | 12 | }
|
8 | 13 |
|
9 | 14 | impl RopeExt for ropey::Rope {
|
10 | 15 | fn line_column(&self, offset: usize) -> (usize, usize) {
|
11 |
| - let line_ix = self.byte_to_line(offset); |
12 |
| - let line_offset = offset.saturating_sub(self.line_to_byte(line_ix)); |
| 16 | + let Ok(line_ix) = self.try_byte_to_line(offset) else { |
| 17 | + return (0, 0); |
| 18 | + }; |
| 19 | + |
13 | 20 | let line = self.line(line_ix);
|
14 |
| - let column_ix = line.byte_to_char(line_offset); |
| 21 | + let line_start_byte = self.line_to_byte(line_ix); |
| 22 | + let line_offset = offset.saturating_sub(line_start_byte); |
| 23 | + |
| 24 | + let column_ix = line |
| 25 | + .try_byte_to_char(line_offset) |
| 26 | + .unwrap_or(line.len_chars()); |
15 | 27 |
|
16 | 28 | (line_ix, column_ix)
|
17 | 29 | }
|
18 | 30 |
|
19 | 31 | fn line_column_to_byte(&self, line_ix: usize, column_ix: usize) -> usize {
|
| 32 | + let line_ix = self.len_lines().saturating_sub(1).min(line_ix); |
20 | 33 | let line = self.line(line_ix);
|
21 |
| - self.line_to_byte(line_ix) + line.char_to_byte(column_ix) |
| 34 | + |
| 35 | + self.line_to_byte(line_ix) |
| 36 | + + line |
| 37 | + .try_char_to_byte(column_ix) |
| 38 | + .unwrap_or(line.len_bytes().saturating_sub(1)) |
22 | 39 | }
|
23 | 40 | }
|
0 commit comments