Files
PowerToys/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs
Michael Jolley 138c66c328 CmdPal: Removing Core projects (#45693)
Functionally, no differences.

- Removed Core projects.
- Core.Common => Microsoft.CmdPal.Common
- Core.ViewModels => Microsoft.CmdPal.UI.ViewModels

---------

Co-authored-by: Jiří Polášek <me@jiripolasek.com>
2026-02-23 06:05:09 -06:00

225 lines
7.0 KiB
C#

// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Ext.Shell.Helpers;
using Microsoft.CmdPal.Ext.Shell.Pages;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Shell;
internal sealed partial class FallbackExecuteItem : FallbackCommandItem, IDisposable
{
private const string _id = "com.microsoft.cmdpal.builtin.shell.fallback";
private static readonly char[] _systemDirectoryRoots = ['\\', '/'];
private readonly Action<string>? _addToHistory;
private readonly ITelemetryService _telemetryService;
private CancellationTokenSource? _cancellationTokenSource;
public FallbackExecuteItem(SettingsManager settings, Action<string>? addToHistory, ITelemetryService telemetryService)
: base(
new NoOpCommand() { Id = _id },
ResourceLoaderInstance.GetString("shell_command_display_title"),
_id)
{
Title = string.Empty;
Subtitle = ResourceLoaderInstance.GetString("generic_run_command");
Icon = Icons.RunV2Icon; // Defined in Icons.cs and contains the execute command icon.
_addToHistory = addToHistory;
_telemetryService = telemetryService;
}
public override void UpdateQuery(string query)
{
// Cancel any ongoing query processing
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = _cancellationTokenSource.Token;
try
{
DoUpdateQuery(query, cancellationToken);
}
catch (Exception)
{
// Handle other exceptions
return;
}
}
private void DoUpdateQuery(string query, CancellationToken cancellationToken)
{
// Check for cancellation at the start
if (cancellationToken.IsCancellationRequested)
{
return;
}
var searchText = query.Trim();
Expand(ref searchText);
if (string.IsNullOrEmpty(searchText) || string.IsNullOrWhiteSpace(searchText))
{
Command = null;
Title = string.Empty;
return;
}
ShellListPageHelpers.NormalizeCommandLineAndArgs(searchText, out var exe, out var args);
// Check for cancellation before file system operations
cancellationToken.ThrowIfCancellationRequested();
var exeExists = false;
var fullExePath = string.Empty;
var pathIsDir = false;
try
{
// Create a timeout for file system operations (200ms)
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMilliseconds(200));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
var timeoutToken = combinedCts.Token;
exeExists = ShellListPageHelpers.FileExistInPath(exe, out fullExePath, cancellationToken);
pathIsDir = Directory.Exists(exe);
}
catch (TimeoutException)
{
// Timeout occurred - use defaults
return;
}
catch (OperationCanceledException)
{
// Timeout occurred (from WaitAsync) - use defaults
return;
}
catch (Exception)
{
// Handle any other exceptions that might bubble up
return;
}
// Check for cancellation before updating UI properties
if (cancellationToken.IsCancellationRequested)
{
return;
}
if (exeExists)
{
// TODO we need to probably get rid of the settings for this provider entirely
var exeItem = ShellListPage.CreateExeItem(exe, args, fullExePath, _addToHistory, telemetryService: _telemetryService);
Title = exeItem.Title;
Subtitle = exeItem.Subtitle;
Icon = exeItem.Icon;
Command = exeItem.Command;
MoreCommands = exeItem.MoreCommands;
}
else if (pathIsDir)
{
var pathItem = new PathListItem(exe, query, _addToHistory, _telemetryService);
Command = pathItem.Command;
MoreCommands = pathItem.MoreCommands;
Title = pathItem.Title;
Subtitle = pathItem.Subtitle;
Icon = pathItem.Icon;
}
else if (System.Uri.TryCreate(searchText, UriKind.Absolute, out var uri))
{
Command = new OpenUrlWithHistoryCommand(searchText, _addToHistory, _telemetryService) { Result = CommandResult.Dismiss() };
Title = searchText;
}
else
{
Command = null;
Title = string.Empty;
}
// Final cancellation check
if (cancellationToken.IsCancellationRequested)
{
return;
}
}
public void Dispose()
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
}
internal static bool SuppressFileFallbackIf(string query)
{
var searchText = query.Trim();
Expand(ref searchText);
if (string.IsNullOrEmpty(searchText) || string.IsNullOrWhiteSpace(searchText))
{
return false;
}
ShellHelpers.ParseExecutableAndArgs(searchText, out var exe, out var args);
var exeExists = ShellListPageHelpers.FileExistInPath(exe, out var fullExePath);
var pathIsDir = Directory.Exists(exe);
return exeExists || pathIsDir;
}
private static void Expand(ref string searchText)
{
if (searchText.Length == 0)
{
return;
}
var singleCharQuery = searchText.Length == 1;
searchText = Environment.ExpandEnvironmentVariables(searchText);
if (!TryExpandHome(ref searchText))
{
TryExpandRoot(ref searchText);
}
}
private static bool TryExpandHome(ref string searchText)
{
if (searchText[0] == '~')
{
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (searchText.Length == 1)
{
searchText = home;
}
else if (_systemDirectoryRoots.Contains(searchText[1]))
{
searchText = Path.Combine(home, searchText[2..]);
}
return true;
}
return false;
}
private static bool TryExpandRoot(ref string searchText)
{
if (_systemDirectoryRoots.Contains(searchText[0]) && (searchText.Length == 1 || !_systemDirectoryRoots.Contains(searchText[1])))
{
var root = Path.GetPathRoot(Environment.SystemDirectory);
if (root != null)
{
searchText = searchText.Length == 1 ? root : Path.Combine(root, searchText[1..]);
return true;
}
}
return false;
}
}