mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 16:36:40 +01:00
Compare commits
107 Commits
tools/Rele
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3da4e8522c | ||
|
|
9f972fe410 | ||
|
|
93a0689e9e | ||
|
|
31f74040aa | ||
|
|
b0a500e875 | ||
|
|
2e2e060f4e | ||
|
|
35ed9f5b56 | ||
|
|
04d626a053 | ||
|
|
b111899ac0 | ||
|
|
e6e0f6d541 | ||
|
|
68c364f75e | ||
|
|
7649c16d2e | ||
|
|
66489acaac | ||
|
|
d62eeb39b9 | ||
|
|
87b79a7ee8 | ||
|
|
367504537d | ||
|
|
9b6794f359 | ||
|
|
0cfb321b9b | ||
|
|
4b0534acf8 | ||
|
|
b79cf60d94 | ||
|
|
74efb00f40 | ||
|
|
bf83bbd94d | ||
|
|
12b7555006 | ||
|
|
77a0d77471 | ||
|
|
9a3667c91f | ||
|
|
45145cf1a4 | ||
|
|
f758907850 | ||
|
|
9323598fed | ||
|
|
e8c63a26e0 | ||
|
|
6f867d178c | ||
|
|
4250bd82ac | ||
|
|
c4047a8ee2 | ||
|
|
d9c168c3fe | ||
|
|
210febe270 | ||
|
|
e53cb409b4 | ||
|
|
98c771788b | ||
|
|
5dee7c9d89 | ||
|
|
c2ba9af144 | ||
|
|
29c15601f0 | ||
|
|
558e2af6cb | ||
|
|
70c5e3c9d5 | ||
|
|
986ffbeede | ||
|
|
8466c47059 | ||
|
|
229d3b4991 | ||
|
|
7ca9798df5 | ||
|
|
a6e39d5535 | ||
|
|
edf02497af | ||
|
|
02b583267d | ||
|
|
e2569ec4ee | ||
|
|
c760962573 | ||
|
|
ec2480385b | ||
|
|
458d3a2699 | ||
|
|
a58b802cb9 | ||
|
|
a72bf5aed7 | ||
|
|
fb49f6a5e5 | ||
|
|
b4a7bb4a7a | ||
|
|
8615c48c5c | ||
|
|
1aa78e1b96 | ||
|
|
b0c862dd67 | ||
|
|
b6e3b8a3ee | ||
|
|
a2d0d3b262 | ||
|
|
ee53a6d138 | ||
|
|
126a3c0de8 | ||
|
|
a94bd91dba | ||
|
|
b2f2462ad6 | ||
|
|
b6f0ced53e | ||
|
|
a405f27d19 | ||
|
|
86d04cc3bd | ||
|
|
381482e9a0 | ||
|
|
9e7d212c31 | ||
|
|
11c9d913cc | ||
|
|
31f5af7e14 | ||
|
|
568c2ca388 | ||
|
|
27124972cd | ||
|
|
ca9488c875 | ||
|
|
d73a8a0a2c | ||
|
|
c953ce7eca | ||
|
|
9fc7a180d4 | ||
|
|
ccca007562 | ||
|
|
07c85065f2 | ||
|
|
c06767b73e | ||
|
|
4c639a085c | ||
|
|
8aa7349f32 | ||
|
|
fa86f2223c | ||
|
|
30215e8b4f | ||
|
|
ee386e7be4 | ||
|
|
9ba2030b5e | ||
|
|
1ba832b732 | ||
|
|
46f27c1612 | ||
|
|
1d81ef8935 | ||
|
|
e19cdba074 | ||
|
|
4223286061 | ||
|
|
8e6bd141ca | ||
|
|
d29121c3fd | ||
|
|
58e0530980 | ||
|
|
9272e3112b | ||
|
|
a64095c3d3 | ||
|
|
49480041cd | ||
|
|
547b664a8c | ||
|
|
72320bea79 | ||
|
|
910de53a0a | ||
|
|
11f60de543 | ||
|
|
e7eb2d0239 | ||
|
|
aefae2935e | ||
|
|
727367960e | ||
|
|
32700658fd | ||
|
|
a7cb535515 |
@@ -9,11 +9,11 @@ using Windows.AI.Actions.Hosting;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class ExecuteActionCommand : InvokableCommand
|
||||
public sealed partial class ExecuteActionCommand : InvokableCommand
|
||||
{
|
||||
private readonly ActionInstance actionInstance;
|
||||
|
||||
internal ExecuteActionCommand(ActionInstance actionInstance)
|
||||
public ExecuteActionCommand(ActionInstance actionInstance)
|
||||
{
|
||||
this.actionInstance = actionInstance;
|
||||
this.Name = actionInstance.DisplayInfo.Description;
|
||||
@@ -6,28 +6,29 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Common.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
namespace Microsoft.CmdPal.Common.Commands;
|
||||
|
||||
internal sealed partial class OpenInConsoleCommand : InvokableCommand
|
||||
public partial class OpenInConsoleCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
internal static IconInfo OpenInConsoleIcon { get; } = new("\uE756");
|
||||
|
||||
internal OpenInConsoleCommand(IndexerItem item)
|
||||
private readonly string _path;
|
||||
|
||||
public OpenInConsoleCommand(string fullPath)
|
||||
{
|
||||
this._item = item;
|
||||
this._path = fullPath;
|
||||
this.Name = Resources.Indexer_Command_OpenPathInConsole;
|
||||
this.Icon = new IconInfo("\uE756");
|
||||
this.Icon = OpenInConsoleIcon;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(_item.FullPath);
|
||||
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(_path);
|
||||
process.StartInfo.FileName = "cmd.exe";
|
||||
|
||||
try
|
||||
@@ -36,10 +37,10 @@ internal sealed partial class OpenInConsoleCommand : InvokableCommand
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Logger.LogError($"Unable to open {_item.FullPath}", ex);
|
||||
Logger.LogError($"Unable to open '{_path}'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.GoHome();
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,17 @@ using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedCommon;
|
||||
using ManagedCsWin32;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Common.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
namespace Microsoft.CmdPal.Common.Commands;
|
||||
|
||||
internal sealed partial class OpenPropertiesCommand : InvokableCommand
|
||||
public partial class OpenPropertiesCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
internal static IconInfo OpenPropertiesIcon { get; } = new("\uE90F");
|
||||
|
||||
private readonly string _path;
|
||||
|
||||
private static unsafe bool ShowFileProperties(string filename)
|
||||
{
|
||||
@@ -31,7 +31,7 @@ internal sealed partial class OpenPropertiesCommand : InvokableCommand
|
||||
LpVerb = propertiesPtr,
|
||||
LpFile = filenamePtr,
|
||||
Show = (int)SHOW_WINDOW_CMD.SW_SHOW,
|
||||
FMask = NativeHelpers.SEEMASKINVOKEIDLIST,
|
||||
FMask = global::Windows.Win32.PInvoke.SEE_MASK_INVOKEIDLIST,
|
||||
};
|
||||
|
||||
return Shell32.ShellExecuteEx(ref info);
|
||||
@@ -43,24 +43,24 @@ internal sealed partial class OpenPropertiesCommand : InvokableCommand
|
||||
}
|
||||
}
|
||||
|
||||
internal OpenPropertiesCommand(IndexerItem item)
|
||||
public OpenPropertiesCommand(string fullPath)
|
||||
{
|
||||
this._item = item;
|
||||
this._path = fullPath;
|
||||
this.Name = Resources.Indexer_Command_OpenProperties;
|
||||
this.Icon = new IconInfo("\uE90F");
|
||||
this.Icon = OpenPropertiesIcon;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShowFileProperties(_item.FullPath);
|
||||
ShowFileProperties(_path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error showing file properties: ", ex);
|
||||
}
|
||||
|
||||
return CommandResult.GoHome();
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,17 @@
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedCsWin32;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Common.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
namespace Microsoft.CmdPal.Common.Commands;
|
||||
|
||||
internal sealed partial class OpenWithCommand : InvokableCommand
|
||||
public partial class OpenWithCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
internal static IconInfo OpenWithIcon { get; } = new("\uE7AC");
|
||||
|
||||
private readonly string _path;
|
||||
|
||||
private static unsafe bool OpenWith(string filename)
|
||||
{
|
||||
@@ -29,7 +29,7 @@ internal sealed partial class OpenWithCommand : InvokableCommand
|
||||
LpVerb = verbPtr,
|
||||
LpFile = filenamePtr,
|
||||
Show = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL,
|
||||
FMask = NativeHelpers.SEEMASKINVOKEIDLIST,
|
||||
FMask = global::Windows.Win32.PInvoke.SEE_MASK_INVOKEIDLIST,
|
||||
};
|
||||
|
||||
return Shell32.ShellExecuteEx(ref info);
|
||||
@@ -41,16 +41,16 @@ internal sealed partial class OpenWithCommand : InvokableCommand
|
||||
}
|
||||
}
|
||||
|
||||
internal OpenWithCommand(IndexerItem item)
|
||||
public OpenWithCommand(string fullPath)
|
||||
{
|
||||
this._item = item;
|
||||
this._path = fullPath;
|
||||
this.Name = Resources.Indexer_Command_OpenWith;
|
||||
this.Icon = new IconInfo("\uE7AC");
|
||||
this.Icon = OpenWithIcon;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
OpenWith(_item.FullPath);
|
||||
OpenWith(_path);
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
@@ -28,7 +28,24 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<ProjectReference Include="..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -9,3 +9,7 @@ GetWindowRect
|
||||
GetMonitorInfo
|
||||
SetWindowPos
|
||||
MonitorFromWindow
|
||||
|
||||
SHOW_WINDOW_CMD
|
||||
ShellExecuteEx
|
||||
SEE_MASK_INVOKEIDLIST
|
||||
99
src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.Designer.cs
generated
Normal file
99
src/modules/cmdpal/Microsoft.CmdPal.Common/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,99 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CmdPal.Common.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Common.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open path in console.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_OpenPathInConsole {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_OpenPathInConsole", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Properties.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_OpenProperties {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_OpenProperties", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open with.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_OpenWith {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_OpenWith", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show in folder.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_ShowInFolder {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_ShowInFolder", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Indexer_Command_OpenPathInConsole" xml:space="preserve">
|
||||
<value>Open path in console</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_OpenProperties" xml:space="preserve">
|
||||
<value>Properties</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_OpenWith" xml:space="preserve">
|
||||
<value>Open with</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_ShowInFolder" xml:space="preserve">
|
||||
<value>Show in folder</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -207,7 +207,10 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
clone.InsertRange(startIndex, newItems);
|
||||
|
||||
// now update the actual observable list with the new contents
|
||||
ListHelpers.InPlaceUpdateList(TopLevelCommands, clone);
|
||||
lock (TopLevelCommands)
|
||||
{
|
||||
ListHelpers.InPlaceUpdateList(TopLevelCommands, clone);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ReloadAllCommandsAsync()
|
||||
|
||||
@@ -63,9 +63,13 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
{
|
||||
return item as IContextItem;
|
||||
}
|
||||
else if (item is CommandContextItemViewModel commandItem)
|
||||
{
|
||||
return commandItem.Model.Unsafe;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((CommandContextItemViewModel)item).Model.Unsafe;
|
||||
return null;
|
||||
}
|
||||
}).ToArray();
|
||||
|
||||
|
||||
@@ -33,9 +33,14 @@ internal sealed partial class ContextItemTemplateSelector : DataTemplateSelector
|
||||
li.AllowFocusOnInteraction = false;
|
||||
dataTemplate = Separator;
|
||||
}
|
||||
else if (item is CommandContextItemViewModel commandItem)
|
||||
{
|
||||
dataTemplate = commandItem.IsCritical ? Critical : Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataTemplate = ((CommandContextItemViewModel)item).IsCritical ? Critical : Default;
|
||||
// Fallback for unknown types
|
||||
dataTemplate = Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
<Application>
|
||||
<Assembly Name="Microsoft.WinUI">
|
||||
<Type Name="Microsoft.UI.Xaml.Controls.FontIconSource" Dynamic="Required All" />
|
||||
<Type Name="Microsoft.UI.Xaml.DataTemplate" Dynamic="Required All" />
|
||||
<Type Name="Microsoft.UI.Xaml.Controls.DataTemplateSelector" Dynamic="Required All" />
|
||||
<Type Name="Microsoft.UI.Xaml.Controls.ListViewItem" Dynamic="Required All" />
|
||||
</Assembly>
|
||||
|
||||
<!-- Add ViewModel types for AOT compatibility -->
|
||||
<Assembly Name="Microsoft.CmdPal.Core.ViewModels">
|
||||
<Type Name="Microsoft.CmdPal.Core.ViewModels.CommandContextItemViewModel" Dynamic="Required All" />
|
||||
<Type Name="Microsoft.CmdPal.Core.ViewModels.SeparatorContextItemViewModel" Dynamic="Required All" />
|
||||
<Type Name="Microsoft.CmdPal.Core.ViewModels.IContextItemViewModel" Dynamic="Required All" />
|
||||
<Type Name="Microsoft.CmdPal.Core.ViewModels.CommandItemViewModel" Dynamic="Required All" />
|
||||
</Assembly>
|
||||
|
||||
<!-- Add UI types for AOT compatibility -->
|
||||
<Assembly Name="Microsoft.CmdPal.UI">
|
||||
<Type Name="Microsoft.CmdPal.UI.ContextItemTemplateSelector" Dynamic="Required All" />
|
||||
</Assembly>
|
||||
<Assembly Name="Microsoft.CmdPal.UI">
|
||||
<Type Name="Microsoft.CmdPal.UI.ContextItemTemplateSelector" Dynamic="Required All" />
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Xml;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Apps.Commands;
|
||||
using Microsoft.CmdPal.Ext.Apps.Properties;
|
||||
using Microsoft.CmdPal.Ext.Apps.State;
|
||||
using Microsoft.CmdPal.Ext.Apps.Utils;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -96,7 +95,7 @@ public class UWPApplication : IProgram
|
||||
|
||||
commands.Add(
|
||||
new CommandContextItem(
|
||||
new CopyPathCommand(Location))
|
||||
new Commands.CopyPathCommand(Location))
|
||||
{
|
||||
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.C),
|
||||
});
|
||||
|
||||
@@ -5,21 +5,15 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Apps.Commands;
|
||||
using Microsoft.CmdPal.Ext.Apps.Properties;
|
||||
using Microsoft.CmdPal.Ext.Apps.State;
|
||||
using Microsoft.CmdPal.Ext.Apps.Utils;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -190,7 +184,7 @@ public class Win32Program : IProgram
|
||||
|
||||
public List<IContextItem> GetCommands()
|
||||
{
|
||||
List<IContextItem> commands = new List<IContextItem>();
|
||||
List<IContextItem> commands = [];
|
||||
|
||||
if (AppType != ApplicationType.InternetShortcutApplication && AppType != ApplicationType.Folder && AppType != ApplicationType.GenericFile)
|
||||
{
|
||||
@@ -208,7 +202,7 @@ public class Win32Program : IProgram
|
||||
}
|
||||
|
||||
commands.Add(new CommandContextItem(
|
||||
new CopyPathCommand(FullPath))
|
||||
new Commands.CopyPathCommand(FullPath))
|
||||
{
|
||||
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.C),
|
||||
});
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
|
||||
@@ -75,31 +73,9 @@ internal sealed partial class AddBookmarkForm : FormContent
|
||||
var formBookmark = formInput["bookmark"] ?? string.Empty;
|
||||
var hasPlaceholder = formBookmark.ToString().Contains('{') && formBookmark.ToString().Contains('}');
|
||||
|
||||
// Determine the type of the bookmark
|
||||
string bookmarkType;
|
||||
|
||||
if (formBookmark.ToString().StartsWith("http://", StringComparison.OrdinalIgnoreCase) || formBookmark.ToString().StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
bookmarkType = "web";
|
||||
}
|
||||
else if (File.Exists(formBookmark.ToString()))
|
||||
{
|
||||
bookmarkType = "file";
|
||||
}
|
||||
else if (Directory.Exists(formBookmark.ToString()))
|
||||
{
|
||||
bookmarkType = "folder";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default to web if we can't determine the type
|
||||
bookmarkType = "web";
|
||||
}
|
||||
|
||||
var updated = _bookmark ?? new BookmarkData();
|
||||
updated.Name = formName.ToString();
|
||||
updated.Bookmark = formBookmark.ToString();
|
||||
updated.Type = bookmarkType;
|
||||
|
||||
AddedCommand?.Invoke(this, updated);
|
||||
return CommandResult.GoHome();
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Bookmarks;
|
||||
|
||||
@@ -12,8 +14,38 @@ public class BookmarkData
|
||||
|
||||
public string Bookmark { get; set; } = string.Empty;
|
||||
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
// public string Type { get; set; } = string.Empty;
|
||||
[JsonIgnore]
|
||||
public bool IsPlaceholder => Bookmark.Contains('{') && Bookmark.Contains('}');
|
||||
|
||||
internal void GetExeAndArgs(out string exe, out string args)
|
||||
{
|
||||
ShellHelpers.ParseExecutableAndArgs(Bookmark, out exe, out args);
|
||||
}
|
||||
|
||||
internal bool IsWebUrl()
|
||||
{
|
||||
GetExeAndArgs(out var exe, out var args);
|
||||
if (string.IsNullOrEmpty(exe))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Uri.TryCreate(exe, UriKind.Absolute, out var uri))
|
||||
{
|
||||
if (uri.Scheme == Uri.UriSchemeFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if the scheme is http or https, or if there's no scheme (e.g., "www.example.com") but there is a dot in the host
|
||||
return
|
||||
uri.Scheme == Uri.UriSchemeHttp ||
|
||||
uri.Scheme == Uri.UriSchemeHttps ||
|
||||
(string.IsNullOrEmpty(uri.Scheme) && uri.Host.Contains('.'));
|
||||
}
|
||||
|
||||
// If we can't parse it as a URI, we assume it's not a web URL
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.RegularExpressions;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Bookmarks;
|
||||
|
||||
@@ -25,7 +22,7 @@ internal sealed partial class BookmarkPlaceholderForm : FormContent
|
||||
private readonly string _bookmark = string.Empty;
|
||||
|
||||
// TODO pass in an array of placeholders
|
||||
public BookmarkPlaceholderForm(string name, string url, string type)
|
||||
public BookmarkPlaceholderForm(string name, string url)
|
||||
{
|
||||
_bookmark = url;
|
||||
var r = new Regex(Regex.Escape("{") + "(.*?)" + Regex.Escape("}"));
|
||||
@@ -88,23 +85,8 @@ internal sealed partial class BookmarkPlaceholderForm : FormContent
|
||||
target = target.Replace(placeholderString, placeholderData);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var uri = UrlCommand.GetUri(target);
|
||||
if (uri != null)
|
||||
{
|
||||
_ = Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// throw new UriFormatException("The provided URL is not valid.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
}
|
||||
var success = UrlCommand.LaunchCommand(target);
|
||||
|
||||
return CommandResult.GoHome();
|
||||
return success ? CommandResult.Dismiss() : CommandResult.KeepOpen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -9,19 +10,30 @@ namespace Microsoft.CmdPal.Ext.Bookmarks;
|
||||
|
||||
internal sealed partial class BookmarkPlaceholderPage : ContentPage
|
||||
{
|
||||
private readonly Lazy<IconInfo> _icon;
|
||||
private readonly FormContent _bookmarkPlaceholder;
|
||||
|
||||
public override IContent[] GetContent() => [_bookmarkPlaceholder];
|
||||
|
||||
public override IconInfo Icon { get => _icon.Value; set => base.Icon = value; }
|
||||
|
||||
public BookmarkPlaceholderPage(BookmarkData data)
|
||||
: this(data.Name, data.Bookmark, data.Type)
|
||||
: this(data.Name, data.Bookmark)
|
||||
{
|
||||
}
|
||||
|
||||
public BookmarkPlaceholderPage(string name, string url, string type)
|
||||
public BookmarkPlaceholderPage(string name, string url)
|
||||
{
|
||||
Name = name;
|
||||
Icon = new IconInfo(UrlCommand.IconFromUrl(url, type));
|
||||
_bookmarkPlaceholder = new BookmarkPlaceholderForm(name, url, type);
|
||||
Name = Properties.Resources.bookmarks_command_name_open;
|
||||
|
||||
_bookmarkPlaceholder = new BookmarkPlaceholderForm(name, url);
|
||||
|
||||
_icon = new Lazy<IconInfo>(() =>
|
||||
{
|
||||
ShellHelpers.ParseExecutableAndArgs(url, out var exe, out var args);
|
||||
var t = UrlCommand.GetIconForPath(exe);
|
||||
t.Wait();
|
||||
return t.Result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ManagedCommon;
|
||||
@@ -35,10 +34,7 @@ public partial class BookmarksCommandProvider : CommandProvider
|
||||
private void AddNewCommand_AddedCommand(object sender, BookmarkData args)
|
||||
{
|
||||
ExtensionHost.LogMessage($"Adding bookmark ({args.Name},{args.Bookmark})");
|
||||
if (_bookmarks != null)
|
||||
{
|
||||
_bookmarks.Data.Add(args);
|
||||
}
|
||||
_bookmarks?.Data.Add(args);
|
||||
|
||||
SaveAndUpdateCommands();
|
||||
}
|
||||
@@ -116,7 +112,7 @@ public partial class BookmarksCommandProvider : CommandProvider
|
||||
// Add commands for folder types
|
||||
if (command is UrlCommand urlCommand)
|
||||
{
|
||||
if (urlCommand.Type == "folder")
|
||||
if (!bookmark.IsWebUrl())
|
||||
{
|
||||
contextMenu.Add(
|
||||
new CommandContextItem(new DirectoryPage(urlCommand.Url)));
|
||||
@@ -124,10 +120,11 @@ public partial class BookmarksCommandProvider : CommandProvider
|
||||
contextMenu.Add(
|
||||
new CommandContextItem(new OpenInTerminalCommand(urlCommand.Url)));
|
||||
}
|
||||
|
||||
listItem.Subtitle = urlCommand.Url;
|
||||
}
|
||||
|
||||
listItem.Title = bookmark.Name;
|
||||
listItem.Subtitle = bookmark.Bookmark;
|
||||
|
||||
var edit = new AddBookmarkPage(bookmark) { Icon = Icons.EditIcon };
|
||||
edit.AddedCommand += Edit_AddedCommand;
|
||||
contextMenu.Add(new CommandContextItem(edit));
|
||||
|
||||
@@ -78,6 +78,15 @@ namespace Microsoft.CmdPal.Ext.Bookmarks.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open.
|
||||
/// </summary>
|
||||
public static string bookmarks_command_name_open {
|
||||
get {
|
||||
return ResourceManager.GetString("bookmarks_command_name_open", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete.
|
||||
/// </summary>
|
||||
|
||||
@@ -148,6 +148,9 @@
|
||||
<data name="bookmarks_form_open" xml:space="preserve">
|
||||
<value>Open</value>
|
||||
</data>
|
||||
<data name="bookmarks_command_name_open" xml:space="preserve">
|
||||
<value>Open</value>
|
||||
</data>
|
||||
<data name="bookmarks_form_name_required" xml:space="preserve">
|
||||
<value>Name is required</value>
|
||||
</data>
|
||||
|
||||
@@ -3,52 +3,89 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Bookmarks;
|
||||
|
||||
public partial class UrlCommand : InvokableCommand
|
||||
{
|
||||
public string Type { get; }
|
||||
private readonly Lazy<IconInfo> _icon;
|
||||
|
||||
public string Url { get; }
|
||||
|
||||
public override IconInfo Icon { get => _icon.Value; set => base.Icon = value; }
|
||||
|
||||
public UrlCommand(BookmarkData data)
|
||||
: this(data.Name, data.Bookmark, data.Type)
|
||||
: this(data.Name, data.Bookmark)
|
||||
{
|
||||
}
|
||||
|
||||
public UrlCommand(string name, string url, string type)
|
||||
public UrlCommand(string name, string url)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
Name = Properties.Resources.bookmarks_command_name_open;
|
||||
|
||||
Url = url;
|
||||
Icon = new IconInfo(IconFromUrl(Url, type));
|
||||
|
||||
_icon = new Lazy<IconInfo>(() =>
|
||||
{
|
||||
ShellHelpers.ParseExecutableAndArgs(Url, out var exe, out var args);
|
||||
var t = GetIconForPath(exe);
|
||||
t.Wait();
|
||||
return t.Result;
|
||||
});
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
var target = Url;
|
||||
try
|
||||
var success = LaunchCommand(Url);
|
||||
|
||||
return success ? CommandResult.Dismiss() : CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
internal static bool LaunchCommand(string target)
|
||||
{
|
||||
ShellHelpers.ParseExecutableAndArgs(target, out var exe, out var args);
|
||||
return LaunchCommand(exe, args);
|
||||
}
|
||||
|
||||
internal static bool LaunchCommand(string exe, string args)
|
||||
{
|
||||
if (string.IsNullOrEmpty(exe))
|
||||
{
|
||||
var uri = GetUri(target);
|
||||
var message = "No executable found in the command.";
|
||||
Logger.LogError(message);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ShellHelpers.OpenInShell(exe, args))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we reach here, it means the command could not be executed
|
||||
// If there aren't args, then try again as a https: uri
|
||||
if (string.IsNullOrEmpty(args))
|
||||
{
|
||||
var uri = GetUri(exe);
|
||||
if (uri != null)
|
||||
{
|
||||
_ = Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// throw new UriFormatException("The provided URL is not valid.");
|
||||
Logger.LogError("The provided URL is not valid.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static Uri? GetUri(string url)
|
||||
@@ -65,35 +102,90 @@ public partial class UrlCommand : InvokableCommand
|
||||
return uri;
|
||||
}
|
||||
|
||||
internal static string IconFromUrl(string url, string type)
|
||||
public static async Task<IconInfo> GetIconForPath(string target)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "file":
|
||||
return "📄";
|
||||
case "folder":
|
||||
return "📁";
|
||||
case "web":
|
||||
default:
|
||||
// Get the base url up to the first placeholder
|
||||
var placeholderIndex = url.IndexOf('{');
|
||||
var baseString = placeholderIndex > 0 ? url.Substring(0, placeholderIndex) : url;
|
||||
try
|
||||
{
|
||||
var uri = GetUri(baseString);
|
||||
if (uri != null)
|
||||
{
|
||||
var hostname = uri.Host;
|
||||
var faviconUrl = $"{uri.Scheme}://{hostname}/favicon.ico";
|
||||
return faviconUrl;
|
||||
}
|
||||
}
|
||||
catch (UriFormatException ex)
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
}
|
||||
IconInfo? icon = null;
|
||||
|
||||
return "🔗";
|
||||
// First, try to get the icon from the thumbnail helper
|
||||
// This works for local files and folders
|
||||
icon = await MaybeGetIconForPath(target);
|
||||
if (icon != null)
|
||||
{
|
||||
return icon;
|
||||
}
|
||||
|
||||
// Okay, that failed. Try to resolve the full path of the executable
|
||||
var exeExists = false;
|
||||
var fullExePath = string.Empty;
|
||||
try
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(200));
|
||||
|
||||
// Use Task.Run with timeout - this will actually timeout even if the sync operations don't respond to cancellation
|
||||
var pathResolutionTask = Task.Run(
|
||||
() =>
|
||||
{
|
||||
// Don't check cancellation token here - let the Task timeout handle it
|
||||
exeExists = ShellHelpers.FileExistInPath(target, out fullExePath);
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
// Wait for either completion or timeout
|
||||
pathResolutionTask.Wait(cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Debug.WriteLine("Operation was canceled.");
|
||||
}
|
||||
|
||||
if (exeExists)
|
||||
{
|
||||
// If the executable exists, try to get the icon from the file
|
||||
icon = await MaybeGetIconForPath(fullExePath);
|
||||
if (icon != null)
|
||||
{
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the base url up to the first placeholder
|
||||
var placeholderIndex = target.IndexOf('{');
|
||||
var baseString = placeholderIndex > 0 ? target.Substring(0, placeholderIndex) : target;
|
||||
try
|
||||
{
|
||||
var uri = GetUri(baseString);
|
||||
if (uri != null)
|
||||
{
|
||||
var hostname = uri.Host;
|
||||
var faviconUrl = $"{uri.Scheme}://{hostname}/favicon.ico";
|
||||
icon = new IconInfo(faviconUrl);
|
||||
}
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
}
|
||||
|
||||
// If we still don't have an icon, use the target as the icon
|
||||
icon = icon ?? new IconInfo(target);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static async Task<IconInfo?> MaybeGetIconForPath(string target)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stream = await ThumbnailHelper.GetThumbnail(target);
|
||||
if (stream != null)
|
||||
{
|
||||
var data = new IconData(RandomAccessStreamReference.CreateFromStream(stream));
|
||||
return new IconInfo(data, data);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// 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.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class CopyPathCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
internal CopyPathCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_CopyPath;
|
||||
this.Icon = new IconInfo("\uE8c8");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
ClipboardHelper.SetText(_item.FullPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// 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 System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class OpenFileCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
internal OpenFileCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_OpenFile;
|
||||
this.Icon = Icons.OpenFileIcon;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo.FileName = _item.FullPath;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Logger.LogError($"Unable to open {_item.FullPath}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,16 @@ internal sealed class IndexerItem
|
||||
|
||||
internal string FileName { get; init; }
|
||||
|
||||
internal IndexerItem()
|
||||
{
|
||||
}
|
||||
|
||||
internal IndexerItem(string fullPath)
|
||||
{
|
||||
FullPath = fullPath;
|
||||
FileName = Path.GetFileName(fullPath);
|
||||
}
|
||||
|
||||
internal bool IsDirectory()
|
||||
{
|
||||
if (!Path.Exists(FullPath))
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.CmdPal.Common.Commands;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Pages;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation.Metadata;
|
||||
|
||||
@@ -28,51 +29,79 @@ internal sealed partial class IndexerListItem : ListItem
|
||||
public IndexerListItem(
|
||||
IndexerItem indexerItem,
|
||||
IncludeBrowseCommand browseByDefault = IncludeBrowseCommand.Include)
|
||||
: base(new OpenFileCommand(indexerItem))
|
||||
: base()
|
||||
{
|
||||
FilePath = indexerItem.FullPath;
|
||||
|
||||
Title = indexerItem.FileName;
|
||||
Subtitle = indexerItem.FullPath;
|
||||
List<CommandContextItem> context = [];
|
||||
if (indexerItem.IsDirectory())
|
||||
|
||||
var commands = FileCommands(indexerItem.FullPath, browseByDefault);
|
||||
if (commands.Any())
|
||||
{
|
||||
var directoryPage = new DirectoryPage(indexerItem.FullPath);
|
||||
Command = commands.First().Command;
|
||||
MoreCommands = commands.Skip(1).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CommandContextItem> FileCommands(string fullPath)
|
||||
{
|
||||
return FileCommands(fullPath, IncludeBrowseCommand.Include);
|
||||
}
|
||||
|
||||
internal static IEnumerable<CommandContextItem> FileCommands(
|
||||
string fullPath,
|
||||
IncludeBrowseCommand browseByDefault = IncludeBrowseCommand.Include)
|
||||
{
|
||||
List<CommandContextItem> commands = [];
|
||||
if (!Path.Exists(fullPath))
|
||||
{
|
||||
return commands;
|
||||
}
|
||||
|
||||
// detect whether it is a directory or file
|
||||
var attr = File.GetAttributes(fullPath);
|
||||
var isDir = (attr & FileAttributes.Directory) == FileAttributes.Directory;
|
||||
|
||||
var openCommand = new OpenFileCommand(fullPath) { Name = Resources.Indexer_Command_OpenFile };
|
||||
if (isDir)
|
||||
{
|
||||
var directoryPage = new DirectoryPage(fullPath);
|
||||
if (browseByDefault == IncludeBrowseCommand.AsDefault)
|
||||
{
|
||||
// Swap the open file command into the context menu
|
||||
context.Add(new CommandContextItem(Command));
|
||||
Command = directoryPage;
|
||||
// AsDefault: browse dir first, then open in explorer
|
||||
commands.Add(new CommandContextItem(directoryPage));
|
||||
commands.Add(new CommandContextItem(openCommand));
|
||||
}
|
||||
else if (browseByDefault == IncludeBrowseCommand.Include)
|
||||
{
|
||||
context.Add(new CommandContextItem(directoryPage));
|
||||
// AsDefault: open in explorer first, then browse
|
||||
commands.Add(new CommandContextItem(openCommand));
|
||||
commands.Add(new CommandContextItem(directoryPage));
|
||||
}
|
||||
else if (browseByDefault == IncludeBrowseCommand.Exclude)
|
||||
{
|
||||
// AsDefault: Just open in explorer
|
||||
commands.Add(new CommandContextItem(openCommand));
|
||||
}
|
||||
}
|
||||
|
||||
IContextItem[] moreCommands = [
|
||||
..context,
|
||||
new CommandContextItem(new OpenWithCommand(indexerItem))];
|
||||
commands.Add(new CommandContextItem(new OpenWithCommand(fullPath)));
|
||||
commands.Add(new CommandContextItem(new ShowFileInFolderCommand(fullPath) { Name = Resources.Indexer_Command_ShowInFolder }));
|
||||
commands.Add(new CommandContextItem(new CopyPathCommand(fullPath) { Name = Resources.Indexer_Command_CopyPath }));
|
||||
commands.Add(new CommandContextItem(new OpenInConsoleCommand(fullPath)));
|
||||
commands.Add(new CommandContextItem(new OpenPropertiesCommand(fullPath)));
|
||||
|
||||
if (IsActionsFeatureEnabled && ApiInformation.IsApiContractPresent("Windows.AI.Actions.ActionsContract", 4))
|
||||
{
|
||||
var actionsListContextItem = new ActionsListContextItem(indexerItem.FullPath);
|
||||
var actionsListContextItem = new ActionsListContextItem(fullPath);
|
||||
if (actionsListContextItem.AnyActions())
|
||||
{
|
||||
moreCommands = [
|
||||
.. moreCommands,
|
||||
actionsListContextItem
|
||||
];
|
||||
commands.Add(actionsListContextItem);
|
||||
}
|
||||
}
|
||||
|
||||
MoreCommands = [
|
||||
.. moreCommands,
|
||||
new CommandContextItem(new ShowFileInFolderCommand(indexerItem.FullPath) { Name = Resources.Indexer_Command_ShowInFolder }),
|
||||
new CommandContextItem(new CopyPathCommand(indexerItem)),
|
||||
new CommandContextItem(new OpenInConsoleCommand(indexerItem)),
|
||||
new CommandContextItem(new OpenPropertiesCommand(indexerItem)),
|
||||
];
|
||||
return commands;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ internal sealed partial class FallbackOpenFileItem : FallbackCommandItem, System
|
||||
if (Path.Exists(query))
|
||||
{
|
||||
// Exit 1: The query is a direct path to a file. Great! Return it.
|
||||
var item = new IndexerItem() { FullPath = query, FileName = Path.GetFileName(query) };
|
||||
var item = new IndexerItem(fullPath: query);
|
||||
var listItemForUs = new IndexerListItem(item, IncludeBrowseCommand.AsDefault);
|
||||
Command = listItemForUs.Command;
|
||||
MoreCommands = listItemForUs.MoreCommands;
|
||||
|
||||
@@ -10,14 +10,15 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<ProjectReference Include="..\..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
using Microsoft.CmdPal.Common.Commands;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -41,16 +41,16 @@ internal sealed partial class ExploreListItem : ListItem
|
||||
}
|
||||
else
|
||||
{
|
||||
Command = new OpenFileCommand(indexerItem);
|
||||
Command = new OpenFileCommand(indexerItem.FullPath);
|
||||
}
|
||||
|
||||
MoreCommands = [
|
||||
..context,
|
||||
new CommandContextItem(new OpenWithCommand(indexerItem)),
|
||||
new CommandContextItem(new OpenWithCommand(indexerItem.FullPath)),
|
||||
new CommandContextItem(new ShowFileInFolderCommand(indexerItem.FullPath) { Name = Resources.Indexer_Command_ShowInFolder }),
|
||||
new CommandContextItem(new CopyPathCommand(indexerItem)),
|
||||
new CommandContextItem(new OpenInConsoleCommand(indexerItem)),
|
||||
new CommandContextItem(new OpenPropertiesCommand(indexerItem)),
|
||||
new CommandContextItem(new CopyPathCommand(indexerItem.FullPath)),
|
||||
new CommandContextItem(new OpenInConsoleCommand(indexerItem.FullPath)),
|
||||
new CommandContextItem(new OpenPropertiesCommand(indexerItem.FullPath)),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem, IDispos
|
||||
return;
|
||||
}
|
||||
|
||||
ShellListPage.ParseExecutableAndArgs(searchText, out var exe, out var args);
|
||||
ShellHelpers.ParseExecutableAndArgs(searchText, out var exe, out var args);
|
||||
|
||||
// Check for cancellation before file system operations
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -191,7 +191,7 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem, IDispos
|
||||
return false;
|
||||
}
|
||||
|
||||
ShellListPage.ParseExecutableAndArgs(searchText, out var exe, out var args);
|
||||
ShellHelpers.ParseExecutableAndArgs(searchText, out var exe, out var args);
|
||||
var exeExists = ShellListPageHelpers.FileExistInPath(exe, out var fullExePath);
|
||||
var pathIsDir = Directory.Exists(exe);
|
||||
|
||||
|
||||
@@ -54,47 +54,8 @@ public class ShellListPageHelpers
|
||||
|
||||
internal static bool FileExistInPath(string filename, out string fullPath, CancellationToken? token = null)
|
||||
{
|
||||
fullPath = string.Empty;
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
token?.ThrowIfCancellationRequested();
|
||||
fullPath = Path.GetFullPath(filename);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var values = Environment.GetEnvironmentVariable("PATH");
|
||||
if (values != null)
|
||||
{
|
||||
foreach (var path in values.Split(';'))
|
||||
{
|
||||
var path1 = Path.Combine(path, filename);
|
||||
if (File.Exists(path1))
|
||||
{
|
||||
fullPath = Path.GetFullPath(path1);
|
||||
return true;
|
||||
}
|
||||
|
||||
token?.ThrowIfCancellationRequested();
|
||||
|
||||
var path2 = Path.Combine(path, filename + ".exe");
|
||||
if (File.Exists(path2))
|
||||
{
|
||||
fullPath = Path.GetFullPath(path2);
|
||||
return true;
|
||||
}
|
||||
|
||||
token?.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO! remove this method and just use ShellHelpers.FileExistInPath directly
|
||||
return ShellHelpers.FileExistInPath(filename, out fullPath, token ?? CancellationToken.None);
|
||||
}
|
||||
|
||||
internal static ListItem? ListItemForCommandString(string query, Action<string>? addToHistory)
|
||||
@@ -109,7 +70,7 @@ public class ShellListPageHelpers
|
||||
return null;
|
||||
}
|
||||
|
||||
ShellListPage.ParseExecutableAndArgs(searchText, out var exe, out var args);
|
||||
ShellHelpers.ParseExecutableAndArgs(searchText, out var exe, out var args);
|
||||
|
||||
var exeExists = false;
|
||||
var pathIsDir = false;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<None Remove="Assets\Run.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Shell.Pages;
|
||||
|
||||
@@ -54,13 +55,13 @@ internal sealed partial class RunExeItem : ListItem
|
||||
{
|
||||
Name = Properties.Resources.cmd_run_as_administrator,
|
||||
Icon = Icons.AdminIcon,
|
||||
}),
|
||||
}) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.Enter) },
|
||||
new CommandContextItem(
|
||||
new AnonymousCommand(RunAsOther)
|
||||
{
|
||||
Name = Properties.Resources.cmd_run_as_user,
|
||||
Icon = Icons.UserIcon,
|
||||
}),
|
||||
}) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.U) },
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
|
||||
|
||||
private readonly IRunHistoryService _historyService;
|
||||
|
||||
private RunExeItem? _exeItem;
|
||||
private ListItem? _exeItem;
|
||||
private List<ListItem> _pathItems = [];
|
||||
private ListItem? _uriItem;
|
||||
|
||||
@@ -152,7 +152,7 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
ParseExecutableAndArgs(expanded, out var exe, out var args);
|
||||
ShellHelpers.ParseExecutableAndArgs(expanded, out var exe, out var args);
|
||||
|
||||
// Check for cancellation before file system operations
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -319,20 +319,19 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
internal static RunExeItem CreateExeItem(string exe, string args, string fullExePath, Action<string>? addToHistory)
|
||||
internal static ListItem CreateExeItem(string exe, string args, string fullExePath, Action<string>? addToHistory)
|
||||
{
|
||||
// PathToListItem will return a RunExeItem if it can find a executable.
|
||||
// It will ALSO add the file search commands to the RunExeItem.
|
||||
return PathToListItem(fullExePath, exe, args, addToHistory) as RunExeItem ??
|
||||
new RunExeItem(exe, args, fullExePath, addToHistory);
|
||||
return PathToListItem(fullExePath, exe, args, addToHistory);
|
||||
}
|
||||
|
||||
private void CreateAndAddExeItems(string exe, string args, string fullExePath)
|
||||
{
|
||||
// If we already have an exe item, and the exe is the same, we can just update it
|
||||
if (_exeItem != null && _exeItem.FullExePath.Equals(fullExePath, StringComparison.OrdinalIgnoreCase))
|
||||
if (_exeItem is RunExeItem exeItem && exeItem.FullExePath.Equals(fullExePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_exeItem.UpdateArgs(args);
|
||||
exeItem.UpdateArgs(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -345,7 +344,8 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
|
||||
// Is this path an executable?
|
||||
// check all the extensions in PATHEXT
|
||||
var extensions = Environment.GetEnvironmentVariable("PATHEXT")?.Split(';') ?? Array.Empty<string>();
|
||||
return extensions.Any(ext => string.Equals(Path.GetExtension(path), ext, StringComparison.OrdinalIgnoreCase));
|
||||
var extension = Path.GetExtension(path);
|
||||
return string.IsNullOrEmpty(extension) || extensions.Any(ext => string.Equals(extension, ext, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private async Task CreatePathItemsAsync(string searchPath, string originalPath, CancellationToken cancellationToken)
|
||||
@@ -439,46 +439,6 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ParseExecutableAndArgs(string input, out string executable, out string arguments)
|
||||
{
|
||||
input = input.Trim();
|
||||
executable = string.Empty;
|
||||
arguments = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.StartsWith("\"", System.StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// Find the closing quote
|
||||
var closingQuoteIndex = input.IndexOf('\"', 1);
|
||||
if (closingQuoteIndex > 0)
|
||||
{
|
||||
executable = input.Substring(1, closingQuoteIndex - 1);
|
||||
if (closingQuoteIndex + 1 < input.Length)
|
||||
{
|
||||
arguments = input.Substring(closingQuoteIndex + 1).TrimStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Executable ends at first space
|
||||
var firstSpaceIndex = input.IndexOf(' ');
|
||||
if (firstSpaceIndex > 0)
|
||||
{
|
||||
executable = input.Substring(0, firstSpaceIndex);
|
||||
arguments = input[(firstSpaceIndex + 1)..].TrimStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
executable = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void CreateUriItems(string searchText)
|
||||
{
|
||||
if (!System.Uri.TryCreate(searchText, UriKind.Absolute, out var uri))
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.CmdPal.Common.Commands;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Shell;
|
||||
|
||||
@@ -46,18 +48,15 @@ internal sealed partial class PathListItem : ListItem
|
||||
}
|
||||
|
||||
TextToSuggest = suggestion;
|
||||
MoreCommands = [
|
||||
new CommandContextItem(new CopyTextCommand(path) { Name = Properties.Resources.copy_path_command_name }) { }
|
||||
];
|
||||
|
||||
// TODO: Follow-up during 0.4. Add the indexer commands here.
|
||||
// MoreCommands = [
|
||||
// new CommandContextItem(new OpenWithCommand(indexerItem)),
|
||||
// new CommandContextItem(new ShowFileInFolderCommand(indexerItem.FullPath) { Name = Resources.Indexer_Command_ShowInFolder }),
|
||||
// new CommandContextItem(new CopyPathCommand(indexerItem)),
|
||||
// new CommandContextItem(new OpenInConsoleCommand(indexerItem)),
|
||||
// new CommandContextItem(new OpenPropertiesCommand(indexerItem)),
|
||||
// ];
|
||||
MoreCommands = [
|
||||
new CommandContextItem(new OpenWithCommand(path)),
|
||||
new CommandContextItem(new ShowFileInFolderCommand(path)) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.E) },
|
||||
new CommandContextItem(new CopyPathCommand(path) { Name = Properties.Resources.copy_path_command_name }) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.C) },
|
||||
new CommandContextItem(new OpenInConsoleCommand(path)) { RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.R) },
|
||||
new CommandContextItem(new OpenPropertiesCommand(path)),
|
||||
];
|
||||
|
||||
_icon = new Lazy<IconInfo>(() =>
|
||||
{
|
||||
var iconStream = ThumbnailHelper.GetThumbnail(path).Result;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CopyPathCommand : InvokableCommand
|
||||
{
|
||||
internal static IconInfo CopyPath { get; } = new("\uE8c8"); // Copy
|
||||
|
||||
private readonly string _path;
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.KeepOpen();
|
||||
|
||||
public CopyPathCommand(string fullPath)
|
||||
{
|
||||
this._path = fullPath;
|
||||
this.Name = "Copy path";
|
||||
this.Icon = CopyPath;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
ClipboardHelper.SetText(_path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class OpenFileCommand : InvokableCommand
|
||||
{
|
||||
internal static IconInfo OpenFile { get; } = new("\uE8E5"); // OpenFile
|
||||
|
||||
private readonly string _fullPath;
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.Dismiss();
|
||||
|
||||
public OpenFileCommand(string fullPath)
|
||||
{
|
||||
this._fullPath = fullPath;
|
||||
this.Name = "Open";
|
||||
this.Icon = OpenFile;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo.FileName = _fullPath;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage($"Unable to open {_fullPath}\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,4 @@ GetPackageFamilyNameFromToken
|
||||
CoRevertToSelf
|
||||
SHGetKnownFolderPath
|
||||
KNOWN_FOLDER_FLAG
|
||||
GetCurrentPackageId
|
||||
GetCurrentPackageId
|
||||
@@ -59,4 +59,101 @@ public static class ShellHelpers
|
||||
Administrator,
|
||||
OtherUser,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the input string to extract the executable and its arguments.
|
||||
/// </summary>
|
||||
public static void ParseExecutableAndArgs(string input, out string executable, out string arguments)
|
||||
{
|
||||
input = input.Trim();
|
||||
executable = string.Empty;
|
||||
arguments = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.StartsWith("\"", System.StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// Find the closing quote
|
||||
var closingQuoteIndex = input.IndexOf('\"', 1);
|
||||
if (closingQuoteIndex > 0)
|
||||
{
|
||||
executable = input.Substring(1, closingQuoteIndex - 1);
|
||||
if (closingQuoteIndex + 1 < input.Length)
|
||||
{
|
||||
arguments = input.Substring(closingQuoteIndex + 1).TrimStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Executable ends at first space
|
||||
var firstSpaceIndex = input.IndexOf(' ');
|
||||
if (firstSpaceIndex > 0)
|
||||
{
|
||||
executable = input.Substring(0, firstSpaceIndex);
|
||||
arguments = input[(firstSpaceIndex + 1)..].TrimStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
executable = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a file exists somewhere in the PATH.
|
||||
/// If it exists, returns the full path to the file in the out parameter.
|
||||
/// If it does not exist, returns false and the out parameter is set to an empty string.
|
||||
/// <param name="filename">The name of the file to check.</param>
|
||||
/// <param name="fullPath">The full path to the file if it exists; otherwise an empty string.</param>
|
||||
/// <param name="token">An optional cancellation token to cancel the operation.</param>
|
||||
/// <returns>True if the file exists in the PATH; otherwise false.</returns>
|
||||
/// </summary>
|
||||
public static bool FileExistInPath(string filename, out string fullPath, CancellationToken? token = null)
|
||||
{
|
||||
fullPath = string.Empty;
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
token?.ThrowIfCancellationRequested();
|
||||
fullPath = Path.GetFullPath(filename);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var values = Environment.GetEnvironmentVariable("PATH");
|
||||
if (values != null)
|
||||
{
|
||||
foreach (var path in values.Split(';'))
|
||||
{
|
||||
var path1 = Path.Combine(path, filename);
|
||||
if (File.Exists(path1))
|
||||
{
|
||||
fullPath = Path.GetFullPath(path1);
|
||||
return true;
|
||||
}
|
||||
|
||||
token?.ThrowIfCancellationRequested();
|
||||
|
||||
var path2 = Path.Combine(path, filename + ".exe");
|
||||
if (File.Exists(path2))
|
||||
{
|
||||
fullPath = Path.GetFullPath(path2);
|
||||
return true;
|
||||
}
|
||||
|
||||
token?.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user