aboutsummaryrefslogtreecommitdiff
path: root/helix-view
diff options
context:
space:
mode:
Diffstat (limited to 'helix-view')
-rw-r--r--helix-view/Cargo.toml2
-rw-r--r--helix-view/src/clipboard.rs2
-rw-r--r--helix-view/src/document.rs97
-rw-r--r--helix-view/src/editor.rs2
-rw-r--r--helix-view/src/graphics.rs87
-rw-r--r--helix-view/src/view.rs7
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) {