state.rs

  1use std::{ops::Range, sync::Arc};
  2
  3use gpui::{keymap_matcher::KeymapContext, Action};
  4use language::CursorShape;
  5use serde::{Deserialize, Serialize};
  6use workspace::searchable::Direction;
  7
  8use crate::motion::Motion;
  9
 10#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
 11pub enum Mode {
 12    Normal,
 13    Insert,
 14    Visual,
 15    VisualLine,
 16    VisualBlock,
 17}
 18
 19impl Mode {
 20    pub fn is_visual(&self) -> bool {
 21        match self {
 22            Mode::Normal | Mode::Insert => false,
 23            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => true,
 24        }
 25    }
 26}
 27
 28impl Default for Mode {
 29    fn default() -> Self {
 30        Self::Normal
 31    }
 32}
 33
 34#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
 35pub enum Operator {
 36    Number(usize),
 37    Change,
 38    Delete,
 39    Yank,
 40    Replace,
 41    Object { around: bool },
 42    FindForward { before: bool },
 43    FindBackward { after: bool },
 44}
 45
 46#[derive(Default, Clone)]
 47pub struct EditorState {
 48    pub mode: Mode,
 49    pub last_mode: Mode,
 50    pub operator_stack: Vec<Operator>,
 51}
 52
 53#[derive(Default, Clone, Debug)]
 54pub enum RecordedSelection {
 55    #[default]
 56    None,
 57    Visual {
 58        rows: u32,
 59        cols: u32,
 60    },
 61    SingleLine {
 62        cols: u32,
 63    },
 64    VisualBlock {
 65        rows: u32,
 66        cols: u32,
 67    },
 68    VisualLine {
 69        rows: u32,
 70    },
 71}
 72
 73#[derive(Default, Clone)]
 74pub struct WorkspaceState {
 75    pub search: SearchState,
 76    pub last_find: Option<Motion>,
 77
 78    pub recording: bool,
 79    pub stop_recording_after_next_action: bool,
 80    pub replaying: bool,
 81    pub recorded_count: Option<usize>,
 82    pub recorded_actions: Vec<ReplayableAction>,
 83    pub recorded_selection: RecordedSelection,
 84}
 85
 86#[derive(Debug)]
 87pub enum ReplayableAction {
 88    Action(Box<dyn Action>),
 89    Insertion {
 90        text: Arc<str>,
 91        utf16_range_to_replace: Option<Range<isize>>,
 92    },
 93}
 94
 95impl Clone for ReplayableAction {
 96    fn clone(&self) -> Self {
 97        match self {
 98            Self::Action(action) => Self::Action(action.boxed_clone()),
 99            Self::Insertion {
100                text,
101                utf16_range_to_replace,
102            } => Self::Insertion {
103                text: text.clone(),
104                utf16_range_to_replace: utf16_range_to_replace.clone(),
105            },
106        }
107    }
108}
109
110#[derive(Clone)]
111pub struct SearchState {
112    pub direction: Direction,
113    pub count: usize,
114    pub initial_query: String,
115}
116
117impl Default for SearchState {
118    fn default() -> Self {
119        Self {
120            direction: Direction::Next,
121            count: 1,
122            initial_query: "".to_string(),
123        }
124    }
125}
126
127impl EditorState {
128    pub fn cursor_shape(&self) -> CursorShape {
129        match self.mode {
130            Mode::Normal => {
131                if self.operator_stack.is_empty() {
132                    CursorShape::Block
133                } else {
134                    CursorShape::Underscore
135                }
136            }
137            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => CursorShape::Block,
138            Mode::Insert => CursorShape::Bar,
139        }
140    }
141
142    pub fn vim_controlled(&self) -> bool {
143        !matches!(self.mode, Mode::Insert)
144            || matches!(
145                self.operator_stack.last(),
146                Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. })
147            )
148    }
149
150    pub fn should_autoindent(&self) -> bool {
151        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
152    }
153
154    pub fn clip_at_line_ends(&self) -> bool {
155        match self.mode {
156            Mode::Insert | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => false,
157            Mode::Normal => true,
158        }
159    }
160
161    pub fn keymap_context_layer(&self) -> KeymapContext {
162        let mut context = KeymapContext::default();
163        context.add_identifier("VimEnabled");
164        context.add_key(
165            "vim_mode",
166            match self.mode {
167                Mode::Normal => "normal",
168                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
169                Mode::Insert => "insert",
170            },
171        );
172
173        if self.vim_controlled() {
174            context.add_identifier("VimControl");
175        }
176
177        let active_operator = self.operator_stack.last();
178
179        if let Some(active_operator) = active_operator {
180            for context_flag in active_operator.context_flags().into_iter() {
181                context.add_identifier(*context_flag);
182            }
183        }
184
185        context.add_key(
186            "vim_operator",
187            active_operator.map(|op| op.id()).unwrap_or_else(|| "none"),
188        );
189
190        context
191    }
192}
193
194impl Operator {
195    pub fn id(&self) -> &'static str {
196        match self {
197            Operator::Number(_) => "n",
198            Operator::Object { around: false } => "i",
199            Operator::Object { around: true } => "a",
200            Operator::Change => "c",
201            Operator::Delete => "d",
202            Operator::Yank => "y",
203            Operator::Replace => "r",
204            Operator::FindForward { before: false } => "f",
205            Operator::FindForward { before: true } => "t",
206            Operator::FindBackward { after: false } => "F",
207            Operator::FindBackward { after: true } => "T",
208        }
209    }
210
211    pub fn context_flags(&self) -> &'static [&'static str] {
212        match self {
213            Operator::Object { .. } => &["VimObject"],
214            Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace => {
215                &["VimWaiting"]
216            }
217            _ => &[],
218        }
219    }
220}