added nullspace alignment calculation

pull/11409/head
Stephen Broadley 2 weeks ago
parent 71b0cb65b8
commit 775660ce1f

@ -88,19 +88,19 @@ impl EditorView {
surface: &mut Surface, surface: &mut Surface,
is_focused: bool, is_focused: bool,
) { ) {
let inner = view.inner_area(doc); let areas = view.get_view_areas(doc);
// TODO: use the 'areas' struct for these!
let inner = areas.text;
let area = view.area; let area = view.area;
let theme = &editor.theme; let theme = &editor.theme;
let config = editor.config(); let config = editor.config();
if config.nullspace.enable { if config.nullspace.enable {
if inner.width < view.area.width { // helper:
let null_style = theme fn fill_area(s: &mut Surface, r: Rect, c: char, style: Style) {
.try_get("ui.nullspace") if r.width > 0 && r.height > 0 {
.or_else(|| Some(theme.get("ui.linenr"))) s.set_style(r, style);
.unwrap();
fn fill_area(s: &mut Surface, r: Rect, c: char) {
for y in r.top()..r.bottom() { for y in r.top()..r.bottom() {
for x in r.left()..r.right() { for x in r.left()..r.right() {
let cell = s.get_mut(x, y).unwrap(); let cell = s.get_mut(x, y).unwrap();
@ -109,20 +109,21 @@ impl EditorView {
} }
} }
} }
}
let view_width = inner.width + view.gutter_offset(doc); if areas.left.width > 0 || areas.right.width > 0 {
let (null_l, _, null_r) = area.clip_bottom(1).split_centre_vertical(view_width); let style = theme
.try_get("ui.nullspace")
.or_else(|| Some(theme.get("ui.linenr")))
.unwrap();
// We currently on use the first char in the 'pattern' // We currently on use the first
// but in future I would like to use the whole string // char in the 'pattern'.
// to render nicer patterns. //
let c = config.nullspace.pattern.chars().nth(0).unwrap(); let c = config.nullspace.pattern.chars().nth(0).unwrap();
fill_area(surface, null_l, c); fill_area(surface, areas.left, c, style);
fill_area(surface, null_r, c); fill_area(surface, areas.right, c, style);
surface.set_style(null_l, null_style);
surface.set_style(null_r, null_style);
} }
} }
@ -253,7 +254,7 @@ impl EditorView {
// if we're not at the edge of the screen, draw a right border // if we're not at the edge of the screen, draw a right border
if viewport.right() != view.area.right() { if viewport.right() != view.area.right() {
let x = area.right(); let x = area.right();
let border_style = theme.get("ui.window"); let border_style = theme.try_get("ui.divide").unwrap_or(theme.get("ui.window"));
for y in area.top()..area.bottom() { for y in area.top()..area.bottom() {
surface[(x, y)] surface[(x, y)]
.set_symbol(tui::symbols::line::VERTICAL) .set_symbol(tui::symbols::line::VERTICAL)
@ -265,7 +266,7 @@ impl EditorView {
if config.inline_diagnostics.disabled() if config.inline_diagnostics.disabled()
&& config.end_of_line_diagnostics == DiagnosticFilter::Disable && config.end_of_line_diagnostics == DiagnosticFilter::Disable
{ {
Self::render_diagnostics(doc, view, inner, surface, theme); Self::render_diagnostics(doc, view, surface, theme);
} }
let statusline_area = view let statusline_area = view
@ -745,13 +746,7 @@ impl EditorView {
} }
} }
pub fn render_diagnostics( pub fn render_diagnostics(doc: &Document, view: &View, surface: &mut Surface, theme: &Theme) {
doc: &Document,
view: &View,
viewport: Rect,
surface: &mut Surface,
theme: &Theme,
) {
use helix_core::diagnostic::Severity; use helix_core::diagnostic::Severity;
use tui::{ use tui::{
layout::Alignment, layout::Alignment,
@ -773,27 +768,56 @@ impl EditorView {
let info = theme.get("info"); let info = theme.get("info");
let hint = theme.get("hint"); let hint = theme.get("hint");
let width = 50.min(viewport.width); let areas = view.get_view_areas(doc);
let width = 50.min(areas.text.width);
let height = 15.min(areas.text.height); // .min(text.lines.len() as u16);
// place it in the nullspace if we have room, and on the same // decide where this diagnostic is gonna get rendered
// line as the cursor to make it easier to read
// //
let gutter_size = view.gutter_offset(doc); let mut x = areas.text.right() - width;
let left_nullspace = viewport.left() - view.area.left() - gutter_size; let mut y = areas.text.top() + 1;
let use_nullspace = left_nullspace > width; let mut align = Alignment::Right;
let mut background = theme.get("ui.background");
let background_style = if use_nullspace { // do we have space in the nullspace to render the diagnostic?
theme //
.try_get("ui.nullspace") const PADDING: u16 = 2;
.unwrap_or(theme.get("ui.background")) let width_pad = width + PADDING;
} else {
theme.get("ui.background")
};
if areas.left.width >= width_pad || areas.right.width >= width_pad {
if let Some(pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) {
background = theme
.try_get("ui.nullspace")
.unwrap_or(theme.get("ui.background"));
// decide if we are placing the diagnositcs in the
// left or right nullspace?
//
y = pos.row as u16 + areas.text.top();
x = if areas.left.width >= width_pad {
align = Alignment::Right;
areas.left.right() - PADDING - width
} else {
align = Alignment::Left;
areas.right.left() + PADDING
};
// correct for overflow in y position
//
if y + height > areas.text.bottom() {
y -= (y + height) - areas.text.bottom();
}
}
}
// build diagnostic lines
//
let mut lines = Vec::new(); let mut lines = Vec::new();
for diagnostic in diagnostics { for diagnostic in diagnostics {
let style = Style::reset() let style = Style::reset()
.patch(background_style) .patch(background)
.patch(match diagnostic.severity { .patch(match diagnostic.severity {
Some(Severity::Error) => error, Some(Severity::Error) => error,
Some(Severity::Warning) | None => warning, Some(Severity::Warning) | None => warning,
@ -813,29 +837,12 @@ impl EditorView {
} }
let text = Text::from(lines); let text = Text::from(lines);
let paragraph = Paragraph::new(&text) let paragraph = Paragraph::new(&text)
.alignment(Alignment::Right) .alignment(align)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
let height = 15.min(viewport.height).min(text.lines.len() as u16);
// decide where this diagnostic is gonna get rendered
//
let mut diag_x = viewport.right() - width;
let mut diag_y = viewport.top() + 1;
if use_nullspace {
if let Some(pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) {
diag_x = viewport.left() - width - gutter_size - 2;
diag_y = pos.row as u16 + viewport.top();
// correct for OOB
if diag_y + height > viewport.bottom() {
diag_y -= (diag_y + height) - viewport.bottom();
}
}
}
paragraph.render(Rect::new(diag_x, diag_y, width, height), surface); paragraph.render(Rect::new(x, y, width, height), surface);
} }
/// Apply the highlighting on the lines where a cursor is active /// Apply the highlighting on the lines where a cursor is active

@ -373,6 +373,7 @@ impl Tree {
match &mut node.content { match &mut node.content {
Content::View(view) => { Content::View(view) => {
// debug!!("setting view area {:?}", area); // debug!!("setting view area {:?}", area);
view.container_area = self.area;
view.area = area; view.area = area;
} // TODO: call f() } // TODO: call f()
Content::Container(container) => { Content::Container(container) => {

@ -25,6 +25,20 @@ use std::{
const JUMP_LIST_CAPACITY: usize = 30; const JUMP_LIST_CAPACITY: usize = 30;
#[derive(Debug, Clone, Default)]
pub struct ViewAreas {
pub left: Rect,
pub right: Rect,
pub gutter: Rect,
pub text: Rect,
}
impl ViewAreas {
pub fn has_nullspace(&self) -> bool {
self.left.width > 0 || self.right.width > 0
}
}
type Jump = (DocumentId, Selection); type Jump = (DocumentId, Selection);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -129,6 +143,7 @@ pub struct ViewPosition {
pub struct View { pub struct View {
pub id: ViewId, pub id: ViewId,
pub area: Rect, pub area: Rect,
pub container_area: Rect,
pub doc: DocumentId, pub doc: DocumentId,
pub jumps: JumpList, pub jumps: JumpList,
// documents accessed from this view from the oldest one to last viewed one // documents accessed from this view from the oldest one to last viewed one
@ -173,6 +188,7 @@ impl View {
id: ViewId::default(), id: ViewId::default(),
doc, doc,
area: Rect::default(), // will get calculated upon inserting into tree area: Rect::default(), // will get calculated upon inserting into tree
container_area: Rect::default(), // will get calculated upon inserting into tree
jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
docs_access_history: Vec::new(), docs_access_history: Vec::new(),
last_modified_docs: [None, None], last_modified_docs: [None, None],
@ -190,26 +206,68 @@ impl View {
self.docs_access_history.push(id); self.docs_access_history.push(id);
} }
pub fn inner_area(&self, doc: &Document) -> Rect { pub fn get_view_areas(&self, doc: &Document) -> ViewAreas {
let config = doc.config.load(); let config = doc.config.load();
let gutter_width = self.gutter_offset(doc);
if config.nullspace.enable {
let text_width = config.text_width as u16;
let view_width = gutter_width + text_width;
if self.area.width > view_width { let gutter = self.gutter_offset(doc);
let null_width = (self.area.width - view_width) / 2; let area = self.area.clip_bottom(1); // -1 for status line
// if we are not using nullspace, then we use the whole
// area (without nullspace)
//
if config.nullspace.enable == false {
let (gutter, text) = self.area.split_left(gutter);
return ViewAreas {
gutter,
text,
..Default::default()
};
}
return self // midpoint (with tolerances)
.area //
.clip_left(gutter_width + null_width) let mid_lo = (self.container_area.width / 2) - 2;
.clip_bottom(1) let mid_hi = (self.container_area.width / 2) + 2;
.with_width(text_width.min(self.area.width - gutter_width));
let width = config.text_width as u16 + gutter;
if area.right() < mid_hi {
// align: right
let (left, text) = area.split_right(width);
let (gutter, text) = text.split_left(gutter);
ViewAreas {
left,
right: Rect::default(),
gutter,
text,
}
} else if area.left() > mid_lo {
// align: left
let (text, right) = area.split_left(width);
let (gutter, text) = text.split_left(gutter);
ViewAreas {
left: Rect::default(),
right,
gutter,
text,
}
} else {
// align: center
let (left, text, right) = area.split_centre_vertical(width);
let (gutter, text) = text.split_left(gutter);
ViewAreas {
left,
right,
gutter,
text,
} }
} }
}
self.area.clip_left(gutter_width).clip_bottom(1) // -1 for statusline /// Returns the 'inner area' (the text renderable area) of the view.
///
pub fn inner_area(&self, doc: &Document) -> Rect {
self.get_view_areas(doc).text
} }
pub fn inner_height(&self) -> usize { pub fn inner_height(&self) -> usize {

Loading…
Cancel
Save