pepper/
command.rs

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                &macro_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}