-
Notifications
You must be signed in to change notification settings - Fork 418
Expand file tree
/
Copy pathArgument.cs
More file actions
186 lines (161 loc) · 6.96 KB
/
Argument.cs
File metadata and controls
186 lines (161 loc) · 6.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.CommandLine.Binding;
using System.CommandLine.Parsing;
using System.CommandLine.Completions;
using System.Linq;
namespace System.CommandLine
{
/// <summary>
/// A symbol defining a value that can be passed on the command line to a <see cref="Command">command</see> or <see cref="Option">option</see>.
/// </summary>
public abstract class Argument : Symbol
{
private ArgumentArity _arity;
private TryConvertArgument? _convertArguments;
private List<Func<CompletionContext, IEnumerable<CompletionItem>>>? _completionSources = null;
private List<Action<ArgumentResult>>? _validators = null;
/// <summary>
/// Initializes a new instance of the Argument class.
/// </summary>
/// <param name="name">The name of the argument. This can be used to look up the parsed value and is displayed in help</param>
protected Argument(string name) : base(name, allowWhitespace: true)
{
}
/// <summary>
/// Gets or sets the arity of the argument.
/// </summary>
public ArgumentArity Arity
{
get
{
if (!_arity.IsNonDefault)
{
_arity = ArgumentArity.Default(this, FirstParent);
}
return _arity;
}
set => _arity = value;
}
/// <summary>
/// Gets or sets a value indicating whether this argument captures all remaining tokens.
/// </summary>
/// <remarks>
/// When set to <see langword="true"/>, once the parser starts filling this argument,
/// all subsequent tokens are consumed as argument values regardless of whether they
/// match known options or commands. This behaves as if <c>--</c> were implicitly
/// inserted before the argument's first value.
/// <para>
/// An argument with this property set to <see langword="true"/> must be the last
/// argument defined on its parent command.
/// </para>
/// </remarks>
public bool CaptureRemainingTokens { get; set; }
/// <summary>
/// Gets or sets the placeholder name shown in usage help for the argument's value.
/// The value will be wrapped in angle brackets (<c><</c> and <c>></c>).
/// </summary>
/// <remarks>
/// If <c>null</c>, the <see cref="Symbol.Name"/> of the argument will be used.
/// </remarks>
/// <example>
/// An argument with <see cref="Symbol.Name"/> of <c>argument</c> and a
/// <see cref="HelpName"/> of <c>Value</c> will be shown in usage help as:
/// <c><Value></c>. If <see cref="HelpName"/> is not set,
/// help output will show: <c><argument></c>.
/// </example>
/// <value>
/// The name to show as the placeholder for the argument's value.
/// </value>
public string? HelpName { get; set; }
internal TryConvertArgument? ConvertArguments
{
get => _convertArguments ??= ArgumentConverter.GetConverter(this);
set => _convertArguments = value;
}
/// <summary>
/// Gets the list of completion sources for the argument.
/// </summary>
public List<Func<CompletionContext, IEnumerable<CompletionItem>>> CompletionSources
{
get
{
if (_completionSources is null)
{
Type? valueType = ValueType;
if (IsBoolean())
{
_completionSources = new ()
{
static _ => new CompletionItem[]
{
new(bool.TrueString),
new(bool.FalseString)
}
};
}
else if (!valueType.IsPrimitive && (valueType.IsEnum || (valueType.TryGetNullableType(out valueType) && valueType.IsEnum)))
{
_completionSources = new()
{
_ => Enum.GetNames(valueType).Select(n => new CompletionItem(n))
};
}
else
{
_completionSources = new();
}
}
return _completionSources;
}
}
/// <summary>
/// Gets or sets the <see cref="Type" /> that the argument's parsed tokens will be converted to.
/// </summary>
public abstract Type ValueType { get; }
/// <summary>
/// Provides a list of argument validators. Validators can be used
/// to provide custom errors based on user input.
/// </summary>
public List<Action<ArgumentResult>> Validators => _validators ??= new ();
internal bool HasValidators => (_validators?.Count ?? 0) > 0;
/// <summary>
/// Gets the default value for the argument.
/// </summary>
/// <returns>Returns the default value for the argument, if defined. Null otherwise.</returns>
public object? GetDefaultValue()
{
var command = Parents.FlattenBreadthFirst(x => x.Parents)
.OfType<Command>()
.FirstOrDefault();
return GetDefaultValue(new ArgumentResult(this, new SymbolResultTree(command), null));
}
internal abstract object? GetDefaultValue(ArgumentResult argumentResult);
/// <summary>
/// Specifies if a default value is defined for the argument.
/// </summary>
public abstract bool HasDefaultValue { get; }
/// <inheritdoc />
public override IEnumerable<CompletionItem> GetCompletions(CompletionContext context)
{
return CompletionSources
.SelectMany(source => source.Invoke(context))
.Distinct()
.OrderBy(c => c.SortText, StringComparer.OrdinalIgnoreCase);
}
/// <inheritdoc />
public override string ToString() => $"{nameof(Argument)}: {Name}";
internal bool IsBoolean() => ValueType == typeof(bool) || ValueType == typeof(bool?);
internal static Argument None { get; } = new NoArgument();
private sealed class NoArgument : Argument
{
internal NoArgument() : base("@none")
{
}
public override Type ValueType { get; } = typeof(void);
internal override object? GetDefaultValue(ArgumentResult argumentResult) => null;
public override bool HasDefaultValue => false;
}
}
}