add 'overtype' edit mode

pull/11265/head
Noktoborus 4 months ago
parent dbaa636683
commit d42b199ccc

@ -727,6 +727,13 @@ impl Transaction {
}) })
} }
/// Replace text at each selection head.
pub fn replace(doc: &Rope, selection: &Selection, text: Tendril) -> Self {
Self::change_by_selection(doc, selection, |range| {
(range.from(), range.to(), Some(text.clone()))
})
}
pub fn changes_iter(&self) -> ChangeIterator { pub fn changes_iter(&self) -> ChangeIterator {
self.changes.changes_iter() self.changes.changes_iter()
} }

@ -291,6 +291,7 @@ impl MappableCommand {
extend_prev_char, "Extend to previous occurrence of char", extend_prev_char, "Extend to previous occurrence of char",
repeat_last_motion, "Repeat last motion", repeat_last_motion, "Repeat last motion",
replace, "Replace with new char", replace, "Replace with new char",
overtype_mode, "Enter overtype mode",
switch_case, "Switch (toggle) case", switch_case, "Switch (toggle) case",
switch_to_uppercase, "Switch to uppercase", switch_to_uppercase, "Switch to uppercase",
switch_to_lowercase, "Switch to lowercase", switch_to_lowercase, "Switch to lowercase",
@ -2788,6 +2789,29 @@ fn insert_mode(cx: &mut Context) {
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
} }
fn enter_overtype_mode(cx: &mut Context) {
cx.editor.mode = Mode::Overtype;
}
// inserts at the start of each selection
fn overtype_mode(cx: &mut Context) {
enter_overtype_mode(cx);
let (view, doc) = current!(cx.editor);
log::trace!(
"entering replace mode with sel: {:?}, text: {:?}",
doc.selection(view.id),
doc.text().to_string()
);
let selection = doc
.selection(view.id)
.clone()
.transform(|range| Range::new(range.to(), range.from()));
doc.set_selection(view.id, selection);
}
// inserts at the end of each selection // inserts at the end of each selection
fn append_mode(cx: &mut Context) { fn append_mode(cx: &mut Context) {
enter_insert_mode(cx); enter_insert_mode(cx);
@ -3746,7 +3770,7 @@ pub mod insert {
Some(transaction) Some(transaction)
} }
use helix_core::auto_pairs; use helix_core::{auto_pairs, graphemes::nth_next_grapheme_boundary};
use helix_view::editor::SmartTabConfig; use helix_view::editor::SmartTabConfig;
pub fn insert_char(cx: &mut Context, c: char) { pub fn insert_char(cx: &mut Context, c: char) {
@ -3768,6 +3792,23 @@ pub mod insert {
helix_event::dispatch(PostInsertChar { c, cx }); helix_event::dispatch(PostInsertChar { c, cx });
} }
pub fn replace_char(cx: &mut Context, c: char) {
let (view, doc) = current!(cx.editor);
let text = doc.text();
let selection = doc.selection(view.id);
let slice = text.slice(..);
let selection_update = selection.clone().transform(|range| {
let new_pos = nth_next_grapheme_boundary(slice, range.cursor(slice), 1);
range.put_cursor(slice, new_pos, false)
});
let mut t = Tendril::new();
t.push(c);
let transaction = Transaction::replace(text, &selection, t);
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view);
doc.set_selection(view.id, selection_update);
}
pub fn smart_tab(cx: &mut Context) { pub fn smart_tab(cx: &mut Context) {
let (view, doc) = current_ref!(cx.editor); let (view, doc) = current_ref!(cx.editor);
let view_id = view.id; let view_id = view.id;
@ -4276,7 +4317,7 @@ pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) {
let count = cx.count(); let count = cx.count();
let paste = match cx.editor.mode { let paste = match cx.editor.mode {
Mode::Insert | Mode::Select => Paste::Cursor, Mode::Insert | Mode::Select => Paste::Cursor,
Mode::Normal => Paste::Before, Mode::Normal | Mode::Overtype => Paste::Before,
}; };
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
paste_impl(&[contents], doc, view, paste, count, cx.editor.mode); paste_impl(&[contents], doc, view, paste, count, cx.editor.mode);

@ -395,9 +395,13 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"home" => goto_line_start, "home" => goto_line_start,
"end" => goto_line_end_newline, "end" => goto_line_end_newline,
}); });
let overtype = keymap!({ "Overtype mode"
"esc" => normal_mode,
});
hashmap!( hashmap!(
Mode::Normal => normal, Mode::Normal => normal,
Mode::Select => select, Mode::Select => select,
Mode::Insert => insert, Mode::Insert => insert,
Mode::Overtype => overtype,
) )
} }

@ -488,6 +488,7 @@ impl EditorView {
let cursor_scope = match mode { let cursor_scope = match mode {
Mode::Insert => theme.find_scope_index_exact("ui.cursor.insert"), Mode::Insert => theme.find_scope_index_exact("ui.cursor.insert"),
Mode::Overtype => theme.find_scope_index_exact("ui.cursor.overtype"),
Mode::Select => theme.find_scope_index_exact("ui.cursor.select"), Mode::Select => theme.find_scope_index_exact("ui.cursor.select"),
Mode::Normal => theme.find_scope_index_exact("ui.cursor.normal"), Mode::Normal => theme.find_scope_index_exact("ui.cursor.normal"),
} }
@ -495,6 +496,7 @@ impl EditorView {
let primary_cursor_scope = match mode { let primary_cursor_scope = match mode {
Mode::Insert => theme.find_scope_index_exact("ui.cursor.primary.insert"), Mode::Insert => theme.find_scope_index_exact("ui.cursor.primary.insert"),
Mode::Overtype => theme.find_scope_index_exact("ui.cursor.overtype"),
Mode::Select => theme.find_scope_index_exact("ui.cursor.primary.select"), Mode::Select => theme.find_scope_index_exact("ui.cursor.primary.select"),
Mode::Normal => theme.find_scope_index_exact("ui.cursor.primary.normal"), Mode::Normal => theme.find_scope_index_exact("ui.cursor.primary.normal"),
} }
@ -908,6 +910,33 @@ impl EditorView {
None None
} }
fn overtype_mode(&mut self, cx: &mut commands::Context, event: KeyEvent) {
if let Some(keyresult) = self.handle_keymap_event(Mode::Overtype, cx, event) {
match keyresult {
KeymapResult::NotFound => {
if let Some(ch) = event.char() {
commands::insert::replace_char(cx, ch)
}
}
KeymapResult::Cancelled(pending) => {
for ev in pending {
match ev.char() {
Some(ch) => commands::insert::replace_char(cx, ch),
None => {
if let KeymapResult::Matched(command) =
self.keymaps.get(Mode::Overtype, ev)
{
command.execute(cx);
}
}
}
}
}
_ => unreachable!(),
}
}
}
fn insert_mode(&mut self, cx: &mut commands::Context, event: KeyEvent) { fn insert_mode(&mut self, cx: &mut commands::Context, event: KeyEvent) {
if let Some(keyresult) = self.handle_keymap_event(Mode::Insert, cx, event) { if let Some(keyresult) = self.handle_keymap_event(Mode::Insert, cx, event) {
match keyresult { match keyresult {
@ -1406,6 +1435,9 @@ impl Component for EditorView {
self.last_insert.1.push(InsertEvent::Key(key)); self.last_insert.1.push(InsertEvent::Key(key));
} }
} }
Mode::Overtype => {
self.overtype_mode(&mut cx, key);
}
mode => self.command_mode(mode, &mut cx, key), mode => self.command_mode(mode, &mut cx, key),
} }
} }

@ -180,6 +180,7 @@ where
if visible { if visible {
match context.editor.mode() { match context.editor.mode() {
Mode::Insert => &modenames.insert, Mode::Insert => &modenames.insert,
Mode::Overtype => &modenames.overtype,
Mode::Select => &modenames.select, Mode::Select => &modenames.select,
Mode::Normal => &modenames.normal, Mode::Normal => &modenames.normal,
} }
@ -191,6 +192,7 @@ where
if visible && config.color_modes { if visible && config.color_modes {
match context.editor.mode() { match context.editor.mode() {
Mode::Insert => Some(context.editor.theme.get("ui.statusline.insert")), Mode::Insert => Some(context.editor.theme.get("ui.statusline.insert")),
Mode::Overtype => Some(context.editor.theme.get("ui.statusline.overtype")),
Mode::Select => Some(context.editor.theme.get("ui.statusline.select")), Mode::Select => Some(context.editor.theme.get("ui.statusline.select")),
Mode::Normal => Some(context.editor.theme.get("ui.statusline.normal")), Mode::Normal => Some(context.editor.theme.get("ui.statusline.normal")),
} }

@ -55,6 +55,7 @@ pub enum Mode {
Normal = 0, Normal = 0,
Select = 1, Select = 1,
Insert = 2, Insert = 2,
Overtype = 3,
} }
impl Display for Mode { impl Display for Mode {
@ -63,6 +64,7 @@ impl Display for Mode {
Mode::Normal => f.write_str("normal"), Mode::Normal => f.write_str("normal"),
Mode::Select => f.write_str("select"), Mode::Select => f.write_str("select"),
Mode::Insert => f.write_str("insert"), Mode::Insert => f.write_str("insert"),
Mode::Overtype => f.write_str("overtype"),
} }
} }
} }
@ -75,6 +77,7 @@ impl FromStr for Mode {
"normal" => Ok(Mode::Normal), "normal" => Ok(Mode::Normal),
"select" => Ok(Mode::Select), "select" => Ok(Mode::Select),
"insert" => Ok(Mode::Insert), "insert" => Ok(Mode::Insert),
"overtype" => Ok(Mode::Overtype),
_ => bail!("Invalid mode '{}'", s), _ => bail!("Invalid mode '{}'", s),
} }
} }

@ -501,6 +501,7 @@ impl Default for StatusLineConfig {
pub struct ModeConfig { pub struct ModeConfig {
pub normal: String, pub normal: String,
pub insert: String, pub insert: String,
pub overtype: String,
pub select: String, pub select: String,
} }
@ -509,6 +510,7 @@ impl Default for ModeConfig {
Self { Self {
normal: String::from("NOR"), normal: String::from("NOR"),
insert: String::from("INS"), insert: String::from("INS"),
overtype: String::from("REP"),
select: String::from("SEL"), select: String::from("SEL"),
} }
} }
@ -584,7 +586,7 @@ pub enum StatusLineElement {
// Cursor shape is read and used on every rendered frame and so needs // Cursor shape is read and used on every rendered frame and so needs
// to be fast. Therefore we avoid a hashmap and use an enum indexed array. // to be fast. Therefore we avoid a hashmap and use an enum indexed array.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct CursorShapeConfig([CursorKind; 3]); pub struct CursorShapeConfig([CursorKind; 4]);
impl CursorShapeConfig { impl CursorShapeConfig {
pub fn from_mode(&self, mode: Mode) -> CursorKind { pub fn from_mode(&self, mode: Mode) -> CursorKind {
@ -603,6 +605,7 @@ impl<'de> Deserialize<'de> for CursorShapeConfig {
into_cursor(Mode::Normal), into_cursor(Mode::Normal),
into_cursor(Mode::Select), into_cursor(Mode::Select),
into_cursor(Mode::Insert), into_cursor(Mode::Insert),
into_cursor(Mode::Overtype),
])) ]))
} }
} }
@ -613,7 +616,7 @@ impl Serialize for CursorShapeConfig {
S: serde::Serializer, S: serde::Serializer,
{ {
let mut map = serializer.serialize_map(Some(self.len()))?; let mut map = serializer.serialize_map(Some(self.len()))?;
let modes = [Mode::Normal, Mode::Select, Mode::Insert]; let modes = [Mode::Normal, Mode::Select, Mode::Insert, Mode::Overtype];
for mode in modes { for mode in modes {
map.serialize_entry(&mode, &self.from_mode(mode))?; map.serialize_entry(&mode, &self.from_mode(mode))?;
} }
@ -622,7 +625,7 @@ impl Serialize for CursorShapeConfig {
} }
impl std::ops::Deref for CursorShapeConfig { impl std::ops::Deref for CursorShapeConfig {
type Target = [CursorKind; 3]; type Target = [CursorKind; 4];
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@ -631,7 +634,7 @@ impl std::ops::Deref for CursorShapeConfig {
impl Default for CursorShapeConfig { impl Default for CursorShapeConfig {
fn default() -> Self { fn default() -> Self {
Self([CursorKind::Block; 3]) Self([CursorKind::Block; 4])
} }
} }

Loading…
Cancel
Save