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}