@@ -1,5 +1,5 @@
11use clap::ArgMatches as Args;
2−use helix_core::{state::coords_at_pos, state::Mode, syntax::HighlightEvent, Range, State};
2+use helix_core::{state::Mode, syntax::HighlightEvent, Range, State};
33use helix_view::{commands, keymap, View};
44
55use std::{
@@ -24,6 +24,8 @@ use crossterm::{
2424
2525use tui::{backend::CrosstermBackend, buffer::Buffer as Surface, layout::Rect, style::Style};
2626
27+const TAB_WIDTH: usize = 4;
28+
2729type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
2830
2931static EX: smol::Executor = smol::Executor::new();
@@ -85,10 +87,7 @@ impl Editor {
8587 // TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|)
8688 let source_code = view.state.doc().to_string();
8789
88− let last_line = std::cmp::min(
89− (view.first_line + viewport.height - 1) as usize,
90− view.state.doc().len_lines() - 1,
91− );
90+ let last_line = view.last_line(viewport);
9291
9392 let range = {
9493 // calculate viewport byte ranges
@@ -172,6 +171,8 @@ impl Editor {
172171 if line >= viewport.height {
173172 break 'outer;
174173 }
174+ } else if grapheme == "\t" {
175+ visual_x += (TAB_WIDTH as u16);
175176 } else {
176177 // Cow will prevent allocations if span contained in a single slice
177178 // which should really be the majority case
@@ -281,12 +282,16 @@ impl Editor {
281282
282283 // render the cursor
283284 let pos = view.state.selection().cursor();
284− let coords = coords_at_pos(&view.state.doc().slice(..), pos);
285+
286+ let pos = view
287+ .screen_coords_at_pos(&view.state.doc().slice(..), pos, area)
288+ .expect("Cursor is out of bounds.");
289+
285290 execute!(
286291 stdout,
287292 cursor::MoveTo(
288− coords.col as u16 + viewport.x,
289− coords.row as u16 - view.first_line + viewport.y,
293+ pos.col as u16 + viewport.x,
294+ pos.row as u16 - view.first_line + viewport.y,
290295 )
291296 );
292297 }