command.rs

  1use command_palette::{humanize_action_name, CommandInterceptResult};
  2use gpui::{actions, impl_actions, Action, AppContext, AsyncAppContext, ViewContext};
  3use itertools::Itertools;
  4use serde::{Deserialize, Serialize};
  5use workspace::{SaveBehavior, Workspace};
  6
  7use crate::{
  8    motion::{motion, Motion},
  9    normal::JoinLines,
 10    Vim,
 11};
 12
 13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 14pub struct GoToLine {
 15    pub line: u32,
 16}
 17
 18impl_actions!(vim, [GoToLine]);
 19
 20pub fn init(cx: &mut AppContext) {
 21    cx.add_action(|_: &mut Workspace, action: &GoToLine, cx| {
 22        Vim::update(cx, |vim, cx| {
 23            vim.push_operator(crate::state::Operator::Number(action.line as usize), cx)
 24        });
 25        motion(Motion::StartOfDocument, cx)
 26    });
 27}
 28
 29pub fn command_interceptor(mut query: &str, _: &AppContext) -> Option<CommandInterceptResult> {
 30    while query.starts_with(":") {
 31        query = &query[1..];
 32    }
 33
 34    let (name, action) = match query {
 35        // :w
 36        "w" | "wr" | "wri" | "writ" | "write" => (
 37            "write",
 38            workspace::Save {
 39                save_behavior: Some(SaveBehavior::PromptOnConflict),
 40            }
 41            .boxed_clone(),
 42        ),
 43        "w!" | "wr!" | "wri!" | "writ!" | "write!" => (
 44            "write",
 45            workspace::Save {
 46                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
 47            }
 48            .boxed_clone(),
 49        ),
 50
 51        // :q
 52        "q" | "qu" | "qui" | "quit" => (
 53            "quit",
 54            workspace::CloseActiveItem {
 55                save_behavior: Some(SaveBehavior::PromptOnWrite),
 56            }
 57            .boxed_clone(),
 58        ),
 59        "q!" | "qu!" | "qui!" | "quit!" => (
 60            "quit!",
 61            workspace::CloseActiveItem {
 62                save_behavior: Some(SaveBehavior::DontSave),
 63            }
 64            .boxed_clone(),
 65        ),
 66
 67        // :wq
 68        "wq" => (
 69            "wq",
 70            workspace::CloseActiveItem {
 71                save_behavior: Some(SaveBehavior::PromptOnConflict),
 72            }
 73            .boxed_clone(),
 74        ),
 75        "wq!" => (
 76            "wq!",
 77            workspace::CloseActiveItem {
 78                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
 79            }
 80            .boxed_clone(),
 81        ),
 82        // :x
 83        "x" | "xi" | "xit" | "exi" | "exit" => (
 84            "exit",
 85            workspace::CloseActiveItem {
 86                save_behavior: Some(SaveBehavior::PromptOnConflict),
 87            }
 88            .boxed_clone(),
 89        ),
 90        "x!" | "xi!" | "xit!" | "exi!" | "exit!" => (
 91            "xit",
 92            workspace::CloseActiveItem {
 93                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
 94            }
 95            .boxed_clone(),
 96        ),
 97
 98        // :wa
 99        "wa" | "wal" | "wall" => (
100            "wall",
101            workspace::SaveAll {
102                save_behavior: Some(SaveBehavior::PromptOnConflict),
103            }
104            .boxed_clone(),
105        ),
106        "wa!" | "wal!" | "wall!" => (
107            "wall!",
108            workspace::SaveAll {
109                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
110            }
111            .boxed_clone(),
112        ),
113
114        // :qa
115        "qa" | "qal" | "qall" | "quita" | "quital" | "quitall" => (
116            "quitall",
117            workspace::CloseAllItemsAndPanes {
118                save_behavior: Some(SaveBehavior::PromptOnWrite),
119            }
120            .boxed_clone(),
121        ),
122        "qa!" | "qal!" | "qall!" | "quita!" | "quital!" | "quitall!" => (
123            "quitall!",
124            workspace::CloseAllItemsAndPanes {
125                save_behavior: Some(SaveBehavior::DontSave),
126            }
127            .boxed_clone(),
128        ),
129
130        // :cq
131        "cq" | "cqu" | "cqui" | "cquit" | "cq!" | "cqu!" | "cqui!" | "cquit!" => (
132            "cquit!",
133            workspace::CloseAllItemsAndPanes {
134                save_behavior: Some(SaveBehavior::DontSave),
135            }
136            .boxed_clone(),
137        ),
138
139        // :xa
140        "xa" | "xal" | "xall" => (
141            "xall",
142            workspace::CloseAllItemsAndPanes {
143                save_behavior: Some(SaveBehavior::PromptOnConflict),
144            }
145            .boxed_clone(),
146        ),
147        "xa!" | "xal!" | "xall!" => (
148            "zall!",
149            workspace::CloseAllItemsAndPanes {
150                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
151            }
152            .boxed_clone(),
153        ),
154
155        // :wqa
156        "wqa" | "wqal" | "wqall" => (
157            "wqall",
158            workspace::CloseAllItemsAndPanes {
159                save_behavior: Some(SaveBehavior::PromptOnConflict),
160            }
161            .boxed_clone(),
162        ),
163        "wqa!" | "wqal!" | "wqall!" => (
164            "wqall!",
165            workspace::CloseAllItemsAndPanes {
166                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
167            }
168            .boxed_clone(),
169        ),
170
171        "j" | "jo" | "joi" | "join" => ("join", JoinLines.boxed_clone()),
172
173        "sp" | "spl" | "spli" | "split" => ("split", workspace::SplitUp.boxed_clone()),
174        "vs" | "vsp" | "vspl" | "vspli" | "vsplit" => {
175            ("vsplit", workspace::SplitLeft.boxed_clone())
176        }
177        "cn" | "cne" | "cnex" | "cnext" => ("cnext", editor::GoToDiagnostic.boxed_clone()),
178        "cp" | "cpr" | "cpre" | "cprev" => ("cprev", editor::GoToPrevDiagnostic.boxed_clone()),
179
180        _ => {
181            if let Ok(line) = query.parse::<u32>() {
182                (query, GoToLine { line }.boxed_clone())
183            } else {
184                return None;
185            }
186        }
187    };
188
189    let string = ":".to_owned() + name;
190    let positions = generate_positions(&string, query);
191
192    Some(CommandInterceptResult {
193        action,
194        string,
195        positions,
196    })
197}
198
199fn generate_positions(string: &str, query: &str) -> Vec<usize> {
200    let mut positions = Vec::new();
201    let mut chars = query.chars().into_iter();
202
203    let Some(mut current) = chars.next() else {
204        return positions;
205    };
206
207    for (i, c) in string.chars().enumerate() {
208        if c == current {
209            positions.push(i);
210            if let Some(c) = chars.next() {
211                current = c;
212            } else {
213                break;
214            }
215        }
216    }
217
218    positions
219}