mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-10 12:32:20 +02:00
Code generator support
This commit is contained in:
parent
195c705a46
commit
73099e4a04
15 changed files with 1472 additions and 83 deletions
|
@ -15,9 +15,10 @@ namespace IDE
|
|||
public Action mOnThreadDone ~ delete _;
|
||||
[ThreadStatic]
|
||||
public static bool mBpSetThreadName;
|
||||
public bool mAllowFastFinish;
|
||||
WaitEvent mWaitEvent = new WaitEvent() ~ delete _;
|
||||
|
||||
public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0)
|
||||
public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0, bool allowFastFinish = true)
|
||||
{
|
||||
Debug.Assert(Thread.CurrentThread == IDEApp.sApp.mMainThread);
|
||||
|
||||
|
@ -26,6 +27,7 @@ namespace IDE
|
|||
|
||||
BeefPerf.Event("DoBackground:starting", "");
|
||||
|
||||
mAllowFastFinish = allowFastFinish;
|
||||
mOnThreadDone = onThreadDone;
|
||||
mThreadRunning = true;
|
||||
mWaitEvent.Reset();
|
||||
|
@ -47,6 +49,7 @@ namespace IDE
|
|||
delete threadStart;
|
||||
mThread = null;
|
||||
mThreadRunning = false;
|
||||
mAllowFastFinish = true;
|
||||
BeefPerf.Event("DoBackground:threadEnd", "");
|
||||
|
||||
mWaitEvent.Set();
|
||||
|
@ -132,7 +135,7 @@ namespace IDE
|
|||
|
||||
}
|
||||
|
||||
public virtual void RequestFastFinish()
|
||||
public virtual void RequestFastFinish(bool force = false)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -190,15 +193,20 @@ namespace IDE
|
|||
return (mThreadWorker.mThreadRunning || mThreadWorkerHi.mThreadRunning);
|
||||
}
|
||||
|
||||
public bool IsPerformingBackgroundOperationHi()
|
||||
{
|
||||
return (mThreadWorkerHi.mThreadRunning);
|
||||
}
|
||||
|
||||
public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0)
|
||||
{
|
||||
CancelBackground();
|
||||
mThreadWorker.DoBackground(threadStart, onThreadDone, maxWait);
|
||||
}
|
||||
|
||||
public void DoBackgroundHi(ThreadStart threadStart, Action onThreadDone = null)
|
||||
public void DoBackgroundHi(ThreadStart threadStart, Action onThreadDone = null, bool allowFastFinish = true)
|
||||
{
|
||||
mThreadWorkerHi.DoBackground(threadStart, onThreadDone);
|
||||
mThreadWorkerHi.DoBackground(threadStart, onThreadDone, 0, allowFastFinish);
|
||||
}
|
||||
|
||||
public void CheckThreadDone()
|
||||
|
|
|
@ -94,6 +94,15 @@ namespace IDE.Compiler
|
|||
[CallingConvention(.Stdcall), CLink]
|
||||
static extern char8* BfCompiler_GetUsedOutputFileNames(void* bfCompiler, void* bfProject, bool flushQueuedHotFiles, out bool hadOutputChanges);
|
||||
|
||||
[CallingConvention(.Stdcall), CLink]
|
||||
static extern char8* BfCompiler_GetGeneratorTypeDefList(void* bfCompiler);
|
||||
|
||||
[CallingConvention(.Stdcall), CLink]
|
||||
static extern char8* BfCompiler_GetGeneratorInitData(void* bfCompiler, char8* typeDefName, char8* args);
|
||||
|
||||
[CallingConvention(.Stdcall), CLink]
|
||||
static extern char8* BfCompiler_GetGeneratorGenData(void* bfCompiler, char8* typeDefName, char8* args);
|
||||
|
||||
[CallingConvention(.Stdcall), CLink]
|
||||
static extern char8* BfCompiler_GetTypeDefList(void* bfCompiler);
|
||||
|
||||
|
@ -674,17 +683,21 @@ namespace IDE.Compiler
|
|||
|
||||
public override void RequestCancelBackground()
|
||||
{
|
||||
if ([Friend]mThreadWorker.mThreadRunning)
|
||||
if (mThreadWorker.mThreadRunning)
|
||||
{
|
||||
if ((mNativeBfCompiler != null) && (!gApp.mDeterministic))
|
||||
BfCompiler_Cancel(mNativeBfCompiler);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RequestFastFinish()
|
||||
public override void RequestFastFinish(bool force = false)
|
||||
{
|
||||
if ([Friend]mThreadWorker.mThreadRunning || [Friend]mThreadWorkerHi.mThreadRunning)
|
||||
if (mThreadWorker.mThreadRunning || mThreadWorkerHi.mThreadRunning)
|
||||
{
|
||||
if ((!force) &&
|
||||
((!mThreadWorker.mAllowFastFinish) || (!mThreadWorkerHi.mAllowFastFinish)))
|
||||
return;
|
||||
|
||||
if ((mNativeBfCompiler != null) && (!gApp.mDeterministic))
|
||||
BfCompiler_RequestFastFinish(mNativeBfCompiler);
|
||||
}
|
||||
|
@ -710,6 +723,21 @@ namespace IDE.Compiler
|
|||
return BfCompiler_GetCurConstEvalExecuteId(mNativeBfCompiler);
|
||||
}
|
||||
|
||||
public void GetGeneratorTypeDefList(String outStr)
|
||||
{
|
||||
outStr.Append(BfCompiler_GetGeneratorTypeDefList(mNativeBfCompiler));
|
||||
}
|
||||
|
||||
public void GetGeneratorInitData(String typeDefName, String args, String outStr)
|
||||
{
|
||||
outStr.Append(BfCompiler_GetGeneratorInitData(mNativeBfCompiler, typeDefName, args));
|
||||
}
|
||||
|
||||
public void GetGeneratorGenData(String typeDefName, String args, String outStr)
|
||||
{
|
||||
outStr.Append(BfCompiler_GetGeneratorGenData(mNativeBfCompiler, typeDefName, args));
|
||||
}
|
||||
|
||||
public void GetTypeDefList(String outStr)
|
||||
{
|
||||
outStr.Append(BfCompiler_GetTypeDefList(mNativeBfCompiler));
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace IDE
|
|||
public ProjectFolder mParentFolder;
|
||||
public String mName = new String() ~ delete _;
|
||||
public String mComment = new String() ~ delete _;
|
||||
public bool mDetached;
|
||||
|
||||
public virtual bool IncludeInMap
|
||||
{
|
||||
|
@ -105,6 +106,7 @@ namespace IDE
|
|||
|
||||
public virtual void Detach()
|
||||
{
|
||||
mDetached = true;
|
||||
ReleaseRef();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -598,10 +598,14 @@ namespace IDE.ui
|
|||
}
|
||||
}
|
||||
|
||||
var bfSystem = gApp.mBfResolveSystem;
|
||||
|
||||
String typeName = scope .();
|
||||
GetName(item, typeName);
|
||||
String info = scope .();
|
||||
bfSystem.Lock(0);
|
||||
gApp.mBfResolveCompiler.GetTypeDefInfo(typeName, info);
|
||||
bfSystem.Unlock();
|
||||
|
||||
for (let str in info.Split('\n'))
|
||||
{
|
||||
|
|
869
IDE/src/ui/GenerateDialog.bf
Normal file
869
IDE/src/ui/GenerateDialog.bf
Normal file
|
@ -0,0 +1,869 @@
|
|||
using Beefy.theme.dark;
|
||||
using Beefy.widgets;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Beefy.gfx;
|
||||
using Beefy.events;
|
||||
using IDE.Compiler;
|
||||
using System.Threading;
|
||||
using System.Security.Cryptography;
|
||||
using Beefy.theme;
|
||||
|
||||
namespace IDE.ui
|
||||
{
|
||||
class GenerateListView : DarkListView
|
||||
{
|
||||
public GenerateDialog mNewClassDialog;
|
||||
}
|
||||
|
||||
class GenerateKindBar : DarkComboBox
|
||||
{
|
||||
public class Entry
|
||||
{
|
||||
public String mTypeName ~ delete _;
|
||||
public String mName ~ delete _;
|
||||
}
|
||||
|
||||
public static Dictionary<String, int32> sMRU = new Dictionary<String, int32>() ~ delete _;
|
||||
public static int32 sCurrentMRUIndex = 1;
|
||||
|
||||
public GenerateDialog mNewClassDialog;
|
||||
public List<Entry> mEntries = new List<Entry>() ~ DeleteContainerAndItems!(_);
|
||||
public List<Entry> mShownEntries = new List<Entry>() ~ delete _;
|
||||
public String mFilterString ~ delete _;
|
||||
public String mCurLocation = new String() ~ delete _;
|
||||
public bool mIgnoreChange = false;
|
||||
|
||||
public this(GenerateDialog dialog)
|
||||
{
|
||||
mNewClassDialog = dialog;
|
||||
mLabelAlign = FontAlign.Left;
|
||||
Label = "";
|
||||
mLabelX = GS!(16);
|
||||
mPopulateMenuAction.Add(new => PopulateNavigationBar);
|
||||
MakeEditable();
|
||||
mEditWidget.mOnContentChanged.Add(new => NavigationBarChanged);
|
||||
mEditWidget.mOnGotFocus.Add(new (widget) => mEditWidget.mEditWidgetContent.SelectAll());
|
||||
mEditWidget.mEditWidgetContent.mWantsUndo = false;
|
||||
mEditWidget.mOnSubmit.Add(new => mNewClassDialog.[Friend]EditSubmitHandler);
|
||||
mFocusDropdown = false;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
}
|
||||
|
||||
static ~this()
|
||||
{
|
||||
for (var key in sMRU.Keys)
|
||||
delete key;
|
||||
}
|
||||
|
||||
public void SetLocation(String location)
|
||||
{
|
||||
if (mCurMenuWidget == null)
|
||||
{
|
||||
mIgnoreChange = true;
|
||||
mEditWidget.SetText(location);
|
||||
mEditWidget.mEditWidgetContent.SelectAll();
|
||||
// SetText can attempt to scroll to the right to make the cursor position visible. Just scroll back to the start.
|
||||
mEditWidget.HorzScrollTo(0);
|
||||
//mNewClassDialog.SelectKind();
|
||||
mIgnoreChange = false;
|
||||
}
|
||||
}
|
||||
private void PopulateNavigationBar(Menu menu)
|
||||
{
|
||||
List<StringView> findStrs = null;
|
||||
if (mFilterString != null)
|
||||
findStrs = scope:: List<StringView>(mFilterString.Split(' '));
|
||||
|
||||
EntryLoop: for (int32 entryIdx = 0; entryIdx < mEntries.Count; entryIdx++)
|
||||
{
|
||||
var entry = mEntries[entryIdx];
|
||||
if (findStrs != null)
|
||||
{
|
||||
for (let findStr in findStrs)
|
||||
{
|
||||
if (entry.mName.IndexOf(findStr, true) == -1)
|
||||
continue EntryLoop;
|
||||
}
|
||||
}
|
||||
|
||||
mShownEntries.Add(entry);
|
||||
var menuItem = menu.AddItem(entry.mName);
|
||||
menuItem.mOnMenuItemSelected.Add(new (evt) =>
|
||||
{
|
||||
mNewClassDialog.mPendingUIFocus = true;
|
||||
ShowEntry(entryIdx, entry);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ShowEntry(int32 entryIdx, Entry entry)
|
||||
{
|
||||
mEditWidget.SetText(entry.mName);
|
||||
mEditWidget.mEditWidgetContent.SelectAll();
|
||||
mCurMenuWidget?.Close();
|
||||
}
|
||||
|
||||
private void NavigationBarChanged(EditEvent theEvent)
|
||||
{
|
||||
if (mIgnoreChange)
|
||||
return;
|
||||
|
||||
var editWidget = (EditWidget)theEvent.mSender;
|
||||
var searchText = scope String();
|
||||
editWidget.GetText(searchText);
|
||||
searchText.Trim();
|
||||
mFilterString = searchText;
|
||||
ShowDropdown();
|
||||
mFilterString = null;
|
||||
}
|
||||
|
||||
bool mIgnoreShowDropdown;
|
||||
|
||||
public override MenuWidget ShowDropdown()
|
||||
{
|
||||
if (mIgnoreShowDropdown)
|
||||
return null;
|
||||
mIgnoreShowDropdown = true;
|
||||
defer { mIgnoreShowDropdown = false; }
|
||||
|
||||
if (!mEditWidget.mHasFocus)
|
||||
SetFocus();
|
||||
|
||||
if (mFilterString == null)
|
||||
mEditWidget.Content.SelectAll();
|
||||
|
||||
mShownEntries.Clear();
|
||||
base.ShowDropdown();
|
||||
|
||||
int32 bestItem = -1;
|
||||
int32 bestPri = -1;
|
||||
var menuWidget = (DarkMenuWidget)mCurMenuWidget;
|
||||
|
||||
for (int32 itemIdx = 0; itemIdx < menuWidget.mItemWidgets.Count; itemIdx++)
|
||||
{
|
||||
var menuItemWidget = (DarkMenuItem)menuWidget.mItemWidgets[itemIdx];
|
||||
|
||||
int32 pri;
|
||||
sMRU.TryGetValue(menuItemWidget.mMenuItem.mLabel, out pri);
|
||||
if (pri > bestPri)
|
||||
{
|
||||
bestItem = itemIdx;
|
||||
bestPri = pri;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestItem != -1)
|
||||
{
|
||||
mCurMenuWidget.mOnSelectionChanged.Add(new => SelectionChanged);
|
||||
mCurMenuWidget.SetSelection(bestItem);
|
||||
}
|
||||
|
||||
return menuWidget;
|
||||
}
|
||||
|
||||
void SelectionChanged(int selIdx)
|
||||
{
|
||||
if (mEditWidget.mEditWidgetContent.HasSelection())
|
||||
{
|
||||
bool prevIgnoreShowDropdown = mIgnoreShowDropdown;
|
||||
mIgnoreShowDropdown = true;
|
||||
mEditWidget.SetText("");
|
||||
mIgnoreShowDropdown = prevIgnoreShowDropdown;
|
||||
}
|
||||
}
|
||||
|
||||
public override void MenuClosed()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class GenerateDialog : IDEDialog
|
||||
{
|
||||
public class UIEntry
|
||||
{
|
||||
public String mName ~ delete _;
|
||||
public String mData ~ delete _;
|
||||
public String mLabel ~ delete _;
|
||||
public Widget mWidget;
|
||||
}
|
||||
|
||||
public enum ThreadState
|
||||
{
|
||||
None,
|
||||
Executing,
|
||||
Done
|
||||
}
|
||||
|
||||
public bool mPendingGenList;
|
||||
public GenerateKindBar mKindBar;
|
||||
public ThreadState mThreadState;
|
||||
public int mThreadWaitCount;
|
||||
public String mNamespace ~ delete _;
|
||||
public String mProjectName ~ delete _;
|
||||
public ProjectItem mProjectItem ~ _.ReleaseRef();
|
||||
public String mFolderPath ~ delete _;
|
||||
public List<UIEntry> mUIEntries = new .() ~ DeleteContainerAndItems!(_);
|
||||
public GenerateKindBar.Entry mSelectedEntry;
|
||||
public GenerateKindBar.Entry mPendingSelectedEntry;
|
||||
public String mUIData ~ delete _;
|
||||
public float mUIHeight = 0;
|
||||
public OutputPanel mOutputPanel;
|
||||
public bool mPendingUIFocus;
|
||||
public bool mSubmitting;
|
||||
public bool mSubmitQueued;
|
||||
public bool mRegenerating;
|
||||
|
||||
public this(ProjectItem projectItem, bool allowHashMismatch = false)
|
||||
{
|
||||
var project = projectItem.mProject;
|
||||
mProjectItem = projectItem;
|
||||
mProjectItem.AddRef();
|
||||
|
||||
mNamespace = new .();
|
||||
|
||||
var projectFolder = projectItem as ProjectFolder;
|
||||
var projectSource = projectItem as ProjectSource;
|
||||
if (projectSource != null)
|
||||
{
|
||||
projectFolder = projectSource.mParentFolder;
|
||||
mRegenerating = true;
|
||||
}
|
||||
|
||||
projectFolder.GetRelDir(mNamespace); mNamespace.Replace('/', '.'); mNamespace.Replace('\\', '.'); mNamespace.Replace(" ", "");
|
||||
if (mNamespace.StartsWith("src."))
|
||||
{
|
||||
mNamespace.Remove(0, 4);
|
||||
if (!project.mBeefGlobalOptions.mDefaultNamespace.IsWhiteSpace)
|
||||
{
|
||||
mNamespace.Insert(0, ".");
|
||||
mNamespace.Insert(0, project.mBeefGlobalOptions.mDefaultNamespace);
|
||||
}
|
||||
}
|
||||
else if (projectItem.mParentFolder == null)
|
||||
{
|
||||
mNamespace.Clear();
|
||||
mNamespace.Append(project.mBeefGlobalOptions.mDefaultNamespace);
|
||||
}
|
||||
else
|
||||
mNamespace.Clear();
|
||||
|
||||
mFolderPath = projectFolder.GetFullImportPath(.. new .());
|
||||
mProjectName = new String(projectItem.mProject.mProjectName);
|
||||
|
||||
mWindowFlags = .ClientSized | .TopMost | .Caption | .Border | .SysMenu | .Resizable | .PopupPosition;
|
||||
|
||||
AddOkCancelButtons(new (evt) => { CreateClass(); }, null, 0, 1);
|
||||
|
||||
Title = "Generate";
|
||||
|
||||
mKindBar = new GenerateKindBar(this);
|
||||
AddWidget(mKindBar);
|
||||
mKindBar.mEditWidget.mOnContentChanged.Add(new (theEvent) => { SelectKind(); });
|
||||
|
||||
if (mRegenerating)
|
||||
{
|
||||
mSubmitQueued = true;
|
||||
mKindBar.SetVisible(false);
|
||||
|
||||
SourceViewPanel sourceViewPanel = gApp.ShowProjectItem(projectSource, false);
|
||||
|
||||
String filePath = projectSource.GetFullImportPath(.. scope .());
|
||||
|
||||
String text = scope .();
|
||||
sourceViewPanel.mEditWidget.GetText(text);
|
||||
|
||||
StringView generatorName = default;
|
||||
StringView hash = default;
|
||||
|
||||
int dataIdx = -1;
|
||||
for (var line in text.Split('\n'))
|
||||
{
|
||||
if (!line.StartsWith("// "))
|
||||
{
|
||||
dataIdx = @line.MatchPos + 1;
|
||||
break;
|
||||
}
|
||||
int eqPos = line.IndexOf('=');
|
||||
if (eqPos == -1)
|
||||
break;
|
||||
StringView key = line.Substring(3, eqPos - 3);
|
||||
StringView value = line.Substring(eqPos + 1);
|
||||
if (key == "Generator")
|
||||
generatorName = value;
|
||||
else if (key == "GenHash")
|
||||
hash = value;
|
||||
else
|
||||
{
|
||||
UIEntry uiEntry = new .();
|
||||
uiEntry.mName = new .(key);
|
||||
uiEntry.mData = new .(value);
|
||||
mUIEntries.Add(uiEntry);
|
||||
}
|
||||
}
|
||||
|
||||
if ((generatorName == default) || (hash == default))
|
||||
{
|
||||
Close();
|
||||
gApp.Fail(scope $"File '{filePath}' was not generated by a generator that include regeneration information");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((dataIdx != -1) && (!allowHashMismatch))
|
||||
{
|
||||
var origHash = MD5Hash.Parse(hash).GetValueOrDefault();
|
||||
|
||||
StringView dataStr = text.Substring(dataIdx);
|
||||
var checkHash = MD5.Hash(.((.)dataStr.Ptr, dataStr.Length));
|
||||
|
||||
if (origHash != checkHash)
|
||||
{
|
||||
Close();
|
||||
Dialog dialog = ThemeFactory.mDefault.CreateDialog("Regenerate?", "This file has been modified since it was generated. Are you sure you want to regenerate?", DarkTheme.sDarkTheme.mIconWarning);
|
||||
dialog.AddButton("Yes", new (evt) =>
|
||||
{
|
||||
gApp.mProjectPanel.Regenerate(true);
|
||||
//dialog.Close();
|
||||
});
|
||||
dialog.AddButton("No", new (evt) =>
|
||||
{
|
||||
//dialog.Close();
|
||||
});
|
||||
dialog.PopupWindow(gApp.GetActiveWindow());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GenerateKindBar.Entry entry = new .();
|
||||
entry.mName = new .(generatorName);
|
||||
entry.mTypeName = new .(generatorName);
|
||||
mKindBar.mEntries.Add(entry);
|
||||
mPendingSelectedEntry = entry;
|
||||
}
|
||||
else
|
||||
mPendingGenList = true;
|
||||
|
||||
mKindBar.mMouseVisible = false;
|
||||
mTabWidgets.Add(mKindBar.mEditWidget);
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
var bfCompiler = gApp.mBfResolveCompiler;
|
||||
if (mThreadState == .Executing)
|
||||
{
|
||||
bfCompiler.WaitForBackground();
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectKind()
|
||||
{
|
||||
GenerateKindBar.Entry foundEntry = null;
|
||||
|
||||
String text = mKindBar.mEditWidget.GetText(.. scope .());
|
||||
for (var entry in mKindBar.mEntries)
|
||||
if (entry.mName == text)
|
||||
foundEntry = entry;
|
||||
|
||||
if (foundEntry == null)
|
||||
return;
|
||||
|
||||
if (mSelectedEntry == foundEntry)
|
||||
return;
|
||||
|
||||
mPendingSelectedEntry = foundEntry;
|
||||
}
|
||||
|
||||
public void ThreadProc()
|
||||
{
|
||||
var bfSystem = gApp.mBfResolveSystem;
|
||||
var bfCompiler = gApp.mBfResolveCompiler;
|
||||
|
||||
String outStr = scope String();
|
||||
|
||||
bfSystem.Lock(0);
|
||||
defer bfSystem.Unlock();
|
||||
|
||||
if (mSelectedEntry != null)
|
||||
{
|
||||
String args = scope .();
|
||||
var project = gApp.mWorkspace.FindProject(mProjectName);
|
||||
if (project == null)
|
||||
return;
|
||||
using (gApp.mMonitor.Enter())
|
||||
{
|
||||
args.AppendF(
|
||||
$"""
|
||||
ProjectName\t{mProjectName}
|
||||
ProjectDir\t{project.mProjectPath}
|
||||
FolderDir\t{mFolderPath}
|
||||
Namespace\t{mNamespace}
|
||||
DefaultNamespace\t{project.mBeefGlobalOptions.mDefaultNamespace}
|
||||
WorkspaceName\t{gApp.mWorkspace.mName}
|
||||
WorkspaceDir\t{gApp.mWorkspace.mDir}
|
||||
DateTime\t{DateTime.Now}
|
||||
|
||||
""");
|
||||
|
||||
if (mSubmitting)
|
||||
{
|
||||
args.AppendF($"Generator\t{mSelectedEntry.mTypeName}\n");
|
||||
for (var uiEntry in mUIEntries)
|
||||
{
|
||||
String data = scope .();
|
||||
if (uiEntry.mData != null)
|
||||
{
|
||||
data.Append(uiEntry.mData);
|
||||
}
|
||||
else if (var editWidget = uiEntry.mWidget as EditWidget)
|
||||
{
|
||||
editWidget.GetText(data);
|
||||
}
|
||||
else if (var comboBox = uiEntry.mWidget as DarkComboBox)
|
||||
{
|
||||
comboBox.GetLabel(data);
|
||||
}
|
||||
else if (var checkBox = uiEntry.mWidget as CheckBox)
|
||||
{
|
||||
checkBox.Checked.ToString(data);
|
||||
}
|
||||
data.Replace('\n', '\r');
|
||||
args.AppendF($"{uiEntry.mName}\t{data}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mUIData = new String();
|
||||
if (mSubmitting)
|
||||
bfCompiler.GetGeneratorGenData(mSelectedEntry.mTypeName, args, mUIData);
|
||||
else
|
||||
bfCompiler.GetGeneratorInitData(mSelectedEntry.mTypeName, args, mUIData);
|
||||
}
|
||||
else
|
||||
{
|
||||
bfCompiler.GetGeneratorTypeDefList(outStr);
|
||||
|
||||
for (var line in outStr.Split('\n', .RemoveEmptyEntries))
|
||||
{
|
||||
if (line.StartsWith("!error"))
|
||||
{
|
||||
ShowError(line.Substring(7));
|
||||
continue;
|
||||
}
|
||||
|
||||
var entry = new GenerateKindBar.Entry();
|
||||
var partItr = line.Split('\t');
|
||||
entry.mTypeName = new String(partItr.GetNext().Value);
|
||||
if (partItr.GetNext() case .Ok(let val))
|
||||
entry.mName = new String(val);
|
||||
else
|
||||
{
|
||||
entry.mName = new String(entry.mTypeName);
|
||||
int termPos = entry.mName.LastIndexOf('.');
|
||||
if (termPos != -1)
|
||||
entry.mName.Remove(0, termPos + 1);
|
||||
termPos = entry.mName.LastIndexOf('+');
|
||||
if (termPos != -1)
|
||||
entry.mName.Remove(0, termPos + 1);
|
||||
}
|
||||
mKindBar.mEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void CalcSize()
|
||||
{
|
||||
mWidth = GS!(320);
|
||||
mHeight = GS!(96);
|
||||
mMinWidth = mWidth;
|
||||
}
|
||||
|
||||
protected override void RehupMinSize()
|
||||
{
|
||||
mWidgetWindow.SetMinimumSize(GS!(240), (.)mUIHeight + GS!(24), true);
|
||||
}
|
||||
|
||||
void CreateClass()
|
||||
{
|
||||
//mClassViewPanel.[Friend]mSearchEdit.mOnSubmit(null);
|
||||
}
|
||||
|
||||
void ShowError(StringView error)
|
||||
{
|
||||
if (mOutputPanel == null)
|
||||
{
|
||||
mOutputPanel = new OutputPanel();
|
||||
AddWidget(mOutputPanel);
|
||||
ResizeComponents();
|
||||
}
|
||||
String str = scope .();
|
||||
str.Append(error);
|
||||
str.Replace('\r', '\n');
|
||||
str.Append("\n");
|
||||
mOutputPanel.WriteSmart(str);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if ((!mKindBar.mEditWidget.mHasFocus) && (mWidgetWindow.mHasFocus))
|
||||
{
|
||||
var sel = mPendingSelectedEntry ?? mSelectedEntry;
|
||||
String editText = mKindBar.mEditWidget.GetText(.. scope .());
|
||||
if ((sel != null) && (editText != sel.mName))
|
||||
{
|
||||
mKindBar.mIgnoreChange = true;
|
||||
mKindBar.mEditWidget.SetText(sel.mName);
|
||||
mKindBar.mIgnoreChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mThreadState == .Done)
|
||||
{
|
||||
if (mSelectedEntry != null)
|
||||
{
|
||||
List<UIEntry> oldEntries = scope .();
|
||||
Dictionary<String, UIEntry> entryMap = scope .();
|
||||
if (!mSubmitting)
|
||||
{
|
||||
for (var uiEntry in mUIEntries)
|
||||
{
|
||||
if (!entryMap.TryAdd(uiEntry.mName, uiEntry))
|
||||
oldEntries.Add(uiEntry);
|
||||
}
|
||||
mUIEntries.Clear();
|
||||
}
|
||||
|
||||
if (mUIData != null)
|
||||
{
|
||||
if (mOutputPanel != null)
|
||||
{
|
||||
mOutputPanel.RemoveSelf();
|
||||
DeleteAndNullify!(mOutputPanel);
|
||||
}
|
||||
|
||||
String fileName = default;
|
||||
StringView genText = default;
|
||||
bool hadError = false;
|
||||
|
||||
if (mUIData.IsEmpty)
|
||||
{
|
||||
gApp.Fail("Generator failed to return results");
|
||||
}
|
||||
|
||||
LinesLoop: for (var line in mUIData.Split('\n', .RemoveEmptyEntries))
|
||||
{
|
||||
var partItr = line.Split('\t');
|
||||
var kind = partItr.GetNext().Value;
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case "!error":
|
||||
ShowError(line.Substring(7));
|
||||
case "addEdit":
|
||||
if (mSubmitting)
|
||||
break;
|
||||
UIEntry uiEntry = new UIEntry();
|
||||
uiEntry.mName = partItr.GetNext().Value.UnQuoteString(.. new .());
|
||||
uiEntry.mLabel = partItr.GetNext().Value.UnQuoteString(.. new .());
|
||||
var defaultValue = partItr.GetNext().Value.UnQuoteString(.. scope .());
|
||||
DarkEditWidget editWidget = new DarkEditWidget();
|
||||
uiEntry.mWidget = editWidget;
|
||||
editWidget.SetText(defaultValue);
|
||||
editWidget.mEditWidgetContent.SelectAll();
|
||||
editWidget.mOnSubmit.Add(new => EditSubmitHandler);
|
||||
AddWidget(editWidget);
|
||||
mUIEntries.Add(uiEntry);
|
||||
mTabWidgets.Add(editWidget);
|
||||
case "addCombo":
|
||||
if (mSubmitting)
|
||||
break;
|
||||
UIEntry uiEntry = new UIEntry();
|
||||
uiEntry.mName = partItr.GetNext().Value.UnQuoteString(.. new .());
|
||||
uiEntry.mLabel = partItr.GetNext().Value.UnQuoteString(.. new .());
|
||||
var defaultValue = partItr.GetNext().Value.UnQuoteString(.. scope .());
|
||||
List<String> choices = new List<String>();
|
||||
DarkComboBox comboBox = new DarkComboBox();
|
||||
while (partItr.GetNext() case .Ok(let val))
|
||||
{
|
||||
choices.Add(val.UnQuoteString(.. new .()));
|
||||
}
|
||||
comboBox.mOnDeleted.Add(new (widget) => { DeleteContainerAndItems!(choices); });
|
||||
comboBox.mPopulateMenuAction.Add(new (menu) =>
|
||||
{
|
||||
for (var choice in choices)
|
||||
{
|
||||
var item = menu.AddItem(choice);
|
||||
item.mOnMenuItemSelected.Add(new (menu) =>
|
||||
{
|
||||
comboBox.Label = menu.mLabel;
|
||||
});
|
||||
}
|
||||
});
|
||||
uiEntry.mWidget = comboBox;
|
||||
comboBox.Label = defaultValue;
|
||||
AddWidget(comboBox);
|
||||
mUIEntries.Add(uiEntry);
|
||||
mTabWidgets.Add(comboBox);
|
||||
case "addCheckbox":
|
||||
if (mSubmitting)
|
||||
break;
|
||||
UIEntry uiEntry = new UIEntry();
|
||||
uiEntry.mName = partItr.GetNext().Value.UnQuoteString(.. new .());
|
||||
uiEntry.mLabel = partItr.GetNext().Value.UnQuoteString(.. new .());
|
||||
var defaultValue = partItr.GetNext().Value;
|
||||
DarkCheckBox checkbox = new DarkCheckBox();
|
||||
uiEntry.mWidget = checkbox;
|
||||
checkbox.Label = uiEntry.mLabel;
|
||||
checkbox.Checked = defaultValue == "True";
|
||||
AddWidget(checkbox);
|
||||
mUIEntries.Add(uiEntry);
|
||||
mTabWidgets.Add(checkbox);
|
||||
case "error":
|
||||
hadError = true;
|
||||
gApp.Fail(line.Substring(6).UnQuoteString(.. scope .()));
|
||||
case "fileName":
|
||||
fileName = line.Substring(9).UnQuoteString(.. scope:: .());
|
||||
case "options":
|
||||
case "data":
|
||||
genText = .(mUIData, @line.MatchPos + 1);
|
||||
break LinesLoop;
|
||||
}
|
||||
}
|
||||
|
||||
ResizeComponents();
|
||||
RehupMinSize();
|
||||
|
||||
if (fileName?.EndsWith(".bf", .OrdinalIgnoreCase) == true)
|
||||
fileName.RemoveFromEnd(3);
|
||||
|
||||
if ((fileName != null) && (!mRegenerating))
|
||||
{
|
||||
for (char8 c in fileName.RawChars)
|
||||
{
|
||||
if (!c.IsLetterOrDigit)
|
||||
{
|
||||
gApp.Fail(scope $"Invalid generated file name: {fileName}");
|
||||
hadError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileName.IsEmpty)
|
||||
{
|
||||
gApp.Fail("Geneator failed to specify file name");
|
||||
hadError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!hadError) && (genText != default) && (fileName != null))
|
||||
{
|
||||
if (!mProjectItem.mDetached)
|
||||
{
|
||||
if (mRegenerating)
|
||||
{
|
||||
gApp.mProjectPanel.Regenerate(mProjectItem as ProjectSource, genText);
|
||||
}
|
||||
else
|
||||
{
|
||||
gApp.mProjectPanel.Generate(mProjectItem as ProjectFolder, fileName, genText);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
if (mPendingUIFocus)
|
||||
{
|
||||
mPendingUIFocus = false;
|
||||
if (!mUIEntries.IsEmpty)
|
||||
mUIEntries[0].mWidget.SetFocus();
|
||||
}
|
||||
|
||||
DeleteAndNullify!(mUIData);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if (mSubmitting)
|
||||
{
|
||||
if (!mClosed)
|
||||
{
|
||||
mSubmitting = false;
|
||||
mSubmitQueued = false;
|
||||
mDefaultButton.mDisabled = false;
|
||||
mEscButton.mDisabled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var uiEntry in entryMap.Values)
|
||||
oldEntries.Add(uiEntry);
|
||||
|
||||
for (var uiEntry in oldEntries)
|
||||
{
|
||||
mTabWidgets.Remove(uiEntry.mWidget);
|
||||
uiEntry.mWidget.RemoveSelf();
|
||||
DeleteAndNullify!(uiEntry.mWidget);
|
||||
}
|
||||
|
||||
ClearAndDeleteItems(oldEntries);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mKindBar.mMouseVisible = true;
|
||||
mKindBar.SetFocus();
|
||||
mKindBar.SetLocation("New Class");
|
||||
}
|
||||
mThreadState = .None;
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
bool isWorking = false;
|
||||
if (mThreadState == .None)
|
||||
{
|
||||
if ((mPendingGenList) || (mPendingSelectedEntry != null))
|
||||
{
|
||||
isWorking = true;
|
||||
var bfCompiler = gApp.mBfResolveCompiler;
|
||||
if (!bfCompiler.IsPerformingBackgroundOperation())
|
||||
{
|
||||
bfCompiler.CheckThreadDone();
|
||||
|
||||
mPendingGenList = false;
|
||||
if (mPendingSelectedEntry != null)
|
||||
{
|
||||
if (mSubmitQueued)
|
||||
mSubmitting = true;
|
||||
mSelectedEntry = mPendingSelectedEntry;
|
||||
mPendingSelectedEntry = null;
|
||||
}
|
||||
mThreadState = .Executing;
|
||||
bfCompiler.DoBackgroundHi(new => ThreadProc, new () =>
|
||||
{
|
||||
mThreadState = .Done;
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gApp.mBfResolveCompiler.CheckThreadDone();
|
||||
|
||||
if ((mThreadState == .Executing) || (isWorking))
|
||||
{
|
||||
mThreadWaitCount++;
|
||||
if (mUpdateCnt % 8 == 0)
|
||||
MarkDirty();
|
||||
}
|
||||
else
|
||||
mThreadWaitCount = 0;
|
||||
}
|
||||
|
||||
public override void Resize(float x, float y, float width, float height)
|
||||
{
|
||||
base.Resize(x, y, width, height);
|
||||
ResizeComponents();
|
||||
//mClassViewPanel.Resize(0, 0, width, height - GS!(34));
|
||||
}
|
||||
|
||||
public override void PopupWindow(WidgetWindow parentWindow, float offsetX = 0, float offsetY = 0)
|
||||
{
|
||||
base.PopupWindow(parentWindow, offsetX, offsetY);
|
||||
//mKindBar.SetFocus();
|
||||
}
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
void DrawLabel(Widget widget, StringView label)
|
||||
{
|
||||
if (widget == null)
|
||||
return;
|
||||
if (widget is CheckBox)
|
||||
return;
|
||||
g.DrawString(label, widget.mX + GS!(6), widget.mY - GS!(20));
|
||||
}
|
||||
|
||||
DrawLabel(mKindBar, mRegenerating ? "Regenerating ..." : "Using Generator");
|
||||
for (var uiEntry in mUIEntries)
|
||||
DrawLabel(uiEntry.mWidget, uiEntry.mLabel);
|
||||
}
|
||||
|
||||
public override void DrawAll(Graphics g)
|
||||
{
|
||||
base.DrawAll(g);
|
||||
|
||||
if (mThreadWaitCount > 10)
|
||||
{
|
||||
using (g.PushColor(0x60505050))
|
||||
g.FillRect(0, 0, mWidth, mHeight - GS!(40));
|
||||
IDEUtils.DrawWait(g, mWidth/2, mHeight/2, mUpdateCnt);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ResizeComponents()
|
||||
{
|
||||
base.ResizeComponents();
|
||||
|
||||
mUIHeight = GS!(32);
|
||||
|
||||
float insetSize = GS!(12);
|
||||
|
||||
mKindBar.Resize(insetSize, mUIHeight, mWidth - insetSize - insetSize, GS!(22));
|
||||
mUIHeight += GS!(52);
|
||||
|
||||
for (var uiEntry in mUIEntries)
|
||||
{
|
||||
if (uiEntry.mWidget == null)
|
||||
continue;
|
||||
|
||||
float height = GS!(22);
|
||||
if (uiEntry.mWidget is ComboBox)
|
||||
height = GS!(26);
|
||||
|
||||
if (uiEntry.mWidget is CheckBox)
|
||||
{
|
||||
mUIHeight -= GS!(20);
|
||||
height = GS!(20);
|
||||
}
|
||||
|
||||
uiEntry.mWidget.Resize(insetSize, mUIHeight, mWidth - insetSize - insetSize, height);
|
||||
mUIHeight += height + GS!(28);
|
||||
}
|
||||
|
||||
if (mOutputPanel != null)
|
||||
{
|
||||
float startY = mKindBar.mVisible ? GS!(60) : GS!(36);
|
||||
mOutputPanel.Resize(insetSize, startY, mWidth - insetSize - insetSize, Math.Max(mHeight - startY - GS!(44), GS!(32)));
|
||||
mUIHeight = Math.Max(mUIHeight, GS!(160));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (mThreadState == .Executing)
|
||||
{
|
||||
var bfCompiler = gApp.mBfResolveCompiler;
|
||||
bfCompiler.RequestFastFinish();
|
||||
bfCompiler.WaitForBackground();
|
||||
}
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Submit()
|
||||
{
|
||||
mDefaultButton.mDisabled = true;
|
||||
mEscButton.mDisabled = true;
|
||||
|
||||
if (mSubmitQueued)
|
||||
return;
|
||||
mSubmitQueued = true;
|
||||
mPendingSelectedEntry = mPendingSelectedEntry ?? mSelectedEntry;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -327,7 +327,7 @@ namespace IDE.ui
|
|||
int lineIdx = (curLine + lineOfs) % lineCount;
|
||||
|
||||
if (content.GotoRefrenceAtLine(lineIdx))
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -792,16 +792,96 @@ namespace IDE.ui
|
|||
}
|
||||
}
|
||||
|
||||
public void NewClass(ProjectFolder folder)
|
||||
public void GenerateCode(ProjectFolder folder)
|
||||
{
|
||||
DarkDialog dialog = (DarkDialog)ThemeFactory.mDefault.CreateDialog("New Class", "Class Name");
|
||||
/*DarkDialog dialog = (DarkDialog)ThemeFactory.mDefault.CreateDialog("New Class", "Class Name");
|
||||
dialog.mMinWidth = GS!(300);
|
||||
dialog.mDefaultButton = dialog.AddButton("OK", new (evt) => DoNewClass(folder, evt));
|
||||
dialog.mEscButton = dialog.AddButton("Cancel");
|
||||
dialog.AddEdit("Unnamed");
|
||||
dialog.PopupWindow(gApp.GetActiveWindow());
|
||||
dialog.PopupWindow(gApp.GetActiveWindow());*/
|
||||
|
||||
var dialog = new GenerateDialog(folder);
|
||||
dialog.PopupWindow(gApp.GetActiveWindow());
|
||||
}
|
||||
|
||||
public void Regenerate(bool allowHashMismatch)
|
||||
{
|
||||
mListView.GetRoot().WithSelectedItems(scope (selectedItem) =>
|
||||
{
|
||||
if (mListViewToProjectMap.GetValue(selectedItem) case .Ok(var sourceProjectItem))
|
||||
{
|
||||
var dialog = new GenerateDialog(sourceProjectItem, allowHashMismatch);
|
||||
dialog.PopupWindow(gApp.GetActiveWindow());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Regenerate(ProjectSource projectSource, StringView fileText)
|
||||
{
|
||||
var sourceViewPanel = gApp.ShowProjectItem(projectSource, false);
|
||||
sourceViewPanel.mEditWidget.SetText(scope .(fileText));
|
||||
}
|
||||
|
||||
public void Generate(ProjectFolder folder, StringView fileName, StringView fileText)
|
||||
{
|
||||
let project = folder.mProject;
|
||||
if (project.mNeedsCreate)
|
||||
project.FinishCreate();
|
||||
String relFileName = scope .(fileName);
|
||||
if (!relFileName.Contains('.'))
|
||||
relFileName.Append(".bf");
|
||||
|
||||
String fullFilePath = scope String();
|
||||
String relPath = scope String();
|
||||
folder.GetRelDir(relPath);
|
||||
if (relPath.Length > 0)
|
||||
relPath.Append("/");
|
||||
relPath.Append(relFileName);
|
||||
folder.mProject.GetProjectFullPath(relPath, fullFilePath);
|
||||
String dirName = scope String();
|
||||
Path.GetDirectoryPath(fullFilePath, dirName);
|
||||
Directory.CreateDirectory(dirName).IgnoreError();
|
||||
|
||||
if (File.Exists(fullFilePath))
|
||||
{
|
||||
var error = scope String();
|
||||
error.AppendF("File '{0}' already exists", fullFilePath);
|
||||
IDEApp.sApp.Fail(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.WriteAllText(fullFilePath, fileText) case .Err)
|
||||
{
|
||||
var error = scope String();
|
||||
error.AppendF("Failed to create file '{0}'", fullFilePath);
|
||||
gApp.Fail(error);
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectSource projectSource = new ProjectSource();
|
||||
projectSource.mIncludeKind = (folder.mIncludeKind == .Auto) ? .Auto : .Manual;
|
||||
projectSource.mName.Set(relFileName);
|
||||
projectSource.mPath = new String();
|
||||
folder.mProject.GetProjectRelPath(fullFilePath, projectSource.mPath);
|
||||
projectSource.mProject = folder.mProject;
|
||||
projectSource.mParentFolder = folder;
|
||||
folder.AddChild(projectSource);
|
||||
let projectItem = AddProjectItem(projectSource);
|
||||
if (projectItem != null)
|
||||
{
|
||||
mListView.GetRoot().SelectItemExclusively(projectItem);
|
||||
mListView.EnsureItemVisible(projectItem, false);
|
||||
}
|
||||
Sort();
|
||||
if (folder.mIncludeKind != .Auto)
|
||||
folder.mProject.SetChanged();
|
||||
|
||||
gApp.RecordHistoryLocation(true);
|
||||
gApp.ShowProjectItem(projectSource);
|
||||
gApp.RecordHistoryLocation(true);
|
||||
}
|
||||
|
||||
void DoNewClass(ProjectFolder folder, DialogEvent evt)
|
||||
{
|
||||
Dialog dlg = (Dialog)evt.mSender;
|
||||
|
@ -1470,7 +1550,10 @@ namespace IDE.ui
|
|||
}
|
||||
|
||||
if (doReleaseRef)
|
||||
{
|
||||
projectItem.mDetached = true;
|
||||
projectItem.ReleaseRef();
|
||||
}
|
||||
//TODO: Defer this, projectItem is needed for a backgrounded QueueProjectSourceRemoved
|
||||
//delete projectItem;
|
||||
}
|
||||
|
@ -2471,17 +2554,26 @@ namespace IDE.ui
|
|||
}
|
||||
});
|
||||
|
||||
item = menu.AddItem("New Class...");
|
||||
item = menu.AddItem("Generate...");
|
||||
item.mOnMenuItemSelected.Add(new (item) =>
|
||||
{
|
||||
var projectFolder = GetSelectedProjectFolder();
|
||||
if (projectFolder != null)
|
||||
{
|
||||
if (CheckProjectModify(projectFolder.mProject))
|
||||
NewClass(projectFolder);
|
||||
GenerateCode(projectFolder);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if ((projectItem != null) && (projectItem is ProjectSource) && (!isProject))
|
||||
{
|
||||
item = menu.AddItem("Regenerate");
|
||||
item.mOnMenuItemSelected.Add(new (item) =>
|
||||
{
|
||||
Regenerate(false);
|
||||
});
|
||||
}
|
||||
|
||||
item = menu.AddItem("Import File...");
|
||||
item.mOnMenuItemSelected.Add(new (item) => { mImportFileDeferred = true; /* ImportFile();*/ });
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue