Skip to content

Commit c22ca70

Browse files
committed
Massive OutConsoleGridView and ShowObjectTree refactor to prepare for unit testing.
Refactored `OutConsoleGridView` and `ShowObjectTree` for improved maintainability and performance: - Introduced `OutGridViewWindow` and `ShowObjectTreeWindow` to encapsulate UI logic. - Added `CachedMemberResult` and `CachedMemberResultElement` for better object hierarchy handling. - Implemented `RegexTreeViewTextFilter` for regex-based filtering in tree views. Enhanced data handling and UI: - Replaced `DataTable` with direct `PSObjects` handling in `ApplicationData`. - Improved grid view filtering, column width calculations, and status bar shortcuts. - Enhanced tree view to display object hierarchies with filtering and status bar updates. Refactored `TypeGetter`: - Added caching for `FormatViewDefinition` lookups. - Made `CastObjectsToTableView` static for reusability. Updated dependencies and removed legacy code: - Updated `Directory.Packages.props` to use `System.Management.Automation`. - Removed redundant and outdated code from `OutConsoleGridView`. Bug fixes and documentation: - Fixed column width calculation issues and concurrency in `TypeGetter`. - Improved error handling for invalid regex patterns. - Added XML documentation for all new and modified classes.
1 parent ee84e8a commit c22ca70

14 files changed

+1267
-833
lines changed

Directory.Packages.props

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<ItemGroup>
33
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.4.7" />
44
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
5-
<PackageVersion Include="Terminal.Gui" Version="2.0.0-develop.4636" />
5+
<PackageVersion Include="System.Management.Automation" Version="7.4.7" />
6+
<PackageVersion Include="Terminal.Gui" Version="2.0.0-develop.4636" />
67
</ItemGroup>
7-
</Project>
8+
</Project>
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Reflection;
9+
10+
namespace Microsoft.PowerShell.ConsoleGuiTools;
11+
12+
/// <summary>
13+
/// Represents a cached reflection result for a property or field member, including its value and collection details.
14+
/// </summary>
15+
internal sealed class CachedMemberResult
16+
{
17+
#region Fields
18+
19+
private readonly string? _representation;
20+
private List<CachedMemberResultElement>? _valueAsList;
21+
22+
#endregion
23+
24+
#region Properties
25+
26+
/// <summary>
27+
/// Gets or sets the member information (property or field) that was accessed.
28+
/// </summary>
29+
public MemberInfo Member { get; set; }
30+
31+
/// <summary>
32+
/// Gets or sets the value retrieved from the member.
33+
/// </summary>
34+
public object? Value { get; set; }
35+
36+
/// <summary>
37+
/// Gets or sets the parent object that contains this member.
38+
/// </summary>
39+
public object Parent { get; set; }
40+
41+
/// <summary>
42+
/// Gets a value indicating whether this member's value is a collection.
43+
/// </summary>
44+
public bool IsCollection => _valueAsList != null;
45+
46+
/// <summary>
47+
/// Gets the collection elements if this member's value is a collection; otherwise, <see langword="null" />.
48+
/// </summary>
49+
public IReadOnlyCollection<CachedMemberResultElement>? Elements => _valueAsList?.AsReadOnly();
50+
51+
#endregion
52+
53+
#region Constructor
54+
55+
/// <summary>
56+
/// Initializes a new instance of the <see cref="CachedMemberResult" /> class by reflecting on the specified member.
57+
/// </summary>
58+
/// <param name="parent">The parent object containing the member.</param>
59+
/// <param name="mem">The member information to retrieve the value from.</param>
60+
public CachedMemberResult(object parent, MemberInfo mem)
61+
{
62+
Parent = parent;
63+
Member = mem;
64+
65+
try
66+
{
67+
if (mem is PropertyInfo p)
68+
Value = p.GetValue(parent);
69+
else if (mem is FieldInfo f)
70+
Value = f.GetValue(parent);
71+
else
72+
throw new NotSupportedException($"Unknown {nameof(MemberInfo)} Type");
73+
74+
_representation = ValueToString();
75+
}
76+
catch (Exception)
77+
{
78+
Value = _representation = "Unavailable";
79+
}
80+
}
81+
82+
#endregion
83+
84+
#region Overrides
85+
86+
/// <summary>
87+
/// Returns a string representation of this member in the format "MemberName: value".
88+
/// </summary>
89+
/// <returns>A formatted string showing the member name and value.</returns>
90+
public override string ToString() => Member.Name + ": " + _representation;
91+
92+
#endregion
93+
94+
#region Private Methods
95+
96+
/// <summary>
97+
/// Converts the member's value to a string representation, detecting collections and formatting them appropriately.
98+
/// </summary>
99+
/// <returns>A string representation of the value.</returns>
100+
private string? ValueToString()
101+
{
102+
if (Value == null)
103+
return "Null";
104+
105+
try
106+
{
107+
if (IsCollectionOfKnownTypeAndSize(out var elementType, out var size))
108+
return $"{elementType!.Name}[{size}]";
109+
}
110+
catch (Exception)
111+
{
112+
return Value?.ToString();
113+
}
114+
115+
return Value?.ToString();
116+
}
117+
118+
/// <summary>
119+
/// Determines whether the value is a collection of a known type and caches the collection elements.
120+
/// </summary>
121+
/// <param name="elementType">When this method returns, contains the element type if the value is a homogeneous collection; otherwise, <see langword="null" />.</param>
122+
/// <param name="size">When this method returns, contains the size of the collection if applicable; otherwise, 0.</param>
123+
/// <returns><see langword="true" /> if the value is a collection of a single known type; otherwise, <see langword="false" />.</returns>
124+
private bool IsCollectionOfKnownTypeAndSize(out Type? elementType, out int size)
125+
{
126+
elementType = null;
127+
size = 0;
128+
129+
if (Value is null or string)
130+
return false;
131+
132+
if (Value is IEnumerable enumerable)
133+
{
134+
var list = enumerable.Cast<object>().ToList();
135+
136+
var types = list.Where(v => v != null).Select(v => v!.GetType()).Distinct().ToArray();
137+
138+
if (types.Length == 1)
139+
{
140+
elementType = types[0];
141+
size = list.Count;
142+
143+
_valueAsList = list.Select((e, i) => new CachedMemberResultElement(e, i)).ToList();
144+
return true;
145+
}
146+
}
147+
148+
return false;
149+
}
150+
151+
#endregion
152+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
6+
namespace Microsoft.PowerShell.ConsoleGuiTools;
7+
8+
/// <summary>
9+
/// Represents an element within a collection member result, providing indexed access to collection items.
10+
/// </summary>
11+
internal sealed class CachedMemberResultElement
12+
{
13+
#region Fields
14+
15+
/// <summary>
16+
/// The index of this element within the collection.
17+
/// </summary>
18+
public int Index;
19+
20+
/// <summary>
21+
/// The value of this collection element.
22+
/// </summary>
23+
public object? Value;
24+
25+
private readonly string _representation;
26+
27+
#endregion
28+
29+
#region Constructor
30+
31+
/// <summary>
32+
/// Initializes a new instance of the <see cref="CachedMemberResultElement" /> class with the specified value and index.
33+
/// </summary>
34+
/// <param name="value">The value of the collection element.</param>
35+
/// <param name="index">The zero-based index of this element within the collection.</param>
36+
public CachedMemberResultElement(object? value, int index)
37+
{
38+
Index = index;
39+
Value = value;
40+
41+
try
42+
{
43+
_representation = Value?.ToString() ?? "Null";
44+
}
45+
catch (Exception)
46+
{
47+
Value = _representation = "Unavailable";
48+
}
49+
}
50+
51+
#endregion
52+
53+
#region Overrides
54+
55+
/// <summary>
56+
/// Returns a string representation of this collection element in the format "[index]: value".
57+
/// </summary>
58+
/// <returns>A formatted string showing the index and value.</returns>
59+
public override string ToString() => $"[{Index}]: {_representation}]";
60+
61+
#endregion
62+
}

0 commit comments

Comments
 (0)