diff options
Diffstat (limited to 'helix-view')
-rw-r--r-- | helix-view/Cargo.toml | 2 | ||||
-rw-r--r-- | helix-view/src/clipboard.rs | 2 | ||||
-rw-r--r-- | helix-view/src/document.rs | 97 | ||||
-rw-r--r-- | helix-view/src/editor.rs | 2 | ||||
-rw-r--r-- | helix-view/src/graphics.rs | 87 | ||||
-rw-r--r-- | helix-view/src/view.rs | 7 |
6 files changed, 95 insertions, 102 deletions
diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index b3991584..1f55a36b 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -19,7 +19,7 @@ anyhow = "1" helix-core = { version = "0.4", path = "../helix-core" } helix-lsp = { version = "0.4", path = "../helix-lsp"} helix-dap = { version = "0.4", path = "../helix-dap"} -crossterm = { version = "0.20", optional = true } +crossterm = { version = "0.21", optional = true } # Conversion traits once_cell = "1.8" diff --git a/helix-view/src/clipboard.rs b/helix-view/src/clipboard.rs index 9dd86da7..a11224ac 100644 --- a/helix-view/src/clipboard.rs +++ b/helix-view/src/clipboard.rs @@ -84,7 +84,7 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> { // FIXME: check performance of is_exit_success command_provider! { paste => "xsel", "-o", "-b"; - copy => "xsel", "--nodetach", "-i", "-b"; + copy => "xsel", "-i", "-b"; primary_paste => "xsel", "-o"; primary_copy => "xsel", "-i"; } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index ff0c8bf4..a238644a 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -4,7 +4,7 @@ use std::cell::Cell; use std::collections::HashMap; use std::fmt::Display; use std::future::Future; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; @@ -317,87 +317,6 @@ where } } -/// Expands tilde `~` into users home directory if avilable, otherwise returns the path -/// unchanged. The tilde will only be expanded when present as the first component of the path -/// and only slash follows it. -pub fn expand_tilde(path: &Path) -> PathBuf { - let mut components = path.components().peekable(); - if let Some(Component::Normal(c)) = components.peek() { - if c == &"~" { - if let Ok(home) = helix_core::home_dir() { - // it's ok to unwrap, the path starts with `~` - return home.join(path.strip_prefix("~").unwrap()); - } - } - } - - path.to_path_buf() -} - -/// Replaces users home directory from `path` with tilde `~` if the directory -/// is available, otherwise returns the path unchanged. -pub fn fold_home_dir(path: &Path) -> PathBuf { - if let Ok(home) = helix_core::home_dir() { - if path.starts_with(&home) { - // it's ok to unwrap, the path starts with home dir - return PathBuf::from("~").join(path.strip_prefix(&home).unwrap()); - } - } - - path.to_path_buf() -} - -/// Normalize a path, removing things like `.` and `..`. -/// -/// CAUTION: This does not resolve symlinks (unlike -/// [`std::fs::canonicalize`]). This may cause incorrect or surprising -/// behavior at times. This should be used carefully. Unfortunately, -/// [`std::fs::canonicalize`] can be hard to use correctly, since it can often -/// fail, or on Windows returns annoying device paths. This is a problem Cargo -/// needs to improve on. -/// Copied from cargo: <https://github.com/rust-lang/cargo/blob/070e459c2d8b79c5b2ac5218064e7603329c92ae/crates/cargo-util/src/paths.rs#L81> -pub fn normalize_path(path: &Path) -> PathBuf { - let path = expand_tilde(path); - let mut components = path.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { - components.next(); - PathBuf::from(c.as_os_str()) - } else { - PathBuf::new() - }; - - for component in components { - match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); - } - Component::CurDir => {} - Component::ParentDir => { - ret.pop(); - } - Component::Normal(c) => { - ret.push(c); - } - } - } - ret -} - -/// Returns the canonical, absolute form of a path with all intermediate components normalized. -/// -/// This function is used instead of `std::fs::canonicalize` because we don't want to verify -/// here if the path exists, just normalize it's components. -pub fn canonicalize_path(path: &Path) -> std::io::Result<PathBuf> { - let path = if path.is_relative() { - std::env::current_dir().map(|current_dir| current_dir.join(path))? - } else { - path.to_path_buf() - }; - - Ok(normalize_path(&path)) -} - use helix_lsp::lsp; use url::Url; @@ -631,7 +550,7 @@ impl Document { } pub fn set_path(&mut self, path: &Path) -> Result<(), std::io::Error> { - let path = canonicalize_path(path)?; + let path = helix_core::path::get_canonicalized_path(path)?; // if parent doesn't exist we still want to open the document // and error out when document is saved @@ -936,15 +855,9 @@ impl Document { } pub fn relative_path(&self) -> Option<PathBuf> { - let cwdir = std::env::current_dir().expect("couldn't determine current directory"); - - self.path.as_ref().map(|path| { - let mut path = path.as_path(); - if path.is_absolute() { - path = path.strip_prefix(cwdir).unwrap_or(path) - }; - fold_home_dir(path) - }) + self.path + .as_deref() + .map(helix_core::path::get_relative_path) } // pub fn slice<R>(&self, range: R) -> RopeSlice where R: RangeBounds { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 17319327..295dfc0e 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -238,7 +238,7 @@ impl Editor { } pub fn open(&mut self, path: PathBuf, action: Action) -> Result<DocumentId, Error> { - let path = crate::document::canonicalize_path(&path)?; + let path = helix_core::path::get_canonicalized_path(&path)?; let id = self .documents() diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index 9a7a86c3..66013ee5 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -24,7 +24,7 @@ pub struct Margin { }
/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
-/// area they are supposed to render to.
+/// area they are supposed to render to. (x, y) = (0, 0) is at the top left corner of the screen.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
@@ -92,6 +92,57 @@ impl Rect { self.y.saturating_add(self.height)
}
+ // Returns a new Rect with width reduced from the left side.
+ // This changes the `x` coordinate and clamps it to the right
+ // edge of the original Rect.
+ pub fn clip_left(self, width: u16) -> Rect {
+ let width = std::cmp::min(width, self.width);
+ Rect {
+ x: self.x.saturating_add(width),
+ width: self.width.saturating_sub(width),
+ ..self
+ }
+ }
+
+ // Returns a new Rect with width reduced from the right side.
+ // This does _not_ change the `x` coordinate.
+ pub fn clip_right(self, width: u16) -> Rect {
+ Rect {
+ width: self.width.saturating_sub(width),
+ ..self
+ }
+ }
+
+ // Returns a new Rect with height reduced from the top.
+ // This changes the `y` coordinate and clamps it to the bottom
+ // edge of the original Rect.
+ pub fn clip_top(self, height: u16) -> Rect {
+ let height = std::cmp::min(height, self.height);
+ Rect {
+ y: self.y.saturating_add(height),
+ height: self.height.saturating_sub(height),
+ ..self
+ }
+ }
+
+ // Returns a new Rect with height reduced from the bottom.
+ // This does _not_ change the `y` coordinate.
+ pub fn clip_bottom(self, height: u16) -> Rect {
+ Rect {
+ height: self.height.saturating_sub(height),
+ ..self
+ }
+ }
+
+ pub fn with_height(self, height: u16) -> Rect {
+ // new height may make area > u16::max_value, so use new()
+ Self::new(self.x, self.y, self.width, height)
+ }
+
+ pub fn with_width(self, width: u16) -> Rect {
+ Self::new(self.x, self.y, width, self.height)
+ }
+
pub fn inner(self, margin: &Margin) -> Rect {
if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
Rect::default()
@@ -495,6 +546,40 @@ mod tests { assert_eq!(rect.height, 100);
}
+ #[test]
+ fn test_rect_chop_from_left() {
+ let rect = Rect::new(0, 0, 20, 30);
+ assert_eq!(Rect::new(10, 0, 10, 30), rect.clip_left(10));
+ assert_eq!(
+ Rect::new(20, 0, 0, 30),
+ rect.clip_left(40),
+ "x should be clamped to original width if new width is bigger"
+ );
+ }
+
+ #[test]
+ fn test_rect_chop_from_right() {
+ let rect = Rect::new(0, 0, 20, 30);
+ assert_eq!(Rect::new(0, 0, 10, 30), rect.clip_right(10));
+ }
+
+ #[test]
+ fn test_rect_chop_from_top() {
+ let rect = Rect::new(0, 0, 20, 30);
+ assert_eq!(Rect::new(0, 10, 20, 20), rect.clip_top(10));
+ assert_eq!(
+ Rect::new(0, 30, 20, 0),
+ rect.clip_top(50),
+ "y should be clamped to original height if new height is bigger"
+ );
+ }
+
+ #[test]
+ fn test_rect_chop_from_bottom() {
+ let rect = Rect::new(0, 0, 20, 30);
+ assert_eq!(Rect::new(0, 0, 20, 20), rect.clip_bottom(10));
+ }
+
fn styles() -> Vec<Style> {
vec![
Style::default(),
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index b707c092..01f18c71 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -83,12 +83,7 @@ impl View { pub fn inner_area(&self) -> Rect { // TODO: not ideal const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter - Rect::new( - self.area.x + OFFSET, - self.area.y, - self.area.width - OFFSET, - self.area.height.saturating_sub(1), // -1 for statusline - ) + self.area.clip_left(OFFSET).clip_bottom(1) // -1 for statusline } pub fn ensure_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) { |