state.rs

  1use gpui::keymap_matcher::KeymapContext;
  2use language::CursorShape;
  3use serde::{Deserialize, Serialize};
  4use workspace::searchable::Direction;
  5
  6#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
  7pub enum Mode {
  8    Normal,
  9    Insert,
 10    Visual { line: bool },
 11}
 12
 13impl Default for Mode {
 14    fn default() -> Self {
 15        Self::Normal
 16    }
 17}
 18
 19#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
 20pub enum Operator {
 21    Number(usize),
 22    Change,
 23    Delete,
 24    Yank,
 25    Replace,
 26    Object { around: bool },
 27    FindForward { before: bool },
 28    FindBackward { after: bool },
 29}
 30
 31#[derive(Default)]
 32pub struct VimState {
 33    pub mode: Mode,
 34    pub operator_stack: Vec<Operator>,
 35    pub search: SearchState,
 36}
 37
 38pub struct SearchState {
 39    pub direction: Direction,
 40    pub count: usize,
 41    pub initial_query: String,
 42}
 43
 44impl Default for SearchState {
 45    fn default() -> Self {
 46        Self {
 47            direction: Direction::Next,
 48            count: 1,
 49            initial_query: "".to_string(),
 50        }
 51    }
 52}
 53
 54impl VimState {
 55    pub fn cursor_shape(&self) -> CursorShape {
 56        match self.mode {
 57            Mode::Normal => {
 58                if self.operator_stack.is_empty() {
 59                    CursorShape::Block
 60                } else {
 61                    CursorShape::Underscore
 62                }
 63            }
 64            Mode::Visual { .. } => CursorShape::Block,
 65            Mode::Insert => CursorShape::Bar,
 66        }
 67    }
 68
 69    pub fn vim_controlled(&self) -> bool {
 70        !matches!(self.mode, Mode::Insert)
 71            || matches!(
 72                self.operator_stack.last(),
 73                Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. })
 74            )
 75    }
 76
 77    pub fn clip_at_line_end(&self) -> bool {
 78        !matches!(self.mode, Mode::Insert | Mode::Visual { .. })
 79    }
 80
 81    pub fn empty_selections_only(&self) -> bool {
 82        !matches!(self.mode, Mode::Visual { .. })
 83    }
 84
 85    pub fn keymap_context_layer(&self) -> KeymapContext {
 86        let mut context = KeymapContext::default();
 87        context.add_identifier("VimEnabled");
 88        context.add_key(
 89            "vim_mode",
 90            match self.mode {
 91                Mode::Normal => "normal",
 92                Mode::Visual { .. } => "visual",
 93                Mode::Insert => "insert",
 94            },
 95        );
 96
 97        if self.vim_controlled() {
 98            context.add_identifier("VimControl");
 99        }
100
101        let active_operator = self.operator_stack.last();
102
103        if let Some(active_operator) = active_operator {
104            for context_flag in active_operator.context_flags().into_iter() {
105                context.add_identifier(*context_flag);
106            }
107        }
108
109        context.add_key(
110            "vim_operator",
111            active_operator.map(|op| op.id()).unwrap_or_else(|| "none"),
112        );
113
114        context
115    }
116}
117
118impl Operator {
119    pub fn id(&self) -> &'static str {
120        match self {
121            Operator::Number(_) => "n",
122            Operator::Object { around: false } => "i",
123            Operator::Object { around: true } => "a",
124            Operator::Change => "c",
125            Operator::Delete => "d",
126            Operator::Yank => "y",
127            Operator::Replace => "r",
128            Operator::FindForward { before: false } => "f",
129            Operator::FindForward { before: true } => "t",
130            Operator::FindBackward { after: false } => "F",
131            Operator::FindBackward { after: true } => "T",
132        }
133    }
134
135    pub fn context_flags(&self) -> &'static [&'static str] {
136        match self {
137            Operator::Object { .. } => &["VimObject"],
138            Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace => {
139                &["VimWaiting"]
140            }
141            _ => &[],
142        }
143    }
144}