Initial commit

This commit is contained in:
Mausham
2025-12-26 17:56:05 -08:00
commit 7cb0a26dae
453 changed files with 53483 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using static System.IO.Path;
#pragma warning disable IDE0005
using Serilog = Meryel.Serilog;
#pragma warning restore IDE0005
#nullable enable
namespace Meryel.UnityCodeAssist.Editor
{
public static class CommonTools
{
public static string GetScriptPath(string script)
{
var projectPath = GetProjectPathRaw();
var toolPath = Combine(projectPath, "Packages/com.merry-yellow.code-assist/Editor/", script);
return toolPath;
}
/// <summary>
/// does NOT include the trailing slash
/// </summary>
/// <returns></returns>
public static string GetExternalReferencesPath()
{
var projectPath = GetProjectPathRaw();
var extRefPath = Combine(projectPath, "Packages/com.merry-yellow.code-assist/Editor/ExternalReferences");
return extRefPath;
}
[Obsolete]
public static string GetToolPath(string tool)
{
var projectPath = GetProjectPathRaw();
var toolPath = Combine(projectPath, "Packages/com.merry-yellow.code-assist/Tools~/", tool);
return toolPath;
}
public static string GetInstallerPath(string installer)
{
var projectPath = GetProjectPathRaw();
var installerPath = Combine(projectPath, "Packages/com.merry-yellow.code-assist/Installers~/", installer);
return installerPath;
}
public static string GetTagManagerFilePath()
{
var projectPath = GetProjectPathRaw();
var tagManagerPath = Combine(projectPath, "ProjectSettings/TagManager.asset");
return tagManagerPath;
}
public static string GetInputManagerFilePath()
{
var projectPath = GetProjectPathRaw();
var inputManagerPath = Combine(projectPath, "ProjectSettings/InputManager.asset");
return inputManagerPath;
}
public static string GetProjectPath()
{
var rawPath = GetProjectPathRaw();
//var pathWithoutWhiteSpace = rawPath.Trim(); // this is done in OSPath ctor
var osPath = new OSPath(rawPath);
var unixPath = osPath.Unix;
var trimmed = unixPath.TrimEnd('\\', '/');
var capitalized = FirstCharToUpper(trimmed); // this is required for TypeScript, so doing it here as well just in case
return capitalized!;
}
static string? FirstCharToUpper(string? input)
{
switch (input)
{
case null: return null;
case "": return "";
default: return input[0].ToString().ToUpper() + input.Substring(1);
}
}
/// <summary>
/// Get the path to the project folder.
/// </summary>
/// <returns>The project folder path</returns>
static string GetProjectPathRaw()
{
// Application.dataPath returns the path including /Assets, which we need to strip off
var path = UnityEngine.Application.dataPath;
var directory = new DirectoryInfo(path);
var parent = directory.Parent;
if (parent != null)
return parent.FullName;
return path;
}
public static string GetHashForLogFile(string path) => Synchronizer.Model.Utilities.GetHashForLogFile(path);
}
// https://github.com/dmitrynogin/cdsf/blob/master/Cds.Folders/OSPath.cs
internal class OSPath
{
public static readonly OSPath Empty = "";
public static bool IsWindows => DirectorySeparatorChar == '\\';
public OSPath(string text)
{
Text = text.Trim();
}
public static implicit operator OSPath(string text) => new OSPath(text);
public static implicit operator string(OSPath path) => path.Normalized;
public override string ToString() => Normalized;
protected string Text { get; }
public string Normalized => IsWindows ? Windows : Unix;
public string Windows => Text.Replace('/', '\\');
//public string Unix => Simplified.Text.Replace('\\', '/');
public string Unix => Text.Replace('\\', '/');
public OSPath Relative => Simplified.Text.TrimStart('/', '\\');
public OSPath Absolute => IsAbsolute ? this : "/" + Relative;
public bool IsAbsolute => IsRooted || HasVolume;
public bool IsRooted => Text.Length >= 1 && (Text[0] == '/' || Text[0] == '\\');
public bool HasVolume => Text.Length >= 2 && Text[1] == ':';
public OSPath Simplified => HasVolume ? Text.Substring(2) : Text;
public OSPath Parent => GetDirectoryName(Text);
public bool Contains(OSPath path) =>
Normalized.StartsWith(path);
public static OSPath operator +(OSPath left, OSPath right) =>
new OSPath(Combine(left, right.Relative));
public static OSPath operator -(OSPath left, OSPath right) =>
left.Contains(right)
? new OSPath(left.Normalized.Substring(right.Normalized.Length)).Relative
: left;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9066c480a2fcb9940a432377a49262af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,203 @@
//using Meryel.UnityCodeAssist.Serilog;
//using Meryel.UnityCodeAssist.Serilog.Core;
using UnityEngine;
using UnityEditor;
using System.Linq;
#if ELOGGER
#pragma warning disable IDE0005
using Serilog = Meryel.Serilog;
#pragma warning restore IDE0005
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Logger
{
//[InitializeOnLoad]
public static class ELogger
{
public static event System.Action? OnVsInternalLogChanged;
// Change 'new LoggerConfiguration().MinimumLevel.Debug();' if you change these values
const Serilog.Events.LogEventLevel fileMinLevel = Serilog.Events.LogEventLevel.Debug;
const Serilog.Events.LogEventLevel outputWindowMinLevel = Serilog.Events.LogEventLevel.Information;
static LoggingLevelSwitch? fileLevelSwitch, outputWindowLevelSwitch;
//static bool IsInitialized { get; set; }
static ILogEventSink? _outputWindowSink;
static ILogEventSink? _memorySink;
public static string GetInternalLogContent() => _memorySink == null ? string.Empty : ((MemorySink)_memorySink).Export();
public static int GetErrorCountInInternalLog() => _memorySink == null ? 0 : ((MemorySink)_memorySink).ErrorCount;
public static int GetWarningCountInInternalLog() => _memorySink == null ? 0 : ((MemorySink)_memorySink).WarningCount;
public static string? FilePath { get; private set; }
public static string? VSFilePath { get; private set; }
//**-- make it work with multiple clients
static string? _vsInternalLog;
public static string? VsInternalLog
{
get => _vsInternalLog;
set
{
_vsInternalLog = value;
OnVsInternalLogChanged?.Invoke();
}
}
static ELogger()
{
var isFirst = false;
const string stateName = "isFirst";
if (!SessionState.GetBool(stateName, false))
{
isFirst = true;
SessionState.SetBool(stateName, true);
}
var projectPath = CommonTools.GetProjectPath();
var outputWindowSink = new System.Lazy<ILogEventSink>(() => new UnityOutputWindowSink(null));
Init(isFirst, projectPath, outputWindowSink);
if (isFirst)
LogHeader(Application.unityVersion, projectPath);
}
/// <summary>
/// Empty method for invoking static class ctor
/// </summary>
public static void Bump() { }
static void LogHeader(string unityVersion, string solutionDir)
{
var os = System.Runtime.InteropServices.RuntimeInformation.OSDescription;
var assisterVersion = Assister.Version;
var syncModel = Synchronizer.Model.Utilities.Version;
var hash = CommonTools.GetHashForLogFile(solutionDir);
var port = Synchronizer.Model.Utilities.GetPortForMQTTnet(solutionDir);
Serilog.Log.Debug(
"Beginning logging {OS}, Unity {U}, Unity Code Assist {A}, Communication Protocol {SM}, Project: '{Dir}', Project Hash: {Hash}, Port: {Port}",
os, unityVersion, assisterVersion, syncModel, solutionDir, hash, port);
}
static string GetFilePath(string solutionDir)
{
var solutionHash = CommonTools.GetHashForLogFile(solutionDir);
var tempDir = System.IO.Path.GetTempPath();
var fileName = $"UnityCodeAssist_U_Log_{solutionHash}_.TXT"; // hour code will be appended to the end of file, so add a trailing '_'
var filePath = System.IO.Path.Combine(tempDir, fileName);
return filePath;
}
static string GetVSFilePath(string solutionDir)
{
var solutionHash = CommonTools.GetHashForLogFile(solutionDir);
var tempDir = System.IO.Path.GetTempPath();
#if MERYEL_UCA_LITE_VERSION
var fileName = $"UnityCodeAssistLite_VS_Log_{solutionHash}_.TXT"; // hour code will be appended to the end of file, so add a trailing '_'
#else
var fileName = $"UnityCodeAssist_VS_Log_{solutionHash}_.TXT"; // hour code will be appended to the end of file, so add a trailing '_'
#endif
var filePath = System.IO.Path.Combine(tempDir, fileName);
return filePath;
}
public static void Init(bool isFirst, string solutionDir, System.Lazy<ILogEventSink> outputWindowSink)
{
FilePath = GetFilePath(solutionDir);
VSFilePath = GetVSFilePath(solutionDir);
fileLevelSwitch = new LoggingLevelSwitch(fileMinLevel);
outputWindowLevelSwitch = new LoggingLevelSwitch(outputWindowMinLevel);
var config = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.With(new DomainHashEnricher());
const string outputTemplate = "{Timestamp:HH:mm:ss.fff} [U] [{Level:u3}] [{DomainHash}] {Message:lj}{NewLine}{Exception}";
config = config.WriteTo.PersistentFile(FilePath
, outputTemplate: outputTemplate
, shared: true
, persistentFileRollingInterval: PersistentFileRollingInterval.Day
, preserveLogFilename: true
, levelSwitch: fileLevelSwitch
, rollOnEachProcessRun: isFirst
);
_outputWindowSink ??= outputWindowSink.Value;
if (_outputWindowSink != null)
config = config.WriteTo.Sink(_outputWindowSink, outputWindowMinLevel, outputWindowLevelSwitch);
_memorySink ??= new MemorySink(outputTemplate);
config = config.WriteTo.Sink(_memorySink, fileMinLevel, null);
config = config.Destructure.With(new MyDestructuringPolicy());
Serilog.Log.Logger = config.CreateLogger();
//switchableLogger.Set(config.CreateLogger(), disposePrev: true);
OnOptionsChanged();
//IsInitialized = true;
}
public static void OnOptionsChanged()
{
// Since we don't use LogEventLevel.Fatal, we can use it for disabling sinks
var isLoggingToFile = OptionsIsLoggingToFile;
var targetFileLevel = isLoggingToFile ? fileMinLevel : Serilog.Events.LogEventLevel.Fatal;
if (fileLevelSwitch != null)
fileLevelSwitch.MinimumLevel = targetFileLevel;
var isLoggingToOutputWindow = OptionsIsLoggingToOutputWindow;
var targetOutputWindowLevel = isLoggingToOutputWindow ? outputWindowMinLevel : Serilog.Events.LogEventLevel.Fatal;
if (outputWindowLevelSwitch != null)
outputWindowLevelSwitch.MinimumLevel = targetOutputWindowLevel;
}
//**-- UI for these two
static bool OptionsIsLoggingToFile => true;
static bool OptionsIsLoggingToOutputWindow => true;
}
public class MyDestructuringPolicy : IDestructuringPolicy
{
// serilog cannot destruct StringArrayContainer by default, so do it manually
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out Serilog.Events.LogEventPropertyValue? result)
{
if (value is Synchronizer.Model.StringArrayContainer sac)
{
var items = sac.Container.Select(item => propertyValueFactory.CreatePropertyValue(item, true));
result = new Serilog.Events.SequenceValue(items);
return true;
}
result = null;
return false;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca3b9ef056f0ba843936ff335d12b0a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,137 @@
//using Meryel.UnityCodeAssist.Serilog;
//using Meryel.UnityCodeAssist.Serilog.Core;
using UnityEngine;
using UnityEditor;
using System.Linq;
using Meryel.Serilog;
using Meryel.Serilog.Core;
#pragma warning disable IDE0005
using Serilog = Meryel.Serilog;
#pragma warning restore IDE0005
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Logger
{
//[InitializeOnLoad]
public static class ELogger
{
public static event System.Action? OnVsInternalLogChanged;
// Change 'new LoggerConfiguration().MinimumLevel.Debug();' if you change these values
const Serilog.Events.LogEventLevel fileMinLevel = Serilog.Events.LogEventLevel.Debug;
const Serilog.Events.LogEventLevel outputWindowMinLevel = Serilog.Events.LogEventLevel.Information;
static LoggingLevelSwitch? fileLevelSwitch, outputWindowLevelSwitch;
//static bool IsInitialized { get; set; }
static ILogEventSink? _outputWindowSink;
static ILogEventSink? _memorySink;
public static string GetInternalLogContent() => _memorySink == null ? string.Empty : ((Meryel.UnityCodeAssist.Logger.MemorySink)_memorySink).Export();
public static int GetErrorCountInInternalLog() => _memorySink == null ? 0 : ((Meryel.UnityCodeAssist.Logger.MemorySink)_memorySink).ErrorCount;
public static int GetWarningCountInInternalLog() => _memorySink == null ? 0 : ((Meryel.UnityCodeAssist.Logger.MemorySink)_memorySink).WarningCount;
public static string? FilePath => Meryel.UnityCodeAssist.Logger.ELogger.UnityFilePath;
public static string? VSFilePath => Meryel.UnityCodeAssist.Logger.ELogger.VisualStudioFilePath;
//**-- make it work with multiple clients
static string? _vsInternalLog;
public static string? VsInternalLog
{
get => _vsInternalLog;
set
{
_vsInternalLog = value;
OnVsInternalLogChanged?.Invoke();
}
}
static ELogger()
{
fileLevelSwitch = null;
outputWindowLevelSwitch = null;
_memorySink = null;
var isFirst = false;
const string stateName = "isFirst";
if (!SessionState.GetBool(stateName, false))
{
isFirst = true;
SessionState.SetBool(stateName, true);
}
var projectPath = CommonTools.GetProjectPath();
var outputWindowSink = new System.Lazy<ILogEventSink>(() => new UnityOutputWindowSink(null));
Init(isFirst, projectPath, outputWindowSink);
if (isFirst)
LogHeader(Application.unityVersion, projectPath);
}
/// <summary>
/// Empty method for invoking static class ctor
/// </summary>
public static void Bump() { }
static void LogHeader(string unityVersion, string solutionDir)
{
var os = System.Runtime.InteropServices.RuntimeInformation.OSDescription;
var assisterVersion = Assister.Version;
var syncModel = Synchronizer.Model.Utilities.Version;
var hash = CommonTools.GetHashForLogFile(solutionDir);
var port = Synchronizer.Model.Utilities.GetPortForMQTTnet(solutionDir);
Serilog.Log.Debug(
"Beginning logging {OS}, Unity {U}, Unity Code Assist {A}, Communication Protocol {SM}, Project: '{Dir}', Project Hash: {Hash}, Port: {Port}",
os, unityVersion, assisterVersion, syncModel, solutionDir, hash, port);
}
public static void Init(bool isFirst, string solutionDir, System.Lazy<ILogEventSink> outputWindowSink)
{
//var solutionHash = Common.CommonTools.GetHashOfPath(solutionDir);
var solutionHash = CommonTools.GetHashForLogFile(solutionDir); // dir is osSafePath
_outputWindowSink ??= outputWindowSink.Value;
var sinkWrapper = new System.Lazy<Meryel.Serilog.Core.ILogEventSink>(() => _outputWindowSink);
Meryel.UnityCodeAssist.Logger.ELogger.Init(
UnityCodeAssist.Logger.ELogger.State.FullyInitialized,
UnityCodeAssist.Logger.ELogger.PackagePriority.High,
solutionDir, solutionHash, "UnityCodeAssist", ProjectData.Domain.Unity,
sinkWrapper, null, null, null, null);
}
public static void OnOptionsChanged()
{
// Since we don't use LogEventLevel.Fatal, we can use it for disabling sinks
var isLoggingToFile = OptionsIsLoggingToFile;
var targetFileLevel = isLoggingToFile ? fileMinLevel : Serilog.Events.LogEventLevel.Fatal;
if (fileLevelSwitch != null)
fileLevelSwitch.MinimumLevel = targetFileLevel;
var isLoggingToOutputWindow = OptionsIsLoggingToOutputWindow;
var targetOutputWindowLevel = isLoggingToOutputWindow ? outputWindowMinLevel : Serilog.Events.LogEventLevel.Fatal;
if (outputWindowLevelSwitch != null)
outputWindowLevelSwitch.MinimumLevel = targetOutputWindowLevel;
}
//**-- UI for these two
static bool OptionsIsLoggingToFile => true;
static bool OptionsIsLoggingToOutputWindow => true;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44a33ba3272c55d4fad27588d4bdcb2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using System;
using System.Linq;
//using Meryel.UnityCodeAssist.Serilog;
//using Meryel.UnityCodeAssist.Serilog.Core;
//using Meryel.UnityCodeAssist.Serilog.Events;
//using Meryel.UnityCodeAssist.Serilog.Configuration;
using Meryel.Serilog;
using Meryel.Serilog.Core;
using Meryel.Serilog.Events;
#pragma warning disable IDE0005
using Serilog = Meryel.Serilog;
#pragma warning restore IDE0005
#nullable enable
namespace Meryel.UnityCodeAssist.Editor.Logger
{
public class UnityOutputWindowSink : ILogEventSink
{
private readonly IFormatProvider? _formatProvider;
public UnityOutputWindowSink(IFormatProvider? formatProvider)
{
_formatProvider = formatProvider;
}
public void Emit(LogEvent? logEvent)
{
if (logEvent == null)
return;
var message = logEvent.RenderMessage(_formatProvider, false);
switch (logEvent.Level)
{
//case LogEventLevel.Verbose:
//case LogEventLevel.Debug:
case LogEventLevel.Information:
UnityEngine.Debug.Log(message);
break;
case LogEventLevel.Warning:
UnityEngine.Debug.LogWarning(message);
break;
case LogEventLevel.Error:
case LogEventLevel.Fatal:
UnityEngine.Debug.LogError(message);
break;
default:
break;
}
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0cc7f1339aeef54898503bd5cdc51fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: