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 {
self.changes.changes_iter()
}

@ -291,6 +291,7 @@ impl MappableCommand {
extend_prev_char, "Extend to previous occurrence of char",
repeat_last_motion, "Repeat last motion",
replace, "Replace with new char",
overtype_mode, "Enter overtype mode",
switch_case, "Switch (toggle) case",
switch_to_uppercase, "Switch to uppercase",
switch_to_lowercase, "Switch to lowercase",
@ -2788,6 +2789,29 @@ fn insert_mode(cx: &mut Context) {
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
fn append_mode(cx: &mut Context) {
enter_insert_mode(cx);
@ -3746,7 +3770,7 @@ pub mod insert {
Some(transaction)
}
use helix_core::auto_pairs;
use helix_core::{auto_pairs, graphemes::nth_next_grapheme_boundary};
use helix_view::editor::SmartTabConfig;
pub fn insert_char(cx: &mut Context, c: char) {
@ -3768,6 +3792,23 @@ pub mod insert {
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) {
let (view, doc) = current_ref!(cx.editor);
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 paste = match cx.editor.mode {
Mode::Insert | Mode::Select => Paste::Cursor,
Mode::Normal => Paste::Before,
Mode::Normal | Mode::Overtype => Paste::Before,
};
let (view, doc) = current!(cx.editor);
paste_impl(&[contents], doc, view, paste, count, cx.editor.mode);

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

@ -488,6 +488,7 @@ impl EditorView {
let cursor_scope = match mode {
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::Normal => theme.find_scope_index_exact("ui.cursor.normal"),
}
@ -495,6 +496,7 @@ impl EditorView {
let primary_cursor_scope = match mode {
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::Normal => theme.find_scope_index_exact("ui.cursor.primary.normal"),
}
@ -908,6 +910,33 @@ impl EditorView {
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) {
if let Some(keyresult) = self.handle_keymap_event(Mode::Insert, cx, event) {
match keyresult {
@ -1406,6 +1435,9 @@ impl Component for EditorView {
self.last_insert.1.push(InsertEvent::Key(key));
}
}
Mode::Overtype => {
self.overtype_mode(&mut cx, key);
}
mode => self.command_mode(mode, &mut cx, key),
}
}

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

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

@ -501,6 +501,7 @@ impl Default for StatusLineConfig {
pub struct ModeConfig {
pub normal: String,
pub insert: String,
pub overtype: String,
pub select: String,
}
@ -509,6 +510,7 @@ impl Default for ModeConfig {
Self {
normal: String::from("NOR"),
insert: String::from("INS"),
overtype: String::from("REP"),
select: String::from("SEL"),
}
}
@ -584,7 +586,7 @@ pub enum StatusLineElement {
// 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.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CursorShapeConfig([CursorKind; 3]);
pub struct CursorShapeConfig([CursorKind; 4]);
impl CursorShapeConfig {
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::Select),
into_cursor(Mode::Insert),
into_cursor(Mode::Overtype),
]))
}
}
@ -613,7 +616,7 @@ impl Serialize for CursorShapeConfig {
S: serde::Serializer,
{
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 {
map.serialize_entry(&mode, &self.from_mode(mode))?;
}
@ -622,7 +625,7 @@ impl Serialize for CursorShapeConfig {
}
impl std::ops::Deref for CursorShapeConfig {
type Target = [CursorKind; 3];
type Target = [CursorKind; 4];
fn deref(&self) -> &Self::Target {
&self.0
@ -631,7 +634,7 @@ impl std::ops::Deref for CursorShapeConfig {
impl Default for CursorShapeConfig {
fn default() -> Self {
Self([CursorKind::Block; 3])
Self([CursorKind::Block; 4])
}
}

Loading…
Cancel
Save