aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGokul Soumya2021-11-24 06:47:41 +0000
committerGokul Soumya2021-11-24 06:56:49 +0000
commit7961355ba1c0cd521372496c507a31a51b41ddf2 (patch)
tree7aa64278562ac9215d422de66747291eedf1046b
parent21143e8d22c13ead8c1835063acb518aa5a42822 (diff)
Change cursor shape on mode change
Fixes #323. Due to terminal limitations we can only change the shape of the primary cursor.
-rw-r--r--book/src/configuration.md42
-rw-r--r--helix-term/src/ui/editor.rs12
-rw-r--r--helix-view/src/editor.rs42
-rw-r--r--helix-view/src/graphics.rs32
4 files changed, 117 insertions, 11 deletions
diff --git a/book/src/configuration.md b/book/src/configuration.md
index 2ed48d51..a40a8959 100644
--- a/book/src/configuration.md
+++ b/book/src/configuration.md
@@ -5,9 +5,26 @@ To override global configuration parameters, create a `config.toml` file located
* Linux and Mac: `~/.config/helix/config.toml`
* Windows: `%AppData%\helix\config.toml`
+Example config:
+
+```toml
+theme = "onedark"
+
+[editor]
+line-number = "relative"
+mouse = false
+
+[editor.cursor-shape]
+normal = "underline"
+insert = "block"
+
+[editor.file-picker]
+hidden = false
+```
+
## Editor
-`[editor]` section of the config.
+### `[editor]` Section
| Key | Description | Default |
|--|--|---------|
@@ -24,7 +41,28 @@ To override global configuration parameters, create a `config.toml` file located
| `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` |
| `auto-info` | Whether to display infoboxes | `true` |
-`[editor.filepicker]` section of the config. Sets options for file picker and global search. All but the last key listed in the default file-picker configuration below are IgnoreOptions: whether hidden files and files listed within ignore files are ignored by (not visible in) the helix file picker and global search. There is also one other key, `max-depth` available, which is not defined by default.
+### `[editor.cursor-shape]` Section
+
+Defines the shape of cursor in each mode. Note that due to limitations
+of the terminal environment, only the primary cursor can change shape.
+
+| Key | Description | Default |
+| --- | ----------- | -------- |
+| `normal` | Cursor shape in [normal mode][normal mode] | `block` |
+| `insert` | Cursor shape in [insert mode][insert mode] | `bar` |
+| `select` | Cursor shape in [select mode][select mode] | `underline` |
+
+[normal mode]: ./keymap.md#normal-mode
+[insert mode]: ./keymap.md#insert-mode
+[select mode]: ./keymap.md#select--extend-mode
+
+### `[editor.filepicker]` Section
+
+Sets options for file picker and global search. All but the last key listed in
+the default file-picker configuration below are IgnoreOptions: whether hidden
+files and files listed within ignore files are ignored by (not visible in) the
+helix file picker and global search. There is also one other key, `max-depth`
+available, which is not defined by default.
| Key | Description | Default |
|--|--|---------|
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 27d33d22..8ad54dbd 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -250,7 +250,9 @@ impl EditorView {
// Special-case: cursor at end of the rope.
if range.head == range.anchor && range.head == text.len_chars() {
- spans.push((cursor_scope, range.head..range.head + 1));
+ if i != primary_idx {
+ spans.push((cursor_scope, range.head..range.head + 1));
+ }
continue;
}
@@ -259,11 +261,15 @@ impl EditorView {
// Standard case.
let cursor_start = prev_grapheme_boundary(text, range.head);
spans.push((selection_scope, range.anchor..cursor_start));
- spans.push((cursor_scope, cursor_start..range.head));
+ if i != primary_idx {
+ spans.push((cursor_scope, cursor_start..range.head));
+ }
} else {
// Reverse case.
let cursor_end = next_grapheme_boundary(text, range.head);
- spans.push((cursor_scope, range.head..cursor_end));
+ if i != primary_idx {
+ spans.push((cursor_scope, range.head..cursor_end));
+ }
spans.push((selection_scope, cursor_end..range.anchor));
}
}
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index 1ce33760..9c77f270 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -1,6 +1,6 @@
use crate::{
clipboard::{get_clipboard_provider, ClipboardProvider},
- document::SCRATCH_BUFFER_NAME,
+ document::{Mode, SCRATCH_BUFFER_NAME},
graphics::{CursorKind, Rect},
theme::{self, Theme},
tree::{self, Tree},
@@ -9,7 +9,7 @@ use crate::{
use futures_util::future;
use std::{
- collections::BTreeMap,
+ collections::{BTreeMap, HashMap},
io::stdin,
path::{Path, PathBuf},
pin::Pin,
@@ -22,7 +22,7 @@ use anyhow::Error;
pub use helix_core::diagnostic::Severity;
pub use helix_core::register::Registers;
-use helix_core::syntax;
+use helix_core::{hashmap, syntax};
use helix_core::{Position, Selection};
use serde::Deserialize;
@@ -103,6 +103,30 @@ pub struct Config {
/// Whether to display infoboxes. Defaults to true.
pub auto_info: bool,
pub file_picker: FilePickerConfig,
+ /// Shape for cursor in each mode
+ pub cursor_shape: CursorShapeConfig,
+}
+
+#[derive(Debug, Clone, PartialEq, Deserialize)]
+#[serde(transparent)]
+pub struct CursorShapeConfig(HashMap<Mode, CursorKind>);
+
+impl std::ops::Deref for CursorShapeConfig {
+ type Target = HashMap<Mode, CursorKind>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl Default for CursorShapeConfig {
+ fn default() -> Self {
+ Self(hashmap!(
+ Mode::Insert => CursorKind::Bar,
+ Mode::Normal => CursorKind::Block,
+ Mode::Select => CursorKind::Underline,
+ ))
+ }
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
@@ -110,7 +134,6 @@ pub struct Config {
pub enum LineNumber {
/// Show absolute line number
Absolute,
-
/// Show relative line number to the primary cursor
Relative,
}
@@ -135,6 +158,7 @@ impl Default for Config {
completion_trigger_len: 2,
auto_info: true,
file_picker: FilePickerConfig::default(),
+ cursor_shape: CursorShapeConfig::default(),
}
}
}
@@ -594,9 +618,15 @@ impl Editor {
let inner = view.inner_area();
pos.col += inner.x as usize;
pos.row += inner.y as usize;
- (Some(pos), CursorKind::Hidden)
+ let cursorkind = self
+ .config
+ .cursor_shape
+ .get(&doc.mode())
+ .copied()
+ .unwrap_or_default();
+ (Some(pos), cursorkind)
} else {
- (None, CursorKind::Hidden)
+ (None, CursorKind::default())
}
}
diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs
index 0bfca04a..c9dd21e3 100644
--- a/helix-view/src/graphics.rs
+++ b/helix-view/src/graphics.rs
@@ -1,4 +1,6 @@
+use anyhow::{anyhow, Error};
use bitflags::bitflags;
+use serde::de::{self, Deserialize, Deserializer};
use std::{
cmp::{max, min},
str::FromStr,
@@ -17,6 +19,36 @@ pub enum CursorKind {
Hidden,
}
+impl Default for CursorKind {
+ fn default() -> Self {
+ Self::Block
+ }
+}
+
+impl FromStr for CursorKind {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "bar" => Ok(Self::Bar),
+ "block" => Ok(Self::Block),
+ "underline" => Ok(Self::Underline),
+ _ => Err(anyhow!("Invalid cursor '{}'", s)),
+ }
+ }
+}
+
+// toml deserializer doesn't seem to recognize string as enum
+impl<'de> Deserialize<'de> for CursorKind {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let s = String::deserialize(deserializer)?;
+ s.parse().map_err(de::Error::custom)
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Margin {
pub vertical: u16,