mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-01 01:25:27 +08:00 
			
		
		
		
	Parse quoted strings correctly
When parsing quoted strings, space was not handled properly in remaining arguments. Fixes #186
This commit is contained in:
		 Patrik Svensson
					Patrik Svensson
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							241423dd16
						
					
				
				
					commit
					79742ce9e3
				
			| @@ -0,0 +1,39 @@ | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Linq; | ||||
| using Spectre.Console.Cli; | ||||
|  | ||||
| namespace Spectre.Console.Tests.Data | ||||
| { | ||||
|     public sealed class DumpRemainingCommand : Command<EmptyCommandSettings> | ||||
|     { | ||||
|         private readonly IAnsiConsole _console; | ||||
|  | ||||
|         public DumpRemainingCommand(IAnsiConsole console) | ||||
|         { | ||||
|             _console = console; | ||||
|         } | ||||
|  | ||||
|         public override int Execute([NotNull] CommandContext context, [NotNull] EmptyCommandSettings settings) | ||||
|         { | ||||
|             if (context.Remaining.Raw.Count > 0) | ||||
|             { | ||||
|                 _console.WriteLine("# Raw"); | ||||
|                 foreach (var item in context.Remaining.Raw) | ||||
|                 { | ||||
|                     _console.WriteLine(item); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (context.Remaining.Parsed.Count > 0) | ||||
|             { | ||||
|                 _console.WriteLine("# Parsed"); | ||||
|                 foreach (var item in context.Remaining.Parsed) | ||||
|                 { | ||||
|                     _console.WriteLine(string.Format("{0}={1}", item.Key, string.Join(",", item.Select(x => x)))); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,22 +0,0 @@ | ||||
| using System; | ||||
| using Spectre.Console.Cli; | ||||
|  | ||||
| namespace Spectre.Console.Tests.Data | ||||
| { | ||||
|     public sealed class InterceptingCommand<TSettings> : Command<TSettings> | ||||
|     where TSettings : CommandSettings | ||||
|     { | ||||
|         private readonly Action<CommandContext, TSettings> _action; | ||||
|  | ||||
|         public InterceptingCommand(Action<CommandContext, TSettings> action) | ||||
|         { | ||||
|             _action = action ?? throw new ArgumentNullException(nameof(action)); | ||||
|         } | ||||
|  | ||||
|         public override int Execute(CommandContext context, TSettings settings) | ||||
|         { | ||||
|             _action(context, settings); | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,3 @@ | ||||
| # Raw | ||||
| /c | ||||
| set && pause | ||||
| @@ -11,7 +11,8 @@ namespace Spectre.Console.Tests.Unit.Cli | ||||
| { | ||||
|     public sealed partial class CommandAppTests | ||||
|     { | ||||
|         public static class Parsing | ||||
|         [UsesVerify] | ||||
|         public sealed class Parsing | ||||
|         { | ||||
|             [UsesVerify] | ||||
|             public sealed class UnknownCommand | ||||
| @@ -575,6 +576,23 @@ namespace Spectre.Console.Tests.Unit.Cli | ||||
|                     result.ShouldBe("Error: Command 'dog' is missing required argument 'AGE'."); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             public Task Should_Parse_Quoted_Strings_Correctly() | ||||
|             { | ||||
|                 // Given | ||||
|                 var fixture = new Fixture(); | ||||
|                 fixture.Configure(configurator => | ||||
|                 { | ||||
|                     configurator.AddCommand<DumpRemainingCommand>("foo"); | ||||
|                 }); | ||||
|  | ||||
|                 // When | ||||
|                 var result = fixture.Run("foo", "--", "/c", "\"set && pause\""); | ||||
|  | ||||
|                 // Then | ||||
|                 return Verifier.Verify(result); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         internal sealed class Fixture | ||||
|   | ||||
| @@ -720,7 +720,7 @@ namespace Spectre.Console.Tests.Unit.Cli | ||||
|                 ctx.Remaining.Raw[0].ShouldBe("--foo"); | ||||
|                 ctx.Remaining.Raw[1].ShouldBe("bar"); | ||||
|                 ctx.Remaining.Raw[2].ShouldBe("-bar"); | ||||
|                 ctx.Remaining.Raw[3].ShouldBe("\"baz\""); | ||||
|                 ctx.Remaining.Raw[3].ShouldBe("baz"); | ||||
|                 ctx.Remaining.Raw[4].ShouldBe("qux"); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| <SolutionConfiguration> | ||||
|   <Settings> | ||||
|     <AllowParallelTestExecution>True</AllowParallelTestExecution> | ||||
|     <InstrumentationMode>Optimised</InstrumentationMode> | ||||
|     <SolutionConfigured>True</SolutionConfigured> | ||||
|   </Settings> | ||||
| </SolutionConfiguration> | ||||
| @@ -24,6 +24,7 @@ namespace Spectre.Console.Cli.Internal | ||||
|             } | ||||
|  | ||||
|             _registrar.RegisterInstance(typeof(IConfiguration), configuration); | ||||
|             _registrar.RegisterInstance(typeof(IAnsiConsole), configuration.Settings.Console.GetConsole()); | ||||
|  | ||||
|             // Create the command model. | ||||
|             var model = CommandModelBuilder.Build(configuration); | ||||
|   | ||||
| @@ -116,34 +116,41 @@ namespace Spectre.Console.Cli.Internal | ||||
|         { | ||||
|             var position = reader.Position; | ||||
|  | ||||
|             context.FlushRemaining(); | ||||
|             reader.Consume('\"'); | ||||
|             context.AddRemaining('\"'); | ||||
|  | ||||
|             var builder = new StringBuilder(); | ||||
|             var terminated = false; | ||||
|             while (!reader.ReachedEnd) | ||||
|             { | ||||
|                 var character = reader.Peek(); | ||||
|                 if (character == '\"') | ||||
|                 { | ||||
|                     terminated = true; | ||||
|                     reader.Read(); | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|                 context.AddRemaining(character); | ||||
|                 builder.Append(reader.Read()); | ||||
|             } | ||||
|  | ||||
|             if (reader.Peek() != '\"') | ||||
|             if (!terminated) | ||||
|             { | ||||
|                 var unterminatedQuote = builder.ToString(); | ||||
|                 var token = new CommandTreeToken(CommandTreeToken.Kind.String, position, unterminatedQuote, $"\"{unterminatedQuote}"); | ||||
|                 throw CommandParseException.UnterminatedQuote(reader.Original, token); | ||||
|             } | ||||
|  | ||||
|             reader.Read(); | ||||
|             context.AddRemaining('\"'); | ||||
|             var quotedString = builder.ToString(); | ||||
|  | ||||
|             var value = builder.ToString(); | ||||
|             return new CommandTreeToken(CommandTreeToken.Kind.String, position, value, $"\"{value}\""); | ||||
|             // Add to the context | ||||
|             context.AddRemaining(quotedString); | ||||
|             context.FlushRemaining(); | ||||
|  | ||||
|             return new CommandTreeToken( | ||||
|                 CommandTreeToken.Kind.String, | ||||
|                 position, quotedString, | ||||
|                 quotedString); | ||||
|         } | ||||
|  | ||||
|         private static IEnumerable<CommandTreeToken> ScanOptions(CommandTreeTokenizerContext context, TextBuffer reader) | ||||
|   | ||||
| @@ -32,6 +32,14 @@ namespace Spectre.Console.Cli.Internal | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void AddRemaining(string text) | ||||
|         { | ||||
|             if (Mode == CommandTreeTokenizer.Mode.Remaining) | ||||
|             { | ||||
|                 _builder.Append(text); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void FlushRemaining() | ||||
|         { | ||||
|             if (Mode == CommandTreeTokenizer.Mode.Remaining) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user