@@ -8,7 +8,7 @@ use crate::{
88
99use helix_core::{
1010 coords_at_pos,
11− graphemes::ensure_grapheme_boundary_next,
11+ graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary},
1212 syntax::{self, HighlightEvent},
1313 LineEnding, Position, Range,
1414};
@@ -165,21 +165,18 @@ impl EditorView {
165165 }
166166 .unwrap_or(base_cursor_scope);
167167
168− let primary_selection_scope = theme
169− .find_scope_index("ui.selection.primary")
170− .unwrap_or(selection_scope);
171−
172168 let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
173− // inject selections as highlight scopes
174− let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
175−
176169 // TODO: primary + insert mode patching:
177170 // (ui.cursor.primary).patch(mode).unwrap_or(cursor)
178−
179171 let primary_cursor_scope = theme
180172 .find_scope_index("ui.cursor.primary")
181173 .unwrap_or(cursor_scope);
174+ let primary_selection_scope = theme
175+ .find_scope_index("ui.selection.primary")
176+ .unwrap_or(selection_scope);
182177
178+ // inject selections as highlight scopes
179+ let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
183180 for (i, range) in selections.iter().enumerate() {
184181 let (cursor_scope, selection_scope) = if i == primary_idx {
185182 (primary_cursor_scope, primary_selection_scope)
@@ -187,19 +184,23 @@ impl EditorView {
187184 (cursor_scope, selection_scope)
188185 };
189186
190− if range.head == range.anchor {
187+ // Special-case: cursor at end of the rope.
188+ if range.head == range.anchor && range.head == text.len_chars() {
191189 spans.push((cursor_scope, range.head..range.head + 1));
192190 continue;
193191 }
194192
195− let reverse = range.head < range.anchor;
196−
197− if reverse {
198− spans.push((cursor_scope, range.head..range.head + 1));
199− spans.push((selection_scope, range.head + 1..range.anchor + 1));
193+ let range = range.min_width_1(text);
194+ if range.head > range.anchor {
195+ // Standard case.
196+ let cursor_start = prev_grapheme_boundary(text, range.head);
197+ spans.push((selection_scope, range.anchor..cursor_start));
198+ spans.push((cursor_scope, cursor_start..range.head));
200199 } else {
201− spans.push((selection_scope, range.anchor..range.head));
202− spans.push((cursor_scope, range.head..range.head + 1));
200+ // Reverse case.
201+ let cursor_end = next_grapheme_boundary(text, range.head);
202+ spans.push((cursor_scope, range.head..cursor_end));
203+ spans.push((selection_scope, cursor_end..range.anchor));
203204 }
204205 }
205206
@@ -232,7 +233,10 @@ impl EditorView {
232233 spans.pop();
233234 }
234235 HighlightEvent::Source { start, end } => {
235− let text = text.slice(start..end);
236+ // `unwrap_or_else` part is for off-the-end indices of
237+ // the rope, to allow cursor highlighting at the end
238+ // of the rope.
239+ let text = text.get_slice(start..end).unwrap_or_else(|| " ".into());
236240
237241 use helix_core::graphemes::{grapheme_width, RopeGraphemes};
238242
@@ -301,7 +305,11 @@ impl EditorView {
301305 let info: Style = theme.get("info");
302306 let hint: Style = theme.get("hint");
303307
304− for (i, line) in (view.first_line..last_line).enumerate() {
308+ // Whether to draw the line number for the last line of the
309+ // document or not. We only draw it if it's not an empty line.
310+ let draw_last = text.line_to_byte(last_line) < text.len_bytes();
311+
312+ for (i, line) in (view.first_line..=last_line).enumerate() {
305313 use helix_core::diagnostic::Severity;
306314 if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) {
307315 surface.set_stringn(
@@ -318,11 +326,17 @@ impl EditorView {
318326 );
319327 }
320328
321− // line numbers having selections are rendered differently
329+ // Line numbers having selections are rendered
330+ // differently, further below.
331+ let line_number_text = if line == last_line && !draw_last {
332+ " ~".into()
333+ } else {
334+ format!("{:>5}", line + 1)
335+ };
322336 surface.set_stringn(
323337 viewport.x + 1 - OFFSET,
324338 viewport.y + i as u16,
325− format!("{:>5}", line + 1),
339+ line_number_text,
326340 5,
327341 linenr,
328342 );
@@ -336,7 +350,7 @@ impl EditorView {
336350 if is_focused {
337351 let screen = {
338352 let start = text.line_to_char(view.first_line);
339− let end = text.line_to_char(last_line + 1);
353+ let end = text.line_to_char(last_line + 1) + 1; // +1 for cursor at end of text.
340354 Range::new(start, end)
341355 };
342356
@@ -345,10 +359,17 @@ impl EditorView {
345359 for selection in selection.iter().filter(|range| range.overlaps(&screen)) {
346360 let head = view.screen_coords_at_pos(doc, text, selection.head);
347361 if let Some(head) = head {
362+ // Draw line number for selected lines.
363+ let line_number = view.first_line + head.row;
364+ let line_number_text = if line_number == last_line && !draw_last {
365+ " ~".into()
366+ } else {
367+ format!("{:>5}", line_number + 1)
368+ };
348369 surface.set_stringn(
349370 viewport.x + 1 - OFFSET,
350371 viewport.y + head.row as u16,
351− format!("{:>5}", view.first_line + head.row + 1),
372+ line_number_text,
352373 5,
353374 linenr_select,
354375 );