Manually draw all block cursors
This commit is contained in:
parent
d4fb1d0633
commit
c0bbadcaaf
4 changed files with 36 additions and 21 deletions
|
@ -188,6 +188,7 @@ These scopes are used for theming the editor interface.
|
|||
| `ui.cursor.insert` | |
|
||||
| `ui.cursor.select` | |
|
||||
| `ui.cursor.match` | Matching bracket etc. |
|
||||
| `ui.cursor.primary` | Cursor with primary selection |
|
||||
| `ui.linenr` | |
|
||||
| `ui.linenr.selected` | |
|
||||
| `ui.statusline` | Statusline |
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit fb23ed9a99da012d86b7a5059b9d8928607cce29
|
||||
Subproject commit 0a3dd53a7fc4b352a538397d054380aaa28be54c
|
|
@ -17,6 +17,7 @@ use helix_core::{
|
|||
};
|
||||
use helix_view::{
|
||||
document::{Mode, SCRATCH_BUFFER_NAME},
|
||||
editor::CursorShapeConfig,
|
||||
graphics::{CursorKind, Modifier, Rect, Style},
|
||||
info::Info,
|
||||
input::KeyEvent,
|
||||
|
@ -79,7 +80,7 @@ impl EditorView {
|
|||
let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
|
||||
Box::new(syntax::merge(
|
||||
highlights,
|
||||
Self::doc_selection_highlights(doc, view, theme),
|
||||
Self::doc_selection_highlights(doc, view, theme, &config.cursor_shape),
|
||||
))
|
||||
} else {
|
||||
Box::new(highlights)
|
||||
|
@ -213,11 +214,16 @@ impl EditorView {
|
|||
doc: &Document,
|
||||
view: &View,
|
||||
theme: &Theme,
|
||||
cursor_shape_config: &CursorShapeConfig,
|
||||
) -> Vec<(usize, std::ops::Range<usize>)> {
|
||||
let text = doc.text().slice(..);
|
||||
let selection = doc.selection(view.id);
|
||||
let primary_idx = selection.primary_index();
|
||||
|
||||
let mode = doc.mode();
|
||||
let cursorkind = cursor_shape_config.from_mode(mode);
|
||||
let cursor_is_block = cursorkind == CursorKind::Block;
|
||||
|
||||
let selection_scope = theme
|
||||
.find_scope_index("ui.selection")
|
||||
.expect("could not find `ui.selection` scope in the theme!");
|
||||
|
@ -225,13 +231,16 @@ impl EditorView {
|
|||
.find_scope_index("ui.cursor")
|
||||
.unwrap_or(selection_scope);
|
||||
|
||||
let cursor_scope = match doc.mode() {
|
||||
let cursor_scope = match mode {
|
||||
Mode::Insert => theme.find_scope_index("ui.cursor.insert"),
|
||||
Mode::Select => theme.find_scope_index("ui.cursor.select"),
|
||||
Mode::Normal => Some(base_cursor_scope),
|
||||
}
|
||||
.unwrap_or(base_cursor_scope);
|
||||
|
||||
let primary_cursor_scope = theme
|
||||
.find_scope_index("ui.cursor.primary")
|
||||
.unwrap_or(cursor_scope);
|
||||
let primary_selection_scope = theme
|
||||
.find_scope_index("ui.selection.primary")
|
||||
.unwrap_or(selection_scope);
|
||||
|
@ -239,16 +248,20 @@ impl EditorView {
|
|||
let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
|
||||
for (i, range) in selection.iter().enumerate() {
|
||||
let selection_is_primary = i == primary_idx;
|
||||
let selection_scope = if selection_is_primary {
|
||||
primary_selection_scope
|
||||
let (cursor_scope, selection_scope) = if selection_is_primary {
|
||||
(primary_cursor_scope, primary_selection_scope)
|
||||
} else {
|
||||
selection_scope
|
||||
(cursor_scope, selection_scope)
|
||||
};
|
||||
|
||||
// Special-case: cursor at end of the rope.
|
||||
if range.head == range.anchor && range.head == text.len_chars() {
|
||||
if !selection_is_primary {
|
||||
// Terminal cursor acts as the primary cursor
|
||||
if !selection_is_primary || cursor_is_block {
|
||||
// Bar and underline cursors are drawn by the terminal
|
||||
// BUG: If the editor area loses focus while having a bar or
|
||||
// underline cursor (eg. when a regex prompt has focus) then
|
||||
// the primary cursor will be invisible. This doesn't happen
|
||||
// with block cursors since we manually draw *all* cursors.
|
||||
spans.push((cursor_scope, range.head..range.head + 1));
|
||||
}
|
||||
continue;
|
||||
|
@ -259,13 +272,13 @@ impl EditorView {
|
|||
// Standard case.
|
||||
let cursor_start = prev_grapheme_boundary(text, range.head);
|
||||
spans.push((selection_scope, range.anchor..cursor_start));
|
||||
if !selection_is_primary {
|
||||
if !selection_is_primary || cursor_is_block {
|
||||
spans.push((cursor_scope, cursor_start..range.head));
|
||||
}
|
||||
} else {
|
||||
// Reverse case.
|
||||
let cursor_end = next_grapheme_boundary(text, range.head);
|
||||
if !selection_is_primary {
|
||||
if !selection_is_primary || cursor_is_block {
|
||||
spans.push((cursor_scope, range.head..cursor_end));
|
||||
}
|
||||
spans.push((selection_scope, cursor_end..range.anchor));
|
||||
|
@ -1140,11 +1153,11 @@ impl Component for EditorView {
|
|||
}
|
||||
|
||||
fn cursor(&self, _area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
|
||||
// match view.doc.mode() {
|
||||
// Mode::Insert => write!(stdout, "\x1B[6 q"),
|
||||
// mode => write!(stdout, "\x1B[2 q"),
|
||||
// };
|
||||
editor.cursor()
|
||||
match editor.cursor() {
|
||||
// All block cursors are drawn manually
|
||||
(pos, CursorKind::Block) => (pos, CursorKind::Hidden),
|
||||
cursor => cursor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,12 @@ pub struct Config {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CursorShapeConfig([CursorKind; 3]);
|
||||
|
||||
impl CursorShapeConfig {
|
||||
pub fn from_mode(&self, mode: Mode) -> CursorKind {
|
||||
self.get(mode as usize).copied().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CursorShapeConfig {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
|
@ -647,12 +653,7 @@ impl Editor {
|
|||
let inner = view.inner_area();
|
||||
pos.col += inner.x as usize;
|
||||
pos.row += inner.y as usize;
|
||||
let cursorkind = self
|
||||
.config
|
||||
.cursor_shape
|
||||
.get(doc.mode() as usize)
|
||||
.copied()
|
||||
.unwrap_or_default();
|
||||
let cursorkind = self.config.cursor_shape.from_mode(doc.mode());
|
||||
(Some(pos), cursorkind)
|
||||
} else {
|
||||
(None, CursorKind::default())
|
||||
|
|
Loading…
Reference in a new issue