CompletionFlow.cpp (8418B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "CompletionFlow.h" 5 6 namespace AppInstaller::CLI::Workflow 7 { 8 using namespace AppInstaller::CLI::Execution; 9 using namespace AppInstaller::Utility::literals; 10 11 namespace 12 { 13 // Outputs the completion string, wrapping it in quotes if needed. 14 void OutputCompletionString(Execution::OutputStream& stream, std::string_view value) 15 { 16 if (value.find_first_of(' ') != std::string_view::npos) 17 { 18 stream << '"' << value << '"' << std::endl; 19 } 20 else 21 { 22 stream << value << std::endl; 23 } 24 } 25 } 26 27 void CompleteSourceName(Execution::Context& context) 28 { 29 const std::string& word = context.Get<Data::CompletionData>().Word(); 30 auto stream = context.Reporter.Completion(); 31 32 for (const auto& source : Repository::Source::GetCurrentSources()) 33 { 34 if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(source.Name, word)) 35 { 36 OutputCompletionString(stream, source.Name); 37 } 38 } 39 } 40 41 void RequireCompletionWordNonEmpty(Execution::Context& context) 42 { 43 if (context.Get<Data::CompletionData>().Word().empty()) 44 { 45 AICLI_LOG(CLI, Verbose, << "Completion word empty, cannot complete"); 46 AICLI_TERMINATE_CONTEXT(E_NOT_SET); 47 } 48 } 49 50 void CompleteWithMatchedField(Execution::Context& context) 51 { 52 auto& searchResult = context.Get<Execution::Data::SearchResult>(); 53 auto stream = context.Reporter.Completion(); 54 55 for (size_t i = 0; i < searchResult.Matches.size(); ++i) 56 { 57 if (searchResult.Matches[i].MatchCriteria.Value.empty()) 58 { 59 OutputCompletionString(stream, searchResult.Matches[i].Package->GetProperty(Repository::PackageProperty::Id)); 60 } 61 else 62 { 63 OutputCompletionString(stream, searchResult.Matches[i].MatchCriteria.Value); 64 } 65 } 66 } 67 68 void CompleteWithSearchResultVersions(Execution::Context& context) 69 { 70 const std::string& word = context.Get<Data::CompletionData>().Word(); 71 auto stream = context.Reporter.Completion(); 72 73 for (const auto& ap : context.Get<Execution::Data::Package>()->GetAvailable()) 74 { 75 for (const auto& vc : ap->GetVersionKeys()) 76 { 77 if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Version, word)) 78 { 79 OutputCompletionString(stream, vc.Version); 80 } 81 } 82 } 83 } 84 85 void CompleteWithSearchResultInstalledVersions(Execution::Context& context) 86 { 87 const std::string& word = context.Get<Data::CompletionData>().Word(); 88 auto stream = context.Reporter.Completion(); 89 90 auto installedPackage = context.Get<Execution::Data::Package>()->GetInstalled(); 91 92 if (installedPackage) 93 { 94 for (const auto& vc : installedPackage->GetVersionKeys()) 95 { 96 if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Version, word)) 97 { 98 OutputCompletionString(stream, vc.Version); 99 } 100 } 101 } 102 } 103 104 void CompleteWithSearchResultChannels(Execution::Context& context) 105 { 106 const std::string& word = context.Get<Data::CompletionData>().Word(); 107 auto stream = context.Reporter.Completion(); 108 109 std::vector<std::string> channels; 110 111 for (const auto& ap : context.Get<Execution::Data::Package>()->GetAvailable()) 112 { 113 for (const auto& vc : ap->GetVersionKeys()) 114 { 115 if ((word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Channel, word)) && 116 std::find(channels.begin(), channels.end(), vc.Channel) == channels.end()) 117 { 118 channels.emplace_back(vc.Channel); 119 } 120 } 121 } 122 123 for (const auto& c : channels) 124 { 125 OutputCompletionString(stream, c); 126 } 127 } 128 129 void CompleteWithSingleSemanticsForValue::operator()(Execution::Context& context) const 130 { 131 switch (m_type) 132 { 133 case Execution::Args::Type::Query: 134 case Execution::Args::Type::MultiQuery: 135 case Execution::Args::Type::Id: 136 case Execution::Args::Type::Name: 137 case Execution::Args::Type::Moniker: 138 case Execution::Args::Type::Tag: 139 case Execution::Args::Type::Command: 140 case Execution::Args::Type::Version: 141 case Execution::Args::Type::Channel: 142 context << 143 Workflow::OpenSource(); 144 break; 145 } 146 147 context << CompleteWithSingleSemanticsForValueUsingExistingSource(m_type); 148 } 149 150 void CompleteWithSingleSemanticsForValueUsingExistingSource::operator()(Execution::Context& context) const 151 { 152 switch (m_type) 153 { 154 case Execution::Args::Type::Query: 155 case Execution::Args::Type::MultiQuery: 156 context << 157 Workflow::RequireCompletionWordNonEmpty << 158 Workflow::SearchSourceForSingleCompletion << 159 Workflow::CompleteWithMatchedField; 160 break; 161 case Execution::Args::Type::Manifest: 162 // Intentionally output none to enable pass through to filesystem. 163 break; 164 case Execution::Args::Type::Id: 165 context << 166 Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Id) << 167 Workflow::CompleteWithMatchedField; 168 break; 169 case Execution::Args::Type::Name: 170 context << 171 Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Name) << 172 Workflow::CompleteWithMatchedField; 173 break; 174 case Execution::Args::Type::Moniker: 175 context << 176 Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Moniker) << 177 Workflow::CompleteWithMatchedField; 178 break; 179 case Execution::Args::Type::Tag: 180 context << 181 Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Tag) << 182 Workflow::CompleteWithMatchedField; 183 break; 184 case Execution::Args::Type::Command: 185 context << 186 Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Command) << 187 Workflow::CompleteWithMatchedField; 188 break; 189 case Execution::Args::Type::Version: 190 // Here we require that the standard search finds a single entry, and we list those versions. 191 context << 192 Workflow::SearchSourceForSingle << 193 Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << 194 Workflow::CompleteWithSearchResultVersions; 195 break; 196 case Execution::Args::Type::TargetVersion: 197 // Here we require that the standard search finds a single entry, and we list the installed versions. 198 context << 199 Workflow::SearchSourceForSingle << 200 Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << 201 Workflow::CompleteWithSearchResultInstalledVersions; 202 break; 203 case Execution::Args::Type::Channel: 204 // Here we require that the standard search finds a single entry, and we list those channels. 205 context << 206 Workflow::SearchSourceForSingle << 207 Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << 208 Workflow::CompleteWithSearchResultChannels; 209 break; 210 case Execution::Args::Type::Source: 211 context << 212 Workflow::CompleteSourceName; 213 break; 214 } 215 } 216 217 void CompleteWithEmptySet(Execution::Context& context) 218 { 219 context.Reporter.Completion() << std::endl; 220 } 221 }