1use strop_core::Range;
7use strop_grammar::{self as grammar, Op};
8
9use super::input::Action;
10use super::{Editor, Key, Mode};
11
12impl Editor {
13 pub(crate) fn feed_visual(&mut self, key: Key) {
14 let p = self.sels().primary();
17 self.last_visual = Some((p.anchor, p.head));
18 match key {
19 Key::Esc => {
20 self.mode = Mode::Normal;
21 self.walker.clear();
22 }
23 Key::Up if self.walker.is_ground() => self.run_motion("k"),
24 Key::Down if self.walker.is_ground() => self.run_motion("j"),
25 Key::Left if self.walker.is_ground() => self.run_motion("h"),
26 Key::Right if self.walker.is_ground() => self.run_motion("l"),
27 Key::Char('>') | Key::Char('<') if self.walker.is_ground() => {
28 let Some(range) = self.visual_range() else {
31 return;
32 };
33 if self.buf().readonly {
34 self.message = "readonly buffer".into();
35 self.mode = Mode::Normal;
36 return;
37 }
38 let right = key == Key::Char('>');
39 self.tx_begin();
40 self.apply_indent(range, right);
41 self.tx_commit();
42 self.mode = Mode::Normal;
43 self.set_head(range.start.get());
44 self.clamp_cursor();
45 self.flash(Range::charwise(self.head(), self.head()));
46 self.last_cmd_keys = if right { "V>" } else { "V<" }.into();
47 self.last_insert = None;
48 }
49 Key::Char('d') | Key::Char('y') | Key::Char('c') | Key::Char('x')
50 if self.walker.is_ground() && self.mode == Mode::VisualBlock =>
51 {
52 match key {
53 Key::Char('y') => self.block_yank(),
54 Key::Char('c') => self.block_change(),
55 _ => self.block_delete(),
56 }
57 }
58 Key::Char('I') if self.mode == Mode::VisualBlock && self.walker.is_ground() => {
59 self.block_insert(false)
60 }
61 Key::Char('A') if self.mode == Mode::VisualBlock && self.walker.is_ground() => {
62 self.block_insert(true)
63 }
64 Key::Char('d') | Key::Char('y') | Key::Char('c') | Key::Char('x')
65 if self.walker.is_ground() =>
66 {
67 let op = match key {
68 Key::Char('d') | Key::Char('x') => Op::Delete,
69 Key::Char('y') => Op::Yank,
70 _ => Op::Change,
71 };
72 if self.mode == Mode::Visual && self.sels().count() > 1 {
75 self.visual_operate_cascade(op);
76 return;
77 }
78 if self.buf().readonly && op != Op::Yank {
79 self.message = "readonly buffer".into();
80 self.mode = Mode::Normal;
81 return;
82 }
83 let Some(range) = self.visual_range() else {
84 return;
85 };
86 let linewise = self.mode == Mode::VisualLine;
87 if op == Op::Change && linewise {
88 self.mode = Mode::Normal;
89 self.change_lines(None, "V...", &[(self.head(), range, true)]);
90 return;
91 }
92 if op == Op::Yank {
93 let text = self.buf().slice_string(range);
94 let mut register = if linewise {
95 super::Register::linewise(text)
96 } else {
97 super::Register::characterwise(text)
98 };
99 register.file_provenance =
100 self.capture_filename_register([(range, linewise)], false, false);
101 self.set_register(None, register);
102 self.flash(range);
103 } else {
104 let text = self.buf().slice_string(range);
105 let provenance =
106 self.capture_filename_register([(range, linewise)], true, false);
107 let changes = crate::editor::transact::ChangeSet {
108 edits: vec![strop_core::Replacement::new(range, String::new())],
109 undo_open: false,
110 };
111 self.filename_delete_hint(range, linewise);
112 if let Err(error) = self.apply(self.current(), self.buf().revision(), changes) {
113 self.clear_filename_hint();
114 self.message = error.to_string();
115 return;
116 }
117 self.clear_filename_hint();
118 let mut register = if linewise {
119 super::Register::linewise(text)
120 } else {
121 super::Register::characterwise(text)
122 };
123 register.file_provenance = provenance;
124 self.set_register(None, register);
125 self.set_head(range.start.get());
126 self.flash(Range::charwise(self.head(), self.head()));
127 }
128 self.mode = Mode::Normal;
129 self.clamp_cursor();
130 if op == Op::Change {
131 self.enter_insert_from(if linewise { "V..." } else { "v..." });
132 }
133 }
134 _ => {
135 let action = self.walker.feed_visual(key);
136 self.dispatch_visual_action(action);
137 }
138 }
139 }
140
141 pub(crate) fn select_visual_object(&mut self, command: &grammar::Command) {
142 if self.defer_resolution(
143 command,
144 self.all_cursors(),
145 super::resolution::ResolutionPurpose::VisualObject,
146 ) {
147 return;
148 }
149 match self.resolved_many(command, &self.all_cursors()) {
150 Ok(resolutions) => {
151 if let Some(Some(resolved)) = resolutions.first() {
152 let head = self.head();
153 self.sels_mut()
154 .stretch_primary(resolved.range.start.get(), head);
155 self.set_head(
156 self.buf()
157 .clamp_boundary(resolved.range.end.get().saturating_sub(1)),
158 );
159 }
160 let olds = self.extra_selections().to_vec();
163 let extras: Vec<strop_core::selection::Selection> = olds
164 .iter()
165 .zip(resolutions.iter().skip(1))
166 .map(|(old, resolved)| match resolved {
167 Some(resolved) => strop_core::selection::Selection {
168 anchor: resolved.range.start.get(),
169 head: self
170 .buf()
171 .clamp_boundary(resolved.range.end.get().saturating_sub(1)),
172 },
173 None => *old,
174 })
175 .collect();
176 self.sels_mut().set_extra_selections(extras);
177 }
178 Err(error) => self.message = error,
179 }
180 }
181
182 fn visual_operate_cascade(&mut self, op: Op) {
187 if self.buf().readonly && op != Op::Yank {
188 self.message = "readonly buffer".into();
189 self.mode = Mode::Normal;
190 return;
191 }
192 let mut spans: Vec<((usize, usize), bool)> = std::iter::once((self.sels().primary(), true))
193 .chain(self.extra_selections().iter().copied().map(|s| (s, false)))
194 .map(|(selection, primary)| (self.selection_span(selection), primary))
195 .collect();
196 spans.sort_by_key(|(span, _)| span.0);
197 spans.dedup_by_key(|(span, _)| *span); let texts: Vec<String> = spans
199 .iter()
200 .map(|((start, end), _)| self.buf().slice_string(Range::charwise(*start, *end)))
201 .collect();
202 if op == Op::Yank {
203 self.set_register(None, super::Register::characterwise(texts.join("\n")));
204 let flash = spans
206 .iter()
207 .find(|(_, primary)| *primary)
208 .map_or(spans[0].0, |(span, _)| *span);
209 self.flash(Range::charwise(flash.0, flash.1));
210 return;
211 }
212 self.tx_begin();
213 for ((start, end), _) in spans.iter().rev() {
214 self.buf_mut().delete(Range::charwise(*start, *end));
215 }
216 self.set_register(None, super::Register::characterwise(texts.join("\n")));
217 let mut shift = 0usize;
220 let mut landings: Vec<(usize, bool)> = Vec::with_capacity(spans.len());
221 for ((start, end), primary) in &spans {
222 landings.push((*start - shift, *primary));
223 shift += end - start;
224 }
225 let head = landings
226 .iter()
227 .find(|(_, primary)| *primary)
228 .map(|(start, _)| *start)
229 .unwrap_or(self.head());
230 self.set_head(head);
231 self.sels_mut().set_extra_selections(
232 landings
233 .iter()
234 .filter(|(_, primary)| !*primary)
235 .map(|(start, _)| strop_core::selection::Selection::cursor(*start)),
236 );
237 self.mode = Mode::Normal;
238 if op == Op::Change {
239 self.enter_insert_from("v...");
241 } else {
242 self.tx_commit();
243 }
244 self.clamp_cursor();
245 self.flash(Range::charwise(self.head(), self.head()));
246 }
247
248 fn dispatch_visual_action(&mut self, action: Action) {
252 match action {
253 Action::Pending => {}
254 Action::Invalid(keys) => self.message = format!("not an editor command: {keys}"),
255 Action::QueryError(error) => self.message = error.to_string(),
256 Action::EnterText { sigil, state } => self.begin_text_line(sigil, state),
257 Action::Grammar(command) if command.op.is_none() => {
258 if let grammar::Target::Object { .. } = command.target {
259 self.select_visual_object(&command);
260 } else {
261 self.move_cursor(&command);
262 }
263 }
264 Action::Grammar(_) => self.message = "operator needs a visual selection".into(),
265 Action::Row { row, .. } if row.id == "git-file-history" => {
266 let a = self.buf().line_of(self.anchor()) + 1;
268 let b = self.buf().line_of(self.head()) + 1;
269 self.open_line_history(a.min(b), a.max(b));
270 }
271 Action::Row { row, .. } if row.id == "clip-yank" => {
272 if let Some(range) = self.visual_range() {
274 let linewise = self.mode == Mode::VisualLine;
275 let text = self.buf().slice_string(range);
276 let mut register = if linewise {
277 super::Register::linewise(text)
278 } else {
279 super::Register::characterwise(text)
280 };
281 register.file_provenance =
282 self.capture_filename_register([(range, linewise)], false, false);
283 self.set_register(Some('+'), register);
284 self.flash(range);
285 }
286 self.mode = Mode::Normal;
287 self.clamp_cursor();
288 }
289 Action::Row { row, key, .. } if row.sections.contains(&"visual") => {
293 if let crate::keymap::Handler::Leaf(f) = row.handler {
294 f(self, key);
295 }
296 }
297 Action::Row { .. } => self.message = "command unavailable in visual mode".into(),
298 Action::VisualSurround(c) => {
299 if self.buf().readonly {
300 self.message = "readonly buffer".into();
301 return;
302 }
303 let Some(range) = self.visual_range() else {
304 return;
305 };
306 let pair = match c {
307 'b' | '(' | ')' => ('(', ')'),
308 'B' | '{' | '}' => ('{', '}'),
309 'r' | '[' | ']' => ('[', ']'),
310 'a' | '<' | '>' => ('<', '>'),
311 q => (q, q),
312 };
313 let changes = crate::editor::transact::ChangeSet {
317 edits: vec![
318 strop_core::Replacement::new(
319 Range::charwise(range.start, range.start),
320 pair.0.to_string(),
321 ),
322 strop_core::Replacement::new(
323 Range::charwise(range.end, range.end),
324 pair.1.to_string(),
325 ),
326 ],
327 undo_open: false,
328 };
329 if let Err(error) = self.apply(self.current(), self.buf().revision(), changes) {
330 self.message = error.to_string();
331 return;
332 }
333 self.mode = Mode::Normal;
334 self.flash(Range::charwise(
335 range.start,
336 range.end.get() + pair.0.len_utf8() + pair.1.len_utf8(),
337 ));
338 self.last_cmd_keys = format!("vS{c}"); self.last_insert = None;
340 }
341 }
342 }
343
344 pub fn visual_range(&self) -> Option<Range> {
345 match self.mode {
346 Mode::Visual => {
347 let (s, e) = (
350 self.anchor().min(self.head()),
351 self.buf().ceil_boundary(self.anchor().max(self.head()) + 1),
352 );
353 Some(Range::charwise(s, e.min(self.buf().len_bytes())))
354 }
355 Mode::VisualLine => {
356 let (a, b) = (
357 self.buf().line_of(self.anchor()),
358 self.buf().line_of(self.head()),
359 );
360 let (a, b) = (a.min(b), a.max(b));
361 let start = self.buf().line_start(a);
362 let end = if b + 1 >= self.buf().len_lines() {
363 self.buf().len_bytes()
364 } else {
365 self.buf().line_start(b + 1)
366 };
367 Some(Range::linewise(start, end))
368 }
369 _ => None,
370 }
371 }
372}