diff --git a/.gitignore b/.gitignore
index de79242..9f78f99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ bin
obj
out
build
-Installing
\ No newline at end of file
+Installing
+artifacts
\ No newline at end of file
diff --git a/.onedev-buildspec.yml b/.onedev-buildspec.yml
index 5f2f5ef..28718b4 100644
--- a/.onedev-buildspec.yml
+++ b/.onedev-buildspec.yml
@@ -17,13 +17,32 @@ jobs:
commands: |
apt update -y
apt install -y nsis cmake g++-mingw-w64-i686-win32 make
+ mkdir artifacts
cd Packaging/Windows
bash build.sh
+ cd ../Linux
+ bash build.sh
+ bash publish.sh
+ envVars:
+ - name: GITEA_AUTH
+ value: '@secret:GITEA_AUTH@'
useTTY: true
condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL
- - !PublishArtifactStep
- name: Publish Windows Artifact
- artifacts: Packaging/Windows/build/package/CrossLangDevStudio-Installer.exe
+ - !SCPCommandStep
+ name: Copy Files
+ privateKeySecret: TRUENAS_SSH
+ source: artifacts
+ target: mike@@10.137.42.30:/mnt/storage24tb/Files/Public/CrossLangDevStudio/@build_number@
+ options: -r
+ condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL
+ - !SSHCommandStep
+ name: Link latest
+ remoteMachine: 10.137.42.30
+ userName: mike
+ privateKeySecret: TRUENAS_SSH
+ commands: |
+ cd /mnt/storage24tb/Files/Public/CrossLangDevStudio
+ ln -s @build_number@ latest
condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL
triggers:
- !BranchUpdateTrigger
diff --git a/App.axaml b/App.axaml
index b2ca1cf..8c59a5d 100644
--- a/App.axaml
+++ b/App.axaml
@@ -16,6 +16,7 @@
+
diff --git a/CrossLangDevStudio.csproj b/CrossLangDevStudio.csproj
index dcf008b..244bc4d 100644
--- a/CrossLangDevStudio.csproj
+++ b/CrossLangDevStudio.csproj
@@ -16,6 +16,7 @@
+
@@ -25,8 +26,11 @@
None
All
+
+
+
diff --git a/CrossLangFile.cs b/CrossLangFile.cs
index 18463ee..3a4d336 100644
--- a/CrossLangFile.cs
+++ b/CrossLangFile.cs
@@ -16,6 +16,24 @@ using Newtonsoft.Json.Linq;
namespace CrossLangDevStudio;
+public static class CrossLangFileExtensions
+{
+ public static void Skip(this Stream strm, long bytes)
+ {
+ byte[] data = new byte[1024];
+ if (strm.CanSeek)
+ strm.Position += bytes;
+ else
+ {
+ do
+ {
+ int read = strm.Read(data, 0, (int)Math.Min(bytes, data.LongLength));
+ if (read == 0) return;
+ bytes -= read;
+ } while (bytes != 0);
+ }
+ }
+}
public class CrossLangFile
{
@@ -68,7 +86,7 @@ public class CrossLangFile
return Strings[(int)index];
}
-
+
uint count = ReadInt();
for (uint i = 0; i < count; i++)
@@ -77,6 +95,42 @@ public class CrossLangFile
uint sectionSize = ReadInt();
switch (section)
{
+ case "FUNS":
+ {
+ uint chks = ReadInt();
+ for (uint j = 0; j < chks; j++)
+ {
+ uint fnParts = ReadInt();
+
+ string[] parts = new string[fnParts];
+ for (uint k = 0; k < fnParts; k++)
+ {
+ parts[k] = GetString();
+ }
+ uint fid = ReadInt();
+ Functions.Add(new CrossLangFunction(parts, fid));
+ }
+ }
+ break;
+ case "CHKS":
+ {
+ uint chks = ReadInt();
+ for (uint j = 0; j < chks; j++)
+ {
+ uint fnParts = ReadInt();
+
+ string[] parts = new string[fnParts];
+ for (uint k = 0; k < fnParts; k++)
+ {
+ parts[k] = GetString();
+ }
+
+ uint len = ReadInt();
+ strm.Skip(len);
+ Chunks.Add(parts);
+ }
+ }
+ break;
case "STRS":
{
uint theCount = ReadInt();
@@ -109,8 +163,69 @@ public class CrossLangFile
Dependencies.Add(new CrossLangDependency() { Name = name, Version = version });
}
break;
+ case "CLSS":
+ {
+ uint chks = ReadInt();
+ for (uint j = 0; j < chks; j++)
+ {
+ string docs = GetString();
+ uint namePartLen = ReadInt();
+ string[] nameParts = new string[namePartLen];
+ for (uint k = 0; k < namePartLen; k++)
+ {
+ nameParts[k] = GetString();
+ }
+ string name = string.Join(".", nameParts);
+ uint inhPartLen = ReadInt();
+ string[] inhParts = new string[inhPartLen];
+ for (uint k = 0; k < inhPartLen; k++)
+ {
+ inhParts[k] = GetString();
+ }
+ string inh = string.Join(".", inhParts);
+ uint entCount = ReadInt();
+ CrossLangClassEntry[] entries = new CrossLangClassEntry[entCount];
+ for (uint k = 0; k < entCount; k++)
+ {
+ /*
+ Ensure(stream,main_header,1);
+ uint8_t sig = main_header[0];
+ ent.isAbstract = (sig & 0b00001000) != 0;
+ ent.isFunction = (sig & 0b00000100) == 0;
+ ent.modifier = (TClassModifier)(sig & 3);
+ ent.documentation = GetString(stream);
+ ent.name = GetString(stream);
+ uint32_t arglen = EnsureInt(stream);
+ for(uint32_t l = 0; l < arglen; l++)
+ ent.args.push_back(GetString(stream));
+ ent.chunkId = EnsureInt(stream);
+ cls.entry.push_back(ent);
+ */
+ int sig = strm.ReadByte();
+
+ if (sig == -1) throw new EndOfStreamException("Could not read flags byte");
+
+ bool isAbstract = ((byte)sig & 0b00001000) != 0;
+ bool isFunction = ((byte)sig & 0b00000100) == 0;
+ CrossLangClassEntryModifier modifier = (CrossLangClassEntryModifier)(sig & 3);
+ string ent_doc = GetString();
+ string ent_name = GetString();
+ uint arglen = ReadInt();
+ string[] ent_args = new string[arglen];
+ for (uint l = 0; l < arglen; l++)
+ {
+ ent_args[l] = GetString();
+ }
+ uint chunkId = ReadInt();
+
+ entries[k] = new CrossLangClassEntry(isAbstract, isFunction, modifier, ent_doc, ent_name, ent_args, chunkId);
+ }
+ Classes.Add(new CrossLangClass(docs, nameParts, name, inhParts, inh, entries));
+ }
+ }
+ break;
default:
- strm.Position += sectionSize;
+ strm.Skip(sectionSize);
break;
}
}
@@ -125,7 +240,7 @@ public class CrossLangFile
public CrossLangInfo InfoObject => JsonConvert.DeserializeObject(Info) ?? new CrossLangInfo();
- public Bitmap GetIcon(int height=128)
+ public Bitmap GetIcon(int height = 128)
{
using (Stream strm = (Icon > -1 && Icon < Resources.Count) ? new MemoryStream(Resources[Icon], false) : AssetLoader.Open(new Uri("avares://CrossLangDevStudio/Assets/crosslang.png")))
{
@@ -133,25 +248,85 @@ public class CrossLangFile
}
}
- internal string GetTemplatePrettyName()
+ internal string GetPrettyName()
{
var info = InfoObject;
if (info is not null)
{
- if (!string.IsNullOrWhiteSpace(info.TemplateNamePretty)) return info.TemplateNamePretty;
- if (!string.IsNullOrWhiteSpace(info.TemplateName)) return info.TemplateName;
+ if (!string.IsNullOrWhiteSpace(info.ShortNamePretty)) return info.ShortNamePretty;
+ if (!string.IsNullOrWhiteSpace(info.ShortName)) return info.ShortName;
}
return this.Name;
}
public List Strings { get; } = new List();
- public List Resources { get; } = new List();
+ public List Resources { get; } = new List();
public List Dependencies { get; } = new List();
+ public List Functions { get; } = new List();
+ public List Classes { get; } = new List();
+ public List Chunks { get; } = new List();
+
+}
+public class CrossLangClass(string doc, string[] nameParts, string name, string[] inhParts, string inh, CrossLangClassEntry[] ents)
+{
+ public string Documentation => doc;
+
+ public string[] NameParts => nameParts;
+ public string Name => name;
+
+ public string[] InheritParts => inhParts;
+
+ public string Inherit => inh;
+
+ public CrossLangClassEntry[] Entries => ents;
+}
+public enum CrossLangClassEntryModifier
+{
+ Private,
+ Protected,
+ Public,
+ Static
+}
+public class CrossLangClassEntry(bool isAbstract, bool isFunction, CrossLangClassEntryModifier modifier, string documentation, string name, string[] args, uint chunkId)
+{
+ public bool IsAbstract => isAbstract;
+
+ public bool IsFunction => isFunction;
+
+ public CrossLangClassEntryModifier Modifier => modifier;
+
+ public string Documentation => documentation;
+ public string Name => name;
+
+ public string[] Arguments => args;
+
+ public uint ChunkId => chunkId;
+
+
+ /*
+ Ensure(stream,main_header,1);
+ uint8_t sig = main_header[0];
+ ent.isAbstract = (sig & 0b00001000) != 0;
+ ent.isFunction = (sig & 0b00000100) == 0;
+ ent.modifier = (TClassModifier)(sig & 3);
+ ent.documentation = GetString(stream);
+ ent.name = GetString(stream);
+ uint32_t arglen = EnsureInt(stream);
+ for(uint32_t l = 0; l < arglen; l++)
+ ent.args.push_back(GetString(stream));
+ ent.chunkId = EnsureInt(stream);
+ cls.entry.push_back(ent);
+ */
}
+public class CrossLangFunction(string[] nameParts, uint chunkId)
+{
+ public string[] NameParts => nameParts;
+ public uint ChunkId => chunkId;
+}
public class CrossLangConfig
{
@@ -190,10 +365,13 @@ public class CrossLangInfo
[JsonProperty("description")]
public string? Description { get; set; } //optional but tells people about the package
- [JsonProperty("template_name")]
- public string? TemplateName { get; set; }
- [JsonProperty("template_name_pretty")]
- public string? TemplateNamePretty { get; set; }
+ [JsonProperty("short_name")]
+ public string? ShortName { get; set; }
+
+ public string? ToolName { get; set; }
+
+ [JsonProperty("short_name_pretty")]
+ public string? ShortNamePretty { get; set; }
[JsonProperty("template_project_dependencies")]
public JToken? TemplateDependencies { get; set; }
@@ -201,9 +379,9 @@ public class CrossLangInfo
[JsonProperty("template_info")]
public JToken? TemplateInfo { get; set; }
[JsonProperty("template_extra_text_ftles")]
- public JToken? TemplateExtraTextFiles { get; set; }
+ public JToken? TemplateExtraTextFiles { get; set; }
[JsonProperty("template_ignored_files")]
- public JToken? TemplateIgnoredFiles { get; set; }
+ public JToken? TemplateIgnoredFiles { get; set; }
}
public class CrossLangDependency
@@ -229,9 +407,9 @@ public class CrossLangVersionConverter : Newtonsoft.Json.JsonConverter, IComparable
{
public void CopyTo(byte[] data, int offset = 0)
@@ -350,10 +528,10 @@ public struct CrossLangVersion : IComparable, IComparable
{
if (offset + 5 > data.Length) return;
- Major = data[offset+0];
- Minor = data[offset+1];
- Patch = data[offset+2];
- ushort build = (ushort)(data[offset+3] << 8 | data[offset+4]);
+ Major = data[offset + 0];
+ Minor = data[offset + 1];
+ Patch = data[offset + 2];
+ ushort build = (ushort)(data[offset + 3] << 8 | data[offset + 4]);
Build = (ushort)(build >> 2);
Stage = (CrossLangVersionStage)(build & 3);
}
@@ -363,7 +541,7 @@ public struct CrossLangVersion : IComparable, IComparable
public ushort Build { get; set; } = 0;
- public static CrossLangVersion OnePointZeroProd => new CrossLangVersion(){Major=1,Minor=0,Patch=0,Build=0,Stage= CrossLangVersionStage.Production};
+ public static CrossLangVersion OnePointZeroProd => new CrossLangVersion() { Major = 1, Minor = 0, Patch = 0, Build = 0, Stage = CrossLangVersionStage.Production };
public CrossLangVersionStage Stage { get; set; } = CrossLangVersionStage.Development;
@@ -386,7 +564,7 @@ public struct CrossLangVersion : IComparable, IComparable
public override string ToString()
{
- string stage="dev";
+ string stage = "dev";
switch (Stage)
{
case CrossLangVersionStage.Development:
diff --git a/CrossLangShell.cs b/CrossLangShell.cs
index 1ff5aaa..fd57608 100644
--- a/CrossLangShell.cs
+++ b/CrossLangShell.cs
@@ -27,7 +27,7 @@ class CrossLangShell
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"];
+ 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("\"");
@@ -53,27 +53,27 @@ class CrossLangShell
private static string EscapeBashCommand(string name, string[] args)
{
StringBuilder builder = new StringBuilder();
- EscapeBashElement(builder,name);
+ EscapeBashElement(builder, name);
foreach (var item in args)
{
builder.Append(' ');
- EscapeBashElement(builder,item);
+ 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)
+ 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);
+ EscapeBashElement(builder, name);
foreach (var item in args)
{
builder.Append(' ');
- EscapeBashElement(builder,item);
+ EscapeBashElement(builder, item);
}
if (keepOpen)
builder.Append(" ; read -p \"Press Enter to close.\"");
@@ -96,7 +96,7 @@ class CrossLangShell
settings = JsonConvert.DeserializeObject(File.ReadAllText(settingsFile));
}
}
-
+
return settings is null ? settings = new CrossLangSettings() : settings;
}
}
@@ -158,18 +158,18 @@ class CrossLangShell
if (execDir is not null)
{
var dir = Path.GetDirectoryName(execDir);
- if(dir is not null)
+ 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))
{
- 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;
- }
+ if (File.Exists(path + ext)) return path + ext;
}
+ }
return "";
}
@@ -188,6 +188,10 @@ class CrossLangShell
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");
@@ -252,7 +256,7 @@ class CrossLangShell
}
process.Start();
}
- if (File.Exists(xfce_terminal))
+ else if (File.Exists(xfce_terminal))
{
using Process process = new Process();
process.StartInfo.WorkingDirectory = workingDirectory;
@@ -435,11 +439,40 @@ class CrossLangShell
}
}
+ 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;
@@ -457,6 +490,29 @@ class CrossLangShell
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;
@@ -478,6 +534,18 @@ class CrossLangShell
}
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
diff --git a/Messages/PublishCloseMessage.cs b/Messages/PublishCloseMessage.cs
new file mode 100644
index 0000000..de24984
--- /dev/null
+++ b/Messages/PublishCloseMessage.cs
@@ -0,0 +1,4 @@
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.Messaging.Messages;
+namespace CrossLangDevStudio.Messages;
+public class PublishCloseMessage : AsyncRequestMessage;
\ No newline at end of file
diff --git a/Messages/PublishMessage.cs b/Messages/PublishMessage.cs
new file mode 100644
index 0000000..228c735
--- /dev/null
+++ b/Messages/PublishMessage.cs
@@ -0,0 +1,7 @@
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.Messaging.Messages;
+namespace CrossLangDevStudio.Messages;
+public class PublishMessage(string path) : AsyncRequestMessage
+{
+ public string Path => path;
+}
\ No newline at end of file
diff --git a/Models/ConsolePackageManger.cs b/Models/ConsolePackageManger.cs
new file mode 100644
index 0000000..bb4b525
--- /dev/null
+++ b/Models/ConsolePackageManger.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+
+namespace CrossLangDevStudio.Models;
+
+public class ConsolePackageManager : IPackageManager
+{
+ public string Filter => "console";
+
+ private IEnumerable GetFiles()
+ {
+ var path = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "ConsoleApps");
+ if (Directory.Exists(path))
+ foreach (var dir in Directory.EnumerateDirectories(path))
+ {
+ var file = Path.Combine(dir, $"{Path.GetFileName(dir)}.crvm");
+ if (File.Exists(file)) yield return file;
+ }
+ }
+
+ public IEnumerable GetInstalledPackages()
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ yield return new CrossLangDependency { Name = file.Name, Version = file.Version };
+ }
+
+ }
+
+ public async Task InstallPackageAsync(string name, CrossLangVersion version)
+ {
+ await CrossLangShell.RunCommandAsync(Environment.CurrentDirectory, "crosslang", ["install-console", name, $"--version={version.ToString()}"]);
+
+ }
+
+ public void UninstallPackage(string name)
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+ if (name == file.Name)
+ {
+ var dir = Path.GetDirectoryName(item);
+ if(dir is not null)
+ Directory.Delete(dir,true);
+ return;
+ }
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ }
+ }
+}
diff --git a/Models/TemplatePackageManager.cs b/Models/TemplatePackageManager.cs
new file mode 100644
index 0000000..c483219
--- /dev/null
+++ b/Models/TemplatePackageManager.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+
+namespace CrossLangDevStudio.Models;
+
+public class TemplatePackageManager : IPackageManager
+{
+ public string Filter => "template";
+
+ private IEnumerable GetFiles()
+ {
+ var path = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "Templates");
+ if (Directory.Exists(path))
+ foreach (var dir in Directory.EnumerateDirectories(path))
+ {
+ var file = Path.Combine(dir, $"{Path.GetFileName(dir)}.crvm");
+ if (File.Exists(file)) yield return file;
+ }
+ }
+
+ public IEnumerable GetInstalledPackages()
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ yield return new CrossLangDependency { Name = file.Name, Version = file.Version };
+ }
+
+ }
+
+ public async Task InstallPackageAsync(string name, CrossLangVersion version)
+ {
+ await CrossLangShell.RunCommandAsync(Environment.CurrentDirectory, "crosslang", ["install-template", name, $"--version={version.ToString()}"]);
+
+ }
+
+ public void UninstallPackage(string name)
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+ if (name == file.Name)
+ {
+ File.Delete(item);
+ return;
+ }
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ }
+ }
+}
diff --git a/Models/ToolPackageManager.cs b/Models/ToolPackageManager.cs
new file mode 100644
index 0000000..58a1d5f
--- /dev/null
+++ b/Models/ToolPackageManager.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+
+namespace CrossLangDevStudio.Models;
+
+public class ToolPackageManager : IPackageManager
+{
+ public string Filter => "tool";
+
+ private IEnumerable GetFiles()
+ {
+ var path = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "Tools");
+ if (Directory.Exists(path))
+ foreach (var dir in Directory.EnumerateDirectories(path))
+ {
+ var file = Path.Combine(dir, $"{Path.GetFileName(dir)}.crvm");
+ if (File.Exists(file)) yield return file;
+ }
+ }
+
+ public IEnumerable GetInstalledPackages()
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ yield return new CrossLangDependency { Name = file.Name, Version = file.Version };
+ }
+
+ }
+
+ public async Task InstallPackageAsync(string name, CrossLangVersion version)
+ {
+ await CrossLangShell.RunCommandAsync(Environment.CurrentDirectory, "crosslang", ["install-tool", name, $"--version={version.ToString()}"]);
+
+ }
+
+ public void UninstallPackage(string name)
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+ if (name == file.Name)
+ {
+ var dir = Path.GetDirectoryName(item);
+ if(dir is not null)
+ Directory.Delete(dir,true);
+ return;
+ }
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ }
+ }
+}
diff --git a/Models/WebAppPackageManager.cs b/Models/WebAppPackageManager.cs
new file mode 100644
index 0000000..6505037
--- /dev/null
+++ b/Models/WebAppPackageManager.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+
+namespace CrossLangDevStudio.Models;
+
+public class WebAppPackageManager : IPackageManager
+{
+ public string Filter => "webapp";
+
+ private IEnumerable GetFiles()
+ {
+ var path = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "WebApps");
+ if (Directory.Exists(path))
+ foreach (var dir in Directory.EnumerateDirectories(path))
+ {
+ var file = Path.Combine(dir, $"{Path.GetFileName(dir)}.crvm");
+ if (File.Exists(file)) yield return file;
+ }
+ }
+
+ public IEnumerable GetInstalledPackages()
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ yield return new CrossLangDependency { Name = file.Name, Version = file.Version };
+ }
+
+ }
+
+ public async Task InstallPackageAsync(string name, CrossLangVersion version)
+ {
+ await CrossLangShell.RunCommandAsync(Environment.CurrentDirectory, "crosslang", ["install-webapp", name, $"--version={version.ToString()}"]);
+
+ }
+
+ public void UninstallPackage(string name)
+ {
+ foreach (var item in GetFiles())
+ {
+ var file = new CrossLangFile();
+ try
+ {
+ file.Load(item);
+ if (name == file.Name)
+ {
+ var dir = Path.GetDirectoryName(item);
+ if(dir is not null)
+ Directory.Delete(dir,true);
+ return;
+ }
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+ }
+ }
+}
diff --git a/Packaging/Linux/build.sh b/Packaging/Linux/build.sh
index bd63f93..3e1922b 100644
--- a/Packaging/Linux/build.sh
+++ b/Packaging/Linux/build.sh
@@ -11,5 +11,23 @@ cp ../../debian/control-amd64 DEBIAN/control
cp ../../crosslang-devstudio.desktop usr/share/applications/
cp ../../crossdev usr/bin/crossdev
chmod 755 usr/bin/crossdev
-cd ..
+cd opt
+tar cvzf ../../../../../artifacts/crosslang-devstudio-x86_64.tar.gz .
+cd ../..
dpkg-deb --build crosslang-devstudio_1.0.0_amd64/
+
+
+mkdir crosslang-devstudio_1.0.0_arm64
+cd crosslang-devstudio_1.0.0_arm64
+dotnet publish -c Release -r linux-arm64 -o opt/CrossLangDevStudio -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained ../../../../CrossLangDevStudio.csproj
+mkdir DEBIAN
+mkdir -p usr/share/applications
+mkdir -p usr/bin
+cp ../../debian/control-arm64 DEBIAN/control
+cp ../../crosslang-devstudio.desktop usr/share/applications/
+cp ../../crossdev usr/bin/crossdev
+chmod 755 usr/bin/crossdev
+cd opt
+tar cvzf ../../../../../artifacts/crosslang-devstudio-aarch64.tar.gz .
+cd ../..
+dpkg-deb --build crosslang-devstudio_1.0.0_arm64/
\ No newline at end of file
diff --git a/Packaging/Linux/debian/control-arm64 b/Packaging/Linux/debian/control-arm64
new file mode 100644
index 0000000..d1fe100
--- /dev/null
+++ b/Packaging/Linux/debian/control-arm64
@@ -0,0 +1,8 @@
+Package: crosslang-devstudio
+Version: 1.0.0
+Architecture: arm64
+Essential: no
+Priority: optional
+Depends: crosslang
+Maintainer: Mike Nolan
+Description: IDE for CrossLang
diff --git a/Packaging/Linux/push.sh b/Packaging/Linux/push.sh
index 2cbe2f8..f7c31e4 100644
--- a/Packaging/Linux/push.sh
+++ b/Packaging/Linux/push.sh
@@ -4,3 +4,21 @@ curl --user tesses50:$GITEA_AUTH -X DELETE \
curl --user tesses50:$GITEA_AUTH \
--upload-file build/crosslang-devstudio_1.0.0_amd64.deb \
https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/upload
+
+curl --user tesses50:$GITEA_AUTH -X DELETE \
+ https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/crosslang-devstudio/1.0.0/arm64
+curl --user tesses50:$GITEA_AUTH \
+ --upload-file build/crosslang-devstudio_1.0.0_arm64.deb \
+ https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/upload
+
+curl --user tesses50:$GITEA_AUTH -X DELETE \
+ https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/plucky/main/crosslang-devstudio/1.0.0/amd64
+curl --user tesses50:$GITEA_AUTH \
+ --upload-file build/crosslang-devstudio_1.0.0_amd64.deb \
+ https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/plucky/main/upload
+
+curl --user tesses50:$GITEA_AUTH -X DELETE \
+ https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/plucky/main/crosslang-devstudio/1.0.0/arm64
+curl --user tesses50:$GITEA_AUTH \
+ --upload-file build/crosslang-devstudio_1.0.0_arm64.deb \
+ https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/plucky/main/upload
diff --git a/Packaging/Windows/build.sh b/Packaging/Windows/build.sh
index a972c2a..3eb0c23 100644
--- a/Packaging/Windows/build.sh
+++ b/Packaging/Windows/build.sh
@@ -17,4 +17,6 @@ cd package
dotnet publish -c Release -r win-x86 -o bin -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained ../../../../CrossLangDevStudio.csproj
cp ../../script.nsh .
cp ../../license.txt .
-makensis script.nsh
\ No newline at end of file
+makensis script.nsh
+mv crosslang-devstudio-win32.exe ../../../../artifacts/
+zip -r ../../../../artifacts/crosslang-devstudio-win32.zip bin share
\ No newline at end of file
diff --git a/Packaging/Windows/script.nsh b/Packaging/Windows/script.nsh
index 4c971cf..14deaf5 100644
--- a/Packaging/Windows/script.nsh
+++ b/Packaging/Windows/script.nsh
@@ -29,7 +29,7 @@ LicenseData "license.txt"
# This will be in the installer/uninstaller's title bar
Name "${COMPANYNAME} - ${APPNAME}"
Icon "crosslang.ico"
-outFile "CrossLangDevStudio-Installer.exe"
+outFile "crosslang-devstudio-win32.exe"
!include LogicLib.nsh
diff --git a/ViewModels/AddProjectReferenceViewModel.cs b/ViewModels/AddProjectReferenceViewModel.cs
new file mode 100644
index 0000000..3e7b35d
--- /dev/null
+++ b/ViewModels/AddProjectReferenceViewModel.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia;
+using Avalonia.Media;
+using Avalonia.Platform.Storage;
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using CommunityToolkit.Mvvm.Messaging;
+using CrossLangDevStudio.Messages;
+using CrossLangDevStudio.Views;
+using MsBox.Avalonia;
+using Newtonsoft.Json;
+
+namespace CrossLangDevStudio.ViewModels;
+
+
+public partial class AddProjectReferenceViewModel : ViewModelBase
+{
+ public AddProjectReferenceViewModel(string projectDir)
+ {
+ ProjectDirectory = projectDir;
+ var data = File.ReadAllText(Path.Combine(projectDir, "cross.json"));
+ var jsonData = JsonConvert.DeserializeObject(data) ?? new CrossLangConfig();
+ foreach (var item in jsonData.ProjectDependencies)
+ References.Add(new ProjectReferenceEntry(this, item));
+ }
+ public string ProjectDirectory { get; init; }
+ public ObservableCollection References { get; } = new ObservableCollection();
+
+ public void Remove(ProjectReferenceEntry projectReferenceEntry)
+ {
+ References.Remove(projectReferenceEntry);
+ var data = File.ReadAllText(Path.Combine(ProjectDirectory, "cross.json"));
+ var jsonData = JsonConvert.DeserializeObject(data) ?? new CrossLangConfig();
+ jsonData.ProjectDependencies.Remove(projectReferenceEntry.Path);
+ File.WriteAllText(Path.Combine(ProjectDirectory, "cross.json"), JsonConvert.SerializeObject(jsonData, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }));
+
+ }
+
+ [RelayCommand]
+ private async Task AddProjectAsync()
+ {
+ var window = await WeakReferenceMessenger.Default.Send();
+ var dir = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions() { Title = "Add Project Dependency" });
+ if (dir.Count != 0)
+ {
+ string? path = dir[0].TryGetLocalPath();
+ if (!string.IsNullOrWhiteSpace(path))
+ {
+ var jsonFile = Path.Combine(path, "cross.json");
+ if (!File.Exists(jsonFile))
+ {
+ await MessageBoxManager.GetMessageBoxStandard("Error", $"Could not find cross.json in folder {path}", MsBox.Avalonia.Enums.ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error).ShowAsync();
+ return;
+ }
+ string projectPath = Path.GetRelativePath(this.ProjectDirectory, path);
+ var abs = Path.GetFullPath(projectPath, ProjectDirectory);
+
+ foreach (var item in References)
+ {
+ if (Path.GetFullPath(item.Path, ProjectDirectory) == abs)
+ {
+ await MessageBoxManager.GetMessageBoxStandard("Error", $"Project {path} already exists in your project.", MsBox.Avalonia.Enums.ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error).ShowAsync();
+
+ return;
+ }
+ }
+ var data = File.ReadAllText(Path.Combine(ProjectDirectory, "cross.json"));
+ var jsonData = JsonConvert.DeserializeObject(data) ?? new CrossLangConfig();
+ jsonData.ProjectDependencies.Add(projectPath);
+ File.WriteAllText(Path.Combine(ProjectDirectory, "cross.json"), JsonConvert.SerializeObject(jsonData, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }));
+ References.Add(new ProjectReferenceEntry(this, projectPath));
+ }
+
+ }
+ }
+}
+
+public partial class ProjectReferenceEntry(AddProjectReferenceViewModel parent,string path) : ObservableObject
+{
+ public string Path => path;
+
+ [RelayCommand]
+ private void Remove()
+ {
+ parent.Remove(this);
+ }
+}
\ No newline at end of file
diff --git a/ViewModels/CRVMViewerViewModel.cs b/ViewModels/CRVMViewerViewModel.cs
new file mode 100644
index 0000000..6f917d9
--- /dev/null
+++ b/ViewModels/CRVMViewerViewModel.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia;
+using Avalonia.Media;
+using Avalonia.Platform.Storage;
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using CommunityToolkit.Mvvm.Messaging;
+using CrossLangDevStudio.Messages;
+using CrossLangDevStudio.Views;
+
+namespace CrossLangDevStudio.ViewModels;
+
+public class CRVMViewerViewModel : ViewModelBase
+{
+ public string FilePath { get; set; }
+ public CRVMViewerViewModel(string path)
+ {
+ FilePath = path;
+ var clf = new CrossLangFile();
+ using (var strm = File.OpenRead(path))
+ {
+
+ clf.Load(strm);
+
+ }
+
+
+ foreach (var fn in clf.Functions)
+ {
+ string docs = $"/^{fn.NameParts[0]}^/";
+ string name = string.Join(".", fn.NameParts.Skip(1));
+ string args = string.Join(", ", clf.Chunks[(int)fn.ChunkId]);
+ Functions.Add(new Function(docs, name, args));
+ }
+ foreach (var cls in clf.Classes)
+ {
+ string docs = $"/^{cls.Documentation}^/";
+ ObservableCollection entries = new ObservableCollection();
+
+ foreach (var item in cls.Entries)
+ {
+ string modifiers = $"\t{item.Modifier.ToString().ToLower()} ";
+ if (item.IsFunction && item.IsAbstract)
+ modifiers += "abstract ";
+
+ Class.ClassEntry ce = new Class.ClassEntry($"\t/^{item.Documentation}^/", item.Name, modifiers, item.IsFunction, item.IsFunction ? string.Join(", ", item.Arguments) : "");
+ entries.Add(ce);
+ }
+
+ Classes.Add(new Class(docs, cls.Name, cls.Inherit, entries));
+ }
+ }
+
+ public ObservableCollection Functions { get; set; } = new ObservableCollection();
+
+ public ObservableCollection Classes { get; set; } = new ObservableCollection();
+
+ public class Function(string docs, string name, string args) : ObservableObject
+ {
+ public string Name => name;
+
+ public string Documentation => docs;
+
+ public string Args => args;
+ }
+
+ public class Class(string docs, string name, string inherits, ObservableCollection entries) : ObservableObject
+ {
+ public string Documentation => docs;
+
+ public string Name => name;
+
+ public string Inherits => inherits == "ClassObject" ? "" : inherits;
+
+ public string InheritsColon => inherits == "ClassObject" ? "" : " : ";
+
+ public ObservableCollection Entries => entries;
+
+ public class ClassEntry(string docs,string name, string modifiers, bool isFunction, string args)
+ {
+ public string Documentation => docs;
+
+ public string Name => name;
+ public string Modifiers => modifiers;
+
+ public IBrush NameColor => new SolidColorBrush(isFunction ? 0xFFDCDCAA : 0xFF9CDCFE);
+
+ public IBrush LeftParenColor => new SolidColorBrush(isFunction ? 0xFFC586C0 : 0xFFCCCCCC);
+
+ public string LeftParen => isFunction ? "(" : ";";
+
+ public string RightParen => isFunction ? ")" : "";
+
+
+ public string Args => args;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ViewModels/FileEditorViewModel.cs b/ViewModels/FileEditorViewModel.cs
index 6e3c52a..4628d45 100644
--- a/ViewModels/FileEditorViewModel.cs
+++ b/ViewModels/FileEditorViewModel.cs
@@ -5,13 +5,22 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
-
+using AvaloniaEdit;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CrossLangDevStudio.Messages;
using CrossLangDevStudio.Views;
-
+using AvaloniaEdit.CodeCompletion;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Editing;
+using AvaloniaEdit.Folding;
+using AvaloniaEdit.Rendering;
+using TextMateSharp.Grammars;
+using AvaloniaEdit.TextMate;
+using Avalonia.Input;
+using System.Collections.Generic;
+using Avalonia.Media;
namespace CrossLangDevStudio.ViewModels;
public partial class FileEditorViewModel : ViewModelBase, ISavable
@@ -19,7 +28,7 @@ public partial class FileEditorViewModel : ViewModelBase, ISavable
TabItemViewModel tab;
public string FilePath { get; }
bool _modified = false;
- private bool Modified
+ public bool Modified
{
get => _modified;
set
@@ -36,30 +45,72 @@ public partial class FileEditorViewModel : ViewModelBase, ISavable
}
}
}
-
- private string _text = "";
- public string Text
- {
- get => _text;
- set
- {
- Modified = true;
- this.SetProperty(ref _text, value, nameof(Text));
- }
- }
- public FileEditorViewModel(string path,TabItemViewModel tab)
+ TextEditor textEdit = new TextEditor();
+
+ public TextEditor TextEditor => textEdit;
+
+
+
+ public FileEditorViewModel(string path, TabItemViewModel tab)
{
this.tab = tab;
FilePath = path;
- _text = File.ReadAllText(path);
+ textEdit.ShowLineNumbers = true;
+ textEdit.FontSize = 24;
+ string ext = Path.GetExtension(FilePath);
+ if (ext == ".tcross")
+ {
+ ext = ".go";
+ }
+ {
+
+ var _registryOptions = new RegistryOptions(ThemeName.Dark);
+
+ var _textMateInstallation = textEdit.InstallTextMate(_registryOptions);
+
+ //definition.
+
+
+
+ //Here we are getting the language by the extension and right after that we are initializing grammar with this language.
+ //And that's all 😀, you are ready to use AvaloniaEdit with syntax highlighting!
+ try
+ {
+ //AssetLoader.Open(new Uri("avares://CrossLangDevStudio/Assets/crosslang.png")
+
+ _textMateInstallation.SetGrammar(_registryOptions.GetScopeByLanguageId(_registryOptions.GetLanguageByExtension(ext).Id));
+ }
+ catch (Exception)
+ {
+
+ }
+ }
+
+
+ textEdit.Text = File.ReadAllText(path);
+ textEdit.TextChanged += (sender, e) =>
+ {
+ Modified = true;
+ };
}
+
public void Save()
{
- File.WriteAllText(FilePath, Text);
+
+ File.WriteAllText(FilePath, textEdit.Text);
Modified = false;
+
}
-}
\ No newline at end of file
+
+ public void SaveRecovery(string path)
+ {
+
+ File.WriteAllText(path, textEdit.Text);
+
+
+ }
+}
diff --git a/ViewModels/ISavable.cs b/ViewModels/ISavable.cs
index 0335067..e8f84b3 100644
--- a/ViewModels/ISavable.cs
+++ b/ViewModels/ISavable.cs
@@ -1,5 +1,10 @@
namespace CrossLangDevStudio.ViewModels;
+
interface ISavable
{
+ string FilePath { get; }
+ bool Modified { get; set; }
void Save();
+
+ void SaveRecovery(string path);
}
\ No newline at end of file
diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs
index e1b5e55..fe3412a 100644
--- a/ViewModels/MainWindowViewModel.cs
+++ b/ViewModels/MainWindowViewModel.cs
@@ -2,15 +2,21 @@
using System.Collections.ObjectModel;
using System.IO;
using System.Net.Http;
+using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
+using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CrossLangDevStudio.Messages;
using CrossLangDevStudio.Models;
using CrossLangDevStudio.Views;
+using MsBox.Avalonia;
+using MsBox.Avalonia.Enums;
+using Newtonsoft.Json;
+using Tabalonia.Events;
namespace CrossLangDevStudio.ViewModels;
@@ -26,6 +32,7 @@ public partial class MainWindowViewModel : ViewModelBase
public ObservableCollection ProjectFiles { get; } = new ObservableCollection();
public EventHandler? Closed { get; } = null;
+ public EventHandler? ClosingTab { get; }
public TabItemViewModel WelcomePage
{
@@ -49,16 +56,120 @@ public partial class MainWindowViewModel : ViewModelBase
value?.Click?.Invoke();
}
}
+ private Avalonia.Threading.DispatcherTimer timer=new ();
+ [RelayCommand]
+ private void Welcome()
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is WelcomeViewModel)
+ {
+ SelectedTab = item;
+ return;
+ }
+ }
+ AddTab(WelcomePage);
+ }
+
+ [RelayCommand]
+ private async Task WebsiteAsync()
+ {
+ var win = await WeakReferenceMessenger.Default.Send(new GetWindowMessage());
+ if (win is not null)
+ {
+ await win.Launcher.LaunchUriAsync(new Uri("https://crosslang.tesseslanguage.com/"));
+ }
+ }
+
+ [RelayCommand]
+ private async Task VideoDocumentationAsync()
+ {
+ var win = await WeakReferenceMessenger.Default.Send(new GetWindowMessage());
+ if (win is not null)
+ {
+ await win.Launcher.LaunchUriAsync(new Uri("https://peertube.site.tesses.net/c/crosslang"));
+ }
+ }
+ [RelayCommand]
+ private async Task DocumentationAsync()
+ {
+ var win = await WeakReferenceMessenger.Default.Send(new GetWindowMessage());
+ if (win is not null)
+ {
+ await win.Launcher.LaunchUriAsync(new Uri("https://crosslang.tesseslanguage.com/language-features/index.html"));
+ }
+ }
+
+ private async Task CloseTabAsync(TabClosingEventArgs e, ISavable savable)
+ {
+ var box = MessageBoxManager
+ .GetMessageBoxStandard("Save?", $"Do you want to save {Path.GetFileName(savable.FilePath)}.?",
+ ButtonEnum.YesNoCancel, Icon.Question);
+ var msg = await WeakReferenceMessenger.Default.Send();
+ if (msg is not null)
+ {
+ switch (await box.ShowWindowDialogAsync(msg))
+ {
+ case ButtonResult.Yes:
+ savable.Save();
+ break;
+ case ButtonResult.Cancel:
+ e.Cancel = true;
+ break;
+ }
+ }
+ }
+
+ public bool HasUnsaved
+ {
+ get
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is ISavable s && s.Modified) return true;
+ }
+ return false;
+ }
+ }
+
public MainWindowViewModel(string[] args)
{
+ this.ClosingTab = (sender, e) =>
+ {
+
+ if (e.Item is TabItemViewModel vm)
+ {
+ if (vm.Body is ISavable savable)
+ {
+ if (savable.Modified)
+ {
+ //thanks https://github.com/AvaloniaUI/Avalonia/issues/4810#issuecomment-704372304
+ using (var source = new CancellationTokenSource())
+ {
+ CloseTabAsync(e, savable).ContinueWith(e => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
+ Dispatcher.UIThread.MainLoop(source.Token);
+ }
+
+
+ }
+ }
+ }
+ };
+ timer.Interval = TimeSpan.FromMinutes(1);
+ timer.Tick += (sender, e) =>
+ {
+ SaveRecovery();
+ };
+
if (args.Length == 1)
{
LoadProject(args[0]);
}
if (string.IsNullOrWhiteSpace(CurrentProject))
- this.TabItems.Add(WelcomePage);
+ this.TabItems.Add(WelcomePage);
+
}
@@ -74,7 +185,7 @@ public partial class MainWindowViewModel : ViewModelBase
var newTabItem = new NewTabViewModel(this, tab);
tab.Body = newTabItem;
}
-
+
return tab;
@@ -82,12 +193,19 @@ public partial class MainWindowViewModel : ViewModelBase
internal void LoadProject(string obj)
{
+ timer.Stop(); //we don't want autorecovery timer ticking when we change projects
if (!File.Exists(Path.Combine(obj, "cross.json")))
{
return;
}
CrossLangShell.EnsureRecent(obj);
CurrentProject = obj.TrimEnd(Path.DirectorySeparatorChar);
+ //we don't care that it isn't awaited due to only Refreshing here (you can referesh in GUI)
+ //and the timer starting after async call inside the async function
+ //so I think I know what I am doing
+#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+ InitRecoveryAsync();
+#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Refresh(CurrentProject);
}
[RelayCommand]
@@ -102,7 +220,7 @@ public partial class MainWindowViewModel : ViewModelBase
private void Refresh(string obj)
{
-
+
for (int i = 0; i < TabItems.Count; i++)
@@ -122,11 +240,11 @@ public partial class MainWindowViewModel : ViewModelBase
OpenProjectConfig();
}),
- new ProjectFileNode("Packages", ()=>{
+ new ProjectFileNode("Project Packages", ()=>{
OpenProjectPackages();
}),
- new ProjectFileNode("Project Dependencies",()=>{
-
+ new ProjectFileNode("Project Dependencies (folders)",()=>{
+ OpenProjectDependencies();
})
])
@@ -155,7 +273,227 @@ public partial class MainWindowViewModel : ViewModelBase
ProjectFiles.Clear();
ProjectFiles.Add(new ProjectFileNode(Path.GetFileName(obj), entries));
}
+ private int proj_id = -1;
+ private async Task InitRecoveryAsync()
+ {
+ int i = 0;
+ var dir = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "DevStudio", "Recovery");
+ foreach (var item in Directory.EnumerateDirectories(dir))
+ {
+ if (int.TryParse(Path.GetFileName(item), out int id))
+ {
+ if (id >= i) i = id + 1;
+ var file = $"{item}.json";
+ if (File.Exists(file))
+ {
+ var cfg = JsonConvert.DeserializeObject(File.ReadAllText(file));
+ if (cfg is not null)
+ {
+ if (cfg.ProjectDirectory == CurrentProject)
+ {
+ proj_id = id;
+
+ if (cfg.HasRecovery)
+ {
+ var box = MessageBoxManager
+ .GetMessageBoxStandard("Autorecovery", $"Found a autorecovery from {cfg.Date} do you want to load it?",
+ ButtonEnum.YesNo, Icon.Question);
+ var msg = await WeakReferenceMessenger.Default.Send();
+ if (msg is not null && await box.ShowWindowDialogAsync(msg) == ButtonResult.Yes)
+ {
+ CopyDir(item, CurrentProject);
+
+
+ }
+ cfg.HasRecovery = false;
+ File.WriteAllText(file, JsonConvert.SerializeObject(cfg));
+ }
+
+ //WeakReferenceMessenger.Default.Send
+
+ timer.Start();
+
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ proj_id = i;
+
+
+ timer.Start();
+ }
+
+ private void CopyDir(string src, string dest)
+ {
+ foreach (var dir in Directory.EnumerateDirectories(src))
+ {
+ var destDir = Path.Combine(dest, Path.GetFileName(dir));
+ Directory.CreateDirectory(destDir);
+ CopyDir(dir, destDir);
+ }
+
+ foreach (var file in Directory.EnumerateFiles(src))
+ {
+ var destFile = Path.Combine(dest, Path.GetFileName(file));
+
+ File.Copy(file, destFile, true);
+ }
+ }
+
+ private void SaveRecovery()
+ {
+ if (proj_id == -1 || !Directory.Exists(CurrentProject)) return;
+ var id = proj_id;
+ var dir = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "DevStudio", "Recovery", $"{id}_tmp");
+ var dirdest = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "DevStudio", "Recovery", $"{id}");
+ Directory.CreateDirectory(dir);
+ bool hasAnything = false;
+
+ foreach (var item in TabItems)
+ {
+ if (item.Body is ISavable savable && savable.Modified)
+ {
+ if (savable.FilePath.StartsWith(CurrentProject))
+ {
+ var path = Path.GetRelativePath(CurrentProject, savable.FilePath);
+ string? dirPath = Path.GetDirectoryName(path);
+ if (dirPath is not null)
+ Directory.CreateDirectory(Path.Combine(dir, dirPath));
+
+ savable.SaveRecovery(Path.Combine(dir, path));
+ hasAnything = true;
+ }
+ }
+ }
+
+ if (Directory.Exists(dirdest))
+ {
+ Directory.Delete(dirdest, true);
+ }
+ if (hasAnything)
+ {
+ Directory.Move(dir, dirdest);
+ File.WriteAllText($"{dirdest}.json", JsonConvert.SerializeObject(new RecoveryConfig { Date = DateTime.Now, ProjectDirectory = CurrentProject, HasRecovery=true }));
+ }
+ else
+ {
+ Directory.Delete(dir, true);
+
+ File.WriteAllText($"{dirdest}.json", JsonConvert.SerializeObject(new RecoveryConfig { Date = DateTime.Now, ProjectDirectory = CurrentProject, HasRecovery=false }));
+ }
+ Directory.CreateDirectory(dirdest);
+ }
+ [RelayCommand]
+ private void InstallTool()
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is PackageManagerViewModel vm)
+ {
+ if (vm.Packages is ToolPackageManager)
+ {
+ SelectedTab = item;
+ return;
+ }
+ }
+ }
+ AddTab(new TabItemViewModel()
+ {
+ Header = "Tool Packages",
+ Body = new PackageManagerViewModel(this, new ToolPackageManager())
+ });
+ }
+ [RelayCommand]
+ private void InstallTemplate()
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is PackageManagerViewModel vm)
+ {
+ if (vm.Packages is TemplatePackageManager)
+ {
+ SelectedTab = item;
+ return;
+ }
+ }
+ }
+ AddTab(new TabItemViewModel()
+ {
+ Header = "Template Packages",
+ Body = new PackageManagerViewModel(this, new TemplatePackageManager())
+ });
+ }
+ [RelayCommand]
+ private void InstallConsole()
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is PackageManagerViewModel vm)
+ {
+ if (vm.Packages is ConsolePackageManager)
+ {
+ SelectedTab = item;
+ return;
+ }
+ }
+ }
+ AddTab(new TabItemViewModel()
+ {
+ Header = "Console Packages",
+ Body = new PackageManagerViewModel(this, new ConsolePackageManager())
+ });
+ }
+
+ [RelayCommand]
+ private void InstallWebApp()
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is PackageManagerViewModel vm)
+ {
+ if (vm.Packages is WebAppPackageManager)
+ {
+ SelectedTab = item;
+ return;
+ }
+ }
+ }
+ AddTab(new TabItemViewModel()
+ {
+ Header = "Web Application Packages",
+ Body = new PackageManagerViewModel(this, new WebAppPackageManager())
+ });
+ }
+ private void OpenProjectDependencies()
+ {
+ if (Directory.Exists(CurrentProject))
+ {
+ var config = Path.Combine(CurrentProject, "cross.json");
+ if (File.Exists(config))
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is AddProjectReferenceViewModel vm)
+ {
+ if (vm.ProjectDirectory == CurrentProject)
+ {
+ SelectedTab = item;
+ return;
+ }
+ }
+ }
+ AddTab(new TabItemViewModel()
+ {
+ Header = "Project Dependencies (folders)",
+ Body = new AddProjectReferenceViewModel(CurrentProject)
+ });
+ }
+ }
+ }
private void OpenProjectPackages()
{
if (Directory.Exists(CurrentProject))
@@ -185,7 +523,7 @@ public partial class MainWindowViewModel : ViewModelBase
private void OpenProjectConfig()
{
-
+
if (Directory.Exists(CurrentProject))
{
var config = Path.Combine(CurrentProject, "cross.json");
@@ -200,12 +538,23 @@ public partial class MainWindowViewModel : ViewModelBase
TabItemViewModel vm = new TabItemViewModel();
vm.Header = "Project Configuration";
var pcm = new ProjectConfigurationViewModel(config, vm);
-
+
vm.Body = pcm;
AddTab(vm);
}
}
+ [RelayCommand]
+ private async Task ReferenceAsync()
+ {
+ await CrossLangShell.RunCommandAsync(Environment.CurrentDirectory, "crosslang", "docs", "--reference");
+ var refFile = Path.Combine(CrossLangShell.CrossLangConfigDirectory, "Reference.crvm");
+ Console.WriteLine(refFile);
+ if(File.Exists(refFile))
+ {
+ OpenFile(refFile);
+ }
+ }
static string[] FILE_EXTS = new string[]{
".tcross",
@@ -232,12 +581,17 @@ public partial class MainWindowViewModel : ViewModelBase
public void OpenFile(string path)
{
bool isValid = false;
+ string ext = Path.GetExtension(path).ToLower();
foreach (var item in FILE_EXTS)
{
- if (Path.GetExtension(path).ToLower() == item)
+ if (ext == item)
isValid = true;
}
- if (!isValid) return;
+ if (!isValid)
+ {
+ OpenOtherFile(path, ext);
+ return;
+ }
foreach (var item in TabItems)
{
@@ -255,10 +609,39 @@ public partial class MainWindowViewModel : ViewModelBase
Header = Path.GetFileName(path),
};
- tab.Body = new FileEditorViewModel(path,tab);
+ tab.Body = new FileEditorViewModel(path, tab);
AddTab(tab);
}
+ private void OpenOtherFile(string path, string ext)
+ {
+ if (ext == ".crvm")
+ {
+ foreach (var item in TabItems)
+ {
+ if (item.Body is CRVMViewerViewModel model)
+ {
+ if (model.FilePath == path)
+ {
+ SelectedTab = item;
+ return;
+ }
+ }
+ }
+ try
+ {
+ var tab = new TabItemViewModel()
+ {
+ Header = Path.GetFileName(path),
+ Body = new CRVMViewerViewModel(path)
+ };
+
+ AddTab(tab);
+ }catch(Exception ex) { Console.WriteLine(ex); }
+
+ }
+ }
+
[ObservableProperty]
private TabItemViewModel? _selectedTab = null;
private void AddTab(TabItemViewModel item)
@@ -337,13 +720,57 @@ public partial class MainWindowViewModel : ViewModelBase
SelectedTab?.Save();
}
[RelayCommand]
- private void SaveAll()
+ public void SaveAll()
{
foreach (var tab in TabItems)
{
tab.Save();
}
}
+
+ [RelayCommand]
+ private void AddPackage()
+ {
+ if (Directory.Exists(CurrentProject))
+ {
+ var conf = Path.Combine(CurrentProject, "cross.json");
+ if (File.Exists(conf))
+ {
+ OpenProjectPackages();
+ }
+ }
+ }
+ [RelayCommand]
+ private async Task PublishAsync()
+ {
+ SaveAll();
+ if(Directory.Exists(CurrentProject))
+ await WeakReferenceMessenger.Default.Send(new PublishMessage(CurrentProject));
+ }
+ [RelayCommand]
+ private async Task PushPackageAsync()
+ {
+ SaveAll();
+ if (Directory.Exists(CurrentProject))
+ {
+ var box = MessageBoxManager
+ .GetMessageBoxStandard("Push Package Confirmation", $"Are you sure you want to push the package?",
+ ButtonEnum.YesNo, Icon.Question);
+ var msg = await WeakReferenceMessenger.Default.Send();
+ if (msg is not null && await box.ShowWindowDialogAsync(msg) == ButtonResult.Yes)
+
+ CrossLangShell.PushProjectInFolder(CurrentProject);
+ }
+ }
+}
+
+public class RecoveryConfig
+{
+ public DateTime Date { get; set; }
+
+ public string ProjectDirectory { get; set; } = "";
+
+ public bool HasRecovery { get; set; }
}
public partial class NewTabViewModel : ViewModelBase
diff --git a/ViewModels/NewProjectDialogViewModel.cs b/ViewModels/NewProjectDialogViewModel.cs
index 6abf493..418c58f 100644
--- a/ViewModels/NewProjectDialogViewModel.cs
+++ b/ViewModels/NewProjectDialogViewModel.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.ObjectModel;
using System.IO;
+using System.Threading.Tasks;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
+using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
@@ -85,6 +87,20 @@ public partial class NewProjectDialogViewModel : ViewModelBase
}
}
}
+ [RelayCommand]
+ public async Task BrowseAsync()
+ {
+ var window = await WeakReferenceMessenger.Default.Send();
+
+
+ var res = await window.StorageProvider.OpenFolderPickerAsync(new Avalonia.Platform.Storage.FolderPickerOpenOptions() { Title = "Pick Project Parent Folder" });
+ if (res.Count != 0)
+ {
+ string? path = res[0].TryGetLocalPath();
+ if(!string.IsNullOrWhiteSpace(path))
+ this.ParentDirectory = path;
+ }
+ }
[RelayCommand]
private void OK()
@@ -111,7 +127,7 @@ public partial class CrossLangTemplate(string name,CrossLangFile file) : Observa
{
[ObservableProperty]
- private string _name = file.GetTemplatePrettyName();
+ private string _name = file.GetPrettyName();
[ObservableProperty]
private IImage _icon = file.GetIcon(64);
diff --git a/ViewModels/PackageManagerViewModel.cs b/ViewModels/PackageManagerViewModel.cs
index f58f10d..72aff3a 100644
--- a/ViewModels/PackageManagerViewModel.cs
+++ b/ViewModels/PackageManagerViewModel.cs
@@ -49,12 +49,12 @@ public partial class PackageManagerViewModel : ViewModelBase
foreach(var svr in CrossLangShell.GetServers())
this.Servers.Add(svr);
}
-
+ //
[RelayCommand]
private async Task SearchPackagesAsync()
{
var svr = Servers[ServerIndex].TrimEnd('/');
- var body = await Main.Client.GetStringAsync($"{svr}/api/v1/search?q={HttpUtility.UrlEncode(Search)}&filter={HttpUtility.UrlEncode(Packages.Filter)}&offset={Page-1}&limit=20");
+ var body = await Main.Client.GetStringAsync($"{svr}/api/v1/search?q={HttpUtility.UrlEncode(Search)}&type={HttpUtility.UrlEncode(Packages.Filter)}&offset={Page-1}&limit=20");
var res = JsonConvert.DeserializeObject(body);
PackageList.Clear();
if (res is not null)
diff --git a/ViewModels/ProjectConfigurationViewModel.cs b/ViewModels/ProjectConfigurationViewModel.cs
index b295207..fa4f81e 100644
--- a/ViewModels/ProjectConfigurationViewModel.cs
+++ b/ViewModels/ProjectConfigurationViewModel.cs
@@ -29,7 +29,7 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable
TabItemViewModel tab;
public string FilePath { get; }
bool _modified = false;
- private bool Modified
+ public bool Modified
{
get => _modified;
set
@@ -56,8 +56,8 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable
string _license;
- string _templatename;
- string _templatenamepretty;
+ string _shortname;
+ string _shortnamepretty;
string _description;
@@ -130,22 +130,22 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable
}
}
- public string TemplateName
+ public string ShortName
{
- get => _templatename;
+ get => _shortname;
set
{
Modified = true;
- SetProperty(ref _templatename, value, nameof(TemplateName));
+ SetProperty(ref _shortname, value, nameof(ShortName));
}
}
- public string TemplateNamePretty
+ public string ShortNamePretty
{
- get => _templatenamepretty;
+ get => _shortnamepretty;
set
{
Modified = true;
- SetProperty(ref _templatenamepretty, value, nameof(TemplateNamePretty));
+ SetProperty(ref _shortnamepretty, value, nameof(ShortNamePretty));
}
}
public string Description
@@ -183,8 +183,8 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable
_homepage = config?.Info?.HomePage ?? "";
_maintainer = config?.Info?.Maintainer ?? "";
_license = config?.Info?.License ?? "";
- _templatename = config?.Info?.TemplateName ?? "";
- _templatenamepretty = config?.Info?.TemplateNamePretty ?? "";
+ _shortname = config?.Info?.ShortName ?? "";
+ _shortnamepretty = config?.Info?.ShortNamePretty ?? "";
_type = Array.IndexOf(CrossLangShell.ProjectTypes, config?.Info?.Type ?? "console");
_type = _type == -1 ? 0 : _type;
_description = config?.Info?.Description ?? "";
@@ -193,6 +193,12 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable
[RelayCommand]
public void Save()
+ {
+ SaveRecovery(FilePath);
+ Modified = false;
+ }
+
+ public void SaveRecovery(string path)
{
var config = JsonConvert.DeserializeObject(File.ReadAllText(FilePath)) ?? new CrossLangConfig();
config.Name = Name;
@@ -207,13 +213,12 @@ public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable
config.Info.License = string.IsNullOrWhiteSpace(License) ? null : License;
config.Info.Maintainer = string.IsNullOrWhiteSpace(Maintainer) ? null : Maintainer;
config.Info.Repoository = string.IsNullOrWhiteSpace(Repository) ? null : Repository;
- config.Info.TemplateName = string.IsNullOrWhiteSpace(TemplateName) ? null : TemplateName;
- config.Info.TemplateNamePretty = string.IsNullOrWhiteSpace(TemplateNamePretty) ? null : TemplateNamePretty;
+ config.Info.ShortName = string.IsNullOrWhiteSpace(ShortName) ? null : ShortName;
+ config.Info.ShortNamePretty = string.IsNullOrWhiteSpace(ShortNamePretty) ? null : ShortNamePretty;
config.Info.Type = CrossLangShell.ProjectTypes[Type];
- File.WriteAllText(FilePath, JsonConvert.SerializeObject(config, Formatting.Indented, new JsonSerializerSettings()
+ File.WriteAllText(path, JsonConvert.SerializeObject(config, Formatting.Indented, new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore
}));
- Modified = false;
}
}
\ No newline at end of file
diff --git a/ViewModels/PublishDialogViewModel.cs b/ViewModels/PublishDialogViewModel.cs
new file mode 100644
index 0000000..510cc26
--- /dev/null
+++ b/ViewModels/PublishDialogViewModel.cs
@@ -0,0 +1,279 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using System.Web;
+using Avalonia.Markup.Xaml.Templates;
+using Avalonia.Media;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using CommunityToolkit.Mvvm.Messaging;
+using CrossLangDevStudio.Messages;
+using CrossLangDevStudio.Views;
+using Newtonsoft.Json;
+using ICSharpCode.SharpZipLib.Zip;
+using ICSharpCode.SharpZipLib.Tar;
+using ICSharpCode.SharpZipLib.GZip;
+using ICSharpCode.SharpZipLib.BZip2;
+using System.Text;
+using Avalonia.Platform.Storage;
+
+namespace CrossLangDevStudio.ViewModels;
+
+public partial class PublishDialogViewModel : ViewModelBase
+{
+ public string Path { get; }
+ public PublishDialogViewModel(string path)
+ {
+ Path = path;
+ foreach (var item in CrossLangShell.GetServers())
+ {
+ Servers.Add(item);
+ }
+ Targets.Add(new Target("copy", "Just Copy"));
+ Targets.Add(new Target("crvm", "Merge"));
+ Targets.Add(new Target("cmake", "CMake"));
+ Targets.Add(new Target("header", "C++ Header"));
+ this.TargetIndex = 0;
+ }
+ [ObservableProperty]
+ private string _prefix = "Tesses.CrossLang.Runtime";
+
+ public ObservableCollection Servers { get; } = new ObservableCollection();
+ [ObservableProperty]
+ private int _serverIndex = 0;
+ public ObservableCollection Targets { get; } = new ObservableCollection();
+ [ObservableProperty]
+ private int _targetIndex = 0;
+
+ [ObservableProperty]
+ private string _output = "";
+ int _ot=0;
+ public int OutputType
+ {
+ get => _ot;
+ set {
+ _ot = value;
+ switch(value)
+ {
+ case 0:
+ OutputLabel = "Directory To Publish To (Optional)";
+ break;
+ case 1:
+ OutputLabel = "Zip Path";
+ break;
+ case 2:
+ case 3:
+ case 4:
+ OutputLabel = "Tar Path";
+ break;
+ }
+ }
+ }
+
+ [ObservableProperty]
+ private string _outputLabel = "Directory To Publish To (Optional)";
+
+ [RelayCommand]
+ private async Task BrowseAsync()
+ {
+ var window = await WeakReferenceMessenger.Default.Send();
+
+ if (OutputType == 0)
+ {
+ var res = await window.StorageProvider.OpenFolderPickerAsync(new Avalonia.Platform.Storage.FolderPickerOpenOptions() { Title = "Pick publish folder" });
+ if (res.Count != 0)
+ {
+ string? path = res[0].TryGetLocalPath();
+ if (!string.IsNullOrWhiteSpace(path))
+ this.Output = path;
+ }
+ }
+ else
+ {
+ string ext = "zip";
+ switch(OutputType)
+ {
+ case 2:
+ ext = "tar";
+ break;
+ case 3:
+ ext = "tar.gz";
+ break;
+ case 4:
+ ext = "tar.bz2";
+ break;
+ }
+
+ var res = await window.StorageProvider.SaveFilePickerAsync(new Avalonia.Platform.Storage.FilePickerSaveOptions() { Title = $"Save {ext}", DefaultExtension = $".{ext}" , FileTypeChoices = [new FilePickerFileType(ext.ToUpper()){ Patterns =[$".{ext}"]}]});
+ if (res is not null)
+ {
+ string? path = res.TryGetLocalPath();
+ if (!string.IsNullOrWhiteSpace(path))
+ this.Output = path;
+ }
+ }
+ }
+
+ [RelayCommand]
+ private async Task LoadAsync()
+ {
+ Targets.Clear();
+ Targets.Add(new Target("copy", "Just Copy"));
+ Targets.Add(new Target("crvm", "Merge"));
+ Targets.Add(new Target("cmake", "CMake"));
+ Targets.Add(new Target("header", "C++ Header"));
+ this.TargetIndex = 0;
+ if (ServerIndex >= 0 && ServerIndex < this.Servers.Count)
+ {
+ string url = $"{this.Servers[ServerIndex].TrimEnd('/')}/api/v1/search?type=archive";
+ if (!string.IsNullOrWhiteSpace(Prefix))
+ {
+ url = $"{url}&q={HttpUtility.UrlEncode(Prefix.TrimEnd('.'))}.";
+ }
+ var window = await WeakReferenceMessenger.Default.Send(new GetWindowMessage());
+ if (window is not null && window.DataContext is MainWindowViewModel mwvm)
+ {
+ int page = 0;
+ bool hadany = false;
+ do
+ {
+ var body = await mwvm.Client.GetStringAsync($"{url}&offset={page}");
+ page++;
+ var res = JsonConvert.DeserializeObject(body);
+ if (res is not null)
+ {
+ hadany = res.Packages.Count > 0;
+ foreach (var item in res.Packages)
+ {
+ string name = item.Name;
+ if (!string.IsNullOrWhiteSpace(Prefix))
+ {
+ int prefixLen = Prefix.TrimEnd('.').Length + 1;
+ name = name.Substring(prefixLen);
+ }
+
+ Targets.Add(new Target(name, name));
+ }
+ }
+ } while (hadany);
+ }
+ }
+
+ }
+
+ [RelayCommand]
+ private async Task PublishAsync()
+ {
+
+ string ident = Targets[TargetIndex].Real;
+ await CrossLangShell.PublishAsync(Path, ident, Prefix, OutputType == 0 ? Output : "");
+
+ void Tar(Stream strm)
+ {
+
+ string dir = System.IO.Path.Combine(Path, "publish", ident);
+
+
+ using (var tarArchive = TarArchive.CreateOutputTarArchive(strm, Encoding.UTF8))
+ {
+ tarArchive.RootPath = System.IO.Path.DirectorySeparatorChar == '/' ? dir : dir.Replace('\\', '/');
+ if (tarArchive.RootPath.EndsWith("/"))
+ tarArchive.RootPath = tarArchive.RootPath.Remove(tarArchive.RootPath.Length - 1);
+
+ DirectoryContents(tarArchive, dir);
+ }
+
+
+
+
+
+ /*
+ TarEntry ent = TarEntry.CreateEntryFromFile();
+ ent.TarHeader.Mode = 0755;
+ ent.TarHeader.UserId = 1000;
+ ent.TarHeader.GroupId = 1000;
+ */
+
+ }
+
+ if (OutputType > 0)
+ {
+ switch (OutputType)
+ {
+ case 1:
+ {
+ FastZip z = new FastZip();
+ z.CreateZip(Output, System.IO.Path.Combine(Path, "publish", ident), true, null);
+ }
+ break;
+ case 2:
+ {
+ using (var strm = File.Create(Output))
+ Tar(strm);
+
+ }
+ break;
+ case 3:
+ {
+ using (var strm = File.Create(Output))
+ {
+ using (var gz = new GZipOutputStream(strm))
+ Tar(gz);
+ }
+ }
+ break;
+ case 4:
+ {
+ using (var strm = File.Create(Output))
+ {
+ using (var bzip2 = new BZip2OutputStream(strm))
+ Tar(bzip2);
+ }
+ }
+ break;
+ }
+ }
+ _ = WeakReferenceMessenger.Default.Send(new PublishCloseMessage());
+ }
+
+ [RelayCommand]
+ private void Cancel()
+ {
+ WeakReferenceMessenger.Default.Send(new PublishCloseMessage());
+ }
+
+ private void DirectoryContents(TarArchive tarArchive, string src)
+ {
+ TarEntry tarEntry = TarEntry.CreateEntryFromFile(src);
+ tarArchive.WriteEntry(tarEntry, false);
+
+ // Write each file to the tar.
+ string[] filenames = Directory.GetFiles(src);
+ foreach (string filename in filenames)
+ {
+ tarEntry = TarEntry.CreateEntryFromFile(filename);
+
+ tarEntry.TarHeader.UserId = 1000;
+ tarEntry.TarHeader.GroupId = 1000;
+ tarArchive.WriteEntry(tarEntry, true);
+
+ }
+ string[] directories = Directory.GetDirectories(src);
+ foreach (string directory in directories)
+ DirectoryContents(tarArchive, directory);
+ }
+}
+
+public class Target(string real, string pretty)
+{
+ public string Real => real;
+
+ public string Pretty => pretty;
+
+ public override string ToString()
+ {
+ return Pretty;
+ }
+}
\ No newline at end of file
diff --git a/Views/AddProjectReferenceView.axaml b/Views/AddProjectReferenceView.axaml
new file mode 100644
index 0000000..ffad591
--- /dev/null
+++ b/Views/AddProjectReferenceView.axaml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Views/AddProjectReferenceView.axaml.cs b/Views/AddProjectReferenceView.axaml.cs
new file mode 100644
index 0000000..26c8f76
--- /dev/null
+++ b/Views/AddProjectReferenceView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia.Controls;
+using AvaloniaEdit;
+using CrossLangDevStudio.ViewModels;
+
+namespace CrossLangDevStudio.Views;
+
+public partial class AddProjectReferenceView : UserControl
+{
+ public AddProjectReferenceView()
+ {
+ InitializeComponent();
+
+ }
+}
\ No newline at end of file
diff --git a/Views/CRVMViewerView.axaml b/Views/CRVMViewerView.axaml
new file mode 100644
index 0000000..1705f63
--- /dev/null
+++ b/Views/CRVMViewerView.axaml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+ Functions:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Classes:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Views/CRVMViewerView.axaml.cs b/Views/CRVMViewerView.axaml.cs
new file mode 100644
index 0000000..9ae49aa
--- /dev/null
+++ b/Views/CRVMViewerView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia.Controls;
+using AvaloniaEdit;
+using CrossLangDevStudio.ViewModels;
+
+namespace CrossLangDevStudio.Views;
+
+public partial class CRVMViewerView : UserControl
+{
+ public CRVMViewerView()
+ {
+ InitializeComponent();
+
+ }
+}
\ No newline at end of file
diff --git a/Views/FileEditorView.axaml b/Views/FileEditorView.axaml
index 4a799e2..136aa20 100644
--- a/Views/FileEditorView.axaml
+++ b/Views/FileEditorView.axaml
@@ -5,13 +5,15 @@
xmlns:vm="clr-namespace:CrossLangDevStudio.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="CrossLangDevStudio.Views.FileEditorView"
+ xmlns:AvaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
x:DataType="vm:FileEditorViewModel">
-
-
+
+
+
\ No newline at end of file
diff --git a/Views/FileEditorView.axaml.cs b/Views/FileEditorView.axaml.cs
index 2312b5b..dfe0002 100644
--- a/Views/FileEditorView.axaml.cs
+++ b/Views/FileEditorView.axaml.cs
@@ -1,4 +1,5 @@
using Avalonia.Controls;
+using AvaloniaEdit;
using CrossLangDevStudio.ViewModels;
namespace CrossLangDevStudio.Views;
@@ -8,5 +9,21 @@ public partial class FileEditorView : UserControl
public FileEditorView()
{
InitializeComponent();
+ this.Loaded += (sender, e) =>
+ {
+ if (this.DataContext is FileEditorViewModel dc)
+ {
+ this.Content = dc.TextEditor;
+ }
+ };
+ this.Unloaded += (sender, e) =>
+ {
+ this.Content = null;
+ };
+
+
}
+
+
+
}
\ No newline at end of file
diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml
index 30ef252..9535cb9 100644
--- a/Views/MainWindow.axaml
+++ b/Views/MainWindow.axaml
@@ -28,37 +28,38 @@
-
-
+
-
-
-
-
+
+
+
+
-
-
+
-
-
-
+
+
+
+
+
-
+
@@ -78,6 +79,7 @@
+ {
+ if (this.DataContext is MainWindowViewModel vm)
+ {
+ if (vm.HasUnsaved)
+ {
+ //thanks https://github.com/AvaloniaUI/Avalonia/issues/4810#issuecomment-704372304
+ using (var source = new CancellationTokenSource())
+ {
+ CloseWindowAsync(e, vm).ContinueWith(e => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
+ Dispatcher.UIThread.MainLoop(source.Token);
+ }
+ }
+ }
+ };
+
// Whenever 'Send(new PurchaseAlbumMessage())' is called, invoke this callback on the MainWindow instance:
WeakReferenceMessenger.Default.Register(this, static (w, m) =>
@@ -27,6 +65,7 @@ public partial class MainWindow : Window
};
// Show dialog window and reply with returned AlbumViewModel or null when the dialog is closed.
m.Reply(dialog.ShowDialog(w));
+
});
WeakReferenceMessenger.Default.Register(this, static (w, m) =>
{
@@ -42,6 +81,13 @@ public partial class MainWindow : Window
m.Reply(w);
}
);
-
+ WeakReferenceMessenger.Default.Register(this, static (w, m) =>
+ {
+ var dialog = new PublishDialogView
+ {
+ DataContext = new PublishDialogViewModel(m.Path)
+ };
+ m.Reply(dialog.ShowDialog(w));
+ });
}
}
\ No newline at end of file
diff --git a/Views/NewProjectDialog.axaml b/Views/NewProjectDialog.axaml
index 2b2a7d2..3f21053 100644
--- a/Views/NewProjectDialog.axaml
+++ b/Views/NewProjectDialog.axaml
@@ -16,7 +16,7 @@
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
-
+
@@ -32,19 +32,19 @@
-
+
-
+
-
+
@@ -53,16 +53,16 @@
-
-
+
+
-
+
-
+
diff --git a/Views/PackageManagerView.axaml b/Views/PackageManagerView.axaml
index 286d5e5..3807926 100644
--- a/Views/PackageManagerView.axaml
+++ b/Views/PackageManagerView.axaml
@@ -11,7 +11,7 @@
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
-
+
@@ -25,21 +25,21 @@
-
+
-
+
-
+
diff --git a/Views/ProjectConfigurationView.axaml b/Views/ProjectConfigurationView.axaml
index 25c3cfe..ec1a899 100644
--- a/Views/ProjectConfigurationView.axaml
+++ b/Views/ProjectConfigurationView.axaml
@@ -43,10 +43,10 @@
License:
- Template Name (Shown in crosslang new --list):
-
- Template Pretty Name (Shown in new project):
-
+ Short Name (Shown in crosslang new --list or tool or console name):
+
+ Short Pretty Name (Shown in new project):
+
Description:
diff --git a/Views/PublishDialogView.axaml b/Views/PublishDialogView.axaml
new file mode 100644
index 0000000..ceb4973
--- /dev/null
+++ b/Views/PublishDialogView.axaml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Directory
+ ZIP
+ TAR
+ TAR.GZ
+ TAR.BZ2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Views/PublishDialogView.axaml.cs b/Views/PublishDialogView.axaml.cs
new file mode 100644
index 0000000..ee6f8cf
--- /dev/null
+++ b/Views/PublishDialogView.axaml.cs
@@ -0,0 +1,21 @@
+using System.Diagnostics;
+using Avalonia.Controls;
+using CommunityToolkit.Mvvm.Messaging;
+using CrossLangDevStudio.Messages;
+
+namespace CrossLangDevStudio.Views;
+
+public partial class PublishDialogView : Window
+{
+ public PublishDialogView()
+ {
+ InitializeComponent();
+ if (Design.IsDesignMode) return;
+
+ WeakReferenceMessenger.Default.Register(this, static (w, m) =>
+ {
+ w.Close(false);
+ }
+ );
+ }
+}
\ No newline at end of file