@@ -4,6 +4,7 @@ use crate::{graphics::Rect, Document, DocumentId, ViewId};
4use helix_core::{
4use helix_core::{
5 coords_at_pos,
5 coords_at_pos,
6 graphemes::{grapheme_width, RopeGraphemes},
6 graphemes::{grapheme_width, RopeGraphemes},
7+ line_ending::line_end_char_index,
7 Position, RopeSlice, Selection,
8 Position, RopeSlice, Selection,
@@ -165,6 +166,74 @@ impl View {
165 Some(Position::new(row, col))
166 Some(Position::new(row, col))
169+ /// Verifies whether a screen position is inside the view
170+ /// Returns true when position is inside the view
171+ pub fn verify_screen_coords(&self, row: usize, column: usize) -> bool {
173+ if row < self.area.y as usize || row > self.area.y as usize + self.area.height as usize - 2
179+ const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
181+ if column < self.area.x as usize + OFFSET
182+ || column > self.area.x as usize + self.area.width as usize
189+ pub fn text_pos_at_screen_coords(
195+ ) -> Option<usize> {
196+ if !self.verify_screen_coords(row, column) {
200+ let line_number = row - self.area.y as usize + self.first_line;
202+ if line_number > text.len_lines() - 1 {
203+ return Some(text.len_chars());
206+ let mut pos = text.line_to_char(line_number);
208+ let current_line = text.line(line_number);
211+ const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
213+ let target = column - OFFSET - self.area.x as usize + self.first_col;
214+ let mut selected = 0;
216+ for grapheme in RopeGraphemes::new(current_line) {
217+ if selected >= target {
220+ if grapheme == "\t" {
221+ selected += tab_width;
223+ let width = grapheme_width(&Cow::from(grapheme));
226+ pos += grapheme.chars().count();
229+ Some(pos.min(line_end_char_index(&text.slice(..), line_number)))
232+ /// Translates a screen position to position in the text document.
233+ /// Returns a usize typed position in bounds of the text if found in this view, None if out of view.
234+ pub fn pos_at_screen_coords(&self, doc: &Document, row: usize, column: usize) -> Option<usize> {
235+ self.text_pos_at_screen_coords(&doc.text().slice(..), row, column, doc.tab_width())
168 // pub fn traverse<F>(&self, text: RopeSlice, start: usize, end: usize, fun: F)
237 // pub fn traverse<F>(&self, text: RopeSlice, start: usize, end: usize, fun: F)
170 // F: Fn(usize, usize),
239 // F: Fn(usize, usize),
@@ -186,3 +255,81 @@ impl View {
262+ use helix_core::Rope;
265+ fn test_text_pos_at_screen_coords() {
266+ let mut view = View::new(DocumentId::default());
267+ view.area = Rect::new(40, 40, 40, 40);
268+ let text = Rope::from_str("abc\n\tdef");
271+ view.text_pos_at_screen_coords(&text.slice(..), 40, 2, 4),
276+ view.text_pos_at_screen_coords(&text.slice(..), 40, 41, 4),
281+ view.text_pos_at_screen_coords(&text.slice(..), 0, 2, 4),
286+ view.text_pos_at_screen_coords(&text.slice(..), 0, 49, 4),
291+ view.text_pos_at_screen_coords(&text.slice(..), 0, 41, 4),
296+ view.text_pos_at_screen_coords(&text.slice(..), 40, 81, 4),
301+ view.text_pos_at_screen_coords(&text.slice(..), 78, 41, 4),
306+ view.text_pos_at_screen_coords(&text.slice(..), 40, 40 + 7 + 3, 4),
311+ view.text_pos_at_screen_coords(&text.slice(..), 40, 80, 4),
316+ view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 1, 4),
321+ view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 4, 4),
326+ view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 7, 4),
331+ view.text_pos_at_screen_coords(&text.slice(..), 41, 80, 4),