From 3af529d868503aef6cef6cc0aa63c41cd5c31cbe Mon Sep 17 00:00:00 2001 From: Ian Hobson Date: Mon, 2 Dec 2024 15:24:59 +0100 Subject: [PATCH] Add support for clicking on the bufferline to select a buffer Co-authored-by: Kyle Smith --- helix-term/src/ui/editor.rs | 72 ++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 5179be4f..eeb71d5c 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -29,7 +29,7 @@ use helix_view::{ graphics::{Color, CursorKind, Modifier, Rect, Style}, input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind}, keyboard::{KeyCode, KeyModifiers}, - Document, Editor, Theme, View, + Document, DocumentId, Editor, Theme, View, }; use std::{mem::take, num::NonZeroUsize, path::PathBuf, rc::Rc, sync::Arc}; @@ -42,6 +42,7 @@ pub struct EditorView { pub(crate) last_insert: (commands::MappableCommand, Vec), pub(crate) completion: Option, spinners: ProgressSpinners, + bufferline_info: BufferLineInfo, /// Tracks if the terminal window is focused by reaction to terminal focus events terminal_focused: bool, } @@ -72,6 +73,7 @@ impl EditorView { last_insert: (commands::MappableCommand::normal_mode, Vec::new()), completion: None, spinners: ProgressSpinners::default(), + bufferline_info: BufferLineInfo::default(), terminal_focused: true, } } @@ -593,7 +595,7 @@ impl EditorView { } /// Render bufferline at the top - pub fn render_bufferline(editor: &Editor, viewport: Rect, surface: &mut Surface) { + pub fn render_bufferline(&mut self, editor: &Editor, viewport: Rect, surface: &mut Surface) { let scratch = PathBuf::from(SCRATCH_BUFFER_NAME); // default filename to use for scratch buffer surface.clear_with( viewport, @@ -616,6 +618,8 @@ impl EditorView { let mut x = viewport.x; let current_doc = view!(editor).doc; + self.bufferline_info.clear(); + for doc in editor.documents() { let fname = doc .path() @@ -635,9 +639,14 @@ impl EditorView { let used_width = viewport.x.saturating_sub(x); let rem_width = surface.area.width.saturating_sub(used_width); + let start_x = x; x = surface .set_stringn(x, viewport.y, text, rem_width as usize, style) .0; + let end_x = x.min(surface.area.right()); + + self.bufferline_info + .add_buffer_info(doc.id(), start_x..end_x); if x >= surface.area.right() { break; @@ -1139,6 +1148,14 @@ impl EditorView { MouseEventKind::Down(MouseButton::Left) => { let editor = &mut cxt.editor; + if is_bufferline_visible(editor) && row == 0 { + if let Some(buffer_info) = self.bufferline_info.get_clicked_buffer(column) { + editor.switch(buffer_info.document_id, helix_view::editor::Action::Replace); + } + + return EventResult::Consumed(None); + } + if let Some((pos, view_id)) = pos_and_view(editor, row, column, true) { let prev_view_id = view!(editor).id; let doc = doc_mut!(editor, &view!(editor, view_id).doc); @@ -1480,12 +1497,7 @@ impl Component for EditorView { let config = cx.editor.config(); // check if bufferline should be rendered - use helix_view::editor::BufferLine; - let use_bufferline = match config.bufferline { - BufferLine::Always => true, - BufferLine::Multiple if cx.editor.documents.len() > 1 => true, - _ => false, - }; + let use_bufferline = is_bufferline_visible(cx.editor); // -1 for commandline and -1 for bufferline let mut editor_area = area.clip_bottom(1); @@ -1497,7 +1509,7 @@ impl Component for EditorView { cx.editor.resize(editor_area); if use_bufferline { - Self::render_bufferline(cx.editor, area.with_height(1), surface); + self.render_bufferline(cx.editor, area.with_height(1), surface); } for (view, is_focused) in cx.editor.tree.views() { @@ -1592,6 +1604,48 @@ impl Component for EditorView { } } +#[derive(Debug, Default)] +struct BufferLineInfo { + visible_buffers: Vec, +} + +impl BufferLineInfo { + fn clear(&mut self) { + self.visible_buffers.clear(); + } + + fn add_buffer_info(&mut self, document_id: DocumentId, columns: std::ops::Range) { + self.visible_buffers.push(BufferInfo { + document_id, + columns, + }); + } + + fn get_clicked_buffer(&self, column: u16) -> Option<&BufferInfo> { + self.visible_buffers + .iter() + .find(|cell| cell.columns.contains(&column)) + } +} + +#[derive(Debug)] +struct BufferInfo { + document_id: DocumentId, + // The bufferline column span used to show the document name + columns: std::ops::Range, +} + +fn is_bufferline_visible(editor: &Editor) -> bool { + use helix_view::editor::BufferLine; + let config = editor.config(); + + match config.bufferline { + BufferLine::Always => true, + BufferLine::Multiple if editor.documents.len() > 1 => true, + _ => false, + } +} + fn canonicalize_key(key: &mut KeyEvent) { if let KeyEvent { code: KeyCode::Char(_),