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