mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-05-07 12:16: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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
|
@ -18,6 +21,7 @@ namespace WelsonJS.Launcher
|
|||
private bool _isRunning;
|
||||
private string _prefix;
|
||||
private string _resourceName;
|
||||
private ExecutablesCollector _executablesCollector;
|
||||
|
||||
public ResourceServer(string prefix, string resourceName)
|
||||
{
|
||||
|
@ -25,6 +29,7 @@ namespace WelsonJS.Launcher
|
|||
_listener = new HttpListener();
|
||||
_listener.Prefixes.Add(prefix);
|
||||
_resourceName = resourceName;
|
||||
_executablesCollector = new ExecutablesCollector();
|
||||
}
|
||||
|
||||
public string GetPrefix()
|
||||
|
@ -81,15 +86,82 @@ namespace WelsonJS.Launcher
|
|||
{
|
||||
string path = context.Request.Url.AbsolutePath.TrimStart('/');
|
||||
|
||||
// Serve the favicon.ico file
|
||||
if ("favicon.ico".Equals(path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ServeResource(context, GetResource("favicon"), "image/x-icon");
|
||||
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");
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
int statusCode = 200;
|
||||
|
@ -105,8 +177,10 @@ namespace WelsonJS.Launcher
|
|||
context.Response.StatusCode = statusCode;
|
||||
context.Response.ContentType = mimeType;
|
||||
context.Response.ContentLength64 = data.Length;
|
||||
context.Response.OutputStream.Write(data, 0, data.Length);
|
||||
context.Response.OutputStream.Close();
|
||||
using (Stream outputStream = context.Response.OutputStream)
|
||||
{
|
||||
outputStream.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
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 Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="EnvForm.cs">
|
||||
|
@ -76,6 +77,7 @@
|
|||
<Compile Include="EnvForm.Designer.cs">
|
||||
<DependentUpon>EnvForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ExecutablesCollector.cs" />
|
||||
<Compile Include="InstancesForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
|
Loading…
Reference in New Issue
Block a user