Merge pull request #88 from levineuwirth/session-ux-gutter-t2
feat(ux): diagnostic gutter signs — sub-arc 2 (TUI + GPU)
This commit is contained in:
commit
eb3bb7f064
|
|
@ -239,4 +239,46 @@ the adapter). Both frontends eyeballed for coordinate correctness.
|
|||
Deferred to later sub-arcs: relative/hybrid modes; diagnostic gutter signs
|
||||
(sub-arc 2); the exact gutter padding is a tunable to eyeball.
|
||||
|
||||
**Sub-arc 2 — diagnostic gutter signs (TUI + GPU).**
|
||||
|
||||
Per-line severity signs riding the sub-arc-1 gutter — closing the last
|
||||
deferred Task #23 item. No protocol/daemon change: the per-line severity is
|
||||
already frontend-side (the TUI's diag store, the GPU's `current_decorations`).
|
||||
|
||||
- **Coupling decision.** Signs ride the *line-number* gutter — they show
|
||||
when line numbers are on, and vanish when off (the legacy col-0
|
||||
background marker returns in the TUI's no-gutter mode). A
|
||||
signs-without-numbers mode is deferred; when it lands, the gutter's
|
||||
presence test widens from "line numbers on" to "line numbers OR signs
|
||||
on."
|
||||
- **TUI** (`diag.rs`/`editor.rs`/`view.rs`): `Viewport` gains `gutter_w` so
|
||||
overlays can address the gutter's leading column at `cell_origin.col -
|
||||
gutter_w`. The gutter number pass moved *before* the overlay loop so
|
||||
`DiagnosticView` can draw its sign into the gutter's blanked leading
|
||||
column without the number pass erasing it. The sign is the severity
|
||||
glyph (`E`/`W`/`I`/`H`) colored by `underline_color()`; most-severe wins
|
||||
(the existing `line_markers` map). Extracted `paint_line_markers`.
|
||||
- **GPU** (`pmacs-gpu/main.rs`): `collect_gutter_sign_rects` walks
|
||||
`layout_runs()`, finds the most-severe diagnostic decoration overlapping
|
||||
each line (`diagnostic_severity_rank`, min wins) and pushes a thin
|
||||
severity-colored bar at the gutter's left edge, riding the existing
|
||||
background quad batch. Rendered as a **bar**, not a glyph — the GPU
|
||||
gutter number layer is single-color, so a per-line-colored bar was the
|
||||
clean path; same convention as the TUI, per-frontend rendering (Q#UX7).
|
||||
|
||||
Validated: `fmt` + `clippy --all-targets` clean both flavors; lib tests
|
||||
(incl. the TUI gutter-sign placement test) + 54 pmacs-gpu (incl. a headless
|
||||
sign render test on the adapter). Both frontends eyeballed.
|
||||
|
||||
**Cross-cutting bug fixed en route (PR #87, off the arc):** a bare
|
||||
`--daemon` + a GPU is a *two-frontend* session; `EditorCore::close_active`
|
||||
/ `close_others` operated on the global `windows` set and so closed *other*
|
||||
frontends' windows, dangling their `view.active` and crashing the daemon in
|
||||
`active_window()`. Scoped both to the active frontend's layout. (Surfaced
|
||||
while eyeballing this sub-arc against the two-frontend setup.)
|
||||
|
||||
**Known follow-up (not this arc):** the GPU minibuffer completion-navigation
|
||||
highlight sticks / doesn't wrap on arrow-up; reproduces on the "normal"
|
||||
nav path but not the alternate one. Its own thread.
|
||||
|
||||
<!-- next sub-arcs appended here -->
|
||||
|
|
|
|||
|
|
@ -96,6 +96,12 @@ const GUTTER_GAP_PX: f32 = 10.0;
|
|||
/// Fallback monospace advance in px when no shaped glyph is available to
|
||||
/// measure (0.6 em at the 16px code font).
|
||||
const GUTTER_MONO_ADVANCE_FALLBACK: f32 = 9.6;
|
||||
/// Diagnostic gutter sign (UX gutter sub-arc 2): a thin severity-colored
|
||||
/// bar hugging the gutter's left edge, left of the line numbers — the GPU
|
||||
/// analogue of the TUI's leading-column sign glyph. `X` is its left inset,
|
||||
/// `W` its width; it spans the full line height.
|
||||
const GUTTER_SIGN_X: f32 = 4.0;
|
||||
const GUTTER_SIGN_W: f32 = 4.0;
|
||||
const MINIMAP_BG: [f32; 4] = [0.075, 0.075, 0.105, 0.92];
|
||||
const MINIMAP_DEFAULT_LINE: [f32; 4] = [0.23, 0.23, 0.29, 0.82];
|
||||
const MINIMAP_THUMB_FILL: [f32; 4] = [0.82, 0.82, 0.92, 0.18];
|
||||
|
|
@ -4338,9 +4344,62 @@ impl State {
|
|||
let mut rects = Vec::new();
|
||||
self.collect_own_decoration_rects(&mut rects, &line_offsets, vstart, vend);
|
||||
self.collect_peer_rects(buffer_id, &line_offsets, vstart, vend, &mut rects);
|
||||
self.collect_gutter_sign_rects(&mut rects, &line_offsets, vstart, vend);
|
||||
rects_to_vertex_bytes(&rects, self.config.width, self.config.height)
|
||||
}
|
||||
|
||||
/// Per-visible-line diagnostic sign bars in the gutter (UX gutter
|
||||
/// sub-arc 2): one severity-colored bar at the gutter's left edge for
|
||||
/// each line carrying a diagnostic, most-severe winning. Only when the
|
||||
/// gutter is on, mirroring the TUI (signs ride the line-number gutter).
|
||||
/// The GPU analogue of the TUI's leading-column `E`/`W`/`I`/`H` glyph.
|
||||
fn collect_gutter_sign_rects(
|
||||
&self,
|
||||
rects: &mut Vec<MinimapRect>,
|
||||
line_offsets: &[u64],
|
||||
vstart: u64,
|
||||
vend: u64,
|
||||
) {
|
||||
if !self.line_numbers {
|
||||
return;
|
||||
}
|
||||
let slice_len = vend - vstart;
|
||||
for run in self.buffer.layout_runs() {
|
||||
let line_base = line_offsets.get(run.line_i).copied().unwrap_or(0);
|
||||
let line_end = line_offsets
|
||||
.get(run.line_i + 1)
|
||||
.copied()
|
||||
.unwrap_or(slice_len);
|
||||
let mut best: Option<(u8, [f32; 4])> = None;
|
||||
for d in &self.current_decorations {
|
||||
let Some(rank) = diagnostic_severity_rank(d.kind) else {
|
||||
continue;
|
||||
};
|
||||
let Some((lo, hi)) = clip_rebase_range(d.range.start, d.range.end, vstart, vend)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if hi <= line_base || lo >= line_end {
|
||||
continue; // decoration doesn't touch this line
|
||||
}
|
||||
if best.is_none_or(|(r, _)| rank < r)
|
||||
&& let Some(color) = decoration_kind_to_underline_color(d.kind)
|
||||
{
|
||||
best = Some((rank, color));
|
||||
}
|
||||
}
|
||||
if let Some((_, color)) = best {
|
||||
rects.push(MinimapRect {
|
||||
x: GUTTER_SIGN_X,
|
||||
y: TEXT_TOP + run.line_top,
|
||||
w: GUTTER_SIGN_W,
|
||||
h: run.line_height,
|
||||
color,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Own-window `Selection` washes from `current_decorations`. The
|
||||
/// caret already marks the own cursor, so the own *`CurrentLine`*
|
||||
/// wash is deliberately NOT rendered — a whole-line highlight on
|
||||
|
|
@ -6083,6 +6142,23 @@ fn decoration_kind_to_underline_color(kind: DecorationKind) -> Option<[f32; 4]>
|
|||
}
|
||||
}
|
||||
|
||||
/// Severity rank of a diagnostic decoration kind (UX gutter sub-arc 2):
|
||||
/// `0` = most severe (`Error`) … `3` = least (`Hint`); `None` for
|
||||
/// non-diagnostic kinds. Lets the gutter sign pick the most-severe
|
||||
/// diagnostic touching a line (min rank wins), mirroring the TUI.
|
||||
fn diagnostic_severity_rank(kind: DecorationKind) -> Option<u8> {
|
||||
match kind {
|
||||
DecorationKind::DiagnosticError => Some(0),
|
||||
DecorationKind::DiagnosticWarning => Some(1),
|
||||
DecorationKind::DiagnosticInfo => Some(2),
|
||||
DecorationKind::DiagnosticHint => Some(3),
|
||||
DecorationKind::Selection
|
||||
| DecorationKind::SearchMatch
|
||||
| DecorationKind::SearchMatchActive
|
||||
| DecorationKind::CurrentLine => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Background-bearing companion to
|
||||
/// [`decoration_kind_to_underline_color`]: maps each
|
||||
/// background-needing `DecorationKind` to its quad-pipeline color as
|
||||
|
|
@ -7348,4 +7424,41 @@ mod tests {
|
|||
"the gutter should add ink + shift the text (only {differing} bytes differ)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn headless_diagnostic_gutter_sign_changes_the_frame() {
|
||||
// UX gutter sub-arc 2: with the gutter on, a diagnostic on a line
|
||||
// must add a severity-colored sign bar in the gutter — the frame
|
||||
// must differ from the same gutter with no diagnostics.
|
||||
let text = "alpha\nbeta\ngamma\n";
|
||||
let Some(mut plain) = headless_or_skip(400, 300, text) else {
|
||||
return;
|
||||
};
|
||||
plain.line_numbers = true;
|
||||
plain.current_buffer_id = Some(BufferId::next());
|
||||
plain.view_range = (0, text.len() as u64);
|
||||
let plain_px = plain.render_offscreen();
|
||||
|
||||
let mut with_diag =
|
||||
State::new_headless(400, 300, text).expect("adapter was just available");
|
||||
with_diag.line_numbers = true;
|
||||
with_diag.current_buffer_id = Some(BufferId::next());
|
||||
with_diag.view_range = (0, text.len() as u64);
|
||||
with_diag.current_decorations.push(Decoration {
|
||||
range: ByteRange { start: 0, end: 5 }, // "alpha"
|
||||
kind: DecorationKind::DiagnosticError,
|
||||
});
|
||||
let diag_px = with_diag.render_offscreen();
|
||||
|
||||
assert_eq!(plain_px.len(), diag_px.len());
|
||||
let differing = plain_px
|
||||
.iter()
|
||||
.zip(&diag_px)
|
||||
.filter(|(a, b)| a != b)
|
||||
.count();
|
||||
assert!(
|
||||
differing > 20,
|
||||
"the diagnostic sign bar should add ink ({differing} bytes differ)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
131
src/diag.rs
131
src/diag.rs
|
|
@ -35,7 +35,7 @@ use serde_json::Value;
|
|||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use crate::buffer::Buffer;
|
||||
use crate::cell::{CellCoord, CellGrid, Color, Style, UnderlineStyle};
|
||||
use crate::cell::{CellCoord, CellGrid, Color, Glyph, Style, UnderlineStyle};
|
||||
use crate::overlay::merge_styles;
|
||||
use crate::view::{View, Viewport};
|
||||
|
||||
|
|
@ -572,17 +572,47 @@ impl View for DiagnosticView {
|
|||
}
|
||||
}
|
||||
|
||||
// Paint the column-0 markers last so a marker is visible even
|
||||
// when an underline span also touches column 0 (bg and
|
||||
// underline merge independently).
|
||||
if max_cols > 0 {
|
||||
for (row_offset, severity) in line_markers {
|
||||
let cell = cells.at(CellCoord::new(
|
||||
cell_origin.row + row_offset,
|
||||
cell_origin.col,
|
||||
));
|
||||
cell.style = merge_styles(cell.style, marker_style_for(severity));
|
||||
}
|
||||
// Paint the per-line severity markers last so one is visible even
|
||||
// when an underline span also touches the same cell.
|
||||
paint_line_markers(
|
||||
cells,
|
||||
cell_origin,
|
||||
viewport.gutter_w,
|
||||
max_cols,
|
||||
&line_markers,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Paint one severity marker per diagnostic line (UX gutter sub-arc 2).
|
||||
///
|
||||
/// When the window reserves a gutter (`gutter_w > 0`), draw the severity
|
||||
/// *sign glyph* in the gutter's leading column (`cell_origin.col -
|
||||
/// gutter_w`, i.e. window column 0), colored by severity. Without a gutter,
|
||||
/// fall back to the legacy column-0 *background* marker on the line's first
|
||||
/// text cell — the "fake gutter" that predates a real gutter column.
|
||||
fn paint_line_markers(
|
||||
cells: &mut CellGrid<'_>,
|
||||
cell_origin: CellCoord,
|
||||
gutter_w: u32,
|
||||
max_cols: u32,
|
||||
line_markers: &std::collections::HashMap<u32, DiagnosticSeverity>,
|
||||
) {
|
||||
for (&row_offset, &severity) in line_markers {
|
||||
let row = cell_origin.row + row_offset;
|
||||
if gutter_w > 0 {
|
||||
let cell = cells.at(CellCoord::new(
|
||||
row,
|
||||
cell_origin.col.saturating_sub(gutter_w),
|
||||
));
|
||||
cell.glyph = Glyph::Char(severity.gutter_glyph());
|
||||
cell.style = Style {
|
||||
fg: severity.underline_color(),
|
||||
..Style::default()
|
||||
};
|
||||
} else if max_cols > 0 {
|
||||
let cell = cells.at(CellCoord::new(row, cell_origin.col));
|
||||
cell.style = merge_styles(cell.style, marker_style_for(severity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -968,6 +998,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 10),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
@ -1031,6 +1062,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(3, 10),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
@ -1054,6 +1086,80 @@ mod tests {
|
|||
assert_eq!(grid.get(CellCoord::new(2, 0)).style.bg, Color::Default);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gutter_sign_replaces_the_column_marker_when_a_gutter_is_reserved() {
|
||||
use crate::cell::{Cell, CellSize, Glyph, UnderlineStyle};
|
||||
|
||||
let store = make_shared_store();
|
||||
{
|
||||
let mut guard = store.lock().expect("diag store");
|
||||
guard.set(
|
||||
"file:///a",
|
||||
vec![
|
||||
// Line 0: Hint + Error overlap → the sign shows Error.
|
||||
diag(0, DiagnosticSeverity::Hint, "h"),
|
||||
diag(0, DiagnosticSeverity::Error, "e"),
|
||||
// Line 1: zero-width Warning (invisible to underline).
|
||||
Diagnostic {
|
||||
start_line: 1,
|
||||
start_col: 2,
|
||||
end_line: 1,
|
||||
end_col: 2,
|
||||
severity: DiagnosticSeverity::Warning,
|
||||
message: "w".to_owned(),
|
||||
source: None,
|
||||
code: None,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
let mut buf = Buffer::new(crate::buffer::BufferId::next(), "test.c");
|
||||
buf.apply_edit(crate::buffer::EditOp::Insert {
|
||||
pos: 0,
|
||||
bytes: b"hello\nworld\nclean\n",
|
||||
})
|
||||
.expect("seed buffer");
|
||||
|
||||
// A 2-cell gutter: text is shifted to column 2, signs land at
|
||||
// window column 0 (`cell_origin.col - gutter_w`).
|
||||
let mut view = DiagnosticView::new("file:///a", store);
|
||||
let mut backing = vec![Cell::default(); 30];
|
||||
let mut grid = CellGrid {
|
||||
cells: &mut backing,
|
||||
stride: 10,
|
||||
size: CellSize::new(3, 10),
|
||||
};
|
||||
view.render(
|
||||
&buf,
|
||||
Viewport {
|
||||
buffer_start: 0,
|
||||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 2),
|
||||
cell_size: CellSize::new(3, 8),
|
||||
gutter_w: 2,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
||||
// Line 0: Error sign glyph 'E' in red at the gutter's leading col.
|
||||
assert_eq!(grid.get(CellCoord::new(0, 0)).glyph, Glyph::Char('E'));
|
||||
assert_eq!(grid.get(CellCoord::new(0, 0)).style.fg, Color::Indexed(1));
|
||||
// Line 1: Warning sign 'W' in yellow.
|
||||
assert_eq!(grid.get(CellCoord::new(1, 0)).glyph, Glyph::Char('W'));
|
||||
assert_eq!(grid.get(CellCoord::new(1, 0)).style.fg, Color::Indexed(3));
|
||||
// The legacy background marker on the first *text* cell is NOT
|
||||
// painted when a gutter carries the sign instead.
|
||||
assert_eq!(grid.get(CellCoord::new(0, 2)).style.bg, Color::Default);
|
||||
// The squiggle still lands in the shifted text area (col 2 + 2).
|
||||
assert_eq!(
|
||||
grid.get(CellCoord::new(1, 4)).style.underline,
|
||||
UnderlineStyle::Curly
|
||||
);
|
||||
// Line 2: clean — no sign glyph.
|
||||
assert_eq!(grid.get(CellCoord::new(2, 0)).glyph, Glyph::Char(' '));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_of_line_zero_width_error_squiggles_the_cell_past_eol() {
|
||||
// The missing-comma shape: rust-analyzer anchors "expected
|
||||
|
|
@ -1099,6 +1205,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(2, 10),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1635,16 +1635,20 @@ pub fn paint_frame(
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(rect.origin.row, rect.origin.col + gutter_w),
|
||||
cell_size: crate::cell::CellSize::new(inner_rows, rect.size.cols - gutter_w),
|
||||
gutter_w,
|
||||
};
|
||||
// Composition (T M2.9): base text_view paints first, then
|
||||
// each overlay in attach order. See [`crate::view::View`].
|
||||
// Composition (T M2.9): base text_view paints first, then the
|
||||
// gutter numbers — before the overlays, so a diagnostic overlay
|
||||
// can draw its severity sign into the gutter's leading column
|
||||
// without the gutter's own blank pass erasing it — then each
|
||||
// overlay in attach order. See [`crate::view::View`].
|
||||
window.text_view.render(buf, viewport, grid);
|
||||
for overlay in &mut window.overlays {
|
||||
overlay.render(buf, viewport, grid);
|
||||
}
|
||||
if gutter_w > 0 {
|
||||
paint_line_number_gutter(grid, window, &rect, inner_rows, gutter_w);
|
||||
}
|
||||
for overlay in &mut window.overlays {
|
||||
overlay.render(buf, viewport, grid);
|
||||
}
|
||||
paint_local_selection(grid, buf, window, &rect, inner_rows, gutter_w);
|
||||
// Mode line for this window. Painted last so the line
|
||||
// itself is always visible regardless of overlay activity.
|
||||
|
|
@ -4839,6 +4843,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: rect.origin,
|
||||
cell_size: CellSize::new(rect.size.rows, rect.size.cols),
|
||||
gutter_w: 0,
|
||||
};
|
||||
let mut grid = CellGrid {
|
||||
cells: &mut backing,
|
||||
|
|
@ -4972,6 +4977,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(24, 80),
|
||||
gutter_w: 0,
|
||||
};
|
||||
|
||||
// Two no-op overlays: probe the dispatch cost only.
|
||||
|
|
|
|||
|
|
@ -861,6 +861,7 @@ mod tests {
|
|||
buffer_end: u64::MAX,
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 20),
|
||||
gutter_w: 0,
|
||||
};
|
||||
let registry = state.core.borrow().registry.clone();
|
||||
let reg = registry.borrow();
|
||||
|
|
@ -957,6 +958,7 @@ mod tests {
|
|||
buffer_end: u64::MAX,
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 20),
|
||||
gutter_w: 0,
|
||||
};
|
||||
let registry = state.core.borrow().registry.clone();
|
||||
let reg = registry.borrow();
|
||||
|
|
@ -1075,6 +1077,7 @@ mod tests {
|
|||
buffer_end: u64::MAX,
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 20),
|
||||
gutter_w: 0,
|
||||
};
|
||||
let registry = state.core.borrow().registry.clone();
|
||||
let reg = registry.borrow();
|
||||
|
|
|
|||
|
|
@ -431,6 +431,7 @@ mod tests {
|
|||
buffer_end: u64::MAX,
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(rows, cols),
|
||||
gutter_w: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 10),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
@ -617,6 +618,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 10),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid2,
|
||||
);
|
||||
|
|
@ -657,6 +659,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(rows, cols),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -564,6 +564,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 16),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
@ -591,6 +592,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 16),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
@ -621,6 +623,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(5, 5),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
@ -652,6 +655,7 @@ mod tests {
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: CellCoord::new(0, 0),
|
||||
cell_size: CellSize::new(1, 5),
|
||||
gutter_w: 0,
|
||||
},
|
||||
&mut grid,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -136,6 +136,12 @@ pub struct Viewport {
|
|||
pub cell_origin: CellCoord,
|
||||
/// Number of cells the viewport occupies.
|
||||
pub cell_size: CellSize,
|
||||
/// Width of the line-number gutter reserved to the *left* of
|
||||
/// `cell_origin` (UX gutter arc). `0` when no gutter. Overlays that
|
||||
/// want to draw in the gutter (e.g. the diagnostic sign) reach it at
|
||||
/// `cell_origin.col - gutter_w`; overlays that only touch the text area
|
||||
/// ignore it (the origin is already shifted past the gutter).
|
||||
pub gutter_w: u32,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -495,6 +495,7 @@ fn render_active_window_to_grid(
|
|||
buffer_end: buf.len(),
|
||||
cell_origin: rect.origin,
|
||||
cell_size: CellSize::new(rect.size.rows, rect.size.cols),
|
||||
gutter_w: 0,
|
||||
};
|
||||
let mut grid = CellGrid {
|
||||
cells: &mut backing,
|
||||
|
|
|
|||
Loading…
Reference in New Issue