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.map.insert(
 77            "vim_mode".to_string(),
 78            match self.mode {
 79                Mode::Normal => "normal",
 80                Mode::Visual { .. } => "visual",
 81                Mode::Insert => "insert",
 82            }
 83            .to_string(),
 84        );
 85
 86        if self.vim_controlled() {
 87            context.set.insert("VimControl".to_string());
 88        }
 89
 90        let active_operator = self.operator_stack.last();
 91
 92        if let Some(active_operator) = active_operator {
 93            for context_flag in active_operator.context_flags().into_iter() {
 94                context.set.insert(context_flag.to_string());
 95            }
 96        }
 97
 98        context.map.insert(
 99            "vim_operator".to_string(),
100            active_operator
101                .map(|op| op.id())
102                .unwrap_or_else(|| "none")
103                .to_string(),
104        );
105
106        context
107    }
108}
109
110impl Operator {
111    pub fn id(&self) -> &'static str {
112        match self {
113            Operator::Number(_) => "n",
114            Operator::Namespace(Namespace::G) => "g",
115            Operator::Namespace(Namespace::Z) => "z",
116            Operator::Object { around: false } => "i",
117            Operator::Object { around: true } => "a",
118            Operator::Change => "c",
119            Operator::Delete => "d",
120            Operator::Yank => "y",
121            Operator::Replace => "r",
122            Operator::FindForward { before: false } => "f",
123            Operator::FindForward { before: true } => "t",
124            Operator::FindBackward { after: false } => "F",
125            Operator::FindBackward { after: true } => "T",
126        }
127    }
128
129    pub fn context_flags(&self) -> &'static [&'static str] {
130        match self {
131            Operator::Object { .. } => &["VimObject"],
132            Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace => {
133                &["VimWaiting"]
134            }
135            _ => &[],
136        }
137    }
138}