mirror of https://github.com/helix-editor/helix
Merge 62a655af77
into cb1ecc9128
commit
603feba5f8
|
@ -743,6 +743,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()
|
||||
}
|
||||
|
|
|
@ -351,6 +351,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",
|
||||
|
@ -3012,6 +3013,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);
|
||||
|
@ -4117,7 +4141,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) {
|
||||
|
@ -4139,6 +4163,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;
|
||||
|
@ -4706,7 +4747,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);
|
||||
|
|
|
@ -336,6 +336,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
|
|||
|
||||
"C-a" => increment,
|
||||
"C-x" => decrement,
|
||||
"ins" => insert_mode,
|
||||
});
|
||||
let mut select = normal.clone();
|
||||
select.merge_nodes(keymap!({ "Select mode"
|
||||
|
@ -393,6 +394,28 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
|
|||
"tab" => smart_tab,
|
||||
"S-tab" => insert_tab,
|
||||
|
||||
"up" => move_visual_line_up,
|
||||
"down" => move_visual_line_down,
|
||||
"left" => move_char_left,
|
||||
"right" => move_char_right,
|
||||
"pageup" => page_up,
|
||||
"pagedown" => page_down,
|
||||
"home" => goto_line_start,
|
||||
"end" => goto_line_end_newline,
|
||||
"ins" => overtype_mode,
|
||||
});
|
||||
let overtype = keymap!({ "Overtype mode"
|
||||
"ins" => insert_mode,
|
||||
"esc" => normal_mode,
|
||||
|
||||
"C-s" => commit_undo_checkpoint,
|
||||
|
||||
"C-w" | "A-backspace" => delete_word_backward,
|
||||
"A-d" | "A-del" => delete_word_forward,
|
||||
"C-h" | "backspace" | "S-backspace" => delete_char_backward,
|
||||
"C-d" | "del" => delete_char_forward,
|
||||
"C-j" | "ret" => insert_newline,
|
||||
|
||||
"up" => move_visual_line_up,
|
||||
"down" => move_visual_line_down,
|
||||
"left" => move_char_left,
|
||||
|
@ -406,5 +429,6 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
|
|||
Mode::Normal => normal,
|
||||
Mode::Select => select,
|
||||
Mode::Insert => insert,
|
||||
Mode::Overtype => overtype,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -491,6 +491,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"),
|
||||
}
|
||||
|
@ -498,6 +499,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"),
|
||||
}
|
||||
|
@ -930,6 +932,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 {
|
||||
|
@ -1471,6 +1500,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")),
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ pub enum Mode {
|
|||
Normal = 0,
|
||||
Select = 1,
|
||||
Insert = 2,
|
||||
Overtype = 3,
|
||||
}
|
||||
|
||||
impl Display for Mode {
|
||||
|
@ -74,6 +75,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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +88,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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -534,6 +534,7 @@ impl Default for StatusLineConfig {
|
|||
pub struct ModeConfig {
|
||||
pub normal: String,
|
||||
pub insert: String,
|
||||
pub overtype: String,
|
||||
pub select: String,
|
||||
}
|
||||
|
||||
|
@ -542,6 +543,7 @@ impl Default for ModeConfig {
|
|||
Self {
|
||||
normal: String::from("NOR"),
|
||||
insert: String::from("INS"),
|
||||
overtype: String::from("REP"),
|
||||
select: String::from("SEL"),
|
||||
}
|
||||
}
|
||||
|
@ -617,7 +619,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 {
|
||||
|
@ -636,6 +638,7 @@ impl<'de> Deserialize<'de> for CursorShapeConfig {
|
|||
into_cursor(Mode::Normal),
|
||||
into_cursor(Mode::Select),
|
||||
into_cursor(Mode::Insert),
|
||||
into_cursor(Mode::Overtype),
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
@ -646,7 +649,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))?;
|
||||
}
|
||||
|
@ -655,7 +658,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
|
||||
|
@ -664,7 +667,7 @@ impl std::ops::Deref for CursorShapeConfig {
|
|||
|
||||
impl Default for CursorShapeConfig {
|
||||
fn default() -> Self {
|
||||
Self([CursorKind::Block; 3])
|
||||
Self([CursorKind::Block; 4])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,6 +84,9 @@
|
|||
"ui.statusline.insert" = { fg = "bg0", bg = "statusline2", modifiers = [
|
||||
"bold",
|
||||
] }
|
||||
"ui.statusline.overtype" = { fg = "bg0", bg = "statusline3", modifiers = [
|
||||
"bold",
|
||||
] }
|
||||
"ui.statusline.select" = { fg = "bg0", bg = "blue", modifiers = ["bold"] }
|
||||
"ui.bufferline" = { fg = "grey2", bg = "bg3" }
|
||||
"ui.bufferline.active" = { fg = "bg0", bg = "statusline1", modifiers = [
|
||||
|
|
|
@ -83,6 +83,9 @@
|
|||
"ui.statusline.insert" = { fg = "bg0", bg = "statusline2", modifiers = [
|
||||
"bold",
|
||||
] }
|
||||
"ui.statusline.overtype" = { fg = "bg0", bg = "statusline3", modifiers = [
|
||||
"bold",
|
||||
] }
|
||||
"ui.statusline.select" = { fg = "bg0", bg = "blue", modifiers = ["bold"] }
|
||||
"ui.bufferline" = { fg = "grey2", bg = "bg3" }
|
||||
"ui.bufferline.active" = { fg = "bg0", bg = "statusline1", modifiers = [
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
"ui.statusline" = { fg = "fg1", bg = "bg2" }
|
||||
"ui.statusline.inactive" = { fg = "fg4", bg = "bg2" }
|
||||
"ui.statusline.insert" = { fg = "bg1", bg = "blue1", modifiers = ["bold"] }
|
||||
"ui.statusline.overtype" = { fg = "bg1", bg = "red1", modifiers = ["bold"] }
|
||||
"ui.statusline.normal" = { fg = "bg1", bg = "fg3", modifiers = ["bold"] }
|
||||
"ui.statusline.select" = { fg = "bg1", bg = "orange1", modifiers = ["bold"] }
|
||||
|
||||
|
|
Loading…
Reference in New Issue