diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 172f25990..731059706 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -25,7 +25,7 @@ use helix_core::{ use helix_view::{ annotations::diagnostics::DiagnosticFilter, document::{Mode, SCRATCH_BUFFER_NAME}, - editor::{CompleteAction, CursorShapeConfig}, + editor::{BufferLineDirectories, CompleteAction, CursorShapeConfig}, graphics::{Color, CursorKind, Modifier, Rect, Style}, input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind}, keyboard::{KeyCode, KeyModifiers}, @@ -633,89 +633,120 @@ impl EditorView { let current_doc = view!(editor).doc; let mut documents: HashMap<_, _>; - let fnames: HashMap<_, _> = if editor.config().bufferline_show_directories { - documents = editor - .documents() - .map(|doc| { - ( - doc.id(), - doc.path().map(|p| { - let mut p = p.clone(); - let f = PathBuf::from(p.file_name().unwrap_or_default()); - p.pop(); - (f, p) - }), - ) - }) - .collect(); - let mut ids: Vec<_> = documents.keys().copied().collect(); - let mut to_remove: Vec = Vec::with_capacity(ids.len()); - while !ids.is_empty() { - for (idx, id) in ids.iter().enumerate() { - let Some((current, full)) = &documents[id] else { - log::info!("to remove scratch"); - to_remove.push(idx); - continue; - }; - if documents - .iter() - .filter_map(|(id, o)| o.as_ref().map(|(p, _)| (id, p))) - .all(|(i, p)| p != current || i == id) - || full.as_os_str().is_empty() - { - log::info!("to remove idx"); - to_remove.push(idx); + let mut fnames: HashMap<_, _> = match &editor.config().bufferline_directories { + BufferLineDirectories::Smart => { + documents = editor + .documents() + .map(|doc| { + ( + doc.id(), + doc.path().map(|p| { + let mut p = p.clone(); + let f = PathBuf::from(p.file_name().unwrap_or_default()); + p.pop(); + (f, p) + }), + ) + }) + .collect(); + let mut ids: Vec<_> = documents.keys().copied().collect(); + let mut to_remove: Vec = Vec::with_capacity(ids.len()); + while !ids.is_empty() { + for (idx, id) in ids.iter().enumerate() { + let Some((current, full)) = &documents[id] else { + to_remove.push(idx); + continue; + }; + if documents + .iter() + .filter_map(|(id, o)| o.as_ref().map(|(p, _)| (id, p))) + .all(|(i, p)| p != current || i == id) + || full.as_os_str().is_empty() + { + to_remove.push(idx); + } + } + + for idx in to_remove.iter().rev() { + ids.remove(*idx); + } + to_remove.clear(); + + for id in &ids { + let Some((current, mut full)) = documents.remove(id).flatten() else { + documents.insert(*id, None); + continue; + }; + let mut new = PathBuf::from(full.file_name().unwrap_or_default()); + new.push(current); + full.pop(); + documents.insert(*id, Some((new, full))); } } - - for idx in to_remove.iter().rev() { - log::info!("removed idx"); - ids.remove(*idx); - } - to_remove.clear(); - - for id in &ids { - let Some((current, mut full)) = documents.remove(id).flatten() else { - documents.insert(*id, None); - continue; - }; - let mut new = PathBuf::from(full.file_name().unwrap_or_default()); - new.push(current); - full.pop(); - documents.insert(*id, Some((new, full))); - } + documents + .iter() + .map(|(id, op)| { + ( + *id, + format!( + " {}{} ", + op.as_ref() + .map(|(p, _)| p.to_str().unwrap_or_default()) + .unwrap_or(SCRATCH_BUFFER_NAME), + if editor.document(*id).map_or(false, |d| d.is_modified()) { + "[+]" + } else { + "" + } + ), + ) + }) + .collect() } - log::info!("completed cycle"); - documents - .iter() - .map(|(id, op)| { - ( - *id, - op.as_ref() - .map(|(p, _)| p.to_str().unwrap_or_default()) - .unwrap_or(SCRATCH_BUFFER_NAME), - ) - }) - .collect() - } else { - editor + BufferLineDirectories::Never => editor .documents() .map(|doc| { ( doc.id(), - doc.path() - .unwrap_or(&scratch) - .file_name() - .unwrap_or_default() - .to_str() - .unwrap_or_default(), + format!( + " {}{} ", + doc.path() + .unwrap_or(&scratch) + .file_name() + .unwrap_or_default() + .to_str() + .unwrap_or_default(), + if doc.is_modified() { "[+]" } else { "" } + ), ) }) - .collect() + .collect(), + BufferLineDirectories::Always => editor + .documents() + .map(|doc| { + let iter = doc.path().unwrap_or(&scratch); + ( + doc.id(), + format!( + " {parent}/{fname}{modified} ", + parent = iter + .parent() + .and_then(|p| p.file_name().unwrap_or_default().to_str()) + .unwrap_or_default(), + fname = iter + .file_name() + .unwrap_or_default() + .to_str() + .unwrap_or_default(), + modified = if doc.is_modified() { "[+]" } else { "" } + ), + ) + }) + .collect(), }; for doc in editor.documents() { - let fname = &fnames[&doc.id()]; + let text = fnames.remove(&doc.id()).unwrap_or_default(); let style = if current_doc == doc.id() { bufferline_active @@ -723,7 +754,6 @@ impl EditorView { bufferline_inactive }; - let text = format!(" {}{} ", fname, if doc.is_modified() { "[+]" } else { "" }); let used_width = viewport.x.saturating_sub(x); let rem_width = surface.area.width.saturating_sub(used_width); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 6e072449a..8e4f7f7d3 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -333,7 +333,7 @@ pub struct Config { /// Persistently display open buffers along the top pub bufferline: BufferLine, /// Show the parent directory of files in the bufferline. - pub bufferline_show_directories: bool, + pub bufferline_directories: BufferLineDirectories, /// Vertical indent width guides. pub indent_guides: IndentGuidesConfig, /// Whether to color modes with different colors. Defaults to `false`. @@ -682,6 +682,18 @@ pub enum BufferLine { /// Only if multiple buffers are open Multiple, } +/// bufferline directory render modes +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum BufferLineDirectories { + /// Don't render directories + #[default] + Never, + /// Always render one directory + Always, + /// Only if multiple buffers with the same name are open + Smart, +} #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] @@ -1001,7 +1013,7 @@ impl Default for Config { rulers: Vec::new(), whitespace: WhitespaceConfig::default(), bufferline: BufferLine::default(), - bufferline_show_directories: false, + bufferline_directories: BufferLineDirectories::default(), indent_guides: IndentGuidesConfig::default(), color_modes: false, soft_wrap: SoftWrap {