on master help sign in
Merge pull request #2 from helix-editor/tab-implementation

added tab to insert mode

commit197651eb309e88ba120dcb7a769acac6091483b1 authorBlaž Hrastnik <blaz@mxxn.io> dateSun 4 Oct 2020, 15:40 (6 years ago) parent7b4a4f6dc11124
8 files changed+75−14
helix-syntax/languages/tree-sitter-c-sharp+1−1 helix-syntax/languages/tree-sitter-javascript+1−1 helix-syntax/languages/tree-sitter-toml+1−1 helix-syntax/languages/tree-sitter-typescript+1−1 helix-term/src/editor.rs+13−8 helix-view/src/commands.rs+4 helix-view/src/keymap.rs+4 helix-view/src/view.rs+50−2
helix-syntax/languages/tree-sitter-c-sharp+1−1

Submodule changed.

helix-syntax/languages/tree-sitter-javascript+1−1

Submodule changed.

helix-syntax/languages/tree-sitter-toml+1−1

Submodule changed.

helix-syntax/languages/tree-sitter-typescript+1−1

Submodule changed.

helix-term/src/editor.rs+13−8
@@ -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 }
helix-view/src/commands.rs+4
@@ -301,6 +301,10 @@ pub fn insert_char(view: &mut View, c: char) {
301301 // TODO: need to store into history if successful
302302}
303303
304+pub fn insert_tab(view: &mut View, _count: usize) {
305+ insert_char(view, '\t');
306+}
307+
304308pub fn insert_newline(view: &mut View, _count: usize) {
305309 insert_char(view, '\n');
306310}
helix-view/src/keymap.rs+4
@@ -157,6 +157,10 @@ pub fn default() -> Keymaps {
157157 code: KeyCode::Enter,
158158 modifiers: Modifiers::NONE
159159 }] => commands::insert_newline,
160+ vec![Key {
161+ code: KeyCode::Tab,
162+ modifiers: Modifiers::NONE
163+ }] => commands::insert_tab,
160164 )
161165 )
162166}
helix-view/src/view.rs+50−2
@@ -1,9 +1,13 @@
11use anyhow::Error;
22
3−use std::path::PathBuf;
3+use std::{borrow::Cow, path::PathBuf};
44
55use crate::theme::Theme;
6−use helix_core::State;
6+use helix_core::{
7+ graphemes::{grapheme_width, RopeGraphemes},
8+ Position, RopeSlice, State,
9+};
10+use tui::layout::Rect;
711
812pub struct View {
913 pub state: State,
@@ -44,4 +48,48 @@ impl View {
4448 self.first_line = line.saturating_sub(padding);
4549 }
4650 }
51+
52+ /// Calculates the last visible line on screen
53+ #[inline]
54+ pub fn last_line(&self, viewport: Rect) -> usize {
55+ std::cmp::min(
56+ (self.first_line + viewport.height - 1) as usize,
57+ self.state.doc().len_lines() - 1,
58+ )
59+ }
60+
61+ /// Translates a document position to an absolute position in the terminal.
62+ /// Returns a (line, col) position if the position is visible on screen.
63+ // TODO: Could return width as well for the character width at cursor.
64+ pub fn screen_coords_at_pos(
65+ &self,
66+ text: &RopeSlice,
67+ pos: usize,
68+ viewport: Rect,
69+ ) -> Option<Position> {
70+ let line = text.char_to_line(pos);
71+
72+ if line < self.first_line as usize || line > self.last_line(viewport) {
73+ // Line is not visible on screen
74+ return None;
75+ }
76+
77+ let line_start = text.line_to_char(line);
78+ let line_slice = text.slice(line_start..pos);
79+ let mut col = 0;
80+
81+ for grapheme in RopeGraphemes::new(&line_slice) {
82+ if grapheme == "\t" {
83+ // TODO: this should be const TAB_WIDTH
84+ col += 4;
85+ } else {
86+ let grapheme = Cow::from(grapheme);
87+ col += grapheme_width(&grapheme);
88+ }
89+ }
90+
91+ let row = line - self.first_line as usize;
92+
93+ Some(Position::new(row, col))
94+ }
4795}