diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index ee5ee787a4..66098c9ac2 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -276,6 +276,7 @@ codecvt codeofconduct codeql codereview +Codespaces COINIT colorconv colorhistory @@ -1808,6 +1809,7 @@ rgelt Rgn rgs rhs +ricardosantos Riched Richtext RIGHTSCROLLBAR @@ -2005,6 +2007,7 @@ srre srw srwlock ssf +ssh sstream STACKFRAME stackoverflow @@ -2224,6 +2227,7 @@ uncompilable UNCPRIORITY undef UNDNAME +unescape Ungroup unicode Unindent @@ -2320,8 +2324,10 @@ VOS VREDRAW VSC VSCBD +vscode VSCROLL vse +vsonline vstemplate VSTHRD VSTS @@ -2432,6 +2438,7 @@ wofstream wordpad workaround workflow +workspaces wostream wox wparam @@ -2444,6 +2451,7 @@ WResize wrl wsf wsh +wsl wss wstr wstring diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml index 43d33d4a2e..d873aed929 100644 --- a/.pipelines/pipeline.user.windows.yml +++ b/.pipelines/pipeline.user.windows.yml @@ -131,6 +131,7 @@ build: - 'modules\launcher\Plugins\Microsoft.Plugin.WindowWalker\Wox.Infrastructure.dll' - 'modules\launcher\Plugins\Microsoft.Plugin.WindowWalker\Wox.Plugin.dll' - 'modules\launcher\Plugins\Microsoft.Plugin.WindowWalker\Telemetry.dll' + - 'modules\launcher\Plugins\VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll' - 'modules\launcher\Plugins\Service\Microsoft.PowerToys.Run.Plugin.Service.dll' - 'modules\launcher\Plugins\System\Microsoft.PowerToys.Run.Plugin.System.dll' - 'modules\launcher\PowerLauncher.dll' diff --git a/PowerToys.sln b/PowerToys.sln index 0b600fd0b0..c12d4dbf79 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -128,6 +128,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{4AFC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.Calculator", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Calculator\Microsoft.PowerToys.Run.Plugin.Calculator.csproj", "{59BD9891-3837-438A-958D-ADC7F91F6F7E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.VSCodeWorkspaces", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj", "{4D971245-7A70-41D5-BAA0-DDB5684CAF51}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.WindowWalker", "src\modules\launcher\Plugins\Microsoft.Plugin.WindowWalker\Microsoft.Plugin.WindowWalker.csproj", "{74F1B9ED-F59C-4FE7-B473-7B453E30837E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Program", "src\modules\launcher\Plugins\Microsoft.Plugin.Program\Microsoft.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}" @@ -418,6 +420,10 @@ Global {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|x64.Build.0 = Debug|x64 {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.ActiveCfg = Release|x64 {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.Build.0 = Release|x64 + {4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Debug|x64.ActiveCfg = Debug|x64 + {4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Debug|x64.Build.0 = Debug|x64 + {4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Release|x64.ActiveCfg = Release|x64 + {4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Release|x64.Build.0 = Release|x64 {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x64.ActiveCfg = Debug|x64 {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x64.Build.0 = Debug|x64 {74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|x64.ActiveCfg = Release|x64 @@ -665,6 +671,7 @@ Global {FF742965-9A80-41A5-B042-D6C7D3A21708} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68} {4AFC9975-2456-4C70-94A4-84073C1CED93} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {4AFC9975-2456-4C70-94A4-84073C1CED93} + {4D971245-7A70-41D5-BAA0-DDB5684CAF51} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {74F1B9ED-F59C-4FE7-B473-7B453E30837E} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {4AFC9975-2456-4C70-94A4-84073C1CED93} diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 6238a4e94c..6130f4c949 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -245,6 +245,10 @@ + + + + @@ -818,7 +822,7 @@ - + @@ -894,6 +898,9 @@ + + + @@ -1006,7 +1013,20 @@ - + + + + + + + + + + + + + + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj new file mode 100644 index 0000000000..605295bc93 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj @@ -0,0 +1,90 @@ + + + + + netcoreapp3.1 + {4D971245-7A70-41D5-BAA0-DDB5684CAF51} + Properties + Community.PowerToys.Run.Plugin.VSCodeWorkspaces + Community.PowerToys.Run.Plugin.VSCodeWorkspaces + true + false + false + x64 + + + + true + ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\VSCodeWorkspaces\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + 4 + false + + + + ..\..\..\..\..\x64\Release\modules\launcher\Plugins\VSCodeWorkspaces\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + 4 + + + + + PreserveNewest + + + + + + + + + + + + + + + + + C:\Program Files (x86)\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.UniversalApiContract\8.0.0.0\Windows.Foundation.UniversalApiContract.winmd + + + + + + Resources.resx + True + True + + + + + + Resources.Designer.cs + ResXFileCodeGenerator + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/folder.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/folder.png new file mode 100644 index 0000000000..0cd340aff9 Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/folder.png differ diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/monitor.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/monitor.png new file mode 100644 index 0000000000..04a46374f2 Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/monitor.png differ diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/vscode_plugin.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/vscode_plugin.png new file mode 100644 index 0000000000..0ecd1147fc Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Images/vscode_plugin.png differ diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/LocProject.json b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/LocProject.json new file mode 100644 index 0000000000..e3b4ee8fc9 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/LocProject.json @@ -0,0 +1,14 @@ +{ + "Projects": [ + { + "LanguageSet": "Azure_Languages", + "LocItems": [ + { + "SourceFile": "src\\modules\\launcher\\Plugins\\VSCodeWorkspaces\\Properties\\Resources.resx", + "CopyOption": "LangIDOnName", + "OutputPath": "src\\modules\\launcher\\Plugins\\VSCodeWorkspaces\\Properties" + } + ] + } + ] +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs new file mode 100644 index 0000000000..3de8a6e89c --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Windows.Controls; +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.Properties; +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.RemoteMachinesHelper; +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper; +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.WorkspacesHelper; +using Microsoft.PowerToys.Settings.UI.Library; +using Wox.Plugin; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces +{ + public class Main : IPlugin, IPluginI18n + { + public PluginInitContext _context { get; private set; } + + public string Name => GetTranslatedPluginTitle(); + + public string Description => GetTranslatedPluginDescription(); + + public Main() + { + VSCodeInstances.LoadVSCodeInstances(); + } + + public readonly VSCodeWorkspacesApi _workspacesApi = new VSCodeWorkspacesApi(); + + public readonly VSCodeRemoteMachinesApi _machinesApi = new VSCodeRemoteMachinesApi(); + + public List Query(Query query) + { + var results = new List(); + + if (query != null) + { + // Search opened workspaces + _workspacesApi.Workspaces.ForEach(a => + { + var title = $"{a.FolderName}"; + + var typeWorkspace = a.WorkspaceTypeToString(); + if (a.TypeWorkspace == TypeWorkspace.Codespaces) + { + title += $" - {typeWorkspace}"; + } + else if (a.TypeWorkspace != TypeWorkspace.Local) + { + title += $" - {(a.ExtraInfo != null ? $"{a.ExtraInfo} ({typeWorkspace})" : typeWorkspace)}"; + } + + results.Add(new Result + { + Title = title, + SubTitle = $"{Resources.Workspace}{(a.TypeWorkspace != TypeWorkspace.Local ? $" {Resources.In} {typeWorkspace}" : "")}: {SystemPath.RealPath(a.RelativePath)}", + Icon = a.VSCodeInstance.WorkspaceIcon, + Action = c => + { + bool hide; + try + { + var process = new ProcessStartInfo + { + FileName = a.VSCodeInstance.ExecutablePath, + UseShellExecute = true, + Arguments = $"--folder-uri {a.Path}", + WindowStyle = ProcessWindowStyle.Hidden + }; + Process.Start(process); + + hide = true; + } + catch (Win32Exception) + { + var name = $"Plugin: {_context.CurrentPluginMetadata.Name}"; + var msg = "Can't Open this file"; + _context.API.ShowMsg(name, msg, string.Empty); + hide = false; + } + return hide; + }, + ContextData = a, + }); + }); + + // Search opened remote machines + _machinesApi.Machines.ForEach(a => + { + var title = $"{a.Host}"; + + if (a.User != null && a.User != String.Empty && a.HostName != null && a.HostName != String.Empty) + { + title += $" [{a.User}@{a.HostName}]"; + } + + results.Add(new Result + { + Title = title, + SubTitle = Resources.SSHRemoteMachine, + Icon = a.VSCodeInstance.RemoteIcon, + Action = c => + { + bool hide; + try + { + var process = new ProcessStartInfo() + { + FileName = a.VSCodeInstance.ExecutablePath, + UseShellExecute = true, + Arguments = $"--new-window --enable-proposed-api ms-vscode-remote.remote-ssh --remote ssh-remote+{((char)34) + a.Host + ((char)34)}", + WindowStyle = ProcessWindowStyle.Hidden, + }; + Process.Start(process); + + hide = true; + } + catch (Win32Exception) + { + var name = $"Plugin: {_context.CurrentPluginMetadata.Name}"; + var msg = "Can't Open this file"; + _context.API.ShowMsg(name, msg, string.Empty); + hide = false; + } + return hide; + }, + ContextData = a, + }); + }); + } + + results.ForEach(x => + { + if (x.Score == 0) + { + x.Score = 100; + } + + //if is a remote machine give it 12 extra points + if (x.ContextData is VSCodeRemoteMachine) + { + x.Score = x.Score + (query.Search.Count() * 5); + } + + //intersect the title with the query + var intersection = x.Title.ToLower().Intersect(query.Search.ToLower()).Count(); + x.Score = x.Score - (Convert.ToInt32(((x.Title.Count() - intersection) *2.5))); + }); + + results = results.OrderBy(x => x.Title).ToList(); + + if (query.ActionKeyword == String.Empty || (query.ActionKeyword != String.Empty && query.Search != String.Empty)) + { + results = results.Where(a => a.Title.ToLower().Contains(query.Search.ToLower())).ToList(); + } + + return results; + } + + public void Init(PluginInitContext context) + { + _context = context; + } + + public string GetTranslatedPluginTitle() + { + return Resources.PluginTitle; + } + + public string GetTranslatedPluginDescription() + { + return Resources.PluginDescription; + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..13ca6c4314 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Properties/Resources.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "16.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("Community.PowerToys.Run.Plugin.VSCodeWorkspaces.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to in. + /// + internal static string In { + get { + return ResourceManager.GetString("In", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open VSCode previously opened workspaces, remote machines (SSH or Codespaces) and containers.. + /// + internal static string PluginDescription { + get { + return ResourceManager.GetString("PluginDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to VSCode Workspaces. + /// + internal static string PluginTitle { + get { + return ResourceManager.GetString("PluginTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SSH remote machine. + /// + internal static string SSHRemoteMachine { + get { + return ResourceManager.GetString("SSHRemoteMachine", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Container. + /// + internal static string TypeWorkspaceContainer { + get { + return ResourceManager.GetString("TypeWorkspaceContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Local. + /// + internal static string TypeWorkspaceLocal { + get { + return ResourceManager.GetString("TypeWorkspaceLocal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Workspace. + /// + internal static string Workspace { + get { + return ResourceManager.GetString("Workspace", resourceCulture); + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Properties/Resources.resx b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Properties/Resources.resx new file mode 100644 index 0000000000..fc639c9cb7 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Properties/Resources.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + VSCode Workspaces + + + in + Used to indicate the location where something is + + + Open VSCode previously opened workspaces, remote machines (SSH or Codespaces) and containers. + + + SSH remote machine + + + Container + As in "Visual Studio Code workspace container type + + + Local + As in "The workspace is on the local machine" + + + Workspace + It refers to the "Visual Studio Code workspace" + + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RealPath.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RealPath.cs new file mode 100644 index 0000000000..04180331ab --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RealPath.cs @@ -0,0 +1,23 @@ +using System; +using System.Text.RegularExpressions; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces +{ + class SystemPath + { + private static readonly Regex WindowsPath = new Regex(@"^([a-zA-Z]:)", RegexOptions.Compiled); + + public static string RealPath(string path) + { + if (WindowsPath.IsMatch(path)) + { + String windowsPath = path.Replace("/", "\\"); + return $"{windowsPath[0]}".ToUpper() + windowsPath.Remove(0,1); + } + else + { + return path; + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachine.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachine.cs new file mode 100644 index 0000000000..db88fe1419 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachine.cs @@ -0,0 +1,15 @@ +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.RemoteMachinesHelper +{ + public class VSCodeRemoteMachine + { + public string Host { get; set; } + + public string User { get; set; } + + public string HostName { get; set; } + + public VSCodeInstance VSCodeInstance { get; set; } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs new file mode 100644 index 0000000000..5571c077d7 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs @@ -0,0 +1,64 @@ +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.SshConfigParser; +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.RemoteMachinesHelper +{ + public class VSCodeRemoteMachinesApi + { + public VSCodeRemoteMachinesApi() { } + + public List Machines + { + get + { + var results = new List(); + + foreach (var vscodeInstance in VSCodeInstances.instances) + { + // settings.json contains path of ssh_config + var vscode_settings = Path.Combine(vscodeInstance.AppData, "User\\settings.json"); + + if (File.Exists(vscode_settings)) + { + var fileContent = File.ReadAllText(vscode_settings); + + try + { + dynamic vscodeSettingsFile = JsonConvert.DeserializeObject(fileContent); + if (vscodeSettingsFile.ContainsKey("remote.SSH.configFile")) + { + var path = vscodeSettingsFile["remote.SSH.configFile"]; + if (File.Exists(path.Value)) + { + foreach (SshHost h in SshConfig.ParseFile(path.Value)) + { + var machine = new VSCodeRemoteMachine(); + machine.Host = h.Host; + machine.VSCodeInstance = vscodeInstance; + machine.HostName = h.HostName != null ? h.HostName : String.Empty; + machine.User = h.User != null ? h.User : String.Empty; + + results.Add(machine); + } + } + } + } + catch (Exception ex) + { + var message = $"Failed to deserialize ${vscode_settings}"; + Log.Exception(message, ex, GetType()); + } + } + } + + return results; + + } + } + } +} \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SshConfigParser/Parser.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SshConfigParser/Parser.cs new file mode 100644 index 0000000000..f94b7747c0 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SshConfigParser/Parser.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.SshConfigParser +{ + public class SshConfig + { + private static readonly Regex SSH_CONFIG = new Regex(@"^(\w[\s\S]*?\w)$(?=(?:\s+^\w|\z))", RegexOptions.Multiline); + private static readonly Regex KEY_VALUE = new Regex(@"(\w+\s\S+)", RegexOptions.Multiline); + + public static IEnumerable ParseFile(string path) + { + return Parse(File.ReadAllText(path)); + } + + public static IEnumerable Parse(string str) + { + + str = str.Replace("\r", ""); + var list = new List(); + foreach (Match match in SSH_CONFIG.Matches(str)) + { + var sshHost = new SshHost(); + string content = match.Groups.Values.ToList()[0].Value; + foreach (Match match1 in KEY_VALUE.Matches(content)) + { + var split = match1.Value.Split(" "); + var key = split[0]; + var value = split[1]; + sshHost.Properties[key] = value; + } + list.Add(sshHost); + } + return list; + } + } +} \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SshConfigParser/SshHost.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SshConfigParser/SshHost.cs new file mode 100644 index 0000000000..f062b3a39d --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SshConfigParser/SshHost.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.SshConfigParser +{ + public class SshHost + { + public string IdentityFile + { + get => this[nameof(IdentityFile)]?.ToString(); + set => this[nameof(IdentityFile)] = value; + } + + public string Host + { + get => this[nameof(Host)]?.ToString(); + set => this[nameof(Host)] = value; + } + + public string HostName + { + get => this[nameof(HostName)]?.ToString(); + set => this[nameof(HostName)] = value; + } + + public string User { + get => this[nameof(User)]?.ToString(); + set => this[nameof(User)] = value; + } + + public string ForwardAgent { + get => this[nameof(ForwardAgent)]?.ToString(); + set => this[nameof(ForwardAgent)] = value; + } + + internal Dictionary Properties { get; } = new Dictionary(); + + public object this[string key] + { + get + { + if (Properties.TryGetValue(key, out var value)) + { + return value; + } + + return null; + } + set { Properties[key] = value; } + } + + public IEnumerable Keys => Properties.Keys; + } +} \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/VSCodeHelper/VSCodeInstance.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/VSCodeHelper/VSCodeInstance.cs new file mode 100644 index 0000000000..212299dea1 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/VSCodeHelper/VSCodeInstance.cs @@ -0,0 +1,30 @@ +using System; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper +{ + public enum VSCodeVersion + { + Stable = 1, + Insiders = 2, + Exploration = 3 + } + + public class VSCodeInstance + { + public VSCodeVersion VSCodeVersion { get; set; } + + public string ExecutablePath { get; set; } = String.Empty; + + public string AppData { get; set; } = String.Empty; + + public ImageSource WorkspaceIcon(){ return WorkspaceIconBitMap; } + + public ImageSource RemoteIcon(){ return RemoteIconBitMap; } + + public BitmapImage WorkspaceIconBitMap { get; set; } + + public BitmapImage RemoteIconBitMap { get; set; } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/VSCodeHelper/VSCodeInstances.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/VSCodeHelper/VSCodeInstances.cs new file mode 100644 index 0000000000..598089d2cd --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/VSCodeHelper/VSCodeInstances.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper +{ + static class VSCodeInstances + { + private static readonly string PathUserAppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + + private static string _systemPath = String.Empty; + + private static string _userAppDataPath = Environment.GetEnvironmentVariable("AppData"); + + public static List instances = new List(); + + private static BitmapImage Bitmap2BitmapImage(Bitmap bitmap) + { + using (var memory = new MemoryStream()) + { + bitmap.Save(memory, ImageFormat.Png); + memory.Position = 0; + + var bitmapImage = new BitmapImage(); + bitmapImage.BeginInit(); + bitmapImage.StreamSource = memory; + bitmapImage.CacheOption = BitmapCacheOption.OnLoad; + bitmapImage.EndInit(); + bitmapImage.Freeze(); + + return bitmapImage; + } + } + + public static Bitmap bitmapOverlayToCenter(Bitmap bitmap1, Bitmap overlayBitmap) + { + int bitmap1Width = bitmap1.Width; + int bitmap1Height = bitmap1.Height; + + Bitmap overlayBitmapResized = new Bitmap(overlayBitmap, new System.Drawing.Size(bitmap1Width / 2, bitmap1Height / 2)); + + float marginLeft = (float)(bitmap1Width * 0.7 - overlayBitmapResized.Width * 0.5); + float marginTop = (float)(bitmap1Height * 0.7 - overlayBitmapResized.Height * 0.5); + + Bitmap finalBitmap = new Bitmap(bitmap1Width, bitmap1Height); + using (Graphics g = Graphics.FromImage(finalBitmap)) + { + g.DrawImage(bitmap1, System.Drawing.Point.Empty); + g.DrawImage(overlayBitmapResized, marginLeft, marginTop); + } + return finalBitmap; + } + + // Gets the executablePath and AppData foreach instance of VSCode + public static void LoadVSCodeInstances() + { + if (_systemPath != Environment.GetEnvironmentVariable("PATH")) + { + + instances = new List(); + + _systemPath = Environment.GetEnvironmentVariable("PATH"); + var paths = _systemPath.Split(";"); + paths = paths.Where(x => x.Contains("VS Code")).ToArray(); + foreach (var path in paths) + { + if (Directory.Exists(path)) + { + var files = Directory.GetFiles(path); + var iconPath = Path.GetDirectoryName(path); + files = files.Where(x => x.Contains("code") && !x.EndsWith(".cmd")).ToArray(); + + if (files.Length > 0) + { + var file = files[0]; + var version = String.Empty; + + var instance = new VSCodeInstance + { + ExecutablePath = file, + }; + + if (file.EndsWith("code")) + { + version = "Code"; + instance.VSCodeVersion = VSCodeVersion.Stable; + } + else if (file.EndsWith("code-insiders")) + { + version = "Code - Insiders"; + instance.VSCodeVersion = VSCodeVersion.Insiders; + } + else if (file.EndsWith("code-exploration")) + { + version = "Code - Exploration"; + instance.VSCodeVersion = VSCodeVersion.Exploration; + } + + if (version != String.Empty) + { + instance.AppData = Path.Combine(_userAppDataPath, version); + var iconVSCode = Path.Join(iconPath, $"{version}.exe"); + + var bitmapIconVscode = Icon.ExtractAssociatedIcon(iconVSCode).ToBitmap(); + + //workspace + var folderIcon = (Bitmap)System.Drawing.Image.FromFile(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "//Images//folder.png"); + instance.WorkspaceIconBitMap = Bitmap2BitmapImage(bitmapOverlayToCenter(folderIcon, bitmapIconVscode)); + + //remote + var monitorIcon = (Bitmap)System.Drawing.Image.FromFile(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "//Images//monitor.png"); + + instance.RemoteIconBitMap = Bitmap2BitmapImage(bitmapOverlayToCenter(monitorIcon, bitmapIconVscode)); + + instances.Add(instance); + } + } + } + } + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/ParseVSCodeUri.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/ParseVSCodeUri.cs new file mode 100644 index 0000000000..32c54f5a26 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/ParseVSCodeUri.cs @@ -0,0 +1,58 @@ +using System; +using System.Text.RegularExpressions; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.WorkspacesHelper +{ + public class ParseVSCodeUri + { + private static readonly Regex LocalWorkspace = new Regex("^file:///(.+)$", RegexOptions.Compiled); + + private static readonly Regex RemoteSSHWorkspace = new Regex(@"^vscode-remote://ssh-remote\+(.+?(?=\/))(.+)$", RegexOptions.Compiled); + + private static readonly Regex RemoteWSLWorkspace = new Regex(@"^vscode-remote://wsl\+(.+?(?=\/))(.+)$", RegexOptions.Compiled); + + private static readonly Regex CodespacesWorkspace = new Regex(@"^vscode-remote://vsonline\+(.+?(?=\/))(.+)$", RegexOptions.Compiled); + + public static (TypeWorkspace? TypeWorkspace, String MachineName, String Path) GetTypeWorkspace(string uri) + { + if (LocalWorkspace.IsMatch(uri)) + { + var match = LocalWorkspace.Match(uri); + + if (match.Groups.Count > 1) + { + return (TypeWorkspace.Local, null, match.Groups[1].Value); + } + } + else if (RemoteSSHWorkspace.IsMatch(uri)) + { + var match = RemoteSSHWorkspace.Match(uri); + + if (match.Groups.Count > 1) + { + return (TypeWorkspace.RemoteSSH, match.Groups[1].Value, match.Groups[2].Value); + } + } + else if (RemoteWSLWorkspace.IsMatch(uri)) + { + var match = RemoteWSLWorkspace.Match(uri); + + if (match.Groups.Count > 1) + { + return (TypeWorkspace.RemoteWSL, match.Groups[1].Value, match.Groups[2].Value); + } + } + else if (CodespacesWorkspace.IsMatch(uri)) + { + var match = CodespacesWorkspace.Match(uri); + + if (match.Groups.Count > 1) + { + return (TypeWorkspace.Codespaces, String.Empty, match.Groups[2].Value); + } + } + + return (null, null, null); + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeStorageFile.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeStorageFile.cs new file mode 100644 index 0000000000..c323dce2db --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeStorageFile.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.WorkspacesHelper +{ + public class VSCodeStorageFile + { + public openedPathsList openedPathsList { get; set; } + } + + public class openedPathsList + { + public List workspaces3 { get; set; } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspace.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspace.cs new file mode 100644 index 0000000000..9690dfd85e --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspace.cs @@ -0,0 +1,43 @@ +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.Properties; +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.WorkspacesHelper +{ + public class VSCodeWorkspace + { + public string Path { get; set; } + + public string RelativePath { get; set; } + + public string FolderName { get; set; } + + public string ExtraInfo { get; set; } + + public TypeWorkspace TypeWorkspace { get; set; } + + public VSCodeInstance VSCodeInstance { get; set; } + + public string WorkspaceTypeToString() + { + switch (TypeWorkspace) + { + case TypeWorkspace.Local: return Resources.TypeWorkspaceLocal; + case TypeWorkspace.Codespaces: return "Codespaces"; + case TypeWorkspace.RemoteContainers: return Resources.TypeWorkspaceContainer; + case TypeWorkspace.RemoteSSH: return "SSH"; + case TypeWorkspace.RemoteWSL: return "WSL"; + } + + return string.Empty; + } + } + + public enum TypeWorkspace + { + Local = 1, + Codespaces = 2, + RemoteWSL = 3, + RemoteSSH = 4, + RemoteContainers = 5 + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs new file mode 100644 index 0000000000..82da3ac43f --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs @@ -0,0 +1,74 @@ +using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.WorkspacesHelper +{ + public class VSCodeWorkspacesApi + { + public VSCodeWorkspacesApi() { } + + public List Workspaces + { + get + { + + var results = new List(); + + foreach (var vscodeInstance in VSCodeInstances.instances) + { + // storage.json contains opened Workspaces + var vscode_storage = Path.Combine(vscodeInstance.AppData, "storage.json"); + + if (File.Exists(vscode_storage)) + { + var fileContent = File.ReadAllText(vscode_storage); + + try + { + VSCodeStorageFile vscodeStorageFile = JsonConvert.DeserializeObject(fileContent); + + if (vscodeStorageFile != null) + { + foreach (var workspaceUri in vscodeStorageFile.openedPathsList.workspaces3) + { + if (workspaceUri != null && workspaceUri is String) + { + string unescapeUri = Uri.UnescapeDataString(workspaceUri); + var typeWorkspace = ParseVSCodeUri.GetTypeWorkspace(unescapeUri); + if (typeWorkspace.TypeWorkspace.HasValue) + { + var folderName = Path.GetFileName(unescapeUri); + results.Add(new VSCodeWorkspace() + { + Path = workspaceUri, + RelativePath = typeWorkspace.Path, + FolderName = folderName, + ExtraInfo = typeWorkspace.MachineName, + TypeWorkspace = typeWorkspace.TypeWorkspace.Value, + VSCodeInstance = vscodeInstance + }); + } + } + } + } + } + catch (Exception ex) + { + var message = $"Failed to deserialize ${vscode_storage}"; + Log.Exception(message, ex, GetType()); + } + + } + + } + + + return results; + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/plugin.json b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/plugin.json new file mode 100644 index 0000000000..bf8ce5323d --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/plugin.json @@ -0,0 +1,14 @@ +{ + "ID": "525995402BEF4A8CA860D92F6D108092", + "ActionKeyword": "{", + "Name": "VSCode Workspaces", + "Description": "Opened VSCode Workspaces", + "Author": "ricardosantos9521", + "Version": "1.0.0", + "Language": "csharp", + "Website": "https://github.com/ricardosantos9521/PowerToys/", + "ExecuteFileName": "Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll", + "IsGlobal": false, + "IcoPathDark": "Images\\vscode_plugin.png", + "IcoPathLight": "Images\\vscode_plugin.png" +} \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs index 50464bdb72..3f3267b747 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs @@ -45,7 +45,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.Properties { return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. @@ -68,7 +68,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.Properties { return ResourceManager.GetString("wox_plugin_calculator_copy_failed", resourceCulture); } } - + /// /// Looks up a localized string similar to Copy this number to the clipboard. /// @@ -95,6 +95,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.Properties { return ResourceManager.GetString("wox_plugin_calculator_not_a_number", resourceCulture); } } + /// /// Looks up a localized string similar to Does mathematical calculations (e.g. 5*3-2).. @@ -104,7 +105,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.Properties { return ResourceManager.GetString("wox_plugin_calculator_plugin_description", resourceCulture); } } - + /// /// Looks up a localized string similar to Calculator. ///