mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
Initial package management support
This commit is contained in:
parent
78138f5c5a
commit
4870c6fdd8
19 changed files with 2520 additions and 205 deletions
|
@ -10,12 +10,22 @@ namespace BeefBuild
|
|||
{
|
||||
class BuildApp : IDEApp
|
||||
{
|
||||
public enum MainVerbState
|
||||
{
|
||||
None,
|
||||
UpdateList,
|
||||
End
|
||||
}
|
||||
|
||||
const int cProgressSize = 30;
|
||||
int mProgressIdx = 0;
|
||||
public bool mIsTest;
|
||||
public bool mTestIncludeIgnored;
|
||||
public bool mDidRun;
|
||||
public bool mWantsGenerate = false;
|
||||
public bool mHandledVerb;
|
||||
public String mRunArgs ~ delete _;
|
||||
MainVerbState mMainVerbState;
|
||||
|
||||
/*void Test()
|
||||
{
|
||||
|
@ -112,20 +122,33 @@ namespace BeefBuild
|
|||
OutputErrorLine("The project '{}' is not empty, but '-generate' was specified.", mWorkspace.mStartupProject.mProjectName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mFailed)
|
||||
{
|
||||
if (mIsTest)
|
||||
{
|
||||
RunTests(mTestIncludeIgnored, false);
|
||||
}
|
||||
else if (mVerb != .New)
|
||||
Compile(.Normal, null);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HandleCommandLineParam(String key, String value)
|
||||
{
|
||||
if (mRunArgs != null)
|
||||
{
|
||||
if (!mRunArgs.IsEmpty)
|
||||
mRunArgs.Append(" ");
|
||||
if (value != null)
|
||||
{
|
||||
String qKey = scope .(key);
|
||||
String qValue = scope .(value);
|
||||
IDEApp.QuoteIfNeeded(qKey);
|
||||
IDEApp.QuoteIfNeeded(qValue);
|
||||
mRunArgs.Append(qKey);
|
||||
mRunArgs.Append('=');
|
||||
mRunArgs.Append(qValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
String qKey = scope .(key);
|
||||
IDEApp.QuoteIfNeeded(qKey);
|
||||
mRunArgs.Append(qKey);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.StartsWith("--"))
|
||||
key.Remove(0, 1);
|
||||
|
||||
|
@ -133,6 +156,10 @@ namespace BeefBuild
|
|||
{
|
||||
switch (key)
|
||||
{
|
||||
case "-args":
|
||||
if (mRunArgs == null)
|
||||
mRunArgs = new .();
|
||||
return true;
|
||||
case "-new":
|
||||
mVerb = .New;
|
||||
return true;
|
||||
|
@ -157,12 +184,63 @@ namespace BeefBuild
|
|||
case "-noir":
|
||||
mConfig_NoIR = true;
|
||||
return true;
|
||||
case "-update":
|
||||
if (mWantUpdateVersionLocks == null)
|
||||
mWantUpdateVersionLocks = new .();
|
||||
return true;
|
||||
case "-version":
|
||||
mVerb = .GetVersion;
|
||||
return true;
|
||||
case "-crash":
|
||||
Runtime.FatalError("-crash specified on command line");
|
||||
}
|
||||
|
||||
if (!key.StartsWith('-'))
|
||||
{
|
||||
switch (mMainVerbState)
|
||||
{
|
||||
case .None:
|
||||
mMainVerbState = .End;
|
||||
switch (key)
|
||||
{
|
||||
case "build":
|
||||
mVerb = .None;
|
||||
case "new":
|
||||
mVerb = .New;
|
||||
case "generate":
|
||||
mWantsGenerate = true;
|
||||
case "run":
|
||||
if (mVerbosity == .Default)
|
||||
mVerbosity = .Minimal;
|
||||
mVerb = .Run;
|
||||
case "test":
|
||||
mIsTest = true;
|
||||
case "testall":
|
||||
mIsTest = true;
|
||||
mTestIncludeIgnored = true;
|
||||
case "clean":
|
||||
mWantsClean = true;
|
||||
case "version":
|
||||
mVerb = .GetVersion;
|
||||
case "crash":
|
||||
Runtime.FatalError("-crash specified on command line");
|
||||
case "update":
|
||||
mVerb = .Update;
|
||||
mWantUpdateVersionLocks = new .();
|
||||
mMainVerbState = .UpdateList;
|
||||
default:
|
||||
mMainVerbState = .None;
|
||||
}
|
||||
if (mMainVerbState != .None)
|
||||
return true;
|
||||
case .UpdateList:
|
||||
mWantUpdateVersionLocks.Add(new .(key));
|
||||
return true;
|
||||
case .End:
|
||||
return false;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -188,6 +266,11 @@ namespace BeefBuild
|
|||
case "-platform":
|
||||
mPlatformName.Set(value);
|
||||
return true;
|
||||
case "-update":
|
||||
if (mWantUpdateVersionLocks == null)
|
||||
mWantUpdateVersionLocks = new .();
|
||||
mWantUpdateVersionLocks.Add(new .(value));
|
||||
return true;
|
||||
case "-verbosity":
|
||||
if (value == "quiet")
|
||||
mVerbosity = .Quiet;
|
||||
|
@ -281,6 +364,27 @@ namespace BeefBuild
|
|||
{
|
||||
base.Update(batchStart);
|
||||
|
||||
if (mWorkspace.mProjectLoadState != .Loaded)
|
||||
{
|
||||
// Wait for workspace to complete loading
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((!mFailed) && (!mHandledVerb))
|
||||
{
|
||||
mHandledVerb = true;
|
||||
if (mIsTest)
|
||||
{
|
||||
RunTests(mTestIncludeIgnored, false);
|
||||
}
|
||||
else if (mVerb == .Update)
|
||||
{
|
||||
// No-op here
|
||||
}
|
||||
else if (mVerb != .New)
|
||||
Compile(.Normal, null);
|
||||
}
|
||||
|
||||
if (mCompilingBeef)
|
||||
{
|
||||
WriteProgress(mBfBuildCompiler.GetCompletionPercentage());
|
||||
|
@ -301,7 +405,7 @@ namespace BeefBuild
|
|||
if (targetPaths.IsEmpty)
|
||||
return;
|
||||
|
||||
ExecutionQueueCmd executionCmd = QueueRun(targetPaths[0], "", curPath);
|
||||
ExecutionQueueCmd executionCmd = QueueRun(targetPaths[0], mRunArgs ?? "", curPath);
|
||||
executionCmd.mIsTargetRun = true;
|
||||
mDidRun = true;
|
||||
return;
|
||||
|
@ -312,3 +416,4 @@ namespace BeefBuild
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace IDE
|
|||
List<String> mConfigPathQueue = new List<String>() ~ DeleteContainerAndItems!(_);
|
||||
List<LibDirectory> mLibDirectories = new List<LibDirectory>() ~ DeleteContainerAndItems!(_);
|
||||
List<FileSystemWatcher> mWatchers = new .() ~ DeleteContainerAndItems!(_);
|
||||
public String mManagedLibPath = new .() ~ delete _;
|
||||
public bool mLibsChanged;
|
||||
|
||||
void LibsChanged()
|
||||
|
@ -221,6 +222,15 @@ namespace IDE
|
|||
}
|
||||
}
|
||||
|
||||
data.GetString("ManagedLibDir", mManagedLibPath);
|
||||
if ((mManagedLibPath.IsEmpty) && (!mLibDirectories.IsEmpty))
|
||||
{
|
||||
var libPath = Path.GetAbsolutePath(mLibDirectories[0].mPath, gApp.mInstallDir, .. scope .());
|
||||
var managedPath = Path.GetAbsolutePath("../BeefManaged", libPath, .. scope .());
|
||||
if (Directory.Exists(managedPath))
|
||||
mManagedLibPath.Set(managedPath);
|
||||
}
|
||||
|
||||
mConfigFiles.Add(configFile);
|
||||
|
||||
return .Ok;
|
||||
|
|
|
@ -76,6 +76,7 @@ namespace IDE
|
|||
OpenOrNew,
|
||||
Test,
|
||||
Run,
|
||||
Update,
|
||||
GetVersion
|
||||
}
|
||||
|
||||
|
@ -182,6 +183,7 @@ namespace IDE
|
|||
public MainFrame mMainFrame;
|
||||
public GlobalUndoManager mGlobalUndoManager = new GlobalUndoManager() ~ delete _;
|
||||
public SourceControl mSourceControl = new SourceControl() ~ delete _;
|
||||
public GitManager mGitManager = new .() ~ delete _;
|
||||
|
||||
public WidgetWindow mPopupWindow;
|
||||
public RecentFileSelector mRecentFileSelector;
|
||||
|
@ -223,6 +225,7 @@ namespace IDE
|
|||
public WakaTime mWakaTime ~ delete _;
|
||||
|
||||
public PackMan mPackMan = new PackMan() ~ delete _;
|
||||
public HashSet<String> mWantUpdateVersionLocks ~ DeleteContainerAndItems!(_);
|
||||
public Settings mSettings = new Settings() ~ delete _;
|
||||
public Workspace mWorkspace = new Workspace() ~ delete _;
|
||||
public FileWatcher mFileWatcher = new FileWatcher() ~ delete _;
|
||||
|
@ -2145,6 +2148,91 @@ namespace IDE
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SaveWorkspaceLockData(bool force = false)
|
||||
{
|
||||
if ((mWorkspace.mProjectLockMap.IsEmpty) && (!force))
|
||||
return true;
|
||||
|
||||
StructuredData sd = scope StructuredData();
|
||||
sd.CreateNew();
|
||||
sd.Add("FileVersion", 1);
|
||||
using (sd.CreateObject("Locks"))
|
||||
{
|
||||
List<String> projectNames = scope .(mWorkspace.mProjectLockMap.Keys);
|
||||
projectNames.Sort();
|
||||
|
||||
for (var projectName in projectNames)
|
||||
{
|
||||
var lock = mWorkspace.mProjectLockMap[projectName];
|
||||
switch (lock)
|
||||
{
|
||||
case .Git(let url, let tag, let hash):
|
||||
using (sd.CreateObject(projectName))
|
||||
{
|
||||
using (sd.CreateObject("Git"))
|
||||
{
|
||||
sd.Add("URL", url);
|
||||
sd.Add("Tag", tag);
|
||||
sd.Add("Hash", hash);
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String jsonString = scope String();
|
||||
sd.ToTOML(jsonString);
|
||||
|
||||
String lockFileName = scope String();
|
||||
GetWorkspaceLockFileName(lockFileName);
|
||||
if (lockFileName.IsEmpty)
|
||||
return false;
|
||||
return SafeWriteTextFile(lockFileName, jsonString);
|
||||
}
|
||||
|
||||
bool LoadWorkspaceLockData()
|
||||
{
|
||||
String lockFilePath = scope String();
|
||||
GetWorkspaceLockFileName(lockFilePath);
|
||||
if (lockFilePath.IsEmpty)
|
||||
return true;
|
||||
|
||||
var sd = scope StructuredData();
|
||||
if (sd.Load(lockFilePath) case .Err)
|
||||
return false;
|
||||
|
||||
for (var projectName in sd.Enumerate("Locks"))
|
||||
{
|
||||
Workspace.Lock lock = default;
|
||||
if (sd.Contains("Git"))
|
||||
{
|
||||
using (sd.Open("Git"))
|
||||
{
|
||||
var url = sd.GetString("URL", .. new .());
|
||||
var tag = sd.GetString("Tag", .. new .());
|
||||
var hash = sd.GetString("Hash", .. new .());
|
||||
lock = .Git(url, tag, hash);
|
||||
}
|
||||
}
|
||||
|
||||
mWorkspace.SetLock(projectName, lock);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetWorkspaceLockFileName(String outResult)
|
||||
{
|
||||
if (mWorkspace.mDir == null)
|
||||
return false;
|
||||
if (mWorkspace.mCompositeFile != null)
|
||||
outResult.Append(mWorkspace.mCompositeFile.mFilePath, ".bfuser");
|
||||
else
|
||||
outResult.Append(mWorkspace.mDir, "/BeefSpace_Lock.toml");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetDefaultLayoutDataFileName(String outResult)
|
||||
{
|
||||
outResult.Append(mInstallDir, "/DefaultLayout.toml");
|
||||
|
@ -2377,6 +2465,17 @@ namespace IDE
|
|||
project.mDependencies.Add(dep);
|
||||
}
|
||||
|
||||
public void ProjectCreated(Project project)
|
||||
{
|
||||
mProjectPanel.InitProject(project, mProjectPanel.GetSelectedWorkspaceFolder());
|
||||
mProjectPanel.Sort();
|
||||
mWorkspace.FixOptions();
|
||||
mWorkspace.mHasChanged = true;
|
||||
|
||||
mWorkspace.ClearProjectNameCache();
|
||||
CurrentWorkspaceConfigChanged();
|
||||
}
|
||||
|
||||
public Project CreateProject(String projName, String projDir, Project.TargetType targetType)
|
||||
{
|
||||
Project project = new Project();
|
||||
|
@ -2391,13 +2490,8 @@ namespace IDE
|
|||
AddNewProjectToWorkspace(project);
|
||||
project.FinishCreate();
|
||||
|
||||
mProjectPanel.InitProject(project, mProjectPanel.GetSelectedWorkspaceFolder());
|
||||
mProjectPanel.Sort();
|
||||
mWorkspace.FixOptions();
|
||||
mWorkspace.mHasChanged = true;
|
||||
ProjectCreated(project);
|
||||
|
||||
mWorkspace.ClearProjectNameCache();
|
||||
CurrentWorkspaceConfigChanged();
|
||||
return project;
|
||||
}
|
||||
|
||||
|
@ -2517,6 +2611,8 @@ namespace IDE
|
|||
|
||||
mBookmarkManager.Clear();
|
||||
|
||||
mPackMan.CancelAll();
|
||||
|
||||
OutputLine("Workspace closed.");
|
||||
}
|
||||
|
||||
|
@ -2757,9 +2853,16 @@ namespace IDE
|
|||
return .Err;
|
||||
}
|
||||
|
||||
public void CheckDependenciesLoaded()
|
||||
{
|
||||
for (var project in mWorkspace.mProjects)
|
||||
project.CheckDependenciesLoaded();
|
||||
}
|
||||
|
||||
void FlushDeferredLoadProjects(bool addToUI = false)
|
||||
{
|
||||
bool hasDeferredProjects = false;
|
||||
bool loadFailed = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -2767,11 +2870,29 @@ namespace IDE
|
|||
for (int projectIdx = 0; projectIdx < mWorkspace.mProjects.Count; projectIdx++)
|
||||
{
|
||||
var project = mWorkspace.mProjects[projectIdx];
|
||||
|
||||
if (project.mDeferState == .Searching)
|
||||
{
|
||||
if (mPackMan.mFailed)
|
||||
{
|
||||
// Just let it fail now
|
||||
LoadFailed();
|
||||
project.mDeferState = .None;
|
||||
project.mFailed = true;
|
||||
loadFailed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasDeferredProjects = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((project.mDeferState == .ReadyToLoad) || (project.mDeferState == .Pending))
|
||||
{
|
||||
hadLoad = true;
|
||||
|
||||
var projectPath = project.mProjectPath;
|
||||
|
||||
if (project.mDeferState == .Pending)
|
||||
{
|
||||
hasDeferredProjects = true;
|
||||
|
@ -2794,9 +2915,26 @@ namespace IDE
|
|||
}
|
||||
|
||||
if (hasDeferredProjects)
|
||||
{
|
||||
mWorkspace.mProjectLoadState = .Preparing;
|
||||
}
|
||||
else
|
||||
{
|
||||
mWorkspace.mProjectLoadState = .Loaded;
|
||||
SaveWorkspaceLockData();
|
||||
CheckDependenciesLoaded();
|
||||
}
|
||||
|
||||
if (loadFailed)
|
||||
{
|
||||
mProjectPanel.RebuildUI();
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelWorkspaceLoading()
|
||||
{
|
||||
mPackMan.CancelAll();
|
||||
FlushDeferredLoadProjects();
|
||||
}
|
||||
|
||||
protected void LoadWorkspace(BeefVerb verb)
|
||||
|
@ -2937,6 +3075,7 @@ namespace IDE
|
|||
}
|
||||
else
|
||||
{
|
||||
LoadWorkspaceLockData();
|
||||
mWorkspace.mProjectFileEntries.Add(new .(workspaceFileName));
|
||||
|
||||
if (mVerb == .New)
|
||||
|
@ -3044,8 +3183,9 @@ namespace IDE
|
|||
outRelaunchCmd.Append(" -safe");
|
||||
}
|
||||
|
||||
public void RetryProjectLoad(Project project)
|
||||
public void RetryProjectLoad(Project project, bool reloadConfig)
|
||||
{
|
||||
if (reloadConfig)
|
||||
LoadConfig();
|
||||
|
||||
var projectPath = project.mProjectPath;
|
||||
|
@ -3054,6 +3194,8 @@ namespace IDE
|
|||
Fail(scope String()..AppendF("Failed to load project '{0}' from '{1}'", project.mProjectName, projectPath));
|
||||
LoadFailed();
|
||||
project.mFailed = true;
|
||||
FlushDeferredLoadProjects();
|
||||
mProjectPanel?.RebuildUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3061,7 +3203,7 @@ namespace IDE
|
|||
mWorkspace.FixOptions();
|
||||
|
||||
project.mFailed = false;
|
||||
mProjectPanel.RebuildUI();
|
||||
mProjectPanel?.RebuildUI();
|
||||
CurrentWorkspaceConfigChanged();
|
||||
}
|
||||
}
|
||||
|
@ -3080,7 +3222,17 @@ namespace IDE
|
|||
String verConfigDir = mWorkspace.mDir;
|
||||
|
||||
if (let project = mWorkspace.FindProject(projectName))
|
||||
{
|
||||
switch (useVerSpec)
|
||||
{
|
||||
case .Git(let url, let ver):
|
||||
if (ver != null)
|
||||
mPackMan.UpdateGitConstraint(url, ver);
|
||||
default:
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
if (useVerSpec case .SemVer)
|
||||
{
|
||||
|
@ -3153,10 +3305,6 @@ namespace IDE
|
|||
case .SemVer(let semVer):
|
||||
//
|
||||
case .Git(let url, let ver):
|
||||
var verReference = new Project.VerReference();
|
||||
verReference.mSrcProjectName = new String(projectName);
|
||||
verReference.mVerSpec = _.Duplicate();
|
||||
project.mVerReferences.Add(verReference);
|
||||
|
||||
var checkPath = scope String();
|
||||
if (mPackMan.CheckLock(projectName, checkPath))
|
||||
|
@ -3164,16 +3312,17 @@ namespace IDE
|
|||
projectFilePath = scope:: String(checkPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
mPackMan.GetWithVersion(projectName, url, ver);
|
||||
isDeferredLoad = true;
|
||||
}
|
||||
default:
|
||||
Fail("Invalid version specifier");
|
||||
return .Err(.InvalidVersionSpec);
|
||||
}
|
||||
|
||||
if ((projectFilePath == null) && (!isDeferredLoad))
|
||||
{
|
||||
return .Err(.NotFound);
|
||||
}
|
||||
|
||||
if (isDeferredLoad)
|
||||
{
|
||||
|
@ -3197,6 +3346,59 @@ namespace IDE
|
|||
return .Ok(project);
|
||||
}
|
||||
|
||||
public void UpdateProjectVersionLocks(params Span<StringView> projectNames)
|
||||
{
|
||||
bool removedLock = false;
|
||||
|
||||
for (var projectName in projectNames)
|
||||
{
|
||||
if (var kv = gApp.mWorkspace.mProjectLockMap.GetAndRemoveAlt(projectName))
|
||||
{
|
||||
removedLock = true;
|
||||
delete kv.key;
|
||||
kv.value.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (removedLock)
|
||||
{
|
||||
if (SaveAll())
|
||||
{
|
||||
SaveWorkspaceLockData(true);
|
||||
CloseOldBeefManaged();
|
||||
ReloadWorkspace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateProjectVersionLocks(Span<String> projectNames)
|
||||
{
|
||||
List<StringView> svNames = scope .();
|
||||
for (var name in projectNames)
|
||||
svNames.Add(name);
|
||||
UpdateProjectVersionLocks(params (Span<StringView>)svNames);
|
||||
}
|
||||
|
||||
public void NotifyProjectVersionLocks(Span<String> projectNames)
|
||||
{
|
||||
if (projectNames.IsEmpty)
|
||||
return;
|
||||
|
||||
String message = scope .();
|
||||
message.Append((projectNames.Length == 1) ? "Project " : "Projects ");
|
||||
for (var projectName in projectNames)
|
||||
{
|
||||
if (@projectName.Index > 0)
|
||||
message.Append(", ");
|
||||
message.AppendF($"'{projectName}'");
|
||||
}
|
||||
|
||||
message.Append((projectNames.Length == 1) ? " has " : " have ");
|
||||
|
||||
message.AppendF("modified version constraints. Use 'Update Version Lock' in the project or workspace right-click menus to apply the new constraints.");
|
||||
MessageDialog("Version Constraints Modified", message, DarkTheme.sDarkTheme.mIconWarning);
|
||||
}
|
||||
|
||||
protected void WorkspaceLoaded()
|
||||
{
|
||||
scope AutoBeefPerf("IDE.WorkspaceLoaded");
|
||||
|
@ -3848,9 +4050,9 @@ namespace IDE
|
|||
return dialog;
|
||||
}
|
||||
|
||||
public void MessageDialog(String title, String text)
|
||||
public void MessageDialog(String title, String text, Image icon = null)
|
||||
{
|
||||
Dialog dialog = ThemeFactory.mDefault.CreateDialog(title, text);
|
||||
Dialog dialog = ThemeFactory.mDefault.CreateDialog(title, text, icon);
|
||||
dialog.mDefaultButton = dialog.AddButton("OK");
|
||||
dialog.mEscButton = dialog.mDefaultButton;
|
||||
dialog.PopupWindow(mMainWindow);
|
||||
|
@ -7433,6 +7635,37 @@ namespace IDE
|
|||
CloseDocument(activeDocumentPanel);
|
||||
}
|
||||
|
||||
public void CloseOldBeefManaged()
|
||||
{
|
||||
List<SourceViewPanel> pendingClosePanels = scope .();
|
||||
WithSourceViewPanels(scope (sourceViewPanel) =>
|
||||
{
|
||||
if (sourceViewPanel.mProjectSource != null)
|
||||
{
|
||||
var checkHash = gApp.mPackMan.GetHashFromFilePath(sourceViewPanel.mFilePath, .. scope .());
|
||||
if (!checkHash.IsEmpty)
|
||||
{
|
||||
bool foundHash = false;
|
||||
|
||||
if (gApp.mWorkspace.mProjectLockMap.TryGet(sourceViewPanel.mProjectSource.mProject.mProjectName, ?, var lock))
|
||||
{
|
||||
if (lock case .Git(let url, let tag, let hash))
|
||||
{
|
||||
if (hash == checkHash)
|
||||
foundHash = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundHash)
|
||||
pendingClosePanels.Add(sourceViewPanel);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (var sourceViewPanel in pendingClosePanels)
|
||||
CloseDocument(sourceViewPanel);
|
||||
}
|
||||
|
||||
public SourceViewPanel ShowProjectItem(ProjectItem projectItem, bool showTemp = true, bool setFocus = true)
|
||||
{
|
||||
if (projectItem is ProjectSource)
|
||||
|
@ -7845,7 +8078,7 @@ namespace IDE
|
|||
case "-autoshutdown":
|
||||
mDebugAutoShutdownCounter = 200;
|
||||
case "-new":
|
||||
mVerb = .New;
|
||||
mVerb = .Open;
|
||||
case "-testNoExit":
|
||||
mExitWhenTestScriptDone = false;
|
||||
case "-firstRun":
|
||||
|
@ -8090,15 +8323,16 @@ namespace IDE
|
|||
#endif
|
||||
}
|
||||
|
||||
public virtual void OutputErrorLine(String format, params Object[] args)
|
||||
public virtual void OutputErrorLine(StringView format, params Object[] args)
|
||||
{
|
||||
mWantShowOutput = true;
|
||||
var errStr = scope String();
|
||||
errStr.Append("ERROR: ", format);
|
||||
errStr.Append("ERROR: ");
|
||||
errStr.Append(format);
|
||||
OutputLineSmart(errStr, params args);
|
||||
}
|
||||
|
||||
public virtual void OutputWarnLine(String format, params Object[] args)
|
||||
public virtual void OutputWarnLine(StringView format, params Object[] args)
|
||||
{
|
||||
var warnStr = scope String();
|
||||
warnStr.AppendF(format, params args);
|
||||
|
@ -8113,7 +8347,7 @@ namespace IDE
|
|||
OutputLine(outStr);
|
||||
}
|
||||
|
||||
public virtual void OutputLineSmart(String format, params Object[] args)
|
||||
public virtual void OutputLineSmart(StringView format, params Object[] args)
|
||||
{
|
||||
String outStr;
|
||||
if (args.Count > 0)
|
||||
|
@ -10027,7 +10261,7 @@ namespace IDE
|
|||
#endif
|
||||
}
|
||||
mWorkspace.ClearProjectNameCache();
|
||||
mProjectPanel.RehupProjects();
|
||||
mProjectPanel?.RehupProjects();
|
||||
}
|
||||
|
||||
/*public string GetClangDepConfigName(Project project)
|
||||
|
@ -10064,8 +10298,8 @@ namespace IDE
|
|||
RemoveProjectItems(project);
|
||||
}
|
||||
|
||||
mBfResolveCompiler.QueueDeferredResolveAll();
|
||||
mBfResolveCompiler.QueueRefreshViewCommand(.FullRefresh);
|
||||
mBfResolveCompiler?.QueueDeferredResolveAll();
|
||||
mBfResolveCompiler?.QueueRefreshViewCommand(.FullRefresh);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10093,11 +10327,14 @@ namespace IDE
|
|||
{
|
||||
mWantsBeefClean = true;
|
||||
|
||||
var checkDeclName = (project.mProjectName !== project.mProjectNameDecl) && (mWorkspace.FindProject(project.mProjectNameDecl) == project);
|
||||
|
||||
for (var checkProject in mWorkspace.mProjects)
|
||||
{
|
||||
for (var dep in checkProject.mDependencies)
|
||||
{
|
||||
if (dep.mProjectName == project.mProjectName)
|
||||
if ((dep.mProjectName == project.mProjectName) ||
|
||||
((checkDeclName) && (dep.mProjectName == project.mProjectNameDecl)))
|
||||
{
|
||||
dep.mProjectName.Set(newName);
|
||||
checkProject.SetChanged();
|
||||
|
@ -10115,14 +10352,27 @@ namespace IDE
|
|||
}
|
||||
|
||||
project.mProjectName.Set(newName);
|
||||
if (project.mProjectNameDecl != project.mProjectName)
|
||||
delete project.mProjectNameDecl;
|
||||
project.mProjectNameDecl = project.mProjectName;
|
||||
project.SetChanged();
|
||||
mWorkspace.ClearProjectNameCache();
|
||||
|
||||
mProjectPanel.RebuildUI();
|
||||
}
|
||||
|
||||
public void RemoveProject(Project project)
|
||||
{
|
||||
RemoveProjectItems(project);
|
||||
|
||||
if (mWorkspace.mProjectLockMap.GetAndRemove(project.mProjectName) case .Ok(let kv))
|
||||
{
|
||||
delete kv.key;
|
||||
kv.value.Dispose();
|
||||
if (mWorkspace.mProjectLockMap.IsEmpty)
|
||||
SaveWorkspaceLockData(true);
|
||||
}
|
||||
|
||||
project.mDeleted = true;
|
||||
mWorkspace.SetChanged();
|
||||
mWorkspace.mProjects.Remove(project);
|
||||
|
@ -10911,7 +11161,7 @@ namespace IDE
|
|||
}
|
||||
}
|
||||
|
||||
static void QuoteIfNeeded(String str)
|
||||
protected static void QuoteIfNeeded(String str)
|
||||
{
|
||||
if (!str.Contains(' '))
|
||||
return;
|
||||
|
@ -12398,6 +12648,8 @@ namespace IDE
|
|||
base.Init();
|
||||
mSettings.Apply();
|
||||
|
||||
mGitManager.Init();
|
||||
|
||||
//Yoop();
|
||||
|
||||
/*for (int i = 0; i < 100*1024*1024; i++)
|
||||
|
@ -14865,6 +15117,8 @@ namespace IDE
|
|||
if (mLongUpdateProfileId != 0)
|
||||
DoLongUpdateCheck();
|
||||
|
||||
mGitManager.Update();
|
||||
mPackMan.Update();
|
||||
if (mWakaTime != null)
|
||||
mWakaTime.Update();
|
||||
if (mFindResultsPanel != null)
|
||||
|
@ -15053,7 +15307,6 @@ namespace IDE
|
|||
[Import("user32.lib"), CLink, CallingConvention(.Stdcall)]
|
||||
public static extern bool MessageBeep(MessageBeepType type);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
|
|
|
@ -9,6 +9,7 @@ using Beefy;
|
|||
using Beefy.gfx;
|
||||
using Beefy.theme.dark;
|
||||
using IDE.ui;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace IDE
|
||||
{
|
||||
|
@ -128,6 +129,29 @@ namespace IDE
|
|||
return true;
|
||||
}
|
||||
|
||||
public static void SafeKill(int processId)
|
||||
{
|
||||
var beefConExe = scope $"{gApp.mInstallDir}/BeefCon.exe";
|
||||
|
||||
ProcessStartInfo procInfo = scope ProcessStartInfo();
|
||||
procInfo.UseShellExecute = false;
|
||||
procInfo.SetFileName(beefConExe);
|
||||
procInfo.SetArguments(scope $"{processId} kill");
|
||||
procInfo.ActivateWindow = false;
|
||||
|
||||
var process = scope SpawnedProcess();
|
||||
process.Start(procInfo).IgnoreError();
|
||||
}
|
||||
|
||||
public static void SafeKill(SpawnedProcess process)
|
||||
{
|
||||
if (process.WaitFor(0))
|
||||
return;
|
||||
SafeKill(process.ProcessId);
|
||||
if (!process.WaitFor(2000))
|
||||
process.Kill();
|
||||
}
|
||||
|
||||
public static bool IsDirectoryEmpty(StringView dirPath)
|
||||
{
|
||||
for (let entry in Directory.Enumerate(scope String()..AppendF("{}/*.*", dirPath), .Directories | .Files))
|
||||
|
|
|
@ -1091,9 +1091,13 @@ namespace IDE
|
|||
public class GeneralOptions
|
||||
{
|
||||
[Reflect]
|
||||
public String mProjectNameDecl; // Points to mProjectNameDecl in Project
|
||||
[Reflect]
|
||||
public TargetType mTargetType;
|
||||
[Reflect]
|
||||
public List<String> mAliases = new .() ~ DeleteContainerAndItems!(_);
|
||||
[Reflect]
|
||||
public SemVer mVersion = new SemVer("") ~ delete _;
|
||||
}
|
||||
|
||||
public class BeefGlobalOptions
|
||||
|
@ -1321,6 +1325,7 @@ namespace IDE
|
|||
{
|
||||
public VerSpec mVerSpec ~ _.Dispose();
|
||||
public String mProjectName ~ delete _;
|
||||
public bool mDependencyChecked;
|
||||
}
|
||||
|
||||
public enum DeferState
|
||||
|
@ -1337,13 +1342,20 @@ namespace IDE
|
|||
public VerSpec mVerSpec ~ _.Dispose();
|
||||
}
|
||||
|
||||
public class ManagedInfo
|
||||
{
|
||||
public SemVer mVersion = new .("") ~ delete _;
|
||||
public String mInfo ~ delete _;
|
||||
}
|
||||
|
||||
public Monitor mMonitor = new Monitor() ~ delete _;
|
||||
public String mNamespace = new String() ~ delete _;
|
||||
public String mProjectDir = new String() ~ delete _;
|
||||
public String mProjectName = new String() ~ delete _;
|
||||
public String mProjectNameDecl = mProjectName ~ { if (mProjectNameDecl != mProjectName) delete _; }
|
||||
public String mProjectPath = new String() ~ delete _;
|
||||
public ManagedInfo mManagedInfo ~ delete _;
|
||||
public DeferState mDeferState;
|
||||
public List<VerReference> mVerReferences = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
//public String mLastImportDir = new String() ~ delete _;
|
||||
public bool mHasChanged;
|
||||
|
@ -1410,6 +1422,16 @@ namespace IDE
|
|||
}
|
||||
}
|
||||
|
||||
public SemVer Version
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mManagedInfo != null)
|
||||
return mManagedInfo.mVersion;
|
||||
return mGeneralOptions.mVersion;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupDefaultOptions(Options options)
|
||||
{
|
||||
options.mBuildOptions.mOtherLinkFlags.Set("$(LinkFlags)");
|
||||
|
@ -1443,6 +1465,7 @@ namespace IDE
|
|||
mGeneralOptions.mTargetType = .CustomBuild;
|
||||
SetupDefaultConfigs();
|
||||
|
||||
mGeneralOptions.mProjectNameDecl = mProjectNameDecl;
|
||||
mBeefGlobalOptions.mStartupObject.Set("Program");
|
||||
}
|
||||
|
||||
|
@ -1526,6 +1549,21 @@ namespace IDE
|
|||
Path.GetDirectoryPath(mProjectPath, mProjectDir);
|
||||
if (structuredData.Load(ProjectFileName) case .Err)
|
||||
return false;
|
||||
|
||||
String managedText = scope .();
|
||||
if (File.ReadAllText(scope $"{mProjectDir}/BeefManaged.toml", managedText) case .Ok)
|
||||
{
|
||||
mManagedInfo = new .();
|
||||
mManagedInfo.mInfo = new .(managedText);
|
||||
|
||||
StructuredData msd = scope .();
|
||||
if (msd.LoadFromString(managedText) case .Ok)
|
||||
{
|
||||
if (mManagedInfo.mVersion.mVersion == null)
|
||||
mManagedInfo.mVersion.mVersion = new .();
|
||||
msd.GetString("Version", mManagedInfo.mVersion.mVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1615,7 +1653,8 @@ namespace IDE
|
|||
using (data.CreateObject("Project"))
|
||||
{
|
||||
if (!IsSingleFile)
|
||||
data.Add("Name", mProjectName);
|
||||
data.Add("Name", mProjectNameDecl);
|
||||
data.ConditionalAdd("Version", mGeneralOptions.mVersion.mVersion);
|
||||
data.ConditionalAdd("TargetType", mGeneralOptions.mTargetType, GetDefaultTargetType());
|
||||
data.ConditionalAdd("StartupObject", mBeefGlobalOptions.mStartupObject, IsSingleFile ? "Program" : "");
|
||||
var defaultNamespace = scope String();
|
||||
|
@ -1955,7 +1994,24 @@ namespace IDE
|
|||
using (data.Open("Project"))
|
||||
{
|
||||
if (!IsSingleFile)
|
||||
data.GetString("Name", mProjectName);
|
||||
{
|
||||
var projectName = data.GetString("Name", .. scope .());
|
||||
if ((!mProjectName.IsEmpty) && (projectName != mProjectName))
|
||||
{
|
||||
// If the name we specified clashes with the delclared project name in the config
|
||||
if (mProjectNameDecl === mProjectName)
|
||||
{
|
||||
mProjectNameDecl = new .(projectName);
|
||||
mGeneralOptions.mProjectNameDecl = mProjectNameDecl;
|
||||
gApp.mWorkspace.ClearProjectNameCache();
|
||||
}
|
||||
else
|
||||
mProjectNameDecl.Set(projectName);
|
||||
}
|
||||
else
|
||||
mProjectName.Set(projectName);
|
||||
}
|
||||
data.GetString("Version", mGeneralOptions.mVersion.mVersion);
|
||||
ReadStrings("Aliases", mGeneralOptions.mAliases);
|
||||
data.GetString("StartupObject", mBeefGlobalOptions.mStartupObject, IsSingleFile ? "Program" : "");
|
||||
var defaultNamespace = scope String();
|
||||
|
@ -2029,7 +2085,7 @@ namespace IDE
|
|||
{
|
||||
case .Ok(let project):
|
||||
case .Err(let err):
|
||||
gApp.OutputLineSmart("ERROR: Unable to load project '{0}' specified in project '{1}'", dep.mProjectName, mProjectName);
|
||||
// Give an error later
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2232,6 +2288,35 @@ namespace IDE
|
|||
mRootFolder.StartWatching();
|
||||
}
|
||||
|
||||
public void CheckDependenciesLoaded()
|
||||
{
|
||||
for (var dep in mDependencies)
|
||||
{
|
||||
if (!dep.mDependencyChecked)
|
||||
{
|
||||
var project = gApp.mWorkspace.FindProject(dep.mProjectName);
|
||||
if (project != null)
|
||||
{
|
||||
var projectVersion = project.Version;
|
||||
if (!projectVersion.mVersion.IsEmpty)
|
||||
{
|
||||
if (dep.mVerSpec case .Git(let url, let ver))
|
||||
{
|
||||
if (!SemVer.IsVersionMatch(projectVersion, ver))
|
||||
gApp.OutputLineSmart($"WARNING: Project '{mProjectName}' has version constraint '{ver}' for '{dep.mProjectName}' which is not satisfied by selected version '{projectVersion}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gApp.OutputLineSmart("ERROR: Unable to load project '{0}' specified in project '{1}'", dep.mProjectName, mProjectName);
|
||||
}
|
||||
|
||||
dep.mDependencyChecked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FinishCreate(bool allowCreateDir = true)
|
||||
{
|
||||
if (!mRootFolder.mIsWatching)
|
||||
|
@ -2414,29 +2499,35 @@ namespace IDE
|
|||
}
|
||||
}
|
||||
|
||||
public bool HasDependency(String projectName, bool checkRecursively = true)
|
||||
public VerSpec* GetDependency(String projectName, bool checkRecursively = true)
|
||||
{
|
||||
HashSet<Project> checkedProject = scope .();
|
||||
|
||||
bool CheckDependency(Project project)
|
||||
VerSpec* CheckDependency(Project project)
|
||||
{
|
||||
if (!checkedProject.Add(project))
|
||||
return false;
|
||||
return null;
|
||||
|
||||
for (var dependency in project.mDependencies)
|
||||
{
|
||||
if (dependency.mProjectName == projectName)
|
||||
return true;
|
||||
return &dependency.mVerSpec;
|
||||
let depProject = gApp.mWorkspace.FindProject(dependency.mProjectName);
|
||||
if ((depProject != null) && (checkRecursively) && (CheckDependency(depProject)))
|
||||
return true;
|
||||
if ((depProject != null) && (checkRecursively))
|
||||
{
|
||||
var verSpec = CheckDependency(depProject);
|
||||
if (verSpec != null)
|
||||
return verSpec;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return CheckDependency(this);
|
||||
}
|
||||
|
||||
public bool HasDependency(String projectName, bool checkRecursively = true) => GetDependency(projectName, checkRecursively) != null;
|
||||
|
||||
public void SetupDefault(Options options, String configName, String platformName)
|
||||
{
|
||||
bool isRelease = configName.Contains("Release");
|
||||
|
|
|
@ -223,7 +223,8 @@ namespace IDE
|
|||
None,
|
||||
Loaded,
|
||||
ReadyToLoad,
|
||||
Preparing
|
||||
Preparing,
|
||||
Failed
|
||||
}
|
||||
|
||||
public class ConfigSelection : IHashable, IEquatable
|
||||
|
@ -488,12 +489,11 @@ namespace IDE
|
|||
public VerSpec mVerSpec ~ _.Dispose();
|
||||
}
|
||||
|
||||
public class Lock
|
||||
{
|
||||
public enum Location
|
||||
public enum Lock
|
||||
{
|
||||
case Cache;
|
||||
case Local(String path);
|
||||
case Git(String url, String tag, String hash);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
@ -502,14 +502,14 @@ namespace IDE
|
|||
case .Cache:
|
||||
case .Local(let path):
|
||||
delete path;
|
||||
case Git(var url, var tag, var hash):
|
||||
delete url;
|
||||
delete tag;
|
||||
delete hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String mVersion ~ delete _;
|
||||
public Location mLocation ~ _.Dispose();
|
||||
}
|
||||
|
||||
public Monitor mMonitor = new Monitor() ~ delete _;
|
||||
public String mName ~ delete _;
|
||||
public String mDir ~ delete _;
|
||||
|
@ -519,7 +519,15 @@ namespace IDE
|
|||
public List<ProjectSpec> mProjectSpecs = new .() ~ DeleteContainerAndItems!(_);
|
||||
public List<ProjectFileEntry> mProjectFileEntries = new .() ~ DeleteContainerAndItems!(_);
|
||||
public Dictionary<String, Project> mProjectNameMap = new .() ~ DeleteDictionaryAndKeys!(_);
|
||||
public Dictionary<String, Lock> mProjectLockMap = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
|
||||
public Dictionary<String, Lock> mProjectLockMap = new .() ~
|
||||
{
|
||||
for (var kv in ref _)
|
||||
{
|
||||
delete kv.key;
|
||||
kv.valueRef.Dispose();
|
||||
}
|
||||
delete _;
|
||||
}
|
||||
public Project mStartupProject;
|
||||
public bool mLoading;
|
||||
public bool mNeedsCreate;
|
||||
|
@ -578,6 +586,20 @@ namespace IDE
|
|||
ClearAndDeleteItems(mPlatforms);
|
||||
}
|
||||
|
||||
public void SetLock(StringView projectName, Lock lock)
|
||||
{
|
||||
if (mProjectLockMap.TryAddAlt(projectName, var keyPtr, var valuePtr))
|
||||
{
|
||||
*keyPtr = new .(projectName);
|
||||
*valuePtr = lock;
|
||||
}
|
||||
else
|
||||
{
|
||||
valuePtr.Dispose();
|
||||
*valuePtr = lock;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetPlatformList(List<String> outList)
|
||||
{
|
||||
if (mPlatforms.IsEmpty)
|
||||
|
@ -947,17 +969,34 @@ namespace IDE
|
|||
{
|
||||
using (mMonitor.Enter())
|
||||
{
|
||||
int GetNamePriority(StringView name, Project project)
|
||||
{
|
||||
if (project.mProjectName == name)
|
||||
return 2;
|
||||
if (project.mProjectNameDecl == name)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Add(String name, Project project)
|
||||
{
|
||||
bool added = mProjectNameMap.TryAdd(name, var keyPtr, var valuePtr);
|
||||
if (!added)
|
||||
return;
|
||||
if (mProjectNameMap.TryAdd(name, var keyPtr, var valuePtr))
|
||||
{
|
||||
*keyPtr = new String(name);
|
||||
*valuePtr = project;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetNamePriority(name, project) > GetNamePriority(*keyPtr, *valuePtr))
|
||||
*valuePtr = project;
|
||||
}
|
||||
}
|
||||
|
||||
Add(project.mProjectName, project);
|
||||
|
||||
if (project.mProjectNameDecl !== project.mProjectName)
|
||||
Add(project.mProjectNameDecl, project);
|
||||
|
||||
for (var alias in project.mGeneralOptions.mAliases)
|
||||
Add(alias, project);
|
||||
}
|
||||
|
@ -979,7 +1018,11 @@ namespace IDE
|
|||
}
|
||||
|
||||
for (var project in mProjects)
|
||||
{
|
||||
Add(project.mProjectName, project);
|
||||
if (project.mProjectName != project.mProjectNameDecl)
|
||||
Add(project.mProjectNameDecl, project);
|
||||
}
|
||||
|
||||
for (var project in mProjects)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,87 @@ namespace IDE.ui
|
|||
|
||||
class BuildPropertiesDialog : TargetedPropertiesDialog
|
||||
{
|
||||
protected class DependencyEntry : IEquatable, IMultiValued
|
||||
{
|
||||
public bool mUse;
|
||||
public String mURL ~ delete _;
|
||||
public String mVersion ~ delete _;
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
}
|
||||
|
||||
public this(DependencyEntry val)
|
||||
{
|
||||
mUse = val.mUse;
|
||||
if (val.mURL != null)
|
||||
mURL = new .(val.mURL);
|
||||
if (val.mVersion != null)
|
||||
mVersion = new .(val.mVersion);
|
||||
}
|
||||
|
||||
public bool Equals(Object val)
|
||||
{
|
||||
if (var rhsDE = val as DependencyEntry)
|
||||
{
|
||||
return
|
||||
(mUse == rhsDE.mUse) &&
|
||||
(mURL == rhsDE.mURL) &&
|
||||
(mVersion == rhsDE.mVersion);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void GetValue(int idx, String outValue)
|
||||
{
|
||||
if ((idx == 1) && (mURL != null))
|
||||
outValue.Set(mURL);
|
||||
if ((idx == 2) && (mVersion != null))
|
||||
outValue.Set(mVersion);
|
||||
}
|
||||
|
||||
public bool SetValue(int idx, StringView value)
|
||||
{
|
||||
if (idx == 1)
|
||||
{
|
||||
if (value.IsEmpty)
|
||||
{
|
||||
DeleteAndNullify!(mURL);
|
||||
}
|
||||
else
|
||||
{
|
||||
String.NewOrSet!(mURL, value);
|
||||
mURL.Trim();
|
||||
}
|
||||
}
|
||||
if (idx == 2)
|
||||
{
|
||||
if (value.IsEmpty)
|
||||
{
|
||||
DeleteAndNullify!(mVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
String.NewOrSet!(mVersion, value);
|
||||
mVersion.Trim();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Set(DependencyEntry value)
|
||||
{
|
||||
mUse = value.mUse;
|
||||
SetValue(1, value.mURL);
|
||||
SetValue(2, value.mVersion);
|
||||
}
|
||||
}
|
||||
|
||||
protected class DistinctOptionBuilder
|
||||
{
|
||||
BuildPropertiesDialog mDialog;
|
||||
|
|
|
@ -145,6 +145,7 @@ namespace IDE.ui
|
|||
bool mImportFolderDeferred;
|
||||
bool mImportProjectDeferred;
|
||||
bool mImportInstalledDeferred;
|
||||
bool mImportRemoteDeferred;
|
||||
public Dictionary<ListViewItem, ProjectItem> mListViewToProjectMap = new .() ~ delete _;
|
||||
public Dictionary<ProjectItem, ProjectListViewItem> mProjectToListViewMap = new .() ~ delete _;
|
||||
public Dictionary<ListViewItem, WorkspaceFolder> mListViewToWorkspaceFolderMap = new .() ~ delete _;
|
||||
|
@ -2904,6 +2905,15 @@ namespace IDE.ui
|
|||
#endif
|
||||
}
|
||||
|
||||
void ImportRemoteProject()
|
||||
{
|
||||
#if !CLI
|
||||
RemoteProjectDialog dialog = new .();
|
||||
dialog.Init();
|
||||
dialog.PopupWindow(gApp.mMainWindow);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ShowProjectProperties(Project project)
|
||||
{
|
||||
var projectProperties = new ProjectProperties(project);
|
||||
|
@ -3081,6 +3091,14 @@ namespace IDE.ui
|
|||
});
|
||||
if (gApp.IsCompiling)
|
||||
anItem.SetDisabled(true);
|
||||
|
||||
anItem = menu.AddItem("Add From Remote...");
|
||||
anItem.mOnMenuItemSelected.Add(new (item) => {
|
||||
mImportRemoteDeferred = true;
|
||||
});
|
||||
if (gApp.IsCompiling)
|
||||
anItem.SetDisabled(true);
|
||||
|
||||
anItem = menu.AddItem("New Folder");
|
||||
anItem.mOnMenuItemSelected.Add(new (item) => {
|
||||
var workspaceFolder = GetSelectedWorkspaceFolder();
|
||||
|
@ -3110,7 +3128,18 @@ namespace IDE.ui
|
|||
}
|
||||
else if (gApp.mWorkspace.IsInitialized)
|
||||
{
|
||||
var item = menu.AddItem("Update Version Locks");
|
||||
item.mDisabled = gApp.mWorkspace.mProjectLockMap.IsEmpty;
|
||||
item.mOnMenuItemSelected.Add(new (item) =>
|
||||
{
|
||||
List<StringView> projectNames = scope .();
|
||||
for (var projectName in gApp.mWorkspace.mProjectLockMap.Keys)
|
||||
projectNames.Add(projectName);
|
||||
gApp.UpdateProjectVersionLocks(params (Span<StringView>)projectNames);
|
||||
});
|
||||
|
||||
AddOpenContainingFolder();
|
||||
|
||||
menu.AddItem();
|
||||
|
||||
AddWorkspaceMenuItems();
|
||||
|
@ -3140,7 +3169,7 @@ namespace IDE.ui
|
|||
{
|
||||
var projectItem = GetSelectedProjectItem();
|
||||
if (projectItem != null)
|
||||
gApp.RetryProjectLoad(projectItem.mProject);
|
||||
gApp.RetryProjectLoad(projectItem.mProject, true);
|
||||
});
|
||||
menu.AddItem();
|
||||
//handled = true;
|
||||
|
@ -3168,6 +3197,18 @@ namespace IDE.ui
|
|||
SetAsStartupProject(projectItem.mProject);
|
||||
});
|
||||
|
||||
item = menu.AddItem("Update Version Lock");
|
||||
item.mDisabled = (projectItem == null) || (!gApp.mWorkspace.mProjectLockMap.ContainsKey(projectItem.mProject.mProjectName));
|
||||
item.mOnMenuItemSelected.Add(new (item) =>
|
||||
{
|
||||
var projectItem = GetSelectedProjectItem();
|
||||
if (projectItem != null)
|
||||
{
|
||||
let project = projectItem.mProject;
|
||||
gApp.UpdateProjectVersionLocks(project.mProjectName);
|
||||
}
|
||||
});
|
||||
|
||||
item = menu.AddItem("Lock Project");
|
||||
if (projectItem.mProject.mLocked)
|
||||
item.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check);
|
||||
|
@ -3570,6 +3611,12 @@ namespace IDE.ui
|
|||
ImportInstalledProject();
|
||||
}
|
||||
|
||||
if (mImportRemoteDeferred)
|
||||
{
|
||||
mImportRemoteDeferred= false;
|
||||
ImportRemoteProject();
|
||||
}
|
||||
|
||||
ValidateCutClipboard();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,13 +10,12 @@ using Beefy.events;
|
|||
using Beefy.theme.dark;
|
||||
using Beefy.gfx;
|
||||
using Beefy.geom;
|
||||
using IDE.Util;
|
||||
|
||||
namespace IDE.ui
|
||||
{
|
||||
public class ProjectProperties : BuildPropertiesDialog
|
||||
{
|
||||
ValueContainer<String> mVC;
|
||||
|
||||
enum CategoryType
|
||||
{
|
||||
General, ///
|
||||
|
@ -27,6 +26,7 @@ namespace IDE.ui
|
|||
Dependencies,
|
||||
Beef_Global,
|
||||
Platform,
|
||||
Managed,
|
||||
|
||||
Targeted, ///
|
||||
Beef_Targeted,
|
||||
|
@ -38,8 +38,9 @@ namespace IDE.ui
|
|||
}
|
||||
|
||||
public Project mProject;
|
||||
Dictionary<String, ValueContainer<bool>> mDependencyValuesMap ~ DeleteDictionaryAndKeysAndValues!(_);
|
||||
Dictionary<String, DependencyEntry> mDependencyValuesMap ~ DeleteDictionaryAndKeysAndValues!(_);
|
||||
Project.Options[] mCurProjectOptions ~ delete _;
|
||||
List<String> mUpdateProjectLocks = new .() ~ DeleteContainerAndItems!(_);
|
||||
float mLockFlashPct;
|
||||
public int32 mNewDebugSessionCountdown;
|
||||
|
||||
|
@ -96,6 +97,7 @@ namespace IDE.ui
|
|||
AddCategoryItem(globalItem, "Dependencies");
|
||||
AddCategoryItem(globalItem, "Beef");
|
||||
AddCategoryItem(globalItem, "Platform");
|
||||
AddCategoryItem(globalItem, "Managed");
|
||||
globalItem.Open(true, true);
|
||||
|
||||
var targetedItem = AddCategoryItem(root, "Targeted");
|
||||
|
@ -149,7 +151,8 @@ namespace IDE.ui
|
|||
case .General,
|
||||
.Project,
|
||||
.Dependencies,
|
||||
.Beef_Global:
|
||||
.Beef_Global,
|
||||
.Managed:
|
||||
return .None;
|
||||
case .Platform:
|
||||
return .Platform;
|
||||
|
@ -432,6 +435,7 @@ namespace IDE.ui
|
|||
default:
|
||||
}
|
||||
}
|
||||
case .Managed:
|
||||
case .Build, .Debugging, .Beef_Targeted:
|
||||
DeleteDistinctBuildOptions();
|
||||
DistinctBuildOptions defaultTypeOptions = scope:: .();
|
||||
|
@ -530,7 +534,9 @@ namespace IDE.ui
|
|||
else
|
||||
{
|
||||
mCurPropertiesTargets = new Object[1];
|
||||
if (categoryType == .Project)
|
||||
if (categoryType == .Managed)
|
||||
mCurPropertiesTargets[0] = mProject.mManagedInfo;
|
||||
else if (categoryType == .Project)
|
||||
mCurPropertiesTargets[0] = mProject.mGeneralOptions;
|
||||
else if (categoryType == .Beef_Global)
|
||||
mCurPropertiesTargets[0] = mProject.mBeefGlobalOptions;
|
||||
|
@ -600,6 +606,8 @@ namespace IDE.ui
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (categoryType == CategoryType.Managed)
|
||||
PopulateManagedOptions();
|
||||
else if (categoryType == CategoryType.Build)
|
||||
PopulateBuildOptions();
|
||||
else if (categoryType == CategoryType.Beef_Global )
|
||||
|
@ -619,6 +627,8 @@ namespace IDE.ui
|
|||
void PopulateGeneralOptions()
|
||||
{
|
||||
var root = (DarkListViewItem)mPropPage.mPropertiesListView.GetRoot();
|
||||
var (listViewItem, propEntry) = AddPropertiesItem(root, "Project Name", "mProjectNameDecl");
|
||||
AddPropertiesItem(root, "Project Name Aliases", "mAliases");
|
||||
AddPropertiesItem(root, "Target Type", "mTargetType", scope String[]
|
||||
(
|
||||
"Console Application",
|
||||
|
@ -627,7 +637,19 @@ namespace IDE.ui
|
|||
"Custom Build",
|
||||
"Test"
|
||||
));
|
||||
AddPropertiesItem(root, "Project Name Aliases", "mAliases");
|
||||
AddPropertiesItem(root, "Version", "mVersion.mVersion");
|
||||
}
|
||||
|
||||
void PopulateManagedOptions()
|
||||
{
|
||||
if (mCurPropertiesTargets[0] == null)
|
||||
return;
|
||||
var root = (DarkListViewItem)mPropPage.mPropertiesListView.GetRoot();
|
||||
var (listViewItem, propEntry) = AddPropertiesItem(root, "Version", "mVersion.mVersion");
|
||||
propEntry.mReadOnly = true;
|
||||
(listViewItem, propEntry) = AddPropertiesItem(root, "Info", "mInfo");
|
||||
propEntry.mAllowMultiline = true;
|
||||
propEntry.mReadOnly = true;
|
||||
}
|
||||
|
||||
void PopulateWindowsOptions()
|
||||
|
@ -696,7 +718,21 @@ namespace IDE.ui
|
|||
|
||||
void PopulateDependencyOptions()
|
||||
{
|
||||
mDependencyValuesMap = new Dictionary<String, ValueContainer<bool>>();
|
||||
mPropPage.mPropertiesListView.mColumns[0].Label = "Project";
|
||||
mPropPage.mPropertiesListView.mColumns[0].mMinWidth = GS!(100);
|
||||
mPropPage.mPropertiesListView.mColumns[0].mWidth = GS!(180);
|
||||
|
||||
mPropPage.mPropertiesListView.mColumns[1].Label = "";
|
||||
mPropPage.mPropertiesListView.mColumns[1].mMinWidth = GS!(20);
|
||||
mPropPage.mPropertiesListView.mColumns[1].mWidth = GS!(20);
|
||||
|
||||
mPropPage.mPropertiesListView.AddColumn(180, "Remote URL");
|
||||
mPropPage.mPropertiesListView.mColumns[2].mMinWidth = GS!(100);
|
||||
|
||||
mPropPage.mPropertiesListView.AddColumn(180, "Ver Constraint");
|
||||
mPropPage.mPropertiesListView.mColumns[3].mMinWidth = GS!(100);
|
||||
|
||||
mDependencyValuesMap = new .();
|
||||
|
||||
var root = (DarkListViewItem)mPropPage.mPropertiesListView.GetRoot();
|
||||
var category = root;
|
||||
|
@ -720,62 +756,183 @@ namespace IDE.ui
|
|||
|
||||
for (var projectName in projectNames)
|
||||
{
|
||||
var dependencyContainer = new ValueContainer<bool>();
|
||||
dependencyContainer.mValue = mProject.HasDependency(projectName, false);
|
||||
mDependencyValuesMap[new String(projectName)] = dependencyContainer;
|
||||
var project = gApp.mWorkspace.FindProject(projectName);
|
||||
|
||||
var dependencyEntry = new DependencyEntry();
|
||||
var verSpec = mProject.GetDependency(projectName, false);
|
||||
if (verSpec != null)
|
||||
{
|
||||
dependencyEntry.mUse = true;
|
||||
if (verSpec case .Git(let url, let ver))
|
||||
{
|
||||
dependencyEntry.mURL = new .(url);
|
||||
if (ver != null)
|
||||
dependencyEntry.mVersion = new .(ver.mVersion);
|
||||
}
|
||||
}
|
||||
mDependencyValuesMap[new String(projectName)] = dependencyEntry;
|
||||
|
||||
var (listViewItem, propItem) = AddPropertiesItem(category, projectName);
|
||||
if (IDEApp.sApp.mWorkspace.FindProject(projectName) == null)
|
||||
listViewItem.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFFF6060);
|
||||
|
||||
var subItem = listViewItem.CreateSubItem(1);
|
||||
var subItem = (DarkListViewItem)listViewItem.CreateSubItem(1);
|
||||
|
||||
var checkbox = new DarkCheckBox();
|
||||
checkbox.Checked = dependencyContainer.mValue;
|
||||
checkbox.Checked = dependencyEntry.mUse;
|
||||
checkbox.Resize(0, 0, DarkTheme.sUnitSize, DarkTheme.sUnitSize);
|
||||
subItem.AddWidget(checkbox);
|
||||
|
||||
PropEntry[] propEntries = new PropEntry[1];
|
||||
|
||||
PropEntry propEntry = new PropEntry();
|
||||
propEntry.mTarget = dependencyContainer;
|
||||
//propEntry.mFieldInfo = dependencyContainer.GetType().GetField("mValue").Value;
|
||||
propEntry.mOrigValue = Variant.Create(dependencyContainer.mValue);
|
||||
propEntry.mCurValue = propEntry.mOrigValue;
|
||||
propEntry.mTarget = dependencyEntry;
|
||||
propEntry.mOrigValue = Variant.Create(dependencyEntry);
|
||||
propEntry.mCurValue = Variant.Create(new DependencyEntry(dependencyEntry), true);
|
||||
|
||||
propEntry.mListViewItem = listViewItem;
|
||||
propEntry.mCheckBox = checkbox;
|
||||
propEntry.mApplyAction = new () =>
|
||||
{
|
||||
if (propEntry.mCurValue.Get<bool>())
|
||||
bool updateProjectLock = false;
|
||||
|
||||
var dependencyEntry = propEntry.mCurValue.Get<DependencyEntry>();
|
||||
if (dependencyEntry.mUse)
|
||||
{
|
||||
if (!mProject.HasDependency(listViewItem.mLabel))
|
||||
VerSpec verSpec = default;
|
||||
if (dependencyEntry.mURL != null)
|
||||
verSpec = .Git(new .(dependencyEntry.mURL), (dependencyEntry.mVersion != null) ? new .(dependencyEntry.mVersion) : null);
|
||||
else if (dependencyEntry.mVersion != null)
|
||||
verSpec = .SemVer(new .(dependencyEntry.mVersion));
|
||||
else
|
||||
verSpec = .SemVer(new .("*"));
|
||||
|
||||
var verSpecPtr = mProject.GetDependency(listViewItem.mLabel);
|
||||
if (verSpecPtr == null)
|
||||
{
|
||||
if (verSpec case .Git(let url, let ver))
|
||||
updateProjectLock = true;
|
||||
|
||||
var dep = new Project.Dependency();
|
||||
dep.mProjectName = new String(listViewItem.mLabel);
|
||||
dep.mVerSpec = .SemVer(new .("*"));
|
||||
dep.mVerSpec = verSpec;
|
||||
mProject.mDependencies.Add(dep);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*verSpecPtr != verSpec)
|
||||
{
|
||||
if ((*verSpecPtr case .Git) ||
|
||||
(verSpecPtr case .Git))
|
||||
updateProjectLock = true;
|
||||
verSpecPtr.Dispose();
|
||||
*verSpecPtr = verSpec;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int idx = mProject.mDependencies.FindIndex(scope (dep) => dep.mProjectName == listViewItem.mLabel);
|
||||
if (idx != -1)
|
||||
{
|
||||
var dep = mProject.mDependencies[idx];
|
||||
if (dep.mVerSpec case .Git)
|
||||
updateProjectLock = true;
|
||||
delete mProject.mDependencies[idx];
|
||||
mProject.mDependencies.RemoveAt(idx);
|
||||
}
|
||||
}
|
||||
propEntry.mOrigValue = propEntry.mCurValue;
|
||||
|
||||
var origDependencyEntry = propEntry.mOrigValue.Get<DependencyEntry>();
|
||||
origDependencyEntry.Set(dependencyEntry);
|
||||
|
||||
if (updateProjectLock)
|
||||
mUpdateProjectLocks.Add(new .(listViewItem.Label));
|
||||
};
|
||||
|
||||
checkbox.mOnMouseUp.Add(new (evt) => { PropEntry.DisposeVariant(ref propEntry.mCurValue); propEntry.mCurValue = Variant.Create(checkbox.Checked); });
|
||||
checkbox.mOnMouseUp.Add(new (evt) =>
|
||||
{
|
||||
var dependencyEntry = propEntry.mCurValue.Get<DependencyEntry>();
|
||||
dependencyEntry.mUse = !dependencyEntry.mUse;
|
||||
if (dependencyEntry.mUse)
|
||||
{
|
||||
var projectName = listViewItem.Label;
|
||||
|
||||
for (var projectSpec in gApp.mWorkspace.mProjectSpecs)
|
||||
{
|
||||
if (projectSpec.mProjectName == projectName)
|
||||
{
|
||||
if (projectSpec.mVerSpec case .Git(let url, let ver))
|
||||
{
|
||||
dependencyEntry.SetValue(1, url);
|
||||
dependencyEntry.SetValue(2, ver.mVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
var propEntries = mPropPage.mPropEntries[listViewItem];
|
||||
UpdatePropertyValue(propEntries);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteAndNullify!(dependencyEntry.mURL);
|
||||
DeleteAndNullify!(dependencyEntry.mVersion);
|
||||
var propEntries = mPropPage.mPropEntries[listViewItem];
|
||||
UpdatePropertyValue(propEntries);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
subItem = (.)listViewItem.GetOrCreateSubItem(2);
|
||||
if (dependencyEntry.mURL != null)
|
||||
subItem.Label = dependencyEntry.mURL;
|
||||
subItem.mOnMouseDown.Add(new => DepPropValueClicked);
|
||||
|
||||
subItem = (.)listViewItem.GetOrCreateSubItem(3);
|
||||
if (dependencyEntry.mVersion != null)
|
||||
{
|
||||
subItem.Label = dependencyEntry.mVersion;
|
||||
if (project != null)
|
||||
{
|
||||
var version = project.Version;
|
||||
if (!version.IsEmpty)
|
||||
{
|
||||
if (!SemVer.IsVersionMatch(version.mVersion, dependencyEntry.mVersion))
|
||||
subItem.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFFFFF60);
|
||||
}
|
||||
}
|
||||
}
|
||||
subItem.mOnMouseDown.Add(new => DepPropValueClicked);
|
||||
|
||||
propEntries[0] = propEntry;
|
||||
mPropPage.mPropEntries[listViewItem] = propEntries;
|
||||
}
|
||||
}
|
||||
|
||||
protected void DepPropValueClicked(MouseEvent theEvent)
|
||||
{
|
||||
DarkListViewItem clickedItem = (DarkListViewItem)theEvent.mSender;
|
||||
if (clickedItem.mColumnIdx == 0)
|
||||
{
|
||||
clickedItem.mListView.SetFocus();
|
||||
clickedItem.mListView.GetRoot().SelectItemExclusively(clickedItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (theEvent.mX != -1)
|
||||
{
|
||||
clickedItem.mListView.GetRoot().SelectItemExclusively(null);
|
||||
}
|
||||
|
||||
DarkListViewItem item = (DarkListViewItem)clickedItem;
|
||||
DarkListViewItem rootItem = (DarkListViewItem)clickedItem.GetSubItem(0);
|
||||
|
||||
PropEntry[] propertyEntries = mPropPage.mPropEntries[rootItem];
|
||||
if (propertyEntries[0].mDisabled)
|
||||
return;
|
||||
EditValue(item, propertyEntries, clickedItem.mColumnIdx - 1);
|
||||
}
|
||||
|
||||
protected override Object[] PhysAddNewDistinctBuildOptions()
|
||||
{
|
||||
if (mCurProjectOptions == null)
|
||||
|
@ -985,6 +1142,8 @@ namespace IDE.ui
|
|||
/*if (!AssertNotCompilingOrRunning())
|
||||
return false;*/
|
||||
|
||||
String newProjectName = scope .();
|
||||
|
||||
using (mProject.mMonitor.Enter())
|
||||
{
|
||||
for (var targetedConfigData in mConfigDatas)
|
||||
|
@ -1000,10 +1159,19 @@ namespace IDE.ui
|
|||
for (var propEntry in propEntries)
|
||||
{
|
||||
if (propEntry.HasChanged())
|
||||
{
|
||||
if ((propEntry.mFieldInfo != default) && (propEntry.mFieldInfo.Name == "mProjectNameDecl"))
|
||||
{
|
||||
var newName = propEntry.mCurValue.Get<String>();
|
||||
newProjectName.Append(newName);
|
||||
newProjectName.Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
configDataHadChange = true;
|
||||
propEntry.ApplyValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (propPage == mPropPage)
|
||||
UpdatePropertyValue(propEntries);
|
||||
|
@ -1060,6 +1228,9 @@ namespace IDE.ui
|
|||
ClearTargetedData();
|
||||
}
|
||||
|
||||
if (!newProjectName.IsEmpty)
|
||||
gApp.RenameProject(mProject, newProjectName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1081,6 +1252,7 @@ namespace IDE.ui
|
|||
{
|
||||
base.Close();
|
||||
SetWorkspaceData(false);
|
||||
gApp.NotifyProjectVersionLocks(mUpdateProjectLocks);
|
||||
}
|
||||
|
||||
public override void PopupWindow(WidgetWindow parentWindow, float offsetX = 0, float offsetY = 0)
|
||||
|
|
|
@ -114,6 +114,12 @@ namespace IDE.ui
|
|||
|
||||
public class PropertiesDialog : IDEDialog
|
||||
{
|
||||
public interface IMultiValued
|
||||
{
|
||||
void GetValue(int idx, String outValue);
|
||||
bool SetValue(int idx, StringView value);
|
||||
}
|
||||
|
||||
class OwnedStringList : List<String>
|
||||
{
|
||||
|
||||
|
@ -215,6 +221,7 @@ namespace IDE.ui
|
|||
public String mRelPath ~ delete _;
|
||||
public bool mIsTypeWildcard;
|
||||
public bool mAllowMultiline;
|
||||
public bool mReadOnly;
|
||||
public Insets mEditInsets ~ delete _;
|
||||
|
||||
public ~this()
|
||||
|
@ -305,6 +312,18 @@ namespace IDE.ui
|
|||
}
|
||||
return true;
|
||||
}
|
||||
else if (type.IsObject)
|
||||
{
|
||||
var lhsObj = lhs.Get<Object>();
|
||||
var rhsObj = rhs.Get<Object>();
|
||||
|
||||
if ((var lhsEq = lhsObj as IEquatable) && (var rhsEq = rhsObj as IEquatable))
|
||||
{
|
||||
return lhsEq.Equals(rhsEq);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else // Could be an int or enum
|
||||
return Variant.Equals!<int32>(lhs, rhs);
|
||||
}
|
||||
|
@ -815,7 +834,7 @@ namespace IDE.ui
|
|||
|
||||
if (mPropEditWidget != null)
|
||||
{
|
||||
DarkListViewItem editItem = (DarkListViewItem)mEditingListViewItem.GetSubItem(1);
|
||||
DarkListViewItem editItem = (DarkListViewItem)mEditingListViewItem;
|
||||
let propEntry = mEditingProps[0];
|
||||
|
||||
float xPos;
|
||||
|
@ -871,7 +890,7 @@ namespace IDE.ui
|
|||
editWidget.GetText(newValue);
|
||||
newValue.Trim();
|
||||
|
||||
DarkListViewItem item = (DarkListViewItem)mEditingListViewItem;
|
||||
DarkListViewItem rootItem = (DarkListViewItem)mEditingListViewItem.GetSubItem(0);
|
||||
//DarkListViewItem valueItem = (DarkListViewItem)item.GetSubItem(1);
|
||||
|
||||
if (!editWidget.mEditWidgetContent.HasUndoData())
|
||||
|
@ -920,14 +939,14 @@ namespace IDE.ui
|
|||
{
|
||||
//
|
||||
}
|
||||
else if (editingProp.mListViewItem != item)
|
||||
else if (editingProp.mListViewItem != rootItem)
|
||||
{
|
||||
List<String> curEntries = editingProp.mCurValue.Get<List<String>>();
|
||||
List<String> entries = new List<String>(curEntries.GetEnumerator());
|
||||
|
||||
for (int32 childIdx = 0; childIdx < editingProp.mListViewItem.GetChildCount(); childIdx++)
|
||||
{
|
||||
if (item == editingProp.mListViewItem.GetChildAtIndex(childIdx))
|
||||
if (rootItem == editingProp.mListViewItem.GetChildAtIndex(childIdx))
|
||||
{
|
||||
if (childIdx >= entries.Count)
|
||||
entries.Add(new String(newValue));
|
||||
|
@ -1027,6 +1046,11 @@ namespace IDE.ui
|
|||
setValue = false;
|
||||
}
|
||||
}
|
||||
else if ((curVariantType.IsObject) && (var multiValue = prevValue.Get<Object>() as IMultiValued))
|
||||
{
|
||||
multiValue.SetValue(mEditingListViewItem.mColumnIdx - 1, newValue);
|
||||
setValue = false;
|
||||
}
|
||||
else
|
||||
editingProp.mCurValue = Variant.Create(new String(newValue), true);
|
||||
|
||||
|
@ -1247,12 +1271,23 @@ namespace IDE.ui
|
|||
if (ewc.mIsMultiline)
|
||||
editWidget.InitScrollbars(false, true);
|
||||
|
||||
if (propEntry.mReadOnly)
|
||||
editWidget.mEditWidgetContent.mIsReadOnly = true;
|
||||
|
||||
editWidget.mScrollContentInsets.Set(GS!(3), GS!(3), GS!(1), GS!(3));
|
||||
editWidget.Content.mTextInsets.Set(GS!(-3), GS!(2), 0, GS!(2));
|
||||
//editWidget.RehupSize();
|
||||
if (subValueIdx != -1)
|
||||
{
|
||||
List<String> stringList = propEntry.mCurValue.Get<List<String>>();
|
||||
var obj = propEntry.mCurValue.Get<Object>();
|
||||
if (var multiValued = obj as IMultiValued)
|
||||
{
|
||||
var label = multiValued.GetValue(subValueIdx, .. scope .());
|
||||
editWidget.SetText(label);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<String> stringList = obj as List<String>;
|
||||
if (subValueIdx < stringList.Count)
|
||||
editWidget.SetText(stringList[subValueIdx]);
|
||||
|
||||
|
@ -1279,6 +1314,7 @@ namespace IDE.ui
|
|||
editWidget.mOnKeyDown.Add(new (evt) => { if (evt.mKeyCode == KeyCode.Down) MoveEditingItem(subValueIdx, 1); });
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String label = valueItem.mLabel;
|
||||
|
@ -1363,8 +1399,8 @@ namespace IDE.ui
|
|||
hasChanged = true;
|
||||
}
|
||||
|
||||
if (propEntry.mFieldInfo == default(FieldInfo))
|
||||
return;
|
||||
/*if (propEntry.mFieldInfo == default(FieldInfo))
|
||||
return;*/
|
||||
|
||||
var curVariantType = propEntry.mCurValue.VariantType;
|
||||
|
||||
|
@ -1516,6 +1552,15 @@ namespace IDE.ui
|
|||
valueItem.Label = allValues;
|
||||
FixLabel(valueItem);
|
||||
}
|
||||
else if ((curVariantType.IsObject) && (var multiValue = propEntry.mCurValue.Get<Object>() as IMultiValued))
|
||||
{
|
||||
for (int columnIdx in 1..<propEntry.mListViewItem.mSubItems.Count)
|
||||
{
|
||||
var subItem = propEntry.mListViewItem.GetSubItem(columnIdx);
|
||||
var label = multiValue.GetValue(columnIdx - 1, .. scope .());
|
||||
subItem.Label = label;
|
||||
}
|
||||
}
|
||||
else if (propEntry.mCheckBox != null)
|
||||
{
|
||||
propEntry.mCheckBox.Checked = propEntry.mCurValue.Get<bool>();
|
||||
|
@ -2026,9 +2071,10 @@ namespace IDE.ui
|
|||
clickedItem.mListView.GetRoot().SelectItemExclusively(null);
|
||||
}
|
||||
|
||||
DarkListViewItem item = (DarkListViewItem)clickedItem.GetSubItem(0);
|
||||
DarkListViewItem item = (DarkListViewItem)clickedItem;
|
||||
DarkListViewItem rootItem = (DarkListViewItem)item.GetSubItem(0);
|
||||
|
||||
PropEntry[] propertyEntries = mPropPage.mPropEntries[item];
|
||||
PropEntry[] propertyEntries = mPropPage.mPropEntries[rootItem];
|
||||
if (propertyEntries[0].mDisabled)
|
||||
return;
|
||||
EditValue(item, propertyEntries);
|
||||
|
@ -2039,7 +2085,7 @@ namespace IDE.ui
|
|||
var propEntry = propEntries[0];
|
||||
DarkListViewItem parentItem = propEntry.mListViewItem;
|
||||
DarkListViewItem clickedItem = (DarkListViewItem)parentItem.GetChildAtIndex(idx);
|
||||
DarkListViewItem item = (DarkListViewItem)clickedItem.GetSubItem(0);
|
||||
DarkListViewItem item = (DarkListViewItem)clickedItem.GetSubItem(1);
|
||||
EditValue(item, propEntries, idx);
|
||||
}
|
||||
|
||||
|
|
179
IDE/src/ui/RemoteProjectDialog.bf
Normal file
179
IDE/src/ui/RemoteProjectDialog.bf
Normal file
|
@ -0,0 +1,179 @@
|
|||
#pragma warning disable 168
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Beefy;
|
||||
using Beefy.gfx;
|
||||
using Beefy.theme.dark;
|
||||
using Beefy.widgets;
|
||||
using Beefy.theme;
|
||||
using IDE.Util;
|
||||
|
||||
namespace IDE.ui
|
||||
{
|
||||
public class RemoteProjectDialog : IDEDialog
|
||||
{
|
||||
public EditWidget mURLEdit;
|
||||
public EditWidget mVersionEdit;
|
||||
public DarkComboBox mTargetComboBox;
|
||||
static String[1] sApplicationTypeNames =
|
||||
.("Git");
|
||||
public bool mNameChanged;
|
||||
public String mDirBase ~ delete _;
|
||||
|
||||
public this()
|
||||
{
|
||||
mTitle = new String("Add Remote Project");
|
||||
}
|
||||
|
||||
public override void CalcSize()
|
||||
{
|
||||
mWidth = GS!(320);
|
||||
mHeight = GS!(200);
|
||||
}
|
||||
|
||||
enum CreateFlags
|
||||
{
|
||||
None,
|
||||
NonEmptyDirOkay = 1,
|
||||
}
|
||||
|
||||
bool CreateProject(CreateFlags createFlags = .None)
|
||||
{
|
||||
var app = IDEApp.sApp;
|
||||
String url = scope String();
|
||||
mURLEdit.GetText(url);
|
||||
url.Trim();
|
||||
|
||||
if (url.IsEmpty)
|
||||
{
|
||||
mURLEdit.SetFocus();
|
||||
app.Fail("Invalid URL");
|
||||
return false;
|
||||
}
|
||||
|
||||
var projName = Path.GetFileName(url, .. scope .());
|
||||
|
||||
var version = mVersionEdit.GetText(.. scope .())..Trim();
|
||||
|
||||
var otherProject = app.mWorkspace.FindProject(projName);
|
||||
if (otherProject != null)
|
||||
{
|
||||
mURLEdit.SetFocus();
|
||||
app.Fail("A project with this name already exists in the workspace.");
|
||||
return false;
|
||||
}
|
||||
|
||||
VerSpec verSpec = .Git(url, scope .(version));
|
||||
if (var project = gApp.AddProject(projName, verSpec))
|
||||
{
|
||||
//gApp.ProjectCreated(project);
|
||||
app.mWorkspace.SetChanged();
|
||||
|
||||
gApp.[Friend]FlushDeferredLoadProjects(true);
|
||||
//gApp.RetryProjectLoad(project, false);
|
||||
//gApp.AddProjectToWorkspace(project);
|
||||
|
||||
var projectSpec = new Workspace.ProjectSpec();
|
||||
projectSpec.mProjectName = new .(project.mProjectName);
|
||||
projectSpec.mVerSpec = .Git(new .(url), new .(version));
|
||||
gApp.mWorkspace.mProjectSpecs.Add(projectSpec);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateProjectName()
|
||||
{
|
||||
if (!mNameChanged)
|
||||
{
|
||||
String path = scope .();
|
||||
mURLEdit.GetText(path);
|
||||
path.Trim();
|
||||
if ((path.EndsWith('\\')) || (path.EndsWith('/')))
|
||||
path.RemoveFromEnd(1);
|
||||
|
||||
String projName = scope .();
|
||||
Path.GetFileName(path, projName);
|
||||
mVersionEdit.SetText(projName);
|
||||
}
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
mDefaultButton = AddButton("Create", new (evt) =>
|
||||
{
|
||||
if (!CreateProject()) evt.mCloseDialog = false;
|
||||
});
|
||||
mEscButton = AddButton("Cancel", new (evt) => Close());
|
||||
|
||||
if (gApp.mWorkspace.IsInitialized)
|
||||
mDirBase = new String(gApp.mWorkspace.mDir);
|
||||
else
|
||||
mDirBase = new String();
|
||||
mURLEdit = new DarkEditWidget();
|
||||
|
||||
AddEdit(mURLEdit);
|
||||
mURLEdit.mOnContentChanged.Add(new (dlg) =>
|
||||
{
|
||||
|
||||
});
|
||||
|
||||
mVersionEdit = AddEdit("");
|
||||
mVersionEdit.mOnContentChanged.Add(new (dlg) =>
|
||||
{
|
||||
if (mVersionEdit.mHasFocus)
|
||||
mNameChanged = true;
|
||||
});
|
||||
|
||||
mTargetComboBox = new DarkComboBox();
|
||||
mTargetComboBox.Label = sApplicationTypeNames[0];
|
||||
mTargetComboBox.mPopulateMenuAction.Add(new (dlg) =>
|
||||
{
|
||||
for (var applicationTypeName in sApplicationTypeNames)
|
||||
{
|
||||
var item = dlg.AddItem(applicationTypeName);
|
||||
item.mOnMenuItemSelected.Add(new (item) =>
|
||||
{
|
||||
mTargetComboBox.Label = item.mLabel;
|
||||
MarkDirty();
|
||||
});
|
||||
}
|
||||
});
|
||||
AddWidget(mTargetComboBox);
|
||||
mTabWidgets.Add(mTargetComboBox);
|
||||
}
|
||||
|
||||
public override void PopupWindow(WidgetWindow parentWindow, float offsetX = 0, float offsetY = 0)
|
||||
{
|
||||
base.PopupWindow(parentWindow, offsetX, offsetY);
|
||||
mURLEdit.SetFocus();
|
||||
}
|
||||
|
||||
public override void ResizeComponents()
|
||||
{
|
||||
base.ResizeComponents();
|
||||
|
||||
float curY = mHeight - GS!(30) - mButtonBottomMargin;
|
||||
mVersionEdit.Resize(GS!(16), curY - GS!(36), mWidth - GS!(16) * 2, GS!(24));
|
||||
|
||||
curY -= GS!(50);
|
||||
mURLEdit.Resize(GS!(16), curY - GS!(36), mWidth - GS!(16) * 2, GS!(24));
|
||||
|
||||
curY -= GS!(60);
|
||||
mTargetComboBox.Resize(GS!(16), curY - GS!(36), mWidth - GS!(16) * 2, GS!(28));
|
||||
}
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
g.DrawString("Remote Project URL", mURLEdit.mX, mURLEdit.mY - GS!(20));
|
||||
g.DrawString("Version Constraint (Blank for HEAD)", mVersionEdit.mX, mVersionEdit.mY - GS!(20));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -414,6 +414,7 @@ namespace IDE.ui
|
|||
|
||||
if (mGettingSymbolInfo)
|
||||
{
|
||||
if (gApp.mWorkspace.mProjectLoadState == .Loaded)
|
||||
gApp.Fail("Cannot rename symbols here");
|
||||
mGettingSymbolInfo = false;
|
||||
return;
|
||||
|
@ -430,6 +431,12 @@ namespace IDE.ui
|
|||
if ((mKind == Kind.ShowFileReferences) || (mResolveParams.mLocalId != -1))
|
||||
{
|
||||
mParser = IDEApp.sApp.mBfResolveSystem.FindParser(mSourceViewPanel.mProjectSource);
|
||||
if (mParser == null)
|
||||
{
|
||||
mGettingSymbolInfo = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mResolveParams != null) && (mResolveParams.mLocalId != -1))
|
||||
mParser.SetAutocomplete(mCursorPos);
|
||||
else
|
||||
|
|
|
@ -1932,7 +1932,7 @@ namespace IDE.ui
|
|||
}
|
||||
else if (resolveType == ResolveType.GetCurrentLocation)
|
||||
{
|
||||
PrimaryNavigationBar.SetLocation(autocompleteInfo);
|
||||
PrimaryNavigationBar.SetLocation(autocompleteInfo ?? "");
|
||||
}
|
||||
else if ((resolveType == .Autocomplete) || (resolveType == .GetFixits))
|
||||
{
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace IDE.ui
|
|||
public DarkButton mSafeModeButton;
|
||||
public bool mWasCompiling;
|
||||
public int mEvalCount;
|
||||
public ImageWidget mCancelSymSrvButton;
|
||||
public ImageWidget mCancelButton;
|
||||
public int mDirtyDelay;
|
||||
public int mStatusBoxUpdateCnt = -1;
|
||||
|
||||
|
@ -117,8 +117,8 @@ namespace IDE.ui
|
|||
mConfigComboBox.Resize(mWidth - btnLeft, GS!(0), GS!(120), GS!(24));
|
||||
mPlatformComboBox.Resize(mWidth - btnLeft - GS!(120), GS!(0), GS!(120), GS!(24));
|
||||
|
||||
if (mCancelSymSrvButton != null)
|
||||
mCancelSymSrvButton.Resize(GS!(546), 0, GS!(20), GS!(20));
|
||||
if (mCancelButton != null)
|
||||
mCancelButton.Resize(GS!(546), 0, GS!(20), GS!(20));
|
||||
|
||||
if (mSafeModeButton != null)
|
||||
{
|
||||
|
@ -182,19 +182,31 @@ namespace IDE.ui
|
|||
else
|
||||
mEvalCount = 0;
|
||||
|
||||
void ShowCancelButton()
|
||||
{
|
||||
if (mCancelButton == null)
|
||||
{
|
||||
mCancelButton = new ImageWidget();
|
||||
mCancelButton.mImage = DarkTheme.sDarkTheme.GetImage(.Close);
|
||||
mCancelButton.mOverImage = DarkTheme.sDarkTheme.GetImage(.CloseOver);
|
||||
mCancelButton.mOnMouseClick.Add(new (evt) =>
|
||||
{
|
||||
if (gApp.mWorkspace.mProjectLoadState == .Preparing)
|
||||
{
|
||||
gApp.CancelWorkspaceLoading();
|
||||
}
|
||||
else
|
||||
gApp.mDebugger.CancelSymSrv();
|
||||
});
|
||||
AddWidget(mCancelButton);
|
||||
ResizeComponents();
|
||||
}
|
||||
}
|
||||
|
||||
if (debugState == .SearchingSymSrv)
|
||||
{
|
||||
MarkDirtyEx();
|
||||
|
||||
if (mCancelSymSrvButton == null)
|
||||
{
|
||||
mCancelSymSrvButton = new ImageWidget();
|
||||
mCancelSymSrvButton.mImage = DarkTheme.sDarkTheme.GetImage(.Close);
|
||||
mCancelSymSrvButton.mOverImage = DarkTheme.sDarkTheme.GetImage(.CloseOver);
|
||||
mCancelSymSrvButton.mOnMouseClick.Add(new (evt) => { gApp.mDebugger.CancelSymSrv(); });
|
||||
AddWidget(mCancelSymSrvButton);
|
||||
ResizeComponents();
|
||||
}
|
||||
ShowCancelButton();
|
||||
|
||||
float len = GS!(200);
|
||||
float x = GS!(350);
|
||||
|
@ -209,15 +221,45 @@ namespace IDE.ui
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (gApp.mWorkspace.mProjectLoadState == .Preparing)
|
||||
{
|
||||
MarkDirtyEx();
|
||||
ShowCancelButton();
|
||||
|
||||
float len = GS!(200);
|
||||
float x = GS!(350);
|
||||
Rect completionRect = Rect(x, GS!(1), len, GS!(17));
|
||||
|
||||
String status = scope .();
|
||||
|
||||
for (var workItem in gApp.mPackMan.mWorkItems)
|
||||
{
|
||||
if (workItem.mGitInstance == null)
|
||||
break;
|
||||
|
||||
//DrawCompletion(workItem.mGitInstance.mProgress);
|
||||
status.AppendF($"Retrieving {workItem.mProjectName}: {(int)(workItem.mGitInstance.mProgress * 100)}%");
|
||||
}
|
||||
|
||||
Point mousePos;
|
||||
if (DarkTooltipManager.CheckMouseover(this, 25, out mousePos, true))
|
||||
{
|
||||
if (completionRect.Contains(mousePos.x, mousePos.y))
|
||||
{
|
||||
if (!status.IsEmpty)
|
||||
DarkTooltipManager.ShowTooltip(status, this, mousePos.x, mousePos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((DarkTooltipManager.sTooltip != null) && (DarkTooltipManager.sTooltip.mRelWidget == this))
|
||||
DarkTooltipManager.sTooltip.Close();
|
||||
|
||||
if (mCancelSymSrvButton != null)
|
||||
if (mCancelButton != null)
|
||||
{
|
||||
RemoveAndDelete(mCancelSymSrvButton);
|
||||
mCancelSymSrvButton = null;
|
||||
RemoveAndDelete(mCancelButton);
|
||||
mCancelButton = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,6 +409,16 @@ namespace IDE.ui
|
|||
|
||||
float statusLabelPos = (int)GS!(-1.3f);
|
||||
|
||||
void DrawCompletion(float pct)
|
||||
{
|
||||
Rect completionRect = Rect(GS!(200), GS!(2), GS!(120), GS!(15));
|
||||
using (g.PushColor(0xFF000000))
|
||||
g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight);
|
||||
completionRect.Inflate(GS!(-1), GS!(-1));
|
||||
using (g.PushColor(0xFF00FF00))
|
||||
g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth * pct, completionRect.mHeight);
|
||||
}
|
||||
|
||||
//completionPct = 0.4f;
|
||||
if ((gApp.mDebugger?.mIsComptimeDebug == true) &&
|
||||
((gApp.mDebugger.IsPaused()) || (debugState == .DebugEval)))
|
||||
|
@ -375,12 +427,7 @@ namespace IDE.ui
|
|||
}
|
||||
else if (completionPct.HasValue)
|
||||
{
|
||||
Rect completionRect = Rect(GS!(200), GS!(2), GS!(120), GS!(15));
|
||||
using (g.PushColor(0xFF000000))
|
||||
g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight);
|
||||
completionRect.Inflate(GS!(-1), GS!(-1));
|
||||
using (g.PushColor(0xFF00FF00))
|
||||
g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth * completionPct.Value, completionRect.mHeight);
|
||||
DrawCompletion(completionPct.Value);
|
||||
}
|
||||
else if ((gApp.mDebugger.mIsRunning) && (gApp.HaveSourcesChanged()))
|
||||
{
|
||||
|
@ -394,7 +441,7 @@ namespace IDE.ui
|
|||
g.DrawString("Source Changed", GS!(200), statusLabelPos, FontAlign.Centered, GS!(120));
|
||||
}
|
||||
|
||||
void DrawStatusBox(StringView str, int32 updateCnt = -1)
|
||||
void DrawStatusBox(StringView str, int32 updateCnt = -1, bool showCancelButton = false)
|
||||
{
|
||||
if (mStatusBoxUpdateCnt == -1)
|
||||
mStatusBoxUpdateCnt = 0;
|
||||
|
@ -415,8 +462,18 @@ namespace IDE.ui
|
|||
using (g.PushColor(Color.FromHSV(0.1f, 0.5f, (float)Math.Max(pulsePct * 0.15f + 0.3f, 0.3f))))
|
||||
g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight);
|
||||
|
||||
if (mCancelSymSrvButton != null)
|
||||
mCancelSymSrvButton.mX = completionRect.Right - GS!(16);
|
||||
if (mCancelButton != null)
|
||||
{
|
||||
if (showCancelButton)
|
||||
{
|
||||
mCancelButton.SetVisible(true);
|
||||
mCancelButton.mX = completionRect.Right - GS!(16);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCancelButton.SetVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
using (g.PushColor(DarkTheme.COLOR_TEXT))
|
||||
g.DrawString(str, x, statusLabelPos, FontAlign.Centered, len);
|
||||
|
@ -429,10 +486,6 @@ namespace IDE.ui
|
|||
chordState.Append(", <Awaiting Key>...");
|
||||
DrawStatusBox(chordState);
|
||||
}
|
||||
else if (mCancelSymSrvButton != null)
|
||||
{
|
||||
DrawStatusBox("Retrieving Debug Symbols... ");
|
||||
}
|
||||
else if (mEvalCount > 20)
|
||||
{
|
||||
DrawStatusBox("Evaluating Expression");
|
||||
|
@ -451,10 +504,16 @@ namespace IDE.ui
|
|||
}
|
||||
else if (gApp.mWorkspace.mProjectLoadState == .Preparing)
|
||||
{
|
||||
DrawStatusBox("Loading Projects");
|
||||
DrawStatusBox("Loading Projects", -1, true);
|
||||
}
|
||||
else if (mCancelButton != null)
|
||||
{
|
||||
DrawStatusBox("Retrieving Debug Symbols... ", -1, true);
|
||||
}
|
||||
else if (gApp.mDeferredShowSource != null)
|
||||
{
|
||||
DrawStatusBox("Queued Showing Source");
|
||||
}
|
||||
else
|
||||
mStatusBoxUpdateCnt = -1;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ using Beefy.theme.dark;
|
|||
using Beefy.theme;
|
||||
using Beefy.events;
|
||||
using System.Diagnostics;
|
||||
using IDE.Util;
|
||||
|
||||
//#define A
|
||||
//#define B
|
||||
|
@ -40,6 +41,7 @@ namespace IDE.ui
|
|||
enum CategoryType
|
||||
{
|
||||
General,
|
||||
Dependencies,
|
||||
Beef_Global,
|
||||
|
||||
Targeted,
|
||||
|
@ -53,6 +55,7 @@ namespace IDE.ui
|
|||
|
||||
ConfigDataGroup mCurConfigDataGroup;
|
||||
Workspace.Options[] mCurWorkspaceOptions ~ delete _;
|
||||
List<String> mUpdateProjectLocks = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
public this()
|
||||
{
|
||||
|
@ -62,8 +65,9 @@ namespace IDE.ui
|
|||
|
||||
var root = (DarkListViewItem)mCategorySelector.GetRoot();
|
||||
var globalItem = AddCategoryItem(root, "General");
|
||||
var item = AddCategoryItem(globalItem, "Beef");
|
||||
var item = AddCategoryItem(globalItem, "Dependencies");
|
||||
item.Focused = true;
|
||||
AddCategoryItem(globalItem, "Beef");
|
||||
globalItem.Open(true, true);
|
||||
|
||||
var targetedItem = AddCategoryItem(root, "Targeted");
|
||||
|
@ -124,6 +128,7 @@ namespace IDE.ui
|
|||
{
|
||||
case .General,
|
||||
//.Targeted,
|
||||
.Dependencies,
|
||||
.Beef_Global:
|
||||
return .None;
|
||||
default:
|
||||
|
@ -454,7 +459,9 @@ namespace IDE.ui
|
|||
mPropPage.mPropertiesListView.mShowColumnGrid = true;
|
||||
mPropPage.mPropertiesListView.mShowGridLines = true;
|
||||
|
||||
if (categoryType == CategoryType.Beef_Global)
|
||||
if (categoryType == CategoryType.Dependencies)
|
||||
PopulateDependencyOptions();
|
||||
else if (categoryType == CategoryType.Beef_Global)
|
||||
PopulateBeefGlobalOptions();
|
||||
else if (categoryType == CategoryType.Build)
|
||||
PopulateBuildOptions();
|
||||
|
@ -705,6 +712,230 @@ namespace IDE.ui
|
|||
}
|
||||
}
|
||||
|
||||
void PopulateDependencyOptions()
|
||||
{
|
||||
mPropPage.mPropertiesListView.mColumns[0].Label = "Project";
|
||||
mPropPage.mPropertiesListView.mColumns[0].mMinWidth = GS!(100);
|
||||
mPropPage.mPropertiesListView.mColumns[0].mWidth = GS!(180);
|
||||
|
||||
mPropPage.mPropertiesListView.mColumns[1].Label = "";
|
||||
mPropPage.mPropertiesListView.mColumns[1].mMinWidth = GS!(20);
|
||||
mPropPage.mPropertiesListView.mColumns[1].mWidth = GS!(20);
|
||||
|
||||
mPropPage.mPropertiesListView.AddColumn(180, "Remote URL");
|
||||
mPropPage.mPropertiesListView.mColumns[2].mMinWidth = GS!(100);
|
||||
|
||||
mPropPage.mPropertiesListView.AddColumn(180, "Ver Constraint");
|
||||
mPropPage.mPropertiesListView.mColumns[3].mMinWidth = GS!(100);
|
||||
|
||||
//mDependencyValuesMap = new .();
|
||||
|
||||
var root = (DarkListViewItem)mPropPage.mPropertiesListView.GetRoot();
|
||||
var category = root;
|
||||
|
||||
List<String> projectNames = scope List<String>();
|
||||
for (int32 projectIdx = 0; projectIdx < IDEApp.sApp.mWorkspace.mProjects.Count; projectIdx++)
|
||||
{
|
||||
var project = IDEApp.sApp.mWorkspace.mProjects[projectIdx];
|
||||
/*if (project == mProject)
|
||||
continue;*/
|
||||
projectNames.Add(project.mProjectName);
|
||||
}
|
||||
|
||||
/*for (var dep in mProject.mDependencies)
|
||||
{
|
||||
if (!projectNames.Contains(dep.mProjectName))
|
||||
projectNames.Add(dep.mProjectName);
|
||||
}*/
|
||||
|
||||
|
||||
projectNames.Sort(scope (a, b) => String.Compare(a, b, true));
|
||||
|
||||
for (var projectName in projectNames)
|
||||
{
|
||||
var dependencyEntry = new DependencyEntry();
|
||||
|
||||
for (var projectSpec in gApp.mWorkspace.mProjectSpecs)
|
||||
{
|
||||
if (projectSpec.mProjectName == projectName)
|
||||
{
|
||||
dependencyEntry.mUse = true;
|
||||
if (projectSpec.mVerSpec case .Git(let url, let ver))
|
||||
{
|
||||
dependencyEntry.mURL = new .(url);
|
||||
if (ver != null)
|
||||
dependencyEntry.mVersion = new .(ver.mVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*var verSpec = mProject.GetDependency(projectName, false);
|
||||
if (verSpec != null)
|
||||
{
|
||||
dependencyEntry.mUse = true;
|
||||
if (verSpec case .Git(let url, let ver))
|
||||
{
|
||||
dependencyEntry.mURL = new .(url);
|
||||
if (ver != null)
|
||||
dependencyEntry.mVersion = new .(ver.mVersion);
|
||||
}
|
||||
}
|
||||
mDependencyValuesMap[new String(projectName)] = dependencyEntry;*/
|
||||
|
||||
var (listViewItem, propItem) = AddPropertiesItem(category, projectName);
|
||||
if (IDEApp.sApp.mWorkspace.FindProject(projectName) == null)
|
||||
listViewItem.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFFF6060);
|
||||
|
||||
var subItem = (DarkListViewItem)listViewItem.CreateSubItem(1);
|
||||
|
||||
var checkbox = new DarkCheckBox();
|
||||
checkbox.Checked = dependencyEntry.mUse;
|
||||
checkbox.Resize(0, 0, DarkTheme.sUnitSize, DarkTheme.sUnitSize);
|
||||
subItem.AddWidget(checkbox);
|
||||
|
||||
PropEntry[] propEntries = new PropEntry[1];
|
||||
|
||||
PropEntry propEntry = new PropEntry();
|
||||
propEntry.mTarget = dependencyEntry;
|
||||
propEntry.mOrigValue = Variant.Create(dependencyEntry, true);
|
||||
propEntry.mCurValue = Variant.Create(new DependencyEntry(dependencyEntry), true);
|
||||
|
||||
propEntry.mListViewItem = listViewItem;
|
||||
propEntry.mCheckBox = checkbox;
|
||||
propEntry.mApplyAction = new () =>
|
||||
{
|
||||
bool updateProjectLock = false;
|
||||
|
||||
var dependencyEntry = propEntry.mCurValue.Get<DependencyEntry>();
|
||||
|
||||
VerSpec verSpec = default;
|
||||
if (dependencyEntry.mUse)
|
||||
{
|
||||
if (dependencyEntry.mURL != null)
|
||||
verSpec = .Git(new .(dependencyEntry.mURL), (dependencyEntry.mVersion != null) ? new .(dependencyEntry.mVersion) : null);
|
||||
else if (dependencyEntry.mVersion != null)
|
||||
verSpec = .SemVer(new .(dependencyEntry.mVersion));
|
||||
else
|
||||
verSpec = .SemVer(new .("*"));
|
||||
}
|
||||
|
||||
FindBlock: do
|
||||
{
|
||||
for (var projectSpec in gApp.mWorkspace.mProjectSpecs)
|
||||
{
|
||||
if (projectSpec.mProjectName == projectName)
|
||||
{
|
||||
if (!dependencyEntry.mUse)
|
||||
{
|
||||
if (projectSpec.mVerSpec case .Git)
|
||||
updateProjectLock = true;
|
||||
@projectSpec.Remove();
|
||||
delete projectSpec;
|
||||
break FindBlock;
|
||||
}
|
||||
|
||||
if (projectSpec.mVerSpec != verSpec)
|
||||
{
|
||||
if ((projectSpec.mVerSpec case .Git) ||
|
||||
(verSpec case .Git))
|
||||
updateProjectLock = true;
|
||||
}
|
||||
|
||||
projectSpec.mVerSpec.Dispose();
|
||||
projectSpec.mVerSpec = verSpec;
|
||||
break FindBlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (dependencyEntry.mUse)
|
||||
{
|
||||
Workspace.ProjectSpec projectSpec = new .();
|
||||
projectSpec.mProjectName = new .(projectName);
|
||||
projectSpec.mVerSpec = verSpec;
|
||||
gApp.mWorkspace.mProjectSpecs.Add(projectSpec);
|
||||
if (verSpec case .Git)
|
||||
updateProjectLock = true;
|
||||
var origDependencyEntry = propEntry.mOrigValue.Get<DependencyEntry>();
|
||||
origDependencyEntry.Set(dependencyEntry);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateProjectLock)
|
||||
mUpdateProjectLocks.Add(new .(listViewItem.Label));
|
||||
};
|
||||
|
||||
checkbox.mOnMouseUp.Add(new (evt) =>
|
||||
{
|
||||
var dependencyEntry = propEntry.mCurValue.Get<DependencyEntry>();
|
||||
dependencyEntry.mUse = !dependencyEntry.mUse;
|
||||
if (dependencyEntry.mUse)
|
||||
{
|
||||
var projectName = listViewItem.Label;
|
||||
|
||||
for (var projectSpec in gApp.mWorkspace.mProjectSpecs)
|
||||
{
|
||||
if (projectSpec.mProjectName == projectName)
|
||||
{
|
||||
if (projectSpec.mVerSpec case .Git(let url, let ver))
|
||||
{
|
||||
dependencyEntry.SetValue(1, url);
|
||||
dependencyEntry.SetValue(2, ver.mVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
var propEntries = mPropPage.mPropEntries[listViewItem];
|
||||
UpdatePropertyValue(propEntries);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteAndNullify!(dependencyEntry.mURL);
|
||||
DeleteAndNullify!(dependencyEntry.mVersion);
|
||||
var propEntries = mPropPage.mPropEntries[listViewItem];
|
||||
UpdatePropertyValue(propEntries);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
subItem = (.)listViewItem.GetOrCreateSubItem(2);
|
||||
if (dependencyEntry.mURL != null)
|
||||
subItem.Label = dependencyEntry.mURL;
|
||||
subItem.mOnMouseDown.Add(new => DepPropValueClicked);
|
||||
|
||||
subItem = (.)listViewItem.GetOrCreateSubItem(3);
|
||||
if (dependencyEntry.mVersion != null)
|
||||
subItem.Label = dependencyEntry.mVersion;
|
||||
subItem.mOnMouseDown.Add(new => DepPropValueClicked);
|
||||
|
||||
propEntries[0] = propEntry;
|
||||
mPropPage.mPropEntries[listViewItem] = propEntries;
|
||||
}
|
||||
}
|
||||
|
||||
protected void DepPropValueClicked(MouseEvent theEvent)
|
||||
{
|
||||
DarkListViewItem clickedItem = (DarkListViewItem)theEvent.mSender;
|
||||
if (clickedItem.mColumnIdx == 0)
|
||||
{
|
||||
clickedItem.mListView.SetFocus();
|
||||
clickedItem.mListView.GetRoot().SelectItemExclusively(clickedItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (theEvent.mX != -1)
|
||||
{
|
||||
clickedItem.mListView.GetRoot().SelectItemExclusively(null);
|
||||
}
|
||||
|
||||
DarkListViewItem item = (DarkListViewItem)clickedItem;
|
||||
DarkListViewItem rootItem = (DarkListViewItem)clickedItem.GetSubItem(0);
|
||||
|
||||
PropEntry[] propertyEntries = mPropPage.mPropEntries[rootItem];
|
||||
if (propertyEntries[0].mDisabled)
|
||||
return;
|
||||
EditValue(item, propertyEntries, clickedItem.mColumnIdx - 1);
|
||||
}
|
||||
|
||||
void PopulateBeefGlobalOptions()
|
||||
{
|
||||
var root = (DarkListViewItem)mPropPage.mPropertiesListView.GetRoot();
|
||||
|
@ -939,6 +1170,7 @@ namespace IDE.ui
|
|||
{
|
||||
base.Close();
|
||||
SetWorkspaceData(false);
|
||||
gApp.NotifyProjectVersionLocks(mUpdateProjectLocks);
|
||||
}
|
||||
|
||||
public override void CalcSize()
|
||||
|
|
326
IDE/src/util/GitManager.bf
Normal file
326
IDE/src/util/GitManager.bf
Normal file
|
@ -0,0 +1,326 @@
|
|||
#pragma warning disable 168
|
||||
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
|
||||
namespace IDE.util;
|
||||
|
||||
class GitManager
|
||||
{
|
||||
public enum Error
|
||||
{
|
||||
Unknown
|
||||
}
|
||||
|
||||
public class GitInstance : RefCounted
|
||||
{
|
||||
public class TagInfo
|
||||
{
|
||||
public String mHash ~ delete _;
|
||||
public String mTag ~ delete _;
|
||||
}
|
||||
|
||||
public GitManager mGitManager;
|
||||
public bool mFailed;
|
||||
public bool mDone;
|
||||
public bool mStarted;
|
||||
public bool mRemoved;
|
||||
|
||||
public String mArgs ~ delete _;
|
||||
public String mPath ~ delete _;
|
||||
public float mProgress;
|
||||
public float mProgressRecv;
|
||||
public float mProgressDeltas;
|
||||
public float mProgressFiles;
|
||||
|
||||
public Stopwatch mStopwatch = new .()..Start() ~ delete _;
|
||||
|
||||
public SpawnedProcess mProcess ~ delete _;
|
||||
public Monitor mMonitor = new .() ~ delete _;
|
||||
public List<String> mDeferredOutput = new .() ~ DeleteContainerAndItems!(_);
|
||||
public List<TagInfo> mTagInfos = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
public Thread mOutputThread ~ delete _;
|
||||
public Thread mErrorThread ~ delete _;
|
||||
|
||||
public this(GitManager gitManager)
|
||||
{
|
||||
mGitManager = gitManager;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
IDEUtils.SafeKill(mProcess);
|
||||
mOutputThread?.Join();
|
||||
mErrorThread?.Join();
|
||||
|
||||
if (!mRemoved)
|
||||
mGitManager.mGitInstances.Remove(this);
|
||||
}
|
||||
|
||||
public void Init(StringView args, StringView path)
|
||||
{
|
||||
mArgs = new .(args);
|
||||
if (path != default)
|
||||
mPath = new .(path);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (mStarted)
|
||||
return;
|
||||
mStarted = true;
|
||||
|
||||
ProcessStartInfo psi = scope ProcessStartInfo();
|
||||
|
||||
String gitPath = scope .();
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
Path.GetAbsolutePath(gApp.mInstallDir, "git/cmd/git.exe", gitPath);
|
||||
if (!File.Exists(gitPath))
|
||||
gitPath.Clear();
|
||||
|
||||
if (gitPath.IsEmpty)
|
||||
{
|
||||
Path.GetAbsolutePath(gApp.mInstallDir, "../../bin/git/cmd/git.exe", gitPath);
|
||||
if (!File.Exists(gitPath))
|
||||
gitPath.Clear();
|
||||
}
|
||||
|
||||
if (gitPath.IsEmpty)
|
||||
{
|
||||
Path.GetAbsolutePath(gApp.mInstallDir, "../../../bin/git/cmd/git.exe", gitPath);
|
||||
if (!File.Exists(gitPath))
|
||||
gitPath.Clear();
|
||||
}
|
||||
#endif
|
||||
if (gitPath.IsEmpty)
|
||||
gitPath.Set("git");
|
||||
|
||||
psi.SetFileName(gitPath);
|
||||
psi.SetArguments(mArgs);
|
||||
if (mPath != null)
|
||||
psi.SetWorkingDirectory(mPath);
|
||||
psi.UseShellExecute = false;
|
||||
psi.RedirectStandardError = true;
|
||||
psi.RedirectStandardOutput = true;
|
||||
psi.CreateNoWindow = true;
|
||||
|
||||
mProcess = new SpawnedProcess();
|
||||
if (mProcess.Start(psi) case .Err)
|
||||
{
|
||||
gApp.OutputErrorLine("Failed to execute Git");
|
||||
mFailed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mOutputThread = new Thread(new => ReadOutputThread);
|
||||
mOutputThread.Start(false);
|
||||
|
||||
mErrorThread = new Thread(new => ReadErrorThread);
|
||||
mErrorThread.Start(false);
|
||||
}
|
||||
|
||||
public void ReadOutputThread()
|
||||
{
|
||||
FileStream fileStream = scope FileStream();
|
||||
if (mProcess.AttachStandardOutput(fileStream) case .Err)
|
||||
return;
|
||||
StreamReader streamReader = scope StreamReader(fileStream, null, false, 4096);
|
||||
|
||||
int count = 0;
|
||||
while (true)
|
||||
{
|
||||
count++;
|
||||
var buffer = scope String();
|
||||
if (streamReader.ReadLine(buffer) case .Err)
|
||||
break;
|
||||
using (mMonitor.Enter())
|
||||
{
|
||||
mDeferredOutput.Add(new .(buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadErrorThread()
|
||||
{
|
||||
FileStream fileStream = scope FileStream();
|
||||
if (mProcess.AttachStandardError(fileStream) case .Err)
|
||||
return;
|
||||
StreamReader streamReader = scope StreamReader(fileStream, null, false, 4096);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var buffer = scope String();
|
||||
if (streamReader.ReadLine(buffer) case .Err)
|
||||
break;
|
||||
|
||||
using (mMonitor.Enter())
|
||||
{
|
||||
//mDeferredOutput.Add(new $"{mStopwatch.ElapsedMilliseconds / 1000.0:0.0}: {buffer}");
|
||||
mDeferredOutput.Add(new .(buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
using (mMonitor.Enter())
|
||||
{
|
||||
while (!mDeferredOutput.IsEmpty)
|
||||
{
|
||||
var line = mDeferredOutput.PopFront();
|
||||
defer delete line;
|
||||
//Debug.WriteLine($"GIT: {line}");
|
||||
|
||||
if (line.StartsWith("Cloning into "))
|
||||
{
|
||||
// May be starting a submodule
|
||||
mProgressRecv = 0;
|
||||
mProgressDeltas = 0;
|
||||
mProgressFiles = 0;
|
||||
}
|
||||
|
||||
if (line.StartsWith("remote: Counting objects"))
|
||||
{
|
||||
mProgressRecv = 0.001f;
|
||||
}
|
||||
|
||||
if (line.StartsWith("Receiving objects: "))
|
||||
{
|
||||
var pctStr = line.Substring("Receiving objects: ".Length, 3)..Trim();
|
||||
mProgressRecv = float.Parse(pctStr).GetValueOrDefault() / 100.0f;
|
||||
}
|
||||
|
||||
if (line.StartsWith("Resolving deltas: "))
|
||||
{
|
||||
var pctStr = line.Substring("Resolving deltas: ".Length, 3)..Trim();
|
||||
mProgressDeltas = float.Parse(pctStr).GetValueOrDefault() / 100.0f;
|
||||
mProgressRecv = 1.0f;
|
||||
}
|
||||
|
||||
if (line.StartsWith("Updating files: "))
|
||||
{
|
||||
var pctStr = line.Substring("Updating files: ".Length, 3)..Trim();
|
||||
mProgressFiles = float.Parse(pctStr).GetValueOrDefault() / 100.0f;
|
||||
mProgressRecv = 1.0f;
|
||||
mProgressDeltas = 1.0f;
|
||||
}
|
||||
|
||||
StringView version = default;
|
||||
|
||||
int refTagIdx = line.IndexOf("\trefs/tags/");
|
||||
if (refTagIdx == 40)
|
||||
version = line.Substring(40 + "\trefs/tags/".Length);
|
||||
|
||||
if ((line.Length == 45) && (line.EndsWith("HEAD")))
|
||||
version = "HEAD";
|
||||
|
||||
if (!version.IsEmpty)
|
||||
{
|
||||
TagInfo tagInfo = new .();
|
||||
tagInfo.mHash = new .(line, 0, 40);
|
||||
tagInfo.mTag = new .(version);
|
||||
mTagInfos.Add(tagInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float pct = 0;
|
||||
if (mProgressRecv > 0)
|
||||
pct = 0.1f + (mProgressRecv * 0.3f) + (mProgressDeltas * 0.4f) + (mProgressFiles * 0.2f);
|
||||
|
||||
if (pct > mProgress)
|
||||
{
|
||||
mProgress = pct;
|
||||
//Debug.WriteLine($"Completed Pct: {pct}");
|
||||
}
|
||||
|
||||
if (mProcess.WaitFor(0))
|
||||
{
|
||||
if (mProcess.ExitCode != 0)
|
||||
mFailed = true;
|
||||
mDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
if (!mProcess.WaitFor(0))
|
||||
{
|
||||
//Debug.WriteLine($"GitManager Cancel {mProcess.ProcessId}");
|
||||
IDEUtils.SafeKill(mProcess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public const int sMaxActiveGitInstances = 4;
|
||||
|
||||
public List<GitInstance> mGitInstances = new .() ~
|
||||
{
|
||||
for (var gitInstance in _)
|
||||
gitInstance.ReleaseRef();
|
||||
delete _;
|
||||
};
|
||||
|
||||
public void Init()
|
||||
{
|
||||
//StartGit("-v");
|
||||
|
||||
//Repository repository = Clone("https://github.com/llvm/llvm-project", "c:/temp/__LLVM");
|
||||
|
||||
//Repository repository = Clone("https://github.com/Starpelly/raylib-beef", "c:/temp/__RAYLIB");
|
||||
/*while (true)
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
Debug.WriteLine($"Repository {repository.mStatus} {repository.GetCompletedPct()}");
|
||||
}*/
|
||||
}
|
||||
|
||||
public GitInstance StartGit(StringView cmd, StringView path = default)
|
||||
{
|
||||
//Debug.WriteLine($"GIT STARTING: {cmd} in {path}");
|
||||
|
||||
GitInstance gitInst = new .(this);
|
||||
gitInst.Init(cmd, path);
|
||||
mGitInstances.Add(gitInst);
|
||||
return gitInst;
|
||||
}
|
||||
|
||||
public GitInstance Clone(StringView url, StringView path)
|
||||
{
|
||||
return StartGit(scope $"clone -v --progress --recurse-submodules {url} \"{path}\"");
|
||||
}
|
||||
|
||||
public GitInstance Checkout(StringView path, StringView hash)
|
||||
{
|
||||
return StartGit(scope $"checkout -b BeefManaged {hash}", path);
|
||||
}
|
||||
|
||||
public GitInstance GetTags(StringView url)
|
||||
{
|
||||
return StartGit(scope $"ls-remote {url}");
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
for (var gitInstance in mGitInstances)
|
||||
{
|
||||
if (@gitInstance.Index >= sMaxActiveGitInstances)
|
||||
break;
|
||||
|
||||
if (!gitInstance.mStarted)
|
||||
gitInstance.Start();
|
||||
gitInstance.Update();
|
||||
|
||||
if (gitInstance.mDone)
|
||||
{
|
||||
@gitInstance.Remove();
|
||||
gitInstance.mRemoved = true;
|
||||
gitInstance.ReleaseRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +1,397 @@
|
|||
#pragma warning disable 168
|
||||
|
||||
using System;
|
||||
using IDE.Util;
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
using static Git.GitApi;
|
||||
#define SUPPORT_GIT
|
||||
#endif
|
||||
using System.Collections;
|
||||
using System.Security.Cryptography;
|
||||
using System.IO;
|
||||
using Beefy.utils;
|
||||
using System.Threading;
|
||||
|
||||
namespace IDE.util
|
||||
{
|
||||
class PackMan
|
||||
{
|
||||
class GitHelper
|
||||
public class WorkItem
|
||||
{
|
||||
static bool sInitialized;
|
||||
public enum Kind
|
||||
{
|
||||
None,
|
||||
FindVersion,
|
||||
Clone,
|
||||
Checkout
|
||||
}
|
||||
|
||||
public this()
|
||||
public Kind mKind;
|
||||
public String mProjectName ~ delete _;
|
||||
public String mURL ~ delete _;
|
||||
public List<String> mConstraints ~ DeleteContainerAndItems!(_);
|
||||
public String mTag ~ delete _;
|
||||
public String mHash ~ delete _;
|
||||
public String mPath ~ delete _;
|
||||
public GitManager.GitInstance mGitInstance ~ _?.ReleaseRef();
|
||||
|
||||
public ~this()
|
||||
{
|
||||
if (!sInitialized)
|
||||
{
|
||||
#if SUPPORT_GIT
|
||||
#unwarn
|
||||
var result = git_libgit2_init();
|
||||
sInitialized = true;
|
||||
#endif
|
||||
mGitInstance?.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public List<WorkItem> mWorkItems = new .() ~ DeleteContainerAndItems!(_);
|
||||
public bool mInitialized;
|
||||
public String mManagedPath ~ delete _;
|
||||
public bool mFailed;
|
||||
|
||||
public void Fail(StringView error)
|
||||
{
|
||||
gApp.OutputErrorLine(error);
|
||||
|
||||
if (!mFailed)
|
||||
{
|
||||
mFailed = true;
|
||||
gApp.[Friend]FlushDeferredLoadProjects();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckInit()
|
||||
{
|
||||
if (mInitialized)
|
||||
return true;
|
||||
|
||||
if (gApp.mBeefConfig.mManagedLibPath.IsEmpty)
|
||||
return false;
|
||||
|
||||
mManagedPath = new .(gApp.mBeefConfig.mManagedLibPath);
|
||||
mInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void GetPath(StringView url, StringView hash, String outPath)
|
||||
{
|
||||
//var urlHash = SHA256.Hash(url.ToRawData()).ToString(.. scope .());
|
||||
//outPath.AppendF($"{mManagedPath}/{urlHash}/{hash}");
|
||||
outPath.AppendF($"{mManagedPath}/{hash}");
|
||||
}
|
||||
|
||||
public bool CheckLock(StringView projectName, String outPath)
|
||||
{
|
||||
if (!CheckInit())
|
||||
return false;
|
||||
|
||||
if (gApp.mWantUpdateVersionLocks != null)
|
||||
{
|
||||
if ((gApp.mWantUpdateVersionLocks.IsEmpty) || (gApp.mWantUpdateVersionLocks.ContainsAlt(projectName)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gApp.mWorkspace.mProjectLockMap.TryGetAlt(projectName, ?, var lock))
|
||||
return false;
|
||||
|
||||
switch (lock)
|
||||
{
|
||||
case .Git(let url, let tag, let hash):
|
||||
var path = GetPath(url, hash, .. scope .());
|
||||
var managedFilePath = scope $"{path}/BeefManaged.toml";
|
||||
if (File.Exists(managedFilePath))
|
||||
{
|
||||
outPath.Append(path);
|
||||
outPath.Append("/BeefProj.toml");
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CloneCompleted(StringView projectName, StringView url, StringView tag, StringView hash, StringView path)
|
||||
{
|
||||
gApp.mWorkspace.SetLock(projectName, .Git(new .(url), new .(tag), new .(hash)));
|
||||
|
||||
StructuredData sd = scope .();
|
||||
sd.CreateNew();
|
||||
sd.Add("FileVersion", 1);
|
||||
sd.Add("Version", tag);
|
||||
sd.Add("GitURL", url);
|
||||
sd.Add("GitTag", tag);
|
||||
sd.Add("GitHash", hash);
|
||||
var tomlText = sd.ToTOML(.. scope .());
|
||||
var managedFilePath = scope $"{path}/BeefManaged.toml";
|
||||
File.WriteAllText(managedFilePath, tomlText).IgnoreError();
|
||||
}
|
||||
|
||||
public void GetWithHash(StringView projectName, StringView url, StringView tag, StringView hash)
|
||||
{
|
||||
if (!CheckInit())
|
||||
return;
|
||||
|
||||
String destPath = GetPath(url, hash, .. scope .());
|
||||
var urlPath = Path.GetDirectoryPath(destPath, .. scope .());
|
||||
Directory.CreateDirectory(urlPath).IgnoreError();
|
||||
if (Directory.Exists(destPath))
|
||||
{
|
||||
var managedFilePath = scope $"{destPath}/BeefManaged.toml";
|
||||
if (File.Exists(managedFilePath))
|
||||
{
|
||||
if (gApp.mVerbosity >= .Normal)
|
||||
{
|
||||
if (tag.IsEmpty)
|
||||
gApp.OutputLine($"Git selecting library '{projectName}' at {hash.Substring(0, 7)}");
|
||||
else
|
||||
gApp.OutputLine($"Git selecting library '{projectName}' tag '{tag}' at {hash.Substring(0, 7)}");
|
||||
}
|
||||
|
||||
CloneCompleted(projectName, url, tag, hash, destPath);
|
||||
ProjectReady(projectName, destPath);
|
||||
return;
|
||||
}
|
||||
|
||||
String tempDir = new $"{destPath}__{(int32)Internal.GetTickCountMicro():X}";
|
||||
|
||||
//if (Directory.DelTree(destPath) case .Err)
|
||||
if (Directory.Move(destPath, tempDir) case .Err)
|
||||
{
|
||||
delete tempDir;
|
||||
Fail(scope $"Failed to remove directory '{destPath}'");
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPool.QueueUserWorkItem(new () =>
|
||||
{
|
||||
Directory.DelTree(tempDir);
|
||||
}
|
||||
~
|
||||
{
|
||||
delete tempDir;
|
||||
});
|
||||
}
|
||||
|
||||
if (gApp.mVerbosity >= .Normal)
|
||||
{
|
||||
if (tag.IsEmpty)
|
||||
gApp.OutputLine($"Git cloning library '{projectName}' at {hash.Substring(0, 7)}...");
|
||||
else
|
||||
gApp.OutputLine($"Git cloning library '{projectName}' tag '{tag}' at {hash.Substring(0, 7)}");
|
||||
}
|
||||
|
||||
WorkItem workItem = new .();
|
||||
workItem.mKind = .Clone;
|
||||
workItem.mProjectName = new .(projectName);
|
||||
workItem.mURL = new .(url);
|
||||
workItem.mTag = new .(tag);
|
||||
workItem.mHash = new .(hash);
|
||||
workItem.mPath = new .(destPath);
|
||||
mWorkItems.Add(workItem);
|
||||
}
|
||||
|
||||
public void GetWithVersion(StringView projectName, StringView url, SemVer semVer)
|
||||
{
|
||||
if (!CheckInit())
|
||||
return;
|
||||
|
||||
bool ignoreLock = false;
|
||||
if (gApp.mWantUpdateVersionLocks != null)
|
||||
{
|
||||
if ((gApp.mWantUpdateVersionLocks.IsEmpty) || (gApp.mWantUpdateVersionLocks.ContainsAlt(projectName)))
|
||||
ignoreLock = true;
|
||||
}
|
||||
|
||||
if ((!ignoreLock) && (gApp.mWorkspace.mProjectLockMap.TryGetAlt(projectName, ?, var lock)))
|
||||
{
|
||||
switch (lock)
|
||||
{
|
||||
case .Git(let checkURL, let tag, let hash):
|
||||
if (checkURL == url)
|
||||
GetWithHash(projectName, url, tag, hash);
|
||||
return;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
if (gApp.mVerbosity >= .Normal)
|
||||
gApp.OutputLine($"Git retrieving version list for '{projectName}'");
|
||||
|
||||
WorkItem workItem = new .();
|
||||
workItem.mKind = .FindVersion;
|
||||
workItem.mProjectName = new .(projectName);
|
||||
workItem.mURL = new .(url);
|
||||
if (semVer != null)
|
||||
workItem.mConstraints = new .() { new String(semVer.mVersion) };
|
||||
mWorkItems.Add(workItem);
|
||||
}
|
||||
|
||||
public void UpdateGitConstraint(StringView url, SemVer semVer)
|
||||
{
|
||||
for (var workItem in mWorkItems)
|
||||
{
|
||||
if ((workItem.mKind == .FindVersion) && (workItem.mURL == url))
|
||||
{
|
||||
if (workItem.mConstraints == null)
|
||||
workItem.mConstraints = new .();
|
||||
workItem.mConstraints.Add(new String(semVer.mVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Checkout(StringView projectName, StringView url, StringView path, StringView tag, StringView hash)
|
||||
{
|
||||
if (!CheckInit())
|
||||
return;
|
||||
|
||||
WorkItem workItem = new .();
|
||||
workItem.mKind = .Checkout;
|
||||
workItem.mProjectName = new .(projectName);
|
||||
workItem.mURL = new .(url);
|
||||
workItem.mTag = new .(tag);
|
||||
workItem.mHash = new .(hash);
|
||||
workItem.mPath = new .(path);
|
||||
mWorkItems.Add(workItem);
|
||||
}
|
||||
|
||||
public void ProjectReady(StringView projectName, StringView path)
|
||||
{
|
||||
if (var project = gApp.mWorkspace.FindProject(projectName))
|
||||
{
|
||||
String projectPath = scope $"{path}/BeefProj.toml";
|
||||
|
||||
project.mProjectPath.Set(projectPath);
|
||||
gApp.RetryProjectLoad(project, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
bool executingGit = false;
|
||||
|
||||
// First handle active git items
|
||||
for (var workItem in mWorkItems)
|
||||
{
|
||||
if (workItem.mGitInstance == null)
|
||||
continue;
|
||||
|
||||
if (!workItem.mGitInstance.mDone)
|
||||
{
|
||||
executingGit = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!workItem.mGitInstance.mFailed)
|
||||
{
|
||||
switch (workItem.mKind)
|
||||
{
|
||||
case .FindVersion:
|
||||
gApp.CompilerLog("");
|
||||
|
||||
StringView bestTag = default;
|
||||
StringView bestHash = default;
|
||||
|
||||
for (var tag in workItem.mGitInstance.mTagInfos)
|
||||
{
|
||||
if ((tag.mTag == "HEAD") && (workItem.mConstraints == null))
|
||||
bestHash = tag.mHash;
|
||||
else if (workItem.mConstraints != null)
|
||||
{
|
||||
bool hasMatch = false;
|
||||
for (var constraint in workItem.mConstraints)
|
||||
{
|
||||
if (SemVer.IsVersionMatch(tag.mTag, constraint))
|
||||
{
|
||||
hasMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMatch)
|
||||
{
|
||||
if ((bestTag.IsEmpty) || (SemVer.Compare(tag.mTag, bestTag) > 0))
|
||||
{
|
||||
bestTag = tag.mTag;
|
||||
bestHash = tag.mHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestHash != default)
|
||||
{
|
||||
GetWithHash(workItem.mProjectName, workItem.mURL, bestTag, bestHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
String constraints = scope .();
|
||||
for (var constraint in workItem.mConstraints)
|
||||
{
|
||||
if (!constraints.IsEmpty)
|
||||
constraints.Append(", ");
|
||||
constraints.Append('\'');
|
||||
constraints.Append(constraint);
|
||||
constraints.Append('\'');
|
||||
}
|
||||
|
||||
Fail(scope $"Failed to locate version for '{workItem.mProjectName}' with constraints '{constraints}'");
|
||||
}
|
||||
case .Clone:
|
||||
Checkout(workItem.mProjectName, workItem.mURL, workItem.mPath, workItem.mTag, workItem.mHash);
|
||||
case .Checkout:
|
||||
CloneCompleted(workItem.mProjectName, workItem.mURL, workItem.mTag, workItem.mHash, workItem.mPath);
|
||||
ProjectReady(workItem.mProjectName, workItem.mPath);
|
||||
|
||||
if (gApp.mVerbosity >= .Normal)
|
||||
gApp.OutputLine($"Git cloning library '{workItem.mProjectName}' done.");
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@workItem.Remove();
|
||||
delete workItem;
|
||||
}
|
||||
|
||||
if (!executingGit)
|
||||
{
|
||||
// First handle active git items
|
||||
for (var workItem in mWorkItems)
|
||||
{
|
||||
if (workItem.mGitInstance != null)
|
||||
continue;
|
||||
|
||||
switch (workItem.mKind)
|
||||
{
|
||||
case .FindVersion:
|
||||
workItem.mGitInstance = gApp.mGitManager.GetTags(workItem.mURL)..AddRef();
|
||||
case .Checkout:
|
||||
workItem.mGitInstance = gApp.mGitManager.Checkout(workItem.mPath, workItem.mHash)..AddRef();
|
||||
case .Clone:
|
||||
workItem.mGitInstance = gApp.mGitManager.Clone(workItem.mURL, workItem.mPath)..AddRef();
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetHashFromFilePath(StringView filePath, String path)
|
||||
{
|
||||
if (mManagedPath == null)
|
||||
return;
|
||||
|
||||
if (!filePath.StartsWith(mManagedPath))
|
||||
return;
|
||||
|
||||
StringView hashPart = filePath.Substring(mManagedPath.Length);
|
||||
if (hashPart.Length < 42)
|
||||
return;
|
||||
|
||||
hashPart.RemoveFromStart(1);
|
||||
hashPart.Length = 40;
|
||||
path.Append(hashPart);
|
||||
}
|
||||
|
||||
public void CancelAll()
|
||||
{
|
||||
if (mWorkItems.IsEmpty)
|
||||
return;
|
||||
|
||||
Fail("Aborted project transfer");
|
||||
mWorkItems.ClearAndDeleteItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,51 @@ using System;
|
|||
|
||||
namespace IDE.Util
|
||||
{
|
||||
[Reflect]
|
||||
class SemVer
|
||||
{
|
||||
public struct Parts
|
||||
{
|
||||
public enum Kind
|
||||
{
|
||||
case Empty;
|
||||
case Num(int32 val);
|
||||
case Wild;
|
||||
|
||||
public int32 NumOrDefault
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this case .Num(let val))
|
||||
return val;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Kind[3] mPart;
|
||||
public StringView mPreRelease;
|
||||
|
||||
public Kind Major => mPart[0];
|
||||
public Kind Minor => mPart[1];
|
||||
public Kind Patch => mPart[2];
|
||||
}
|
||||
|
||||
enum CompareKind
|
||||
{
|
||||
Caret, // Default
|
||||
Tilde,
|
||||
Equal,
|
||||
Gt,
|
||||
Gte,
|
||||
Lt,
|
||||
Lte
|
||||
}
|
||||
|
||||
public String mVersion ~ delete _;
|
||||
|
||||
public bool IsEmpty => String.IsNullOrEmpty(mVersion);
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
|
@ -27,5 +68,240 @@ namespace IDE.Util
|
|||
mVersion = new String(ver);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<Parts> GetParts(StringView version)
|
||||
{
|
||||
int startIdx = 0;
|
||||
int partIdx = 0;
|
||||
|
||||
if (version.IsEmpty)
|
||||
return .Err;
|
||||
|
||||
if (version.StartsWith("V", .OrdinalIgnoreCase))
|
||||
startIdx++;
|
||||
|
||||
Parts parts = .();
|
||||
|
||||
Result<void> SetPart(Parts.Kind kind)
|
||||
{
|
||||
if (partIdx >= 3)
|
||||
return .Err;
|
||||
parts.mPart[partIdx] = kind;
|
||||
partIdx++;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
Result<void> FlushPart(int i)
|
||||
{
|
||||
StringView partStr = version.Substring(startIdx, i - startIdx);
|
||||
if (!partStr.IsEmpty)
|
||||
{
|
||||
int32 partNum = Try!(int32.Parse(partStr));
|
||||
Try!(SetPart(.Num(partNum)));
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
for (int i in startIdx ..< version.Length)
|
||||
{
|
||||
char8 c = version[i];
|
||||
if (c.IsWhiteSpace)
|
||||
return .Err;
|
||||
|
||||
if (c == '.')
|
||||
{
|
||||
Try!(FlushPart(i));
|
||||
startIdx = i + 1;
|
||||
continue;
|
||||
}
|
||||
else if (c.IsNumber)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (c == '-')
|
||||
{
|
||||
if (partIdx == 0)
|
||||
return .Err;
|
||||
parts.mPreRelease = version.Substring(i);
|
||||
return .Ok(parts);
|
||||
}
|
||||
else if (c == '*')
|
||||
{
|
||||
Try!(SetPart(.Wild));
|
||||
continue;
|
||||
}
|
||||
|
||||
return .Err;
|
||||
}
|
||||
Try!(FlushPart(version.Length));
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
public Result<Parts> GetParts()
|
||||
{
|
||||
return GetParts(mVersion);
|
||||
}
|
||||
|
||||
public static bool IsVersionMatch(StringView fullVersion, StringView wildcard)
|
||||
{
|
||||
int commaPos = wildcard.IndexOf(',');
|
||||
if (commaPos != -1)
|
||||
return IsVersionMatch(fullVersion, wildcard.Substring(0, commaPos)..Trim()) && IsVersionMatch(fullVersion, wildcard.Substring(commaPos + 1)..Trim());
|
||||
|
||||
var wildcard;
|
||||
|
||||
wildcard.Trim();
|
||||
CompareKind compareKind = .Caret;
|
||||
if (wildcard.StartsWith('^'))
|
||||
{
|
||||
compareKind = .Caret;
|
||||
wildcard.RemoveFromStart(1);
|
||||
}
|
||||
else if (wildcard.StartsWith('~'))
|
||||
{
|
||||
compareKind = .Tilde;
|
||||
wildcard.RemoveFromStart(1);
|
||||
}
|
||||
else if (wildcard.StartsWith('='))
|
||||
{
|
||||
compareKind = .Equal;
|
||||
wildcard.RemoveFromStart(1);
|
||||
}
|
||||
else if (wildcard.StartsWith('>'))
|
||||
{
|
||||
compareKind = .Gt;
|
||||
wildcard.RemoveFromStart(1);
|
||||
if (wildcard.StartsWith('='))
|
||||
{
|
||||
compareKind = .Gte;
|
||||
wildcard.RemoveFromStart(1);
|
||||
}
|
||||
}
|
||||
else if (wildcard.StartsWith('<'))
|
||||
{
|
||||
compareKind = .Lt;
|
||||
wildcard.RemoveFromStart(1);
|
||||
if (wildcard.StartsWith('='))
|
||||
{
|
||||
compareKind = .Lte;
|
||||
wildcard.RemoveFromStart(1);
|
||||
}
|
||||
}
|
||||
wildcard.Trim();
|
||||
|
||||
// Does we include equality?
|
||||
if ((compareKind != .Gt) && (compareKind != .Lt))
|
||||
{
|
||||
if (fullVersion == wildcard)
|
||||
return true;
|
||||
}
|
||||
|
||||
Parts full;
|
||||
if (!(GetParts(fullVersion) case .Ok(out full)))
|
||||
return false;
|
||||
Parts wild;
|
||||
if (!(GetParts(wildcard) case .Ok(out wild)))
|
||||
return false;
|
||||
|
||||
// Don't allow a general wildcard to match a pre-prelease
|
||||
if ((!full.mPreRelease.IsEmpty) && (full.mPreRelease != wild.mPreRelease))
|
||||
return false;
|
||||
|
||||
for (int partIdx < 3)
|
||||
{
|
||||
if (wild.mPart[partIdx] case .Wild)
|
||||
return true;
|
||||
int comp = full.mPart[partIdx].NumOrDefault <=> wild.mPart[partIdx].NumOrDefault;
|
||||
switch (compareKind)
|
||||
{
|
||||
case .Caret:
|
||||
if ((full.mPart[partIdx].NumOrDefault > 0) || (wild.mPart[partIdx].NumOrDefault > 0))
|
||||
{
|
||||
if (comp != 0)
|
||||
return false;
|
||||
// First number matches, now make sure we are at least a high enough version on the other numbers
|
||||
compareKind = .Gte;
|
||||
}
|
||||
case .Tilde:
|
||||
if (wild.mPart[partIdx] case .Empty)
|
||||
return true;
|
||||
if (partIdx == 2)
|
||||
{
|
||||
if (comp < 0)
|
||||
return false;
|
||||
}
|
||||
else if (comp != 0)
|
||||
return false;
|
||||
case .Equal:
|
||||
if (wild.mPart[partIdx] case .Empty)
|
||||
return true;
|
||||
if (comp != 0)
|
||||
return false;
|
||||
case .Gt:
|
||||
if (comp > 0)
|
||||
return true;
|
||||
if (partIdx == 2)
|
||||
return false;
|
||||
if (comp < 0)
|
||||
return false;
|
||||
case .Gte:
|
||||
if (comp < 0)
|
||||
return false;
|
||||
case .Lt:
|
||||
if (comp < 0)
|
||||
return true;
|
||||
if (partIdx == 2)
|
||||
return false;
|
||||
if (comp > 0)
|
||||
return false;
|
||||
case .Lte:
|
||||
if (comp > 0)
|
||||
return false;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsVersionMatch(SemVer fullVersion, SemVer wildcard) => IsVersionMatch(fullVersion.mVersion, wildcard.mVersion);
|
||||
|
||||
public static Result<int> Compare(StringView lhs, StringView rhs)
|
||||
{
|
||||
Parts lhsParts;
|
||||
if (!(GetParts(lhs) case .Ok(out lhsParts)))
|
||||
return .Err;
|
||||
Parts rhsParts;
|
||||
if (!(GetParts(rhs) case .Ok(out rhsParts)))
|
||||
return .Err;
|
||||
|
||||
int comp = 0;
|
||||
for (int partIdx < 3)
|
||||
{
|
||||
comp = lhsParts.mPart[partIdx].NumOrDefault <=> rhsParts.mPart[partIdx].NumOrDefault;
|
||||
if (comp != 0)
|
||||
return comp;
|
||||
}
|
||||
|
||||
// Don't allow a general wildcard to match a pre-prelease
|
||||
if ((!lhsParts.mPreRelease.IsEmpty) || (!rhsParts.mPreRelease.IsEmpty))
|
||||
{
|
||||
if (lhsParts.mPreRelease.IsEmpty)
|
||||
return 1;
|
||||
if (rhsParts.mPreRelease.IsEmpty)
|
||||
return -1;
|
||||
return lhsParts.mPreRelease <=> rhsParts.mPreRelease;
|
||||
}
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
strBuffer.Append(mVersion);
|
||||
}
|
||||
|
||||
public static int operator<=>(Self lhs, Self rhs) => (lhs?.mVersion ?? "") <=> (rhs?.mVersion ?? "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ namespace IDE.Util
|
|||
case .Path(let path):
|
||||
return .Path(new String(path));
|
||||
case .Git(let url, let ver):
|
||||
if (ver == null)
|
||||
return .Git(new String(url), null);
|
||||
return .Git(new String(url), new SemVer(ver));
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +115,7 @@ namespace IDE.Util
|
|||
using (data.CreateObject(name))
|
||||
{
|
||||
data.Add("Git", path);
|
||||
if (ver != null)
|
||||
if ((ver != null) && (!ver.mVersion.IsEmpty))
|
||||
data.Add("Version", ver.mVersion);
|
||||
}
|
||||
case .SemVer(var ver):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue