Add support to colorize FileInfo file names (#14403)
This commit is contained in:
parent
adc236c598
commit
bfb4a818bf
|
@ -276,6 +276,10 @@ namespace System.Management.Automation.Runspaces
|
|||
"System.Management.Automation.PSStyle+ProgressConfiguration",
|
||||
ViewsOf_System_Management_Automation_PSStyleProgressConfiguration());
|
||||
|
||||
yield return new ExtendedTypeDefinition(
|
||||
"System.Management.Automation.PSStyle+FileInfoFormatting",
|
||||
ViewsOf_System_Management_Automation_PSStyleFileInfoFormat());
|
||||
|
||||
yield return new ExtendedTypeDefinition(
|
||||
"System.Management.Automation.PSStyle+ForegroundColor",
|
||||
ViewsOf_System_Management_Automation_PSStyleForegroundColor());
|
||||
|
@ -2060,6 +2064,10 @@ namespace System.Management.Automation.Runspaces
|
|||
.AddItemScriptBlock(@"""$($_.Progress.MaxWidth)""", label: "Progress.MaxWidth")
|
||||
.AddItemScriptBlock(@"""$($_.Progress.View)""", label: "Progress.View")
|
||||
.AddItemScriptBlock(@"""$($_.Progress.UseOSCIndicator)""", label: "Progress.UseOSCIndicator")
|
||||
.AddItemScriptBlock(@"""$($_.FileInfo.Directory)$($_.FileInfo.Directory.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "FileInfo.Directory")
|
||||
.AddItemScriptBlock(@"""$($_.FileInfo.SymbolicLink)$($_.FileInfo.SymbolicLink.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "FileInfo.SymbolicLink")
|
||||
.AddItemScriptBlock(@"""$($_.FileInfo.Executable)$($_.FileInfo.Executable.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "FileInfo.Executable")
|
||||
.AddItemScriptBlock(@"""$([string]::Join(',',$_.FileInfo.Extension.Keys))""", label: "FileInfo.Extension")
|
||||
.AddItemScriptBlock(@"""$($_.Foreground.Black)$($_.Foreground.Black.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Black")
|
||||
.AddItemScriptBlock(@"""$($_.Foreground.White)$($_.Foreground.White.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.White")
|
||||
.AddItemScriptBlock(@"""$($_.Foreground.DarkGray)$($_.Foreground.DarkGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.DarkGray")
|
||||
|
@ -2124,6 +2132,39 @@ namespace System.Management.Automation.Runspaces
|
|||
.EndList());
|
||||
}
|
||||
|
||||
private static IEnumerable<FormatViewDefinition> ViewsOf_System_Management_Automation_PSStyleFileInfoFormat()
|
||||
{
|
||||
yield return new FormatViewDefinition("System.Management.Automation.PSStyle+FileInfoFormatting",
|
||||
ListControl.Create()
|
||||
.StartEntry()
|
||||
.AddItemScriptBlock(@"""$($_.Directory)$($_.Directory.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Directory")
|
||||
.AddItemScriptBlock(@"""$($_.SymbolicLink)$($_.SymbolicLink.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "SymbolicLink")
|
||||
.AddItemScriptBlock(@"""$($_.Executable)$($_.Executable.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Executable")
|
||||
.AddItemScriptBlock(@"
|
||||
$sb = [System.Text.StringBuilder]::new()
|
||||
$maxKeyLength = 0
|
||||
foreach ($key in $_.Extension.Keys) {
|
||||
if ($key.Length -gt $maxKeyLength) {
|
||||
$maxKeyLength = $key.Length
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($key in $_.Extension.Keys) {
|
||||
$null = $sb.Append($key.PadRight($maxKeyLength))
|
||||
$null = $sb.Append(' = ""')
|
||||
$null = $sb.Append($_.Extension[$key])
|
||||
$null = $sb.Append($_.Extension[$key].Replace(""`e"",'`e'))
|
||||
$null = $sb.Append($PSStyle.Reset)
|
||||
$null = $sb.Append('""')
|
||||
$null = $sb.Append([Environment]::NewLine)
|
||||
}
|
||||
|
||||
$sb.ToString()",
|
||||
label: "Extension")
|
||||
.EndEntry()
|
||||
.EndList());
|
||||
}
|
||||
|
||||
private static IEnumerable<FormatViewDefinition> ViewsOf_System_Management_Automation_PSStyleForegroundColor()
|
||||
{
|
||||
yield return new FormatViewDefinition("System.Management.Automation.PSStyle+ForegroundColor",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Management.Automation
|
||||
{
|
||||
#region OutputRendering
|
||||
|
@ -34,7 +36,7 @@ namespace System.Management.Automation
|
|||
/// <summary>Classic rendering of progress.</summary>
|
||||
Classic = 1,
|
||||
}
|
||||
|
||||
|
||||
#region PSStyle
|
||||
/// <summary>
|
||||
/// Contains configuration for how PowerShell renders text.
|
||||
|
@ -333,6 +335,55 @@ namespace System.Management.Automation
|
|||
public string Debug { get; set; } = "\x1b[33;1m";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains formatting styles for FileInfo objects.
|
||||
/// </summary>
|
||||
public sealed class FileInfoFormatting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the style for directories.
|
||||
/// </summary>
|
||||
public string Directory { get; set; } = "\x1b[44;1m";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the style for symbolic links.
|
||||
/// </summary>
|
||||
public string SymbolicLink { get; set; } = "\x1b[36;1m";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the style for executables.
|
||||
/// </summary>
|
||||
public string Executable { get; set; } = "\x1b[32;1m";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the style for archive.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Extension { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileInfoFormatting"/> class.
|
||||
/// </summary>
|
||||
public FileInfoFormatting()
|
||||
{
|
||||
Extension = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// archives
|
||||
Extension.Add(".zip", "\x1b[31;1m");
|
||||
Extension.Add(".tgz", "\x1b[31;1m");
|
||||
Extension.Add(".gz", "\x1b[31;1m");
|
||||
Extension.Add(".tar", "\x1b[31;1m");
|
||||
Extension.Add(".nupkg", "\x1b[31;1m");
|
||||
Extension.Add(".cab", "\x1b[31;1m");
|
||||
Extension.Add(".7z", "\x1b[31;1m");
|
||||
|
||||
// powershell
|
||||
Extension.Add(".ps1", "\x1b[33;1m");
|
||||
Extension.Add(".psd1", "\x1b[33;1m");
|
||||
Extension.Add(".psm1", "\x1b[33;1m");
|
||||
Extension.Add(".ps1xml", "\x1b[33;1m");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rendering mode for output.
|
||||
/// </summary>
|
||||
|
@ -444,6 +495,11 @@ namespace System.Management.Automation
|
|||
/// </summary>
|
||||
public BackgroundColor Background { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets FileInfo colors.
|
||||
/// </summary>
|
||||
public FileInfoFormatting FileInfo { get; }
|
||||
|
||||
private static readonly PSStyle s_psstyle = new PSStyle();
|
||||
|
||||
private PSStyle()
|
||||
|
@ -452,6 +508,7 @@ namespace System.Management.Automation
|
|||
Progress = new ProgressConfiguration();
|
||||
Foreground = new ForegroundColor();
|
||||
Background = new BackgroundColor();
|
||||
FileInfo = new FileInfoFormatting();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1410,7 +1410,7 @@ namespace System.Management.Automation
|
|||
lock (s_lockObject)
|
||||
{
|
||||
s_cachedPathExtCollection = pathExt != null
|
||||
? pathExt.Split(Utils.Separators.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
|
||||
? pathExt.ToLower().Split(Utils.Separators.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
|
||||
: Array.Empty<string>();
|
||||
s_cachedPathExtCollectionWithPs1 = new string[s_cachedPathExtCollection.Length + 1];
|
||||
s_cachedPathExtCollectionWithPs1[0] = StringLiterals.PowerShellScriptFileExtension;
|
||||
|
|
|
@ -140,6 +140,9 @@ namespace System.Management.Automation
|
|||
new ExperimentalFeature(
|
||||
name: "PSLoadAssemblyFromNativeCode",
|
||||
description: "Expose an API to allow assembly loading from native code"),
|
||||
new ExperimentalFeature(
|
||||
name: "PSAnsiRenderingFileInfo",
|
||||
description: "Enable coloring for FileInfo objects"),
|
||||
};
|
||||
|
||||
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);
|
||||
|
|
|
@ -2058,11 +2058,43 @@ namespace Microsoft.PowerShell.Commands
|
|||
/// <returns>Name if a file or directory, Name -> Target if symlink.</returns>
|
||||
public static string NameString(PSObject instance)
|
||||
{
|
||||
return instance?.BaseObject is FileSystemInfo fileInfo
|
||||
? InternalSymbolicLinkLinkCodeMethods.IsReparsePointWithTarget(fileInfo)
|
||||
? $"{fileInfo.Name} -> {InternalSymbolicLinkLinkCodeMethods.GetTarget(instance)}"
|
||||
: fileInfo.Name
|
||||
: string.Empty;
|
||||
if (ExperimentalFeature.IsEnabled("PSAnsiRendering") && ExperimentalFeature.IsEnabled("PSAnsiRenderingFileInfo"))
|
||||
{
|
||||
if (instance?.BaseObject is FileSystemInfo fileInfo)
|
||||
{
|
||||
if (InternalSymbolicLinkLinkCodeMethods.IsReparsePointWithTarget(fileInfo))
|
||||
{
|
||||
return $"{PSStyle.Instance.FileInfo.SymbolicLink}{fileInfo.Name}{PSStyle.Instance.Reset} -> {InternalSymbolicLinkLinkCodeMethods.GetTarget(instance)}";
|
||||
}
|
||||
else if (fileInfo.Attributes.HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
return $"{PSStyle.Instance.FileInfo.Directory}{fileInfo.Name}{PSStyle.Instance.Reset}";
|
||||
}
|
||||
else if (PSStyle.Instance.FileInfo.Extension.ContainsKey(fileInfo.Extension))
|
||||
{
|
||||
return $"{PSStyle.Instance.FileInfo.Extension[fileInfo.Extension]}{fileInfo.Name}{PSStyle.Instance.Reset}";
|
||||
}
|
||||
else if ((Platform.IsWindows && CommandDiscovery.PathExtensions.Contains(fileInfo.Extension.ToLower())) ||
|
||||
(!Platform.IsWindows && Platform.NonWindowsIsExecutable(fileInfo.FullName)))
|
||||
{
|
||||
return $"{PSStyle.Instance.FileInfo.Executable}{fileInfo.Name}{PSStyle.Instance.Reset}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return fileInfo.Name;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return instance?.BaseObject is FileSystemInfo fileInfo
|
||||
? InternalSymbolicLinkLinkCodeMethods.IsReparsePointWithTarget(fileInfo)
|
||||
? $"{fileInfo.Name} -> {InternalSymbolicLinkLinkCodeMethods.GetTarget(instance)}"
|
||||
: fileInfo.Name
|
||||
: string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -205,3 +205,62 @@ Describe "Get-Item environment provider on Windows with accidental case-variant
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Formatting for FileInfo objects' -Tags 'CI' {
|
||||
BeforeAll {
|
||||
$PSDefaultParameterValues.Add('It:Skip', (-not $EnabledExperimentalFeatures.Contains('PSAnsiRenderingFileInfo')))
|
||||
$extensionTests = [System.Collections.Generic.List[HashTable]]::new()
|
||||
foreach ($extension in @('.zip', '.tgz', '.tar', '.gz', '.nupkg', '.cab', '.7z', '.ps1', '.psd1', '.psm1', '.ps1xml')) {
|
||||
$extensionTests.Add(@{extension = $extension})
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$PSDefaultParameterValues.Remove('It:Skip')
|
||||
}
|
||||
|
||||
It 'File type <extension> should have correct color' -TestCases $extensionTests {
|
||||
param($extension)
|
||||
|
||||
$testFile = Join-Path -Path $TestDrive -ChildPath "test$extension"
|
||||
$file = New-Item -ItemType File -Path $testFile
|
||||
$file.NameString | Should -BeExactly "$($PSStyle.FileInfo.Extension[$extension] + $file.Name + $PSStyle.Reset)"
|
||||
}
|
||||
|
||||
It 'Directory should have correct color' {
|
||||
$dirPath = Join-Path -Path $TestDrive -ChildPath 'myDir'
|
||||
$dir = New-Item -ItemType Directory -Path $dirPath
|
||||
$dir.NameString | Should -BeExactly "$($PSStyle.FileInfo.Directory + $dir.Name + $PSStyle.Reset)"
|
||||
}
|
||||
|
||||
It 'Executable should have correct color' {
|
||||
if ($IsWindows) {
|
||||
$exePath = Join-Path -Path $TestDrive -ChildPath 'myExe.exe'
|
||||
$exe = New-Item -ItemType File -Path $exePath
|
||||
}
|
||||
else {
|
||||
$exePath = Join-Path -Path $TestDrive -ChildPath 'myExe'
|
||||
$null = New-Item -ItemType File -Path $exePath
|
||||
chmod +x $exePath
|
||||
$exe = Get-Item -Path $exePath
|
||||
}
|
||||
|
||||
$exe.NameString | Should -BeExactly "$($PSStyle.FileInfo.Executable + $exe.Name + $PSStyle.Reset)"
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Formatting for FileInfo requiring admin' -Tags 'CI','RequireAdminOnWindows' {
|
||||
BeforeAll {
|
||||
$PSDefaultParameterValues.Add('It:Skip', (-not $EnabledExperimentalFeatures.Contains('PSAnsiRenderingFileInfo')))
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$PSDefaultParameterValues.Remove('It:Skip')
|
||||
}
|
||||
|
||||
It 'Symlink should have correct color' {
|
||||
$linkPath = Join-Path -Path $TestDrive -ChildPath 'link'
|
||||
$link = New-Item -ItemType SymbolicLink -Name 'link' -Value $TestDrive -Path $TestDrive
|
||||
$link.NameString | Should -BeExactly "$($PSStyle.FileInfo.SymbolicLink + $link.Name + $PSStyle.Reset) -> $TestDrive"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue