mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-05-28 20:47:01 +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 ResourceServer Server;
|
||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
private const string Prefix = "completion/";
|
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)
|
public Completion(ResourceServer server, HttpClient httpClient)
|
||||||
{
|
{
|
||||||
Server = server;
|
Server = server;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
|
||||||
new Task(() =>
|
Task.Run(() => DiscoverFromInstalledSoftware());
|
||||||
{
|
Task.Run(() => DiscoverFromPathVariable());
|
||||||
Executables.AddRange(GetInstalledSoftwareExecutables());
|
Task.Run(() => DiscoverFromProgramDirectories());
|
||||||
Executables.AddRange(GetExecutablesFromPath());
|
|
||||||
Executables.AddRange(GetExecutablesFromNetFx());
|
|
||||||
}).Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanHandle(string path)
|
public bool CanHandle(string path)
|
||||||
|
@ -45,7 +42,7 @@ namespace WelsonJS.Launcher.ResourceTools
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CompletionItem[] completionItems = Executables
|
CompletionItem[] completionItems = DiscoverdExecutables
|
||||||
.Where(exec => exec.IndexOf(word, 0, StringComparison.OrdinalIgnoreCase) > -1)
|
.Where(exec => exec.IndexOf(word, 0, StringComparison.OrdinalIgnoreCase) > -1)
|
||||||
.Take(100) // Limit the number of results
|
.Take(100) // Limit the number of results
|
||||||
.Select(exec => new CompletionItem
|
.Select(exec => new CompletionItem
|
||||||
|
@ -70,137 +67,109 @@ namespace WelsonJS.Launcher.ResourceTools
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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>();
|
const string registryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||||
string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
|
||||||
|
|
||||||
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey))
|
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryPath))
|
||||||
{
|
{
|
||||||
if (key != null)
|
if (key == null) return;
|
||||||
{
|
|
||||||
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);
|
foreach (string subKeyName in key.GetSubKeyNames())
|
||||||
executables.AddRange(executablePaths);
|
{
|
||||||
|
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)
|
private void DiscoverFromPathVariable() {
|
||||||
{
|
var paths = (Environment.GetEnvironmentVariable("PATH") ?? string.Empty)
|
||||||
List<string> executables = new List<string>();
|
.Split(';')
|
||||||
|
.Select(p => p.Trim())
|
||||||
|
.Where(p => !string.IsNullOrEmpty(p));
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(installLocation) && Directory.Exists(installLocation))
|
foreach (string path in paths)
|
||||||
{
|
{
|
||||||
try
|
SearchAllExecutables(path, SearchOption.TopDirectoryOnly);
|
||||||
{
|
|
||||||
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)
|
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 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[]
|
// Default directory
|
||||||
{
|
Path.Combine(Program.GetAppDataPath(), "bin"),
|
||||||
Path.Combine(windir, "Microsoft.NET", "Framework"),
|
|
||||||
Path.Combine(windir, "Microsoft.NET", "Framework64")
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (string path in paths)
|
// Standard program installation directories
|
||||||
{
|
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
||||||
if (Directory.Exists(path))
|
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
|
||||||
{
|
|
||||||
try
|
// .NET Framework directories
|
||||||
{
|
Path.Combine(windir, "Microsoft.NET", "Framework"),
|
||||||
executables.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.AllDirectories));
|
Path.Combine(windir, "Microsoft.NET", "Framework64"),
|
||||||
}
|
|
||||||
catch (Exception ex)
|
// Chocolatey package directory
|
||||||
{
|
Path.Combine(programData, "chocolatey", "lib"),
|
||||||
Debug.WriteLine($"Error enumerating executables in '{path}': {ex}");
|
|
||||||
}
|
// 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 }) {
|
function Editor({ editorRef }) {
|
||||||
const containerRef = React.useRef(null);
|
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(() => {
|
React.useEffect(() => {
|
||||||
if (!containerRef.current)
|
if (!containerRef.current)
|
||||||
return;
|
return;
|
||||||
|
@ -180,6 +205,20 @@
|
||||||
language: 'javascript'
|
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;
|
editorRef.current = instance;
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -234,14 +273,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const serverPrefix = "http://localhost:3000/";
|
|
||||||
const editorRef = React.useRef(null);
|
const editorRef = React.useRef(null);
|
||||||
const promptEditorRef = React.useRef(null);
|
const promptEditorRef = React.useRef(null);
|
||||||
const settingsRef = React.useRef({});
|
const settingsRef = React.useRef({});
|
||||||
const fileNameRef = React.useRef('sayhello.js');
|
const fileNameRef = React.useRef('sayhello.js');
|
||||||
const promptMessagesRef = React.useRef([]);
|
const promptMessagesRef = React.useRef([]);
|
||||||
|
|
||||||
const fetchSettings = () => axios.get(`${serverPrefix}settings`)
|
const fetchSettings = () => axios.get(`/settings`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const parser = new XMLParser();
|
const parser = new XMLParser();
|
||||||
const result = parser.parse(response.data);
|
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) => {
|
const pushPromptMessage = (role, content) => {
|
||||||
promptMessagesRef.current.push({
|
promptMessagesRef.current.push({
|
||||||
role: role,
|
role: role,
|
||||||
|
@ -442,7 +448,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTargetByUrl = async (urlPart) => {
|
const getTargetByUrl = async (urlPart) => {
|
||||||
const response = await fetch(`${serverPrefix}devtools/json`);
|
const response = await fetch(`/devtools/json`);
|
||||||
const targets = await response.json();
|
const targets = await response.json();
|
||||||
|
|
||||||
const target = targets.find(target => target.url.includes(urlPart));
|
const target = targets.find(target => target.url.includes(urlPart));
|
||||||
|
@ -542,7 +548,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get(`${serverPrefix}whois/${hostname}`).then(response => {
|
axios.get(`/whois/${hostname}`).then(response => {
|
||||||
const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
||||||
appendTextToEditor(`/*\nHostname:${hostname}\n\n${responseText}\n*/`);
|
appendTextToEditor(`/*\nHostname:${hostname}\n\n${responseText}\n*/`);
|
||||||
pushPromptMessage("system", responseText);
|
pushPromptMessage("system", responseText);
|
||||||
|
@ -558,7 +564,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get(`${serverPrefix}dns-query/${hostname}`).then(response => {
|
axios.get(`/dns-query/${hostname}`).then(response => {
|
||||||
const responseText = response.data;
|
const responseText = response.data;
|
||||||
appendTextToEditor(`/*\nHostname:${hostname}\n\n${responseText}\n*/`);
|
appendTextToEditor(`/*\nHostname:${hostname}\n\n${responseText}\n*/`);
|
||||||
pushPromptMessage("system", responseText);
|
pushPromptMessage("system", responseText);
|
||||||
|
@ -583,7 +589,7 @@
|
||||||
const apiPrefix = settingsRef.current.CitiApiPrefix;
|
const apiPrefix = settingsRef.current.CitiApiPrefix;
|
||||||
const ip = encodeURIComponent(hostname.trim());
|
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 parser = new XMLParser();
|
||||||
const result = parser.parse(response.data);
|
const result = parser.parse(response.data);
|
||||||
const data = JSON.parse(result.json);
|
const data = JSON.parse(result.json);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user