From 4393e1c159ea5ea58145a7ad2283cc886d640ac9 Mon Sep 17 00:00:00 2001 From: Jan Hrastnik Date: Thu, 1 Oct 2020 21:16:24 +0200 Subject: [PATCH] added tab to insert mode --- helix-syntax/languages/tree-sitter-c-sharp | 2 +- helix-syntax/languages/tree-sitter-javascript | 2 +- helix-syntax/languages/tree-sitter-toml | 2 +- helix-syntax/languages/tree-sitter-typescript | 2 +- helix-term/src/editor.rs | 33 ++++++++------- helix-view/src/commands.rs | 4 ++ helix-view/src/keymap.rs | 4 ++ helix-view/src/view.rs | 42 ++++++++++++++++++- 8 files changed, 72 insertions(+), 19 deletions(-) diff --git a/helix-syntax/languages/tree-sitter-c-sharp b/helix-syntax/languages/tree-sitter-c-sharp index 47cde3884c0b5..1349bbfd7e846 160000 --- a/helix-syntax/languages/tree-sitter-c-sharp +++ b/helix-syntax/languages/tree-sitter-c-sharp @@ -1 +1 @@ -Subproject commit 47cde3884c0b52307ca032317bed3dc06185a047 +Subproject commit 1349bbfd7e84655abdf94d320c115ba754fda53e diff --git a/helix-syntax/languages/tree-sitter-javascript b/helix-syntax/languages/tree-sitter-javascript index 3d5493495b62b..c4f5c8f657b03 160000 --- a/helix-syntax/languages/tree-sitter-javascript +++ b/helix-syntax/languages/tree-sitter-javascript @@ -1 +1 @@ -Subproject commit 3d5493495b62b4ff8e8c24aee7519dd904e25813 +Subproject commit c4f5c8f657b03be92eada845e9ebbf869fb4b4d2 diff --git a/helix-syntax/languages/tree-sitter-toml b/helix-syntax/languages/tree-sitter-toml index 42c9ff20c0371..e1aa4dd51bfa8 160000 --- a/helix-syntax/languages/tree-sitter-toml +++ b/helix-syntax/languages/tree-sitter-toml @@ -1 +1 @@ -Subproject commit 42c9ff20c0371bed7f514036e823f10793caacec +Subproject commit e1aa4dd51bfa83fbde26565e0b80f5ed17b0bdc8 diff --git a/helix-syntax/languages/tree-sitter-typescript b/helix-syntax/languages/tree-sitter-typescript index 07a12bdf024d6..767165ad527cb 160000 --- a/helix-syntax/languages/tree-sitter-typescript +++ b/helix-syntax/languages/tree-sitter-typescript @@ -1 +1 @@ -Subproject commit 07a12bdf024d66d267bd7f96870f8bbbaceaa5d9 +Subproject commit 767165ad527cbe98d513c7eb16161d1251bfd4e3 diff --git a/helix-term/src/editor.rs b/helix-term/src/editor.rs index a3bc8b3b0ecbb..abe56c55bc9b2 100644 --- a/helix-term/src/editor.rs +++ b/helix-term/src/editor.rs @@ -1,5 +1,5 @@ use clap::ArgMatches as Args; -use helix_core::{state::coords_at_pos, state::Mode, syntax::HighlightEvent, Range, State}; +use helix_core::{state::Mode, syntax::HighlightEvent, Range, State}; use helix_view::{commands, keymap, View}; use std::{ @@ -24,6 +24,8 @@ use crossterm::{ use tui::{backend::CrosstermBackend, buffer::Buffer as Surface, layout::Rect, style::Style}; +const TAB_WIDTH: usize = 4; + type Terminal = tui::Terminal>; static EX: smol::Executor = smol::Executor::new(); @@ -85,11 +87,7 @@ impl Editor { // TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|) let source_code = view.state.doc().to_string(); - let last_line = std::cmp::min( - (view.first_line + viewport.height - 1) as usize, - view.state.doc().len_lines() - 1, - ); - + let last_line = view.get_last_line(viewport); let range = { // calculate viewport byte ranges let start = view.state.doc().line_to_byte(view.first_line.into()); @@ -172,6 +170,8 @@ impl Editor { if line >= viewport.height { break 'outer; } + } else if grapheme == "\t" { + visual_x += (TAB_WIDTH as u16); } else { // Cow will prevent allocations if span contained in a single slice // which should really be the majority case @@ -281,14 +281,19 @@ impl Editor { // render the cursor let pos = view.state.selection().cursor(); - let coords = coords_at_pos(&view.state.doc().slice(..), pos); - execute!( - stdout, - cursor::MoveTo( - coords.col as u16 + viewport.x, - coords.row as u16 - view.first_line + viewport.y, - ) - ); + + if let (pos, _width) = view + .screen_coords_at_pos(&view.state.doc().slice(..), pos, area) + .expect("Cursor is out of bounds.") + { + execute!( + stdout, + cursor::MoveTo( + pos.col as u16 + viewport.x, + pos.row as u16 - view.first_line + viewport.y, + ) + ); + } } None => (), } diff --git a/helix-view/src/commands.rs b/helix-view/src/commands.rs index 1fb9c90e61c4f..6dd1101cf0821 100644 --- a/helix-view/src/commands.rs +++ b/helix-view/src/commands.rs @@ -301,6 +301,10 @@ pub fn insert_char(view: &mut View, c: char) { // TODO: need to store into history if successful } +pub fn insert_tab(view: &mut View, _count: usize) { + insert_char(view, '\t'); +} + pub fn insert_newline(view: &mut View, _count: usize) { insert_char(view, '\n'); } diff --git a/helix-view/src/keymap.rs b/helix-view/src/keymap.rs index d4ab85a2c9d60..ef23ff2a44269 100644 --- a/helix-view/src/keymap.rs +++ b/helix-view/src/keymap.rs @@ -157,6 +157,10 @@ pub fn default() -> Keymaps { code: KeyCode::Enter, modifiers: Modifiers::NONE }] => commands::insert_newline, + vec![Key { + code: KeyCode::Tab, + modifiers: Modifiers::NONE + }] => commands::insert_tab, ) ) } diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index d96752d04f4d3..f655b0e3c9306 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -3,7 +3,8 @@ use anyhow::Error; use std::path::PathBuf; use crate::theme::Theme; -use helix_core::State; +use helix_core::{graphemes::grapheme_width, Position, RopeSlice, State}; +use tui::layout::Rect; pub struct View { pub state: State, @@ -44,4 +45,43 @@ impl View { self.first_line = line.saturating_sub(padding); } } + + /// Calculates the last visible line on screen + pub fn get_last_line(&self, viewport: Rect) -> usize { + std::cmp::min( + (self.first_line + viewport.height - 1) as usize, + self.state.doc().len_lines() - 1, + ) + } + + /// Translates a document position to an absolute position in the terminal. + pub fn screen_coords_at_pos( + &self, + text: &RopeSlice, + pos: usize, + viewport: Rect, + ) -> Option<(Position, usize)> { + let line = text.char_to_line(pos); + + if line < self.first_line as usize || line > self.get_last_line(viewport) { + // Line is not visible on screen + return None; + } + + let line_start = text.line_to_char(line); + let line_slice = text.slice(line_start..pos); + let mut col = 0; + + for character in line_slice.chars() { + if character == '\t' { + col += 4; + } else { + col += grapheme_width(&character.to_string()[..]); + } + } + + let row = line - self.first_line as usize; + + Some((Position::new(row, col), 0)) + } }