Files
crosslangdevstudio/CrossLangShell.cs
2025-10-22 17:31:32 -05:00

572 lines
21 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace CrossLangDevStudio;
class CrossLangShell
{
public static string PackageServerFile = Path.Combine(CrossLangConfigDirectory, "package_servers.json");
public static List<string> GetServers()
{
if (File.Exists(PackageServerFile))
{
var res = JsonConvert.DeserializeObject<List<string>>(File.ReadAllText(PackageServerFile));
if (res is not null) return res;
}
return ["https://cpkg.tesseslanguage.com/"];
}
public static string[] ProjectTypes { get; } = ["console", "lib", "webapp", "template", "compile_tool", "tool", "archive"];
public static string[] ProjectProperTypes { get; } = ["Console Application", "Library", "Web Application", "Template", "Compile Tool", "Tool", "Archive"];
private static void EscapeBashElement(StringBuilder builder, string val)
{
builder.Append("\"");
foreach (var item in val)
{
switch (item)
{
case '$':
case '`':
case '\\':
case '\"':
case '!':
builder.Append('\\');
builder.Append(item);
break;
default:
builder.Append(item);
break;
}
}
builder.Append("\"");
}
private static string EscapeBashCommand(string name, string[] args)
{
StringBuilder builder = new StringBuilder();
EscapeBashElement(builder, name);
foreach (var item in args)
{
builder.Append(' ');
EscapeBashElement(builder, item);
}
builder.Append(" ; read -p \"Press Enter to close.\"");
return builder.ToString();
}
private static string EscapeMacBashCommand(string workDir, bool keepOpen, string name, string[] args)
{
StringBuilder builder = new StringBuilder();
builder.Append("cd ");
EscapeBashElement(builder, workDir);
builder.Append(" ; ");
EscapeBashElement(builder, name);
foreach (var item in args)
{
builder.Append(' ');
EscapeBashElement(builder, item);
}
if (keepOpen)
builder.Append(" ; read -p \"Press Enter to close.\"");
return builder.ToString();
}
private static CrossLangSettings? settings = null;
public static CrossLangSettings Settings
{
get
{
if (settings != null) return settings;
var config = CrossLangConfigDirectory;
if (!string.IsNullOrWhiteSpace(config))
{
var devStudioDir = Path.Combine(config, "DevStudio");
Directory.CreateDirectory(devStudioDir);
var settingsFile = Path.Combine(devStudioDir, "Settings.json");
if (File.Exists(settingsFile))
{
settings = JsonConvert.DeserializeObject<CrossLangSettings>(File.ReadAllText(settingsFile));
}
}
return settings is null ? settings = new CrossLangSettings() : settings;
}
}
private static string _crosslang_config_dir = "";
public static string CrossLangConfigDirectory => string.IsNullOrWhiteSpace(_crosslang_config_dir) ? _crosslang_config_dir = GetCrossLangDirectory() : _crosslang_config_dir;
public static List<string> GetRecentProjects()
{
var config = CrossLangConfigDirectory;
if (string.IsNullOrWhiteSpace(config)) return new List<string>();
var devStudioDir = Path.Combine(config, "DevStudio");
Directory.CreateDirectory(devStudioDir);
var configPath = Path.Combine(devStudioDir, "Recents.json");
if (File.Exists(configPath))
{
var list = JsonConvert.DeserializeObject<List<string>>(File.ReadAllText(configPath));
if (list is not null) return list;
}
return new List<string>();
}
public static void OpenFolderInFileManager(string path)
{
using (Process p = new Process())
{
p.StartInfo.FileName = path;
p.StartInfo.UseShellExecute = true;
p.Start();
}
}
public static void EnsureRecent(string projectPath)
{
projectPath = Path.GetFullPath(projectPath).TrimEnd(Path.DirectorySeparatorChar);
var recents = GetRecentProjects();
if (recents.Contains(projectPath))
{
recents.Remove(projectPath);
}
recents.Insert(0, projectPath);
if (recents.Count > 5)
{
recents.RemoveRange(5, recents.Count - 5);
}
var config = CrossLangConfigDirectory;
if (string.IsNullOrWhiteSpace(config)) return;
var devStudioDir = Path.Combine(config, "DevStudio");
Directory.CreateDirectory(devStudioDir);
var configPath = Path.Combine(devStudioDir, "Recents.json");
File.WriteAllText(configPath, JsonConvert.SerializeObject(recents, Formatting.Indented));
}
public static string GetRealPath(string exec)
{
var paths = (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator).ToList();
var execDir = Environment.ProcessPath;
if (execDir is not null)
{
var dir = Path.GetDirectoryName(execDir);
if (dir is not null)
paths.Add(dir);
}
foreach (var item in paths)
{
var path = Path.Combine(item, exec);
if (File.Exists(path)) return path;
foreach (var ext in (Environment.GetEnvironmentVariable("PATHEXT") ?? "").Split(Path.PathSeparator))
{
if (File.Exists(path + ext)) return path + ext;
}
}
return "";
}
public static string CrossLangPath
{
get
{
var path = GetRealPath("crosslang");
return path;
}
}
public static bool HaveCrossLang
{
get
{
return !string.IsNullOrWhiteSpace(CrossLangPath);
}
}
public static void PushProjectInFolder(string folder)
{
OpenTerminal(false, folder, CrossLangPath, "upload-package");
}
public static void BuildProjectInFolder(string folder)
{
OpenTerminal(false, folder, CrossLangPath, "build");
}
public static void RunProjectInFolder(string folder)
{
OpenTerminal(true, folder, CrossLangPath, "run");
}
public static void OpenTerminalInFolder(string folder)
{
if (OperatingSystem.IsLinux())
{
OpenTerminal(false, folder, "", []);
}
}
public static void OpenTerminal(bool keepOpen, string workingDirectory, string commandName, params string[] args)
{
var preferedTerminalCommand = Settings.PreferedTerminalCommand;
if (!string.IsNullOrWhiteSpace(preferedTerminalCommand))
{
var firstArgs = preferedTerminalCommand.Split(" ");
using Process process = new();
process.StartInfo.UseShellExecute = false;
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = firstArgs[0];
for (int i = 1; i < firstArgs.Length; i++)
{
process.StartInfo.ArgumentList.Add(firstArgs[i]);
}
foreach (var arg in args)
process.StartInfo.ArgumentList.Add(arg);
process.Start();
return;
}
if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
{
string konsole = GetRealPath("konsole");
string gnome_terminal = GetRealPath("gnome-terminal");
string mate_terminal = GetRealPath("mate-terminal");
string lxterminal = GetRealPath("lxterminal");
string deepin_terminal = GetRealPath("deepin-terminal");
string xfce_terminal = GetRealPath("xfce4-terminal");
string xterm = GetRealPath("xterm");
if (File.Exists(konsole))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = konsole;
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
if (keepOpen)
process.StartInfo.ArgumentList.Add("--hold");
process.StartInfo.ArgumentList.Add("-e");
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
process.Start();
}
else if (File.Exists(xfce_terminal))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = xfce_terminal;
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
if (keepOpen)
process.StartInfo.ArgumentList.Add("--hold");
process.StartInfo.ArgumentList.Add("-e");
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
process.Start();
}
else if (File.Exists(gnome_terminal))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = gnome_terminal;
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
process.StartInfo.ArgumentList.Add("--");
if (keepOpen)
{
process.StartInfo.ArgumentList.Add("bash");
process.StartInfo.ArgumentList.Add("-c");
process.StartInfo.ArgumentList.Add(EscapeBashCommand(commandName, args));
}
else
{
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
}
process.Start();
}
else if (File.Exists(mate_terminal))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = mate_terminal;
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
process.StartInfo.ArgumentList.Add("--");
if (keepOpen)
{
process.StartInfo.ArgumentList.Add("bash");
process.StartInfo.ArgumentList.Add("-c");
process.StartInfo.ArgumentList.Add(EscapeBashCommand(commandName, args));
}
else
{
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
}
process.Start();
}
else if (File.Exists(lxterminal))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = lxterminal;
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
process.StartInfo.ArgumentList.Add("-e");
if (keepOpen)
{
process.StartInfo.ArgumentList.Add("bash");
process.StartInfo.ArgumentList.Add("-c");
process.StartInfo.ArgumentList.Add(EscapeBashCommand(commandName, args));
}
else
{
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
}
process.Start();
}
else if (File.Exists(deepin_terminal))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = deepin_terminal;
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
if (keepOpen)
process.StartInfo.ArgumentList.Add("--keep-open");
process.StartInfo.ArgumentList.Add("-e");
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
process.Start();
}
else if (File.Exists(xterm))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = xterm;
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
if (keepOpen)
process.StartInfo.ArgumentList.Add("-hold");
process.StartInfo.ArgumentList.Add("-e");
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
process.Start();
}
}
else if (OperatingSystem.IsWindows())
{
using Process process = new Process();
process.StartInfo.FileName = GetRealPath("cmd.exe");
if (commandName.Length > 0)
process.StartInfo.ArgumentList.Add(keepOpen ? "/K" : "/C");
process.StartInfo.CreateNoWindow = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WorkingDirectory = workingDirectory;
if (commandName.Length > 0)
process.StartInfo.ArgumentList.Add(commandName);
if (commandName.Length > 0)
foreach (var item in args)
process.StartInfo.ArgumentList.Add(item);
process.Start();
}
else if (OperatingSystem.IsMacOS())
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = GetRealPath("open");
process.StartInfo.UseShellExecute = false;
if (commandName.Length != 0)
{
process.StartInfo.ArgumentList.Add("-b");
process.StartInfo.ArgumentList.Add("com.apple.terminal");
if (keepOpen)
{
process.StartInfo.ArgumentList.Add("bash");
process.StartInfo.ArgumentList.Add("-c");
process.StartInfo.ArgumentList.Add(EscapeMacBashCommand(workingDirectory, keepOpen, commandName, args));
}
else
{
process.StartInfo.ArgumentList.Add(commandName);
foreach (var arg in args)
{
process.StartInfo.ArgumentList.Add(arg);
}
}
}
process.Start();
}
}
public static async Task RunCommandAsync(string workingDirectory, string commandName, params string[] args)
{
using Process process = new();
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.FileName = GetRealPath(commandName);
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
foreach (var arg in args)
process.StartInfo.ArgumentList.Add(arg);
if (process.Start()) await process.WaitForExitAsync();
}
private static string GetCrossLangDirectory()
{
string path = CrossLangPath;
if (string.IsNullOrWhiteSpace(path)) return "";
using Process process0 = new Process();
process0.StartInfo.ArgumentList.Add("configdir");
process0.StartInfo.FileName = path;
process0.StartInfo.CreateNoWindow = true;
process0.StartInfo.RedirectStandardOutput = true;
process0.StartInfo.UseShellExecute = false;
process0.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process0.StartInfo.RedirectStandardInput = true;
if (process0.Start())
{
process0.StandardInput.WriteLine("y");
process0.WaitForExit();
var text = process0.StandardOutput.ReadToEnd();
return (text ?? "").Replace("\n", "").Replace("\r", "");
}
using Process process = new Process();
process.StartInfo.ArgumentList.Add("configdir");
process.StartInfo.FileName = path;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (process.Start())
{
process.WaitForExit();
var text = process.StandardOutput.ReadToEnd();
return (text ?? "").Replace("\n", "").Replace("\r", "");
}
return "";
}
public async Task<string> GetReferenceAsync()
{
await RunCommandAsync(Environment.CurrentDirectory, "crosslang", "docs", "--reference");
string path = CrossLangPath;
if (string.IsNullOrWhiteSpace(path)) return "";
using Process process = new Process();
process.StartInfo.ArgumentList.Add("docs");
process.StartInfo.ArgumentList.Add("--reference-path");
process.StartInfo.FileName = path;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (process.Start())
{
process.WaitForExit();
var text = process.StandardOutput.ReadToEnd();
return (text ?? "").Replace("\n", "").Replace("\r", "");
}
return "";
}
internal static bool CreateProject(string templateName, string path)
{
string cpath = CrossLangPath;
if (string.IsNullOrWhiteSpace(cpath)) return false;
using Process process = new Process();
process.StartInfo.ArgumentList.Add("new");
process.StartInfo.ArgumentList.Add(templateName);
process.StartInfo.WorkingDirectory = path;
process.StartInfo.FileName = cpath;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (process.Start())
{
process.WaitForExit();
return process.ExitCode == 0;
}
return false;
}
public static async Task PublishAsync(string project, string identifier, string prefix, string outputDir)
{
List<string> args = new List<string>();
args.Add("publish");
args.Add($"--runtime-package-prefix={prefix}");
if (!string.IsNullOrWhiteSpace(outputDir))
args.Add($"--out={outputDir}");
args.Add(identifier);
await RunCommandAsync(project,"crosslang",args.ToArray());
}
}
public class CrossLangSettings
{
[JsonProperty("prefered_terminal_command")]
public string PreferedTerminalCommand { get; set; } = "";
[JsonProperty("prefered_project_directory")]
public string PreferedProjectDirectory { get; set; } = "";
[Newtonsoft.Json.JsonIgnore]
public string ProjectDirectory
{
get
{
if (!string.IsNullOrWhiteSpace(PreferedProjectDirectory))
return PreferedProjectDirectory;
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "CrossLangProjects");
Directory.CreateDirectory(path);
return path;
}
}
}