1use std::{collections::VecDeque, fmt, ops::Range};
2
3use crate::{
4 buffer::{Buffer, BufferHandle, BufferReadError, BufferWriteError},
5 buffer_view::{BufferView, BufferViewHandle},
6 client::ClientHandle,
7 config::ParseConfigError,
8 cursor::Cursor,
9 editor::{EditorContext, EditorFlow},
10 editor_utils::{LogKind, ParseKeyMapError},
11 events::KeyParseAllError,
12 glob::InvalidGlobError,
13 pattern::PatternError,
14 plugin::PluginHandle,
15};
16
17mod builtins;
18mod expansions;
19
20const HISTORY_CAPACITY: usize = 8;
21
22pub enum CommandError {
23 InvalidMacroName,
24 ExpansionError(ExpansionError),
25 NoSuchCommand,
26 CommandArgsError(CommandArgsError),
27 NoTargetClient,
28 InvalidLogKind,
29 EditorNotLogging,
30 NoBufferOpened,
31 UnsavedChanges,
32 BufferReadError(BufferReadError),
33 BufferWriteError(BufferWriteError),
34 InvalidBufferPath,
35 NoSuchBufferProperty,
36 NoSuchBreakpointSubcommand,
37 ConfigError(ParseConfigError),
38 NoSuchColor,
39 InvalidColorValue,
40 InvalidModeKind,
41 KeyMapError(ParseKeyMapError),
42 KeyParseError(KeyParseAllError),
43 InvalidRegisterKey,
44 InvalidTokenKind,
45 PatternError(PatternError),
46 InvalidEnvironmentVariable,
47 InvalidProcessCommand,
48 InvalidIfOp,
49 InvalidGlob(InvalidGlobError),
50 OtherStatic(&'static str),
51 OtherOwned(String),
52}
53impl fmt::Display for CommandError {
54 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55 match self {
56 Self::InvalidMacroName => f.write_str("invalid command name"),
57 Self::ExpansionError(error) => write!(f, "expansion error: {}", error),
58 Self::NoSuchCommand => f.write_str("no such command"),
59 Self::CommandArgsError(error) => write!(f, "args error: {}", error),
60 Self::NoTargetClient => f.write_str("no target client"),
61 Self::InvalidLogKind => f.write_str("invalid log kind"),
62 Self::EditorNotLogging => f.write_str("editor is not logging"),
63 Self::NoBufferOpened => f.write_str("no buffer opened"),
64 Self::UnsavedChanges => f.write_str("unsaved changes"),
65 Self::BufferReadError(error) => write!(f, "buffer read error: {}", error),
66 Self::BufferWriteError(error) => write!(f, "buffer write error: {}", error),
67 Self::InvalidBufferPath => f.write_str("invalid buffer path"),
68 Self::NoSuchBufferProperty => f.write_str("no such buffer property"),
69 Self::NoSuchBreakpointSubcommand => f.write_str("no such breakpoint subcommand"),
70 Self::ConfigError(error) => write!(f, "config error: {}", error),
71 Self::NoSuchColor => f.write_str("no such color"),
72 Self::InvalidColorValue => f.write_str("invalid color value"),
73 Self::InvalidModeKind => f.write_str("invalid mode"),
74 Self::KeyMapError(error) => write!(f, "key map error: {}", error),
75 Self::KeyParseError(error) => write!(f, "key parse error: {}", error),
76 Self::InvalidRegisterKey => f.write_str("invalid register key"),
77 Self::InvalidTokenKind => f.write_str("invalid token kind"),
78 Self::PatternError(error) => write!(f, "pattern error: {}", error),
79 Self::InvalidEnvironmentVariable => f.write_str("invalid environment variable"),
80 Self::InvalidProcessCommand => f.write_str("invalid process command"),
81 Self::InvalidIfOp => f.write_str("invalid if comparison operator"),
82 Self::InvalidGlob(error) => write!(f, "glob error: {}", error),
83 Self::OtherStatic(error) => f.write_str(error),
84 Self::OtherOwned(error) => f.write_str(&error),
85 }
86 }
87}
88impl From<CommandArgsError> for CommandError {
89 fn from(other: CommandArgsError) -> Self {
90 Self::CommandArgsError(other)
91 }
92}
93
94pub enum ExpansionError {
95 NoSuchExpansion,
96 IgnoreExpansion,
97 CommandArgsError(CommandArgsError),
98 InvalidArgIndex,
99 InvalidBufferId,
100 NoSuchCommand,
101 InvalidCursorIndex,
102 InvalidRegisterKey,
103 OtherStatic(&'static str),
104 OtherOwned(String),
105}
106impl fmt::Display for ExpansionError {
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 match self {
109 Self::NoSuchExpansion => f.write_str("no such expansion"),
110 Self::IgnoreExpansion => f.write_str("invalid use of @arg(*)"),
111 Self::CommandArgsError(error) => write!(f, "args error: {}", error),
112 Self::InvalidArgIndex => f.write_str("invalid arg index"),
113 Self::InvalidBufferId => f.write_str("invalid buffer id"),
114 Self::NoSuchCommand => f.write_str("no such command"),
115 Self::InvalidCursorIndex => f.write_str("invalid cursor index"),
116 Self::InvalidRegisterKey => f.write_str("invalid register key"),
117 Self::OtherStatic(error) => f.write_str(error),
118 Self::OtherOwned(error) => f.write_str(&error),
119 }
120 }
121}
122impl From<CommandArgsError> for ExpansionError {
123 fn from(other: CommandArgsError) -> Self {
124 Self::CommandArgsError(other)
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum CompletionSource {
130 Commands,
131 Expansions,
132 Buffers,
133 Files,
134 HelpPages,
135 Custom(&'static [&'static str]),
136}
137
138pub enum CommandArgsError {
139 TooFewArguments,
140 TooManyArguments,
141}
142impl fmt::Display for CommandArgsError {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 match self {
145 Self::TooFewArguments => f.write_str("too few arguments"),
146 Self::TooManyArguments => f.write_str("too many arguments"),
147 }
148 }
149}
150
151#[derive(Clone, Copy)]
152pub struct CommandArgs<'command>(pub(crate) &'command str);
153impl<'command> CommandArgs<'command> {
154 pub fn try_next(&mut self) -> Option<&'command str> {
155 let i = self.0.find('\0')?;
156 let next = &self.0[..i];
157 self.0 = &self.0[i + 1..];
158 Some(next)
159 }
160
161 pub fn next(&mut self) -> Result<&'command str, CommandArgsError> {
162 match self.try_next() {
163 Some(value) => Ok(value),
164 None => Err(CommandArgsError::TooFewArguments),
165 }
166 }
167
168 pub fn assert_empty(&mut self) -> Result<(), CommandArgsError> {
169 match self.try_next() {
170 Some(_) => Err(CommandArgsError::TooManyArguments),
171 None => Ok(()),
172 }
173 }
174}
175
176pub struct CommandIO<'a> {
177 client_handle: Option<ClientHandle>,
178 plugin_handle: Option<PluginHandle>,
179
180 pub args: CommandArgs<'a>,
181 pub bang: bool,
182 pub flow: EditorFlow,
183}
184impl<'a> CommandIO<'a> {
185 pub fn client_handle(&self) -> Result<ClientHandle, CommandError> {
186 match self.client_handle {
187 Some(handle) => Ok(handle),
188 None => Err(CommandError::NoTargetClient),
189 }
190 }
191
192 pub fn plugin_handle(&self) -> PluginHandle {
193 self.plugin_handle.unwrap()
194 }
195
196 pub fn current_buffer_view_handle(
197 &self,
198 ctx: &EditorContext,
199 ) -> Result<BufferViewHandle, CommandError> {
200 let client_handle = self.client_handle()?;
201 match ctx.clients.get(client_handle).buffer_view_handle() {
202 Some(handle) => Ok(handle),
203 None => Err(CommandError::NoBufferOpened),
204 }
205 }
206
207 pub fn current_buffer_handle(&self, ctx: &EditorContext) -> Result<BufferHandle, CommandError> {
208 let buffer_view_handle = self.current_buffer_view_handle(ctx)?;
209 let buffer_handle = ctx
210 .editor
211 .buffer_views
212 .get(buffer_view_handle)
213 .buffer_handle;
214 Ok(buffer_handle)
215 }
216
217 pub fn assert_can_discard_all_buffers(&self, ctx: &EditorContext) -> Result<(), CommandError> {
218 if self.bang || !ctx.editor.buffers.iter().any(Buffer::needs_save) {
219 Ok(())
220 } else {
221 Err(CommandError::UnsavedChanges)
222 }
223 }
224
225 pub fn assert_can_discard_buffer(
226 &self,
227 ctx: &EditorContext,
228 handle: BufferHandle,
229 ) -> Result<(), CommandError> {
230 if self.bang || !ctx.editor.buffers.get(handle).needs_save() {
231 Ok(())
232 } else {
233 Err(CommandError::UnsavedChanges)
234 }
235 }
236}
237
238pub struct CommandIter<'a>(pub &'a str);
239impl<'a> Iterator for CommandIter<'a> {
240 type Item = &'a str;
241 fn next(&mut self) -> Option<Self::Item> {
242 loop {
243 self.0 = self.0.trim_start_matches(&[' ', '\t', '\n', '\r']);
244 if !self.0.starts_with('#') {
245 break;
246 }
247 match self.0.find('\n') {
248 Some(i) => self.0 = &self.0[i + 1..],
249 None => self.0 = "",
250 }
251 }
252
253 let mut tokens = CommandTokenizer(self.0);
254 tokens.next()?;
255 while tokens.next().is_some() {}
256 let len = tokens.0.as_ptr() as usize - self.0.as_ptr() as usize;
257 if len == 0 {
258 return None;
259 }
260
261 let command = &self.0[..len];
262 self.0 = tokens.0;
263
264 Some(command)
265 }
266}
267
268pub struct CommandToken<'a> {
269 pub slice: &'a str,
270 pub is_simple: bool,
271 pub can_expand_variables: bool,
272 pub has_escaping: bool,
273}
274
275#[derive(Clone)]
276pub struct CommandTokenizer<'a>(pub &'a str);
277impl<'a> Iterator for CommandTokenizer<'a> {
278 type Item = CommandToken<'a>;
279 fn next(&mut self) -> Option<Self::Item> {
280 fn next_literal_end(s: &str) -> usize {
281 match s.find(&[' ', '\t', '\n', '\r', '"', '\'', '{', '}', '#']) {
282 Some(0) => 1,
283 Some(i) => i,
284 None => s.len(),
285 }
286 }
287
288 fn parse_string_token(delim: char, s: &str) -> Option<(&str, &str, bool)> {
289 let mut chars = s.chars();
290 let mut has_escaping = false;
291 loop {
292 match chars.next()? {
293 '\n' | '\r' => break None,
294 '\\' => {
295 chars.next();
296 has_escaping = true;
297 }
298 c if c == delim => {
299 let rest = chars.as_str();
300 let len = rest.as_ptr() as usize - s.as_ptr() as usize - 1;
301 let slice = &s[..len];
302 break Some((slice, rest, has_escaping));
303 }
304 _ => (),
305 }
306 }
307 }
308
309 fn parse_block_token(s: &str) -> Option<(&str, &str)> {
310 let mut chars = s.chars();
311 let mut balance = 1;
312 loop {
313 match chars.next()? {
314 '{' => balance += 1,
315 '}' => {
316 balance -= 1;
317 if balance == 0 {
318 let rest = chars.as_str();
319 let len = rest.as_ptr() as usize - s.as_ptr() as usize - 1;
320 break Some((&s[..len], rest));
321 }
322 }
323 '#' => {
324 let rest = chars.as_str();
325 let i = rest.find('\n')?;
326 chars = rest[i + 1..].chars();
327 }
328 '\\' => {
329 chars.next();
330 }
331 _ => (),
332 }
333 }
334 }
335
336 self.0 = self.0.trim_start_matches(&[' ', '\t']);
337
338 let previous_text = self.0;
339 let mut can_expand_variables = true;
340
341 loop {
342 let mut chars = self.0.chars();
343 let next_char = match chars.next() {
344 Some(c) => c,
345 None => {
346 return if can_expand_variables {
347 None
348 } else {
349 Some(CommandToken {
350 slice: &self.0,
351 is_simple: true,
352 can_expand_variables,
353 has_escaping: false,
354 })
355 }
356 }
357 };
358
359 match next_char {
360 '@' => {
361 can_expand_variables = false;
362 self.0 = chars.as_str();
363 }
364 delim @ ('"' | '\'') => {
365 let rest = chars.as_str();
366 match parse_string_token(delim, rest) {
367 Some((slice, rest, has_escaping)) => {
368 self.0 = rest;
369 return Some(CommandToken {
370 slice,
371 is_simple: false,
372 can_expand_variables,
373 has_escaping,
374 });
375 }
376 None => {
377 let slice = &self.0[..1];
378 self.0 = rest;
379 return Some(CommandToken {
380 slice,
381 is_simple: false,
382 can_expand_variables,
383 has_escaping: false,
384 });
385 }
386 }
387 }
388 '\n' | '\r' | '#' => return None,
389 c => {
390 if c == '{' {
391 if let Some((slice, rest)) = parse_block_token(chars.as_str()) {
392 self.0 = rest;
393 return Some(CommandToken {
394 slice,
395 is_simple: false,
396 can_expand_variables,
397 has_escaping: false,
398 });
399 }
400 }
401
402 if !can_expand_variables {
403 can_expand_variables = true;
404 self.0 = previous_text;
405 }
406
407 let end = next_literal_end(self.0);
408 let (slice, rest) = self.0.split_at(end);
409 self.0 = rest;
410 return Some(CommandToken {
411 slice,
412 is_simple: true,
413 can_expand_variables,
414 has_escaping: false,
415 });
416 }
417 }
418 }
419 }
420}
421
422pub type CommandFn = fn(ctx: &mut EditorContext, io: &mut CommandIO) -> Result<(), CommandError>;
423
424pub struct Command {
425 plugin_handle: Option<PluginHandle>,
426 pub completions: &'static [CompletionSource],
427 command_fn: CommandFn,
428}
429
430struct Macro {
431 name_range: Range<u16>,
432 source_range: Range<u32>,
433}
434impl Macro {
435 pub fn name<'a>(&self, names: &'a str) -> &'a str {
436 &names[self.name_range.start as usize..self.name_range.end as usize]
437 }
438
439 pub fn source<'a>(&self, sources: &'a str) -> &'a str {
440 &sources[self.source_range.start as usize..self.source_range.end as usize]
441 }
442}
443
444pub struct ExpansionIO<'a> {
445 pub client_handle: Option<ClientHandle>,
446 plugin_handle: Option<PluginHandle>,
447
448 pub args: CommandArgs<'a>,
449 pub output: &'a mut String,
450}
451impl<'a> ExpansionIO<'a> {
452 pub fn plugin_handle(&self) -> PluginHandle {
453 self.plugin_handle.unwrap()
454 }
455
456 pub fn current_buffer_view<'ctx>(&self, ctx: &'ctx EditorContext) -> Option<&'ctx BufferView> {
457 let client_handle = self.client_handle?;
458 let buffer_view_handle = ctx.clients.get(client_handle).buffer_view_handle()?;
459 let buffer_view = ctx.editor.buffer_views.get(buffer_view_handle);
460 Some(buffer_view)
461 }
462
463 pub fn current_buffer<'ctx>(&self, ctx: &'ctx EditorContext) -> Option<&'ctx Buffer> {
464 let buffer_view = self.current_buffer_view(ctx)?;
465 let buffer = ctx.editor.buffers.get(buffer_view.buffer_handle);
466 Some(buffer)
467 }
468
469 pub fn parse_cursor(&self, ctx: &EditorContext, text: &str) -> Result<Option<Cursor>, ExpansionError> {
470 let cursors = match self.current_buffer_view(ctx) {
471 Some(view) => &view.cursors,
472 None => return Ok(None),
473 };
474 let index = if text.is_empty() {
475 cursors.main_cursor_index()
476 } else {
477 text
478 .parse()
479 .map_err(|_| ExpansionError::InvalidCursorIndex)?
480 };
481 Ok(cursors[..].get(index).cloned())
482 }
483}
484
485pub type ExpansionFn =
486 fn(ctx: &mut EditorContext, io: &mut ExpansionIO) -> Result<(), ExpansionError>;
487
488pub struct Expansion {
489 plugin_handle: Option<PluginHandle>,
490 expansion_fn: ExpansionFn,
491}
492
493#[derive(Default)]
494pub struct MacroCollection {
495 macros: Vec<Macro>,
496 names: String,
497 sources: String,
498}
499impl MacroCollection {
500 fn add(&mut self, name: &str, source: &str) {
501 for (i, m) in self.macros.iter().enumerate() {
502 if name == m.name(&self.names) {
503 let old_source_range = m.source_range.start as usize..m.source_range.end as usize;
504 let old_source_len = old_source_range.end - old_source_range.start;
505
506 let new_source_len = source.len();
507 if self.sources.len() - old_source_len + new_source_len > u32::MAX as _ {
508 return;
509 }
510
511 self.sources.replace_range(old_source_range, source);
512
513 let old_source_len = old_source_len as u32;
514 let new_source_len = new_source_len as u32;
515
516 self.macros[i].source_range.end =
517 self.macros[i].source_range.end - old_source_len + new_source_len;
518 for m in &mut self.macros[i + 1..] {
519 m.source_range.start = m.source_range.start - old_source_len + new_source_len;
520 m.source_range.end = m.source_range.end - old_source_len + new_source_len;
521 }
522 return;
523 }
524 }
525
526 let name_start = self.names.len();
527 let name_end = name_start + name.len();
528 if name_end > u16::MAX as _ {
529 return;
530 }
531
532 let source_start = self.sources.len();
533 let source_end = source_start + source.len();
534 if source_end > u32::MAX as _ {
535 return;
536 }
537
538 self.names.push_str(name);
539 self.sources.push_str(source);
540
541 self.macros.push(Macro {
542 name_range: name_start as _..name_end as _,
543 source_range: source_start as _..source_end as _,
544 });
545 }
546
547 pub fn find(&self, name: &str) -> Option<&str> {
548 for m in &self.macros {
549 if name == m.name(&self.names) {
550 return Some(m.source(&self.sources));
551 }
552 }
553 None
554 }
555
556 pub fn names(&self) -> impl Iterator<Item = &str> {
557 self.macros.iter().map(move |m| m.name(&self.names))
558 }
559}
560
561struct EvalStackEntry {
562 name: String,
563 command: String,
564 line_index: u32,
565}
566
567pub struct CommandManager {
568 command_names: Vec<&'static str>,
569 commands: Vec<Command>,
570 pub macros: MacroCollection,
571 expansion_names: Vec<&'static str>,
572 expansions: Vec<Expansion>,
573 history: VecDeque<String>,
574 eval_stack: Vec<EvalStackEntry>,
575}
576
577impl CommandManager {
578 pub fn new() -> Self {
579 let mut this = Self {
580 command_names: Vec::new(),
581 commands: Vec::new(),
582 macros: MacroCollection::default(),
583 expansion_names: Vec::new(),
584 expansions: Vec::new(),
585 history: VecDeque::with_capacity(HISTORY_CAPACITY),
586 eval_stack: Vec::new(),
587 };
588
589 builtins::register_commands(&mut this);
590 expansions::register_expansions(&mut this);
591
592 this
593 }
594
595 pub fn register_command(
596 &mut self,
597 plugin_handle: Option<PluginHandle>,
598 name: &'static str,
599 completions: &'static [CompletionSource],
600 command_fn: CommandFn,
601 ) {
602 self.command_names.push(name);
603 self.commands.push(Command {
604 plugin_handle,
605 completions,
606 command_fn,
607 });
608 }
609
610 pub fn register_macro(&mut self, name: &str, source: &str) -> Result<(), CommandError> {
611 if self.find_command(name).is_some() {
612 return Err(CommandError::InvalidMacroName);
613 }
614
615 let mut chars = name.chars();
616 match chars.next() {
617 Some(c) if c.is_ascii_alphabetic() || matches!(c, '-' | '_') => (),
618 _ => return Err(CommandError::InvalidMacroName),
619 }
620 if name
621 .chars()
622 .any(|c| !c.is_ascii_alphanumeric() && !matches!(c, '-' | '_'))
623 {
624 return Err(CommandError::InvalidMacroName);
625 }
626
627 self.macros.add(name, source);
628 Ok(())
629 }
630
631 pub fn register_expansion(
632 &mut self,
633 plugin_handle: Option<PluginHandle>,
634 name: &'static str,
635 expansion_fn: ExpansionFn,
636 ) {
637 self.expansion_names.push(name);
638 self.expansions.push(Expansion {
639 plugin_handle,
640 expansion_fn,
641 });
642 }
643
644 pub fn find_command(&self, name: &str) -> Option<&Command> {
645 let index = self.command_names.iter().position(|&n| n == name)?;
646 Some(&self.commands[index])
647 }
648
649 pub fn command_names(&self) -> &[&'static str] {
650 &self.command_names
651 }
652
653 pub fn expansion_names(&self) -> &[&'static str] {
654 &self.expansion_names
655 }
656
657 pub fn history_len(&self) -> usize {
658 self.history.len()
659 }
660
661 pub fn history_entry(&self, index: usize) -> &str {
662 match self.history.get(index) {
663 Some(e) => &e[..],
664 None => "",
665 }
666 }
667
668 pub fn add_to_history(&mut self, entry: &str) {
669 if entry.is_empty() || entry.starts_with(|c: char| c.is_ascii_whitespace()) {
670 return;
671 }
672 if let Some(back) = self.history.back() {
673 if back == entry {
674 return;
675 }
676 }
677
678 let mut s = if self.history.len() == self.history.capacity() {
679 self.history.pop_front().unwrap()
680 } else {
681 String::new()
682 };
683
684 s.clear();
685 s.push_str(entry);
686 self.history.push_back(s);
687 }
688
689 pub fn unwrap_eval_result(
690 ctx: &mut EditorContext,
691 result: Result<EditorFlow, CommandError>,
692 ) -> EditorFlow {
693 match result {
694 Ok(flow) => flow,
695 Err(error) => {
696 {
697 let mut write = ctx.editor.logger.write(LogKind::Error);
698 write.str("trace:\n");
699 for eval_stack_entry in ctx.editor.commands.eval_stack.drain(..).rev() {
700 write.fmt(format_args!(
701 "\n{}:{}:",
702 eval_stack_entry.name,
703 eval_stack_entry.line_index + 1,
704 ));
705 if eval_stack_entry.command.find('\n').is_some() {
706 for (line_index, line) in eval_stack_entry.command.lines().enumerate() {
707 write.fmt(format_args!("\n {:>4}| ", line_index + 1));
708 write.str(line);
709 }
710 } else {
711 write.str(" ");
712 write.str(&eval_stack_entry.command);
713 }
714
715 ctx.editor.string_pool.release(eval_stack_entry.name);
716 ctx.editor.string_pool.release(eval_stack_entry.command);
717 }
718 }
719
720 {
721 let mut write = ctx.editor.logger.write(LogKind::Error);
722 write.fmt(format_args!("{}", error));
723 }
724
725 EditorFlow::Continue
726 }
727 }
728 }
729
730 pub fn eval(
731 ctx: &mut EditorContext,
732 client_handle: Option<ClientHandle>,
733 name: &str,
734 source: &str,
735 ) -> Result<EditorFlow, CommandError> {
736 Self::eval_recursive(ctx, client_handle, name, source, "", false)
737 }
738
739 fn eval_recursive(
740 ctx: &mut EditorContext,
741 client_handle: Option<ClientHandle>,
742 name: &str,
743 source: &str,
744 args: &str,
745 bang: bool,
746 ) -> Result<EditorFlow, CommandError> {
747 for command in CommandIter(source) {
748 let mut aux = ctx.editor.string_pool.acquire();
749 let mut expanded = ctx.editor.string_pool.acquire();
750 let result = match expand_variables(
751 ctx,
752 client_handle,
753 args,
754 bang,
755 command,
756 &mut aux,
757 &mut expanded,
758 ) {
759 Ok(()) => Self::eval_single(ctx, client_handle, &expanded),
760 Err(error) => Err(CommandError::ExpansionError(error)),
761 };
762 ctx.editor.string_pool.release(aux);
763 ctx.editor.string_pool.release(expanded);
764
765 match result {
766 Ok(EditorFlow::Continue) => (),
767 Ok(flow) => return Ok(flow),
768 Err(error) => {
769 let offset = command.as_ptr() as usize - source.as_ptr() as usize;
770 let line_index = source[..offset].chars().filter(|&c| c == '\n').count() as _;
771
772 ctx.editor.commands.eval_stack.push(EvalStackEntry {
773 name: ctx.editor.string_pool.acquire_with(name),
774 command: ctx.editor.string_pool.acquire_with(command),
775 line_index,
776 });
777 return Err(error);
778 }
779 }
780 }
781
782 Ok(EditorFlow::Continue)
783 }
784
785 fn eval_single(
786 ctx: &mut EditorContext,
787 client_handle: Option<ClientHandle>,
788 command: &str,
789 ) -> Result<EditorFlow, CommandError> {
790 let mut args = CommandArgs(command);
791 let command_name = match args.try_next() {
792 Some(command) => command,
793 None => return Err(CommandError::NoSuchCommand),
794 };
795 let (command_name, bang) = match command_name.strip_suffix('!') {
796 Some(command) => (command, true),
797 None => (command_name, false),
798 };
799
800 if let Some(command) = ctx.editor.commands.find_command(command_name) {
801 let plugin_handle = command.plugin_handle;
802 let command_fn = command.command_fn;
803 let mut io = CommandIO {
804 client_handle,
805 plugin_handle,
806 args,
807 bang,
808 flow: EditorFlow::Continue,
809 };
810 command_fn(ctx, &mut io)?;
811 return Ok(io.flow);
812 }
813
814 if let Some(macro_source) = ctx.editor.commands.macros.find(command_name) {
815 let macro_source = ctx.editor.string_pool.acquire_with(macro_source);
816 let result = Self::eval_recursive(
817 ctx,
818 client_handle,
819 command_name,
820 ¯o_source,
821 args.0,
822 bang,
823 );
824 ctx.editor.string_pool.release(macro_source);
825 return result;
826 }
827
828 Err(CommandError::NoSuchCommand)
829 }
830}
831
832fn expand_variables<'a>(
833 ctx: &mut EditorContext,
834 client_handle: Option<ClientHandle>,
835 args: &str,
836 bang: bool,
837 text: &str,
838 aux: &mut String,
839 output: &mut String,
840) -> Result<(), ExpansionError> {
841 fn write_variable_expansion(
842 ctx: &mut EditorContext,
843 client_handle: Option<ClientHandle>,
844 mut command_args: CommandArgs,
845 command_bang: bool,
846 name: &str,
847 args: &str,
848 output: &mut String,
849 ) -> Result<(), ExpansionError> {
850 let mut args = CommandArgs(args);
851 if name == "arg" {
852 let arg = args.next()?;
853 args.assert_empty()?;
854
855 match arg {
856 "!" => {
857 if command_bang {
858 output.push('!');
859 }
860 }
861 "*" => {
862 let command_args = match command_args.0.strip_suffix('\0') {
863 Some(command_args) => command_args,
864 None => return Err(ExpansionError::IgnoreExpansion),
865 };
866 output.push_str(command_args);
867 }
868 _ => {
869 let mut index: usize =
870 arg.parse().map_err(|_| ExpansionError::InvalidArgIndex)?;
871 while let Some(command_arg) = command_args.try_next() {
872 if index == 0 {
873 output.push_str(command_arg);
874 break;
875 }
876 index -= 1;
877 }
878 }
879 }
880 Ok(())
881 } else {
882 for (i, &expansion_name) in ctx.editor.commands.expansion_names().iter().enumerate() {
883 if expansion_name == name {
884 let expansion = &ctx.editor.commands.expansions[i];
885 let plugin_handle = expansion.plugin_handle;
886 let expansion_fn = expansion.expansion_fn;
887
888 let mut io = ExpansionIO {
889 client_handle,
890 plugin_handle,
891 args,
892 output,
893 };
894 return expansion_fn(ctx, &mut io);
895 }
896 }
897 Err(ExpansionError::NoSuchExpansion)
898 }
899 }
900
901 fn parse_variable_name(text: &str) -> Result<&str, usize> {
902 let mut chars = text.chars();
903 loop {
904 match chars.next() {
905 Some('a'..='z' | '-') => (),
906 Some('(') => {
907 let name = &text[..text.len() - chars.as_str().len() - 1];
908 return Ok(name);
909 }
910 _ => return Err(text.len() - chars.as_str().len()),
911 }
912 }
913 }
914
915 fn parse_variable_args(text: &str) -> Option<&str> {
916 let mut chars = text.chars();
917 let mut balance = 1;
918 loop {
919 match chars.next()? {
920 '(' => balance += 1,
921 ')' => {
922 balance -= 1;
923 if balance == 0 {
924 let rest = chars.as_str();
925 let len = rest.as_ptr() as usize - text.as_ptr() as usize - 1;
926 break Some(&text[..len]);
927 }
928 }
929 '\\' => {
930 chars.next();
931 }
932 _ => (),
933 }
934 }
935 }
936
937 fn write_escaped(mut slice: &str, has_escaping: bool, output: &mut String) {
938 if !has_escaping {
939 output.push_str(slice);
940 return;
941 }
942
943 loop {
944 match slice.find('\\') {
945 Some(i) => {
946 let (before, after) = slice.split_at(i);
947 output.push_str(before);
948 let mut chars = after.chars();
949 chars.next();
950 match chars.next() {
951 Some('t') => output.push('\t'),
952 Some('n') => output.push('\n'),
953 Some(c) => output.push(c),
954 _ => (),
955 }
956 slice = chars.as_str();
957 }
958 None => {
959 output.push_str(slice);
960 break;
961 }
962 }
963 }
964 }
965
966 let aux_prev_len = aux.len();
967
968 'tokens: for token in CommandTokenizer(text) {
969 if !token.can_expand_variables {
970 write_escaped(token.slice, token.has_escaping, output);
971 output.push('\0');
972 continue;
973 }
974
975 let mut rest = token.slice;
976 loop {
977 match rest.find('@') {
978 Some(i) => {
979 let (before, after) = rest.split_at(i);
980 write_escaped(before, token.has_escaping, output);
981 rest = after;
982 }
983 None => {
984 write_escaped(rest, token.has_escaping, output);
985 break;
986 }
987 }
988
989 let variable_name = match parse_variable_name(&rest[1..]) {
990 Ok(name) => name,
991 Err(skip) => {
992 let (before, after) = rest.split_at(skip + 1);
993 write_escaped(before, token.has_escaping, output);
994 rest = after;
995 continue;
996 }
997 };
998
999 let args_skip = 1 + variable_name.len() + 1;
1000 let variable_args = match parse_variable_args(&rest[args_skip..]) {
1001 Some(args) => args,
1002 None => {
1003 let (before, after) = rest.split_at(args_skip);
1004 write_escaped(before, token.has_escaping, output);
1005 rest = after;
1006 continue;
1007 }
1008 };
1009 rest = &rest[args_skip + variable_args.len() + 1..];
1010
1011 let aux_len = aux.len();
1012 expand_variables(ctx, client_handle, args, bang, variable_args, output, aux)?;
1013 let variable_args = &aux[aux_len..];
1014
1015 let result = write_variable_expansion(
1016 ctx,
1017 client_handle,
1018 CommandArgs(args),
1019 bang,
1020 variable_name,
1021 variable_args,
1022 output,
1023 );
1024 match result {
1025 Ok(()) => (),
1026 Err(ExpansionError::IgnoreExpansion) => {
1027 if token.is_simple {
1028 continue 'tokens;
1029 }
1030 }
1031 Err(error) => return Err(error),
1032 }
1033 }
1034
1035 output.push('\0');
1036 }
1037
1038 aux.truncate(aux_prev_len);
1039 Ok(())
1040}
1041
1042#[cfg(test)]
1043mod tests {
1044 use super::*;
1045
1046 use std::{
1047 env,
1048 path::{Path, PathBuf},
1049 };
1050
1051 use crate::{
1052 client::ClientManager, editor::Editor, editor_utils::RegisterKey, platform::Platform,
1053 plugin::PluginCollection,
1054 };
1055
1056 #[test]
1057 fn command_iter() {
1058 let mut commands = CommandIter("cmd");
1059 assert_eq!(Some("cmd"), commands.next());
1060 assert_eq!(None, commands.next());
1061
1062 let mut commands = CommandIter("cmd1\ncmd2");
1063 assert_eq!(Some("cmd1"), commands.next());
1064 assert_eq!(Some("cmd2"), commands.next());
1065 assert_eq!(None, commands.next());
1066
1067 let mut commands = CommandIter("cmd1 {\narg1\n} arg2\ncmd2 {\narg1}\n \t \n ");
1068 assert_eq!(Some("cmd1 {\narg1\n} arg2"), commands.next());
1069 assert_eq!(Some("cmd2 {\narg1}"), commands.next());
1070 assert_eq!(None, commands.next());
1071
1072 let mut commands = CommandIter("cmd1 ' arg\ncmd2 arg'");
1073 assert_eq!(Some("cmd1 ' arg"), commands.next());
1074 assert_eq!(Some("cmd2 arg'"), commands.next());
1075 assert_eq!(None, commands.next());
1076
1077 let mut commands = CommandIter("cmd1 '\ncmd2 arg'");
1078 assert_eq!(Some("cmd1 '"), commands.next());
1079 assert_eq!(Some("cmd2 arg'"), commands.next());
1080 assert_eq!(None, commands.next());
1081
1082 let mut commands = CommandIter(" #cmd1\ncmd2 arg #arg2\n \t #cmd3 arg\ncmd4 arg'");
1083 assert_eq!(Some("cmd2 arg "), commands.next());
1084 assert_eq!(Some("cmd4 arg'"), commands.next());
1085 assert_eq!(None, commands.next());
1086
1087 let mut commands = CommandIter("cmd1 {\n a\n} b {\n c}\ncmd2");
1088 assert_eq!(Some("cmd1 {\n a\n} b {\n c}"), commands.next());
1089 assert_eq!(Some("cmd2"), commands.next());
1090 assert_eq!(None, commands.next());
1091
1092 let mut commands = CommandIter("cmd1 {\ncmd2 arg #arg2\n \t #cmd3 arg}\ncmd4 arg'}");
1093 assert_eq!(
1094 Some("cmd1 {\ncmd2 arg #arg2\n \t #cmd3 arg}\ncmd4 arg'}"),
1095 commands.next()
1096 );
1097 assert_eq!(None, commands.next());
1098 }
1099
1100 #[test]
1101 fn command_tokenizer() {
1102 let mut tokens = CommandTokenizer("cmd arg1 arg2");
1103 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1104 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1105 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1106 assert_eq!(None, tokens.next().map(|t| t.slice));
1107
1108 let mut tokens = CommandTokenizer("cmd arg1\\'arg2");
1109 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1110 assert_eq!(Some("arg1\\"), tokens.next().map(|t| t.slice));
1111 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1112 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1113 assert_eq!(None, tokens.next().map(|t| t.slice));
1114
1115 let mut tokens = CommandTokenizer("cmd arg1\\'arg2'");
1116 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1117 assert_eq!(Some("arg1\\"), tokens.next().map(|t| t.slice));
1118 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1119 assert_eq!(None, tokens.next().map(|t| t.slice));
1120
1121 let mut tokens = CommandTokenizer("cmd 'arg0 \"arg1 ");
1122 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1123 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1124 assert_eq!(Some("arg0"), tokens.next().map(|t| t.slice));
1125 assert_eq!(Some("\""), tokens.next().map(|t| t.slice));
1126 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1127 assert_eq!(None, tokens.next().map(|t| t.slice));
1128
1129 let mut tokens = CommandTokenizer("cmd arg0'arg1 ");
1130 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1131 assert_eq!(Some("arg0"), tokens.next().map(|t| t.slice));
1132 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1133 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1134 assert_eq!(None, tokens.next().map(|t| t.slice));
1135
1136 let mut tokens = CommandTokenizer("cmd arg0\"arg1 ");
1137 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1138 assert_eq!(Some("arg0"), tokens.next().map(|t| t.slice));
1139 assert_eq!(Some("\""), tokens.next().map(|t| t.slice));
1140 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1141 assert_eq!(None, tokens.next().map(|t| t.slice));
1142
1143 let mut tokens = CommandTokenizer("cmd \"aaa\\\"bbb\"");
1144 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1145 assert_eq!(Some("aaa\\\"bbb"), tokens.next().map(|t| t.slice));
1146 assert_eq!(None, tokens.next().map(|t| t.slice));
1147
1148 let mut tokens = CommandTokenizer("cmd 'arg\"0' \"arg'1\"");
1149 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1150 assert_eq!(Some("arg\"0"), tokens.next().map(|t| t.slice));
1151 assert_eq!(Some("arg'1"), tokens.next().map(|t| t.slice));
1152 assert_eq!(None, tokens.next().map(|t| t.slice));
1153
1154 let mut tokens = CommandTokenizer("cmd arg1\"arg2\"arg3");
1155 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1156 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1157 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1158 assert_eq!(Some("arg3"), tokens.next().map(|t| t.slice));
1159 assert_eq!(None, tokens.next().map(|t| t.slice));
1160
1161 let mut tokens = CommandTokenizer("cmd arg1 \" arg2");
1162 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1163 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1164 assert_eq!(Some("\""), tokens.next().map(|t| t.slice));
1165 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1166 assert_eq!(None, tokens.next().map(|t| t.slice));
1167
1168 let mut tokens = CommandTokenizer("cmd arg1 \"arg2");
1169 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1170 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1171 assert_eq!(Some("\""), tokens.next().map(|t| t.slice));
1172 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1173 assert_eq!(None, tokens.next().map(|t| t.slice));
1174
1175 let mut tokens = CommandTokenizer("cmd 'arg1\narg2'");
1176 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1177 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1178 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1179 assert_eq!(None, tokens.next().map(|t| t.slice));
1180
1181 let mut tokens = CommandTokenizer("cmd 'aaa\\'bbb'");
1182 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1183 assert_eq!(Some("aaa\\'bbb"), tokens.next().map(|t| t.slice));
1184 assert_eq!(None, tokens.next().map(|t| t.slice));
1185
1186 let mut tokens = CommandTokenizer("cmd arg1 ' arg2");
1187 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1188 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1189 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1190 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1191 assert_eq!(None, tokens.next().map(|t| t.slice));
1192
1193 let mut tokens = CommandTokenizer("cmd arg1 'arg2");
1194 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1195 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1196 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1197 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1198 assert_eq!(None, tokens.next().map(|t| t.slice));
1199
1200 let mut tokens = CommandTokenizer("cmd arg1'arg2'arg3");
1201 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1202 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1203 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1204 assert_eq!(Some("arg3"), tokens.next().map(|t| t.slice));
1205 assert_eq!(None, tokens.next().map(|t| t.slice));
1206
1207 let mut tokens = CommandTokenizer("cmd {arg}");
1208 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1209 assert_eq!(Some("arg"), tokens.next().map(|t| t.slice));
1210 assert_eq!(None, tokens.next().map(|t| t.slice));
1211
1212 let mut tokens = CommandTokenizer("cmd {arg\\}}");
1213 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1214 assert_eq!(Some("arg\\}"), tokens.next().map(|t| t.slice));
1215 assert_eq!(None, tokens.next().map(|t| t.slice));
1216
1217 let mut tokens = CommandTokenizer("cmd {aa\\{ bb} arg");
1218 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1219 assert_eq!(Some("aa\\{ bb"), tokens.next().map(|t| t.slice));
1220 assert_eq!(Some("arg"), tokens.next().map(|t| t.slice));
1221 assert_eq!(None, tokens.next().map(|t| t.slice));
1222
1223 let mut tokens = CommandTokenizer("cmd arg1{arg2}arg3");
1224 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1225 assert_eq!(Some("arg1"), tokens.next().map(|t| t.slice));
1226 assert_eq!(Some("arg2"), tokens.next().map(|t| t.slice));
1227 assert_eq!(Some("arg3"), tokens.next().map(|t| t.slice));
1228 assert_eq!(None, tokens.next().map(|t| t.slice));
1229
1230 let mut tokens = CommandTokenizer("cmd {'}}'}");
1231 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1232 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1233 assert_eq!(Some("}"), tokens.next().map(|t| t.slice));
1234 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1235 assert_eq!(Some("}"), tokens.next().map(|t| t.slice));
1236 assert_eq!(None, tokens.next().map(|t| t.slice));
1237
1238 let mut tokens = CommandTokenizer("cmd {arg'}{'}");
1239 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1240 assert_eq!(Some("arg'"), tokens.next().map(|t| t.slice));
1241 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1242 assert_eq!(None, tokens.next().map(|t| t.slice));
1243
1244 let mut tokens = CommandTokenizer("cmd }arg");
1245 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1246 assert_eq!(Some("}"), tokens.next().map(|t| t.slice));
1247 assert_eq!(Some("arg"), tokens.next().map(|t| t.slice));
1248 assert_eq!(None, tokens.next().map(|t| t.slice));
1249
1250 let mut tokens = CommandTokenizer("cmd {arg");
1251 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1252 assert_eq!(Some("{"), tokens.next().map(|t| t.slice));
1253 assert_eq!(Some("arg"), tokens.next().map(|t| t.slice));
1254 assert_eq!(None, tokens.next().map(|t| t.slice));
1255
1256 let mut tokens = CommandTokenizer("cmd arg}'{\"arg");
1257 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1258 assert_eq!(Some("arg"), tokens.next().map(|t| t.slice));
1259 assert_eq!(Some("}"), tokens.next().map(|t| t.slice));
1260 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1261 assert_eq!(Some("{"), tokens.next().map(|t| t.slice));
1262 assert_eq!(Some("\""), tokens.next().map(|t| t.slice));
1263 assert_eq!(Some("arg"), tokens.next().map(|t| t.slice));
1264 assert_eq!(None, tokens.next().map(|t| t.slice));
1265
1266 let mut tokens = CommandTokenizer("cmd '{arg}");
1267 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1268 assert_eq!(Some("'"), tokens.next().map(|t| t.slice));
1269 assert_eq!(Some("arg"), tokens.next().map(|t| t.slice));
1270 assert_eq!(None, tokens.next().map(|t| t.slice));
1271
1272 let mut tokens = CommandTokenizer("cmd {\"{(\\\")!\".}|'{(\\')!'.}}");
1273 assert_eq!(Some("cmd"), tokens.next().map(|t| t.slice));
1274 assert_eq!(
1275 Some("\"{(\\\")!\".}|'{(\\')!'.}"),
1276 tokens.next().map(|t| t.slice)
1277 );
1278 assert_eq!(None, tokens.next().map(|t| t.slice));
1279
1280 let mut tokens = CommandTokenizer("@");
1281 let token = tokens.next().unwrap();
1282 assert_eq!("", token.slice);
1283 assert!(!token.can_expand_variables);
1284 assert_eq!(None, tokens.next().map(|t| t.slice));
1285
1286 let mut tokens = CommandTokenizer("@'aa'");
1287 let token = tokens.next().unwrap();
1288 assert_eq!("aa", token.slice);
1289 assert!(!token.can_expand_variables);
1290 assert_eq!(None, tokens.next().map(|t| t.slice));
1291
1292 let mut tokens = CommandTokenizer("@@'aa'");
1293 let token = tokens.next().unwrap();
1294 assert_eq!("aa", token.slice);
1295 assert!(!token.can_expand_variables);
1296 assert_eq!(None, tokens.next().map(|t| t.slice));
1297
1298 let mut tokens = CommandTokenizer("@{{aa}{}}");
1299 let token = tokens.next().unwrap();
1300 assert_eq!("{aa}{}", token.slice);
1301 assert!(!token.can_expand_variables);
1302 assert_eq!(None, tokens.next().map(|t| t.slice));
1303
1304 let mut tokens = CommandTokenizer("@@{{aa}{}}");
1305 let token = tokens.next().unwrap();
1306 assert_eq!("{aa}{}", token.slice);
1307 assert!(!token.can_expand_variables);
1308 assert_eq!(None, tokens.next().map(|t| t.slice));
1309
1310 let mut tokens = CommandTokenizer("@aa");
1311 let token = tokens.next().unwrap();
1312 assert_eq!("@aa", token.slice);
1313 assert!(token.can_expand_variables);
1314 assert_eq!(None, tokens.next().map(|t| t.slice));
1315
1316 let mut tokens = CommandTokenizer("@@aa");
1317 let token = tokens.next().unwrap();
1318 assert_eq!("@@aa", token.slice);
1319 assert!(token.can_expand_variables);
1320 assert_eq!(None, tokens.next().map(|t| t.slice));
1321 }
1322
1323 #[test]
1324 fn variable_expansion() {
1325 let current_dir = env::current_dir().unwrap_or(PathBuf::new());
1326 let mut ctx = EditorContext {
1327 editor: Editor::new(current_dir, String::new()),
1328 platform: Platform::default(),
1329 clients: ClientManager::default(),
1330 plugins: PluginCollection::default(),
1331 };
1332
1333 let register = ctx
1334 .editor
1335 .registers
1336 .get_mut(RegisterKey::from_char('x').unwrap());
1337 register.clear();
1338 register.push_str("my register contents");
1339
1340 let register = ctx
1341 .editor
1342 .registers
1343 .get_mut(RegisterKey::from_char('l').unwrap());
1344 register.clear();
1345 register.push_str("very long register contents");
1346
1347 let register = ctx
1348 .editor
1349 .registers
1350 .get_mut(RegisterKey::from_char('s').unwrap());
1351 register.clear();
1352 register.push_str("short");
1353
1354 let register = ctx
1355 .editor
1356 .registers
1357 .get_mut(RegisterKey::from_char('r').unwrap());
1358 register.clear();
1359 register.push_str("x");
1360
1361 let register = ctx
1362 .editor
1363 .registers
1364 .get_mut(RegisterKey::from_char('t').unwrap());
1365 register.clear();
1366 register.push_str("r");
1367
1368 let buffer = ctx.editor.buffers.add_new();
1369 assert_eq!(0, buffer.handle().0);
1370 buffer.set_path(Path::new("buffer/path0"));
1371 let buffer = ctx.editor.buffers.add_new();
1372 assert_eq!(1, buffer.handle().0);
1373 buffer.set_path(Path::new("buffer/veryverylong/path1"));
1374
1375 let client_handle = ClientHandle(0);
1376 let buffer_view_handle = ctx
1377 .editor
1378 .buffer_views
1379 .add_new(client_handle, BufferHandle(0));
1380
1381 ctx.clients.on_client_joined(client_handle);
1382 ctx.clients
1383 .get_mut(client_handle)
1384 .set_buffer_view_handle(Some(buffer_view_handle), &ctx.editor.buffer_views);
1385
1386 fn assert_expansion(expected_expanded: &str, ctx: &mut EditorContext, text: &str) {
1387 let mut aux = String::new();
1388 let mut expanded = String::new();
1389 let result = expand_variables(
1390 ctx,
1391 Some(ClientHandle(0)),
1392 "",
1393 false,
1394 text,
1395 &mut aux,
1396 &mut expanded,
1397 );
1398 if let Err(error) = result {
1399 panic!("expansion error: {}", error);
1400 }
1401 assert_eq!(expected_expanded, &expanded);
1402 }
1403
1404 let mut aux = String::new();
1405 let mut expanded = String::new();
1406
1407 expanded.clear();
1408 let r = expand_variables(
1409 &mut ctx,
1410 Some(ClientHandle(0)),
1411 "",
1412 false,
1413 " ",
1414 &mut aux,
1415 &mut expanded,
1416 );
1417 assert!(r.is_ok());
1418 assert_eq!("", &expanded);
1419
1420 expanded.clear();
1421 let r = expand_variables(
1422 &mut ctx,
1423 Some(ClientHandle(0)),
1424 "",
1425 false,
1426 "two args",
1427 &mut aux,
1428 &mut expanded,
1429 );
1430 assert!(r.is_ok());
1431 assert_eq!("two\0args\0", &expanded);
1432
1433 assert_expansion("cmd\0", &mut ctx, "cmd");
1434
1435 assert_expansion("my register contents\0", &mut ctx, "@register(x)");
1436 assert_expansion(
1437 "very long register contents short\0",
1438 &mut ctx,
1439 "{@register(l) @register(s)}",
1440 );
1441 assert_expansion(
1442 "short very long register contents\0",
1443 &mut ctx,
1444 "{@register(s) @register(l)}",
1445 );
1446
1447 expanded.clear();
1448 let r = expand_variables(
1449 &mut ctx,
1450 Some(ClientHandle(0)),
1451 "",
1452 false,
1453 "@register()",
1454 &mut aux,
1455 &mut expanded,
1456 );
1457 assert!(matches!(r, Err(ExpansionError::CommandArgsError(CommandArgsError::TooFewArguments))));
1458 expanded.clear();
1459 let r = expand_variables(
1460 &mut ctx,
1461 Some(ClientHandle(0)),
1462 "",
1463 false,
1464 "@register(xx)",
1465 &mut aux,
1466 &mut expanded,
1467 );
1468 assert!(matches!(r, Err(ExpansionError::InvalidRegisterKey)));
1469
1470 assert_expansion("buffer/path0\0", &mut ctx, "@buffer-path()");
1471 assert_expansion(
1472 "cmd\0buffer/path0\0asd\0buffer/path0\0",
1473 &mut ctx,
1474 "cmd @buffer-path() asd @buffer-path()",
1475 );
1476
1477 assert_expansion(
1478 "cmd\0buffer/path0\0asd\0buffer/veryverylong/path1\0fgh\0\0",
1479 &mut ctx,
1480 "cmd @buffer-path(0) asd @buffer-path(1) fgh @buffer-path(2)",
1481 );
1482
1483 assert_expansion("\"\0", &mut ctx, "\"\\\"\"");
1484 assert_expansion("'\0", &mut ctx, "'\\''");
1485 assert_expansion("\\}\0", &mut ctx, "{\\}}");
1486 assert_expansion("\\\0", &mut ctx, "'\\\\'");
1487
1488 expanded.clear();
1489 let r = expand_variables(
1490 &mut ctx,
1491 Some(ClientHandle(0)),
1492 "arg0\0arg1\0arg2\0",
1493 false,
1494 "@arg(*)",
1495 &mut aux,
1496 &mut expanded,
1497 );
1498 if let Err(e) = &r {
1499 eprintln!("aaaaaa ---------------------------- {}", e);
1500 }
1501 assert!(r.is_ok());
1502 assert_eq!("arg0\0arg1\0arg2\0", &expanded);
1503
1504 expanded.clear();
1505 let r = expand_variables(
1506 &mut ctx,
1507 Some(ClientHandle(0)),
1508 "",
1509 false,
1510 "@arg(*)",
1511 &mut aux,
1512 &mut expanded,
1513 );
1514 assert!(r.is_ok());
1515 assert_eq!("", &expanded);
1516
1517 expanded.clear();
1518 let r = expand_variables(
1519 &mut ctx,
1520 Some(ClientHandle(0)),
1521 "arg0\0arg1\0arg2\0",
1522 false,
1523 "@arg(0)",
1524 &mut aux,
1525 &mut expanded,
1526 );
1527 assert!(r.is_ok());
1528 assert_eq!("arg0\0", &expanded);
1529
1530 expanded.clear();
1531 let r = expand_variables(
1532 &mut ctx,
1533 Some(ClientHandle(0)),
1534 "arg0\0arg1\0arg2\0",
1535 false,
1536 "@arg(1)",
1537 &mut aux,
1538 &mut expanded,
1539 );
1540 assert!(r.is_ok());
1541 assert_eq!("arg1\0", &expanded);
1542
1543 expanded.clear();
1544 let r = expand_variables(
1545 &mut ctx,
1546 Some(ClientHandle(0)),
1547 "arg0\0arg1\0arg2\0",
1548 false,
1549 "@arg(2)",
1550 &mut aux,
1551 &mut expanded,
1552 );
1553 assert!(r.is_ok());
1554 assert_eq!("arg2\0", &expanded);
1555
1556 expanded.clear();
1557 let r = expand_variables(
1558 &mut ctx,
1559 Some(ClientHandle(0)),
1560 "arg0\0arg1\0arg2\0",
1561 false,
1562 "@arg(3)",
1563 &mut aux,
1564 &mut expanded,
1565 );
1566 assert!(r.is_ok());
1567 assert_eq!("\0", &expanded);
1568
1569 assert_expansion(
1570 "my register contents\0",
1571 &mut ctx,
1572 "@register(@register(r))",
1573 );
1574 assert_expansion(
1575 "my register contents\0",
1576 &mut ctx,
1577 "@register(@register(@register(t)))",
1578 );
1579 }
1580}