on master help sign in
Add primitives for converting between char offset indices and coords.
commit10d53f3ef0f519dc6b55f8a882a32ec7b1c4e4e1 authorBlaž Hrastnik <blaz@mxxn.io> dateFri 5 Jun 2020, 14:02 (6 years ago) parent387fb57
2 files changed+44−3
helix-core/src/lib.rs+1−1 helix-core/src/state.rs+43−2
helix-core/src/lib.rs+1−1
@@ -5,7 +5,7 @@ mod selection;
55mod state;
66mod transaction;
77
8−pub use ropey::Rope;
8+pub use ropey::{Rope, RopeSlice};
99pub use tendril::StrTendril as Tendril;
1010
1111pub use buffer::Buffer;
helix-core/src/state.rs+43−2
@@ -1,5 +1,5 @@
1−use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary};
2−use crate::{Buffer, Selection, SelectionRange};
1+use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
2+use crate::{Buffer, Rope, RopeSlice, Selection, SelectionRange};
33
44/// A state represents the current editor state of a single buffer.
55pub struct State {
@@ -117,3 +117,44 @@ impl State {
117117 // TODO: update selection in state via transaction
118118 }
119119}
120+
121+/// Coordinates are a 0-indexed line and column pair.
122+type Coords = (usize, usize); // line, col
123+
124+/// Convert a character index to (line, column) coordinates.
125+pub fn coords_at_pos(text: &RopeSlice, pos: usize) -> Coords {
126+ let line = text.char_to_line(pos);
127+ let line_start = text.line_to_char(line);
128+ let col = RopeGraphemes::new(&text.slice(line_start..pos)).count();
129+ (line, col)
130+}
131+
132+/// Convert (line, column) coordinates to a character index.
133+pub fn pos_at_coords(text: &RopeSlice, coords: Coords) -> usize {
134+ let (line, col) = coords;
135+ let line_start = text.line_to_char(line);
136+ nth_next_grapheme_boundary(text, line_start, col)
137+}
138+
139+#[cfg(test)]
140+mod test {
141+ use super::*;
142+
143+ #[test]
144+ fn test_coords_at_pos() {
145+ let text = Rope::from("ḧëḷḷö\nẅöṛḷḋ");
146+ assert_eq!(coords_at_pos(&text.slice(..), 0), (0, 0));
147+ assert_eq!(coords_at_pos(&text.slice(..), 5), (0, 5)); // position on \n
148+ assert_eq!(coords_at_pos(&text.slice(..), 6), (1, 0)); // position on w
149+ assert_eq!(coords_at_pos(&text.slice(..), 11), (1, 5)); // position on d
150+ }
151+
152+ #[test]
153+ fn test_pos_at_coords() {
154+ let text = Rope::from("ḧëḷḷö\nẅöṛḷḋ");
155+ assert_eq!(pos_at_coords(&text.slice(..), (0, 0)), 0);
156+ assert_eq!(pos_at_coords(&text.slice(..), (0, 5)), 5); // position on \n
157+ assert_eq!(pos_at_coords(&text.slice(..), (1, 0)), 6); // position on w
158+ assert_eq!(pos_at_coords(&text.slice(..), (1, 5)), 11); // position on d
159+ }
160+}