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 GetServers() { if (File.Exists(PackageServerFile)) { var res = JsonConvert.DeserializeObject>(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(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 GetRecentProjects() { var config = CrossLangConfigDirectory; if (string.IsNullOrWhiteSpace(config)) return new List(); var devStudioDir = Path.Combine(config, "DevStudio"); Directory.CreateDirectory(devStudioDir); var configPath = Path.Combine(devStudioDir, "Recents.json"); if (File.Exists(configPath)) { var list = JsonConvert.DeserializeObject>(File.ReadAllText(configPath)); if (list is not null) return list; } return new List(); } 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 alacritty = GetRealPath("alacritty"); 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(alacritty)) { using Process process = new Process(); process.StartInfo.WorkingDirectory = workingDirectory; process.StartInfo.FileName = alacritty; 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(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 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 args = new List(); 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; } } }