1 module jcli.autocomplete.complete; 2 3 import jcli.core, jcli.introspect, jcli.argparser; 4 5 struct AutoComplete(alias CommandT) 6 { 7 private 8 { 9 alias Info = CommandInfo!CommandT.Arguments; 10 } 11 12 string[] complete(string[] args) 13 { 14 // TODO: currently scrapped, needs more work considering the rewrite. 15 static if (true) 16 { 17 return null; 18 } 19 else 20 { 21 string[] ret; 22 size_t positionalCount; 23 Pattern[] namedFound; 24 25 // TODO: 26 // Map the indices perhaps? 27 // There are so many things wrong with this code. 28 typeof(Info.named[0])[Pattern] namedByPattern; 29 typeof(Info.positional[0])[] positionalByPosition; 30 31 static foreach(pos; Info.positional) 32 positionalByPosition ~= pos; 33 static foreach(named; Info.named) 34 namedByPattern[named.pattern] = named; 35 36 enum State 37 { 38 lookingForNamedValue, 39 lookingForPositionalOrNamed 40 } 41 42 State state; 43 auto parser = ArgParser(args); 44 ArgParser.Result lastResult; 45 46 while(!parser.empty) 47 { 48 lastResult = parser.front; 49 50 if(lastResult.kind == ArgParser.Result.Kind.rawText) 51 { 52 positionalCount++; 53 parser.popFront(); 54 } 55 else 56 { 57 typeof(Info.named[0]) argInfo; 58 foreach(pattern, arg; namedByPattern) 59 { 60 // TODO: this is a wtf 61 enum caseInsensitive = argInfo.flags.has(ArgFlags._caseInsensitiveBit); 62 if(pattern.matches!caseInsensitive(lastResult.nameSlice).matched) 63 { 64 namedFound ~= pattern; 65 argInfo = arg; 66 break; 67 } 68 } 69 70 // Skip over the name 71 parser.popFront(); 72 73 // Skip over its argument (what??) 74 if(parser.front.fullSlice.length 75 && (parser.front.kind & ArgToken.Kind.valueBit)) 76 { 77 lastResult = parser.front; 78 // TODO: Duplicate logic already present in the command parser. 79 if(argInfo.flags.has(ArgFlags._parseAsFlagBit) 80 || (parser.front.fullSlice == "true" 81 || parser.front.fullSlice == "false")) 82 { 83 parser.popFront(); 84 } 85 } 86 } 87 } 88 89 state = (lastResult.kind & ArgToken.Kind.argumentNameBit) 90 ? State.lookingForNamedValue 91 : State.lookingForPositionalOrNamed; 92 93 if (state == State.lookingForNamedValue) 94 { 95 bool isBool = false; 96 static foreach(named; Info.namedArgs) 97 { 98 if(named.pattern.matches(lastResult.nameSlice).empty) 99 { 100 isBool = named.scheme == ArgParseScheme.bool_; 101 102 alias Symbol = getArgSymbol!named; 103 104 import std.traits : isInstanceOf; 105 static if(isInstanceOf!(Nullable, typeof(Symbol))) 106 alias SymbolT = typeof(typeof(Symbol)().get()); 107 else 108 alias SymbolT = typeof(Symbol); 109 110 // Enums are a special case 111 static if(is(SymbolT == enum)) 112 { 113 static foreach(name; __traits(allMembers, SymbolT)) 114 ret ~= name; 115 } 116 117 static foreach(uda; __traits(getAttributes, Symbol)) 118 { 119 // TODO: 120 } 121 } 122 } 123 124 if(ret.length == 0) 125 { 126 if(isBool) 127 ret ~= ["true", "false"]; 128 else 129 ret ~= "[Value for argument "~lastResult.nameSlice~"]"; 130 } 131 } 132 else 133 { 134 if(positionalCount < positionalByPosition.length) 135 { 136 foreach(pos; positionalByPosition[positionalCount..$]) 137 ret ~= "<"~pos.uda.name~">"; 138 } 139 140 import std.algorithm; 141 foreach(pattern; namedByPattern.byKey.filter!(k => !namedFound.canFind(k))) 142 { 143 foreach(p; pattern.patterns.map!(p => p.length == 1 ? "-"~p : "--"~p)) 144 ret ~= p; 145 } 146 } 147 148 return ret; 149 } 150 } 151 }