From 3859f6963dfad2d2d09c979a8e6bb283bc5e2cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Sat, 19 Sep 2020 11:55:15 +0900 Subject: [PATCH] More work on the UI. --- Cargo.lock | 125 ++++++++++++++++++++++++---------- helix-core/src/state.rs | 5 ++ helix-core/src/syntax.rs | 2 +- helix-core/src/transaction.rs | 4 +- helix-term/src/editor.rs | 70 +++++++++++++------ helix-term/src/theme.rs | 4 ++ 6 files changed, 149 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9351ac63..f9b52d7db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f049075ec7a47ee59ed2e3013026e26e66b7430b1f2276c1e6ad9d5cfbff8f" +checksum = "a831e74aa1937d3bbd3a356f34c23dbc6b6f0abc5160bd5484a9f75d5e76aea8" dependencies = [ "async-task", "concurrent-queue", @@ -86,9 +86,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3361d088d288026af2cb21b9d9b8444bf0ba73bce56a4fc4b5742ba88f82ee74" +checksum = "e3572236ba37147ca2b674a0bd5afd20aec0cd925ab125ab6fad6543960f9002" dependencies = [ "blocking", "futures-lite", @@ -96,31 +96,27 @@ dependencies = [ [[package]] name = "async-io" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016a7f0eda7091ef24ad8562d6503ad8da47af8c432d4d3fa440eea9e89055fe" +checksum = "64c629684e697f58c0e99e5e2d84a840e3b336afbcfdbac7b44c3b1e222c2fd8" dependencies = [ - "cfg-if", "concurrent-queue", "fastrand", "futures-lite", - "libc", "log", + "nb-connect", "once_cell", "parking", "polling", - "socket2", "vec-arena", "waker-fn", - "wepoll-sys-stjepang", - "winapi", ] [[package]] name = "async-lock" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b123db9bf64e4c4840d5b1985fdb4f69fbe50a4fc95e895d11ff49931ce9802" +checksum = "ab3ad7fb4345397e57c19566844b0eba274b92e5d2d2791bb0664cc441697b95" dependencies = [ "async-barrier", "async-mutex", @@ -139,9 +135,9 @@ dependencies = [ [[package]] name = "async-net" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9c1d358c27ba114eab4330c50d21879ad0e6af192f01dba6fec1ab3b1e03d90" +checksum = "a48af5438be856056bdeb6c5d895148a715be5915fccee49d1e5b50851dc9b8b" dependencies = [ "async-io", "blocking", @@ -229,9 +225,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" +checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" dependencies = [ "jobserver", ] @@ -262,13 +258,13 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.17.8" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "858085e389f71d31a6909f2b55a56b87d1cb8b168c0f513dcbed5e66a3e1039c" +checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" dependencies = [ "bitflags", "crossterm_winapi", - "futures-core", + "futures-util", "lazy_static", "libc", "mio", @@ -342,9 +338,9 @@ checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-lite" -version = "1.3.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc6854fcb40c6446abf6043e82604e42567dcf3d652a5ff4e997fc36876414c" +checksum = "5b77e08e656f472d8ea84c472fa8b0a7a917883048e1cf2d4e34a323cd0aaf63" dependencies = [ "fastrand", "futures-core", @@ -355,6 +351,24 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project", + "pin-utils", +] + [[package]] name = "heck" version = "0.3.1" @@ -370,6 +384,7 @@ version = "0.1.0" dependencies = [ "anyhow", "helix-syntax", + "once_cell", "ropey", "smallvec", "tendril", @@ -427,9 +442,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "lock_api" @@ -485,6 +500,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "nb-connect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847c76b390f44529c2071ef06d0b52fbb4bdb04cc8987a5cfa63954c000abca" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -546,6 +571,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "pin-project" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.1.7" @@ -553,10 +598,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" [[package]] -name = "polling" -version = "1.0.1" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0835fa5f9af34c170eb38638ae6bc88e1b11ecdd0b968c9d9de8e343450385eb" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polling" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0307b8c7f438902536321f63c28cab0362f6ee89f1c7da47e3642ff956641c8b" dependencies = [ "cfg-if", "libc", @@ -567,9 +618,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" dependencies = [ "unicode-xid", ] @@ -651,9 +702,9 @@ checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" [[package]] name = "smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee895b9f862d88d6bad9d82f6ce727710d177338e697057a691ea684800d4a6" +checksum = "712d02afa6ac9e7b8c777fd181aff476d009280b54b8c28703d10fa5d7e80d83" dependencies = [ "async-channel", "async-executor", @@ -669,9 +720,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.3.12" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" dependencies = [ "cfg-if", "libc", @@ -681,9 +732,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" dependencies = [ "proc-macro2", "quote", @@ -770,9 +821,9 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "wepoll-sys-stjepang" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd319e971980166b53e17b1026812ad66c6b54063be879eb182342b55284694" +checksum = "1fdfbb03f290ca0b27922e8d48a0997b4ceea12df33269b9f75e713311eb178d" dependencies = [ "cc", ] diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs index 150874b15..55ac50953 100644 --- a/helix-core/src/state.rs +++ b/helix-core/src/state.rs @@ -102,6 +102,11 @@ pub fn mode(&self) -> Mode { self.mode } + #[inline] + pub fn path(&self) -> Option<&PathBuf> { + self.path.as_ref() + } + // pub fn doc(&self, range: R) -> RopeSlice // where // R: std::ops::RangeBounds, diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index c0b67f5d1..1eb156334 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -173,7 +173,7 @@ fn tree(&self) -> &Tree { self.tree.as_ref().unwrap() } - fn parse<'a>( + fn parse( &mut self, parser: &mut Parser, config: &HighlightConfiguration, diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index ab32d5daa..f6fbddf8e 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -243,13 +243,13 @@ pub fn map_pos(&self, pos: usize, assoc: Assoc) -> usize { let old_end = old_pos + len; match change { - Retain(_len) => { + Retain(_) => { if old_end > pos { return new_pos + (pos - old_pos); } new_pos += len; } - Delete(_len) => { + Delete(_) => { // a subsequent ins means a replace, consume it let ins = if let Some(Insert(s)) = iter.peek() { iter.next(); diff --git a/helix-term/src/editor.rs b/helix-term/src/editor.rs index 7352b1421..ed2d3a13c 100644 --- a/helix-term/src/editor.rs +++ b/helix-term/src/editor.rs @@ -80,6 +80,8 @@ pub fn open(&mut self, path: PathBuf) -> Result<(), Error> { } fn render(&mut self) { + use tui::backend::Backend; + use tui::style::Color; // TODO: ideally not mut but highlights require it because of cursor cache match &mut self.state { Some(state) => { @@ -87,9 +89,21 @@ fn render(&mut self) { let mut stdout = stdout(); self.surface.reset(); // reset is faster than allocating new empty surface + // clear with background color + self.surface + .set_style(area, self.theme.get("ui.background")); + + let offset = 5 + 1; // 5 linenr + 1 gutter + let viewport = Rect::new(offset, 0, self.size.0, self.size.1 - 1); // - 1 for statusline + // TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|) let source_code = state.doc().to_string(); + let last_line = std::cmp::min( + (self.first_line + viewport.height - 1) as usize, + state.doc().len_lines() - 1, + ); + // TODO: cache highlight results // TODO: only recalculate when state.doc is actually modified let highlights: Vec<_> = state @@ -102,12 +116,10 @@ fn render(&mut self) { let mut spans = Vec::new(); - let offset = 2; - let mut visual_x = 0; - let mut line = 0; + let mut line = 0u16; - for event in highlights { + 'outer: for event in highlights { match event.unwrap() { HighlightEvent::HighlightStart(span) => { spans.push(span); @@ -125,8 +137,6 @@ fn render(&mut self) { use helix_core::graphemes::{grapheme_width, RopeGraphemes}; - use tui::style::Color; - let style = match spans.first() { Some(span) => self.theme.get(self.theme.scopes()[span.0].as_str()), None => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender @@ -143,6 +153,11 @@ fn render(&mut self) { if grapheme == "\n" { visual_x = 0; line += 1; + + // TODO: with proper iter this shouldn't be necessary + if line >= viewport.height { + break 'outer; + } } else { // Cow will prevent allocations if span contained in a single slice // which should really be the majority case @@ -163,7 +178,13 @@ fn render(&mut self) { } } - // + let mut line = 0; + let style = self.theme.get("ui.linenr"); + for i in self.first_line..(last_line as u16) { + self.surface + .set_stringn(0, line, format!("{:>5}", i + 1), 5, style); // lavender + line += 1; + } // let lines = state // .doc @@ -190,19 +211,23 @@ fn render(&mut self) { // // TODO: don't highlight next char in append mode // } - // let mode = match state.mode { - // Mode::Insert => "INS", - // Mode::Normal => "NOR", - // }; - - // execute!( - // stdout, - // SetForegroundColor(Color::Reset), - // cursor::MoveTo(0, self.size.1), - // Print(mode) - // ); - - use tui::backend::Backend; + // statusline + let mode = match state.mode() { + Mode::Insert => "INS", + Mode::Normal => "NOR", + }; + self.surface.set_style( + Rect::new(0, self.size.1 - 1, self.size.0, 1), + self.theme.get("ui.statusline"), + ); + // TODO: unfocused one with different color + let text_color = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac + self.surface + .set_string(1, self.size.1 - 1, mode, text_color); + if let Some(path) = state.path() { + self.surface + .set_string(6, self.size.1 - 1, path.to_string_lossy(), text_color); + } self.terminal .backend_mut() @@ -221,7 +246,10 @@ fn render(&mut self) { let coords = coords_at_pos(&state.doc().slice(..), pos); execute!( stdout, - cursor::MoveTo((coords.col + 2) as u16, coords.row as u16) + cursor::MoveTo( + coords.col as u16 + viewport.x, + coords.row as u16 - self.first_line + viewport.y, + ) ); } None => (), diff --git a/helix-term/src/theme.rs b/helix-term/src/theme.rs index 5b6eb7de2..4b2f102eb 100644 --- a/helix-term/src/theme.rs +++ b/helix-term/src/theme.rs @@ -72,6 +72,10 @@ fn default() -> Self { "module" => Style::default().fg(Color::Rgb(255, 0, 0)), // white "variable" => Style::default().fg(Color::Rgb(255, 0, 0)), // white "function.builtin" => Style::default().fg(Color::Rgb(255, 0, 0)), // white + + "ui.background" => Style::default().bg(Color::Rgb(59, 34, 76)), // midnight + "ui.linenr" => Style::default().fg(Color::Rgb(90, 89, 119)), // comet + "ui.statusline" => Style::default().bg(Color::Rgb(40, 23, 51)), // revolver }; let scopes = mapping.keys().map(ToString::to_string).collect();