mirror of https://github.com/helix-editor/helix
perf: cache `Document`s relative path (#12385)
parent
64b38d1a28
commit
f80ae997f2
|
@ -33,7 +33,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands tilde `~` into users home directory if available, otherwise returns the path
|
/// 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.
|
/// and only slash follows it.
|
||||||
pub fn expand_tilde<'a, P>(path: P) -> Cow<'a, Path>
|
pub fn expand_tilde<'a, P>(path: P) -> Cow<'a, Path>
|
||||||
where
|
where
|
||||||
|
@ -54,11 +56,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize a path without resolving symlinks.
|
/// 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.
|
// join component, canonicalize new path, strip prefix and join to the final result.
|
||||||
pub fn normalize(path: impl AsRef<Path>) -> PathBuf {
|
pub fn normalize(path: impl AsRef<Path>) -> PathBuf {
|
||||||
let mut components = path.as_ref().components().peekable();
|
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();
|
components.next();
|
||||||
PathBuf::from(c.as_os_str())
|
PathBuf::from(c.as_os_str())
|
||||||
} else {
|
} else {
|
||||||
|
@ -209,7 +211,7 @@ fn path_component_regex(windows: bool) -> String {
|
||||||
// TODO: support backslash path escape on windows (when using git bash for example)
|
// TODO: support backslash path escape on windows (when using git bash for example)
|
||||||
let space_escape = if windows { r"[\^`]\s" } else { r"[\\]\s" };
|
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
|
// 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
|
r"[\w@.\-+#$%?!,;~&]|".to_owned() + space_escape
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,9 +185,7 @@ fn buffer_gather_paths_impl(editor: &mut Editor, args: Args) -> Vec<DocumentId>
|
||||||
for arg in args {
|
for arg in args {
|
||||||
let doc_id = editor.documents().find_map(|doc| {
|
let doc_id = editor.documents().find_map(|doc| {
|
||||||
let arg_path = Some(Path::new(arg));
|
let arg_path = Some(Path::new(arg));
|
||||||
if doc.path().map(|p| p.as_path()) == arg_path
|
if doc.path().map(|p| p.as_path()) == arg_path || doc.relative_path() == arg_path {
|
||||||
|| doc.relative_path().as_deref() == arg_path
|
|
||||||
{
|
|
||||||
Some(doc.id())
|
Some(doc.id())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -625,11 +623,12 @@ fn force_write_quit(
|
||||||
/// error, otherwise returns `Ok(())`. If the current document is unmodified,
|
/// error, otherwise returns `Ok(())`. If the current document is unmodified,
|
||||||
/// and there are modified documents, switches focus to one of them.
|
/// and there are modified documents, switches focus to one of them.
|
||||||
pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> {
|
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()
|
.documents()
|
||||||
.filter(|doc| doc.is_modified())
|
.filter(|doc| doc.is_modified())
|
||||||
.map(|doc| (doc.id(), doc.display_name()))
|
.map(|doc| doc.id())
|
||||||
.unzip();
|
.collect();
|
||||||
|
|
||||||
if let Some(first) = modified_ids.first() {
|
if let Some(first) = modified_ids.first() {
|
||||||
let current = doc!(editor);
|
let current = doc!(editor);
|
||||||
// If the current document is unmodified, and there are modified
|
// 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()) {
|
if !modified_ids.contains(¤t.id()) {
|
||||||
editor.switch(*first, Action::Replace);
|
editor.switch(*first, Action::Replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let modified_names: Vec<_> = modified_ids
|
||||||
|
.iter()
|
||||||
|
.map(|doc_id| doc!(editor, doc_id).display_name())
|
||||||
|
.collect();
|
||||||
|
|
||||||
bail!(
|
bail!(
|
||||||
"{} unsaved buffer{} remaining: {:?}",
|
"{} unsaved buffer{} remaining: {:?}",
|
||||||
modified_names.len(),
|
modified_names.len(),
|
||||||
|
@ -1023,14 +1028,14 @@ fn change_current_directory(
|
||||||
let dir = match args.next() {
|
let dir = match args.next() {
|
||||||
Some("-") => cx
|
Some("-") => cx
|
||||||
.editor
|
.editor
|
||||||
.last_cwd
|
.get_last_cwd()
|
||||||
.clone()
|
.map(|path| Cow::Owned(path.to_path_buf()))
|
||||||
.ok_or_else(|| anyhow!("No previous working directory"))?,
|
.ok_or_else(|| anyhow!("No previous working directory"))?,
|
||||||
Some(path) => helix_stdx::path::expand_tilde(Path::new(path)).to_path_buf(),
|
Some(path) => helix_stdx::path::expand_tilde(Path::new(path)),
|
||||||
None => home_dir()?,
|
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!(
|
cx.editor.set_status(format!(
|
||||||
"Current working directory is now {}",
|
"Current working directory is now {}",
|
||||||
|
|
|
@ -13,6 +13,7 @@ use helix_core::text_annotations::{InlineAnnotation, Overlay};
|
||||||
use helix_lsp::util::lsp_pos_to_pos;
|
use helix_lsp::util::lsp_pos_to_pos;
|
||||||
use helix_stdx::faccess::{copy_metadata, readonly};
|
use helix_stdx::faccess::{copy_metadata, readonly};
|
||||||
use helix_vcs::{DiffHandle, DiffProviderRegistry};
|
use helix_vcs::{DiffHandle, DiffProviderRegistry};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use thiserror;
|
use thiserror;
|
||||||
|
|
||||||
use ::parking_lot::Mutex;
|
use ::parking_lot::Mutex;
|
||||||
|
@ -148,6 +149,7 @@ pub struct Document {
|
||||||
pub inlay_hints_oudated: bool,
|
pub inlay_hints_oudated: bool,
|
||||||
|
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
|
relative_path: OnceCell<Option<PathBuf>>,
|
||||||
encoding: &'static encoding::Encoding,
|
encoding: &'static encoding::Encoding,
|
||||||
has_bom: bool,
|
has_bom: bool,
|
||||||
|
|
||||||
|
@ -300,6 +302,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 {
|
enum Encoder {
|
||||||
Utf16Be,
|
Utf16Be,
|
||||||
Utf16Le,
|
Utf16Le,
|
||||||
|
@ -659,6 +669,7 @@ impl Document {
|
||||||
id: DocumentId::default(),
|
id: DocumentId::default(),
|
||||||
active_snippet: None,
|
active_snippet: None,
|
||||||
path: None,
|
path: None,
|
||||||
|
relative_path: OnceCell::new(),
|
||||||
encoding,
|
encoding,
|
||||||
has_bom,
|
has_bom,
|
||||||
text,
|
text,
|
||||||
|
@ -1172,6 +1183,10 @@ impl Document {
|
||||||
pub fn set_path(&mut self, path: Option<&Path>) {
|
pub fn set_path(&mut self, path: Option<&Path>) {
|
||||||
let path = path.map(helix_stdx::path::canonicalize);
|
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
|
// if parent doesn't exist we still want to open the document
|
||||||
// and error out when document is saved
|
// and error out when document is saved
|
||||||
self.path = path;
|
self.path = path;
|
||||||
|
@ -1867,16 +1882,19 @@ impl Document {
|
||||||
self.view_data_mut(view_id).view_position = new_offset;
|
self.view_data_mut(view_id).view_position = new_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn relative_path(&self) -> Option<Cow<Path>> {
|
pub fn relative_path(&self) -> Option<&Path> {
|
||||||
|
self.relative_path
|
||||||
|
.get_or_init(|| {
|
||||||
self.path
|
self.path
|
||||||
|
.as_ref()
|
||||||
|
.map(|path| helix_stdx::path::get_relative_path(path).to_path_buf())
|
||||||
|
})
|
||||||
.as_deref()
|
.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()
|
self.relative_path()
|
||||||
.map(|path| path.to_string_lossy().to_string().into())
|
.map_or_else(|| SCRATCH_BUFFER_NAME.into(), |path| path.to_string_lossy())
|
||||||
.unwrap_or_else(|| SCRATCH_BUFFER_NAME.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transact(Fn) ?
|
// transact(Fn) ?
|
||||||
|
|
|
@ -1079,7 +1079,7 @@ pub struct Editor {
|
||||||
redraw_timer: Pin<Box<Sleep>>,
|
redraw_timer: Pin<Box<Sleep>>,
|
||||||
last_motion: Option<Motion>,
|
last_motion: Option<Motion>,
|
||||||
pub last_completion: Option<CompleteAction>,
|
pub last_completion: Option<CompleteAction>,
|
||||||
pub last_cwd: Option<PathBuf>,
|
last_cwd: Option<PathBuf>,
|
||||||
|
|
||||||
pub exit_code: i32,
|
pub exit_code: i32,
|
||||||
|
|
||||||
|
@ -2209,6 +2209,16 @@ impl Editor {
|
||||||
current_view.id
|
current_view.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
fn try_restore_indent(doc: &mut Document, view: &mut View) {
|
||||||
|
|
Loading…
Reference in New Issue