state.rs

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