Detailed changes
@@ -671,6 +671,18 @@
// 3. Always blink the cursor, ignoring the terminal mode
// "blinking": "on",
"blinking": "terminal_controlled",
+ // Default cursor shape for the terminal.
+ // 1. A block that surrounds the following character
+ // "block"
+ // 2. A vertical bar
+ // "bar"
+ // 3. An underline that runs along the following character
+ // "underscore"
+ // 4. A box drawn around the following character
+ // "hollow"
+ //
+ // Default: not set, defaults to "block"
+ "cursor_shape": null,
// Set whether Alternate Scroll mode (code: ?1007) is active by default.
// Alternate Scroll mode converts mouse scroll events into up / down key
// presses when in the alternate screen (e.g. when running applications
@@ -216,6 +216,7 @@ impl Project {
shell,
env,
Some(settings.blinking),
+ settings.cursor_shape.unwrap_or_default(),
settings.alternate_scroll,
settings.max_scroll_history_lines,
window,
@@ -18,7 +18,9 @@ use alacritty_terminal::{
Config, RenderableCursor, TermMode,
},
tty::{self},
- vte::ansi::{ClearMode, Handler, NamedPrivateMode, PrivateMode},
+ vte::ansi::{
+ ClearMode, CursorStyle as AlacCursorStyle, Handler, NamedPrivateMode, PrivateMode,
+ },
Term,
};
use anyhow::{bail, Result};
@@ -40,7 +42,7 @@ use serde::{Deserialize, Serialize};
use settings::Settings;
use smol::channel::{Receiver, Sender};
use task::{HideStrategy, Shell, TaskId};
-use terminal_settings::{AlternateScroll, TerminalBlink, TerminalSettings};
+use terminal_settings::{AlternateScroll, CursorShape, TerminalBlink, TerminalSettings};
use theme::{ActiveTheme, Theme};
use util::truncate_and_trailoff;
@@ -314,6 +316,7 @@ impl TerminalBuilder {
shell: Shell,
mut env: HashMap<String, String>,
blink_settings: Option<TerminalBlink>,
+ cursor_shape: CursorShape,
alternate_scroll: AlternateScroll,
max_scroll_history_lines: Option<usize>,
window: AnyWindowHandle,
@@ -353,6 +356,7 @@ impl TerminalBuilder {
// Setup Alacritty's env, which modifies the current process's environment
alacritty_terminal::tty::setup_env();
+ let default_cursor_style = AlacCursorStyle::from(cursor_shape);
let scrolling_history = if task.is_some() {
// Tasks like `cargo build --all` may produce a lot of output, ergo allow maximum scrolling.
// After the task finishes, we do not allow appending to that terminal, so small tasks output should not
@@ -365,6 +369,7 @@ impl TerminalBuilder {
};
let config = Config {
scrolling_history,
+ default_cursor_style,
..Config::default()
};
@@ -951,6 +956,10 @@ impl Terminal {
&self.last_content
}
+ pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape) {
+ self.term.lock().set_cursor_style(Some(cursor_shape.into()));
+ }
+
pub fn total_lines(&self) -> usize {
let term = self.term.clone();
let terminal = term.lock_unfair();
@@ -1,3 +1,6 @@
+use alacritty_terminal::vte::ansi::{
+ CursorShape as AlacCursorShape, CursorStyle as AlacCursorStyle,
+};
use collections::HashMap;
use gpui::{
px, AbsoluteLength, AppContext, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString,
@@ -32,6 +35,7 @@ pub struct TerminalSettings {
pub font_weight: Option<FontWeight>,
pub line_height: TerminalLineHeight,
pub env: HashMap<String, String>,
+ pub cursor_shape: Option<CursorShape>,
pub blinking: TerminalBlink,
pub alternate_scroll: AlternateScroll,
pub option_as_meta: bool,
@@ -129,6 +133,11 @@ pub struct TerminalSettingsContent {
///
/// Default: {}
pub env: Option<HashMap<String, String>>,
+ /// Default cursor shape for the terminal.
+ /// Can be "bar", "block", "underscore", or "hollow".
+ ///
+ /// Default: None
+ pub cursor_shape: Option<CursorShape>,
/// Sets the cursor blinking behavior in the terminal.
///
/// Default: terminal_controlled
@@ -282,3 +291,37 @@ pub struct ToolbarContent {
/// Default: true
pub title: Option<bool>,
}
+
+#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum CursorShape {
+ /// Cursor is a block like `█`.
+ #[default]
+ Block,
+ /// Cursor is an underscore like `_`.
+ Underline,
+ /// Cursor is a vertical bar like `⎸`.
+ Bar,
+ /// Cursor is a hollow box like `▯`.
+ Hollow,
+}
+
+impl From<CursorShape> for AlacCursorShape {
+ fn from(value: CursorShape) -> Self {
+ match value {
+ CursorShape::Block => AlacCursorShape::Block,
+ CursorShape::Underline => AlacCursorShape::Underline,
+ CursorShape::Bar => AlacCursorShape::Beam,
+ CursorShape::Hollow => AlacCursorShape::HollowBlock,
+ }
+ }
+}
+
+impl From<CursorShape> for AlacCursorStyle {
+ fn from(value: CursorShape) -> Self {
+ AlacCursorStyle {
+ shape: value.into(),
+ blinking: false,
+ }
+ }
+}
@@ -19,7 +19,7 @@ use terminal::{
index::Point,
term::{search::RegexSearch, TermMode},
},
- terminal_settings::{TerminalBlink, TerminalSettings, WorkingDirectory},
+ terminal_settings::{CursorShape, TerminalBlink, TerminalSettings, WorkingDirectory},
Clear, Copy, Event, MaybeNavigationTarget, Paste, ScrollLineDown, ScrollLineUp, ScrollPageDown,
ScrollPageUp, ScrollToBottom, ScrollToTop, ShowCharacterPalette, TaskStatus, Terminal,
TerminalSize,
@@ -102,6 +102,7 @@ pub struct TerminalView {
//Currently using iTerm bell, show bell emoji in tab until input is received
has_bell: bool,
context_menu: Option<(View<ContextMenu>, gpui::Point<Pixels>, Subscription)>,
+ cursor_shape: CursorShape,
blink_state: bool,
blinking_on: bool,
blinking_paused: bool,
@@ -171,6 +172,9 @@ impl TerminalView {
let focus_out = cx.on_focus_out(&focus_handle, |terminal_view, _event, cx| {
terminal_view.focus_out(cx);
});
+ let cursor_shape = TerminalSettings::get_global(cx)
+ .cursor_shape
+ .unwrap_or_default();
Self {
terminal,
@@ -178,6 +182,7 @@ impl TerminalView {
has_bell: false,
focus_handle,
context_menu: None,
+ cursor_shape,
blink_state: true,
blinking_on: false,
blinking_paused: false,
@@ -255,6 +260,16 @@ impl TerminalView {
fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
let settings = TerminalSettings::get_global(cx);
self.show_title = settings.toolbar.title;
+
+ let new_cursor_shape = settings.cursor_shape.unwrap_or_default();
+ let old_cursor_shape = self.cursor_shape;
+ if old_cursor_shape != new_cursor_shape {
+ self.cursor_shape = new_cursor_shape;
+ self.terminal.update(cx, |term, _| {
+ term.set_cursor_shape(self.cursor_shape);
+ });
+ }
+
cx.notify();
}
@@ -903,7 +918,10 @@ impl TerminalView {
}
fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
- self.terminal.read(cx).focus_in();
+ self.terminal.update(cx, |terminal, _| {
+ terminal.set_cursor_shape(self.cursor_shape);
+ terminal.focus_in();
+ });
self.blink_cursors(self.blink_epoch, cx);
cx.invalidate_character_coordinates();
cx.notify();
@@ -912,6 +930,7 @@ impl TerminalView {
fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
self.terminal.update(cx, |terminal, _| {
terminal.focus_out();
+ terminal.set_cursor_shape(CursorShape::Hollow);
});
cx.notify();
}