winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }