state.rs

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