@@ -18,6 +18,13 @@ public import manager.console.session;
1818public import manager.expression : NamedArgument;
1919
2020
21+ // UDA to attach custom tab completion to a command function
22+ struct TabComplete
23+ {
24+ Array! String function (bool is_value, const (char )[] name, const (char )[] value) nothrow @nogc suggest;
25+ }
26+
27+
2128// uses GC
2229char [] transformCommandName (const (char )[] name)
2330{
@@ -30,6 +37,7 @@ char[] transformCommandName(const(char)[] name)
3037 }
3138 return result;
3239}
40+ enum TransformCommandName (const (char )[] name) = transformCommandName(name);
3341
3442nothrow @nogc :
3543
@@ -104,14 +112,14 @@ nothrow @nogc:
104112
105113 FunctionCommand fnCmd = console.m_allocator.allocT! FunctionCommand(console, commandName ? commandName.makeString(defaultAllocator) : StringLit! FunctionName, cast (void * )i, &functionAdapter);
106114
107- alias ParamNames = parameter_identifier_tuple! fun[1 .. $];
115+ alias ParamNames = STATIC_MAP ! (TransformCommandName, parameter_identifier_tuple! fun[1 .. $]) ;
108116 alias Params = STATIC_MAP ! (Unqual, Parameters! fun[1 .. $]);
109117
110118 static foreach (j; 0 .. ParamNames.length)
111119 {
112- static if (ParamNames[j] != " args" )
120+ static if (ParamNames[j] != " args" && ParamNames[j] != " named_args " )
113121 {{
114- fnCmd.args ~= FunctionArgument(StringLit! (ParamNames[j].transformCommandName() ));
122+ fnCmd.args ~= FunctionArgument(StringLit! (ParamNames[j]));
115123 static if (is (Params[j] == Nullable! T, T))
116124 alias ArgTy = T;
117125 else
@@ -121,6 +129,13 @@ nothrow @nogc:
121129 }}
122130 }
123131
132+ // Check for TabComplete UDA on the function
133+ static foreach (attr; __traits (getAttributes , fun))
134+ {
135+ static if (is (typeof (attr) == TabComplete))
136+ fnCmd.custom_suggest = attr.suggest;
137+ }
138+
124139 return fnCmd;
125140 }
126141
@@ -211,20 +226,30 @@ private:
211226 void * instance;
212227 GenericCall fn;
213228 Array! FunctionArgument args;
229+ Array! String function (bool , const (char )[], const (char )[]) nothrow @nogc custom_suggest;
214230
215- Array! String suggestArgs (const (char )[] argPrefix )
231+ Array! String suggestArgs (const (char )[] arg_prefix )
216232 {
217233 Array! String suggestions;
234+ if (custom_suggest ! is null )
235+ suggestions = custom_suggest(false , arg_prefix, null );
218236 foreach (ref arg; args)
219237 {
220- if (arg.name.startsWith(argPrefix ))
238+ if (arg.name.startsWith(arg_prefix ))
221239 suggestions ~= String(MutableString! 0 (Concat, arg.name, ' =' )); // TODO: MOVE construct!
222240 }
223241 return suggestions;
224242 }
225243
226244 Array! String suggestValues (const (char )[] argument, const (char )[] value)
227245 {
246+ if (custom_suggest ! is null )
247+ {
248+ Array! String suggestions = custom_suggest(true , argument, value);
249+ if (suggestions.length > 0 )
250+ return suggestions;
251+ }
252+
228253 foreach (ref arg; args)
229254 {
230255 if (arg.name[] == argument[])
@@ -253,7 +278,7 @@ auto makeArgTuple(alias F)(const Variant[] args, const NamedArgument[] parameter
253278 import urt.meta;
254279
255280 alias Params = STATIC_MAP ! (Unqual, Parameters! F[1 .. $]);
256- alias ParamNames = parameter_identifier_tuple! F[1 .. $];
281+ alias ParamNames = STATIC_MAP ! (TransformCommandName, parameter_identifier_tuple! F[1 .. $]) ;
257282
258283 Tuple ! Params params;
259284 error = null ;
@@ -265,15 +290,18 @@ auto makeArgTuple(alias F)(const Variant[] args, const NamedArgument[] parameter
265290 {
266291 static foreach (i, P; Params)
267292 {
268- case Alias! (transformCommandName(ParamNames[i])):
269- error = convertVariant(param.value, params[i]);
270- if (error)
271- {
272- error = tconcat(" Argument '" , param.name, " ' error: " , error);
273- break outer;
274- }
275- gotArg[i] = true ;
276- break param_switch;
293+ static if (ParamNames[i] != " args" && ParamNames[i] != " named-args" )
294+ {
295+ case ParamNames[i]:
296+ error = convertVariant(param.value, params[i]);
297+ if (error)
298+ {
299+ error = tconcat(" Argument '" , param.name, " ' error: " , error);
300+ break outer;
301+ }
302+ gotArg[i] = true ;
303+ break param_switch;
304+ }
277305 }
278306 default :
279307 error = tconcat(" Unknown parameter '" , param.name, " '" );
@@ -284,16 +312,21 @@ auto makeArgTuple(alias F)(const Variant[] args, const NamedArgument[] parameter
284312 static foreach (i, P; Params)
285313 {
286314 {
287- static if (transformCommandName( ParamNames[i]) == " args" )
315+ static if (ParamNames[i] == " args" )
288316 {
289- static assert (is (P == Variant [] ), " `args` parameter must be of type Variant[]" );
317+ static assert (is (const ( Variant )[] : P ), " `args` parameter must be of type const( Variant) []" );
290318 params[i] = args;
291319 }
320+ else static if (ParamNames[i] == " named-args" )
321+ {
322+ static assert (is (const (NamedArgument)[] : P), " `named_args` parameter must be of type const(NamedArgument)[]" );
323+ params[i] = parameters;
324+ }
292325 else static if (! is (P : Nullable! U, U))
293326 {
294327 if (! gotArg[i])
295328 {
296- error = tconcat(" Missing argument: " , Alias ! (transformCommandName( ParamNames[i])) );
329+ error = tconcat(" Missing argument: " , ParamNames[i]);
297330 goto done;
298331 }
299332 }
0 commit comments