mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-05-27 20:17:00 +00:00
Fix issue with file executable path auto-completion
This commit is contained in:
parent
7e29cc649f
commit
d9f9d3e38b
|
@ -17,19 +17,16 @@ namespace WelsonJS.Launcher.ResourceTools
|
|||
private readonly ResourceServer Server;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string Prefix = "completion/";
|
||||
private List<string> Executables = new List<string>();
|
||||
private List<string> DiscoverdExecutables = new List<string>();
|
||||
|
||||
public Completion(ResourceServer server, HttpClient httpClient)
|
||||
{
|
||||
Server = server;
|
||||
_httpClient = httpClient;
|
||||
|
||||
new Task(() =>
|
||||
{
|
||||
Executables.AddRange(GetInstalledSoftwareExecutables());
|
||||
Executables.AddRange(GetExecutablesFromPath());
|
||||
Executables.AddRange(GetExecutablesFromNetFx());
|
||||
}).Start();
|
||||
Task.Run(() => DiscoverFromInstalledSoftware());
|
||||
Task.Run(() => DiscoverFromPathVariable());
|
||||
Task.Run(() => DiscoverFromProgramDirectories());
|
||||
}
|
||||
|
||||
public bool CanHandle(string path)
|
||||
|
@ -45,7 +42,7 @@ namespace WelsonJS.Launcher.ResourceTools
|
|||
|
||||
try
|
||||
{
|
||||
CompletionItem[] completionItems = Executables
|
||||
CompletionItem[] completionItems = DiscoverdExecutables
|
||||
.Where(exec => exec.IndexOf(word, 0, StringComparison.OrdinalIgnoreCase) > -1)
|
||||
.Take(100) // Limit the number of results
|
||||
.Select(exec => new CompletionItem
|
||||
|
@ -70,137 +67,109 @@ namespace WelsonJS.Launcher.ResourceTools
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.ServeResource(context, $"<error>Failed to process completion request. {ex.Message}</error>", "application/xml", 500);
|
||||
Server.ServeResource(context, $"<error>Failed to try completion. {ex.Message}</error>", "application/xml", 500);
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> GetInstalledSoftwareExecutables()
|
||||
private void DiscoverFromInstalledSoftware()
|
||||
{
|
||||
List<string> executables = new List<string>();
|
||||
string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
const string registryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey))
|
||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryPath))
|
||||
{
|
||||
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;
|
||||
if (key == null) return;
|
||||
|
||||
List<string> executablePaths = FindExecutables(installLocation, uninstallString);
|
||||
executables.AddRange(executablePaths);
|
||||
foreach (string subKeyName in key.GetSubKeyNames())
|
||||
{
|
||||
RegistryKey subKey = key.OpenSubKey(subKeyName);
|
||||
if (subKey == null) continue;
|
||||
|
||||
using (subKey)
|
||||
{
|
||||
string installLocation = subKey.GetValue("InstallLocation") as string;
|
||||
if (!string.IsNullOrEmpty(installLocation))
|
||||
{
|
||||
SearchAllExecutables(installLocation);
|
||||
}
|
||||
|
||||
string uninstallString = subKey.GetValue("UninstallString") as string;
|
||||
if (!string.IsNullOrEmpty(uninstallString))
|
||||
{
|
||||
var match = Regex.Match(uninstallString, @"(?<=""|^)([a-zA-Z]:\\[^""]+\.exe)", RegexOptions.IgnoreCase);
|
||||
if (match.Success && File.Exists(match.Value))
|
||||
{
|
||||
DiscoverdExecutables.Add(match.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
|
||||
private List<string> FindExecutables(string installLocation, string uninstallString)
|
||||
{
|
||||
List<string> executables = new List<string>();
|
||||
private void DiscoverFromPathVariable() {
|
||||
var paths = (Environment.GetEnvironmentVariable("PATH") ?? string.Empty)
|
||||
.Split(';')
|
||||
.Select(p => p.Trim())
|
||||
.Where(p => !string.IsNullOrEmpty(p));
|
||||
|
||||
if (!string.IsNullOrEmpty(installLocation) && Directory.Exists(installLocation))
|
||||
foreach (string path in paths)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
SearchAllExecutables(path, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(uninstallString))
|
||||
{
|
||||
if (TryParseExecutablePath(uninstallString, out string executablePath))
|
||||
{
|
||||
executables.Add(executablePath);
|
||||
}
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
|
||||
private static bool TryParseExecutablePath(string s, out string path)
|
||||
private void DiscoverFromProgramDirectories()
|
||||
{
|
||||
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[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(';');
|
||||
|
||||
if (paths != null)
|
||||
{
|
||||
foreach (string path in paths)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private List<string> GetExecutablesFromNetFx()
|
||||
{
|
||||
List<string> executables = new List<string>();
|
||||
|
||||
string windir = Environment.GetEnvironmentVariable("WINDIR");
|
||||
string programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
||||
string userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
|
||||
if (!string.IsNullOrEmpty(windir))
|
||||
var paths = new[]
|
||||
{
|
||||
string[] paths = new string[]
|
||||
{
|
||||
Path.Combine(windir, "Microsoft.NET", "Framework"),
|
||||
Path.Combine(windir, "Microsoft.NET", "Framework64")
|
||||
};
|
||||
// Default directory
|
||||
Path.Combine(Program.GetAppDataPath(), "bin"),
|
||||
|
||||
foreach (string path in paths)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
executables.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.AllDirectories));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error enumerating executables in '{path}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Standard program installation directories
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
|
||||
|
||||
// .NET Framework directories
|
||||
Path.Combine(windir, "Microsoft.NET", "Framework"),
|
||||
Path.Combine(windir, "Microsoft.NET", "Framework64"),
|
||||
|
||||
// Chocolatey package directory
|
||||
Path.Combine(programData, "chocolatey", "lib"),
|
||||
|
||||
// Scoop apps directory
|
||||
Path.Combine(userProfile, "scoop", "apps")
|
||||
};
|
||||
|
||||
foreach (string path in paths)
|
||||
{
|
||||
SearchAllExecutables(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchAllExecutables(string path, SearchOption searchOption = SearchOption.AllDirectories)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Trace.TraceWarning("Directory does not exist: {0}", path);
|
||||
return;
|
||||
}
|
||||
|
||||
return executables;
|
||||
try
|
||||
{
|
||||
var executableFiles = Directory.GetFiles(path, "*.exe", searchOption)
|
||||
.OrderByDescending(f => new FileInfo(f).Length)
|
||||
.ToList();
|
||||
|
||||
DiscoverdExecutables.AddRange(executableFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceWarning("Error enumerating executables in '{0}': {1}", path, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -170,6 +170,31 @@
|
|||
function Editor({ editorRef }) {
|
||||
const containerRef = React.useRef(null);
|
||||
|
||||
const getSuggestions = (word, range) => axios.get(`/completion/${encodeURIComponent(word)}`)
|
||||
.then(response => {
|
||||
const parser = new XMLParser();
|
||||
const result = parser.parse(response.data);
|
||||
|
||||
const suggestions = ((item) => Array.isArray(item) ? item : [item])(result.suggestions.item).map((item) => {
|
||||
return {
|
||||
label: item.label,
|
||||
kind: monaco.languages.CompletionItemKind.Text,
|
||||
documentation: item.documentation || "",
|
||||
insertText: '"' + item.insertText + '"',
|
||||
range: range
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
suggestions: suggestions
|
||||
};
|
||||
})
|
||||
.catch(function () {
|
||||
return {
|
||||
suggestions: []
|
||||
};
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!containerRef.current)
|
||||
return;
|
||||
|
@ -180,6 +205,20 @@
|
|||
language: 'javascript'
|
||||
});
|
||||
|
||||
monaco.languages.registerCompletionItemProvider('javascript', {
|
||||
provideCompletionItems: function (model, position) {
|
||||
const word = model.getWordUntilPosition(position);
|
||||
const range = {
|
||||
startLineNumber: position.lineNumber,
|
||||
endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endColumn: word.endColumn
|
||||
};
|
||||
|
||||
return getSuggestions(word.word, range);
|
||||
}
|
||||
});
|
||||
|
||||
editorRef.current = instance;
|
||||
});
|
||||
}, []);
|
||||
|
@ -234,14 +273,13 @@
|
|||
}
|
||||
|
||||
function App() {
|
||||
const serverPrefix = "http://localhost:3000/";
|
||||
const editorRef = React.useRef(null);
|
||||
const promptEditorRef = React.useRef(null);
|
||||
const settingsRef = React.useRef({});
|
||||
const fileNameRef = React.useRef('sayhello.js');
|
||||
const promptMessagesRef = React.useRef([]);
|
||||
|
||||
const fetchSettings = () => axios.get(`${serverPrefix}settings`)
|
||||
const fetchSettings = () => axios.get(`/settings`)
|
||||
.then(response => {
|
||||
const parser = new XMLParser();
|
||||
const result = parser.parse(response.data);
|
||||
|
@ -259,38 +297,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
const getSuggestions = (word) => axios.get(`${serverPrefix}completion/${encodeURIComponent(word)}`)
|
||||
.then(response => {
|
||||
const parser = new XMLParser();
|
||||
const result = parser.parse(response.data);
|
||||
|
||||
if (!result.suggestions || !result.suggestions.item) {
|
||||
return {
|
||||
suggestions: []
|
||||
};
|
||||
}
|
||||
|
||||
const items = Array.isArray(result.suggestions.item) ? result.suggestions.item : [result.suggestions.item];
|
||||
const suggestions = items.map(function (item) {
|
||||
return {
|
||||
label: item.label,
|
||||
kind: monaco.languages.CompletionItemKind.Text,
|
||||
documentation: item.documentation || "",
|
||||
insertText: '"' + item.insertText + '"',
|
||||
range: range
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
suggestions: suggestions
|
||||
};
|
||||
})
|
||||
.catch(function () {
|
||||
return {
|
||||
suggestions: []
|
||||
};
|
||||
});
|
||||
|
||||
const pushPromptMessage = (role, content) => {
|
||||
promptMessagesRef.current.push({
|
||||
role: role,
|
||||
|
@ -442,7 +448,7 @@
|
|||
};
|
||||
|
||||
const getTargetByUrl = async (urlPart) => {
|
||||
const response = await fetch(`${serverPrefix}devtools/json`);
|
||||
const response = await fetch(`/devtools/json`);
|
||||
const targets = await response.json();
|
||||
|
||||
const target = targets.find(target => target.url.includes(urlPart));
|
||||
|
@ -542,7 +548,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
axios.get(`${serverPrefix}whois/${hostname}`).then(response => {
|
||||
axios.get(`/whois/${hostname}`).then(response => {
|
||||
const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
||||
appendTextToEditor(`/*\nHostname:${hostname}\n\n${responseText}\n*/`);
|
||||
pushPromptMessage("system", responseText);
|
||||
|
@ -558,7 +564,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
axios.get(`${serverPrefix}dns-query/${hostname}`).then(response => {
|
||||
axios.get(`/dns-query/${hostname}`).then(response => {
|
||||
const responseText = response.data;
|
||||
appendTextToEditor(`/*\nHostname:${hostname}\n\n${responseText}\n*/`);
|
||||
pushPromptMessage("system", responseText);
|
||||
|
@ -583,7 +589,7 @@
|
|||
const apiPrefix = settingsRef.current.CitiApiPrefix;
|
||||
const ip = encodeURIComponent(hostname.trim());
|
||||
|
||||
axios.get(`${serverPrefix}citi-query/${hostname}`).then(response => {
|
||||
axios.get(`/citi-query/${hostname}`).then(response => {
|
||||
const parser = new XMLParser();
|
||||
const result = parser.parse(response.data);
|
||||
const data = JSON.parse(result.json);
|
||||
|
|
Loading…
Reference in New Issue
Block a user