Skip to content
This repository was archived by the owner on Jun 24, 2026. It is now read-only.

Commit 8278a28

Browse files
tigCopilot
andcommitted
Add column sorting with direction indicators (▲/▼)
- Click column header to sort ascending, click again to toggle descending - Sort glyph (▲/▼) appended to sorted column header name - Numeric-aware sorting (numbers sort by value, not lexicographically) - Sorting composes with filter (filter applied first, then sort) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 63aabf2 commit 8278a28

3 files changed

Lines changed: 84 additions & 1 deletion

File tree

src/Microsoft.PowerShell.ConsoleGuiTools/OutTableViewDataSource.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Linq;
67
using System.Text.RegularExpressions;
@@ -129,6 +130,48 @@ public OutTableViewDataSource Filter(string pattern)
129130
return new OutTableViewDataSource(_columns, result);
130131
}
131132

133+
/// <summary>
134+
/// Creates a new data source with rows sorted by the specified column.
135+
/// The sorted column header is decorated with ▲ (ascending) or ▼ (descending).
136+
/// </summary>
137+
public OutTableViewDataSource Sort(int columnIndex, bool descending)
138+
{
139+
if (columnIndex < 0 || columnIndex >= _columns.Count)
140+
return this;
141+
142+
List<DataTableRow> allRows;
143+
lock (_lock)
144+
{
145+
allRows = new List<DataTableRow>(_rows);
146+
}
147+
148+
var columnKey = _columns[columnIndex].ToString();
149+
150+
var sorted = descending
151+
? allRows.OrderByDescending(r => GetSortValue(r, columnKey))
152+
: allRows.OrderBy(r => GetSortValue(r, columnKey));
153+
154+
var result = new OutTableViewDataSource(_columns, sorted.ToList());
155+
156+
// Decorate the sorted column header with a direction glyph
157+
string glyph = descending ? " ▼" : " ▲";
158+
result.ColumnNames[columnIndex] = _columns[columnIndex].Label + glyph;
159+
160+
return result;
161+
}
162+
163+
private static IComparable GetSortValue(DataTableRow row, string columnKey)
164+
{
165+
if (!row.Values.TryGetValue(columnKey, out var value))
166+
return string.Empty;
167+
168+
// Try numeric sort first
169+
if (double.TryParse(value.DisplayValue, out var numericValue))
170+
return numericValue;
171+
172+
return value.DisplayValue;
173+
}
174+
132175
private static bool RowMatchesFilter(DataTableRow row, Regex regex, List<DataTableColumn> columns)
133176
{
134177
foreach (var column in columns)

src/Microsoft.PowerShell.ConsoleGuiTools/OutTableViewWindow.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ internal sealed class OutTableViewWindow : Runnable<HashSet<int>>
3232
private TextField? _filterField;
3333
private Label? _filterLabel;
3434
private bool _isLoading = true;
35+
private int _sortColumn = -1;
36+
private bool _sortDescending;
3537

3638
private StatusBar? _statusBar;
3739
private TableView? _tableView;
@@ -217,6 +219,19 @@ private void RebuildTableSource()
217219
_tableView.Table = _filteredDataSource;
218220
}
219221

222+
private void ApplySortAndFilter()
223+
{
224+
// Apply filter first, then sort
225+
ApplyFilter();
226+
227+
if (_sortColumn < 0 || _filteredDataSource == null || _tableView == null)
228+
return;
229+
230+
_filteredDataSource = _filteredDataSource.Sort(_sortColumn, _sortDescending);
231+
_tableView.Table = _filteredDataSource;
232+
_tableView?.Update();
233+
}
234+
220235
private void ApplySearch()
221236
{
222237
if (string.IsNullOrEmpty(_applicationData.Search) || _filteredDataSource == null || _tableView == null)
@@ -336,6 +351,31 @@ private void AddTableView()
336351
// Enter key activates selection
337352
if (_applicationData.OutputMode != OutputModeOption.None) _tableView.Accepted += (_, _) => Accept();
338353

354+
// Column header click sorts
355+
_tableView.Activating += (_, e) =>
356+
{
357+
if (e.Context?.Binding is not MouseBinding { MouseEvent: { } mouse })
358+
return;
359+
360+
if (!mouse.Flags.HasFlag(MouseFlags.LeftButtonClicked))
361+
return;
362+
363+
_tableView.ScreenToCell(mouse.Position!.Value, out int? clickedCol);
364+
if (clickedCol == null)
365+
return;
366+
367+
// Toggle direction if clicking same column, otherwise sort ascending
368+
if (clickedCol.Value == _sortColumn)
369+
_sortDescending = !_sortDescending;
370+
else
371+
{
372+
_sortColumn = clickedCol.Value;
373+
_sortDescending = false;
374+
}
375+
376+
ApplySortAndFilter();
377+
};
378+
339379
Add(_tableView);
340380
}
341381

src/Microsoft.PowerShell.ConsoleGuiTools/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"OCTV": {
3434
"commandName": "Executable",
3535
"executablePath": "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
36-
"commandLineArgs": "-NoProfile -NoLogo -NoExit -Command \"& { Import-Module '$(TargetPath)'; Get-Process | Select-Object ProcessName, Id,\r\n @{Name='CPU(s)'; Expression={[math]::Round($_.CPU,2)}},\r\n @{Name='WorkingSet(MB)'; Expression={[math]::Round($_.WorkingSet64/1MB,2)}},\r\n @{Name='Private(MB)'; Expression={[math]::Round($_.PrivateMemorySize64/1MB,2)}}\r\n | Out-ConsoleTableView -Debug -Title \"processes\" -MinUI -Filter pass -OutputMode Single}\"",
36+
"commandLineArgs": "-NoProfile -NoLogo -NoExit -Command \"& { Import-Module '$(TargetPath)'; Get-Process | Select-Object ProcessName, Id,\r\n @{Name='CPU(s)'; Expression={[math]::Round($_.CPU,2)}},\r\n @{Name='WorkingSet(MB)'; Expression={[math]::Round($_.WorkingSet64/1MB,2)}},\r\n @{Name='Private(MB)'; Expression={[math]::Round($_.PrivateMemorySize64/1MB,2)}}\r\n | Out-ConsoleTableView -Debug -Title \"processes\" }\"",
3737
"workingDirectory": "$(TargetDir)"
3838
},
3939
"OCTV Slow": {

0 commit comments

Comments
 (0)