1use editor::CursorShape;
2use gpui::keymap::Context;
3use serde::Deserialize;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
6pub enum Mode {
7 Normal,
8 Insert,
9 Visual { line: bool },
10}
11
12impl Default for Mode {
13 fn default() -> Self {
14 Self::Normal
15 }
16}
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
19pub enum Namespace {
20 G,
21}
22
23#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
24pub enum Operator {
25 Namespace(Namespace),
26 Change,
27 Delete,
28 Yank,
29}
30
31#[derive(Default)]
32pub struct VimState {
33 pub mode: Mode,
34 pub operator_stack: Vec<Operator>,
35}
36
37impl VimState {
38 pub fn cursor_shape(&self) -> CursorShape {
39 match self.mode {
40 Mode::Normal => {
41 if self.operator_stack.is_empty() {
42 CursorShape::Block
43 } else {
44 CursorShape::Underscore
45 }
46 }
47 Mode::Visual { .. } => CursorShape::Block,
48 Mode::Insert => CursorShape::Bar,
49 }
50 }
51
52 pub fn vim_controlled(&self) -> bool {
53 !matches!(self.mode, Mode::Insert)
54 }
55
56 pub fn clip_at_line_end(&self) -> bool {
57 match self.mode {
58 Mode::Insert | Mode::Visual { .. } => false,
59 _ => true,
60 }
61 }
62
63 pub fn empty_selections_only(&self) -> bool {
64 !matches!(self.mode, Mode::Visual { .. })
65 }
66
67 pub fn keymap_context_layer(&self) -> Context {
68 let mut context = Context::default();
69 context.map.insert(
70 "vim_mode".to_string(),
71 match self.mode {
72 Mode::Normal => "normal",
73 Mode::Visual { .. } => "visual",
74 Mode::Insert => "insert",
75 }
76 .to_string(),
77 );
78
79 if self.vim_controlled() {
80 context.set.insert("VimControl".to_string());
81 }
82
83 Operator::set_context(self.operator_stack.last(), &mut context);
84
85 context
86 }
87}
88
89impl Operator {
90 pub fn set_context(operator: Option<&Operator>, context: &mut Context) {
91 let operator_context = match operator {
92 Some(Operator::Namespace(Namespace::G)) => "g",
93 Some(Operator::Change) => "c",
94 Some(Operator::Delete) => "d",
95 Some(Operator::Yank) => "y",
96 None => "none",
97 }
98 .to_owned();
99
100 context
101 .map
102 .insert("vim_operator".to_string(), operator_context.to_string());
103 }
104}