From f9375f449c8b7bbf792c88b89903fe38d524f2e5 Mon Sep 17 00:00:00 2001 From: Gokul Soumya Date: Sat, 21 Aug 2021 10:51:20 +0530 Subject: Refactor new Rect construction (#575) * Refactor new Rect construction Introduces methods that can be chained to construct new Rects out of pre-existing ones * Clamp x and y to edges in Rect chop methods * Rename Rect clipping functions--- helix-term/src/ui/picker.rs | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'helix-term/src/ui/picker.rs') diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 2cc25c24..ecf9d528 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -19,7 +19,7 @@ use helix_core::Position; use helix_view::{ document::canonicalize_path, editor::Action, - graphics::{Color, CursorKind, Rect, Style}, + graphics::{Color, CursorKind, Margin, Rect, Style}, Document, Editor, }; @@ -90,23 +90,26 @@ impl Component for FilePicker { area.width }; - let picker_area = Rect::new(area.x, area.y, picker_width, area.height); + let picker_area = area.with_width(picker_width); self.picker.render(picker_area, surface, cx); if !render_preview { return; } - let preview_area = Rect::new(area.x + picker_width, area.y, area.width / 2, area.height); + let preview_area = area.clip_left(picker_width); // don't like this but the lifetime sucks let block = Block::default().borders(Borders::ALL); // calculate the inner area inside the box - let mut inner = block.inner(preview_area); + let inner = block.inner(preview_area); // 1 column gap on either side - inner.x += 1; - inner.width = inner.width.saturating_sub(2); + let margin = Margin { + vertical: 1, + horizontal: 0, + }; + let inner = inner.inner(&margin); block.render(preview_area, surface); @@ -282,15 +285,11 @@ impl Picker { // - score all the names in relation to input fn inner_rect(area: Rect) -> Rect { - let padding_vertical = area.height * 10 / 100; - let padding_horizontal = area.width * 10 / 100; - - Rect::new( - area.x + padding_horizontal, - area.y + padding_vertical, - area.width - padding_horizontal * 2, - area.height - padding_vertical * 2, - ) + let margin = Margin { + vertical: area.height * 10 / 100, + horizontal: area.width * 10 / 100, + }; + area.inner(&margin) } impl Component for Picker { @@ -410,7 +409,7 @@ impl Component for Picker { // -- Render the input bar: - let area = Rect::new(inner.x + 1, inner.y, inner.width - 1, 1); + let area = inner.clip_left(1).with_height(1); let count = format!("{}/{}", self.matches.len(), self.options.len()); surface.set_stringn( @@ -434,8 +433,8 @@ impl Component for Picker { } // -- Render the contents: - // subtract the area of the prompt (-2) and current item marker " > " (-3) - let inner = Rect::new(inner.x + 3, inner.y + 2, inner.width - 3, inner.height - 2); + // subtract area of prompt from top and current item marker " > " from left + let inner = inner.clip_top(2).clip_left(3); let selected = cx.editor.theme.get("ui.text.focus"); @@ -474,7 +473,7 @@ impl Component for Picker { let inner = block.inner(area); // prompt area - let area = Rect::new(inner.x + 1, inner.y, inner.width - 1, 1); + let area = inner.clip_left(1).with_height(1); self.prompt.cursor(area, editor) } -- cgit v1.2.3-70-g09d2 From a5c3c6c6a9e21e850440130e00ebaff57e7d4f4d Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Tue, 24 Aug 2021 13:21:06 +0900 Subject: ui: Highlight line ranges in the preview --- helix-term/src/commands.rs | 12 +++++++++--- helix-term/src/ui/picker.rs | 29 +++++++++++++++++++---------- 2 files changed, 28 insertions(+), 13 deletions(-) (limited to 'helix-term/src/ui/picker.rs') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 7434d4cd..89855cbb 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2314,7 +2314,7 @@ fn buffer_picker(cx: &mut Context) { .selection(view_id) .primary() .cursor_line(doc.text().slice(..)); - Some((path.clone()?, Some(line))) + Some((path.clone()?, Some((line, line)))) }, ); cx.push_layer(Box::new(picker)); @@ -2387,7 +2387,10 @@ fn symbol_picker(cx: &mut Context) { }, move |_editor, symbol| { let path = symbol.location.uri.to_file_path().unwrap(); - let line = Some(symbol.location.range.start.line as usize); + let line = Some(( + symbol.location.range.start.line as usize, + symbol.location.range.end.line as usize, + )); Some((path, line)) }, ); @@ -2820,7 +2823,10 @@ fn goto_impl( }, |_editor, location| { let path = location.uri.to_file_path().unwrap(); - let line = Some(location.range.start.line as usize); + let line = Some(( + location.range.start.line as usize, + location.range.end.line as usize, + )); Some((path, line)) }, ); diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index ecf9d528..036b41c8 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -26,7 +26,7 @@ use helix_view::{ pub const MIN_SCREEN_WIDTH_FOR_PREVIEW: u16 = 80; /// File path and line number (used to align and highlight a line) -type FileLocation = (PathBuf, Option); +type FileLocation = (PathBuf, Option<(usize, usize)>); pub struct FilePicker { picker: Picker, @@ -113,14 +113,17 @@ impl Component for FilePicker { block.render(preview_area, surface); - if let Some((doc, line)) = self.current_file(cx.editor).and_then(|(path, line)| { + if let Some((doc, line)) = self.current_file(cx.editor).and_then(|(path, range)| { cx.editor .document_by_path(&path) .or_else(|| self.preview_cache.get(&path)) - .zip(Some(line)) + .zip(Some(range)) }) { // align to middle - let first_line = line.unwrap_or(0).saturating_sub(inner.height as usize / 2); + let first_line = line + .map(|(start, _)| start) + .unwrap_or(0) + .saturating_sub(inner.height as usize / 2); let offset = Position::new(first_line, 0); let highlights = EditorView::doc_syntax_highlights( @@ -140,12 +143,18 @@ impl Component for FilePicker { ); // highlight the line - if let Some(line) = line { - for x in inner.left()..inner.right() { - surface - .get_mut(x, inner.y + line.saturating_sub(first_line) as u16) - .set_style(cx.editor.theme.get("ui.selection")); - } + if let Some((start, end)) = line { + let offset = start.saturating_sub(first_line) as u16; + surface.set_style( + Rect::new( + inner.x, + inner.y + offset, + inner.width, + (end.saturating_sub(start) as u16 + 1) + .min(inner.height.saturating_sub(offset)), + ), + cx.editor.theme.get("ui.selection"), + ); } } } -- cgit v1.2.3-70-g09d2 From e6cb183134c34a55b2bb6edba468fbcb3b1118c4 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Tue, 24 Aug 2021 13:21:49 +0900 Subject: ui: Fix preview window padding: we want horizontal, not vertical --- helix-term/src/ui/picker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'helix-term/src/ui/picker.rs') diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 036b41c8..c6b9a3e6 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -106,8 +106,8 @@ impl Component for FilePicker { let inner = block.inner(preview_area); // 1 column gap on either side let margin = Margin { - vertical: 1, - horizontal: 0, + vertical: 0, + horizontal: 1, }; let inner = inner.inner(&margin); -- cgit v1.2.3-70-g09d2 From bf5b9a9f354135933d7970863cf81e5a36585d03 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Tue, 24 Aug 2021 13:24:36 +0900 Subject: ui: Tone down the preview highlight by adding a new scope --- helix-term/src/ui/picker.rs | 5 ++++- theme.toml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'helix-term/src/ui/picker.rs') diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index c6b9a3e6..a864ab6d 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -153,7 +153,10 @@ impl Component for FilePicker { (end.saturating_sub(start) as u16 + 1) .min(inner.height.saturating_sub(offset)), ), - cx.editor.theme.get("ui.selection"), + cx.editor + .theme + .try_get("ui.highlight") + .unwrap_or_else(|| cx.editor.theme.get("ui.selection")), ); } } diff --git a/theme.toml b/theme.toml index b66ac499..3166b2d6 100644 --- a/theme.toml +++ b/theme.toml @@ -51,6 +51,7 @@ module = "#ff0000" "ui.cursor.insert" = { bg = "white" } "ui.cursor.match" = { fg = "#212121", bg = "#6C6999" } "ui.cursor" = { modifiers = ["reversed"] } +"ui.highlight" = { bg = "bossanova" } "ui.menu.selected" = { fg = "revolver", bg = "white" } -- cgit v1.2.3-70-g09d2 From b99db7c6875f74a15dcfb0cfdb3334c837d4aab1 Mon Sep 17 00:00:00 2001 From: Kirawi Date: Tue, 24 Aug 2021 21:04:05 -0400 Subject: Move path util functions from helix-term to helix-core (#650) --- helix-core/src/lib.rs | 1 + helix-core/src/path.rs | 92 ++++++++++++++++++++++++++++++++++++++++++ helix-term/src/commands.rs | 3 +- helix-term/src/ui/mod.rs | 2 +- helix-term/src/ui/picker.rs | 7 +++- helix-view/src/document.rs | 97 +++------------------------------------------ helix-view/src/editor.rs | 2 +- 7 files changed, 106 insertions(+), 98 deletions(-) create mode 100644 helix-core/src/path.rs (limited to 'helix-term/src/ui/picker.rs') diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 2823959f..be01c302 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -11,6 +11,7 @@ pub mod macros; pub mod match_brackets; pub mod movement; pub mod object; +pub mod path; mod position; pub mod register; pub mod search; diff --git a/helix-core/src/path.rs b/helix-core/src/path.rs new file mode 100644 index 00000000..6c37cfa1 --- /dev/null +++ b/helix-core/src/path.rs @@ -0,0 +1,92 @@ +use std::path::{Component, Path, PathBuf}; + +/// 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) = super::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() +} + +/// 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) = super::home_dir() { + // it's ok to unwrap, the path starts with `~` + return home.join(path.strip_prefix("~").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: +pub fn get_normalized_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 get_canonicalized_path(path: &Path) -> std::io::Result { + let path = if path.is_relative() { + std::env::current_dir().map(|current_dir| current_dir.join(path))? + } else { + path.to_path_buf() + }; + + Ok(get_normalized_path(path.as_path())) +} + +pub fn get_relative_path(path: &Path) -> PathBuf { + let path = if path.is_absolute() { + let cwdir = std::env::current_dir().expect("couldn't determine current directory"); + path.strip_prefix(cwdir).unwrap_or(path) + } else { + path + }; + fold_home_dir(path) +} diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 89855cbb..9a7b6510 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2291,8 +2291,7 @@ fn buffer_picker(cx: &mut Context) { .map(|(id, doc)| (id, doc.path().cloned())) .collect(), move |(id, path): &(DocumentId, Option)| { - use helix_view::document::relative_path; - let path = path.as_deref().map(relative_path); + let path = path.as_deref().map(helix_core::path::get_relative_path); match path.as_ref().and_then(|path| path.to_str()) { Some(path) => { if *id == current { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 390f1a66..e4871312 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -208,7 +208,7 @@ pub mod completers { use std::path::Path; let is_tilde = input.starts_with('~') && input.len() == 1; - let path = helix_view::document::expand_tilde(Path::new(input)); + let path = helix_core::path::expand_tilde(Path::new(input)); let (dir, file_name) = if input.ends_with('/') { (path, None) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index a864ab6d..ef2c434c 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -17,7 +17,6 @@ use std::{borrow::Cow, collections::HashMap, path::PathBuf}; use crate::ui::{Prompt, PromptEvent}; use helix_core::Position; use helix_view::{ - document::canonicalize_path, editor::Action, graphics::{Color, CursorKind, Margin, Rect, Style}, Document, Editor, @@ -54,7 +53,11 @@ impl FilePicker { self.picker .selection() .and_then(|current| (self.file_fn)(editor, current)) - .and_then(|(path, line)| canonicalize_path(&path).ok().zip(Some(line))) + .and_then(|(path, line)| { + helix_core::path::get_canonicalized_path(&path) + .ok() + .zip(Some(line)) + }) } fn calculate_preview(&mut self, editor: &Editor) { diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index b38a94ab..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,95 +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: -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 { - 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)) -} - -pub fn relative_path(mut path: &Path) -> PathBuf { - let cwdir = std::env::current_dir().expect("couldn't determine current directory"); - if path.is_absolute() { - path = path.strip_prefix(cwdir).unwrap_or(path) - }; - fold_home_dir(path) -} - use helix_lsp::lsp; use url::Url; @@ -639,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 @@ -944,7 +855,9 @@ impl Document { } pub fn relative_path(&self) -> Option { - self.path.as_deref().map(relative_path) + self.path + .as_deref() + .map(helix_core::path::get_relative_path) } // pub fn slice(&self, range: R) -> RopeSlice where R: RangeBounds { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index ec80580e..18cb9106 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -229,7 +229,7 @@ impl Editor { } pub fn open(&mut self, path: PathBuf, action: Action) -> Result { - let path = crate::document::canonicalize_path(&path)?; + let path = helix_core::path::get_canonicalized_path(&path)?; let id = self .documents() -- cgit v1.2.3-70-g09d2