Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions doc/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,30 @@ This feature enables support for additional source command improvements via `win
"sourceEdit": true
},
```

### listDetails

This feature enables support for displaying detailed output from the `list` command. Rather than a table view of the results, when the `--details` option is provided
to the `list` command, it will output information similar to how `show` would. Most of the data presented is directly from the local installation.

Example output:
```PowerShell
> winget list Microsoft.VisualStudio.2022.Enterprise --details
Visual Studio Enterprise 2022 [Microsoft.VisualStudio.2022.Enterprise]
Version: 17.14.21 (November 2025)
Publisher: Microsoft Corporation
Local Identifier: ARP\Machine\X86\875fed29
Product Code: 875fed29
Installer Category: exe
Installed Scope: Machine
Installed Location: C:\Program Files\Microsoft Visual Studio\2022\Enterprise
Available Upgrades:
winget [17.14.23]
```

To enable:
```json
"experimentalFeatures": {
"listDetails": true
},
```
10 changes: 10 additions & 0 deletions schemas/JSON/settings/settings.schema.0.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,16 @@
"description": "Enable support for managing fonts",
"type": "boolean",
"default": false
},
"listDetails": {
"description": "Enable detailed output option for list command",
"type": "boolean",
"default": false
},
"sourceEdit": {
"description": "Enable source edit command",
"type": "boolean",
"default": false
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/AppInstallerCLICore/Argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ namespace AppInstaller::CLI
case Execution::Args::Type::TargetVersion:
return { type, "version"_liv, 'v', ArgTypeCategory::SinglePackageQuery, ArgTypeExclusiveSet::AllAndTargetVersion };

//Source Command
// Source Command
case Execution::Args::Type::SourceName:
return { type, "name"_liv, 'n' };
case Execution::Args::Type::SourceType:
Expand All @@ -124,13 +124,13 @@ namespace AppInstaller::CLI
case Execution::Args::Type::SourceEditExplicit:
return { type, "explicit"_liv, 'e' };

//Hash Command
// Hash Command
case Execution::Args::Type::HashFile:
return { type, "file"_liv, 'f' };
case Execution::Args::Type::Msix:
return { type, "msix"_liv, 'm' };

//Validate Command
// Validate Command
case Execution::Args::Type::ValidateManifest:
return { type, "manifest"_liv };
case Execution::Args::Type::IgnoreWarnings:
Expand Down Expand Up @@ -183,6 +183,8 @@ namespace AppInstaller::CLI
// List command
case Execution::Args::Type::Upgrade:
return { type, "upgrade-available"_liv};
case Execution::Args::Type::ListDetails:
return { type, "details"_liv };

// Pin command
case Execution::Args::Type::GatedVersion:
Expand Down Expand Up @@ -482,6 +484,8 @@ namespace AppInstaller::CLI
return Argument{ type, Resource::String::FontDetailsArgumentDescription, ArgumentType::Flag, false };
case Args::Type::Correlation:
return Argument{ type, Resource::String::CorrelationArgumentDescription, ArgumentType::Standard, Argument::Visibility::Hidden };
case Args::Type::ListDetails:
return Argument{ type, Resource::String::ListDetailsArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help, ExperimentalFeature::Feature::ListDetails };
default:
THROW_HR(E_UNEXPECTED);
}
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/ListCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace AppInstaller::CLI
Argument{ Execution::Args::Type::Upgrade, Resource::String::UpgradeArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help },
Argument{ Execution::Args::Type::IncludeUnknown, Resource::String::IncludeUnknownInListArgumentDescription, ArgumentType::Flag },
Argument{ Execution::Args::Type::IncludePinned, Resource::String::IncludePinnedInListArgumentDescription, ArgumentType::Flag},
Argument::ForType(Execution::Args::Type::ListDetails),
};
}

Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/ExecutionArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ namespace AppInstaller::CLI::Execution

// List Command
Upgrade, // Used in List command to only show versions with upgrades
ListDetails,

// Pin command
GatedVersion, // Differs from Version in that this supports wildcards
Expand Down
13 changes: 13 additions & 0 deletions src/AppInstallerCLICore/Resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(Links);
WINGET_DEFINE_RESOURCE_STRINGID(ListCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(ListCommandShortDescription);
WINGET_DEFINE_RESOURCE_STRINGID(ListDetailsArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(LocaleArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(LocationArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(LogArgumentDescription);
Expand Down Expand Up @@ -635,6 +636,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(ShowCommandShortDescription);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelAgreements);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelAuthor);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelChannel);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelCopyright);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelCopyrightUrl);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelDependencies);
Expand Down Expand Up @@ -666,6 +668,17 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelVersion);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelWindowsFeaturesDependencies);
WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelWindowsLibrariesDependencies);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListAvailableUpgrades);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledArchitecture);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledLocale);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledLocation);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledScope);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledSource);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstallerCategory);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListLocalIdentifier);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListPackageFamilyName);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListProductCode);
WINGET_DEFINE_RESOURCE_STRINGID(ShowListUpgradeCode);
WINGET_DEFINE_RESOURCE_STRINGID(ShowVersion);
WINGET_DEFINE_RESOURCE_STRINGID(SilentArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(SingleCharAfterDashError);
Expand Down
13 changes: 10 additions & 3 deletions src/AppInstallerCLICore/Sixel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,15 +432,18 @@ namespace AppInstaller::CLI::VirtualTerminal::Sixel
m_sourceImage = anon::CacheToBitmap(m_factory.get(), decodedFrame.get());
}

ImageSource::ImageSource(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding)
ImageSource::ImageSource(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding) :
ImageSource(Utility::ReadEntireStreamAsByteArray(imageStream), imageEncoding)
{
}

ImageSource::ImageSource(const std::vector<uint8_t>& imageBytes, Manifest::IconFileTypeEnum imageEncoding)
{
m_factory = anon::CreateFactory();

wil::com_ptr<IStream> stream;
THROW_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream));

auto imageBytes = Utility::ReadEntireStreamAsByteArray(imageStream);

ULONG written = 0;
THROW_IF_FAILED(stream->Write(imageBytes.data(), static_cast<ULONG>(imageBytes.size()), &written));
THROW_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr));
Expand Down Expand Up @@ -637,6 +640,10 @@ namespace AppInstaller::CLI::VirtualTerminal::Sixel
m_imageSource(imageStream, imageEncoding)
{}

Image::Image(const std::vector<uint8_t>& imageBytes, Manifest::IconFileTypeEnum imageEncoding) :
m_imageSource(imageBytes, imageEncoding)
{}

Image& Image::AspectRatio(Sixel::AspectRatio aspectRatio)
{
m_renderControls.AspectRatio = aspectRatio;
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/Sixel.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ namespace AppInstaller::CLI::VirtualTerminal::Sixel
// Create an image source from a stream.
ImageSource(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding);

// Create an image source from bytes.
ImageSource(const std::vector<uint8_t>& imageBytes, Manifest::IconFileTypeEnum imageEncoding);

// Resize the image to the given width and height, factoring in the target aspect ratio for rendering.
// If stretchToFill is true, the resulting image will be both the given width and height.
// If false, the resulting image will be at most the given width or height while preserving the aspect ratio.
Expand Down Expand Up @@ -221,6 +224,9 @@ namespace AppInstaller::CLI::VirtualTerminal::Sixel
// Create an image from a stream.
Image(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding);

// Create an image from bytes.
Image(const std::vector<uint8_t>& imageBytes, Manifest::IconFileTypeEnum imageEncoding);

// Set the aspect ratio of the result.
Image& AspectRatio(AspectRatio aspectRatio);

Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1417,7 +1417,7 @@ namespace AppInstaller::CLI::Workflow
unit.Identifier(sourceNameWide + L'_' + packageIdWide);
unit.Intent(ConfigurationUnitIntent::Apply);

auto description = Resource::String::ConfigureExportUnitInstallDescription(Utility::LocIndView{ package.Id });
auto description = Resource::String::ConfigureExportUnitInstallDescription(package.Id);

ValueSet directives;
directives.Insert(s_Directive_Description, PropertyValue::CreateString(winrt::to_hstring(description.get())));
Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ namespace AppInstaller::CLI::Workflow
auto channel = installedPackageVersion->GetProperty(PackageVersionProperty::Channel);

// Find an available version of this package to determine its source.
auto availablePackageVersion = GetAvailableVersionForInstalledPackage(context, packageMatch.Package, Utility::LocIndView{ version }, Utility::LocIndView{ channel }, includeVersions);
auto availablePackageVersion = GetAvailableVersionForInstalledPackage(context, packageMatch.Package, version, channel, includeVersions);
if (!availablePackageVersion)
{
// Report package not found and move to next package.
Expand Down
134 changes: 67 additions & 67 deletions src/AppInstallerCLICore/Workflows/ShowFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,90 +11,56 @@ using namespace AppInstaller::CLI;
using namespace AppInstaller::Utility;
using namespace AppInstaller::Utility::literals;

namespace {

template <typename String>
void ShowSingleLineField(Execution::OutputStream outputStream, AppInstaller::StringResource::StringId label, const String& value, bool indent = false)
namespace AppInstaller::CLI::Workflow
{
namespace
{
if (value.empty())
void ShowSingleLineField(Execution::OutputStream& outputStream, StringResource::StringId label, const Manifest::Manifest::string_t& value, bool indent = false)
{
return;
Workflow::ShowSingleLineField(outputStream, label, LocIndView{ value }, indent ? 1 : 0);
}
if (indent)
{
outputStream << " "_liv;
}
outputStream << Execution::ManifestInfoEmphasis << label << ' ' << value << std::endl;
}

template <typename String>
void ShowMultiLineField(Execution::OutputStream outputStream, AppInstaller::StringResource::StringId label, const String& value)
{
if (value.empty())
void ShowMultiLineField(Execution::OutputStream& outputStream, StringResource::StringId label, const Manifest::Manifest::string_t& value)
{
return;
Workflow::ShowMultiLineField(outputStream, label, LocIndView{ value });
}
/*
We need to be able to find and replace within the string.However, we don't want to own the original string
Therefore, a copy is created here so we can manipulate it. The memory should be freed again once this method
returns and the string is no longer in scope.
*/
std::string shownValue = value;
bool isMultiLine = FindAndReplace(shownValue, "\n", "\n ");
outputStream << Execution::ManifestInfoEmphasis << label;
if (isMultiLine)
{
outputStream << std::endl << " "_liv << shownValue << std::endl;
}
else
{
outputStream << ' ' << shownValue << std::endl;
}
}

template <typename Enumerable>
void ShowMultiValueField(Execution::OutputStream outputStream, AppInstaller::StringResource::StringId label, const Enumerable& values)
{
if (values.empty())
{
return;
}
outputStream << Execution::ManifestInfoEmphasis << label << std::endl;
for (const auto& value : values)
{
outputStream << " "_liv << value << std::endl;
}
}
void ShowAgreements(Execution::OutputStream& outputStream, const std::vector<Manifest::Agreement>& agreements) {

void ShowAgreements(Execution::OutputStream outputStream, const std::vector<AppInstaller::Manifest::Agreement>& agreements) {
if (agreements.empty()) {
return;
}

if (agreements.empty()) {
return;
}
outputStream << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelAgreements << std::endl;
for (const auto& agreement : agreements) {

outputStream << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelAgreements << std::endl;
for (const auto& agreement : agreements) {
if (!agreement.Label.empty())
{
outputStream << " "_liv << Execution::ManifestInfoEmphasis << agreement.Label << ": "_liv;
}

if (!agreement.Label.empty())
{
outputStream << " "_liv << Execution::ManifestInfoEmphasis << agreement.Label << ": "_liv;
}
if (!agreement.AgreementText.empty())
{
outputStream << agreement.AgreementText << std::endl;
}

if (!agreement.AgreementText.empty())
{
outputStream << agreement.AgreementText << std::endl;
if (!agreement.AgreementUrl.empty())
{
outputStream << agreement.AgreementUrl << std::endl;
}
}
}
}

if (!agreement.AgreementUrl.empty())
{
outputStream << agreement.AgreementUrl << std::endl;
}
namespace details
{
LocIndView GetIndentFor(size_t indentLevel)
{
static constexpr std::array<LocIndView, 4> s_indents{ ""_liv, " "_liv, " "_liv, " "_liv };
return s_indents.at(indentLevel);
}
}
}

namespace AppInstaller::CLI::Workflow
{
void ShowAgreementsInfo(Execution::Context& context)
{
const auto& manifest = context.Get<Execution::Data::Manifest>();
Expand Down Expand Up @@ -264,4 +230,38 @@ namespace AppInstaller::CLI::Workflow
GetManifestFromPackage(m_considerPins);
}
}

void ShowSingleLineField(Execution::OutputStream& outputStream, StringResource::StringId label, LocIndView value, size_t indentLevel)
{
if (value.empty())
{
return;
}

outputStream << details::GetIndentFor(indentLevel) << Execution::ManifestInfoEmphasis << label << ' ' << value << '\n';
}

void ShowMultiLineField(Execution::OutputStream& outputStream, StringResource::StringId label, LocIndView value, size_t indentLevel)
{
if (value.empty())
{
return;
}

auto lines = Split(value, '\n');

outputStream << details::GetIndentFor(indentLevel) << Execution::ManifestInfoEmphasis << label;

if (lines.size() > 1)
{
for (const auto& line : lines)
{
outputStream << '\n' << details::GetIndentFor(indentLevel + 1) << line << '\n';
}
}
else
{
outputStream << ' ' << value << '\n';
}
}
}
Loading