feat: add config option to make statusline unobtrusive

pull/12020/head
Nikita Revenco 3 weeks ago
parent b53dafe326
commit d9727082b3

@ -105,6 +105,7 @@ impl Application {
use helix_view::editor::Action; use helix_view::editor::Action;
let unobtrusive_statusline = config.editor.statusline.unobtrusive;
let mut theme_parent_dirs = vec![helix_loader::config_dir()]; let mut theme_parent_dirs = vec![helix_loader::config_dir()];
theme_parent_dirs.extend(helix_loader::runtime_dirs().iter().cloned()); theme_parent_dirs.extend(helix_loader::runtime_dirs().iter().cloned());
let theme_loader = std::sync::Arc::new(theme::Loader::new(&theme_parent_dirs)); let theme_loader = std::sync::Arc::new(theme::Loader::new(&theme_parent_dirs));
@ -221,7 +222,7 @@ impl Application {
// align the view to center after all files are loaded, // align the view to center after all files are loaded,
// does not affect views without pos since it is at the top // does not affect views without pos since it is at the top
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center, unobtrusive_statusline);
} }
} else { } else {
editor.new_file(Action::VerticalSplit); editor.new_file(Action::VerticalSplit);
@ -396,10 +397,12 @@ impl Application {
self.editor.refresh_config(); self.editor.refresh_config();
// reset view position in case softwrap was enabled/disabled // reset view position in case softwrap was enabled/disabled
let scrolloff = self.editor.config().scrolloff; let config = self.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
for (view, _) in self.editor.tree.views() { for (view, _) in self.editor.tree.views() {
let doc = doc_mut!(self.editor, &view.doc); let doc = doc_mut!(self.editor, &view.doc);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
} }
} }
@ -1171,6 +1174,8 @@ impl Application {
} }
}; };
let config = &self.editor.config();
let unobtrusive_statusline = config.statusline.unobtrusive;
let doc = doc_mut!(self.editor, &doc_id); let doc = doc_mut!(self.editor, &doc_id);
if let Some(range) = selection { if let Some(range) = selection {
// TODO: convert inside server // TODO: convert inside server
@ -1181,7 +1186,7 @@ impl Application {
// (for example start of the function). // (for example start of the function).
doc.set_selection(view.id, Selection::single(new_range.head, new_range.anchor)); doc.set_selection(view.id, Selection::single(new_range.head, new_range.anchor));
if action.align_view(view, doc.id()) { if action.align_view(view, doc.id()) {
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center, unobtrusive_statusline);
} }
} else { } else {
log::warn!("lsp position out of bounds - {:?}", range); log::warn!("lsp position out of bounds - {:?}", range);

@ -676,9 +676,12 @@ type MoveFn =
fn move_impl(cx: &mut Context, move_fn: MoveFn, dir: Direction, behaviour: Movement) { fn move_impl(cx: &mut Context, move_fn: MoveFn, dir: Direction, behaviour: Movement) {
let count = cx.count(); let count = cx.count();
let config = cx.editor.config();
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let unobtrusive_statusline = config.statusline.unobtrusive;
let text = doc.text().slice(..); let text = doc.text().slice(..);
let text_fmt = doc.text_format(view.inner_area(doc).width, None); let text_fmt = doc.text_format(view.inner_area(doc, unobtrusive_statusline).width, None);
let mut annotations = view.text_annotations(doc, None); let mut annotations = view.text_annotations(doc, None);
let selection = doc.selection(view.id).clone().transform(|range| { let selection = doc.selection(view.id).clone().transform(|range| {
@ -1079,10 +1082,11 @@ fn align_selections(cx: &mut Context) {
fn goto_window(cx: &mut Context, align: Align) { fn goto_window(cx: &mut Context, align: Align) {
let count = cx.count() - 1; let count = cx.count() - 1;
let config = cx.editor.config(); let config = cx.editor.config();
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let view_offset = doc.view_offset(view.id); let view_offset = doc.view_offset(view.id);
let height = view.inner_height(); let height = view.inner_height(unobtrusive_statusline);
// respect user given count if any // respect user given count if any
// - 1 so we have at least one gap in the middle. // - 1 so we have at least one gap in the middle.
@ -1090,7 +1094,7 @@ fn goto_window(cx: &mut Context, align: Align) {
// as we type // as we type
let scrolloff = config.scrolloff.min(height.saturating_sub(1) / 2); let scrolloff = config.scrolloff.min(height.saturating_sub(1) / 2);
let last_visual_line = view.last_visual_line(doc); let last_visual_line = view.last_visual_line(doc, unobtrusive_statusline);
let visual_line = match align { let visual_line = match align {
Align::Top => view_offset.vertical_offset + scrolloff + count, Align::Top => view_offset.vertical_offset + scrolloff + count,
@ -1752,7 +1756,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor
let text = doc.text().slice(..); let text = doc.text().slice(..);
let cursor = range.cursor(text); let cursor = range.cursor(text);
let height = view.inner_height(); let height = view.inner_height(config.statusline.unobtrusive);
let scrolloff = config.scrolloff.min(height.saturating_sub(1) / 2); let scrolloff = config.scrolloff.min(height.saturating_sub(1) / 2);
let offset = match direction { let offset = match direction {
@ -1761,7 +1765,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor
}; };
let doc_text = doc.text().slice(..); let doc_text = doc.text().slice(..);
let viewport = view.inner_area(doc); let viewport = view.inner_area(doc, config.statusline.unobtrusive);
let text_fmt = doc.text_format(viewport.width, None); let text_fmt = doc.text_format(viewport.width, None);
(view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset( (view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset(
doc_text, doc_text,
@ -1854,49 +1858,49 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor
fn page_up(cx: &mut Context) { fn page_up(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height(); let offset = view.inner_height(cx.editor.config().statusline.unobtrusive);
scroll(cx, offset, Direction::Backward, false); scroll(cx, offset, Direction::Backward, false);
} }
fn page_down(cx: &mut Context) { fn page_down(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height(); let offset = view.inner_height(cx.editor.config().statusline.unobtrusive);
scroll(cx, offset, Direction::Forward, false); scroll(cx, offset, Direction::Forward, false);
} }
fn half_page_up(cx: &mut Context) { fn half_page_up(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height() / 2; let offset = view.inner_height(cx.editor.config().statusline.unobtrusive) / 2;
scroll(cx, offset, Direction::Backward, false); scroll(cx, offset, Direction::Backward, false);
} }
fn half_page_down(cx: &mut Context) { fn half_page_down(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height() / 2; let offset = view.inner_height(cx.editor.config().statusline.unobtrusive) / 2;
scroll(cx, offset, Direction::Forward, false); scroll(cx, offset, Direction::Forward, false);
} }
fn page_cursor_up(cx: &mut Context) { fn page_cursor_up(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height(); let offset = view.inner_height(cx.editor.config().statusline.unobtrusive);
scroll(cx, offset, Direction::Backward, true); scroll(cx, offset, Direction::Backward, true);
} }
fn page_cursor_down(cx: &mut Context) { fn page_cursor_down(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height(); let offset = view.inner_height(cx.editor.config().statusline.unobtrusive);
scroll(cx, offset, Direction::Forward, true); scroll(cx, offset, Direction::Forward, true);
} }
fn page_cursor_half_up(cx: &mut Context) { fn page_cursor_half_up(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height() / 2; let offset = view.inner_height(cx.editor.config().statusline.unobtrusive) / 2;
scroll(cx, offset, Direction::Backward, true); scroll(cx, offset, Direction::Backward, true);
} }
fn page_cursor_half_down(cx: &mut Context) { fn page_cursor_half_down(cx: &mut Context) {
let view = view!(cx.editor); let view = view!(cx.editor);
let offset = view.inner_height() / 2; let offset = view.inner_height(cx.editor.config().statusline.unobtrusive) / 2;
scroll(cx, offset, Direction::Forward, true); scroll(cx, offset, Direction::Forward, true);
} }
@ -2073,6 +2077,7 @@ fn search_impl(
scrolloff: usize, scrolloff: usize,
wrap_around: bool, wrap_around: bool,
show_warnings: bool, show_warnings: bool,
unobtrusive_statusline: bool,
) { ) {
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
let text = doc.text().slice(..); let text = doc.text().slice(..);
@ -2143,7 +2148,7 @@ fn search_impl(
}; };
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
view.ensure_cursor_in_view_center(doc, scrolloff); view.ensure_cursor_in_view_center(doc, scrolloff, unobtrusive_statusline);
}; };
} }
@ -2168,6 +2173,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
let reg = cx.register.unwrap_or('/'); let reg = cx.register.unwrap_or('/');
let config = cx.editor.config(); let config = cx.editor.config();
let scrolloff = config.scrolloff; let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let wrap_around = config.search.wrap_around; let wrap_around = config.search.wrap_around;
let movement = if cx.editor.mode() == Mode::Select { let movement = if cx.editor.mode() == Mode::Select {
Movement::Extend Movement::Extend
@ -2203,6 +2209,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
scrolloff, scrolloff,
wrap_around, wrap_around,
false, false,
unobtrusive_statusline,
); );
}, },
); );
@ -2215,6 +2222,7 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
.unwrap_or(cx.editor.registers.last_search_register); .unwrap_or(cx.editor.registers.last_search_register);
let config = cx.editor.config(); let config = cx.editor.config();
let scrolloff = config.scrolloff; let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
if let Some(query) = cx.editor.registers.first(register, cx.editor) { if let Some(query) = cx.editor.registers.first(register, cx.editor) {
let search_config = &config.search; let search_config = &config.search;
let case_insensitive = if search_config.smart_case { let case_insensitive = if search_config.smart_case {
@ -2240,6 +2248,7 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
scrolloff, scrolloff,
wrap_around, wrap_around,
true, true,
unobtrusive_statusline,
); );
} }
} else { } else {
@ -2498,6 +2507,7 @@ fn global_search(cx: &mut Context) {
[], [],
config, config,
move |cx, FileResult { path, line_num, .. }, action| { move |cx, FileResult { path, line_num, .. }, action| {
let config = cx.editor.config();
let doc = match cx.editor.open(path, action) { let doc = match cx.editor.open(path, action) {
Ok(id) => doc_mut!(cx.editor, &id), Ok(id) => doc_mut!(cx.editor, &id),
Err(e) => { Err(e) => {
@ -2508,6 +2518,7 @@ fn global_search(cx: &mut Context) {
}; };
let line_num = *line_num; let line_num = *line_num;
let unobtrusive_statusline = config.statusline.unobtrusive;
let view = view_mut!(cx.editor); let view = view_mut!(cx.editor);
let text = doc.text(); let text = doc.text();
if line_num >= text.len_lines() { if line_num >= text.len_lines() {
@ -2521,7 +2532,7 @@ fn global_search(cx: &mut Context) {
doc.set_selection(view.id, Selection::single(start, end)); doc.set_selection(view.id, Selection::single(start, end));
if action.align_view(view, doc.id()) { if action.align_view(view, doc.id()) {
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center, unobtrusive_statusline);
} }
}, },
) )
@ -3095,7 +3106,7 @@ fn jumplist_picker(cx: &mut Context) {
let (view, doc) = (view_mut!(cx.editor), doc_mut!(cx.editor, &meta.id)); let (view, doc) = (view_mut!(cx.editor), doc_mut!(cx.editor, &meta.id));
doc.set_selection(view.id, meta.selection.clone()); doc.set_selection(view.id, meta.selection.clone());
if action.align_view(view, doc.id()) { if action.align_view(view, doc.id()) {
view.ensure_cursor_in_view_center(doc, config.scrolloff); view.ensure_cursor_in_view_center(doc, config.scrolloff, config.statusline.unobtrusive);
} }
}, },
) )
@ -3271,7 +3282,7 @@ pub fn command_palette(cx: &mut Context) {
let view = view_mut!(ctx.editor, focus); let view = view_mut!(ctx.editor, focus);
let doc = doc_mut!(ctx.editor, &view.doc); let doc = doc_mut!(ctx.editor, &view.doc);
view.ensure_cursor_in_view(doc, config.scrolloff); view.ensure_cursor_in_view(doc, config.scrolloff, config.statusline.unobtrusive);
if mode != Mode::Insert { if mode != Mode::Insert {
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
@ -3397,7 +3408,9 @@ async fn make_format_callback(
return; return;
} }
let scrolloff = editor.config().scrolloff; let config = editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let doc = doc_mut!(editor, &doc_id); let doc = doc_mut!(editor, &doc_id);
let view = view_mut!(editor, view_id); let view = view_mut!(editor, view_id);
@ -3406,7 +3419,7 @@ async fn make_format_callback(
doc.apply(&format, view.id); doc.apply(&format, view.id);
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
doc.detect_indent_and_line_ending(); doc.detect_indent_and_line_ending();
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
} else { } else {
log::info!("discarded formatting changes because the document changed"); log::info!("discarded formatting changes because the document changed");
} }
@ -4451,7 +4464,10 @@ fn replace_with_yanked_impl(editor: &mut Editor, register: char, count: usize) {
return; return;
}; };
let values: Vec<_> = values.map(|value| value.to_string()).collect(); let values: Vec<_> = values.map(|value| value.to_string()).collect();
let scrolloff = editor.config().scrolloff;
let config = editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
let repeat = std::iter::repeat( let repeat = std::iter::repeat(
@ -4475,7 +4491,7 @@ fn replace_with_yanked_impl(editor: &mut Editor, register: char, count: usize) {
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
} }
fn replace_selections_with_clipboard(cx: &mut Context) { fn replace_selections_with_clipboard(cx: &mut Context) {
@ -5210,7 +5226,7 @@ fn jump_forward(cx: &mut Context) {
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
// Document we switch to might not have been opened in the view before // Document we switch to might not have been opened in the view before
doc.ensure_view_init(view.id); doc.ensure_view_init(view.id);
view.ensure_cursor_in_view_center(doc, config.scrolloff); view.ensure_cursor_in_view_center(doc, config.scrolloff, config.statusline.unobtrusive);
}; };
} }
@ -5232,7 +5248,7 @@ fn jump_backward(cx: &mut Context) {
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
// Document we switch to might not have been opened in the view before // Document we switch to might not have been opened in the view before
doc.ensure_view_init(view.id); doc.ensure_view_init(view.id);
view.ensure_cursor_in_view_center(doc, config.scrolloff); view.ensure_cursor_in_view_center(doc, config.scrolloff, config.statusline.unobtrusive);
}; };
} }
@ -5374,21 +5390,25 @@ fn insert_register(cx: &mut Context) {
} }
fn align_view_top(cx: &mut Context) { fn align_view_top(cx: &mut Context) {
let unobtrusive_statusline = cx.editor.config().statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
align_view(doc, view, Align::Top); align_view(doc, view, Align::Top, unobtrusive_statusline);
} }
fn align_view_center(cx: &mut Context) { fn align_view_center(cx: &mut Context) {
let unobtrusive_statusline = cx.editor.config().statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center, unobtrusive_statusline);
} }
fn align_view_bottom(cx: &mut Context) { fn align_view_bottom(cx: &mut Context) {
let unobtrusive_statusline = cx.editor.config().statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
align_view(doc, view, Align::Bottom); align_view(doc, view, Align::Bottom, unobtrusive_statusline);
} }
fn align_view_middle(cx: &mut Context) { fn align_view_middle(cx: &mut Context) {
let config = cx.editor.config();
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let inner_width = view.inner_width(doc); let inner_width = view.inner_width(doc);
let text_fmt = doc.text_format(inner_width, None); let text_fmt = doc.text_format(inner_width, None);
@ -5408,9 +5428,10 @@ fn align_view_middle(cx: &mut Context) {
.0; .0;
let mut offset = doc.view_offset(view.id); let mut offset = doc.view_offset(view.id);
let unobtrusive_statusline = config.statusline.unobtrusive;
offset.horizontal_offset = pos offset.horizontal_offset = pos
.col .col
.saturating_sub((view.inner_area(doc).width as usize) / 2); .saturating_sub((view.inner_area(doc, unobtrusive_statusline).width as usize) / 2);
doc.set_view_offset(view.id, offset); doc.set_view_offset(view.id, offset);
} }
@ -5975,7 +5996,7 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
// after replace cursor may be out of bounds, do this to // after replace cursor may be out of bounds, do this to
// make sure cursor is in view and update scroll as well // make sure cursor is in view and update scroll as well
view.ensure_cursor_in_view(doc, config.scrolloff); view.ensure_cursor_in_view(doc, config.scrolloff, config.statusline.unobtrusive);
} }
fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
@ -6282,7 +6303,9 @@ fn jump_to_label(cx: &mut Context, labels: Vec<Range>, behaviour: Movement) {
fn jump_to_word(cx: &mut Context, behaviour: Movement) { fn jump_to_word(cx: &mut Context, behaviour: Movement) {
// Calculate the jump candidates: ranges for any visible words with two or // Calculate the jump candidates: ranges for any visible words with two or
// more characters. // more characters.
let alphabet = &cx.editor.config().jump_label_alphabet; let config = cx.editor.config();
let alphabet = &config.jump_label_alphabet;
let unobtrusive_statusline = config.statusline.unobtrusive;
let jump_label_limit = alphabet.len() * alphabet.len(); let jump_label_limit = alphabet.len() * alphabet.len();
let mut words = Vec::with_capacity(jump_label_limit); let mut words = Vec::with_capacity(jump_label_limit);
let (view, doc) = current_ref!(cx.editor); let (view, doc) = current_ref!(cx.editor);
@ -6291,7 +6314,7 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) {
// This is not necessarily exact if there is virtual text like soft wrap. // This is not necessarily exact if there is virtual text like soft wrap.
// It's ok though because the extra jump labels will not be rendered. // It's ok though because the extra jump labels will not be rendered.
let start = text.line_to_char(text.char_to_line(doc.view_offset(view.id).anchor)); let start = text.line_to_char(text.char_to_line(doc.view_offset(view.id).anchor));
let end = text.line_to_char(view.estimate_last_doc_line(doc) + 1); let end = text.line_to_char(view.estimate_last_doc_line(doc, unobtrusive_statusline) + 1);
let primary_selection = doc.selection(view.id).primary(); let primary_selection = doc.selection(view.id).primary();
let cursor = primary_selection.cursor(text); let cursor = primary_selection.cursor(text);

@ -134,6 +134,7 @@ fn jump_to_position(
offset_encoding: OffsetEncoding, offset_encoding: OffsetEncoding,
action: Action, action: Action,
) { ) {
let config = &editor.config();
let doc = match editor.open(path, action) { let doc = match editor.open(path, action) {
Ok(id) => doc_mut!(editor, &id), Ok(id) => doc_mut!(editor, &id),
Err(err) => { Err(err) => {
@ -154,8 +155,9 @@ fn jump_to_position(
// we flip the range so that the cursor sits on the start of the symbol // we flip the range so that the cursor sits on the start of the symbol
// (for example start of the function). // (for example start of the function).
doc.set_selection(view.id, Selection::single(new_range.head, new_range.anchor)); doc.set_selection(view.id, Selection::single(new_range.head, new_range.anchor));
let unobtrusive_statusline = config.statusline.unobtrusive;
if action.align_view(view, doc.id()) { if action.align_view(view, doc.id()) {
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center, unobtrusive_statusline);
} }
} }
@ -1239,7 +1241,9 @@ pub fn select_references_to_symbol_under_cursor(cx: &mut Context) {
} }
pub fn compute_inlay_hints_for_all_views(editor: &mut Editor, jobs: &mut crate::job::Jobs) { pub fn compute_inlay_hints_for_all_views(editor: &mut Editor, jobs: &mut crate::job::Jobs) {
if !editor.config().lsp.display_inlay_hints { let config = editor.config();
let unobtrusive_statusline = config.statusline.unobtrusive;
if !config.lsp.display_inlay_hints {
return; return;
} }
@ -1248,7 +1252,7 @@ pub fn compute_inlay_hints_for_all_views(editor: &mut Editor, jobs: &mut crate::
Some(doc) => doc, Some(doc) => doc,
None => continue, None => continue,
}; };
if let Some(callback) = compute_inlay_hints_for_view(view, doc) { if let Some(callback) = compute_inlay_hints_for_view(view, doc, unobtrusive_statusline) {
jobs.callback(callback); jobs.callback(callback);
} }
} }
@ -1257,6 +1261,7 @@ pub fn compute_inlay_hints_for_all_views(editor: &mut Editor, jobs: &mut crate::
fn compute_inlay_hints_for_view( fn compute_inlay_hints_for_view(
view: &View, view: &View,
doc: &Document, doc: &Document,
unobtrusive_statusline: bool,
) -> Option<std::pin::Pin<Box<impl Future<Output = Result<crate::job::Callback, anyhow::Error>>>>> { ) -> Option<std::pin::Pin<Box<impl Future<Output = Result<crate::job::Callback, anyhow::Error>>>>> {
let view_id = view.id; let view_id = view.id;
let doc_id = view.doc; let doc_id = view.doc;
@ -1272,7 +1277,7 @@ fn compute_inlay_hints_for_view(
// will not show half the view with hints and half without while still being faster // will not show half the view with hints and half without while still being faster
// than computing all the hints for the full file (which could be dozens of time // than computing all the hints for the full file (which could be dozens of time
// longer than the view is). // longer than the view is).
let view_height = view.inner_height(); let view_height = view.inner_height(unobtrusive_statusline);
let first_visible_line = let first_visible_line =
doc_text.char_to_line(doc.view_offset(view_id).anchor.min(doc_text.len_chars())); doc_text.char_to_line(doc.view_offset(view_id).anchor.min(doc_text.len_chars()));
let first_line = first_visible_line.saturating_sub(view_height); let first_line = first_visible_line.saturating_sub(view_height);

@ -127,12 +127,13 @@ fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
cx.jobs.callback(callback); cx.jobs.callback(callback);
} else { } else {
// Otherwise, just open the file // Otherwise, just open the file
let unobtrusive_statusline = cx.editor.config().statusline.unobtrusive;
let _ = cx.editor.open(&path, Action::Replace)?; let _ = cx.editor.open(&path, Action::Replace)?;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let pos = Selection::point(pos_at_coords(doc.text().slice(..), pos, true)); let pos = Selection::point(pos_at_coords(doc.text().slice(..), pos, true));
doc.set_selection(view.id, pos); doc.set_selection(view.id, pos);
// does not affect opening a buffer without pos // does not affect opening a buffer without pos
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center, unobtrusive_statusline);
} }
} }
Ok(()) Ok(())
@ -1277,10 +1278,12 @@ fn reload(
return Ok(()); return Ok(());
} }
let scrolloff = cx.editor.config().scrolloff; let config = cx.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.reload(view, &cx.editor.diff_providers).map(|_| { doc.reload(view, &cx.editor.diff_providers).map(|_| {
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
})?; })?;
if let Some(path) = doc.path() { if let Some(path) = doc.path() {
cx.editor cx.editor
@ -1300,7 +1303,9 @@ fn reload_all(
return Ok(()); return Ok(());
} }
let scrolloff = cx.editor.config().scrolloff; let config = cx.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let view_id = view!(cx.editor).id; let view_id = view!(cx.editor).id;
let docs_view_ids: Vec<(DocumentId, Vec<ViewId>)> = cx let docs_view_ids: Vec<(DocumentId, Vec<ViewId>)> = cx
@ -1342,7 +1347,7 @@ fn reload_all(
for view_id in view_ids { for view_id in view_ids {
let view = view_mut!(cx.editor, view_id); let view = view_mut!(cx.editor, view_id);
if view.doc.eq(&doc_id) { if view.doc.eq(&doc_id) {
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
} }
} }
} }
@ -1578,6 +1583,8 @@ fn tree_sitter_highlight_name(
) -> Option<helix_core::syntax::Highlight> { ) -> Option<helix_core::syntax::Highlight> {
use helix_core::syntax::HighlightEvent; use helix_core::syntax::HighlightEvent;
let config = &cx.editor.config();
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let syntax = doc.syntax()?; let syntax = doc.syntax()?;
let text = doc.text().slice(..); let text = doc.text().slice(..);
@ -1590,7 +1597,7 @@ fn tree_sitter_highlight_name(
let row = text.char_to_line(doc.view_offset(view.id).anchor.min(text.len_chars())); let row = text.char_to_line(doc.view_offset(view.id).anchor.min(text.len_chars()));
// Saturating subs to make it inclusive zero indexing. // Saturating subs to make it inclusive zero indexing.
let last_line = text.len_lines().saturating_sub(1); let last_line = text.len_lines().saturating_sub(1);
let height = view.inner_area(doc).height; let height = view.inner_area(doc, unobtrusive_statusline).height;
let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line); let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
let start = text.line_to_byte(row.min(last_line)); let start = text.line_to_byte(row.min(last_line));
let end = text.line_to_byte(last_visible_line + 1); let end = text.line_to_byte(last_visible_line + 1);
@ -1795,11 +1802,14 @@ fn tutor(
fn abort_goto_line_number_preview(cx: &mut compositor::Context) { fn abort_goto_line_number_preview(cx: &mut compositor::Context) {
if let Some(last_selection) = cx.editor.last_selection.take() { if let Some(last_selection) = cx.editor.last_selection.take() {
let scrolloff = cx.editor.config().scrolloff; let config = cx.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.set_selection(view.id, last_selection); doc.set_selection(view.id, last_selection);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
} }
} }
@ -1812,12 +1822,14 @@ fn update_goto_line_number_preview(
doc.selection(view.id).clone() doc.selection(view.id).clone()
}); });
let scrolloff = cx.editor.config().scrolloff; let config = cx.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let line = args[0].parse::<usize>()?; let line = args[0].parse::<usize>()?;
goto_line_without_jumplist(cx.editor, NonZeroUsize::new(line)); goto_line_without_jumplist(cx.editor, NonZeroUsize::new(line));
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
Ok(()) Ok(())
} }
@ -2062,7 +2074,9 @@ fn sort_impl(
_args: &[Cow<str>], _args: &[Cow<str>],
reverse: bool, reverse: bool,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let scrolloff = cx.editor.config().scrolloff; let config = cx.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let text = doc.text().slice(..); let text = doc.text().slice(..);
@ -2088,7 +2102,7 @@ fn sort_impl(
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
Ok(()) Ok(())
} }
@ -2102,7 +2116,9 @@ fn reflow(
return Ok(()); return Ok(());
} }
let scrolloff = cx.editor.config().scrolloff; let config = cx.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let cfg_text_width: usize = cx.editor.config().text_width; let cfg_text_width: usize = cx.editor.config().text_width;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
@ -2129,7 +2145,7 @@ fn reflow(
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
Ok(()) Ok(())
} }
@ -2329,7 +2345,9 @@ fn reset_diff_change(
ensure!(args.is_empty(), ":reset-diff-change takes no arguments"); ensure!(args.is_empty(), ":reset-diff-change takes no arguments");
let editor = &mut cx.editor; let editor = &mut cx.editor;
let scrolloff = editor.config().scrolloff; let config = editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
let Some(handle) = doc.diff_handle() else { let Some(handle) = doc.diff_handle() else {
@ -2363,7 +2381,7 @@ fn reset_diff_change(
drop(diff); // make borrow check happy drop(diff); // make borrow check happy
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
cx.editor.set_status(format!( cx.editor.set_status(format!(
"Reset {changes} change{}", "Reset {changes} change{}",
if changes == 1 { "" } else { "s" } if changes == 1 { "" } else { "s" }
@ -2492,7 +2510,9 @@ fn read(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
return Ok(()); return Ok(());
} }
let scrolloff = cx.editor.config().scrolloff; let config = cx.editor.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
ensure!(!args.is_empty(), "file name is expected"); ensure!(!args.is_empty(), "file name is expected");
@ -2515,7 +2535,7 @@ fn read(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
let transaction = Transaction::insert(doc.text(), selection, contents); let transaction = Transaction::insert(doc.text(), selection, contents);
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
doc.append_changes_to_history(view); doc.append_changes_to_history(view);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline);
Ok(()) Ok(())
} }

@ -88,10 +88,11 @@ impl EditorView {
surface: &mut Surface, surface: &mut Surface,
is_focused: bool, is_focused: bool,
) { ) {
let inner = view.inner_area(doc);
let area = view.area; let area = view.area;
let theme = &editor.theme; let theme = &editor.theme;
let config = editor.config(); let config = editor.config();
let unobtrusive_statusline = config.statusline.unobtrusive;
let inner = view.inner_area(doc, unobtrusive_statusline);
let view_offset = doc.view_offset(view.id); let view_offset = doc.view_offset(view.id);
@ -103,7 +104,7 @@ impl EditorView {
} }
if is_focused && config.cursorcolumn { if is_focused && config.cursorcolumn {
Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations); Self::highlight_cursorcolumn(doc, view, surface, theme, inner, &text_annotations, config.statusline.unobtrusive);
} }
// Set DAP highlights, if needed. // Set DAP highlights, if needed.
@ -240,7 +241,7 @@ impl EditorView {
let mut context = let mut context =
statusline::RenderContext::new(editor, doc, view, is_focused, &self.spinners); statusline::RenderContext::new(editor, doc, view, is_focused, &self.spinners);
statusline::render(&mut context, statusline_area, surface); statusline::render(&mut context, statusline_area, surface, unobtrusive_statusline);
} }
pub fn render_rulers( pub fn render_rulers(
@ -812,6 +813,7 @@ impl EditorView {
theme: &Theme, theme: &Theme,
viewport: Rect, viewport: Rect,
text_annotations: &TextAnnotations, text_annotations: &TextAnnotations,
unobtrusive_statusline: bool,
) { ) {
let text = doc.text().slice(..); let text = doc.text().slice(..);
@ -826,7 +828,7 @@ impl EditorView {
.or_else(|| theme.try_get_exact("ui.cursorcolumn")) .or_else(|| theme.try_get_exact("ui.cursorcolumn"))
.unwrap_or_else(|| theme.get("ui.cursorline.secondary")); .unwrap_or_else(|| theme.get("ui.cursorline.secondary"));
let inner_area = view.inner_area(doc); let inner_area = view.inner_area(doc, unobtrusive_statusline);
let selection = doc.selection(view.id); let selection = doc.selection(view.id);
let view_offset = doc.view_offset(view.id); let view_offset = doc.view_offset(view.id);
@ -1122,6 +1124,7 @@ impl EditorView {
row, row,
column, column,
ignore_virtual_text, ignore_virtual_text,
editor.config().statusline.unobtrusive,
) )
.map(|pos| (pos, view.id)) .map(|pos| (pos, view.id))
}) })
@ -1193,7 +1196,7 @@ impl EditorView {
MouseEventKind::Drag(MouseButton::Left) => { MouseEventKind::Drag(MouseButton::Left) => {
let (view, doc) = current!(cxt.editor); let (view, doc) = current!(cxt.editor);
let pos = match view.pos_at_screen_coords(doc, row, column, true) { let pos = match view.pos_at_screen_coords(doc, row, column, true, config.statusline.unobtrusive) {
Some(pos) => pos, Some(pos) => pos,
None => return EventResult::Ignored(None), None => return EventResult::Ignored(None),
}; };
@ -1340,7 +1343,7 @@ impl Component for EditorView {
let config = cx.editor.config(); let config = cx.editor.config();
let mode = cx.editor.mode(); let mode = cx.editor.mode();
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, config.scrolloff); view.ensure_cursor_in_view(doc, config.scrolloff, config.statusline.unobtrusive);
// Store a history state if not in insert mode. Otherwise wait till we exit insert // Store a history state if not in insert mode. Otherwise wait till we exit insert
// to include any edits to the paste in the history state. // to include any edits to the paste in the history state.
@ -1434,7 +1437,7 @@ impl Component for EditorView {
let mode = cx.editor.mode(); let mode = cx.editor.mode();
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, config.scrolloff); view.ensure_cursor_in_view(doc, config.scrolloff, config.statusline.unobtrusive);
// Store a history state if not in insert mode. This also takes care of // Store a history state if not in insert mode. This also takes care of
// committing changes when leaving insert mode. // committing changes when leaving insert mode.

@ -131,7 +131,7 @@ pub fn raw_regex_prompt(
fun(cx, regex, input, event); fun(cx, regex, input, event);
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, config.scrolloff); view.ensure_cursor_in_view(doc, config.scrolloff, config.statusline.unobtrusive);
} }
Err(err) => { Err(err) => {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);

@ -49,14 +49,16 @@ pub struct RenderBuffer<'a> {
pub right: Spans<'a>, pub right: Spans<'a>,
} }
pub fn render(context: &mut RenderContext, viewport: Rect, surface: &mut Surface) { pub fn render(context: &mut RenderContext, viewport: Rect, surface: &mut Surface, unobtrusive_statusline: bool) {
let base_style = if context.focused { let base_style = if context.focused {
context.editor.theme.get("ui.statusline") context.editor.theme.get("ui.statusline")
} else { } else {
context.editor.theme.get("ui.statusline.inactive") context.editor.theme.get("ui.statusline.inactive")
}; };
if !unobtrusive_statusline {
surface.set_style(viewport.with_height(1), base_style); surface.set_style(viewport.with_height(1), base_style);
}
let write_left = |context: &mut RenderContext, text, style| { let write_left = |context: &mut RenderContext, text, style| {
append(&mut context.parts.left, text, &base_style, style) append(&mut context.parts.left, text, &base_style, style)

@ -464,6 +464,7 @@ pub struct SearchConfig {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
pub struct StatusLineConfig { pub struct StatusLineConfig {
pub unobtrusive: bool,
pub left: Vec<StatusLineElement>, pub left: Vec<StatusLineElement>,
pub center: Vec<StatusLineElement>, pub center: Vec<StatusLineElement>,
pub right: Vec<StatusLineElement>, pub right: Vec<StatusLineElement>,
@ -476,6 +477,7 @@ impl Default for StatusLineConfig {
use StatusLineElement as E; use StatusLineElement as E;
Self { Self {
unobtrusive: false,
left: vec![ left: vec![
E::Mode, E::Mode,
E::Spinner, E::Spinner,
@ -1538,12 +1540,14 @@ impl Editor {
let doc = doc_mut!(self, &view.doc); let doc = doc_mut!(self, &view.doc);
view.sync_changes(doc); view.sync_changes(doc);
view.gutters = config.gutters.clone(); view.gutters = config.gutters.clone();
view.ensure_cursor_in_view(doc, config.scrolloff) view.ensure_cursor_in_view(doc, config.scrolloff, config.statusline.unobtrusive)
} }
} }
fn replace_document_in_view(&mut self, current_view: ViewId, doc_id: DocumentId) { fn replace_document_in_view(&mut self, current_view: ViewId, doc_id: DocumentId) {
let scrolloff = self.config().scrolloff; let config = self.config();
let scrolloff = config.scrolloff;
let unobtrusive_statusline = config.statusline.unobtrusive;
let view = self.tree.get_mut(current_view); let view = self.tree.get_mut(current_view);
view.doc = doc_id; view.doc = doc_id;
@ -1553,7 +1557,7 @@ impl Editor {
view.sync_changes(doc); view.sync_changes(doc);
doc.mark_as_focused(); doc.mark_as_focused();
view.ensure_cursor_in_view(doc, scrolloff) view.ensure_cursor_in_view(doc, scrolloff, unobtrusive_statusline)
} }
pub fn switch(&mut self, id: DocumentId, action: Action) { pub fn switch(&mut self, id: DocumentId, action: Action) {
@ -1915,7 +1919,7 @@ impl Editor {
let config = self.config(); let config = self.config();
let view = self.tree.get(id); let view = self.tree.get(id);
let doc = doc_mut!(self, &view.doc); let doc = doc_mut!(self, &view.doc);
view.ensure_cursor_in_view(doc, config.scrolloff) view.ensure_cursor_in_view(doc, config.scrolloff, config.statusline.unobtrusive)
} }
#[inline] #[inline]
@ -2005,8 +2009,9 @@ impl Editor {
pub fn cursor(&self) -> (Option<Position>, CursorKind) { pub fn cursor(&self) -> (Option<Position>, CursorKind) {
let config = self.config(); let config = self.config();
let (view, doc) = current_ref!(self); let (view, doc) = current_ref!(self);
if let Some(mut pos) = self.cursor_cache.get(view, doc) { let unobtrusive_statusline = config.statusline.unobtrusive;
let inner = view.inner_area(doc); if let Some(mut pos) = self.cursor_cache.get(view, doc, unobtrusive_statusline) {
let inner = view.inner_area(doc, unobtrusive_statusline);
pos.col += inner.x as usize; pos.col += inner.x as usize;
pos.row += inner.y as usize; pos.row += inner.y as usize;
let cursorkind = config.cursor_shape.from_mode(self.mode); let cursorkind = config.cursor_shape.from_mode(self.mode);
@ -2205,14 +2210,14 @@ fn try_restore_indent(doc: &mut Document, view: &mut View) {
pub struct CursorCache(Cell<Option<Option<Position>>>); pub struct CursorCache(Cell<Option<Option<Position>>>);
impl CursorCache { impl CursorCache {
pub fn get(&self, view: &View, doc: &Document) -> Option<Position> { pub fn get(&self, view: &View, doc: &Document, unobtrusive_statusline: bool) -> Option<Position> {
if let Some(pos) = self.0.get() { if let Some(pos) = self.0.get() {
return pos; return pos;
} }
let text = doc.text().slice(..); let text = doc.text().slice(..);
let cursor = doc.selection(view.id).primary().cursor(text); let cursor = doc.selection(view.id).primary().cursor(text);
let res = view.screen_coords_at_pos(doc, text, cursor); let res = view.screen_coords_at_pos(doc, text, cursor, unobtrusive_statusline);
self.set(res); self.set(res);
res res
} }

@ -147,8 +147,10 @@ pub fn line_numbers<'doc>(
) -> GutterFn<'doc> { ) -> GutterFn<'doc> {
let text = doc.text().slice(..); let text = doc.text().slice(..);
let width = line_numbers_width(view, doc); let width = line_numbers_width(view, doc);
let config = editor.config();
let unobtrusive_statusline = config.statusline.unobtrusive;
let last_line_in_view = view.estimate_last_doc_line(doc); let last_line_in_view = view.estimate_last_doc_line(doc, unobtrusive_statusline);
// Whether to draw the line number for the last line of the // Whether to draw the line number for the last line of the
// document or not. We only draw it if it's not an empty line. // document or not. We only draw it if it's not an empty line.
@ -161,7 +163,7 @@ pub fn line_numbers<'doc>(
.text() .text()
.char_to_line(doc.selection(view.id).primary().cursor(text)); .char_to_line(doc.selection(view.id).primary().cursor(text));
let line_number = editor.config().line_number; let line_number = config.line_number;
let mode = editor.mode; let mode = editor.mode;
Box::new( Box::new(

@ -68,6 +68,7 @@ pub fn jump_to_stack_frame(editor: &mut Editor, frame: &helix_dap::StackFrame) {
return; return;
} }
let unobtrusive_statusline = editor.config().statusline.unobtrusive;
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
let text_end = doc.text().len_chars().saturating_sub(1); let text_end = doc.text().len_chars().saturating_sub(1);
@ -79,7 +80,7 @@ pub fn jump_to_stack_frame(editor: &mut Editor, frame: &helix_dap::StackFrame) {
let selection = Selection::single(start.min(text_end), end.min(text_end)); let selection = Selection::single(start.min(text_end), end.min(text_end));
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center, unobtrusive_statusline);
} }
pub fn breakpoints_changed( pub fn breakpoints_changed(

@ -47,10 +47,10 @@ pub enum Align {
Bottom, Bottom,
} }
pub fn align_view(doc: &mut Document, view: &View, align: Align) { pub fn align_view(doc: &mut Document, view: &View, align: Align, unobtrusive_statusline: bool) {
let doc_text = doc.text().slice(..); let doc_text = doc.text().slice(..);
let cursor = doc.selection(view.id).primary().cursor(doc_text); let cursor = doc.selection(view.id).primary().cursor(doc_text);
let viewport = view.inner_area(doc); let viewport = view.inner_area(doc, unobtrusive_statusline);
let last_line_height = viewport.height.saturating_sub(1); let last_line_height = viewport.height.saturating_sub(1);
let mut view_offset = doc.view_offset(view.id); let mut view_offset = doc.view_offset(view.id);

@ -190,13 +190,33 @@ impl View {
self.docs_access_history.push(id); self.docs_access_history.push(id);
} }
pub fn inner_area(&self, doc: &Document) -> Rect { /// Accounts for height of statusline
pub fn inner_area(&self, doc: &Document, unobtrusive_statusline: bool) -> Rect {
if unobtrusive_statusline {
self.area.clip_left(self.gutter_offset(doc))
} else {
self.area.clip_left(self.gutter_offset(doc)).clip_bottom(1) // -1 for statusline self.area.clip_left(self.gutter_offset(doc)).clip_bottom(1) // -1 for statusline
} }
}
pub fn inner_height(&self) -> usize { /// Accounts for height of statusline
pub fn inner_height(&self, unobtrusive_statusline: bool) -> usize {
if unobtrusive_statusline {
self.area.height.into()
} else {
self.area.clip_bottom(1).height.into() // -1 for statusline self.area.clip_bottom(1).height.into() // -1 for statusline
} }
}
/// Does not account for height of statusline
pub fn inner_area_raw(&self, doc: &Document) -> Rect {
self.area.clip_left(self.gutter_offset(doc))
}
/// Does not account for height of statusline
pub fn inner_height_raw(&self) -> usize {
self.area.height.into()
}
pub fn inner_width(&self, doc: &Document) -> u16 { pub fn inner_width(&self, doc: &Document) -> u16 {
self.area.clip_left(self.gutter_offset(doc)).width self.area.clip_left(self.gutter_offset(doc)).width
@ -225,18 +245,20 @@ impl View {
&self, &self,
doc: &Document, doc: &Document,
scrolloff: usize, scrolloff: usize,
unobtrusive_statusline: bool,
) -> Option<ViewPosition> { ) -> Option<ViewPosition> {
self.offset_coords_to_in_view_center::<false>(doc, scrolloff) self.offset_coords_to_in_view_center::<false>(doc, scrolloff, unobtrusive_statusline)
} }
pub fn offset_coords_to_in_view_center<const CENTERING: bool>( pub fn offset_coords_to_in_view_center<const CENTERING: bool>(
&self, &self,
doc: &Document, doc: &Document,
scrolloff: usize, scrolloff: usize,
unobtrusive_statusline: bool,
) -> Option<ViewPosition> { ) -> Option<ViewPosition> {
let view_offset = doc.get_view_offset(self.id)?; let view_offset = doc.get_view_offset(self.id)?;
let doc_text = doc.text().slice(..); let doc_text = doc.text().slice(..);
let viewport = self.inner_area(doc); let viewport = self.inner_area(doc, unobtrusive_statusline);
let vertical_viewport_end = view_offset.vertical_offset + viewport.height as usize; let vertical_viewport_end = view_offset.vertical_offset + viewport.height as usize;
let text_fmt = doc.text_format(viewport.width, None); let text_fmt = doc.text_format(viewport.width, None);
let annotations = self.text_annotations(doc, None); let annotations = self.text_annotations(doc, None);
@ -333,22 +355,22 @@ impl View {
Some(offset) Some(offset)
} }
pub fn ensure_cursor_in_view(&self, doc: &mut Document, scrolloff: usize) { pub fn ensure_cursor_in_view(&self, doc: &mut Document, scrolloff: usize, unobtrusive_statusline: bool) {
if let Some(offset) = self.offset_coords_to_in_view_center::<false>(doc, scrolloff) { if let Some(offset) = self.offset_coords_to_in_view_center::<false>(doc, scrolloff, unobtrusive_statusline) {
doc.set_view_offset(self.id, offset); doc.set_view_offset(self.id, offset);
} }
} }
pub fn ensure_cursor_in_view_center(&self, doc: &mut Document, scrolloff: usize) { pub fn ensure_cursor_in_view_center(&self, doc: &mut Document, scrolloff: usize, unobtrusive_statusline: bool) {
if let Some(offset) = self.offset_coords_to_in_view_center::<true>(doc, scrolloff) { if let Some(offset) = self.offset_coords_to_in_view_center::<true>(doc, scrolloff, unobtrusive_statusline) {
doc.set_view_offset(self.id, offset); doc.set_view_offset(self.id, offset);
} else { } else {
align_view(doc, self, Align::Center); align_view(doc, self, Align::Center, unobtrusive_statusline);
} }
} }
pub fn is_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) -> bool { pub fn is_cursor_in_view(&mut self, doc: &Document, scrolloff: usize, unobtrusive_statusline: bool) -> bool {
self.offset_coords_to_in_view(doc, scrolloff).is_none() self.offset_coords_to_in_view(doc, scrolloff, unobtrusive_statusline).is_none()
} }
/// Estimates the last visible document line on screen. /// Estimates the last visible document line on screen.
@ -357,20 +379,20 @@ impl View {
/// The actual last visible line may be smaller if softwrapping occurs /// The actual last visible line may be smaller if softwrapping occurs
/// or virtual text lines are visible /// or virtual text lines are visible
#[inline] #[inline]
pub fn estimate_last_doc_line(&self, doc: &Document) -> usize { pub fn estimate_last_doc_line(&self, doc: &Document, unobtrusive_statusline: bool) -> usize {
let doc_text = doc.text().slice(..); let doc_text = doc.text().slice(..);
let line = doc_text.char_to_line(doc.view_offset(self.id).anchor.min(doc_text.len_chars())); let line = doc_text.char_to_line(doc.view_offset(self.id).anchor.min(doc_text.len_chars()));
// Saturating subs to make it inclusive zero indexing. // Saturating subs to make it inclusive zero indexing.
(line + self.inner_height()) (line + self.inner_height(unobtrusive_statusline))
.min(doc_text.len_lines()) .min(doc_text.len_lines())
.saturating_sub(1) .saturating_sub(1)
} }
/// Calculates the last non-empty visual line on screen /// Calculates the last non-empty visual line on screen
#[inline] #[inline]
pub fn last_visual_line(&self, doc: &Document) -> usize { pub fn last_visual_line(&self, doc: &Document, unobtrusive_statusline: bool) -> usize {
let doc_text = doc.text().slice(..); let doc_text = doc.text().slice(..);
let viewport = self.inner_area(doc); let viewport = self.inner_area(doc, unobtrusive_statusline);
let text_fmt = doc.text_format(viewport.width, None); let text_fmt = doc.text_format(viewport.width, None);
let annotations = self.text_annotations(doc, None); let annotations = self.text_annotations(doc, None);
let view_offset = doc.view_offset(self.id); let view_offset = doc.view_offset(self.id);
@ -379,7 +401,7 @@ impl View {
let visual_height = doc.view_offset(self.id).vertical_offset + viewport.height as usize; let visual_height = doc.view_offset(self.id).vertical_offset + viewport.height as usize;
// fast path when the EOF is not visible on the screen, // fast path when the EOF is not visible on the screen,
if self.estimate_last_doc_line(doc) < doc_text.len_lines() - 1 { if self.estimate_last_doc_line(doc, unobtrusive_statusline) < doc_text.len_lines() - 1 {
return visual_height.saturating_sub(1); return visual_height.saturating_sub(1);
} }
@ -408,10 +430,11 @@ impl View {
doc: &Document, doc: &Document,
text: RopeSlice, text: RopeSlice,
pos: usize, pos: usize,
unobtrusive_statusline: bool,
) -> Option<Position> { ) -> Option<Position> {
let view_offset = doc.view_offset(self.id); let view_offset = doc.view_offset(self.id);
let viewport = self.inner_area(doc); let viewport = self.inner_area(doc, unobtrusive_statusline);
let text_fmt = doc.text_format(viewport.width, None); let text_fmt = doc.text_format(viewport.width, None);
let annotations = self.text_annotations(doc, None); let annotations = self.text_annotations(doc, None);
@ -512,8 +535,9 @@ impl View {
fmt: TextFormat, fmt: TextFormat,
annotations: &TextAnnotations, annotations: &TextAnnotations,
ignore_virtual_text: bool, ignore_virtual_text: bool,
unobtrusive_statusline: bool,
) -> Option<usize> { ) -> Option<usize> {
let inner = self.inner_area(doc); let inner = self.inner_area(doc, unobtrusive_statusline);
// 1 for status // 1 for status
if row < inner.top() || row >= inner.bottom() { if row < inner.top() || row >= inner.bottom() {
return None; return None;
@ -572,6 +596,7 @@ impl View {
row: u16, row: u16,
column: u16, column: u16,
ignore_virtual_text: bool, ignore_virtual_text: bool,
unobtrusive_statusline: bool,
) -> Option<usize> { ) -> Option<usize> {
self.text_pos_at_screen_coords( self.text_pos_at_screen_coords(
doc, doc,
@ -580,6 +605,7 @@ impl View {
doc.text_format(self.inner_width(doc), None), doc.text_format(self.inner_width(doc), None),
&self.text_annotations(doc, None), &self.text_annotations(doc, None),
ignore_virtual_text, ignore_virtual_text,
unobtrusive_statusline,
) )
} }
@ -712,7 +738,8 @@ mod tests {
2, 2,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
None None
); );
@ -725,6 +752,7 @@ mod tests {
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true
false,
), ),
None None
); );
@ -736,7 +764,8 @@ mod tests {
2, 2,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
None None
); );
@ -748,7 +777,8 @@ mod tests {
49, 49,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
None None
); );
@ -760,7 +790,8 @@ mod tests {
41, 41,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
None None
); );
@ -772,7 +803,8 @@ mod tests {
81, 81,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
None None
); );
@ -784,7 +816,8 @@ mod tests {
41, 41,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
None None
); );
@ -796,7 +829,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 3, 40 + DEFAULT_GUTTER_OFFSET + 3,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(3) Some(3)
); );
@ -808,7 +842,8 @@ mod tests {
80, 80,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(3) Some(3)
); );
@ -820,7 +855,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 1, 40 + DEFAULT_GUTTER_OFFSET + 1,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(4) Some(4)
); );
@ -832,7 +868,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 4, 40 + DEFAULT_GUTTER_OFFSET + 4,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(5) Some(5)
); );
@ -844,7 +881,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 7, 40 + DEFAULT_GUTTER_OFFSET + 7,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(8) Some(8)
); );
@ -856,7 +894,8 @@ mod tests {
80, 80,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(8) Some(8)
); );
@ -886,7 +925,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET_ONLY_DIAGNOSTICS + 1, 40 + DEFAULT_GUTTER_OFFSET_ONLY_DIAGNOSTICS + 1,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(4) Some(4)
); );
@ -916,7 +956,8 @@ mod tests {
40 + 1, 40 + 1,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(4) Some(4)
); );
@ -941,7 +982,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET, 40 + DEFAULT_GUTTER_OFFSET,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(0) Some(0)
); );
@ -953,7 +995,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 4, 40 + DEFAULT_GUTTER_OFFSET + 4,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(4) Some(4)
); );
@ -964,7 +1007,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 5, 40 + DEFAULT_GUTTER_OFFSET + 5,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(4) Some(4)
); );
@ -976,7 +1020,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 6, 40 + DEFAULT_GUTTER_OFFSET + 6,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(5) Some(5)
); );
@ -988,7 +1033,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 7, 40 + DEFAULT_GUTTER_OFFSET + 7,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(5) Some(5)
); );
@ -1000,7 +1046,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 8, 40 + DEFAULT_GUTTER_OFFSET + 8,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(6) Some(6)
); );
@ -1025,7 +1072,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET, 40 + DEFAULT_GUTTER_OFFSET,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(0) Some(0)
); );
@ -1037,7 +1085,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 1, 40 + DEFAULT_GUTTER_OFFSET + 1,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(1) Some(1)
); );
@ -1049,7 +1098,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 2, 40 + DEFAULT_GUTTER_OFFSET + 2,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(3) Some(3)
); );
@ -1061,7 +1111,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 3, 40 + DEFAULT_GUTTER_OFFSET + 3,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false
), ),
Some(5) Some(5)
); );
@ -1073,7 +1124,8 @@ mod tests {
40 + DEFAULT_GUTTER_OFFSET + 4, 40 + DEFAULT_GUTTER_OFFSET + 4,
TextFormat::default(), TextFormat::default(),
&TextAnnotations::default(), &TextAnnotations::default(),
true true,
false,
), ),
Some(7) Some(7)
); );

Loading…
Cancel
Save