Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/ui/prompt.rs')
-rw-r--r--helix-term/src/ui/prompt.rs470
1 files changed, 118 insertions, 352 deletions
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index d2448335..e224180a 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -1,55 +1,36 @@
-use crate::compositor::{Component, Compositor, Context, Event, EventResult};
+use crate::compositor::{Component, Compositor, Context, EventResult};
use crate::{alt, ctrl, key, shift, ui};
-use arc_swap::ArcSwap;
-use helix_core::syntax;
-use helix_view::document::Mode;
+use crossterm::event::Event;
use helix_view::input::KeyEvent;
-use helix_view::keyboard::KeyCode;
-use std::sync::Arc;
+use helix_view::keyboard::{KeyCode, KeyModifiers};
use std::{borrow::Cow, ops::RangeFrom};
-use tui::buffer::Buffer as Surface;
-use tui::text::Span;
-use tui::widgets::{Block, Widget};
+use tui::buffer::{Buffer as Surface, SurfaceExt};
+use tui::widgets::{Block, Borders, Widget};
use helix_core::{
- unicode::segmentation::{GraphemeCursor, UnicodeSegmentation},
- unicode::width::UnicodeWidthStr,
- Position,
+ unicode::segmentation::GraphemeCursor, unicode::width::UnicodeWidthStr, Position,
};
use helix_view::{
graphics::{CursorKind, Margin, Rect},
Editor,
};
-type PromptCharHandler = Box<dyn Fn(&mut Prompt, char, &Context)>;
-
-pub type Completion = (RangeFrom<usize>, Span<'static>);
-type CompletionFn = Box<dyn FnMut(&Editor, &str) -> Vec<Completion>>;
-type CallbackFn = Box<dyn FnMut(&mut Context, &str, PromptEvent)>;
-pub type DocFn = Box<dyn Fn(&str) -> Option<Cow<str>>>;
+pub type Completion = (RangeFrom<usize>, Cow<'static, str>);
pub struct Prompt {
prompt: Cow<'static, str>,
- line: String,
+ pub line: String,
cursor: usize,
- // Fields used for Component callbacks and rendering:
- line_area: Rect,
- anchor: usize,
- truncate_start: bool,
- truncate_end: bool,
- // ---
completion: Vec<Completion>,
selection: Option<usize>,
history_register: Option<char>,
history_pos: Option<usize>,
- completion_fn: CompletionFn,
- callback_fn: CallbackFn,
- pub doc_fn: DocFn,
- next_char_handler: Option<PromptCharHandler>,
- language: Option<(&'static str, Arc<ArcSwap<syntax::Loader>>)>,
+ completion_fn: Box<dyn FnMut(&Editor, &str) -> Vec<Completion>>,
+ callback_fn: Box<dyn FnMut(&mut Context, &str, PromptEvent)>,
+ pub doc_fn: Box<dyn Fn(&str) -> Option<Cow<str>>>,
}
-#[derive(Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, PartialEq)]
pub enum PromptEvent {
/// The prompt input has been updated.
Update,
@@ -75,10 +56,6 @@ pub enum Movement {
None,
}
-fn is_word_sep(c: char) -> bool {
- c == std::path::MAIN_SEPARATOR || c.is_whitespace()
-}
-
impl Prompt {
pub fn new(
prompt: Cow<'static, str>,
@@ -90,10 +67,6 @@ impl Prompt {
prompt,
line: String::new(),
cursor: 0,
- line_area: Rect::default(),
- anchor: 0,
- truncate_start: false,
- truncate_end: false,
completion: Vec::new(),
selection: None,
history_register,
@@ -101,66 +74,15 @@ impl Prompt {
completion_fn: Box::new(completion_fn),
callback_fn: Box::new(callback_fn),
doc_fn: Box::new(|_| None),
- next_char_handler: None,
- language: None,
}
}
- /// Gets the byte index in the input representing the current cursor location.
- #[inline]
- pub(crate) fn position(&self) -> usize {
- self.cursor
- }
-
- pub fn with_line(mut self, line: String, editor: &Editor) -> Self {
- self.set_line(line, editor);
- self
- }
-
- pub fn set_line(&mut self, line: String, editor: &Editor) {
- let cursor = line.len();
- self.line = line;
- self.cursor = cursor;
- self.recalculate_completion(editor);
- }
-
- pub fn with_language(
- mut self,
- language: &'static str,
- loader: Arc<ArcSwap<syntax::Loader>>,
- ) -> Self {
- self.language = Some((language, loader));
- self
- }
-
- pub fn line(&self) -> &String {
- &self.line
- }
-
- pub fn with_history_register(&mut self, history_register: Option<char>) -> &mut Self {
- self.history_register = history_register;
- self
- }
-
- pub(crate) fn history_register(&self) -> Option<char> {
- self.history_register
- }
-
- pub(crate) fn first_history_completion<'a>(
- &'a self,
- editor: &'a Editor,
- ) -> Option<Cow<'a, str>> {
- self.history_register
- .and_then(|reg| editor.registers.first(reg, editor))
- }
-
pub fn recalculate_completion(&mut self, editor: &Editor) {
- self.exit_selection();
self.completion = (self.completion_fn)(editor, &self.line);
}
/// Compute the cursor position after applying movement
- /// Taken from: <https://github.com/wez/wezterm/blob/e0b62d07ca9bf8ce69a61e30a3c20e7abc48ce7e/termwiz/src/lineedit/mod.rs#L516-L611>
+ /// Taken from: https://github.com/wez/wezterm/blob/e0b62d07ca9bf8ce69a61e30a3c20e7abc48ce7e/termwiz/src/lineedit/mod.rs#L516-L611
fn eval_movement(&self, movement: Movement) -> usize {
match movement {
Movement::BackwardChar(rep) => {
@@ -192,7 +114,7 @@ impl Prompt {
let mut found = None;
for prev in (0..char_position - 1).rev() {
- if is_word_sep(char_indices[prev].1) {
+ if char_indices[prev].1.is_whitespace() {
found = Some(prev + 1);
break;
}
@@ -215,14 +137,14 @@ impl Prompt {
for _ in 0..rep {
// Skip any non-whitespace characters
while char_position < char_indices.len()
- && !is_word_sep(char_indices[char_position].1)
+ && !char_indices[char_position].1.is_whitespace()
{
char_position += 1;
}
// Skip any whitespace characters
while char_position < char_indices.len()
- && is_word_sep(char_indices[char_position].1)
+ && char_indices[char_position].1.is_whitespace()
{
char_position += 1;
}
@@ -247,31 +169,32 @@ impl Prompt {
position
}
Movement::StartOfLine => 0,
- Movement::EndOfLine => self.line.len(),
+ Movement::EndOfLine => {
+ let mut cursor =
+ GraphemeCursor::new(self.line.len().saturating_sub(1), self.line.len(), false);
+ if let Ok(Some(pos)) = cursor.next_boundary(&self.line, 0) {
+ pos
+ } else {
+ self.cursor
+ }
+ }
Movement::None => self.cursor,
}
}
pub fn insert_char(&mut self, c: char, cx: &Context) {
- if let Some(handler) = &self.next_char_handler.take() {
- handler(self, c, cx);
-
- self.next_char_handler = None;
- return;
- }
-
self.line.insert(self.cursor, c);
let mut cursor = GraphemeCursor::new(self.cursor, self.line.len(), false);
if let Ok(Some(pos)) = cursor.next_boundary(&self.line, 0) {
self.cursor = pos;
}
self.recalculate_completion(cx.editor);
+ self.exit_selection();
}
- pub fn insert_str(&mut self, s: &str, editor: &Editor) {
+ pub fn insert_str(&mut self, s: &str) {
self.line.insert_str(self.cursor, s);
self.cursor += s.len();
- self.recalculate_completion(editor);
}
pub fn move_cursor(&mut self, movement: Movement) {
@@ -287,89 +210,84 @@ impl Prompt {
self.cursor = self.line.len();
}
- pub fn delete_char_backwards(&mut self, editor: &Editor) {
+ pub fn delete_char_backwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::BackwardChar(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
- self.recalculate_completion(editor);
+ self.exit_selection();
+ self.recalculate_completion(cx.editor);
}
- pub fn delete_char_forwards(&mut self, editor: &Editor) {
+ pub fn delete_char_forwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::ForwardChar(1));
self.line.replace_range(self.cursor..pos, "");
- self.recalculate_completion(editor);
+ self.exit_selection();
+ self.recalculate_completion(cx.editor);
}
- pub fn delete_word_backwards(&mut self, editor: &Editor) {
+ pub fn delete_word_backwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::BackwardWord(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
- self.recalculate_completion(editor);
+ self.exit_selection();
+ self.recalculate_completion(cx.editor);
}
- pub fn delete_word_forwards(&mut self, editor: &Editor) {
+ pub fn delete_word_forwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::ForwardWord(1));
self.line.replace_range(self.cursor..pos, "");
- self.recalculate_completion(editor);
+ self.exit_selection();
+ self.recalculate_completion(cx.editor);
}
- pub fn kill_to_start_of_line(&mut self, editor: &Editor) {
+ pub fn kill_to_start_of_line(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::StartOfLine);
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
- self.recalculate_completion(editor);
+ self.exit_selection();
+ self.recalculate_completion(cx.editor);
}
- pub fn kill_to_end_of_line(&mut self, editor: &Editor) {
+ pub fn kill_to_end_of_line(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::EndOfLine);
self.line.replace_range(self.cursor..pos, "");
- self.recalculate_completion(editor);
+ self.exit_selection();
+ self.recalculate_completion(cx.editor);
}
- pub fn clear(&mut self, editor: &Editor) {
+ pub fn clear(&mut self, cx: &Context) {
self.line.clear();
self.cursor = 0;
- self.recalculate_completion(editor);
+ self.recalculate_completion(cx.editor);
+ self.exit_selection();
}
- pub fn change_history(
- &mut self,
- cx: &mut Context,
- register: char,
- direction: CompletionDirection,
- ) {
- (self.callback_fn)(cx, &self.line, PromptEvent::Abort);
- let mut values = match cx.editor.registers.read(register, cx.editor) {
- Some(values) if values.len() > 0 => values.rev(),
- _ => return,
- };
+ pub fn change_history(&mut self, register: &[String], direction: CompletionDirection) {
+ if register.is_empty() {
+ return;
+ }
- let end = values.len().saturating_sub(1);
+ let end = register.len().saturating_sub(1);
let index = match direction {
CompletionDirection::Forward => self.history_pos.map_or(0, |i| i + 1),
- CompletionDirection::Backward => self
- .history_pos
- .unwrap_or_else(|| values.len())
- .saturating_sub(1),
+ CompletionDirection::Backward => {
+ self.history_pos.unwrap_or(register.len()).saturating_sub(1)
+ }
}
.min(end);
- self.line = values.nth(index).unwrap().to_string();
- // Appease the borrow checker.
- drop(values);
+ self.line = register[index].clone();
self.history_pos = Some(index);
self.move_end();
- (self.callback_fn)(cx, &self.line, PromptEvent::Update);
- self.recalculate_completion(cx.editor);
}
pub fn change_completion_selection(&mut self, direction: CompletionDirection) {
@@ -388,7 +306,7 @@ impl Prompt {
let (range, item) = &self.completion[index];
- self.line.replace_range(range.clone(), &item.content);
+ self.line.replace_range(range.clone(), item);
self.move_end();
}
@@ -401,19 +319,17 @@ impl Prompt {
const BASE_WIDTH: u16 = 30;
impl Prompt {
- pub fn render_prompt(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
+ pub fn render_prompt(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
let theme = &cx.editor.theme;
let prompt_color = theme.get("ui.text");
- let completion_color = theme.get("ui.menu");
+ let completion_color = theme.get("ui.statusline");
let selected_color = theme.get("ui.menu.selected");
- let suggestion_color = theme.get("ui.text.inactive");
- let background = theme.get("ui.background");
// completion
let max_len = self
.completion
.iter()
- .map(|(_, completion)| completion.content.len() as u16)
+ .map(|(_, completion)| completion.len() as u16)
.max()
.unwrap_or(BASE_WIDTH)
.max(BASE_WIDTH);
@@ -421,8 +337,7 @@ impl Prompt {
let cols = std::cmp::max(1, area.width / max_len);
let col_width = (area.width.saturating_sub(cols)) / cols;
- let height = (self.completion.len() as u16)
- .div_ceil(cols)
+ let height = ((self.completion.len() as u16 + cols - 1) / cols)
.min(10) // at most 10 rows (or less)
.min(area.height.saturating_sub(1));
@@ -433,9 +348,9 @@ impl Prompt {
height,
);
- if completion_area.height > 0 && !self.completion.is_empty() {
+ if !self.completion.is_empty() {
let area = completion_area;
- let background = theme.get("ui.menu");
+ let background = theme.get("ui.statusline");
let items = height as usize * cols as usize;
@@ -452,22 +367,18 @@ impl Prompt {
for (i, (_range, completion)) in
self.completion.iter().enumerate().skip(offset).take(items)
{
- let is_selected = Some(i) == self.selection;
-
- let completion_item_style = if is_selected {
- selected_color
+ let color = if Some(i) == self.selection {
+ selected_color // TODO: just invert bg
} else {
- completion_color.patch(completion.style)
+ completion_color
};
-
surface.set_stringn(
area.x + col * (1 + col_width),
area.y + row,
- &completion.content,
+ &completion,
col_width.saturating_sub(1) as usize,
- completion_item_style,
+ color,
);
-
row += 1;
if row > area.height - 1 {
row = 0;
@@ -496,116 +407,36 @@ impl Prompt {
let background = theme.get("ui.help");
surface.clear_with(area, background);
- let block = Block::bordered()
+ let block = Block::default()
// .title(self.title.as_str())
+ .borders(Borders::ALL)
.border_style(background);
- let inner = block.inner(area).inner(Margin::horizontal(1));
+ let inner = block.inner(area).inner(&Margin {
+ vertical: 0,
+ horizontal: 1,
+ });
block.render(area, surface);
text.render(inner, surface, cx);
}
let line = area.height - 1;
- surface.clear_with(area.clip_top(line), background);
// render buffer text
surface.set_string(area.x, area.y + line, &self.prompt, prompt_color);
-
- self.line_area = area
- .clip_left(self.prompt.len() as u16)
- .clip_top(line)
- .clip_right(2);
-
- if self.line.is_empty() {
- // Show the most recently entered value as a suggestion.
- if let Some(suggestion) = self.first_history_completion(cx.editor) {
- surface.set_string(
- self.line_area.x,
- self.line_area.y,
- suggestion,
- suggestion_color,
- );
- }
- } else if let Some((language, loader)) = self.language.as_ref() {
- let mut text: ui::text::Text = crate::ui::markdown::highlighted_code_block(
- &self.line,
- language,
- Some(&cx.editor.theme),
- &loader.load(),
- None,
- )
- .into();
- text.render(self.line_area, surface, cx);
- } else {
- let line_width = self.line_area.width as usize;
-
- if self.line.width() < line_width {
- self.anchor = 0;
- } else if self.cursor <= self.anchor {
- // Ensure the grapheme under the cursor is in view.
- self.anchor = self.line[..self.cursor]
- .grapheme_indices(true)
- .next_back()
- .map(|(i, _)| i)
- .unwrap_or_default();
- } else if self.line[self.anchor..self.cursor].width() > line_width {
- // Set the anchor to the last grapheme cluster before the width is exceeded.
- let mut width = 0;
- self.anchor = self.line[..self.cursor]
- .grapheme_indices(true)
- .rev()
- .find_map(|(idx, g)| {
- width += g.width();
- if width > line_width {
- Some(idx + g.len())
- } else {
- None
- }
- })
- .unwrap();
- }
-
- self.truncate_start = self.anchor > 0;
- self.truncate_end = self.line[self.anchor..].width() > line_width;
-
- // if we keep inserting characters just before the end elipsis, we move the anchor
- // so that those new characters are displayed
- if self.truncate_end && self.line[self.anchor..self.cursor].width() >= line_width {
- // Move the anchor forward by one non-zero-width grapheme.
- self.anchor += self.line[self.anchor..]
- .grapheme_indices(true)
- .find_map(|(idx, g)| {
- if g.width() > 0 {
- Some(idx + g.len())
- } else {
- None
- }
- })
- .unwrap();
- }
-
- surface.set_string_anchored(
- self.line_area.x,
- self.line_area.y,
- self.truncate_start,
- self.truncate_end,
- &self.line.as_str()[self.anchor..],
- line_width,
- |_| prompt_color,
- );
- }
+ surface.set_string(
+ area.x + self.prompt.len() as u16,
+ area.y + line,
+ &self.line,
+ prompt_color,
+ );
}
}
impl Component for Prompt {
- fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
let event = match event {
- Event::Paste(data) => {
- self.insert_str(data, cx.editor);
- self.recalculate_completion(cx.editor);
- return EventResult::Consumed(None);
- }
- Event::Key(event) => *event,
+ Event::Key(event) => event,
Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored(None),
};
@@ -615,39 +446,27 @@ impl Component for Prompt {
compositor.pop();
})));
- match event {
+ match event.into() {
ctrl!('c') | key!(Esc) => {
(self.callback_fn)(cx, &self.line, PromptEvent::Abort);
return close_fn;
}
- alt!('b') | ctrl!(Left) => self.move_cursor(Movement::BackwardWord(1)),
- alt!('f') | ctrl!(Right) => self.move_cursor(Movement::ForwardWord(1)),
+ alt!('b') | alt!(Left) => self.move_cursor(Movement::BackwardWord(1)),
+ alt!('f') | alt!(Right) => self.move_cursor(Movement::ForwardWord(1)),
ctrl!('b') | key!(Left) => self.move_cursor(Movement::BackwardChar(1)),
ctrl!('f') | key!(Right) => self.move_cursor(Movement::ForwardChar(1)),
ctrl!('e') | key!(End) => self.move_end(),
ctrl!('a') | key!(Home) => self.move_start(),
- ctrl!('w') | alt!(Backspace) | ctrl!(Backspace) => {
- self.delete_word_backwards(cx.editor);
- (self.callback_fn)(cx, &self.line, PromptEvent::Update);
- }
- alt!('d') | alt!(Delete) | ctrl!(Delete) => {
- self.delete_word_forwards(cx.editor);
- (self.callback_fn)(cx, &self.line, PromptEvent::Update);
- }
- ctrl!('k') => {
- self.kill_to_end_of_line(cx.editor);
- (self.callback_fn)(cx, &self.line, PromptEvent::Update);
- }
- ctrl!('u') => {
- self.kill_to_start_of_line(cx.editor);
- (self.callback_fn)(cx, &self.line, PromptEvent::Update);
- }
- ctrl!('h') | key!(Backspace) | shift!(Backspace) => {
- self.delete_char_backwards(cx.editor);
+ ctrl!('w') => self.delete_word_backwards(cx),
+ alt!('d') => self.delete_word_forwards(cx),
+ ctrl!('k') => self.kill_to_end_of_line(cx),
+ ctrl!('u') => self.kill_to_start_of_line(cx),
+ ctrl!('h') | key!(Backspace) => {
+ self.delete_char_backwards(cx);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('d') | key!(Delete) => {
- self.delete_char_forwards(cx.editor);
+ self.delete_char_forwards(cx);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('s') => {
@@ -664,58 +483,41 @@ impl Component for Prompt {
);
let line = text.slice(range.from()..range.to()).to_string();
if !line.is_empty() {
- self.insert_str(line.as_str(), cx.editor);
+ self.insert_str(line.as_str());
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
key!(Enter) => {
if self.selection.is_some() && self.line.ends_with(std::path::MAIN_SEPARATOR) {
self.recalculate_completion(cx.editor);
+ self.exit_selection();
} else {
- let last_item = self
- .first_history_completion(cx.editor)
- .map(|entry| entry.to_string())
- .unwrap_or_else(|| String::from(""));
-
- // handle executing with last command in history if nothing entered
- let input = if self.line.is_empty() {
- &last_item
- } else {
- if last_item != self.line {
- // store in history
- if let Some(register) = self.history_register {
- if let Err(err) =
- cx.editor.registers.push(register, self.line.clone())
- {
- cx.editor.set_error(err.to_string());
- }
- };
- }
-
- &self.line
- };
-
- (self.callback_fn)(cx, input, PromptEvent::Validate);
+ (self.callback_fn)(cx, &self.line, PromptEvent::Validate);
+ if let Some(register) = self.history_register {
+ // store in history
+ let register = cx.editor.registers.get_mut(register);
+ register.push(self.line.clone());
+ }
return close_fn;
}
}
ctrl!('p') | key!(Up) => {
if let Some(register) = self.history_register {
- self.change_history(cx, register, CompletionDirection::Backward);
+ let register = cx.editor.registers.get_mut(register);
+ self.change_history(register.read(), CompletionDirection::Backward);
+ (self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
ctrl!('n') | key!(Down) => {
if let Some(register) = self.history_register {
- self.change_history(cx, register, CompletionDirection::Forward);
+ let register = cx.editor.registers.get_mut(register);
+ self.change_history(register.read(), CompletionDirection::Forward);
+ (self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
key!(Tab) => {
self.change_completion_selection(CompletionDirection::Forward);
- // if single completion candidate is a directory list content in completion
- if self.completion.len() == 1 && self.line.ends_with(std::path::MAIN_SEPARATOR) {
- self.recalculate_completion(cx.editor);
- }
(self.callback_fn)(cx, &self.line, PromptEvent::Update)
}
shift!(Tab) => {
@@ -723,31 +525,11 @@ impl Component for Prompt {
(self.callback_fn)(cx, &self.line, PromptEvent::Update)
}
ctrl!('q') => self.exit_selection(),
- ctrl!('r') => {
- self.completion = cx
- .editor
- .registers
- .iter_preview()
- .map(|(ch, preview)| (0.., format!("{} {}", ch, &preview).into()))
- .collect();
- self.next_char_handler = Some(Box::new(|prompt, c, context| {
- prompt.insert_str(
- &context
- .editor
- .registers
- .first(c, context.editor)
- .unwrap_or_default(),
- context.editor,
- );
- }));
- (self.callback_fn)(cx, &self.line, PromptEvent::Update);
- return EventResult::Consumed(None);
- }
- // any char event that's not mapped to any other combo
+ // any char event that's not combined with control or mapped to any other combo
KeyEvent {
code: KeyCode::Char(c),
- modifiers: _,
- } => {
+ modifiers,
+ } if !modifiers.contains(KeyModifiers::CONTROL) => {
self.insert_char(c, cx);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
@@ -761,32 +543,16 @@ impl Component for Prompt {
self.render_prompt(area, surface, cx)
}
- 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());
- }
-
+ fn cursor(&self, area: Rect, _editor: &Editor) -> (Option<Position>, CursorKind) {
let line = area.height as usize - 1;
-
(
- Some(Position::new(area.y as usize + line, col)),
- editor.config().cursor_shape.from_mode(Mode::Insert),
+ Some(Position::new(
+ area.y as usize + line,
+ area.x as usize
+ + self.prompt.len()
+ + UnicodeWidthStr::width(&self.line[..self.cursor]),
+ )),
+ CursorKind::Block,
)
}
}