@@ -38,8 +38,6 @@ pub struct EditorView {
3838 autoinfo: Option<Info>,
3939}
4040
41−pub const GUTTER_OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
42−
4341impl Default for EditorView {
4442 fn default() -> Self {
4543 Self::new(Keymaps::default())
@@ -74,15 +72,10 @@ impl EditorView {
7472 loader: &syntax::Loader,
7573 config: &helix_view::editor::Config,
7674 ) {
77− let area = Rect::new(
78− view.area.x + GUTTER_OFFSET,
79− view.area.y,
80− view.area.width - GUTTER_OFFSET,
81− view.area.height.saturating_sub(1),
82− ); // - 1 for statusline
83− let height = view.area.height.saturating_sub(1); // - 1 for statusline
84−
85− let highlights = Self::doc_syntax_highlights(doc, view.offset, height, theme, loader);
75+ let inner = view.inner_area();
76+ let area = view.area;
77+
78+ let highlights = Self::doc_syntax_highlights(doc, view.offset, inner.height, theme, loader);
8679 let highlights = syntax::merge(highlights, Self::doc_diagnostics_highlights(doc, theme));
8780 let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
8881 Box::new(syntax::merge(
@@ -93,11 +86,11 @@ impl EditorView {
9386 Box::new(highlights)
9487 };
9588
96− Self::render_text_highlights(doc, view.offset, area, surface, theme, highlights);
97− Self::render_gutter(doc, view, area, surface, theme, config);
89+ Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
90+ Self::render_gutter(doc, view, view.area, surface, theme, is_focused, config);
9891
9992 if is_focused {
100− Self::render_focused_view_elements(view, doc, area, theme, surface);
93+ Self::render_focused_view_elements(view, doc, inner, theme, surface);
10194 }
10295
10396 // if we're not at the edge of the screen, draw a right border
@@ -113,7 +106,7 @@ impl EditorView {
113106 }
114107 }
115108
116− self.render_diagnostics(doc, view, area, surface, theme);
109+ self.render_diagnostics(doc, view, inner, surface, theme);
117110
118111 let area = Rect::new(
119112 view.area.x,
@@ -369,7 +362,7 @@ impl EditorView {
369362 }
370363 }
371364
372− /// Render brace match, selected line numbers, etc (meant for the focused view only)
365+ /// Render brace match, etc (meant for the focused view only)
373366 pub fn render_focused_view_elements(
374367 view: &View,
375368 doc: &Document,
@@ -377,77 +370,29 @@ impl EditorView {
377370 theme: &Theme,
378371 surface: &mut Surface,
379372 ) {
380− let text = doc.text().slice(..);
381− let selection = doc.selection(view.id);
382− let last_line = view.last_line(doc);
383− let screen = {
384− let start = text.line_to_char(view.offset.row);
385− let end = text.line_to_char(last_line + 1) + 1; // +1 for cursor at end of text.
386− Range::new(start, end)
387− };
388−
389− // render selected linenr(s)
390− let linenr_select: Style = theme
391− .try_get("ui.linenr.selected")
392− .unwrap_or_else(|| theme.get("ui.linenr"));
393−
394− // Whether to draw the line number for the last line of the
395− // document or not. We only draw it if it's not an empty line.
396− let draw_last = text.line_to_byte(last_line) < text.len_bytes();
397−
398− for selection in selection.iter().filter(|range| range.overlaps(&screen)) {
399− let head = view.screen_coords_at_pos(
400− doc,
401− text,
402− if selection.head > selection.anchor {
403− selection.head - 1
404− } else {
405− selection.head
406− },
407− );
408− if let Some(head) = head {
409− // Highlight line number for selected lines.
410− let line_number = view.offset.row + head.row;
411− let line_number_text = if line_number == last_line && !draw_last {
412− " ~".into()
413− } else {
414− format!("{:>5}", line_number + 1)
415− };
416− surface.set_stringn(
417− viewport.x - GUTTER_OFFSET + 1,
418− viewport.y + head.row as u16,
419− line_number_text,
420− 5,
421− linenr_select,
422− );
373+ // Highlight matching braces
374+ if let Some(syntax) = doc.syntax() {
375+ let text = doc.text().slice(..);
376+ use helix_core::match_brackets;
377+ let pos = doc.selection(view.id).primary().cursor(text);
378+
379+ let pos = match_brackets::find(syntax, doc.text(), pos)
380+ .and_then(|pos| view.screen_coords_at_pos(doc, text, pos));
381+
382+ if let Some(pos) = pos {
383+ // ensure col is on screen
384+ if (pos.col as u16) < viewport.width + view.offset.col as u16
385+ && pos.col >= view.offset.col
386+ {
387+ let style = theme.try_get("ui.cursor.match").unwrap_or_else(|| {
388+ Style::default()
389+ .add_modifier(Modifier::REVERSED)
390+ .add_modifier(Modifier::DIM)
391+ });
423392
424− // Highlight matching braces
425− // TODO: set cursor position for IME
426− if let Some(syntax) = doc.syntax() {
427− use helix_core::match_brackets;
428− let pos = doc
429− .selection(view.id)
430− .primary()
431− .cursor(doc.text().slice(..));
432− let pos = match_brackets::find(syntax, doc.text(), pos)
433− .and_then(|pos| view.screen_coords_at_pos(doc, text, pos));
434−
435− if let Some(pos) = pos {
436− // ensure col is on screen
437− if (pos.col as u16) < viewport.width + view.offset.col as u16
438− && pos.col >= view.offset.col
439− {
440− let style = theme.try_get("ui.cursor.match").unwrap_or_else(|| {
441− Style::default()
442− .add_modifier(Modifier::REVERSED)
443− .add_modifier(Modifier::DIM)
444− });
445−
446− surface
447− .get_mut(viewport.x + pos.col as u16, viewport.y + pos.row as u16)
448− .set_style(style);
449− }
450− }
393+ surface
394+ .get_mut(viewport.x + pos.col as u16, viewport.y + pos.row as u16)
395+ .set_style(style);
451396 }
452397 }
453398 }
@@ -460,12 +405,15 @@ impl EditorView {
460405 viewport: Rect,
461406 surface: &mut Surface,
462407 theme: &Theme,
408+ is_focused: bool,
463409 config: &helix_view::editor::Config,
464410 ) {
465411 let text = doc.text().slice(..);
466412 let last_line = view.last_line(doc);
467413
468414 let linenr = theme.get("ui.linenr");
415+ let linenr_select: Style = theme.try_get("ui.linenr.selected").unwrap_or(linenr);
416+
469417 let warning = theme.get("warning");
470418 let error = theme.get("error");
471419 let info = theme.get("info");
@@ -478,11 +426,21 @@ impl EditorView {
478426 let current_line = doc
479427 .text()
480428 .char_to_line(doc.selection(view.id).primary().anchor);
429+
430+ // it's used inside an iterator so the collect isn't needless:
431+ // https://github.com/rust-lang/rust-clippy/issues/6164
432+ #[allow(clippy::clippy::needless_collect)]
433+ let cursors: Vec<_> = doc
434+ .selection(view.id)
435+ .iter()
436+ .map(|range| range.cursor_line(text))
437+ .collect();
438+
481439 for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
482440 use helix_core::diagnostic::Severity;
483441 if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) {
484442 surface.set_stringn(
485− viewport.x - GUTTER_OFFSET,
443+ viewport.x,
486444 viewport.y + i as u16,
487445 "●",
488446 1,
@@ -495,25 +453,27 @@ impl EditorView {
495453 );
496454 }
497455
498− // Line numbers having selections are rendered
499− // differently, further below.
500− let line_number_text = if line == last_line && !draw_last {
456+ let selected = cursors.contains(&line);
457+
458+ let text = if line == last_line && !draw_last {
501459 " ~".into()
502460 } else {
503− match config.line_number {
504− LineNumber::Absolute => format!("{:>5}", line + 1),
505− LineNumber::Relative => {
506− let relative_line = abs_diff(current_line, line);
507− format!("{:>5}", relative_line)
508− }
509− }
461+ let line = match config.line_number {
462+ LineNumber::Absolute => line + 1,
463+ LineNumber::Relative => abs_diff(current_line, line),
464+ };
465+ format!("{:>5}", line)
510466 };
511467 surface.set_stringn(
512− viewport.x + 1 - GUTTER_OFFSET,
468+ viewport.x + 1,
513469 viewport.y + i as u16,
514− line_number_text,
470+ text,
515471 5,
516− linenr,
472+ if selected && is_focused {
473+ linenr_select
474+ } else {
475+ linenr
476+ },
517477 );
518478 }
519479 }