diff --git a/helix-stdx/src/path.rs b/helix-stdx/src/path.rs index d32c0f59..ebc6b3b2 100644 --- a/helix-stdx/src/path.rs +++ b/helix-stdx/src/path.rs @@ -34,7 +34,9 @@ where } /// Expands tilde `~` into users home directory if available, otherwise returns the path -/// unchanged. The tilde will only be expanded when present as the first component of 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<'a, P>(path: P) -> Cow<'a, Path> where @@ -55,11 +57,11 @@ where } /// Normalize a path without resolving symlinks. -// Strategy: start from the first component and move up. Cannonicalize previous path, +// Strategy: start from the first component and move up. Canonicalize previous path, // join component, canonicalize new path, strip prefix and join to the final result. pub fn normalize(path: impl AsRef) -> PathBuf { let mut components = path.as_ref().components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { + let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() { components.next(); PathBuf::from(c.as_os_str()) } else { @@ -210,7 +212,7 @@ fn path_component_regex(windows: bool) -> String { // TODO: support backslash path escape on windows (when using git bash for example) let space_escape = if windows { r"[\^`]\s" } else { r"[\\]\s" }; // partially baesd on what's allowed in an url but with some care to avoid - // false positivies (like any kind of brackets or quotes) + // false positives (like any kind of brackets or quotes) r"[\w@.\-+#$%?!,;~&]|".to_owned() + space_escape } diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 728d6dc7..fd5f9792 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -185,9 +185,7 @@ fn buffer_gather_paths_impl(editor: &mut Editor, args: Args) -> Vec for arg in args { let doc_id = editor.documents().find_map(|doc| { let arg_path = Some(Path::new(arg)); - if doc.path().map(|p| p.as_path()) == arg_path - || doc.relative_path().as_deref() == arg_path - { + if doc.path().map(|p| p.as_path()) == arg_path || doc.relative_path() == arg_path { Some(doc.id()) } else { None @@ -625,11 +623,12 @@ fn force_write_quit( /// error, otherwise returns `Ok(())`. If the current document is unmodified, /// and there are modified documents, switches focus to one of them. pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> { - let (modified_ids, modified_names): (Vec<_>, Vec<_>) = editor + let modified_ids: Vec<_> = editor .documents() .filter(|doc| doc.is_modified()) - .map(|doc| (doc.id(), doc.display_name())) - .unzip(); + .map(|doc| doc.id()) + .collect(); + if let Some(first) = modified_ids.first() { let current = doc!(editor); // If the current document is unmodified, and there are modified @@ -637,6 +636,12 @@ pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> if !modified_ids.contains(¤t.id()) { editor.switch(*first, Action::Replace); } + + let modified_names: Vec<_> = modified_ids + .iter() + .map(|doc_id| doc!(editor, doc_id).display_name()) + .collect(); + bail!( "{} unsaved buffer{} remaining: {:?}", modified_names.len(), @@ -1023,14 +1028,14 @@ fn change_current_directory( let dir = match args.next() { Some("-") => cx .editor - .last_cwd - .clone() + .get_last_cwd() + .map(|path| Cow::Owned(path.to_path_buf())) .ok_or_else(|| anyhow!("No previous working directory"))?, - Some(path) => helix_stdx::path::expand_tilde(Path::new(path)).to_path_buf(), - None => home_dir()?, + Some(path) => helix_stdx::path::expand_tilde(Path::new(path)), + None => Cow::Owned(home_dir()?), }; - cx.editor.last_cwd = helix_stdx::env::set_current_working_dir(dir)?; + cx.editor.set_cwd(&dir)?; cx.editor.set_status(format!( "Current working directory is now {}", diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index adacfad3..f5b547f0 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -92,8 +92,9 @@ impl menu::Item for CompletionItem { value, .. }) => value, }; - Color::from_hex(text) + text.get(text.len() - 7..) }) + .and_then(Color::from_hex) .map_or("color".into(), |color| { Spans::from(vec![ Span::raw("color "), diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index f7fb422c..b7cf7c7e 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -14,6 +14,7 @@ use helix_core::text_annotations::{InlineAnnotation, Overlay}; use helix_lsp::util::lsp_pos_to_pos; use helix_stdx::faccess::readonly; use helix_vcs::{DiffHandle, DiffProviderRegistry}; +use once_cell::sync::OnceCell; use thiserror; use ::parking_lot::Mutex; @@ -304,6 +305,7 @@ pub struct Document { pub color_swatches_outdated: bool, path: Option, + relative_path: OnceCell>, encoding: &'static encoding::Encoding, has_bom: bool, @@ -503,6 +505,14 @@ impl fmt::Debug for DocumentInlayHintsId { } } +impl Editor { + pub(crate) fn clear_doc_relative_paths(&mut self) { + for doc in self.documents_mut() { + doc.relative_path.take(); + } + } +} + enum Encoder { Utf16Be, Utf16Le, @@ -862,6 +872,7 @@ impl Document { id: DocumentId::default(), active_snippet: None, path: None, + relative_path: OnceCell::new(), encoding, has_bom, text, @@ -1408,6 +1419,10 @@ impl Document { pub fn set_path(&mut self, path: Option<&Path>) { let path = path.map(helix_stdx::path::canonicalize); + // `take` to remove any prior relative path that may have existed. + // This will get set in `relative_path()`. + self.relative_path.take(); + // if parent doesn't exist we still want to open the document // and error out when document is saved self.path = path; @@ -2118,16 +2133,19 @@ impl Document { self.view_data_mut(view_id).view_position = new_offset; } - pub fn relative_path(&self) -> Option> { - self.path + pub fn relative_path(&self) -> Option<&Path> { + self.relative_path + .get_or_init(|| { + self.path + .as_ref() + .map(|path| helix_stdx::path::get_relative_path(path).to_path_buf()) + }) .as_deref() - .map(helix_stdx::path::get_relative_path) } - pub fn display_name(&self) -> Cow<'static, str> { + pub fn display_name(&self) -> Cow<'_, str> { self.relative_path() - .map(|path| path.to_string_lossy().to_string().into()) - .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into()) + .map_or_else(|| SCRATCH_BUFFER_NAME.into(), |path| path.to_string_lossy()) } // transact(Fn) ? diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index f44c9a67..8f427c17 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1147,7 +1147,7 @@ pub struct Editor { redraw_timer: Pin>, last_motion: Option, pub last_completion: Option, - pub last_cwd: Option, + last_cwd: Option, pub exit_code: i32, @@ -2369,6 +2369,16 @@ impl Editor { helix_event::dispatch(crate::events::DiagnosticsDidChange { editor: self, doc }); } + + pub fn set_cwd(&mut self, path: &Path) -> std::io::Result<()> { + self.last_cwd = helix_stdx::env::set_current_working_dir(path)?; + self.clear_doc_relative_paths(); + Ok(()) + } + + pub fn get_last_cwd(&mut self) -> Option<&Path> { + self.last_cwd.as_deref() + } } fn try_restore_indent(doc: &mut Document, view: &mut View) { diff --git a/runtime/queries/_jsx/highlights.scm b/runtime/queries/_jsx/highlights.scm index 2a696641..7061b581 100644 --- a/runtime/queries/_jsx/highlights.scm +++ b/runtime/queries/_jsx/highlights.scm @@ -25,7 +25,7 @@ ; Attributes ; ---------- -(jsx_attribute (property_identifier) @variable.other.member) +(jsx_attribute (property_identifier) @attribute) ; Punctuation ; ----------- diff --git a/runtime/queries/markdown/highlights.scm b/runtime/queries/markdown/highlights.scm index 72bb68dc..a80fc1be 100644 --- a/runtime/queries/markdown/highlights.scm +++ b/runtime/queries/markdown/highlights.scm @@ -2,12 +2,12 @@ (setext_heading (paragraph) @markup.heading.1 (setext_h1_underline) @markup.heading.marker) (setext_heading (paragraph) @markup.heading.2 (setext_h2_underline) @markup.heading.marker) -(atx_heading (atx_h1_marker) @markup.heading.marker (inline) @markup.heading.1) -(atx_heading (atx_h2_marker) @markup.heading.marker (inline) @markup.heading.2) -(atx_heading (atx_h3_marker) @markup.heading.marker (inline) @markup.heading.3) -(atx_heading (atx_h4_marker) @markup.heading.marker (inline) @markup.heading.4) -(atx_heading (atx_h5_marker) @markup.heading.marker (inline) @markup.heading.5) -(atx_heading (atx_h6_marker) @markup.heading.marker (inline) @markup.heading.6) +(atx_heading (atx_h1_marker) @markup.heading.marker) @markup.heading.1 +(atx_heading (atx_h2_marker) @markup.heading.marker) @markup.heading.2 +(atx_heading (atx_h3_marker) @markup.heading.marker) @markup.heading.3 +(atx_heading (atx_h4_marker) @markup.heading.marker) @markup.heading.4 +(atx_heading (atx_h5_marker) @markup.heading.marker) @markup.heading.5 +(atx_heading (atx_h6_marker) @markup.heading.marker) @markup.heading.6 [ (indented_code_block)