Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion book/src/editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ The following statusline elements can be configured:
### `[editor.cursor-shape]` Section

Defines the shape of cursor in each mode.
Valid values for these options are `block`, `bar`, `underline`, or `hidden`.
Valid values for these options are `block`, `block-hardware`, `bar`, `underline`, or `hidden`.

> 💡 Due to limitations of the terminal environment, only the primary cursor can
> change shape.
Expand Down
22 changes: 16 additions & 6 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use helix_view::{
align_view,
document::{DocumentOpenError, DocumentSavedEventResult},
editor::{ConfigEvent, EditorEvent},
graphics::Rect,
graphics::{CursorKind, Modifier, Rect},
theme,
tree::Layout,
Align, Editor,
Expand Down Expand Up @@ -288,6 +288,21 @@ impl Application {
self.editor.cursor_cache.reset();

let pos = pos.map(|pos| (pos.col as u16, pos.row as u16));

if kind == CursorKind::BlockHardware {
if let Some((x, y)) = pos {
if let Some(cell) = surface.get_mut(x, y) {
if cell.modifier.contains(Modifier::REVERSED) {
// Themes with reversed cursor colors: remove the reverse modifier
cell.modifier.remove(Modifier::REVERSED);
} else {
// Themes with static cursor colors: swap foreground and background
std::mem::swap(&mut cell.fg, &mut cell.bg);
}
}
}
}

self.terminal.draw(pos, kind).unwrap();
}

Expand Down Expand Up @@ -1271,11 +1286,6 @@ impl Application {
}

fn restore_term(&mut self) -> std::io::Result<()> {
use helix_view::graphics::CursorKind;
self.terminal
.backend_mut()
.show_cursor(CursorKind::Block)
.ok();
self.terminal.restore()
}

Expand Down
16 changes: 3 additions & 13 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,8 @@ impl EditorView {
let primary_idx = selection.primary_index();

let cursorkind = cursor_shape_config.from_mode(mode);
let cursor_is_block = cursorkind == CursorKind::Block;
let cursor_is_block =
cursorkind == CursorKind::BlockSoftware || cursorkind == CursorKind::BlockHardware;

let selection_scope = theme
.find_highlight_exact("ui.selection")
Expand Down Expand Up @@ -1735,18 +1736,7 @@ impl Component for EditorView {
}

fn cursor(&self, _area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
match editor.cursor() {
// all block cursors are drawn manually
(pos, CursorKind::Block) => {
if self.terminal_focused {
(pos, CursorKind::Hidden)
} else {
// use terminal cursor when terminal loses focus
(pos, CursorKind::Underline)
}
}
cursor => cursor,
}
editor.cursor()
}
}

Expand Down
62 changes: 39 additions & 23 deletions helix-term/src/ui/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,44 @@ impl Prompt {
|_| prompt_color,
);
}

// Draw the block cursor
let cursor_kind = cx.editor.config().cursor_shape.from_mode(Mode::Insert);
let cursor_is_block =
cursor_kind == CursorKind::BlockSoftware || cursor_kind == CursorKind::BlockHardware;
if cursor_is_block {
let pos = self.cursor_position(area);
if let Some(cell) = surface.get_mut(pos.col as u16, pos.row as u16) {
let cursor_style = cx.editor.theme.get("ui.cursor.primary");
cell.set_style(cursor_style);
}
}
}

fn cursor_position(&self, area: Rect) -> Position {
let area = area
.clip_left(self.prompt.len() as u16)
.clip_right(if self.prompt.is_empty() { 2 } else { 0 });

let mut col = area.left() as usize + self.line[self.anchor..self.cursor].width();

// ensure the cursor does not go beyond elipses
if self.truncate_end
&& self.line[self.anchor..self.cursor].width() >= self.line_area.width as usize
{
col -= 1;
}

if self.truncate_start && self.cursor == self.anchor {
col += self.line[self.cursor..]
.graphemes(true)
.next()
.map_or(0, |g| g.width());
}

let row = area.y as usize + area.height as usize - 1;

Position::new(row, col)
}
}

Expand Down Expand Up @@ -770,30 +808,8 @@ impl Component for Prompt {
}

fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
let area = area
.clip_left(self.prompt.len() as u16)
.clip_right(if self.prompt.is_empty() { 2 } else { 0 });

let mut col = area.left() as usize + self.line[self.anchor..self.cursor].width();

// ensure the cursor does not go beyond elipses
if self.truncate_end
&& self.line[self.anchor..self.cursor].width() >= self.line_area.width as usize
{
col -= 1;
}

if self.truncate_start && self.cursor == self.anchor {
col += self.line[self.cursor..]
.graphemes(true)
.next()
.map_or(0, |g| g.width());
}

let line = area.height as usize - 1;

(
Some(Position::new(area.y as usize + line, col)),
Some(self.cursor_position(area)),
editor.config().cursor_shape.from_mode(Mode::Insert),
)
}
Expand Down
3 changes: 2 additions & 1 deletion helix-tui/src/backend/crossterm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ where

fn show_cursor(&mut self, kind: CursorKind) -> io::Result<()> {
let shape = match kind {
CursorKind::Block => SetCursorStyle::SteadyBlock,
CursorKind::BlockSoftware => unreachable!(),
CursorKind::BlockHardware => SetCursorStyle::SteadyBlock,
CursorKind::Bar => SetCursorStyle::SteadyBar,
CursorKind::Underline => SetCursorStyle::SteadyUnderScore,
CursorKind::Hidden => unreachable!(),
Expand Down
3 changes: 2 additions & 1 deletion helix-tui/src/backend/termina.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,8 @@ impl Backend for TerminaBackend {

fn show_cursor(&mut self, kind: CursorKind) -> io::Result<()> {
let style = match kind {
CursorKind::Block => CursorStyle::SteadyBlock,
CursorKind::BlockSoftware => unreachable!(),
CursorKind::BlockHardware => CursorStyle::SteadyBlock,
CursorKind::Bar => CursorStyle::SteadyBar,
CursorKind::Underline => CursorStyle::SteadyUnderline,
CursorKind::Hidden => unreachable!(),
Expand Down
3 changes: 2 additions & 1 deletion helix-tui/src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ where
Buffer::empty(options.viewport.area),
],
current: 0,
cursor_kind: CursorKind::Block,
cursor_kind: CursorKind::default(),
viewport: options.viewport,
force_clear: false,
})
Expand Down Expand Up @@ -211,6 +211,7 @@ where

match cursor_kind {
CursorKind::Hidden => self.hide_cursor()?,
CursorKind::BlockSoftware => self.hide_cursor()?,
kind => self.show_cursor(kind)?,
}

Expand Down
2 changes: 1 addition & 1 deletion helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ impl std::ops::Deref for CursorShapeConfig {

impl Default for CursorShapeConfig {
fn default() -> Self {
Self([CursorKind::Block; 3])
Self([CursorKind::default(); 3])
}
}

Expand Down
7 changes: 5 additions & 2 deletions helix-view/src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ const fn byte_from_hex(mut h: [u8; 2]) -> Option<u8> {
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[serde(rename_all = "kebab-case")]
/// UNSTABLE
#[derive(Default)]
pub enum CursorKind {
/// █
#[default]
Block,
#[serde(rename = "block")]
BlockSoftware,
/// █
BlockHardware,
/// |
Bar,
/// _
Expand Down