mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-05-09 05:06:04 +00:00
Merge pull request #184 from gnh1201/dev
Add some code for #183 (Auto completion)
This commit is contained in:
commit
7947d91c3a
122
WelsonJS.Toolkit/WelsonJS.Launcher/ExecutablesCollector.cs
Normal file
122
WelsonJS.Toolkit/WelsonJS.Launcher/ExecutablesCollector.cs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace WelsonJS.Launcher
|
||||||
|
{
|
||||||
|
public class ExecutablesCollector
|
||||||
|
{
|
||||||
|
private List<string> executables = new List<string>();
|
||||||
|
|
||||||
|
public ExecutablesCollector()
|
||||||
|
{
|
||||||
|
executables.AddRange(GetInstalledSoftwareExecutables());
|
||||||
|
executables.AddRange(GetExecutablesFromPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetExecutables()
|
||||||
|
{
|
||||||
|
return executables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> GetInstalledSoftwareExecutables()
|
||||||
|
{
|
||||||
|
List<string> executables = new List<string>();
|
||||||
|
string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||||
|
|
||||||
|
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey))
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
foreach (string subKeyName in key.GetSubKeyNames())
|
||||||
|
{
|
||||||
|
using (RegistryKey subKey = key.OpenSubKey(subKeyName))
|
||||||
|
{
|
||||||
|
string installLocation = subKey?.GetValue("InstallLocation") as string;
|
||||||
|
string uninstallString = subKey?.GetValue("UninstallString") as string;
|
||||||
|
|
||||||
|
List<string> executablePaths = FindExecutables(installLocation, uninstallString);
|
||||||
|
executables.AddRange(executablePaths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return executables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> FindExecutables(string installLocation, string uninstallString)
|
||||||
|
{
|
||||||
|
List<string> executables = new List<string>();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(installLocation) && Directory.Exists(installLocation))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<string> executableFiles = Directory.GetFiles(installLocation, "*.exe", SearchOption.AllDirectories)
|
||||||
|
.OrderByDescending(f => new FileInfo(f).Length)
|
||||||
|
.ToList();
|
||||||
|
executables.AddRange(executableFiles);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error enumerating executables in '{installLocation}': {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(uninstallString))
|
||||||
|
{
|
||||||
|
if (TryParseExecutablePath(uninstallString, out string executablePath))
|
||||||
|
{
|
||||||
|
executables.Add(executablePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return executables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseExecutablePath(string s, out string path)
|
||||||
|
{
|
||||||
|
Match match = Regex.Match(s, @"(?<=""|^)([a-zA-Z]:\\[^""]+\.exe)", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
path = match.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> GetExecutablesFromPath()
|
||||||
|
{
|
||||||
|
List<string> executables = new List<string>();
|
||||||
|
string pathEnv = Environment.GetEnvironmentVariable("PATH");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(pathEnv))
|
||||||
|
{
|
||||||
|
foreach (string path in pathEnv.Split(';'))
|
||||||
|
{
|
||||||
|
if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
executables.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.TopDirectoryOnly));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error enumerating executables in '{path}': {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return executables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace WelsonJS.Launcher
|
namespace WelsonJS.Launcher
|
||||||
{
|
{
|
||||||
|
@ -18,6 +21,7 @@ namespace WelsonJS.Launcher
|
||||||
private bool _isRunning;
|
private bool _isRunning;
|
||||||
private string _prefix;
|
private string _prefix;
|
||||||
private string _resourceName;
|
private string _resourceName;
|
||||||
|
private ExecutablesCollector _executablesCollector;
|
||||||
|
|
||||||
public ResourceServer(string prefix, string resourceName)
|
public ResourceServer(string prefix, string resourceName)
|
||||||
{
|
{
|
||||||
|
@ -25,6 +29,7 @@ namespace WelsonJS.Launcher
|
||||||
_listener = new HttpListener();
|
_listener = new HttpListener();
|
||||||
_listener.Prefixes.Add(prefix);
|
_listener.Prefixes.Add(prefix);
|
||||||
_resourceName = resourceName;
|
_resourceName = resourceName;
|
||||||
|
_executablesCollector = new ExecutablesCollector();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetPrefix()
|
public string GetPrefix()
|
||||||
|
@ -81,15 +86,82 @@ namespace WelsonJS.Launcher
|
||||||
{
|
{
|
||||||
string path = context.Request.Url.AbsolutePath.TrimStart('/');
|
string path = context.Request.Url.AbsolutePath.TrimStart('/');
|
||||||
|
|
||||||
|
// Serve the favicon.ico file
|
||||||
if ("favicon.ico".Equals(path, StringComparison.OrdinalIgnoreCase))
|
if ("favicon.ico".Equals(path, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ServeResource(context, GetResource("favicon"), "image/x-icon");
|
ServeResource(context, GetResource("favicon"), "image/x-icon");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve the code completion (word suggestion)
|
||||||
|
if (path.StartsWith("completion/", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
ServeCompletion(context, path.Substring("completion/".Length));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve a resource
|
||||||
ServeResource(context, GetResource(_resourceName), "text/html");
|
ServeResource(context, GetResource(_resourceName), "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ServeCompletion(HttpListenerContext context, string word)
|
||||||
|
{
|
||||||
|
int statusCode = 200;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<string> executables = _executablesCollector.GetExecutables();
|
||||||
|
|
||||||
|
CompletionItem[] completionItems = executables
|
||||||
|
.Where(exec => exec.IndexOf(word, 0, StringComparison.OrdinalIgnoreCase) > -1)
|
||||||
|
.Take(100) // Limit results to prevent excessive response sizes
|
||||||
|
.Select(exec => new CompletionItem
|
||||||
|
{
|
||||||
|
Label = Path.GetFileName(exec),
|
||||||
|
Kind = "Text",
|
||||||
|
Documentation = "An executable file",
|
||||||
|
InsertText = exec
|
||||||
|
})
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
XElement response = new XElement("suggestions",
|
||||||
|
completionItems.Select(item => new XElement("item",
|
||||||
|
new XElement("label", item.Label),
|
||||||
|
new XElement("kind", item.Kind),
|
||||||
|
new XElement("documentation", item.Documentation),
|
||||||
|
new XElement("insertText", item.InsertText)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
byte[] data = Encoding.UTF8.GetBytes(
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
|
||||||
|
response.ToString()
|
||||||
|
);
|
||||||
|
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
context.Response.ContentType = "application/xml";
|
||||||
|
context.Response.ContentLength64 = data.Length;
|
||||||
|
using (Stream outputStream = context.Response.OutputStream)
|
||||||
|
{
|
||||||
|
outputStream.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
byte[] errorData = Encoding.UTF8.GetBytes(
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
|
||||||
|
$"<error>Failed to process completion request. {ex.Message}</error>"
|
||||||
|
);
|
||||||
|
context.Response.StatusCode = 500;
|
||||||
|
context.Response.ContentType = "application/xml";
|
||||||
|
context.Response.ContentLength64 = errorData.Length;
|
||||||
|
using (Stream outputStream = context.Response.OutputStream)
|
||||||
|
{
|
||||||
|
outputStream.Write(errorData, 0, errorData.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ServeResource(HttpListenerContext context, byte[] data, string mimeType = "text/html")
|
private void ServeResource(HttpListenerContext context, byte[] data, string mimeType = "text/html")
|
||||||
{
|
{
|
||||||
int statusCode = 200;
|
int statusCode = 200;
|
||||||
|
@ -105,8 +177,10 @@ namespace WelsonJS.Launcher
|
||||||
context.Response.StatusCode = statusCode;
|
context.Response.StatusCode = statusCode;
|
||||||
context.Response.ContentType = mimeType;
|
context.Response.ContentType = mimeType;
|
||||||
context.Response.ContentLength64 = data.Length;
|
context.Response.ContentLength64 = data.Length;
|
||||||
context.Response.OutputStream.Write(data, 0, data.Length);
|
using (Stream outputStream = context.Response.OutputStream)
|
||||||
context.Response.OutputStream.Close();
|
{
|
||||||
|
outputStream.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] GetResource(string resourceName)
|
private byte[] GetResource(string resourceName)
|
||||||
|
@ -159,4 +233,12 @@ namespace WelsonJS.Launcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CompletionItem
|
||||||
|
{
|
||||||
|
public string Label { get; set; }
|
||||||
|
public string Kind { get; set; }
|
||||||
|
public string Documentation { get; set; }
|
||||||
|
public string InsertText { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="EnvForm.cs">
|
<Compile Include="EnvForm.cs">
|
||||||
|
@ -76,6 +77,7 @@
|
||||||
<Compile Include="EnvForm.Designer.cs">
|
<Compile Include="EnvForm.Designer.cs">
|
||||||
<DependentUpon>EnvForm.cs</DependentUpon>
|
<DependentUpon>EnvForm.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="ExecutablesCollector.cs" />
|
||||||
<Compile Include="InstancesForm.cs">
|
<Compile Include="InstancesForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user